I have an array of simple 2D objects, mostly made up of two triangles.
Although the objects are quite simple, each one is drawn using stencil operations
so every object will require it's own drawTriangles()
call.
What is the best way to store and handle these objects and their vertex and index buffers?
I can think of a few different ways of doing it :
At start up create one large vertex and index buffer and add each object to it, eg :
public function initialize():void{ for each(object in objectList){ vertexData.push(object.vertices); indexData.push(triangle1, triangle2); } indices = context3D.createIndexBuffer( numTriangles * 3 ); vertices = context3D.createVertexBuffer( numVertices, dataPerVertex ); indices.uploadFromVector( indexData, 0, numIndices ); vertices.uploadFromVector( vertexData, 0, numVertices ); }
During rendering loop through all objects, check which ones are visible on screen and update their vertices. Then re-upload the entire vertex buffer. Afterwards loop through the visible objects and draw each object's triangles with individual calls to
drawTriangles()
public function onRender():void{ for each(object in objectList){ if(object.visibleOnScreen) object.updateVertexData(); } vertices.uploadFromVector( vertexData, 0, numVertices ); for each(object in objectsOnScreen){ drawTriangles(indices, object.vertexDataOffset, object.numTriangles); } }
You could also do something similar to number 1 except re-upload each object's vertex data only when needed :
public function onRender():void{ for each(object in objectList){ if(object.visibleOnScreen){ if(object.hasChanged) vertices.uploadFromVector( vertexData, object.vertexDataOffset, object.numTriangles ); drawTriangles(indices, object.vertexDataOffset, object.numTriangles); } } }
You could also create a new vertex buffer consisting of only visible objects on each frame :
public function onRender():void{ for each(object in objectList){ if(object.visibleOnScreen){ vertexData.push(object.vertices); indexData.push(triangle1, triangle2); } } indices = context3D.createIndexBuffer( numTriangles * 3 ); vertices = context3D.createVertexBuffer( numVertices, dataPerVertex ); indices.uploadFromVector( indexData, 0, numIndices ); vertices.uploadFromVector( vertexData, 0, numVertices ); for each(object in objectsOnScreen){ drawTriangles(indices, object.vertexDataOffset, object.numTriangles); } }
Another option is to create individual vertex buffers for each object :
public function initialize():void{ for each(object in objectList){ object.indices = context3D.createIndexBuffer( object.numTriangles * 3 ); object.vertices = context3D.createVertexBuffer( object.numVertices, dataPerVertex ); } } public function onRender():void{ for each(object in objectList){ if(object.visibleOnScreen){ if(object.hasChanged) object.vertices.uploadFromVector( object.vertexData, 0, object.numTriangles ); drawTriangles(indices, 0, object.numTriangles); } } }
I can imagine that option 1 will be slow because the entire vertex buffer needs to be uploaded each frame.
Option 2 has the advantage that it only needs to to upload vertex data that has changed but might suffer from multiple calls to uploadFromVector
.
UPDATE
I've had a look at the Starling framework source, specifically the QuadBatch
class :
http://gamua.com/starling/
https://github.com/PrimaryFeather/Starling-Framework
It seems that all vertices are manually transformed before hand and the the entire vertex buffer is rebuilt and uploaded every frame.