Stage3D Vertex Buffer formats

If you played with the Stage3D spinning cube sample I linked in the previous post, you will notice one thing: the vertex colors in the vertex buffer are given as 3 floats (red, green and blue). This is rather wasteful in most cases because color components often vary between 0.0 and 1.0, and it’s more efficient to specify vertex colors (including alpha) as a single 32-bit value. For example, in hexadecimal notation, 0xFF800040 would mean alpha of 1.0 (0xFF), blue 0.5 (0x80), green 0 (0x00) and red 0.25 (0x40).

If you look in the Stage3D docs, you will find the constant Context3DVertexBufferFormat.BYTES_4 to use in the call to Context3D.setVertexBufferAt(). But if you naively just change the vertex buffer array to turn each vertex color’s set of 3 components into a single hex value, make dataPerVertex = 4 instead of 6, and use BYTES_4 for va1, then you will find that the colors are coming out wrong. Why? Because the function VertexBuffer3D.uploadFromVector() always stores the values from the Vector in floating point format. Of course! This is the most common situation for the components of the vertex positions, normals, texture coordinates and such. But we need our BYTES_4 color value to be stored verbatim as a 32-bit uint.

Vector.<Number> and uploadFromVector() do not give you control over the byte format of value. How do you solve this?

You must use a ByteArray, to which you will copy the values manually with the right format. In our example above, 3 writeFloat() calls plus a writeUnsignedInt() for the 32-bit color. A detail that stumped me for a while is that you must ensure the ByteArray is Endian.LITTLE_ENDIAN, it defaults to BIG_ENDIAN. I wrote this function to perform this process, with the aid of an array that stores the format of each varying parameter:

private static function DumpVectorComponentsIntoByteArray(dest:ByteArray,
                                                          src:Vector.<Number>,
                                                          componentFormats:Array): void
{
	dest.endian = Endian.LITTLE_ENDIAN;
	for (var i:int = 0; i < src.length; )
	{
		for (var j:int = 0; j < componentFormats.length; ++j)
		{
			switch (componentFormats[j])
			{
				case Context3DVertexBufferFormat.FLOAT_2:
					dest.writeFloat(src[i]);
					dest.writeFloat(src[i+1]);
					i += 2;
					break;
				case Context3DVertexBufferFormat.FLOAT_3:
					dest.writeFloat(src[i]);
					dest.writeFloat(src[i + 1]);
					dest.writeFloat(src[i + 2]);
					i += 3;
					break;
				case Context3DVertexBufferFormat.FLOAT_4:
					dest.writeFloat(src[i]);
					dest.writeFloat(src[i + 1]);
					dest.writeFloat(src[i + 2]);
					dest.writeFloat(src[i + 3]);
					i += 4;
					break;
				case Context3DVertexBufferFormat.BYTES_4:
					dest.writeUnsignedInt(src[i]);
					++i;
					break;
			}
		}
	}
}

So, in the case of the spinning cube, the component array would be:

var vertexBufferComponents:Array = new Array(
	Context3DVertexBufferFormat.FLOAT_3,
	Context3DVertexBufferFormat.BYTES_4
);

With that array in hand, another useful function to set the array components into the context:

private function SetVertexArrays(componentFormats:Array, vertices:VertexBuffer3D): void
{
	var offset:int = 0;
	for (var j:int = 0; j < componentFormats.length; ++j)
	{
		renderContext.setVertexBufferAt( j, vertices, offset, componentFormats[j] );
		switch (componentFormats[j])
		{
			case Context3DVertexBufferFormat.FLOAT_2:
				offset += 2;
				break;
			case Context3DVertexBufferFormat.FLOAT_3:
				offset += 3;
				break;
			case Context3DVertexBufferFormat.FLOAT_4:
				offset += 4;
				break;
			case Context3DVertexBufferFormat.BYTES_4:
				offset += 1;
				break;
		}
	}
}

One thought on “Stage3D Vertex Buffer formats

Comments are closed.