This week’s article offers another useful utility function: indexedTrisToString. This function is especially useful when dealing with 3D engines such as those based on Context3D.drawTriangles or Graphics.drawTriangles. It helps to untangle the complicated indices/vertices format that these API functions require into something much more readable and, therefore, debuggable.

Both the software-based Graphics.drawTriangles and the hardware-based Context3D.drawTriangles require you to specify two sets of data: vertex data (X, Y, Z) and sets of three indices into those vertices forming a triangle. For example:

var tris:Vector.<uint> = new <uint>[
	0, 1, 2,
	2, 1, 0
];
var vertices:Vector.<Number> = new <Number>[
	11, 12, 13,
	21, 22, 23,
	31, 32, 33
];

The tris specify two triangles ([0,1,2] and [2,1,0]) in the vertices. Each index I actually resolves to the vertex with components [I*3, I*3+1, I*3+2]. To make matters worse, Stage3D often packs more vertex data in with X, Y, and Z to specify things like normal vectors and texture coordinates. At this point it’s very difficult to interpret the data in its raw form:

trace(tris);
trace(vertices);
 
// output:
0,1,2,2,1,0
11,12,13,21,22,23,31,32,33

That’s not very helpful. Enter today’s utility function-indexedTrisToString:

trace(indexedTrisToString(tris, vertices, "\n"));
 
// output:
V0: (X: 11, Y: 12, Z: 13)
V1: (X: 21, Y: 22, Z: 23)
V2: (X: 31, Y: 32, Z: 33)
 
V0: (X: 31, Y: 32, Z: 33)
V1: (X: 21, Y: 22, Z: 23)
V2: (X: 11, Y: 12, Z: 13)

Now that’s a lot more useful! So what if we had extra data after the X, Y, and Z components:

var verticesExtraRight:Vector.<Number> = new <Number>[
	11, 12, 13, 14,
	21, 22, 23, 15,
	31, 32, 33, 16
];
trace(indexedTrisToString(tris, verticesExtraRight, "\n", 4));

All we had to do was specify that there were four elements per vertex and the function took care of it. How about data before that X, Y, and Z components?

var verticesExtraLeft:Vector.<Number> = new <Number>[
	10, 11, 12, 13,
	20, 21, 22, 23,
	30, 31, 32, 33
];
trace(indexedTrisToString(tris, verticesExtraLeft, "\n", 4, 1, 2, 3));

Here we had to specify that there were four elements per vertex and also the indices of the X, Y, and Z components. Now it’s easy to handle data on the left and and right of the X, Y, and Z components:

var verticesExtraLeftRight:Vector.<Number> = new <Number>[
	10, 11, 12, 13, 14,
	20, 21, 22, 23, 15,
	30, 31, 32, 33, 16
];
trace(indexedTrisToString(tris, verticesExtraLeftRight, "\n", 5, 1, 2, 3));

The only modification is to tell indexedTrisToString that there are five components per vertex.

Here is the source code of today’s utility function:

/**
*   Get a string representation of indexed triangles
*   @param tris Indices of the triangles
*   @param vertices Vertex data (may contain more than just X, Y, and Z)
*   @param sep Separator between triangles
*   @param attributesPerVertex Number of attributes per vertex. For example, (XYZ) is 3 and
*                              (XYZUV) is 5.
*   @param xIndex Index into the vertex attributes of the X component
*   @param yIndex Index into the vertex attributes of the Y component
*   @param zIndex Index into the vertex attributes of the Z component
*   @return A string representation of the given indexed triangles
*   @author Jackson Dunstan (http://JacksonDunstan.com)
*   @license MIT
*/
public static function indexedTrisToString(
	tris:Vector.<uint>,
	vertices:Vector.<Number>,
	sep:String,
	attributesPerVertex:uint=3,
	xIndex:uint=0,
	yIndex:uint=1,
	zIndex:uint=2
	): String
{
	var ret:String = "";
	for (var i:uint, triIndex:uint, len:uint = tris.length; i < len; i+=3, triIndex++)
	{
		if (triIndex)
		{
			ret += sep;
		}
		var baseVertexIndex:uint = tris[i];
		ret += "V0: (X: " + vertices[baseVertexIndex*attributesPerVertex+xIndex]
			+ ", Y: " + vertices[baseVertexIndex*attributesPerVertex+yIndex]
			+ ", Z: " + vertices[baseVertexIndex*attributesPerVertex+zIndex] + ")" + sep;
		baseVertexIndex = tris[i+1];
		ret += "V1: (X: " + vertices[baseVertexIndex*attributesPerVertex+xIndex]
			+ ", Y: " + vertices[baseVertexIndex*attributesPerVertex+yIndex]
			+ ", Z: " + vertices[baseVertexIndex*attributesPerVertex+zIndex] + ")" + sep;
		baseVertexIndex = tris[i+2];
		ret += "V2: (X: " + vertices[baseVertexIndex*attributesPerVertex+xIndex]
			+ ", Y: " + vertices[baseVertexIndex*attributesPerVertex+yIndex]
			+ ", Z: " + vertices[baseVertexIndex*attributesPerVertex+zIndex] + ")" + sep;
	}
	return ret;
}

I hope you find it useful in your 3D debugging! If you spot a bug in it or have a question, leave a comment below!