Programming Forums
User Name Password Register
 

RSS Feed
FORUM INDEX | TODAY'S POSTS | UNANSWERED THREADS | ADVANCED SEARCH

Reply
 
Thread Tools Display Modes
Old Jun 8th, 2006, 1:22 AM   #1
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 439
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Smile 3D Engine in PHP

Well I originally started this project in Flash but it didn't work out for some reason there. I actually posted that problem here. After looking at the code for a while I realized that my math worked out and that something was wrong with the way Flash accesses arrays, at least in my humble opinion. Nevertheless, I succeeded in creating the 3d engine in PHP and it outputs itself as a still image of the view of a cube with white lines for visible edges and gray lines for non-visible ones. It turned out pretty nice I think. It uses pretty much every matrix operation you can think of and that's how it's structured. Test it out and tell me what you think. I still have to make modifications to it in order to be able to import some other formats for objects like in the VRML language, but for now enjoy the cube, change some of the numbers around and see the result.
<?php
	// Creates a virtual cube composed of polygons
	$cube = new VirtualObject();
	// Viewable screen dimensions
	$width = 640;
	$height = 480;
	// Sets up the camera's orientation and its focal distance
	$x = sqrt(2) / 2;
	$focal = 500;
	$a =	array(
				array(-$x, 0.5, -0.5, 0),
				array($x, 0.5, -0.5, 0),
				array(0, -$x, -$x, 0),
				array(0, 0, 0, 1)
			);
	// The vector normal to the viewing plane and parallel to the viewing direction
	$n2[0][0] = $a[0][2];
	$n2[1][0] = $a[1][2];
	$n2[2][0] = $a[2][2];
	$n2[3][0] = 0;
	// Invert camera's plane to allow for change of basis
	$a = inverse($a);
	// Displace camera to its position in the world
	$a[0][3] = 150;
	$a[1][3] = 150;
	$a[2][3] = 300 * $x;

	// Create two images
	$image1 = imagecreate($width, $height);
	$image2 = imagecreate($width, $height);
	// Set the line thickness of each image
	imagesetthickness($image1, 1);
	imagesetthickness($image2, 1);
	// Set the background colors for each image
	$blue1 = imagecolorallocate($image1, 0, 0, 127);
	$blue2 = imagecolorallocate($image2, 0, 0, 127);
	// Define some colors for each image
	$white = imagecolorallocate($image1, 255, 255, 255);
	$gray = imagecolorallocate($image2, 127, 127, 127);

	// Draw each polygon
	for($i = 0; $i < $cube->getPolyCount(); $i++){
		// Retrieve the polygons from the cube object
		$points = $cube->getPolygon($i);
		// Modify each point of the polygon so that its now distanced to the camera's center point
		for($j = 0; $j < 3; $j++){
			$points[0][$j][0] -= $a[$j][3];
			$points[1][$j][0] -= $a[$j][3];
			$points[2][$j][0] -= $a[$j][3];
		}
		// Determine the normal vector for each polygon
		$n1 = crossProduct(addition($points[0], scale(-1, $points[1])), addition($points[2], scale(-1, $points[1])));
		// Write each vector in terms of the camera's orientation (change of basis)
		$points[0] = multiply($a, $points[0]);
		$points[1] = multiply($a, $points[1]);
		$points[2] = multiply($a, $points[2]);
		// Determine the distortion due to perspective
		$focus[0] = $focal / magnitude($points[0]);
		$focus[1] = $focal / magnitude($points[1]);
		$focus[2] = $focal / magnitude($points[2]);
		// Transform the 3d points into 2d coordinates for drawing
		$x0 = $width / 2 + ($points[0][0][0] * $focus[0]);
		$x1 = $width / 2 + ($points[1][0][0] * $focus[1]);
		$x2 = $width / 2 + ($points[2][0][0] * $focus[2]);
		$y0 = $height / 2 + ($points[0][1][0] * $focus[0]);
		$y1 = $height / 2 + ($points[1][1][0] * $focus[1]);
		$y2 = $height / 2 + ($points[2][1][0] * $focus[2]);
		// Determine the cosine of the angle between the viewing plane and the polygonal plane
		$cosTheta = dotProduct($n1, $n2) / (magnitude($n1) * magnitude($n2));
		if($cosTheta <= 0){
			// White lines because these are the planes at the front of the object
			imageline($image1, $x0, $y0, $x1, $y1, $white);
			imageline($image1, $x1, $y1, $x2, $y2, $white);
//			imageline($image1, $x2, $y2, $x0, $y0, $white);	// Draws the final polygon line
		}else{
			// Gray lines because these are the planes at the reare of the object
			imageline($image2, $x0, $y0, $x1, $y1, $gray);
			imageline($image2, $x1, $y1, $x2, $y2, $gray);
//			imageline($image2, $x2, $y2, $x0, $y0, $gray);	// Draws the final polygon line
		}
	}

	// Make transparent the background of the viewable polygon drawing
	imagecolortransparent($image1, $blue1);
	// Layer it over the non-viewable polygon drawing
	imagecopymerge($image2, $image1, 0, 0, 0, 0, 640, 480, 100);
	// Output the finished image
	imagepng($image2);
	// Destroy the images
	imagedestroy($image1);
	imagedestroy($image2);

	// Computes the cross product of two vectors
	// $a: a row or column vector
	// $b: a row or column vector
	function crossProduct($a, $b){
		if(sizeof($a) == 1){	// Cross product of row vectors
			return	array(	// Return the cross product of row vectors as a row vector
						array(	$a[0][1] * $b[0][2] - $a[0][2] * $b[0][1],
								-1 * ($a[0][0] * $b[0][2] - $a[0][2] * $b[0][0]),
								$a[0][0] * $b[0][1] - $a[0][1] * $b[0][0],
								0
						)
					);
		}else{	// Cross product of column vectors
			return	array(	// Return the cross product of column vectors as a column vector
						array($a[1][0] * $b[2][0] - $a[2][0] * $b[1][0]),
						array(-1 * ($a[0][0] * $b[2][0] - $a[2][0] * $b[0][0])),
						array($a[0][0] * $b[1][0] - $a[1][0] * $b[0][0]),
						array(0)
					);
		}
	}

	// Computes the addition of two matrices
	// $A: an m x n matrix
	// $B: an m x n matrix
	function addition($A, $B){
		$M;	// The added matrix

		// Add the two matrices together
		for($i = 0; $i < sizeof($A); $i++){
			for($j = 0; $j < sizeof($A[0]); $j++){
				$M[$i][$j] = $A[$i][$j] + $B[$i][$j];
			}
		}

		return $M;	// Return the added matrix
	}

	// Computes a random matrix
	// $m: the row dimension
	// $n: the column dimension
	// $lower: the lower bound of random numbers
	// $upper: the upper bound of random numbers
	function randMatrix($m, $n, $lower, $upper){
		$M;	// A random matrix

		// Generate the random matrix
		for($i = 0; $i < $m; $i++){
			for($j = 0; $j < $n; $j++){
				$M[$i][$j] = rand($lower, $upper);
			}
		}

		return $M;	// Return the random matrix
	}

	// Computes the magnitude of a vector
	// $a: a row or column vector
	function magnitude($a){
		$result = 0;	// The magnitude of the vector

		// Use the Pythagorean theorem to determine the vector's magnitude
		for($i = 0; $i < sizeof($a); $i++){
			for($j = 0; $j < sizeof($a[0]); $j++){
				$result += pow($a[$i][$j], 2);
			}
		}

		return sqrt($result);	// Return the magnitude of the vector
	}

	// Computes the adjoint of a matrix
	// $A: a square matrix
	function adjoint($A){
		return scale(determinant($A), inverse($A));	// Returns the adjoint of a matrix
	}

	// Computes a scaled matrix
	// $k: a scalar number
	// $A: an m x n matrix
	function scale($k, $A){

		// Scale the matrix by multiplying each entry by k
		for($i = 0; $i < sizeof($A); $i++){
			for($j = 0; $j < sizeof($A[0]); $j++){
				$A[$i][$j] *= $k;
			}
		}

		return $A;	// Returns the scaled matrix
	}

	// Computes the determinant of a matrix
	// $A: a square matrix
	function determinant($A){
		$determinant = 1;	// The determinant of the matrix
		$n = sizeof($A);	// Dimension of the square matrix
		$E = identity($n);	// An elementary matrix

		// Find the determinant of a matrix by performing elementary matrix operations
		// on an augmented matrix through the use of elementary matrices in order
		// to convert the matrix into upper triangular form
		// Multiply the diagonals of the upper triangular matrix to arrive at the
		// determinant, only elementary operations I and III are allowed on the matrix.
		// If operation I is performed at any time, multiply the determinant by a -1.
		for($j = 0; $j < $n; $j++){
			for($i = $j; $i < $n; $i++){
				if($i == $j){
					if($A[$i][$j] == 0){
						for($k = $j + 1; $k < $n; $k++){
							if($A[$k][$j] != 0){
								$swap = $A[$i];
								$A[$i] = $A[$k];
								$A[$k] = $swap;
								$determinant *= -1;
								$k = $n;
							}
						}
					}
					$determinant *= $A[$i][$j];
				}else{
					$E[$i][$j] = -$A[$i][$j] / $A[$j][$j];
				}
				$A = multiply($E, $A);
				$E = identity($n);
			}
		}

		return $determinant;	// Return the determinant of the matrix
	}

	// Computes the inverse of a matrix
	// $A: a square matrix
	function inverse($A){
		$n = sizeof($A);	// Dimension of the square matrix
		$V = identity($n);	// The inverse of the matrix
		$E = identity($n);	// An elementary matrix

		// Find the inverse matrix by performing elementary matrix operations
		// on an augmented matrix through the use of elementary matrices
		for($j = 0; $j < $n; $j++){
			for($i = $j; $i < $n; $i++){
				if($i == $j){
					if($A[$i][$j] == 0){
						for($k = $j + 1; $k < $n; $k++){
							if($A[$k][$j] != 0){
								$swap = $A[$i];
								$A[$i] = $A[$k];
								$A[$k] = $swap;
								$swap = $V[$i];
								$V[$i] = $V[$k];
								$V[$k] = $swap;
								$k = $n;
							}
						}
					}
					$E[$i][$j] = 1 / $A[$i][$j];
				}else{
					$E[$i][$j] = -$A[$i][$j];
				}
				$A = multiply($E, $A);
				$V = multiply($E, $V);
				$E = identity($n);
			}
		}
		for($j = $n - 1; $j > 0; $j--){
			for($i = $j - 1; $i > -1; $i--){
				$E[$i][$j] = -$A[$i][$j];
				$A = multiply($E, $A);
				$V = multiply($E, $V);
				$E = identity($n);
			}
		}

		return $V;	// Return the inverse matrix
	}

	// Computes the product of a matrix multiplication
	// $A: An m x n matrix
	// $B: An n x o matrix
	function multiply($A, $B){
		$M;	// The product of the multiplication
		$B = transpose($B);	// Transpose the second matrix

		// Do the multiplication by making use of the dot product
		for($i = 0; $i < sizeof($A); $i++){
			for($j = 0; $j < sizeof($B); $j++){
				$M[$i][$j] = dotProduct(array($A[$i]), array($B[$j]));
			}
		}

		return $M;	// Return the product
	}

	// Computes the dot product of two vectors
	// $a: a row or column vector
	// $b; a row or column vector
	function dotProduct($a, $b){
		$result = 0;	// The result of the dot product

		// The dot product
		for($i = 0; $i < sizeof($a); $i++){
			for($j = 0; $j < sizeof($a[0]); $j++){
				$result += $a[$i][$j] * $b[$i][$j];
			}
		}

		return $result;	// Return the dot product
	}

	// Computes the transpose of a matrix
	// $A: the matrix to be transposed
	function transpose($A){
		$T;		// Transpose matrix

		// Switch the i,jth entry to the j,ith entry
		for($i = 0; $i < sizeof($A); $i++){
			for($j = 0; $j < sizeof($A[0]); $j++){
				$T[$j][$i] = $A[$i][$j];
			}
		}

		return $T;	// Return the transpose matrix
	}

	// Computes an identity matrix of size n x n
	// $n: size of the matrix wanted
	function identity($n){
		$I;		// Identity matrix

		// Fill the matrices diagonal with 1's and everything else with 0's
		for($i = 0; $i < $n; $i++){
			for($j = 0; $j < $n; $j++){
				if($i == $j){
					$I[$i][$j] = 1;
				}else{
					$I[$i][$j] = 0;
				}
			}
		}

		return $I;	// Return the identity matrix
	}

	class VirtualObject{
		// Member declarations
		private $polyObject;

		// Constructor
		function __construct(){
			$this->polyObject =
			array(	// object
				array(	// polygon
					array(	// vector
						array(50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(-50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(-50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(50),
						array(50),
						array(-50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(-50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(50),
						array(-50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(-50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(50),
						array(0)
					)
				),
				array(	// polygon
					array(	// vector
						array(50),
						array(-50),
						array(50),
						array(0)
					),
					array(	// vector
						array(50),
						array(-50),
						array(-50),
						array(0)
					),
					array(	// vector
						array(-50),
						array(-50),
						array(-50),
						array(0)
					)
				)
			);
		}

		// Method declarations

		// Returns the polygon count of the object
		public function getPolyCount(){
			return sizeof($this->polyObject);
		}

		// Returns a particular polygon of the object
		public function getPolygon($n){
			return $this->polyObject[$n];
		}
	}
?>
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jun 8th, 2006, 3:10 AM   #2
a thing
Unverified User
 
a thing's Avatar
 
Join Date: Aug 2005
Location: none
Posts: 146
Rep Power: 0 a thing is on a distinguished road
Very exotic for PHP.
__________________
Warning: My posts may change (dramatically) within the first 15 minutes they're posted.
Got 'Nux?—GNU/Linux and other free software support.
It's GNU/Linux, not just Linux.
a thing is offline   Reply With Quote
Old Jun 8th, 2006, 4:12 AM   #3
mrynit
Hobbyist Programmer
 
mrynit's Avatar
 
Join Date: Mar 2006
Location: WA, USA
Posts: 343
Rep Power: 3 mrynit is on a distinguished road
Send a message via AIM to mrynit Send a message via MSN to mrynit Send a message via Yahoo to mrynit Send a message via Skype™ to mrynit
i get this error when i try to run it on my php 5 server.

Quote:
Fatal error: Call to undefined function imagecreate() in C:\Program Files\Apache Group\Apache2\document_root\3d.php on line 29
28  // Create two images
29  $image1 = imagecreate($width, $height);
30  $image2 = imagecreate($width, $height);
__________________
i dont know much about programming but i try to help
mrynit is offline   Reply With Quote
Old Jun 8th, 2006, 6:27 PM   #4
Eric the Red
Hobbyist Programmer
 
Eric the Red's Avatar
 
Join Date: Feb 2006
Posts: 214
Rep Power: 0 Eric the Red is an unknown quantity at this point
I got an error too.

Quote:
Parse error: parse error, unexpected T_STRING, expecting T_OLD_FUNCTION or T_FUNCTION or T_VAR or '}' in /home/guest/user1/public_html/coolFolder/new2.php on line 339
__________________
Death smiles at us all. All a man can do is smile back.
Eric the Red is offline   Reply With Quote
Old Jun 8th, 2006, 9:19 PM   #5
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 439
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
Mrynit I think your error may be that the graphical functions for PHP aren't installed on your system. Check the PHP manual and look at the Image Functions section. So you need to find out how to install them, since it says they're undefined. That's just a theory though I'm not entirely sure.

Eric The Red, I have NO idea what your error is so I can't offer you a fix. I can however show you what the output of the program is.
Attached Images
File Type: png php_cube.png (2.5 KB, 242 views)
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jun 9th, 2006, 11:33 AM   #6
big_k105
PFO Founder

 
big_k105's Avatar
 
Join Date: Mar 2004
Location: Fargo, ND
Posts: 1,667
Rep Power: 10 big_k105 is on a distinguished road
Send a message via AIM to big_k105 Send a message via MSN to big_k105 Send a message via Yahoo to big_k105
Well I must be missing something on my server because this is what I get

http://www.kylekonline.com/test/newtest.php
__________________
BIG K aka Kyle
Programming Forums
Kyle K Online

Please do not PM or email me programming questions. Post them in the forums instead.
big_k105 is offline   Reply With Quote
Old Jun 9th, 2006, 11:40 AM   #7
Pizentios
Programming Guru
 
Pizentios's Avatar
 
Join Date: May 2004
Location: Brandon, Manitoba, Canada
Posts: 2,023
Rep Power: 7 Pizentios is on a distinguished road
Send a message via ICQ to Pizentios Send a message via MSN to Pizentios
Quote:
Originally Posted by mrynit
i get this error when i try to run it on my php 5 server.



28  // Create two images
29  $image1 = imagecreate($width, $height);
30  $image2 = imagecreate($width, $height);

you need to have GD enabled.

looks pretty cool though, what are you planning on coding with it (if anything at all)?
__________________
Profanity is the one language that all programmers understand.

Check out my Blog <---updated Nov 30 2007!
Pizentios is offline   Reply With Quote
Old Jun 9th, 2006, 2:41 PM   #8
grimpirate
King of Portal
 
grimpirate's Avatar
 
Join Date: Sep 2005
Posts: 439
Rep Power: 4 grimpirate is on a distinguished road
Send a message via Yahoo to grimpirate
I'm confused, when I looked at your page big k everything looked in order. I'm not really planning on doing anything much with it Pizentios, I was just curious to undertake the mathematics behind such a thing since I'd never quite understood how people achieved the effect. There's also some stuff in there which isn't quite right, like if it so happens that a point is located at the camera's center point the program will crash because of a division by 0 which I have to fix with a try catch routine or something. The matrix subroutines should also catch some errors, like if dimensions don't match for certain cases, and for instance if a matrix is singular (that it has no inverse), in which case changing the basis would be impossible. I'm not quite sure how that would work. I know some people place their camera at the world origin and then translate everything accordingly, but that didn't seem very realistic, since every object in the world should move independently, but that's just my own opinion. And I'm not too sure how to achieve the planar projections without performing a change of basis. Perhaps some sort of vector subtraction or addition I don't know, also programming object interactions and rotations as well as translations on the fly I'm still not so sure how to achieve. But if I do I'll post it up here rest assured.
__________________
Lo, there do I see my father. 'Lo, there do I see My mother, and my sisters, and my brothers. 'Lo, there do I see The line of my people... Back to the beginning. 'Lo, they do call to me. They bid me take my place among them. In the halls of Valhalla... Where the brave... May live... ...forever.. GrimBB | Mimesis
grimpirate is offline   Reply With Quote
Old Jun 9th, 2006, 5:48 PM   #9
a thing
Unverified User
 
a thing's Avatar
 
Join Date: Aug 2005
Location: none
Posts: 146
Rep Power: 0 a thing is on a distinguished road
Quote:
Originally Posted by big_k105
Well I must be missing something on my server because this is what I get

http://www.kylekonline.com/test/newtest.php
You just need to send it as image/png.
__________________
Warning: My posts may change (dramatically) within the first 15 minutes they're posted.
Got 'Nux?—GNU/Linux and other free software support.
It's GNU/Linux, not just Linux.
a thing is offline   Reply With Quote
Old Jun 11th, 2006, 3:51 AM   #10
Mocker
Hobbyist Programmer
 
Mocker's Avatar
 
Join Date: Oct 2005
Location: Indiana
Posts: 224
Rep Power: 0 Mocker is an unknown quantity at this point
Send a message via AIM to Mocker
Nifty program. I'd suggest this be moved to the Finished Projects section . I might play with your code if you don't mind. It would be neat for it to take a text file as input and then render the object
__________________
#programmingforums relay - http://thegupstudio.com/cgi-bin/pforelay.cgi
freelance scripts - http://ryanguthrie.com/index.html
Mocker is offline   Reply With Quote
Reply

Bookmarks

« Previous Thread in Forum | Next Thread in Forum »

Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Forum Jump




DaniWeb IT Discussion Community
All times are GMT -5. The time now is 12:14 AM.

Powered by vBulletin® Version 3.7.0, Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Copyright ©2007 DaniWeb® LLC