Versatile2DEngine_p02

Here comes the 2nd part of the “How to build a versatile 2D engine in Stage3D” series!

Once again this will be a blind tutorial covering mostly theory and a few lines of code. I call it “blind” because even with VertexBuffer and IndexBuffer objects, it won’t be enough to reveal anything on the screen! Not to worry though, we’ll get there!

Carrying the tradition from the previous post, here’s another quick poll I would like your votes on:

When you run into problems using Stage3D, do you usually ... (can pick up to 2 answers)

View Results

Loading ... Loading ...

What to expect:

Here’s the main points that you can get out of this tutorial:

– Gain a better understanding of what IndexBuffers are;
– Understand how IndexBuffers stores vertices’ indices as a series of triangle points;
– Tell the Context3D object to draw an IndexBuffer’s triangles;
– See how they are instantiated;

What ARE IndexBuffers?

IndexBuffers, also known as IBO (Index Buffer Object), are a resource on the GPU (Graphics Processing Unit) used for specifying a list of indices that refers to the vertices of one (or many) VertexBuffer.

In other words, you can think of an IndexBuffer a little bit like an actual Index of a book.
Instead of words pointing to pages, you have a series of integers pointing to the vertex index they correspond to.

Before we get too deep in the details, here’s a visual overview of where IndexBuffers fits in the puzzle of Stage3D.

VersatileStage3D_p2_ibos_01

Just like VertexBuffer objects, IndexBuffers have to be created from a Context3D object with a given amount of units (in this case, amount of indices), and then populated with integers forming a series of triangles.

How many indices should I prepare?

