ActionScript.org Flash, Flex and ActionScript Resources - http://www.actionscript.org/resources
Simple 3D programming for AS2.0
http://www.actionscript.org/resources/articles/717/1/Simple-3D-programming-for-AS20/Page1.html
Alejandro Quarto
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.
Prior to joining to Persuasive Games, Mr. Quarto started his career in interactive entertainment as a freelance programmer using different object-oriented programming tools specifically specializing in Macromedia Flash, Shockwave, and Java development platforms among others. In 2002 Mr. Quarto co-founded Platico Games! in Buenos Aires, Argentina. While at Platico Games Studio, Mr. Quarto produced a wide variety of video game projects including: advergames, educational games, and multiplayer games oriented to big communities.
These games have been developed for clients such as: Claxson Interactive Group, Budweiser, Mattel, Turbo Video Brazil, Canal 13 (TV) Argentina, and FM HIT in Chile. 
Quarto is currently teaching about Flash development, in an institute located in Buenos Aires. In his role as instructor, he teach how to develop webs and games, for online content. Personal Blog: http://www.alejandroquarto.com

 
By Alejandro Quarto
Published on December 28, 2007
 
Tired of using 3D renders without understanding what is really happen? Well, this is a good start to be able to improve your skills in 3d.
The aim of this tutorial, is to introduce a simple technique for a basic 3D render in Flash.
Even if you need to know some math, basically sin and cosine, the example is really short and clear.
Requirements:
   Some experience with AS2.0 or  1.0
   Flash 8 at least.  
   You should like math a bit, else it can be frustrating.
   

Our goal


    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.
  


Model definition

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]); }

     Basically we use vertex and shapes. Each shape is made with 4 vertex in this example. We are going to use two arrays, one for vertex, and other for shapes.
     Points array, who store vertex, have an object in each position with 3 properties: x, y and z. The numbers in this properties are basically pixels, where the vertex will be in the screen.
    Shapes array, have a reference to each vertex array position. With 4 vertex position we have a closed shape.

    This seems easy, but you may think is a sort of pain to write all this stuff. Ok, you are right, there is another way.
   
     If you have user PaperVision3D, maybe you know what COLLADA is. Take a look at http://collada.org/mediawiki/index.php/Main_Page if you want. Basically is an easy way to define 3D models in a XML basis. Maybe is not the best explanation. Anyway main idea with this 3D format is that you can get the model definition from this files, and build this files easyly with 3D studio or a similar software. The file usually includes vertex position, shapes definition using vertex, and texture mapping.
   If you want, you can always replace this function with a Collada file.

   Let start with our 3D space.

Fixed camera and initial definitions

    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();

    This is our first code, don't forget to write the generateCube function.
   
    Camera is placed in the space using just the z axis, cos we asume is at x=0 and y=0. At the Z axis we use the var fl. You can change it to see the differences.

   Our model will be placed in the middle of the screen defined with vpX and vpY vars.

    Since we want to move our cube, we also need to declare the axis offset, and we use xOffset, yOffset and zOffset. For cube rotation, we also have the angleX, angleY and angleZ vars. I've used 0.01, to get an initial rotation in the 3 axis when the example loads.

Detecting keys
This is a very simple function and is really for a test purpose. Feel free to change it.


//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; } } }

The only thing this function does, is to change the angle and offset vars we declared in the previous page. Im also adding a bit of "inertia".

Next section is the render function.

How to render the cube
Firsr of all, take a look to the render function, is commented. Then i will try to explain what it does.

///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! };

   That's all the code we are going to use.
   Since we are going to move the cube, we need to refresh that stage a couple of times per sec. I've used 120Fps, but you may want to use a setInterval instead.
   First, i will save the sine and cosine values for each cube angle in each axis. This is basically to avoid a lot of writing later.
   Next line "clear()" is to clear the stage, cos we will use the flash drawing api. Every update, we clear before drawing.
   The nex line is an array declaration, i will explain more on this later, but basically we want to know the distance from the observer to a each shape.
   First loop start, we will follow each shape, and we will intialize 3 vars used to store a distance from each shape to the observer, we use 3 cos we need to know the 3 axis distance first.
   Then, is time to see what is inside the shape, for us: points. Here is where the important calculation comes. We usually have a point defined in 2D in flash, but now we need to use projections to determine how to show a 3D object in a 2D space.
   For our goal, we have the math needed, and is basically matrix transformations. I have to be honest and say that all i did is to follow the formula in this link http://en.wikipedia.org/wiki/3D_projection
   By using the formula, we get x1, y1 and z1 which are our 3d projection of each point.
   Then we apply those temporal vars to our point.
   Next lines are the sum of our coordinates to our distance vars that we are going to use later.
   If you take a look to the next 2 lines, you will see is very similar to the ones at wikipedia link. We have to take care from the cube offsets and also camera position. By using the formula, we have our 2d point that represents a 3d point.
   Ok, we have all the points updated, loops ends. Now we need to know the average distance from each point to the camera, and that's what the next lines do. Calculate the average, then push it in our array.

   It seems we are done, but at the moment we can just draw squares. Question is, which is in front? Cos that, we have an array made with the distance of each shape to the observer and a shape ID.
   Now, all we need to do is to sort this array by the distance and finally draw it in the screen. When the distance is greater, we draw first, when is closer, later.
   Last loop, is the real render. Follow all the distance array, then look at the shape and draw an square between the 4 points.
  You have rendered a cube, last thing is to detect when the user press a key, and that's why we call the function we alredy made.
 
  That's all!

Notes and comments
   This tutorial and example, is basically to start looking at the 3D world inside flash. Is possible not the best way to do it, cos it uses a lot of CPU. Anyway, you can always improve the main idea.
  
    Main features to implement is to render bitmaps textures instead of vector shapes, cos in this way you can get realistic results like papervision3d
    Another issue is the COLLADA import, to avoid model definition inside the flash code.

   If you have doubts you can always contact me at my blog: www.alejandroquarto.com or just leave a comment.