Partner, Software Development/General Manager at Persuasive Games. In his role, Mr. Quarto is responsible for software development and testing, as well as studio management and strategy.
We are going to start with a basic 3D primitive: the cube. I've choosed a cube, cos basically is so easy to define. Anyway, this simple example is able to render any shape you want, if you take some time for definitions.
If you feel like is a lot for you, please take a look at my blog examples at http://www.alejandroquarto.com
Take a look at the example, here you will use keyboard.
Keys:
LEFT, RIGHT, UP and DOWN arrows to move the cube
INSERT, DEL, HOME, END, PAGE UP and PAGE DOWN to rotate the cube in X, Y and Z axis.
So, thats what you will learn how to do.
You may think: "Ok, but how can i define a complex model?", keep reading the next page.
We will use a simple function to be able to define our cube in 3D.
function generateCube() { ///First we need to define the Vertex, Cube have exacly 8 vertex. points = new Array(); //Each vertex is composed from X Y and Z coordinates in the space points.push({x:-50, y:-50, z:-50}); points.push({x:50, y:-50, z:-50}); points.push({x:50, y:50, z:-50}); points.push({x:-50, y:50, z:-50}); points.push({x:-50, y:-50, z:50}); points.push({x:50, y:-50, z:50}); points.push({x:50, y:50, z:50}); points.push({x:-50, y:50, z:50}); //Then, if we want to draw more than a point, we need to define shapes with those points //Each shape (6) will have 4 vertex, the order is important when we want to use the draw api to fill it. shapes = new Array(); shapes.push([0, 1, 2, 3]); shapes.push([4, 5, 6, 7]); shapes.push([0, 4, 7, 3]); shapes.push([0, 1, 5, 4]); shapes.push([1, 5, 6, 2]); shapes.push([3, 7, 6, 2]); }
Ok, we need to place the observer or camera in some place in our 3D space. In this example i've used a fixed camera, it means we are not going to move the camera. Anyway, we are moving the 3D object.
//Lens distance var fl:Number = 400; //Center of the Stage var vpX:Number = Stage.width/2; var vpY:Number = Stage.height/2; //Render Offsets, here you can move the 3D object var xOffset:Number = 0; var zOffset:Number = fl; var yOffset:Number = 0; //vars used to rotate the cube in the Y and X axis var angleY:Number = 0.01; var angleX:Number = 0.01; var angleZ:Number = 0.01; } //We call the cube function generateCube();
//Simple function to detect the keys pressed by the user. We just change the offsets and angles. function checkKeys() { if (Key.isDown(Key.RIGHT)) { xOffset += 4; } if (Key.isDown(Key.LEFT)) { xOffset -= 4; } if (Key.isDown(Key.UP)) { yOffset -= 4; } if (Key.isDown(Key.DOWN)) { yOffset += 4; } if (Key.isDown(Key.INSERT)) { angleY -= 0.001; } else { if (Key.isDown(Key.DELETEKEY)) { angleY += 0.001; } else { angleY *= 0.95; } } if (Key.isDown(Key.HOME)) { angleX += 0.001; } else { if (Key.isDown(Key.END)) { angleX -= 0.001; } else { angleX *= 0.95; } } if (Key.isDown(Key.PGDN)) { angleZ += 0.001; } else { if (Key.isDown(Key.PGUP)) { angleZ -= 0.001; } else { angleZ *= 0.95; } } }
///Start with the update, right now is just an enterframe this.onEnterFrame = function() { //Cosine and sin calculation from the angles var cosY:Number = Math.cos(angleY); var sinY:Number = Math.sin(angleY); var cosX:Number = Math.cos(angleX); var sinX:Number = Math.sin(angleX); var cosZ:Number = Math.cos(angleZ); var sinZ:Number = Math.sin(angleZ); //We need to clear the stage to be able to draw the new cube on every frame clear(); //This array will store the distance form the camera to each Shape distanceArray = []; //Ok, let's start with each shape, remember each one is made with 4 points. for (var j:Number = 0; j<shapes.length; j++) { //Vars to store the distance from each point in the shape, in each axis. dx = 0; dy = 0; dz = 0; //Now, we need to look inside each shape, obviously, we will find 4 points, so let's take a look to all the points, and make some calculations. for (var i:Number = 0; i<shapes[j].length; i++) { //Here, is where we apply the projections for each point coordinate x1 = cosY*(sinZ*(points[shapes[j][i]].y)+cosZ*(points[shapes[j][i]].x))-sinY*(points[shapes[j][i]].z); y1 = sinX*(cosY*(points[shapes[j][i]].z)+sinY*(sinZ*(points[shapes[j][i]].y)+cosZ*(points[shapes[j][i]].x)))+cosX*(cosZ*(points[shapes[j][i]].y)-sinZ*(points[shapes[j][i]].x)); z1 = cosX*(cosY*(points[shapes[j][i]].z)+sinY*(sinZ*(points[shapes[j][i]].y)+cosZ*(points[shapes[j][i]].x)))-sinX*(cosZ*(points[shapes[j][i]].y)-sinZ*(points[shapes[j][i]].x)); //Now we store the new point coodinates, based on the angles. points[shapes[j][i]].x = x1; points[shapes[j][i]].y = y1; points[shapes[j][i]].z = z1; //we add each point coordinate to the relative distance var dx += x1; dy += y1; dz += z1; //Next code, is the way to get the X and Y points to draw in the Stage, we take care about offsets, and distance from the camera in the Z Axis //Each point will be scaled acording the distance to the camera. We are using the _x and _y as variables to render, not for calculations points[shapes[j][i]]._x = vpX+(points[shapes[j][i]].x+xOffset)*(fl/(points[shapes[j][i]].z+fl+zOffset));//scale; points[shapes[j][i]]._y = vpY+(points[shapes[j][i]].y+yOffset)*(fl/(points[shapes[j][i]].z+fl+zOffset));//scale; } //Ok, you have checked all the points in the shape, now is time too look at the distances //This is important, cos the distance to the observer, will mean which shape is render on top. //Since we have a sum of distances in X Y and Z, we need an average //Next code is the same than dividing by four (cos the four points per shape) dx /= shapes[j].length-1; dy /= shapes[j].length-1; dz /= shapes[j].length-1; //We add an element to the distances, and is basically the distance from each point, to the camera. Note than fl is on the Z axis, and is where the camera is. //We store and object with the distance, and also a shape ID to know which shape corresponds with the distance. distanceArray.push({d:Math.round(Math.sqrt(Math.pow(dx-xOffset, 2)+Math.pow(dy-yOffset, 2)+Math.pow(fl-dz, 2))), index:j}); } //Now, we need to sort the array, to know wich shape is near and need to be render on top. So a simple Sort function. distanceArray.sortOn("d"); //Here is where the fun starts. Now is time to render each polygon lineStyle(0,0xFFFFFF,40); //for each element in the distance array (the same length than the shape array, we will render the shape. for (w=0; w<distanceArray.length; w++) { //Drawing sfuff to do the line and the fill color (this can be better :D) beginFill(0xff-(distanceArray[w].index*10),100); moveTo(points[shapes[distanceArray[w].index][0]]._x,points[shapes[distanceArray[w].index][0]]._y); //Now in each shape, we draw lines to each point to close the shape. for (var j:Number = 0; j<shapes[distanceArray[w].index].length; j++) { lineTo(points[shapes[distanceArray[w].index][j]]._x,points[shapes[distanceArray[w].index][j]]._y); } //Close the shape with the endFill endFill(); } //Capture Keys pressed to move and rotate the cube checkKeys(); //DONE! };