/* * * Copyright (C) 2000 Silicon Graphics, Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * Further, this software is distributed without any warranty that it is * free of the rightful claim of any third person regarding infringement * or the like. Any license provided herein, whether implied or * otherwise, applies only to this software file. Patent licenses, if * any, provided herein do not apply to combinations of this program with * other software, or any other product whatsoever. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Contact information: Silicon Graphics, Inc., 1600 Amphitheatre Pkwy, * Mountain View, CA 94043, or: * * http://www.sgi.com * * For further information regarding this notice, see: * * http://oss.sgi.com/projects/GenInfo/NoticeExplan/ * */ #include #include #include #include "IfAssert.h" #include "IfStripper.h" #include "IfHolder.h" ///////////////////////////////////////////////////////////////////////////// // // Constructor. Initializes data structures. // ///////////////////////////////////////////////////////////////////////////// IfStripper::IfStripper() { holder = NULL; } ///////////////////////////////////////////////////////////////////////////// // // Destructor. // ///////////////////////////////////////////////////////////////////////////// IfStripper::~IfStripper() { } ///////////////////////////////////////////////////////////////////////////// // // This takes a scene graph produced by flattening and condensing a // graph and produces better triangle strips, if possible. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::strip(IfHolder *_holder) { holder = _holder; // There had better be an SoIndexedTriangleStripSet in the IfHolder ASSERT(holder->stripSet != NULL); // This assumes that there is currently 1 triangle per strip in // the SoIndexedTriangleStripSet ASSERT(holder->stripSet->coordIndex.getNum() % 4 == 0); numTriangles = holder->stripSet->coordIndex.getNum() / 4; // See if there are any normal, texture coord, and material indices haveNormals = (holder->doNormals && holder->stripSet->normalIndex.getNum() > 1); haveTexCoords = (holder->doTexCoords && holder->stripSet->textureCoordIndex.getNum() > 1); haveMaterials = (holder->stripSet->materialIndex.getNum() > 1); // Coordinate, normal, and texture coordinate values have been // condensed, but we don't know how many distinct vertices there // are. If two vertices have all identical indices, they are // duplicates. (It's possible for two vertices to share // coordinates, but not normals, for example.) Determine // how many distinct vertices there are and allocate StripVertex // structures for them. createVertexList(); // Allocate and fill in triangle data structures createTriangleList(); // Find adjacent triangles findNeighborTriangles(); // Set up the pending triangle lists setUpPendingTriangleLists(); // We are going to fill in the vertex pointer list (vertexPtrList) // with pointers to the vertices forming the new triangle // strips. A NULL pointer indicates the end of a strip. First, // create the list with a reasonable size guess, to avoid lots of // reallocing. vertexPtrList = new SbPList(numTriangles * 2); // Create strips from the connected triangles createStrips(); // Change the indices in the triangle strip adjustIndices(); // Clean up delete [] vertices; delete [] triangles; delete vertexPtrList; } //////////////////////////////////////////////////////////////////////// // // Fills in StripVertex structures that represent triangle // vertices. Returns the number of distinct vertices. // //////////////////////////////////////////////////////////////////////// void IfStripper::createVertexList() { // Step 0: Access the fields we will need to use int numCoordIndices = holder->stripSet->coordIndex.getNum(); const int32_t *coordIndices = holder->stripSet->coordIndex.getValues(0); const int32_t *normalIndices = (! haveNormals ? NULL : holder->stripSet->normalIndex.getValues(0)); const int32_t *texCoordIndices = (! haveTexCoords ? NULL : holder->stripSet-> textureCoordIndex.getValues(0)); const int32_t *mtlIndices = (! haveMaterials ? NULL : holder->stripSet->materialIndex.getValues(0)); // Step 1: Assume the worst: all coordinates are distinct int maxNumVertices = numTriangles * 3; // Step 2: Allocate an array of StripVertex instances big enough // to hold all of them StripVertex *allVertices = new StripVertex[maxNumVertices]; // Step 3: Create an array of pointers to StripVertex instances, // indexed by coordinate index. This will allow use to find // duplicates very easily. (Note that the maximum coordinate index // we can find is the number of coordinate values - 1.) int i, numCoords = holder->coords->point.getNum(); StripVertex **table = new StripVertex *[numCoords]; for (i = 0; i < numCoords; i++) table[i] = NULL; // Step 4: Also create an array of indices into the StripVertex // instances, indexed by the index into the coordIndex field. This // is used later on to create triangles from the StripVertex // instances. vertexMap = new int [numCoordIndices]; // Step 5: Fill in the StripVertex instances with actual indices, // checking for duplicates. If we find a duplicate, we don't save // it. StripVertex *curVert = allVertices; for (i = 0; i < numCoordIndices; i++) { int coordIndex = coordIndices[i]; if (coordIndex < 0) { vertexMap[i] = -1; continue; } // Fill in the next StripVertex instance curVert->coordIndex = coordIndex; curVert->normIndex = (haveNormals ? normalIndices[i] : -1); curVert->texCoordIndex = (haveTexCoords ? texCoordIndices[i] : -1); curVert->mtlIndex = (haveMaterials ? mtlIndices[i] : -1); curVert->uniqueID = curVert - allVertices; // See if we have any entries at that slot in the table StripVertex *oldVert; for (oldVert = table[coordIndex]; oldVert != NULL; oldVert = oldVert->next) { if (oldVert->normIndex == curVert->normIndex && oldVert->texCoordIndex == curVert->texCoordIndex && oldVert->mtlIndex == curVert->mtlIndex) break; } // If we found a match, re-use the old vertex if (oldVert != NULL) vertexMap[i] = oldVert->uniqueID; // Otherwise, store this vertex in the table and prepare for // the next vertex else { curVert->next = table[coordIndex]; table[coordIndex] = curVert; vertexMap[i] = curVert->uniqueID; curVert++; } } // Step 6: The number of vertices we stored is the number of // distinct vertices numVertices = curVert - allVertices; // Step 7: Copy the distinct vertices into the real array vertices = new StripVertex[numVertices]; for (i = 0; i < numVertices; i++) vertices[i] = allVertices[i]; // Step 8: Get rid of the stuff we don't need any more delete [] table; delete [] allVertices; } //////////////////////////////////////////////////////////////////////// // // Creates structures that will be easier to deal with when creating // triangle strips. // //////////////////////////////////////////////////////////////////////// void IfStripper::createTriangleList() { const int32_t *coordIndices = holder->stripSet->coordIndex.getValues(0); // Allocate triangles triangles = new StripTriangle[numTriangles]; // Fill in triangle structures, using the vertexMap created in // createVertexList for (int i = 0; i < numTriangles; i++) { StripTriangle *tri = &triangles[i]; tri->index = i; for (int j = 0; j < 3; j++) { ASSERT(coordIndices[4*i + j] >= 0); int vertexIndex = vertexMap[4*i + j]; ASSERT(vertexIndex >= 0); ASSERT(vertexIndex < numVertices); tri->v[j] = &vertices[vertexIndex]; } // No neighbors yet tri->t[0] = tri->t[1] = tri->t[2] = NULL; // Not used in a real strip yet tri->isUsed = FALSE; // We have no neighbor info yet. We will compute this later. tri->numUnusedNeighbors = -1; // Not in a list yet tri->prev = tri->next = NULL; } // We don't need this any more delete [] vertexMap; } ///////////////////////////////////////////////////////////////////////////// // // Finds adjacent triangles // ///////////////////////////////////////////////////////////////////////////// void IfStripper::findNeighborTriangles() { // Allocate edge structures StripEdge *edges = new StripEdge[3 * numTriangles]; // Create a dictionary for finding edges edgeDict = new SbDict(1235); // Run through triangles, storing edge structures and finding neighbors StripEdge *edge = edges; for (int i = 0; i < numTriangles; i++) { StripTriangle *tri = &triangles[i]; // Look for a neighbor edge for each of the 3 edges. If found, // mark the two triangles as neighbors and remove the neighbor // edge from the list of unpaired edges. If not found, add the // edge to the list. #define DO_EDGE(VA, VB, WHICH) \ edge->v1 = tri->v[VA]; \ edge->v2 = tri->v[VB]; \ edge->tri = tri; \ edge->which = WHICH; \ edge->next = NULL; \ addEdge(edge); \ edge++ DO_EDGE(1, 2, 0); DO_EDGE(2, 0, 1); DO_EDGE(0, 1, 2); #undef DO_EDGE } // We created the edges only to set up the neighbor relationships // in triangles. Now that that is done, we don't need the edges // any more delete [] edges; delete edgeDict; } ///////////////////////////////////////////////////////////////////////////// // // Adds an edge to the list of edges, checking for neighbors // ///////////////////////////////////////////////////////////////////////////// void IfStripper::addEdge(StripEdge *newEdge) { // See if any edges like this one are already in the dictionary uint32_t key = hashEdge(newEdge); void *listPtr; if (edgeDict->find(key, listPtr)) { // Look through edge list for a neighbor SbBool foundNeighbor = FALSE; StripEdge *prevEdge = NULL; for (StripEdge *edge = (StripEdge *) listPtr; edge != NULL; edge = edge->next) { // See if the edges are neighbors if (edge->v1 == newEdge->v2 && edge->v2 == newEdge->v1) { // Store the neighbor relationship in both triangles edge->tri->t[edge->which] = newEdge->tri; newEdge->tri->t[newEdge->which] = edge->tri; // Remove the neighbor edge from the dictionary if (prevEdge != NULL) prevEdge->next = edge->next; else { listPtr = edge->next; // If nothing left in list, remove the whole list // from dictionary if (listPtr == NULL) edgeDict->remove(key); } // No need to keep looking foundNeighbor = TRUE; break; } prevEdge = edge; } // Add the new edge to the end of the list if no neighbor was found if (! foundNeighbor) prevEdge->next = newEdge; } // If no list exists yet for this hash key, create a new one else edgeDict->enter(key, newEdge); } ///////////////////////////////////////////////////////////////////////////// // // Hash function for edges. This function is guaranteed to return the // same value for two edges containing the same vertices, regardless // of direction; i.e., it's symmetric. // ///////////////////////////////////////////////////////////////////////////// uint32_t IfStripper::hashEdge(StripEdge *edge) { // Use the unique ID in each vertex to create the hashing key. // Since we have to maintain symmetry, use the smaller index first. int i1 = edge->v1->uniqueID; int i2 = edge->v2->uniqueID; if (i2 < i1) { int t = i1; i1 = i2; i2 = t; } // Note: this function is not guaranteed to produce all unique // keys (if there are > 65536 unique vertices), but that's no // problem, because there is a list of edges in each hash slot. return (i1 << 16) + i2; } ///////////////////////////////////////////////////////////////////////////// // // Sets up the pending triangle lists. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::setUpPendingTriangleLists() { int i; for (i = 0; i < 4; i++) pendingTriList[i] = NULL; // Now that all neighbors are known, figure out how many neighbors // each triangle has and set numUnusedNeighbors to that // number. Then add the triangle to the appropriate list. for (i = 0; i < numTriangles; i++) { StripTriangle *tri = &triangles[i]; tri->numUnusedNeighbors = 0; for (int j = 0; j < 3; j++) if (tri->t[j] != NULL) tri->numUnusedNeighbors++; addTriangle(tri, tri->numUnusedNeighbors); } } ///////////////////////////////////////////////////////////////////////////// // // Creates strips from the connected triangles. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::createStrips() { StripTriangle *tri; // Repeat until there are no more unmarked triangles while (chooseStartTriangle(tri)) { // Find a shared edge (if any) int shared = 3; for (int j = 0; j < 3; j++) { if (tri->t[j] != NULL && ! tri->t[j]->isUsed) { shared = j; break; } } // Add 3 vertices of triangle, starting with vertex opposite // shared edge to strip switch (shared) { case 0: vertexPtrList->append(tri->v[0]); vertexPtrList->append(tri->v[1]); vertexPtrList->append(tri->v[2]); break; case 1: vertexPtrList->append(tri->v[1]); vertexPtrList->append(tri->v[2]); vertexPtrList->append(tri->v[0]); break; case 2: case 3: // No shared edges; order doesn't matter vertexPtrList->append(tri->v[2]); vertexPtrList->append(tri->v[0]); vertexPtrList->append(tri->v[1]); break; } // No shared edges? Mark triangle as used, terminate strip, // and try another triangle if (shared == 3) { markTriangleUsed(tri); vertexPtrList->append(NULL); continue; } // This is used to choose which edge to go to next. It // alternates after each triangle, to form a proper strip. SbBool oddFace = TRUE; while (TRUE) { markTriangleUsed(tri); // Get the next triangle ASSERT(shared != 3); StripTriangle *nextTri = tri->t[shared]; // Stop if there isn't any next triangle if (nextTri == NULL || nextTri->isUsed) break; // Add the vertex of this triangle opposite the shared edge if (nextTri->t[0] == tri) { vertexPtrList->append(nextTri->v[0]); shared = (oddFace ? 2 : 1); } else if (nextTri->t[1] == tri) { vertexPtrList->append(nextTri->v[1]); shared = (oddFace ? 0 : 2); } else if (nextTri->t[2] == tri) { vertexPtrList->append(nextTri->v[2]); shared = (oddFace ? 1 : 0); } // It's possible (if more than 2 triangles share an edge) // that nextTri does not consider tri to be a neighbor. If // this happens, end the current strip and start another. else break; // Prepare for next triangle tri = nextTri; oddFace = ! oddFace; } // Terminate the strip vertexPtrList->append(NULL); } } //////////////////////////////////////////////////////////////////////// // // Chooses and returns a good starting triangle for tstrip generation. // //////////////////////////////////////////////////////////////////////// SbBool IfStripper::chooseStartTriangle(StripTriangle *&tri) { // Choose the triangle with the fewest unused neighbors for (int i = 0; i < 4; i++) { if (pendingTriList[i] != NULL) { tri = pendingTriList[i]; ASSERT(! tri->isUsed); return TRUE; } } tri = NULL; return FALSE; } ///////////////////////////////////////////////////////////////////////////// // // Marks a triangle as used, removing it from the pending list and // changing the status of all neighbor triangles. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::markTriangleUsed(StripTriangle *tri) { ASSERT(! tri->isUsed); tri->isUsed = TRUE; // Remove it from its current list removeTriangle(tri, tri->numUnusedNeighbors); // Decrement the numUnusedNeighbors in all neighbors and move them // to the correct list for (int i = 0; i < 3; i++) { StripTriangle *neigh = tri->t[i]; if (neigh != NULL && ! neigh->isUsed) { ASSERT(neigh->numUnusedNeighbors > 0); removeTriangle(neigh, neigh->numUnusedNeighbors); addTriangle(neigh, --neigh->numUnusedNeighbors); } } } ///////////////////////////////////////////////////////////////////////////// // // Adds a triangle to the given pending list. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::addTriangle(StripTriangle *tri, int listIndex) { StripTriangle *first = pendingTriList[listIndex]; if (first != NULL) first->prev = tri; tri->next = first; tri->prev = NULL; pendingTriList[listIndex] = tri; } ///////////////////////////////////////////////////////////////////////////// // // Removes a triangle from the given pending list. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::removeTriangle(StripTriangle *tri, int listIndex) { if (tri->prev != NULL) tri->prev->next = tri->next; else pendingTriList[listIndex] = tri->next; if (tri->next != NULL) tri->next->prev = tri->prev; } ///////////////////////////////////////////////////////////////////////////// // // Changes the indices in the triangle strip. // ///////////////////////////////////////////////////////////////////////////// void IfStripper::adjustIndices() { int numVertices = vertexPtrList->getLength(); // Fill in the coordinate indices holder->stripSet->coordIndex.setNum(numVertices); int32_t *indices = holder->stripSet->coordIndex.startEditing(); for (int i = 0; i < numVertices; i++) { StripVertex *vert = (StripVertex *) (*vertexPtrList)[i]; indices[i] = (vert == NULL ? -1 : vert->coordIndex); } holder->stripSet->coordIndex.finishEditing(); // Do same for normal indices, if necessary if (haveNormals) { holder->stripSet->normalIndex.setNum(numVertices); indices = holder->stripSet->normalIndex.startEditing(); for (int i = 0; i < numVertices; i++) { StripVertex *vert = (StripVertex *) (*vertexPtrList)[i]; indices[i] = (vert == NULL ? -1 : vert->normIndex); } holder->stripSet->normalIndex.finishEditing(); } // Do same for texture coordinate indices, if necessary if (haveTexCoords) { holder->stripSet->textureCoordIndex.setNum(numVertices); indices = holder->stripSet->textureCoordIndex.startEditing(); for (int i = 0; i < numVertices; i++) { StripVertex *vert = (StripVertex *) (*vertexPtrList)[i]; indices[i] = (vert == NULL ? -1 : vert->texCoordIndex); } holder->stripSet->textureCoordIndex.finishEditing(); } // Do same for material indices, if necessary if (haveMaterials) { holder->stripSet->materialIndex.setNum(numVertices); indices = holder->stripSet->materialIndex.startEditing(); for (int i = 0; i < numVertices; i++) { StripVertex *vert = (StripVertex *) (*vertexPtrList)[i]; indices[i] = (vert == NULL ? -1 : vert->mtlIndex); } holder->stripSet->materialIndex.finishEditing(); } }