
/* md2edge.c - edge sharing for MD2 model triangles */

/* Copyright NVIDIA Corporation, 2000. */

/* $Id: //sw/main/apps/OpenGL/mjk/md2shader/md2edge.c#17 $ */

#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#ifdef __APPLE__
#include <GLUT/glut.h>  // for GLfloat
#else
#include <GL/glut.h>  // for GLfloat
#endif

#include "md2.h"
#include "vecutil.h"

extern const char *myProgramName;

/* sameVertex - determine if two vertices are identical.  To be
   identical, the two vertices must have the same respective
   X,Y,Z values in each and every frame. */
static int
sameVertex(Md2Model *model, int v0, int v1)
{
  int i;

  for (i=0; i<model->header.numFrames; i++) {
    Md2TriangleVertex *v = model->frames[i].vertices;  
    
    if (v[v0].vertex[0] != v[v1].vertex[0] ||
        v[v0].vertex[1] != v[v1].vertex[1] ||
        v[v0].vertex[2] != v[v1].vertex[2]) {
      return 0;
    }
  }
  return 1;
}

static void
joinTriangles(Md2TriangleEdgeInfo *edgeInfo,
              int tri1, unsigned int edge1,
              int tri2, unsigned int edge2)
{
  assert(edge1 < 3);
  assert(tri1 >= 0);
  assert(edge2 < 3);
  assert(tri2 >= 0);

  edgeInfo[tri1].adjacentTriangle[edge1] = tri2;
  edgeInfo[tri1].adjacentTriangleEdges &= ~(0x3 << (2*edge1));
  edgeInfo[tri1].adjacentTriangleEdges |= edge2 << (2*edge1);

  edgeInfo[tri2].adjacentTriangle[edge2] = tri1;
  edgeInfo[tri2].adjacentTriangleEdges &= ~(0x3 << (2*edge2));
  edgeInfo[tri2].adjacentTriangleEdges |= edge1 << (2*edge2);
}

static void
matchWithTriangleSharingEdge(Md2Model *model,
                             int triangle, int edge,
                             int v0, int v1, int otherv)
{
  const int numTriangles = model->header.numTriangles;
  int doubleTri = -1;
  int otherEdge = 0;
  int i;
  
  /* Match shared edges based on vertex numbers (relatively fast). */
  for (i = triangle+1; i < numTriangles; i++) {
    Md2Triangle *t = &model->triangles[i];
    
    if (t->vertexIndices[0] == v0) {
      if (t->vertexIndices[2] == v1) {
        if (model->edgeInfo[i].adjacentTriangle[2] < 0) {
          if (t->vertexIndices[1] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 2;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 2, triangle, edge);
            return;
          }
        }
      }
    }
    if (t->vertexIndices[1] == v0) {
      if (t->vertexIndices[0] == v1) {
        if (model->edgeInfo[i].adjacentTriangle[0] < 0) {
          if (t->vertexIndices[2] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 0;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 0, triangle, edge);
            return;
          }
        }
      }
    }
    if (t->vertexIndices[2] == v0) {
      if (t->vertexIndices[1] == v1) {
        if (model->edgeInfo[i].adjacentTriangle[1] < 0) {
          if (t->vertexIndices[0] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 1;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 1, triangle, edge);
            return;
          }
        }
      }
    }
  }
  
  /* Match shared edges based on vertex XYZ values (slow check). */
  for (i = triangle+1; i < model->header.numTriangles; i++) {
    Md2Triangle *t = &model->triangles[i];
    
    if (sameVertex(model, t->vertexIndices[0], v0)) {
      if (sameVertex(model, t->vertexIndices[2], v1)) {
        if (model->edgeInfo[i].adjacentTriangle[2] < 0) {
          if (t->vertexIndices[0] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 2;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 2, triangle, edge);
            return;
          }
        }
      }
    }
    if (sameVertex(model, t->vertexIndices[1], v0)) {
      if (sameVertex(model, t->vertexIndices[0], v1)) {
        if (model->edgeInfo[i].adjacentTriangle[0] < 0) {
          if (t->vertexIndices[0] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 0;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 0, triangle, edge);
            return;
          }
        }
      }
    }
    if (sameVertex(model, t->vertexIndices[2], v0)) {
      if (sameVertex(model, t->vertexIndices[1], v1)) {
        if (model->edgeInfo[i].adjacentTriangle[1] < 0) {
          if (t->vertexIndices[0] == otherv) {
            if (doubleTri < 0) {
              doubleTri = i;
              otherEdge = 1;
            }
          } else {
            joinTriangles(model->edgeInfo, i, 1, triangle, edge);
            return;
          }
        }
      }
    }
  }
  
  /* Only connect a triangle to a triangle with the exact
     same three vertices as a last resort. */
  if (doubleTri >= 0) {
    joinTriangles(model->edgeInfo, doubleTri, otherEdge, triangle, edge);
    return;
  }
}

