Da Fish in Sea

These are the voyages of Captain Observant

Drawing in 3D

| Comments

Last year Seb Lee-Delisle posted about how it would be nice to be able to draw in 3D with the same ease as you can draw in 2D, using the flash drawing API. It inspired me to try something similar, but with a different approach. I was looking into WebGL and OpenGL late last year, and though I got a bit bogged down with my lack of C knowledge, I found I liked the procedural API. The entire scene is actually redrawn every frame. This year I’ve also been thinking about the Turtle graphics system I once used in school, and how it works in a similar way (albeit in 2D). So I took a stab at creating a simple procedural 3D drawing API, along the lines of OpenGL and Logo. First the demo..

flash 10 required

It is currently implemented as a base class which exposes the following methods:

    rotate(angleX, angleY, angleZ)
    draw(x,y,z)
    move(x,y,z)

    pushMatrix()
    popMatrix()

The x, y, z values here are relative to the current position. The current position (and rotation) is maintained by the modelViewMatrix. By calling pushMatrix() you can save the current state and return to it later with popMatrix(). If you have subroutines, it is generally a good idea if they return the modelViewMatrix to the same state. For example in this example the drawSquare() method does this:

private function drawSquare()
{
    draw(100,0,0);
    rotate(0,0,90);
    draw(100,0,0);
    rotate(0,0,90);
    draw(100,0,0);
    rotate(0,0,90);
    draw(100,0,0);
    rotate(0,0,90);//return to starting orientation
}

So this simply draws 4 lines of length 100, rotating 90 degrees around the Z axis. The final rotation isn’t necessary for the drawing, but it ensures that the state is not changed after the routine is called. To draw a cube, we call the drawSquare routine while rotating and moving in 3D.

private function drawCube()
{
    //draw cube
    //back
    drawSquare();

    //left
    pushMatrix();
    rotate(0,90,0);
    drawSquare();
    popMatrix();

    //top
    pushMatrix();
    rotate(-90,0,0);
    drawSquare();
    popMatrix();

    //right
    pushMatrix();
    move(100,0,0);
    rotate(0,90,0);
    drawSquare();
    popMatrix();

    //bottom
    pushMatrix();
    move(0,100,0);
    rotate(-90,0,0);
    drawSquare();
    popMatrix();

    //front 
    pushMatrix();
    move(0,0,-100);
    drawSquare();
    popMatrix();

}

finally, to draw the cubes, we call the drawCube() routine inbetween calls to push/popMatrix() and any changes in position we wish to make.

    pushMatrix();
    drawCube();
    popMatrix();

    pushMatrix();
    move(150,0,0);
    drawCube();
    popMatrix();

    pushMatrix();
    move(-150,0,0);
    drawCube();
    popMatrix();

Once you get the hang of this approach, it is quite intuitive, and reduces the need for calculating a lot of 3D coordinates. This is done behind the scenes in the draw() method, which adds the real 3D coordinates to a list of vertices, which ultimately get projected to 2D coordinates. It’s easy to rotate the objects in the scene by calling rotate() before drawing the objects in the scene.

I’ve used Haxe here because of the faster compile time. There is also an earlier ActionScript version which I’ll link to below.

Da Code

DrawCubes.hx

Draw3D.hx

MatrixStack.as (the AS3 prototype)

GitHub