Primitive Drawing

We skipped over how the Mesh::Render function and mesh loading works. So let's cover that now.

The XML-based mesh files define a number of vertex attribute arrays, followed by a number of rendering commands. The format fully supports all features of OpenGL, including options not previously discussed. One of these options deals with how vertex data is interpreted by OpenGL.

The glDraw* commands, whether using indexed rendering or array rendering, establish a vertex stream. A vertex stream is an ordered list of vertices, with each vertex having a specific set of vertex attributes. A vertex stream is processed by the vertex shader in order.

In array rendering, the order is determined by the order of the vertices in the attribute arrays. In indexed rendering, the order is determined by the order of the indices.

Once the stream is processed by the vertex shader, it must be interpreted into something meaningful by OpenGL. Every glDraw* command takes, as its first parameter, a value that tells OpenGL how to interpret the stream. Thus far, we have used GL_TRIANGLES, but there are many options. This parameter is called the rendering mode or primitive.

The parameter actually determines two things. The first it determines is what kind of things the vertex stream refers to; this is the primitive type. OpenGL can render points and lines in addition to triangles. These are all different primitive types.

The other thing the parameter determines is how to interpret the vertex stream for that primitive type. This is the primitive representation. GL_TRIANGLES says more than simply that the primitive type is triangles.

What GL_TRIANGLES means is that a vertex stream will generate triangles as follows: (0, 1, 2), (3, 4, 5), (6, 7, 8), …. The numbers represent vertices in the vertex stream, not indexed rendering indices. Among other things, this means that the vertex stream must have a length divisible by 3. For N vertices in the stream, this representation will generate N / 3 triangles.

There are two other triangular primitive representations. They are both used in the cylinder mesh, so let's take a look at that.

Example 7.9. Cylinder Mesh File

<indices cmd="tri-fan" type="ushort" >0 1 3 5 7 9 11 ...</indices>
<indices cmd="tri-fan" type="ushort" >61 60 58 56 54 ...</indices>
<indices cmd="tri-strip" type="ushort" >1 2 3 4 5 6 7 8 ...</indices>

Each indices element maps to a call to glDrawElements with the given index array. The cmd attribute determines the primitive that will be passed to glDrawElements. The value triangles means to use the GL_TRIANGLES primitive.

The tri-fan used above means to use the GL_TRIANGLE_FAN primitive. This primitive has the triangle primitive type, so this vertex stream will generate triangles. But it will generate them using a different representation.

GL_TRIANGLES takes each independent set of 3 vertices as a single triangle. GL_TRIANGLE_FAN takes the first vertex and holds on to it. Then, for every vertices and its next vertex, a triangle is made out of these two plus the initial vertex. So GL_TRIANGLE_FAN will generate triangles as follows: (0, 1, 2), (0, 2, 3), (0, 3, 4), …. Visually, a triangle fan looks like this:

The numbers represent the order that the vertices are in in the vertex stream. The red line shows the triangle edges that are directly specified by the vertex stream. All other edges are generated automatically by the primitive representation.

This is why it is called a fan. The number of vertices in a triangle fan vertex stream must be 3 or greater, but can be otherwise any number. For N vertices in a stream, triangle fans will generate N-2 triangles.

The cylinder mesh uses two fans to render the end pieces (caps) of the cylinder.

The tri-strip in the cylinder mesh represents the GL_TRIANGLE_STRIP primitive. As the name suggests, it has a triangle primitive type. The primitive representation means that every 3 adjacent vertices will generate a triangle, in order. So strips generate triangles as follows: (0, 1, 2), (1, 2, 3), (2, 3, 4), …. Visually, a triangle strip looks like this:

Like with triangle fans, the number of vertices must be 3 or greater, but can be any number otherwise. For N vertices in a stream, triangle strips will generate N-2 triangles.

The cylinder mesh uses a single triangle strip to render the sides of the cylinder.

Winding Order.  There is one other issue with triangle strips. This has to do with the winding order of the triangles.

The winding order for the triangles in a strip looks like this:

Notice how it alternates between clockwise and counter-clockwise. This means that, regardless of what face you consider front, and what face you cull, you'll always lose about half of the faces.

However, OpenGL is rather intelligent about this. Triangle strips do face culling differently. For every second triangle, the one who's winding order is opposite from the first triangle's order, the winding order is considered backwards for culling purposes.

So if you have set the front face to be clockwise, and have face culling cull back-facing triangles, everything will work exactly as you expect so long as the order of the first triangle is correct. Every even numbered triangle will be culled if it has a clockwise winding, and every odd numbered triangle will be culled if it has a counter-clockwise winding.

Fork me on GitHub