static float
polygonArea(Md2Model *model, Md2Boundary *boundaryList, int boundaryIndex)
{
  Md2TriangleVertex *v;
  float d01[3], d02[3], prod[3];
  float norm, maxNorm;
  int v0, v1, v2;
  int i;
  
  /* Get the vertices of the triangle along the boundary. */
  v0 = boundaryList[boundaryIndex].vertexIndex;
  v1 = boundaryList[boundaryList[boundaryIndex].next[0]].vertexIndex;
  v2 = boundaryList[boundaryList[boundaryIndex].next[1]].vertexIndex;

  /* Compute the area of the triangle in the first frame. */
  v = model->frames[0].vertices;  
  v3sub(v[v0].vertex, v[v1].vertex, d01);
  v3sub(v[v0].vertex, v[v2].vertex, d02);
  v3cross(d01, d02, prod);
  maxNorm = v3sqlength(prod);
  
  /* For each frame in the model (beyond the first)... */
  for (i=1; i<model->header.numFrames; i++) {

    /* Compute the area of the triangle in the ith frame. */
    v = model->frames[i].vertices;  
    v3sub(v[v0].vertex, v[v1].vertex, d01);
    v3sub(v[v0].vertex, v[v2].vertex, d02);
    v3cross(d01, d02, prod);
    norm = v3sqlength(prod);

    /* Is this the maximum area that we have encountered? */
    if (norm > maxNorm) {
      maxNorm = norm;
    }
  }

  /* Return the maximum triangle area among all frames. */
  return maxNorm;
}

static int
addNewTriangle(Md2Model *model)
{
  int newTriIndex = model->header.numTriangles;

  /* Add another triangle to the model and resize the
     model's triangle-sized lists. */
  model->header.numTriangles++;
  model->triangles = (Md2Triangle*)
    realloc(model->triangles,
      sizeof(Md2Triangle) * model->header.numTriangles);
  model->edgeInfo = (Md2TriangleEdgeInfo*)
    realloc(model->edgeInfo,
      sizeof(Md2TriangleEdgeInfo) * model->header.numTriangles);

  return newTriIndex;
}

static void
fixOpenTriangle(Md2Model *model, Md2Boundary *boundaryList, int boundaryIndex)
{
  Md2Triangle *newTri;
  int newTriIndex;
  int b0 = boundaryIndex;
  int bp = boundaryList[b0].prev;
  int b1 = boundaryList[b0].next[0];
  int b2 = boundaryList[b0].next[1];

  assert(boundaryList[b1].next[0] == b2);
  assert(boundaryList[bp].next[0] == b0);
  assert(boundaryList[bp].next[1] == b1);
  
  newTriIndex = addNewTriangle(model);
  newTri = &model->triangles[newTriIndex];

  /** Initialize the new triangle **/

  /* Assign the new vertices. */
  newTri->vertexIndices[0] = boundaryList[b2].vertexIndex;
  newTri->vertexIndices[1] = boundaryList[b1].vertexIndex;
  newTri->vertexIndices[2] = boundaryList[b0].vertexIndex;

  /* Bogus texture indices are fine since triangle is (hopefully)
     interior to the model. */
  newTri->textureIndices[0] = 0;
  newTri->textureIndices[1] = 0;
  newTri->textureIndices[2] = 0;

  /* Mark edge 2 unconnected */
  model->edgeInfo[newTriIndex].adjacentTriangle[2] = -1;
  model->edgeInfo[newTriIndex].adjacentTriangleEdges = 0x3 << 4;

  /* Make sure edges we are joining are currently unconnected. */
  assert(model->edgeInfo[boundaryList[b1].triangle].adjacentTriangle[boundaryList[b1].edge] == -1);
  assert(model->edgeInfo[boundaryList[b0].triangle].adjacentTriangle[boundaryList[b0].edge] == -1);

  /* Join the triangles with the new triangle. */
  joinTriangles(model->edgeInfo, 
                newTriIndex, 0, 
                boundaryList[b1].triangle, boundaryList[b1].edge);
  joinTriangles(model->edgeInfo, 
                newTriIndex, 1, 
                boundaryList[b0].triangle, boundaryList[b0].edge);

  /** Update the boundary list based on the addition of the new triangle. **/

  boundaryList[b0].triangle = newTriIndex;
  boundaryList[b0].edge = 2;
  boundaryList[b0].next[0] = b2;
  boundaryList[b0].next[1] = boundaryList[b2].next[0];
  boundaryList[b0].maxSqArea = polygonArea(model, boundaryList, b0);

  boundaryList[bp].next[1] = b2;

  boundaryList[b1].active = 0;

  boundaryList[b2].prev = b0;
}