In the world of 2D, it might be easier to decide the size of your IndexBuffer since you will be primarily rendering Quads (2 triangles glued together). So if you know the maximum amount of Quads you will need in your game, you can multiply that by six (6 = 3 vertices for triangle #1 + 3 more for triangle #2) and have just enough indices to render all of the Quads.

But if you’re interested in having shapes made up from complex triangulation, you may want to pad it a little bit by adding  a few more in case you run out of indices (although ideally – the whole point of creating custom shapes should  use optimized meshes specifically for reducing the number of required indices).

VersatileStage3D_p2_ibos_03

Does each index have to be unique or can I reuse them?

No need to be unique! As you can see in the illustration above, it’s totally acceptable (and encouraged whenever possible) to reuse indices of vertices that are common between triangles. Quads normally always share 2 vertices between the two triangles that composes it. More complex shapes may reuse a lot more or less, all depending on what you’re going for.

About advanced OpenGL Drawing Modes
Although other GPU frameworks can employ different modes for drawing a sequence of triangles, Stage3D only supports one – which OpenGL users may know as:
GL_TRIANGLES.

At this present time, the other two common triangle-drawing modes (GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN) cannot be specified in Stage3D.

If you’re curious on how those works, here’s a few links to read up on OpenGL’s supported modes:
http://www.opengl.org/wiki/Primitives#Triangle_Primitives
http://stackoverflow.com/questions/20394727/gl-triangle-strip-vs-gl-triangle-fan
http://stackoverflow.com/questions/8043923/gl-triangle-fan-explanation/

Do I have to draw my entire IndexBuffer?

Oh, no! Absolutely not necessary.

If you’re using only the first… say… 50 Quads out of a maximum of 100, you can tell the Context3D.drawTriangles(…) method to only draw the first 100 triangles (2 triangles per quads, remember?) and skip the remaining 100.

You can also specify which index to start from. This can be useful if your IndexBuffer is organized in such a way that your game / level is partitioned into consecutive index ranges relating to the same section . That way you could break it down into drawing indices 0-99 for the start of the level, 100-199 once the player moves ahead, 200-299, etc… right up to the ending of your level. This way, at any given time, you only have to draw one range of triangles (or two ranges, in case it overlaps in certain situations).

VersatileStage3D_p2_ibos_04

BUT, that being said…

Unless your engine specializes in a very specific kind of 2D layout (Top-Down, Side-Scroller), it might not be realistic to engineer an IndexBuffer that will simply be read from one index-range to the next.

A more interesting approach might be to dynamically change the IndexBuffer to draw only the visible vertices on screen where your game “camera” is currently located.

Can I update the IndexBuffer on each frame?

Yes! In fact, if you were to implement a “visible” property in your engine’s quads / meshes, you could iterate through each visible objects, populate a Vector of uint numbers (or a ByteArray of Shorts, as we will see in a later article), reupload the indices to the IndexBuffer, and draw the triangles ONLY of those visible objects (as opposed to ALL indices allocated in the index buffer – which might cause messy results!)

Some developers may prefer to offset the invisible objects somewhere off-screen and let some of the hardware optimization take care of not  drawing the triangles, but in my opinion – I would think it makes more sense to tell the GPU exactly and only which indices it has to draw from the list of vertices.

How do you create an IndexBuffer?

Quite simple! First declare it like so:

private var _index:IndexBuffer3D;

Then, you can instantiate it from the Context3D object (if you don’t have one already, see part-1 for details):

var numIndices:int = 3000;
_index = CONTEXT.createIndexBuffer(numIndices);

Combined with the code we’ve seen in Part-1, here’s what the whole thing should look like now:

package  {
	import flash.display.Sprite;
	import flash.display.Stage3D;
	import flash.display3D.Context3D;
	import flash.display3D.IndexBuffer3D;
	import flash.display3D.VertexBuffer3D;
	import flash.events.Event;
	

	/**
	 * A simple class to prepare a Stage3D / Context3D object.
	 * @author Pierre Chamberlain
	 */
	public class Stage3DMain extends Sprite {
		private var _vertex:VertexBuffer3D;
		private var _index:IndexBuffer3D;
		
		
		public static var STAGE3D:Stage3D;
		public static var CONTEXT:Context3D;
		
		public function Stage3DMain() {
			super();
			
			stage ? init() : addEventListener(Event.ADDED_TO_STAGE, init );
		}
		
		private function init(e:Event=null):void {
			e && removeEventListener(Event.ADDED_TO_STAGE, init);
			
			//Pick the first Stage3D object:
			STAGE3D = stage.stage3Ds[0];
			STAGE3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreated );
			STAGE3D.requestContext3D("auto", "baseline");
		}
		
		private function onContextCreated(e:Event):void {
			CONTEXT = STAGE3D.context3D;
			if (!CONTEXT) {
				throw new Error("The context could not be requested.");
			}
			
			CONTEXT.configureBackBuffer(stage.stageWidth, stage.stageHeight, 0 );
			
			prepareGPUResources();
			
			stage.addEventListener(Event.ENTER_FRAME, onRender);
		}
		
		private function prepareGPUResources():void {
			var numVertices:int = 2000;
			var numOfFields:int = 6;
			_vertex = CONTEXT.createVertexBuffer(numVertices, numOfFields);
			
			var numIndices:int = 3000; //Since for every 4 vertices we need 6 indices to make a quad, make it a 2:3 ratio.
			_index = CONTEXT.createIndexBuffer(numIndices);
		}
		
		private function onRender(e:Event):void {
			CONTEXT.clear(0.0, 0.2, 0.8);
			
			// Bind / Draw from GPU resources here...
			CONTEXT.drawTriangles(_index, 0);
			CONTEXT.present();
		}
	}
}

Notice how the onRender(…) method calls the CONTEXT.drawTriangles( _index, 0) method? You would think something would get drawn by now… but NOPE! Still got a few things missing to get this puppy up and running. It certainly isn’t “broken”, but it doesn’t have any data in either the VertexBuffer, nor the IndexBuffer.

Ah, hmm… what to do?

What now?

All this theory thus far might be frustrating; but next time, we’ll take a look at how to upload data to the VertexBuffer and the IndexBuffer. And in Stage3D, there’s two ways of doing that:
1) with Vectors of Numbers and uints, or
2) with ByteArrays of Floats and Shorts.

More information on that later!

‘Til then, see you next time!

Go to previous Part 1: Using VertexBuffers