• home
  • forum
  • my
  • kt
  • download
  • home / 2D Graphics / Flash / 3D

    3D Engine for Dummies

    Author: 2007-06-05 15:28:36 From:

    Introduction

    The purpose of this tutorial is to teach you the fine art of making a Flash 3D-engine. What this means is that we are going to find out how to take a set of 3D coordinates and transform them into 2D coordinates.

    Limitations

    My engine has a few limitations, some because this is a simpler "tutorial version", some because I was too lazy to implement the functions, some because the functions were to processor-consuming and last but not least those that I couldn't figure out:

    • no shadows
    • no solid faces
    • no curbs
    • etc.

    This tutorial is definetely advanced so most of the time I won't include any images of the program since you probably know it perfectly. I've included one thing: an actionscript summary at the very end of the tutorial, for quick reference. Newbies, please stop reading now...
    The last thing I would like to say is that making a functional 3D engine is a kind of test, and you'll feel much prouder having figured it out all by yourselves. So if you want to give it a try, just check out the math link.

    AND PLEASE RATE THIS TUTORIAL

    (a good rating, hopefully)

    View Plane

    As I've already said, we want to transform a set of 3D points into a set of 2D points. To do this we must somehow eliminate the third coordinate, the depth (z)to only have the x and the y left. However we cannot simply discard the depth information because we would lose the perspective. To keep the perspective, we must chose a point of view, the eye of the observer, and a view plane on which all the points are drawn. then for each point we trace the line between the eye and the point. We then calculate the coordinates of the intersection of the line and the plane: these are the 2D-coordinates of the point. Is this a bit complicated? Don't worry, I've made a picture:


    To make the calculations a bit simpler (and the 3D engine faster) I've chosen to put the eye on the z-axis (eyex=0 and eyey=0). The view plane is parallel to the xy plane, that way every point on the plane has a fixed z and the 2D coordinates of each point are the x and y of the corresponding point on the view plane.
    The closer the view plane is to the object, the bigger it will appear. To change the zoom, we vary the position of the view-plane.

    If you still haven't understood, I'm probably not such a good teacher and I can't help you anymore. However if you still want your engine you can just follow my step by step guideline and it will work. You should also take a look at the math page I link to here.


    The Basic Movie

    All right,let's start.
    We'll begin by making our basic bricks, the framework for our tutorial. This part will be as short as possible since I suppose that if you are trying this tutorial, you already feel quite at ease with flash. If not, I suggest you study the Getting Started tutorials first.
    These are the things we need:

    Start by creating a new movie approximately 400*500 in size.
    Then create 3 layers: actions, buttons, and lines. Create 4 frames on the two last layers and insert ten blank key-frames on the actions layer.
    Now name these blank key frames in this order:

    • "model", for loading the points and lines
    • "start", for initiating all variables, etc.
    • "loop", for the basic program loop
    • "loop2"
    • "draw", for the drawing routine
    • "rotx", for the rotation around the x-axis
    • "roty", guess
    • "rotz", same again
    • "zoom", for calculating the zoom
    • "drawline", the line-drawing routine

    Now we have the frame of our movie. Note that I am not making a preloader, you can do that all by yourselves!

    Buttons, Line & Stuff

    The buttons

    Now just create a simple button. It's only for the rotations and the zoom so you don't even need any second states. Now then again if you want to make it fancy, go ahead, no one will stop you.
    Place 8 instances of this button on the buttons layer, towards the bottom of the movie. We'll define their actions later.

    The line

    This part is a bit more important since it determines whether your lines will be drawn correctly or not. Create a new movie clip and name it baseline. In it, draw a simple black line directed from the top left to the bottom right. Then select this line and set its length to 100px and its width to 100px.
    Now you should have a line that's oriented 45¡ã downwards. Also, the good thing about it is that it's lenght in pixels is equal to its length in percent, so you can easily set its size with SetProperty.
    Now place one instance of this line on the lines layer. Name it baseline.

    The extra stuff
    Just add some simple stuff such as a title, your name and preferably mine too (or some kind of a small note saying "original idea by KStor, but now my much better version" etc.)
    Also add an editable text-field. Name the corresponding variable zoomvalue. This will be used to display the value of the zoom.

    The Math

    This part of the tute is purely theoretical. It's a very simple explanation of the math I use (actually you'll notice it's not an explanation at all but just a couple of formulas)You don't have to read it if you don't care about how the tute works, but it's kind of interesting to know. On the other hand, if you want to know more, click here.
    The math we use is coordinates geometry in space. Let's start out with some definitions.

    Vector
    A vector is an abstract kind of notion (that's an easy way of saying I don't know what it is) but very practical. It basically consists of a line drawn from the origin (0,0,0) to a point (x,y,z). It can also be thought of as a direction from one point to another: x units along the x-axis, y units along the y-axis and z units along the z-axis. The vector is defined by the three coordinates x, y and z.

    Line equation
    A line can be described as all the points aligned with two points A (0x,0y,0z) and B. That way, it is defined by its origin A and the vector (dx,dy,dz) going from A to B. The equation looks like this:
    point = org + k * dir
    or with the coordinates (x,y,z) = (0x,0y,0z) + k * (dx,dy,dz) ,k being any real number.

    Plane equation
    A plane is a surface that can be described by a normal vector (dx,dy,dz), ie a direction the plane is facinng, and a value k, indicating the position of the plane along this vector. The equation is:
    point * normal = k
    or with the coordinates (x,y,z) * (dx,dy,dz) = k, k being any real number.

    Intersection of a plane and a line
    The line equation looks like this: point = org + u * dir. We have to find the value of u corresponding to the intersection. Using the plane equation, we find
    u = (k - org * normal) / (dir * normal)
    We then plug this into the line equation and find the coordinates of the point.

    The rotations
    For the rotations we just apply the following formulas:
    Rotatation about the x axis:
    x' = x
    y' = (cos ¨¦ * y) - (sin ¨¦ * z)
    z' = (sin ¨¦ * y) + (cos ¨¦ * z)
    Rotation about the y axis:
    x' = (cos ¨¦ * x) + (sin ¨¦ * z)
    y' = y
    z' = -(sin ¨¦ * x) + (cos ¨¦ * z)
    Rotation about the z axis:
    x' = (cos ¨¦ * x) - (sin ¨¦ * y)
    y' = (sin ¨¦ * x) + (cos ¨¦ * y)
    z' = z
    ...¨¦ being the angle of the rotation.

    The Model

    The model is the way our 3D object is coded, the way we define it mathematically. As I said in the introduction my 3D-engine is used mainly for lines, ie two connected points. Therefore, my model consists of two main parts: the coordinates of the points and the lines to be drawn. Added to this are the eyez, the maximum zoom value and the original zoom.

    The points
    The points are coded in one long variable called points. They are coded this way:

    "x1,y1,z1Rx2,y2,z2Rx3...Rxn,yn,zn"


    xn, yn and zn are the x, y and z coordinates of point number "n". They contain three digits, so 50 will be coded "050", 6 will be coded "006", etc.
    Each point is separated by an "R" to make editing easier.
    You must also create a variable named totalpoints that contains the total number n of points.

    The lines
    The lines are defined by the numbers of their extremeties. For example the line from point 7 to point 11 will be coded "0711". They are stored in a large variable named line:

    "p1p'1Rp2p'2Rp3...Rpmp'm"


    pm is one extremity of the line number m, p'm is the second extremity. Each point is coded by two digits, so point number 2 will be coded "02" and point 11 will become "11". You'll notice that as a result, my engine only accepts 99 points. If you need more, it's easy to change. However too many points will slow it down quite a bit (I haven't tried).
    You must also create a variable named totallines that contains the total number m of lines.

    The Model (con't)

    The other variables
    To this we have to add three extra variables that define the general way the object appears.
    First is the eyez, the z coordinate of the point of view. It should be negative. The further it is from the object, the less perspective there will be. In my example I set it to -500, my closest point being at -99. It's a pretty good value.
    Second is the maxzoom. This value is the z position of the view plane when the zoom is 100 percent. Be careful so that this value always is bigger than the eyez but smaller than the minimum possible z coordinate for any of your points after a rotation. For example when I rotate my point at z=-99, it will get closer to z=-140, so my maxzoom has to be lower than say 150.
    Third is the initial value of the zoom, set between 5 percent and 100. the variable is called zoom.

    The cube
    These are all the variables you need to set. Now I'm going to give you the variables corresponding to a cube. Open the first key-frame of the actions layer. Then set this:

    Set Variable: "totalpoints" = "8"
    Set Variable: "points" = "099,099R099,099,099R-99,099,-99R
    099,099,-99R-99,-99,099R099,-99,099R-99,-99,-99R099,-99,-99R-99" Set Variable: "totallines" = "12" Set Variable: "lines" = "0102R0103R0105R0804R0806R0807R0506R
    0507R0307R0304R0206R0204" Set Variable: "eyez" = "-500" Set Variable: "maxzoom" = "-200" Set Variable: "zoom" = "75"

    Now your model is set up and we can start building the engine.

    The Routine

    Now comes the real part: the engine program itself.

    The loading setup
    First we are going to create the initiation routine, the one that starts it all out. Open the second key-frame of the actions layer. This is what we do:

    • move the baseline instance so that no one can see it on the picture.
      Set Property ("/baseline", X Position) = "1000"
    • then let's cut the points variable up into lots of separate coordinate variables:
      Set Variable: "n" = "1"
      Loop While (n <= totalpoints)
       Set Variable: "x"&n = Substring (points,( (n-1) * 12 + 1), 3 )
       Set Variable: "y"&n = Substring (points, ((n-1 ) * 12 + 5), 3 )
       Set Variable: "z"&n = Substring (points, ((n-1) * 12 + 9), 3 )
       Set Variable: "n" = n+1
      End Loop
      This places the x-coordinate of point 1 in variable x1, the y-coordinate in y1, the z in z1 and the same thing for all other points.
    • now we do the same thing for the lines variable:
      Set Variable: "n" = "1"
      Loop While (n <=totallines)
       Set Variable: "pt1"&n = Substring (lines, (n-1)*5+1, 2 )
       Set Variable: "pt2"&n = Substring (lines, (n-1)*5+3, 2 )
       Set Variable: "n" = n+1
      End Loop
      The first end of point 1 is put in pt11, the second one in pt21, etc.
    • set the center of the display. These are the coordinates of point (0,0) in the movie.
      Set Variable: "centerx" = "190"
      Set Variable: "centery" = "215"

    That's all for the initial frame

    The Routine (con't)

    The loop
    Next we are just going to set up the basic loop. This will be used later for the zoom and the rotations. Open the 4th frame loop2 and enter

    Go to and Play ("loop")

    The zoom routine (part 1)
    We now have to make a basic zoom routine to transform the zoom value in percent into the z-coordinate of the view plane. We'll make the zoom buttons in a while. They way we do our conversion is to take a percent of the distance between the eyez (minimal zoom position) and the maxzoom position, and add the maxzoom. This way the z of the view plane takes values between eyez and maxzoom when the zoom takes values between 0 and 100 percent. Open the next to last keyframe zoom and enter this:

    Set Variable: "depth" = - zoom * (eyez -maxzoom) / 100 + eyez
    Call ("draw")

    The depth variable contains the z-coordinate of the zoom plane. The draw command is the central routine of our engine, we'll build it soon.
    Also add this command to set the zoomvalue variable so the zoom shows up.

    Set Variable: "zoomvalue" = "zoom= " & zoom & "%"

    The drawline routine
    This is the last thing we have to do before the drawing routine it self. This routine will be used to draw the line linking two points. The way it works is that the baseline is duplicated and its size and position are adjusted to correspond to the points (it works, that's all that counts).

    Set Variable: "pt1" = eval("pt1"&n)
    Set Variable: "pt2" = eval("pt2"&n)
    Set Property ("line"&n, X Position) = eval("2Dx"&pt1)
    Set Property ("line"&n, Y Position) = eval("2Dy"&pt1)
    Set Property ("line"&n, X Scale) = eval("2Dx"&pt2) - eval("2Dx"&pt1)
    Set Property ("line"&n, Y Scale) = eval("2Dy"&pt2) - eval("2Dy"&pt1)

    If the number of the line is stored in variable n, the line number n is drawn.

    The Routine (con't)

    The drawing routine
    This is the very core of the engine: the program that converts the 3D coordinates to 2D coordinates (as explained in The Math). In the fifth frame, draw, enter this:

    • Set Variable: "normalz" = depth * depth
      Normalz is the value k in the plane equation, depth being the z-coordinate of the normal vector of the view-plane (the plane being parallel to the xy plane, the vectors coordinates are (0,0,depth))
    • Set Variable: "n" = "1"
      Loop While (n<=totalpoints)
       Set Variable: "u" = (depth - eyez)/(eval("z"&n) - eyez)
       If (n<=9)
       Set Variable: "2Dx0"&n =  u * eval("x"&n) + centerx
       Set Variable: "2Dy0"&n =  u * eval("y"&n) + centery
       Else
       Set Variable: "2Dx"&n =  u * eval("x"&n) + centerx
       Set Variable: "2Dy"&n =  u * eval("y"&n) + centery
       End If
       Set Variable: "n" = n + 1
      End Loop
      This part is a bit more complicated. We actually convert the 3D coordinates to 2D coordinates. For each point, we first calculate the value u as explained in The Math. Then we recalculate the 2D coordinates by multiplying the 3d coordinates by u. I can't explain exactly what I've done here but if you study the math link I gave you should be able to understand it.
    • Set Variable: "n" = "1"
      Loop While (n<=totallines)
       Duplicate Movie Clip ("/baseline", "line" & n, n)
       Call ("drawline")
       Set Variable: "n" = n + 1
      End Loop
      Finally we just draw the the lines by duplicating the basic movie clip and calling the drawline frame.

    Ok, now your 3D engine is basically finished. But we also want a zoom and rotations. That's easy, follow the guide!

    The Zoom

    Let's work out the zoom. We really don't have very much to do, since most of it has alreday been done. Each time the main loop is looped it adds a certain value to the zoom (or substracts). When you roll over the zoom buttons, they change the value added from 0 to a positive or negative value (zoom in or out). When you roll out they reset it to 0.

    First open the start frame(n¡ã2) and add this code just after the setting of the xcenter and the ycenter.

    Set Variable: "zm" = "0"

    This initiates the zm variable, which indicates by how much the zoom should be changed.

    Now open the loop layer and add this:

    Call ("zoom")
    Call ("draw")

    That way the zoom routine is called every time the engine loops. The draw routine is also called to refresh.

    The Zoom (con't)

    Let's continue the zoom routine. Open the next to last frame zoom. Normally we only have two commands: the calculation of the depth and the setting of the zoomvalue. Before this we are going to add a new command:

    Set Variable: "zoom" = zoom + zm
    If (zoom> 100)
     Set Variable: "zoom" = 100
    End If
    If (zoom <10)
     Set Variable: "zoom" = 10
    End If

    First we add the zm value to the zoom, then we check that it doesn't exceed 100% or go under 10% (under ten percent the object gets too small).

    Last we set the buttons: double-click on your zoom-in button (any one of your eight buttons). Then set these actions:

    On (Roll Over)
     Set Variable: "zm" = "5"
    End On
    On (Release, Roll Out)
     Set Variable: "zm" = "0"
    End On

    I don't think this needs to be explained: while the mouse is over the button, the zoom increases by 5 percent at every loop. Now set the same thing for the zoom-out button but with "zm"="-5"

    The Rotations

    This is the last thing we have to do to have our functional 3D engine. The rotation works in a similar way to the zoom. We set two basic basesin and basecos variables. These contain the values of sin 10¡ã and cos 10¡ã. Then while one of the rotation buttons is rolled over the coordinates of all points are rotated around the corresponding axis by 10¡ã at each loop. The rotation frames work with a sin variable and a cos variable, so the zoombuttons only load values in sin and cos and set an axis variable to x, y or z depending on the axis that should be rotated around.

    First we modify the start frame. After all the rest we first set the basesin and basecos:

    Set Variable: "basecos" = 0.9848
    Set Variable: "basesin" = 0.1736

    Then we call an initial rotation around the y-axis and the x-axis:

    Set Variable: "sin" = basesin
    Set Variable: "cos" = basecos
    Call ("rotx")
    Call ("roty")

    Now we change the loop frame. Add this:

    Call ("rot"&axis)

    This way the rotation is called at every loop. Notice that if axis=none then there will be no rotation since there is no rotnone frame.

    The Rotations (con't)

    Now we are going to do the buttons:

    • open your first button (say the up-rotation around the x-axis). Enter this
      On (Roll Over)
       Set Variable: "sin" = -  basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "x"
      End On
      On (Release, Roll Out)
       Set Variable: "axis" = "none"
      End On
      The sign of the sine determines which direction the object turns. When you roll over the button, the axis turned around is set to x, and the sin and cos are stored. When you roll out, the axis is set to none. The principle is the same for all the other buttons, but with different values.
    • x-axis down rotation
            Set Variable: "sin" =  basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "x"
    • y-axis right-rotation
            Set Variable: "sin" = - basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "y"
    • y-axis left-rotation
            Set Variable: "sin" = - basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "y"
    • z-axis rotation(1)
            Set Variable: "sin" = - basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "z"
    • z-axis rotation(2)
            Set Variable: "sin" = basesin
       Set Variable: "cos" = basecos
       Set Variable: "axis" = "z"

    The last thing we have to do to make our rotation functional is obviously to program the rot frames. This is a simple use of the math-formulas. Open the rotx frame and enter this

    Set Variable: "n" = "1"
    Loop While (n<=totalpoints)
     Set Variable: "tmp" = (sin * eval("y"&n)) + ( cos * eval("z"&n))
     Set Variable: "y"&n = cos * eval("y"&n) - sin * eval("z"&n)
     Set Variable: "z"&n = tmp
     Set Variable: "n" = n + 1
    End Loop

    If you check this out it corresponds to the formulas in The Math. Now do the same for the roty frame:

    Set Variable: "n" = "1"
    Loop While (n<=totalpoints)
     Set Variable: "tmp" = - (sin * eval("x"&n)) + ( cos * eval("z"&n))
     Set Variable: "x"&n = cos * eval("x"&n) + sin * eval("z"&n)
     Set Variable: "z"&n = tmp
     Set Variable: "n" = n + 1
    End Loop

    and for the rotz frame:

    Set Variable: "n" = "1"
    Loop While (n<=totalpoints)
     Set Variable: "tmp" = (sin * eval("x"&n)) + ( cos * eval("y"&n))
     Set Variable: "x"&n = cos * eval("x"&n) - sin * eval("y"&n)
     Set Variable: "y"&n = tmp
     Set Variable: "n" = n + 1
    End Loop

    That's all, your engine is finished!

    Finished

    OK, here it is. After hard work the engine is finally finished. You might not have understood everything, the math isn't really easy and I didn't explain very much. However if you study the math link you should be able to understand it pretty easily. To finish off here's some extra stuff I want to tell you:

    Actionscript summary

    Model standard
    This little paragraph is to deal about other 3D engines. I've seen a few, only two which draw lines and of the same level as mine. Their coding of points is equal or very similar to mine. Their lines were coded without the separating R to distinguish lines. This is both inconsistent with the points coding and not very practical. That's why I used my coding, and I suggest that it could be used as a preliminary standard for Flash 3D engines. Please mail me to say what you think about it: KStor.

    What's Next?

    Here are some suggestions about what you could add to the engine.

    • a different rotation system, not working with independent buttons but with a single circle. The object rotates in a different direction depending on the position of the mouse and at varying speeds. By the way you don't really need a slow sine and cosine function, use only the square root function... It can all be done, I did it.
    • z-sorting, not too hard.
    • shadows
      These are pretty simple to make: place a light at any position, then calculate the intersection of the line light-point and the "floor plane" parallel to the xz plane, for each point. Then redraw each line with the new coordinates.
      You can also make an outdoor light by chosing not an origin of the light but it's direction (a vector). Then calculate parallel lines through each point and calculate the intersection with the floor. Easy! I'll let you figure out the math formulas all by yourselves (if you don't manage, mail me)
      An extra thing would be different light intensities: the stronger the light, the darker the shadow (set alpha...); and why not light colours.
    • full faces
      If ever you manage this, MAIL ME!!!!!!!!!!

    One last thing,
    PLEASE RATE THIS TUTORIAL (highly)!



    discuss this topic to forum

    relation tutorial

    No information

    Category

      3D (36)
      Math Physics (18)
      3rd Party (10)
      Navigation (70)
      Actionscripting (228)
      Optimization (17)
      Animation (166)
      Projector (11)
      Audio (54)
      Special Effects (170)
      Backend (26)
      Text Effects (92)
      Drawing (34)
      Tips and Techniques (58)
      Dynamic Content (38)
      Tricks (8)
      Games (114)
      Utilities (24)
      Getting Started (99)
      Video (59)
      Interactivity (48)
      Web Design (37)

    New

    Hot