static void
findOpenBoundary(Md2Model *model, int triangle, int edge,
                 int *boundaryVertices, Md2Boundary *boundaryList)
{
  short v0, v;
  int nextEdge;
  int otherTriangle;
  int count;
  int i;

  if (model->edgeInfo[triangle].openEdgeMask & 1<<edge) {
    return;
  }

  count = 0;

  assert(model->edgeInfo[triangle].adjacentTriangle[edge] == -1);

  model->edgeInfo[triangle].openEdgeMask |= 1<<edge;
  
  v0 = model->triangles[triangle].vertexIndices[edge];
  boundaryList[count].vertexIndex = v0;
  boundaryList[count].triangle = triangle;
  boundaryList[count].edge = edge;
  count++;

  nextEdge = (edge+1)%3;
  v = model->triangles[triangle].vertexIndices[nextEdge];
  while (!sameVertex(model, v, v0)) {
    otherTriangle = model->edgeInfo[triangle].adjacentTriangle[nextEdge];
    while (otherTriangle >= 0) {
      for (i=0; i<3; i++) {
        if (model->edgeInfo[otherTriangle].adjacentTriangle[i] == triangle) {
          assert(sameVertex(model,
	           model->triangles[otherTriangle].vertexIndices[(i+1)%3], v));

          triangle = otherTriangle;
      
          nextEdge = (i+1)%3;
          break;
        }
      }
      assert(i<3);
      otherTriangle = model->edgeInfo[triangle].adjacentTriangle[nextEdge];
    }

    /* Mark this edge as processed to avoid reprocessing
       the boundary multiple times. */
    model->edgeInfo[triangle].openEdgeMask |= 1<<nextEdge;

    boundaryList[count].vertexIndex = v;
    boundaryList[count].triangle = triangle;
    boundaryList[count].edge = nextEdge;
    count++;

    nextEdge = (nextEdge+1)%3;
    v = model->triangles[triangle].vertexIndices[nextEdge];
  }
  *boundaryVertices = count;
}

