< / >

This is a blog by about coding and web development.

Molehill Spinning Cube

Posted on in

There was quite a meltdown of posts after the release of Molehill yesterday. Unfortunately, most of the posts were either “our huge 3D framework now supports Molehill” or “hey check out this awesome demo I made and no you can’t see the source.” Michael Baczynski posted a great 2D example, though.

Luckily, I’ve done this 3D thing a few times. In the spirit of getting people started, here’s an example of a spinning 3D cube using the new Molehill APIs:

Flash is required to view this content.

Okay, maybe I added some tweening. This is Flash, after all.

You can view the source here:
https://gist.github.com/847106

You’ll also need this guy from Adobe (throw it into a com/adobe/utils folder):
https://gist.github.com/847108

I use the command line Flex SDK on Mac. It took a few steps to get everything up and running so I could build Molehill code. Here’s what you need to do:

  1. Download and install the incubator release of Flash Player 11
  2. Download build 19786 from the Flex SDK Hero stable builds table
  3. Extract the SDK somewhere (e.g. /Developer/SDKs/flex_sdk_4.5)
  4. Create a new folder in the SDK: mkdir /Developer/SDKs/flex_sdk_4.5/frameworks/libs/player/11.0
  5. Download the SWC for Flash Player 11 and move it into the new 11.0 folder you created. Name it playerglobal.swc.
  6. Compile using the bin/mxmlc from this new SDK, and add -swf-version and -target-player to the command line parameters: mxmlc -swf-version=13 -target-player=11.0.0 -o bin/cube.swf src/Cube.as

I’ll highlight a few parts of the code.

_stage = stage.stage3Ds[0];
_stage.addEventListener(Event.CONTEXT3D_CREATE, onContext);
_stage.requestContext3D(Context3DRenderMode.AUTO);
_stage.viewPort = new Rectangle(0, 0, _width, _height);

The first thing you need to do is request a 3D rendering context. The stage will fire a CONTEXT3D_CREATE event once the context is ready for you. You can also define the viewport size you want to use for that context.

_context = _stage.context3D;
_context.configureBackBuffer(_width, _height, 2, true);
_context.enableErrorChecking = true;

Once the context is ready, you need to initialize it. configureBackBuffer allows you to set the pixel width and height of the buffer, the amount of anti-aliasing, and whether or not you want depth and stencil buffers. enableErrorChecking will allow the context to throw errors when you make calls to context.clear() or context.drawTriangles(). This will, however, cause a bit of a performance hit, as it forces these calls to block.

_projection = perspectiveProjection(60, _width / _height, 0.1, 2048);

You’ll need to create a projection matrix to transform your vertexes. I tried to get this working with the old flash.geom.PerspectiveProjection class, but it just would not behave. I ended up rolling my own and life got a lot easier.

This demo uses standard OpenGL axes: * -x / +x = left / right * -y / +y = down / up (opposite what you’re used to in 2D) * -z / +z = far / near

_vertexBuffer = _context.createVertexBuffer(_cubeVertexes.length / 6, 6);
_vertexBuffer.uploadFromVector(_cubeVertexes, 0, _cubeVertexes.length / 6);
_context.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
_context.setVertexBufferAt(1, _vertexBuffer, 3, Context3DVertexBufferFormat.FLOAT_3);

_indexBuffer = _context.createIndexBuffer(_cubeIndexes.length);
_indexBuffer.uploadFromVector(_cubeIndexes, 0, _cubeIndexes.length);

You’ll be using vertex and index buffers for everything in Molehill. The vertex buffer I use in this example has the format (x, y, z, r, g, b). The setVertexBufferAt calls are supplying this data to the vertex program as two separate tuples: va0 = (x, y, z) and va1 = (r, g, b).

The index buffer is created, but isn’t used until later.

var vsAssembler:AGALMiniAssembler = new AGALMiniAssembler;
vsAssembler.assemble(Context3DProgramType.VERTEX, [
  "m44 op, va0, vc0",  // multiply vertex by modelViewProjection
  "mov v0, va1"        // copy the vertex color
].join("\n"));

var fsAssembler:AGALMiniAssembler = new AGALMiniAssembler;
fsAssembler.assemble(Context3DProgramType.FRAGMENT, [
  "mov oc, v0"  // output the fragment color
].join("\n"));

_program = _context.createProgram();
_program.upload(vsAssembler.agalcode, fsAssembler.agalcode);
_context.setProgram(_program);

This code is just setting up some bare bones vertex and fragment shaders. The vertex shader transforms the vertex, and sends the color data down to the fragment shader. The fragment shader just uses the color directly.

Once all the setup is done, there are just a few things to be done each frame.

_context.setProgramConstantsFromMatrix(
  Context3DProgramType.VERTEX, 0, _modelViewProjection, true);

Once I’ve concatenated the modelView and projection matrices, this line makes the matrix available to the vertex shader as a constant (vc0).

_context.clear();
_context.drawTriangles(_indexBuffer);
_context.present();

These three lines are pretty straightforward: clear the screen, draw the triangles specified by the given index buffer, then get it all onto the screen. Note that clear defaults to black, but allows you to supply a color and clear value for the various buffers. drawTriangles also defaults to drawing all triangles, but can optionally take an offset and limit.

blog comments powered by Disqus