What´s an "IB"?
Yes the viewer does analyze and correct (if necessary) the topology of the mesh. That´s necessary because of the reorganization for tangent-space bumpmapping anyway (vertices have to be duplicated at the symmetry-seam, and duplicated vertices that are not located at the seam have to be welded). But you asked what my viewer does in preparation for the stencil shadowing. And the edge list creation is the only thing in that regard, and I will not post all the other load-time initialization code.
Yours is not an efficient way to store the edge data for shadowing. You don´t end up with a list of edges, but just with "tri-edges", that is edges that belong to a triangle. One and the same edge is represented multiple times, in each triangle that uses that edge (usually two). But edges in the shadowing kind of sense are a thing of their own.
You want to have a quick way to determine the silhouette of an object. The silhouette is a collection of edges (pairs of vertices, basically), a sub-collection of all the edges of the model. Your representation of edges is unefficient because they are stored within the triangles, but an edge can belong to more than one triangle, and thus shouldn´t be stored like that.
Here´s how the viewer does it. The result of a call to
createedgelist is a filled
edgelist[meshnumber]. An
edge is a struct that contains two vertex-indices, two triangle-indices and a bool that flags that the edge is a silhouette edge.
Code:
void MD5Model::appendedge(int meshnumber, int v1, int v2, int tri)
{
numedges[meshnumber]++;
edgelist[meshnumber]=(edge*) realloc(edgelist[meshnumber], numedges[meshnumber]*sizeof(edge));
edge* newedge = &edgelist[meshnumber][numedges[meshnumber]-1];
newedge->silhouette=0;
newedge->tri1=tri;
newedge->vert1=v1;
newedge->vert2=v2;
newedge->tri2=-1; //no second triangle yet
}
void MD5Model::findcreatematchingedge(int meshnumber, int v1, int v2, int tri)
{
for (int i=0; i<numedges[meshnumber]; i++)
{
if (edgelist[meshnumber][i].vert1==v2 && edgelist[meshnumber][i].vert2==v1)
{
if (edgelist[meshnumber][i].tri2==-1)
{
edgelist[meshnumber][i].tri2=tri;
return;
}
else
fprintf(debugfile(), "geometryanalyzer: edge with more than two tris: meshnumber, v1, v2, tri: %i %i %i %i\n", meshnumber, v1, v2, tri);
}
}
//we haven´t found a match for this edge, so we create it as a new one
appendedge(meshnumber, v1, v2, tri);
}
void MD5Model::createedgelist(int meshnumber)
{
for (int i=0; i<numtris[meshnumber]; i++)
{
trianglestruct* tri = &meshtrisarray[meshnumber][i];
if (tri->vertex1<tri->vertex2)
appendedge(meshnumber, tri->vertex1, tri->vertex2, i);
if (tri->vertex2<tri->vertex3)
appendedge(meshnumber, tri->vertex2, tri->vertex3, i);
if (tri->vertex3<tri->vertex1)
appendedge(meshnumber, tri->vertex3, tri->vertex1, i);
}
for (i=0; i<numtris[meshnumber]; i++)
{
trianglestruct* tri = &meshtrisarray[meshnumber][i];
if (tri->vertex1>tri->vertex2)
findcreatematchingedge(meshnumber, tri->vertex1, tri->vertex2, i);
if (tri->vertex2>tri->vertex3)
findcreatematchingedge(meshnumber, tri->vertex2, tri->vertex3, i);
if (tri->vertex3>tri->vertex1)
findcreatematchingedge(meshnumber, tri->vertex3, tri->vertex1, i);
}
}
How the viewer uses that edgelist at runtime should be self-explanatory if you have understood stencil-shadowing. First, for each triangle we do a light-test (tri-normal dot L), so that each triangle knows whether it faces the light or not. Then we determine the silhouette edges, by looking at the two tris of an edge (or only one for one-winged edges). The result is that the
silhouette boolean members of the edges get filled. Then we´re ready to draw the shadow volumes.