static void
fixOpenBoundary(Md2Model *model,
                int count, Md2Boundary *boundaryList)
{
  int b0, b1, b2;
  int i;

  if (count == 1) {
    /* Ugh, a degenerate triangle with two (or perhaps three) 
       identical vertices tricking us into thinking that there
       is an open edge.  Hopefully these should be eliminated
       by an earlier "eliminate" pass, but such triangles are
       harmless. */
    return;
  }

  assert(count >= 3);

  if (count == 3) {
    /* Often a common case.  Save bookkeeping and close the triangle
       boundary immediately. */
    b0 = 0;
    b1 = 1;
    b2 = 2;
  } else {
    float maxMaxSqArea;
    int numActive;
    int minIndex = 0;
    
    boundaryList[0].prev = count-1;
    boundaryList[0].next[0] = 1;
    boundaryList[0].next[1] = 2;
    boundaryList[0].active = 1;

    for (i=1; i<count-2; i++) {
      boundaryList[i].prev = i-1;
      boundaryList[i].next[0] = i+1;
      boundaryList[i].next[1] = i+2;
      boundaryList[i].active = 1;
    }

    boundaryList[i].prev = i-1;
    boundaryList[i].next[0] = i+1;
    boundaryList[i].next[1] = 0;
    boundaryList[i].active = 1;

    boundaryList[i+1].prev = i;
    boundaryList[i+1].next[0] = 0;
    boundaryList[i+1].next[1] = 1;
    boundaryList[i+1].active = 1;
    
    boundaryList[0].maxSqArea = polygonArea(model, boundaryList, 0);
    maxMaxSqArea = boundaryList[0].maxSqArea;
    
    for (i=1; i<count; i++) {
      boundaryList[i].maxSqArea = polygonArea(model, boundaryList, i);
      if (boundaryList[i].maxSqArea > maxMaxSqArea) {
        maxMaxSqArea = boundaryList[i].maxSqArea;
      }
    }

    /* If triangles are formed from adjacent edges along the
       boundary, at least front-facing such triangle should
       be front-facing (ie, have a non-negative area). */
    /* XXX Would this apply to Mobeous strips and Kline bottles? */
    assert(maxMaxSqArea >= 0.0);

    maxMaxSqArea = 2.0f * maxMaxSqArea;
    
    numActive = count;
    
    while (numActive > 3) {
      float min;

      min = maxMaxSqArea;
      for (i=0; i<count; i++) {
        if (boundaryList[i].active) {
          if (boundaryList[i].maxSqArea < min) {
            if (boundaryList[i].maxSqArea >= 0.0) {
              min = boundaryList[i].maxSqArea;
              minIndex = i;
            }
          }
        }
      }
      assert(min < maxMaxSqArea);
      fixOpenTriangle(model, boundaryList, minIndex);

      /* Newly created triangle formed from adjacent edges
         along the boundary could be larger than the
         previous largest triangle. */
      if (boundaryList[minIndex].maxSqArea > maxMaxSqArea) {
        maxMaxSqArea = 2.0f * boundaryList[minIndex].maxSqArea;
      
      }
      numActive--;
    }

    for (i=0; i<count; i++) {
      if (boundaryList[i].active) {
        minIndex = i;
        break;
      }
    }
    assert(i < count);

    b0 = minIndex;
    b1 = boundaryList[b0].next[0];
    b2 = boundaryList[b0].next[1];

    assert(boundaryList[b0].prev == b2);
    assert(boundaryList[b1].prev == b0);
    assert(boundaryList[b1].next[0] == b2);
    assert(boundaryList[b1].next[1] == b0);
    assert(boundaryList[b2].prev == b1);
    assert(boundaryList[b2].next[0] == b0);
    assert(boundaryList[b2].next[1] == b1);
  }

  /* Place final "keystone" triangle to fill completely
     the open boundary. */
  {
    Md2Triangle *newTri;
    int newTriIndex = model->header.numTriangles;

    model->header.numTriangles++;
    model->triangles = (Md2Triangle*)
      realloc(model->triangles,
        sizeof(Md2Triangle) * model->header.numTriangles);
    model->edgeInfo = (Md2TriangleEdgeInfo*)
      realloc(model->edgeInfo,
        sizeof(Md2TriangleEdgeInfo) * model->header.numTriangles);

    newTri = &model->triangles[newTriIndex];
    newTri->vertexIndices[0] = boundaryList[b2].vertexIndex;
    newTri->vertexIndices[1] = boundaryList[b1].vertexIndex;
    newTri->vertexIndices[2] = boundaryList[b0].vertexIndex;
    /* Bogus texture indices are fine since triangle is (hopefully)
       interior to themodel. */
    newTri->textureIndices[0] = 0;
    newTri->textureIndices[1] = 0;
    newTri->textureIndices[2] = 0;

    /* Join keystone triangle. */
    joinTriangles(model->edgeInfo,
                  newTriIndex, 0,
                  boundaryList[b1].triangle, boundaryList[b1].edge);
    joinTriangles(model->edgeInfo, 
                  newTriIndex, 1,
                  boundaryList[b0].triangle, boundaryList[b0].edge);
    joinTriangles(model->edgeInfo,
                  newTriIndex, 2,
                  boundaryList[b2].triangle, boundaryList[b2].edge);
  }
}

static void
findAndFixOpenTriangleGroups(Md2Model *model, int triangle)
{
  int count;
  Md2Boundary *boundaryList = (Md2Boundary*)
    malloc((1+2*model->header.numTriangles)*sizeof(Md2Boundary));

  if (model->edgeInfo[triangle].adjacentTriangle[0] < 0) {
    findOpenBoundary(model, triangle, 0, &count, boundaryList);
    fixOpenBoundary(model, count, boundaryList);
  }
  if (model->edgeInfo[triangle].adjacentTriangle[1] < 0) {
    findOpenBoundary(model, triangle, 1, &count, boundaryList);
    fixOpenBoundary(model, count, boundaryList);
  }
  if (model->edgeInfo[triangle].adjacentTriangle[2] < 0) {
    findOpenBoundary(model, triangle, 2, &count, boundaryList);
    fixOpenBoundary(model, count, boundaryList);
  }
}

/* md2EliminateTrivialDegenerateTriangles - throw away triangles
   in the model that are trivially degenerate.  Triangles are
   trivially degenerate if they have two (ie, the triangle is
   really a line) or three identical vertices (ie, the triangle
   is really a point).

   A non-trivial degenerate triangle has effectively zero area,
   but does not duplicate vertices.  The vertices are in a line,
   but no two vertices are identical.  To avoid floating-point
   accuracy issues (and to avoid a lot of computation), a
   subsequent "eliminate" routine kills non-trivial degenerate
   triangles based on adjacency information.

   This routine is dedicated to the jupiter.md2 model.  What
   an utterly crappy model. */
