11.11.2011 15:22
Basics of Stage3D part I: displaying multiple objects [advanced]
Two weeks ago I've wrote about support in Flash Professional for Flash Player 11 while mentioning about using the example of 3D rendering from the documentation ( http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/display3D/Context3D.html#includeExamplesSummary). However using a pre-made example is one thing and understanding it is whole different matter - that is why this time around I will write about Stage3D (as Adobe like to call there 3D acceleration in Flash).
Just in case the whole code from that example (along with additional libraries needed to run it) is available here: Context3DExample.zip In the first part of this article I will write a bit about creating and displaying multiple objects while still allowing for separate transformations on each of them. To make this simpler I suggest making a class for 3D cubes, using the already existing code (from the example in the documentation) responsible for creating geometry: IndexBuffer and VertexBuffer. Such class might look like this:
So, creating a class for cubes is pretty simple, the problems start when we decide to display them. Assuming all those boxes are stored in vBox vector, the render function used in Context3Dexample needs to be changed to this:
This stuff will probably be pretty confusing to people not familiar with 3D graphics, but the basic idea to remember is that every object (cubs in our case) is actually never modified during the runtime. In other words we create our cube, define its triangles and set some colours, but by it self the cube does not have specific position in the system - we use matrices for that. But the thing is matrices also don't do anything special by themselves, until we call the setProgramConstantsFromMatrix function to combine them. Note however this works somewhat like a filter - "Program" (external code used for rendering process) takes the geometry and passes it through the matrix, displaying results to the screen while not modifying any variables.
Now as for the finalTransform - we start each animation frame by resetting this matrix (identity function) to make sure it contains absolutely no transformations. This is very important for append operations, because existing transformations affect the results, so for example a translation by 10 units to the left followed by a 90 degrees turn will place an object in different spot than by starting with 90 degrees turn and then following it with a translation to the left by 10 units. Just like in the real world the direction we will take on "move to the left" command will be different depending on the side we are already facing. In any case it is best to experiment with this, while remembering that order of matrices addition is extremely important and that is why finalTransform is created by appending other matrices in this particular order: world, view (camera) and projection (defines how we see the world). As usual the whole code is packed and ready to download from here: Tutorial3D.zip
Just in case the whole code from that example (along with additional libraries needed to run it) is available here: Context3DExample.zip In the first part of this article I will write a bit about creating and displaying multiple objects while still allowing for separate transformations on each of them. To make this simpler I suggest making a class for 3D cubes, using the already existing code (from the example in the documentation) responsible for creating geometry: IndexBuffer and VertexBuffer. Such class might look like this:
private var vbBuffer:VertexBuffer3D; private var ibIndex:IndexBuffer3D; private var m3D:Matrix3D; private var nCount:int; public function Box3D(context:Context3D) { var triangles:Vector.<uint> = Vector.<uint>([...]); ibIndex = context.createIndexBuffer(triangles.length); ibIndex.uploadFromVector(triangles, 0, triangles.length); const dataPerVertex:int = 6; var vertexData:Vector.<Number> = Vector.<Number>([...]); vbBuffer = context.createVertexBuffer(vertexData.length / dataPerVertex, dataPerVertex); vbBuffer.uploadFromVector(vertexData, 0, vertexData.length / dataPerVertex); m3D = new Matrix3D(); nCount = 12; } public function render(context:Context3D):void { context.setVertexBufferAt( 0, vbBuffer, 0, Context3DVertexBufferFormat.FLOAT_3 ); context.setVertexBufferAt( 1, vbBuffer, 3, Context3DVertexBufferFormat.FLOAT_3 ); context.drawTriangles(ibIndex, 0, nCount); } public function getMatrix3D():Matrix3D {return m3D;}Nothing especially complicated here - just remember that m3D matrix will hold the transformation (position, scale) so it is best to build the cube around 0,0,0 coordinates and then later move it with appendTranslation function. Also, one more thing worth pointing out is that setVertexBufferAt is used to set which VertexBuffer will be used to draw triangles (drawTriangles function) and it is called twice, because first it points out the vertexes and next the colours.
So, creating a class for cubes is pretty simple, the problems start when we decide to display them. Assuming all those boxes are stored in vBox vector, the render function used in Context3Dexample needs to be changed to this:
private function render(event:Event):void { renderContext.clear(.3, .3, .3); for each(var box:Box3D in vBox) { finalTransform.identity(); finalTransform.append(box.getMatrix3D()); finalTransform.append(world); finalTransform.append(view); finalTransform.append(projection); renderContext.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, finalTransform, true); box.render(renderContext); } renderContext.present(); }Here it gets a bit more interesting thanks to the two most important things: setProgramConstantsFromMatrix function and finalTransform matrix construction. With setProgramConstantsFromMatrix function we set the matrix (transformations) for the rendered object and it cannot be skipped (or at least it won't make any sense to do this, since you won't be able to see anything on the screen), while finalTransform will be used to create that matrix.
This stuff will probably be pretty confusing to people not familiar with 3D graphics, but the basic idea to remember is that every object (cubs in our case) is actually never modified during the runtime. In other words we create our cube, define its triangles and set some colours, but by it self the cube does not have specific position in the system - we use matrices for that. But the thing is matrices also don't do anything special by themselves, until we call the setProgramConstantsFromMatrix function to combine them. Note however this works somewhat like a filter - "Program" (external code used for rendering process) takes the geometry and passes it through the matrix, displaying results to the screen while not modifying any variables.
Now as for the finalTransform - we start each animation frame by resetting this matrix (identity function) to make sure it contains absolutely no transformations. This is very important for append operations, because existing transformations affect the results, so for example a translation by 10 units to the left followed by a 90 degrees turn will place an object in different spot than by starting with 90 degrees turn and then following it with a translation to the left by 10 units. Just like in the real world the direction we will take on "move to the left" command will be different depending on the side we are already facing. In any case it is best to experiment with this, while remembering that order of matrices addition is extremely important and that is why finalTransform is created by appending other matrices in this particular order: world, view (camera) and projection (defines how we see the world). As usual the whole code is packed and ready to download from here: Tutorial3D.zip