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.