void
md2EliminateTrivialDegenerateTriangles(Md2Model *model)
{
  int lineCount = 0, pointCount = 0;
  int i;

  /* Work backwards so we can kill degenerate triangles by
     replacing the dengenerate triangle with the last triangle
     in the list.  For this to work, we must make sure that
     the last triangles in the list have already been verified
     not to be degenerate. */
  for (i = model->header.numTriangles-1; i >= 0; i--) {
    Md2Triangle *t = &model->triangles[i];
    int v0 = t->vertexIndices[0];
    int v1 = t->vertexIndices[1];
    int v2 = t->vertexIndices[2];
    int dupCount = 0;

    if (sameVertex(model, v0, v1)) {
      dupCount++;
    }
    if (sameVertex(model, v1, v2)) {
      dupCount++;
    }
    if (sameVertex(model, v2, v0)) {
      dupCount++;
    }
    if (dupCount > 0) {
      assert(dupCount != 2);
      model->triangles[i] = model->triangles[model->header.numTriangles-1];
      model->header.numTriangles--;
      if (dupCount == 3) {
        pointCount++;
      } else {
        lineCount++;
      }
    }
  }
  if (lineCount) {
    printf("%s: %d line-degenerate triangles removed from \"%s\"\n",
      myProgramName,
      lineCount, model->filename);
  }
  if (pointCount) {
    printf("%s: %d point-degenerate triangles removed from \"%s\"\n",
      myProgramName,
      pointCount, model->filename);
  }
}

static void
reconnectSharedEdges(Md2Model *model, int isTri, int wasTri)
{
  int tri;
  int i, j;
  int count;
  
  for (i=0; i<3; i++) {
    tri = model->edgeInfo[wasTri].adjacentTriangle[i];
    
    if (tri >= 0) {
      count = 0;
      for (j=0; j<3; j++) {
        if (model->edgeInfo[tri].adjacentTriangle[j] == wasTri) {
          model->edgeInfo[tri].adjacentTriangle[j] = isTri;
          count++;
        }
        if (model->edgeInfo[tri].adjacentTriangle[j] == isTri) {
          count++;
        }
      }
      assert(count > 0);
    }
  }
}

void
possiblyReconnectTriangle(Md2Model *model, int tri, int isTri, int wasTri)
{
  int j;

  for (j=0; j<3; j++) {
    if (model->edgeInfo[tri].adjacentTriangle[j] == wasTri) {
      model->edgeInfo[tri].adjacentTriangle[j] = isTri;
    }
  }
}

static int
eliminateAdjacentDegeneratePair(Md2Model *model, int badTri, int otherBadTri,
                                int goodTri)
{
  int otherGoodTri = 0;
  int numTris;
  int i, j;

  assert(badTri < model->header.numTriangles);
  assert(otherBadTri < model->header.numTriangles);
  assert(goodTri < model->header.numTriangles);

  /* The other good triangle is the triangle adjacent to the other
     bad triangle but which is not the bad triangle. */
  for (i=0; i<3; i++) {
    if (model->edgeInfo[otherBadTri].adjacentTriangle[i] != badTri) {
      otherGoodTri = model->edgeInfo[otherBadTri].adjacentTriangle[i];
      break;
    }
  }
  assert(i < 3);
  
  /* Fix the good triangle so that both edges adjacent to the
     bad triangle are now adjacent to the other good triangle. */
  for (i=0; i<3; i++) {
    if (model->edgeInfo[goodTri].adjacentTriangle[i] == badTri) {
      model->edgeInfo[goodTri].adjacentTriangle[i] = otherGoodTri;
    }
  }
  
  /* Fix the other good triangle so that both edges adjacent to the
     other bad triangle are now adjacent to the good triangle. */
  for (i=0; i<3; i++) {
    if (model->edgeInfo[otherGoodTri].adjacentTriangle[i] == otherBadTri) {
      model->edgeInfo[otherGoodTri].adjacentTriangle[i] = goodTri;
    }
  }

  /* Decrement the model's triangle count by 2.  Then copy
     non-degenerate triangles from the end of the triangle
     list to the slots once used by the eliminated triangles.
     Be sure to copy the edgeInfo data structure too.  Also
     if goodTri is one of the last two triangles, be careful
     to make sure it gets copied. */

  model->header.numTriangles -= 2;
  numTris = model->header.numTriangles;
  
  if (goodTri < numTris) {
    model->triangles[badTri] = model->triangles[numTris+1];
    model->edgeInfo[badTri]  = model->edgeInfo[numTris+1];    
    model->triangles[otherBadTri] = model->triangles[numTris];
    model->edgeInfo[otherBadTri]  = model->edgeInfo[numTris];   
    reconnectSharedEdges(model, badTri, numTris+1);
    reconnectSharedEdges(model, otherBadTri, numTris);
    /* We are moving two triangles and they each might be
       connected to each other.  Possibly reconnect the
       edges appropriately if so. */
    possiblyReconnectTriangle(model, badTri, otherBadTri, numTris);
    possiblyReconnectTriangle(model, otherBadTri, badTri, numTris+1);
  } else {
    if (goodTri == numTris+1) {
      if (badTri < numTris) {
        model->triangles[badTri] = model->triangles[numTris+1];
        model->edgeInfo[badTri]  = model->edgeInfo[numTris+1];
        model->triangles[otherBadTri] = model->triangles[numTris];
        model->edgeInfo[otherBadTri]  = model->edgeInfo[numTris];  
        reconnectSharedEdges(model, badTri, numTris+1);
        possiblyReconnectTriangle(model, badTri, otherBadTri, numTris);

        if (otherBadTri < numTris) {
          reconnectSharedEdges(model, otherBadTri, numTris);
          possiblyReconnectTriangle(model, otherBadTri, badTri, numTris+1);
        }

        goodTri = badTri;
      } else {
        assert(otherBadTri < numTris);
        model->triangles[otherBadTri] = model->triangles[numTris+1];
        model->edgeInfo[otherBadTri]  = model->edgeInfo[numTris+1];
        model->triangles[badTri] = model->triangles[numTris];
        model->edgeInfo[badTri]  = model->edgeInfo[numTris];           
        reconnectSharedEdges(model, otherBadTri, numTris+1);
        possiblyReconnectTriangle(model, otherBadTri, badTri, numTris);

        if (badTri < numTris) {
          reconnectSharedEdges(model, badTri, numTris);
          possiblyReconnectTriangle(model, badTri, otherBadTri, numTris+1);
        }

        goodTri = otherBadTri;
      }
    } else {
      assert(goodTri == numTris);
      if (badTri < numTris) {
        model->triangles[badTri] = model->triangles[numTris];
        model->edgeInfo[badTri]  = model->edgeInfo[numTris];
        model->triangles[otherBadTri] = model->triangles[numTris+1];
        model->edgeInfo[otherBadTri]  = model->edgeInfo[numTris+1];  
        reconnectSharedEdges(model, badTri, numTris);
        possiblyReconnectTriangle(model, badTri, otherBadTri, numTris+1);

        if (otherBadTri < numTris) {
          reconnectSharedEdges(model, otherBadTri, numTris+1);
          possiblyReconnectTriangle(model, otherBadTri, badTri, numTris);
        }

        goodTri = badTri;
      } else {
        assert(otherBadTri < numTris);
        model->triangles[otherBadTri] = model->triangles[numTris];
        model->edgeInfo[otherBadTri]  = model->edgeInfo[numTris];    
        model->triangles[badTri] = model->triangles[numTris+1];
        model->edgeInfo[badTri]  = model->edgeInfo[numTris+1];  
        reconnectSharedEdges(model, otherBadTri, numTris);
        possiblyReconnectTriangle(model, otherBadTri, badTri, numTris+1);

        if (badTri < numTris) {
          reconnectSharedEdges(model, badTri, numTris+1);
          possiblyReconnectTriangle(model, badTri, otherBadTri, numTris);
        }

        goodTri = otherBadTri;
      }
    }
  }
  
  assert(goodTri < model->header.numTriangles);

  /* Patch up the edge info for the two relocated triangles. */
  for (i=model->header.numTriangles-1; i >= 0; i--) {
    for (j=0; j<3; j++) {
      assert(model->edgeInfo[i].adjacentTriangle[j] <
             model->header.numTriangles);
    }
  }

#ifndef NDEBUG
  for (i=model->header.numTriangles-1; i >= 0; i--) {
    for (j=0; j<3; j++) {
      assert(model->edgeInfo[i].adjacentTriangle[j] <
             model->header.numTriangles);
    }
  }
  for (i=model->header.numTriangles-1; i >= 0; i--) {
    if ((model->edgeInfo[i].adjacentTriangle[0] ==
         model->edgeInfo[i].adjacentTriangle[1]) &&
        (model->edgeInfo[i].adjacentTriangle[1] ==
	 model->edgeInfo[i].adjacentTriangle[2]) &&
        (model->edgeInfo[i].adjacentTriangle[0] != -1)) {
      assert(model->edgeInfo[model->edgeInfo[i].adjacentTriangle[0]].adjacentTriangle[0] == i);
      assert(model->edgeInfo[model->edgeInfo[i].adjacentTriangle[0]].adjacentTriangle[1] == i);
      assert(model->edgeInfo[model->edgeInfo[i].adjacentTriangle[0]].adjacentTriangle[2] == i);
      printf("badness\n");
    }
  }
#endif

  /* Two degenerate triangles eliminated. */
  return 2;
}

static int
findAndFixAdjacentDegeneratePair(Md2Model *model, int tri)
{
  int t0, t1, t2;
  
  t0 = model->edgeInfo[tri].adjacentTriangle[0];
  t1 = model->edgeInfo[tri].adjacentTriangle[1];
  t2 = model->edgeInfo[tri].adjacentTriangle[2];
  
  /* Trivially degnerate triangles should have already been eliminated. */
  assert(t0 != tri);
  assert(t1 != tri);
  assert(t2 != tri);

  if ((t0 == t1) && (t1 == t2)) {
    if (t0 >= 0) {
      assert(model->edgeInfo[t0].adjacentTriangle[0] == tri);
      assert(model->edgeInfo[t0].adjacentTriangle[1] == tri);
      assert(model->edgeInfo[t0].adjacentTriangle[2] == tri);
    }
    return 0;
  }
  
  if (t0 == t1) {
    if (t0 >= 0) {
      return eliminateAdjacentDegeneratePair(model, tri, t0, t2);
    }
  }
  if (t1 == t2) {
    if (t1 >= 0) {
      return eliminateAdjacentDegeneratePair(model, tri, t1, t0);
    }
  }
  if (t2 == t0) {
    if (t2 >= 0) {
      return eliminateAdjacentDegeneratePair(model, tri, t2, t1);
    }
  }
  return 0;
}

void
md2EliminateAdjacentDegenerateTriangles(Md2Model *model)
{
  int count = 0;
  int loopCount;
  int i;

  /* Eliminating two degenerate triangle pairs may
     not be the end of the story if the two "good" triangles
     that get connected are also degenerate.  Loop to
     handle this unlikely event. */
  do {
    loopCount = count;
    for (i = 0; i<model->header.numTriangles; i++) {
      count += findAndFixAdjacentDegeneratePair(model, i);
    }
  } while (count > loopCount);

  if (count) {
    printf("%s: eliminated %d adjacent degenerate triangles from \"%s\"\n",
      myProgramName,
      count, model->filename);
  }
}

void
md2CloseOpenTriangleGroups(Md2Model *model)
{
  int groups = 0;
  int i;
  
  for (i = model->header.numTriangles-1; i >= 0; i--) {
    if (model->edgeInfo[i].adjacentTriangle[0] < 0 ||
      model->edgeInfo[i].adjacentTriangle[1] < 0 ||
      model->edgeInfo[i].adjacentTriangle[2] < 0) {
      findAndFixOpenTriangleGroups(model, i);
      groups++;
    } 
  }
  if (groups > 0) {
    printf("%s: had to close %d open triangle groups in \"%s\" model\n",
      myProgramName,
      groups, model->filename);
  }
}

void
md2ComputeTriangleEdgeInfo(Md2Model *model)
{
  const int numTriangles = model->header.numTriangles;
  int i;

  /* Allocate edge information for all triangles in the model.
     Note that the connectivity is assumed to be the same
     among all frames. */
  model->edgeInfo = (Md2TriangleEdgeInfo *)
    malloc(sizeof(Md2TriangleEdgeInfo) * numTriangles);

  /* Initialize edge information as if all triangles are
     fully disconnected. */
  for (i = 0; i < numTriangles; i++) {
    model->edgeInfo[i].adjacentTriangle[0] = -1;  /* Vertex 0,1 edge */
    model->edgeInfo[i].adjacentTriangle[1] = -1;  /* Vertex 1,2 edge */
    model->edgeInfo[i].adjacentTriangle[2] = -1;  /* Vertex 2,0 edge */

    model->edgeInfo[i].adjacentTriangleEdges = (0x3 << 0) | 
                                               (0x3 << 2) | 
                                               (0x3 << 4);

    model->edgeInfo[i].openEdgeMask = 0;
  }

  for (i = 0; i < numTriangles; i++) {
    Md2Triangle *t = &model->triangles[i];

    if (model->edgeInfo[i].adjacentTriangle[0] < 0) {
      matchWithTriangleSharingEdge(model, i, 0,
        t->vertexIndices[0], t->vertexIndices[1], t->vertexIndices[2]);
    }
    if (model->edgeInfo[i].adjacentTriangle[1] < 0) {
      matchWithTriangleSharingEdge(model, i, 1,
        t->vertexIndices[1], t->vertexIndices[2], t->vertexIndices[0]);
    }
    if (model->edgeInfo[i].adjacentTriangle[2] < 0) {
      matchWithTriangleSharingEdge(model, i, 2,
        t->vertexIndices[2], t->vertexIndices[0], t->vertexIndices[1]);
    }
  }
}

void
md2CheckForBogusAdjacency(Md2Model *model)
{
  const int numTriangles = model->header.numTriangles;
  int i, k;
  unsigned char j;

  for (i = 0; i < numTriangles; i++) {
    for (j=0; j<3; j++) {
      int mutuallyAdjacentCount0, mutuallyAdjacentCount1;
      int adjacentTriangle;
#ifndef NDEBUG  /* Only used by assert macros. */
      unsigned char adjacentTriangleEdges =
        model->edgeInfo[i].adjacentTriangleEdges;
      int adjacentTriangleSharedEdge =
        ADJACENT_EDGE(adjacentTriangleEdges, j);
#endif
      
      adjacentTriangle = model->edgeInfo[i].adjacentTriangle[j];

      if (adjacentTriangle >= 0) {
        
        assert(adjacentTriangleSharedEdge < 3);
        assert(model->edgeInfo[adjacentTriangle].adjacentTriangle[adjacentTriangleSharedEdge] == i);
        assert(ADJACENT_EDGE(model->edgeInfo[adjacentTriangle].adjacentTriangleEdges, adjacentTriangleSharedEdge) == j);
        
        if (adjacentTriangle == i) {
          printf("warning: triangle %d should not be adjacent to itself!\n", i);
        }
        
        mutuallyAdjacentCount0 = 0;
        for (k=0; k<3; k++) {
          if (model->edgeInfo[i].adjacentTriangle[j] ==
	      model->edgeInfo[i].adjacentTriangle[k]) {
            mutuallyAdjacentCount0++;
          }
        }
        
        mutuallyAdjacentCount1 = 0;
        for (k=0; k<3; k++) {
          if (model->edgeInfo[adjacentTriangle].adjacentTriangle[k] == i) {
            mutuallyAdjacentCount1++;
          }
        }
        
        if (mutuallyAdjacentCount0 != mutuallyAdjacentCount1) {
          printf("warning: triangles %d and %d should be "
	    "mutually adjacent but are not!\n",
            i, adjacentTriangle);
        }
      } else {
        assert(adjacentTriangle == -1);
        assert(adjacentTriangleSharedEdge == 3);
      }
    }
  }
}

static void
makePlane(float p[4], 
          const float v0[3], const float v1[3], const float v2[3])
{
  GLfloat vec0[3], vec1[3];

  /* Need 2 vectors to find cross product. */
  vec0[0] = v1[0] - v0[0];
  vec0[1] = v1[1] - v0[1];
  vec0[2] = v1[2] - v0[2];

  vec1[0] = v2[0] - v0[0];
  vec1[1] = v2[1] - v0[1];
  vec1[2] = v2[2] - v0[2];

  /* Use cross product to get A, B, and C of plane equation */
  p[0] =   vec0[1] * vec1[2] - vec0[2] * vec1[1];
  p[1] = -(vec0[0] * vec1[2] - vec0[2] * vec1[0]);
  p[2] =   vec0[0] * vec1[1] - vec0[1] * vec1[0];
  p[3] = -(p[0] * v0[0] + p[1] * v0[1] + p[2] * v0[2]);
}

/* Instead of trying to compute the plane equation for each interpolated
   triangle, we pre-compute the plane equation for every triangle in
   every key frame.  Since the key frame interpolation is simply linear,
   we can interpolate the two plane equations from corresponding key
   frame triangles and reduce our per-frame math.

   md2ComputeFrameTrianglePlanes pre-computes the plane equation for
   every triangle in every key frame. */
void
md2ComputeFrameTrianglePlanes(Md2Model *model)
{
  Md2Frame *frame;
  Md2FrameTrianglePlane *framePlane;
  const int numTriangles = model->header.numTriangles;
  const int numFrames = model->header.numFrames;
  int i, j;
  
  /* Allocate enough space for a plane equation for every
     triangle in every frame of the model. */
  model->framePlane = (Md2FrameTrianglePlane *)
    malloc(numFrames * numTriangles * sizeof(Md2FrameTrianglePlane));

  framePlane = model->framePlane;
  frame = model->frames;

  /* For each frame... */
  for (i=0; i<numFrames; i++) {
    Md2TriangleVertex *vertices = frame->vertices;
    Md2Triangle *t = model->triangles;

    /* For each triangle... */
    for (j=0; j<numTriangles; j++) {
      /* Get the vertex position of each vertex of triangle for the frame. */
      const float *v0 = vertices[t->vertexIndices[0]].vertex;
      const float *v1 = vertices[t->vertexIndices[1]].vertex;
      const float *v2 = vertices[t->vertexIndices[2]].vertex;

      /* Compute and stash the plane equation for the triangle
         in this particular frame. */
      makePlane(framePlane->p, v0, v1, v2);
      framePlane++;
      t++;
    }
    frame++;
  }
}

void md2ComputeAdjacencyInfo(Md2Model *model)
{
  md2EliminateTrivialDegenerateTriangles(model);
  md2ComputeTriangleEdgeInfo(model);
  md2CheckForBogusAdjacency(model);
#if 0
  md2EliminateAdjacentDegenerateTriangles(model);
#endif
  md2CloseOpenTriangleGroups(model);
}
