QEM_ProgMesh.cpp

00001 // QEM_ProgMesh.cpp : Implementation for the ProgMesh class, and some related classes.
00002 //
00003 // Author: Matt Loper
00004 // $Revision: 1.4 $
00005 // $Date: 2002/05/15 08:28:12 $
00006 
00007 
00008 #include "../common/standard.h"
00009 #include "Matrix.h"
00010 
00011 #include "QEM_Quadric.h"
00012 #include "QEM_ProgMesh.h"
00013   
00014  
00015 using namespace QEM;
00016 
00017 void ProgMesh::recalculateCosts()
00018 {
00019     int nFaces = faces.size();
00020     
00021     // Calculate a quadric for each face...
00022     for (int i = 0; i < nFaces; i++)
00023     {
00024         Face &f = faces[i];
00025         f.assertValid(i);
00026         Quadric q = f.calculateQuadric(i, vertices);
00027         
00028         //...and add that quadric to the quadrics of each of its three vertices
00029         f.getVertex(0, vertices, i).quadric += q;
00030         f.getVertex(1, vertices, i).quadric += q;
00031         f.getVertex(2, vertices, i).quadric += q;
00032         
00033         if (i%40 == 0) 
00034         {
00035 //            glutSetWindowTitle (fmtString("Calculating quadrics, %.2f%% done.", (float)100 * i / nFaces));
00036         }
00037     }
00038     
00039     // For each of our edges...
00040 //    glutSetWindowTitle (fmtString("Calculating edge costs..."));
00041     for (EdgePtr iter = edges.begin(); iter != edges.end(); iter++)
00042     {
00043         Edge &e = *iter;
00044         
00045         VertexQuadric v1 = vertices[e.getV1()];
00046         VertexQuadric v2 = vertices[e.getV2()];
00047         
00048         // ...sum up the quadrics of our neighboring vertices...
00049         Quadric sum = v1.quadric + v2.quadric;
00050         
00051         // and try to get an optimum point. If the quadric doesn't specify an
00052         // optimum, then take an average of the two points
00053         Vertex v;
00054         if (!sum.getOptimumPoint (v))
00055             v = (v1 + v2) / 2.;
00056         
00057         e.setCachedCost(sum.getCost(v));
00058     }
00059     
00060 //    glutSetWindowTitle (fmtString("Sorting..."));
00061     
00062     // see Edge::operator<
00063     edges.sort(&vertices);
00064     
00065 //    glutSetWindowTitle (fmtString("Done..."));
00066 //    glutPostRedisplay();
00067 }
00068 
00069 EdgePtr ProgMesh::addEdge (int faceIndex, int /* 0, 1, or 2 */ whichEdge, int vertexIndex1, int vertexIndex2)
00070 {
00071         VertexPairEdgeMap::iterator i;
00072         i = vertexPairEdgeMap.find (VertexPair (vertexIndex2, vertexIndex1));   
00073 
00074         // if this edge has a sister edge that's been added already....
00075         if (i != vertexPairEdgeMap.end())
00076         {
00077                 int index = (*i).second;
00078                 Face &f = faces[index/3];
00079                 Edge &e = f.getEdge(index%3);
00080                 e.setFace2(faceIndex,whichEdge);
00081 
00082                 vertexPairEdgeMap.erase (i);
00083 
00084                 return faces[index/3].getEdgePtr(index%3);
00085         }
00086         else // insert ourselves into the map
00087         {
00088                 Edge newEdge(faceIndex, whichEdge, vertexIndex1, vertexIndex2);
00089                 edges.push_front (newEdge);
00090                 vertexPairEdgeMap[VertexPair(vertexIndex1,vertexIndex2)] = newEdge.edgeIndexFirst();
00091                 return edges.begin();
00092         }
00093 }
00094 
00095 std::pair<int, int> findCommon(int a, int b, int c, int d)
00096 {
00097         int af = a/3;
00098         int bf = b/3;
00099         int cf = c/3;
00100         int df = d/3;
00101 
00102 
00103     if (a == b || c == d)
00104     {
00105         throw "findCommon(): degeneracy occurred. No further collapses allowed. (1)";
00106     }
00107     if (af == bf || cf == df)
00108     {
00109         throw "findCommon(): degeneracy occurred. No further collapses allowed. (2)";
00110     }
00111 
00112         if (af == cf)
00113                 a = d;
00114         else if (af == df)
00115                 a = c;
00116         else if (bf == cf)
00117                 b = d;
00118         else if (bf == df)
00119                 b = c;
00120         else
00121     {
00122                 throw "findCommon(): degeneracy occurred. No further collapses allowed.";
00123     }
00124 
00125         std::pair<int,int> rtn(a,b);
00126         rtn.first = a;
00127         rtn.second = b;
00128 
00129         return rtn;
00130 }
00131 
00132 bool ProgMesh::willDegenerate (EdgePtr e1, EdgePtr e2) const
00133 {
00134         Edge &edge1 = *e1;
00135         Edge &edge2 = *e2;
00136 /*
00137  * If one of the edges is a border, we know it won't degenerate
00138  */ 
00139         if (edge1.isBorder() || edge2.isBorder())
00140                 return false;
00141 
00142         std::pair<int,int> p = findCommon(
00143                 edge1.edgeIndexFirst(), 
00144                 edge1.edgeIndexSecond(),
00145                 edge2.edgeIndexFirst(), 
00146                 edge2.edgeIndexSecond());
00147                 
00148         int firstIndex = p.first/3;
00149         int secondIndex = p.second/3;
00150         
00151         int v1, v2, v3, v4, v5, v6;
00152         v1 = faces[firstIndex].getVertexIndex(0, firstIndex);
00153         v2 = faces[firstIndex].getVertexIndex(1, firstIndex);
00154         v3 = faces[firstIndex].getVertexIndex(2, firstIndex);
00155         v4 = faces[secondIndex].getVertexIndex(0, secondIndex);
00156         v5 = faces[secondIndex].getVertexIndex(1, secondIndex);
00157         v6 = faces[secondIndex].getVertexIndex(2, secondIndex);
00158         
00159         int n = 0;
00160         if (v1 == v4 || v1 == v5 || v1 == v6) n++;
00161         if (v2 == v4 || v2 == v5 || v2 == v6) n++;
00162         if (v3 == v4 || v3 == v5 || v3 == v6) n++;
00163         
00164         if (n > 1)
00165                 return true;
00166         return false;
00167 }
00168 
00169 
00170 EdgePtr ProgMesh::consolidateEdges (EdgePtr e1, EdgePtr e2, int deadFaceIndex)
00171 {
00172         Edge &edge1 = *e1;
00173         Edge &edge2 = *e2;
00174     
00175     if (!edge1.isBorder() &&
00176         edge1.edgeIndexFirst()/3 == edge1.edgeIndexSecond()/3)
00177     {
00178         return edges.end();
00179     }
00180 
00181     if (!edge2.isBorder() && 
00182         edge2.edgeIndexFirst()/3 == edge2.edgeIndexSecond()/3)
00183     {
00184         return edges.end();
00185     }
00186     
00187 /*
00188  * If we're in a manifold neighborhood, we have some work to do.
00189  */ 
00190         if (!edge1.isBorder() && !edge2.isBorder())
00191         {
00192         
00193                 // the two edges given to us have a face in common:
00194                 // the dead face.  We want one edge, with the two
00195                 // still-alive faces attached to it
00196                 std::pair<int,int> p = findCommon(
00197                         edge1.edgeIndexFirst(), 
00198                         edge1.edgeIndexSecond(),
00199                         edge2.edgeIndexFirst(), 
00200                         edge2.edgeIndexSecond());
00201 
00202 
00203 #if 0
00204         int v1, v2, v3, v4, v5, v6;
00205                 int firstIndex = p.first/3;
00206                 int secondIndex = p.second/3;
00207                 v1 = faces[firstIndex].getVertexIndex(0, firstIndex);
00208                 v2 = faces[firstIndex].getVertexIndex(1, firstIndex);
00209                 v3 = faces[firstIndex].getVertexIndex(2, firstIndex);
00210                 v4 = faces[secondIndex].getVertexIndex(0, secondIndex);
00211                 v5 = faces[secondIndex].getVertexIndex(1, secondIndex);
00212                 v6 = faces[secondIndex].getVertexIndex(2, secondIndex);
00213 
00214                 int n = 0;
00215                 if (v1 == v4 || v1 == v5 || v1 == v6) n++;
00216                 if (v2 == v4 || v2 == v5 || v2 == v6) n++;
00217                 if (v3 == v4 || v3 == v5 || v3 == v6) n++;
00218 #endif
00219 
00220                 edge1.setFace1 (p.first/3, p.first%3);
00221                 edge1.setFace2 (p.second/3, p.second%3);
00222 
00223                 faces[p.first/3].setEdge(p.first%3, e1);
00224                 faces[p.second/3].setEdge(p.second%3,e1);
00225 
00226         edges.erase (e2);
00227                 return e1;
00228 
00229         }
00230         // if both edges are borders, then they'll collapse
00231         // to nothing, and they should both just be deleted
00232         if (edge1.isBorder() && edge2.isBorder())
00233         {
00234                 edges.erase (e1);
00235                 edges.erase (e2);
00236                 return edges.end();
00237         }
00238 
00239         if (edge1.isBorder())
00240         {
00241                 return consolidateEdges (e2, e1, deadFaceIndex);
00242         }
00243 
00244         assert (!edge1.isBorder());
00245         assert (edge1.hasFirstNeighbor());
00246 
00247 /*
00248  * If our second edge is a border, we need to do three things...
00249  */
00250         if (edge2.isBorder())
00251         {
00252                 // (1) Erase the second edge
00253                 edges.erase (e2);
00254 
00255                 // (2) Edge1 points to two faces. Find the one that
00256                 // points to the dead face.
00257                 if (edge1.faceIndexFirst() == deadFaceIndex)
00258                 {
00259                         assert (edge1.faceIndexSecond() != deadFaceIndex);
00260                         // (3) Turn edge1 into a border, by removing
00261                         // our reference to its face
00262                         edge1.removeFace1();
00263                 }
00264                 else if (edge1.faceIndexSecond() == deadFaceIndex)
00265                 {
00266                         assert (edge1.faceIndexFirst() != deadFaceIndex);
00267                         // (3) Turn edge1 into a border, by removing
00268                         // our reference to its face
00269                         edge1.removeFace2();
00270                 }
00271                 else 
00272                         assert(!"ProgMesh::consolidateEdges(): one of the edges passed in isn't attached to a dead face");
00273                 return e1;
00274         }
00275     else
00276         throw ("ProgMesh::consolidateEdges(): shouldn't ever get here!");
00277 
00278     return edges.end();
00279 }
00280 
00281 
00282 
00283 Vertex ProgMesh::getOptimumPointForEdge (Edge &edge)
00284 {       
00285         Vertex optimumPoint;
00286         Quadric q = 
00287                 vertices[edge.getV1()].quadric +
00288                 vertices[edge.getV2()].quadric;
00289 
00290         if (!q.getOptimumPoint(optimumPoint))
00291         {
00292                 VertexQuadric &v1 = vertices[edge.getV1()];
00293                 VertexQuadric &v2 = vertices[edge.getV2()];
00294                 optimumPoint = (v1 + v2) / 2.;
00295         }
00296     return optimumPoint;
00297 }
00298 
00304 void ProgMesh::reportProgress (char *str)
00305 {
00306 
00307 }
00308 
00309         
00310 int ProgMesh::addOptimumPoint(Edge &deadEdge)
00311 {
00312         Vertex optimumPoint = getOptimumPointForEdge (deadEdge);
00313         VertexQuadric v(
00314                 optimumPoint[0], 
00315                 optimumPoint[1], 
00316                 optimumPoint[2]);
00317 
00318         v.quadric = vertices[deadEdge.getV1()].quadric + vertices[deadEdge.getV2()].quadric;
00319 
00320         vertices.push_back (v);
00321 
00322         int newVertexIndex = vertices.size() - 1;
00323         return newVertexIndex;
00324 }
00325 
00326 
00327 
00328 void ProgMesh::doCollapse(int finalPolygonCount)
00329 {
00330     int n = 0;
00331     int originalPolyCount = getNumFacesTotal();
00332     int numToRemove = originalPolyCount - finalPolygonCount;
00333 
00334     reportProgress ("Collapsing...");
00335     
00336     while (faces.getNumFacesVisible() > finalPolygonCount)
00337     {
00338         doOneCollapse();
00339         
00340         n++;
00341         if (n % 60 == 0)
00342         {
00343             int numRemoved = originalPolyCount - faces.getNumFacesVisible();
00344             float percentRemoved = 100 * (float)numRemoved / numToRemove;
00345             
00346             reportProgress (fmtString ("Collapsing %.2lf%% done. %d faces, %d vertices, %d edges", 
00347                 percentRemoved,
00348                 faces.getNumFacesVisible(), 
00349                 vertices.size(), 
00350                 edges.size()));
00351 //            glutPostRedisplay();
00352         }
00353     }
00354 }
00355 
00356 
00357 
00358 void ProgMesh::doOneCollapse()
00359 {
00360         if (edges.empty())
00361                 return; // nothing to collapse
00362 
00363 /* 
00364  * Get the first edge from our edgelist, and calculate its optimum vertex
00365  */
00366         EdgePtr deadEdgePtr = edges.begin();
00367         Edge &deadEdge = *deadEdgePtr;
00368         int newVertexIndex = addOptimumPoint (deadEdge);
00369 
00370     int oldV1 = deadEdge.getV1();
00371     int oldV2 = deadEdge.getV2();
00372 
00373     if (oldV1 == oldV2)
00374         MTRACE("warning: collapsing degenerate edge; both vertices have index %d", oldV1);
00375 /* 
00376  * Our soon-to-be-collapsed edge has two adjacent faces, which will
00377  * also collapse.  We can't just delete those adjacent faces, because
00378  * that would create holes in the mesh.  Instead, we have to sew two
00379  * of its edges together (the two that aren't the dead edge.  Here, we
00380  * just get the other two edges.  
00381  */
00382         EdgePair pairsToSew1; 
00383         EdgePair pairsToSew2; 
00384 
00385     // if our dead edge has a first neighbor...
00386     if (deadEdge.hasFirstNeighbor())
00387         {
00388                 // ...that neighbor has two other edges. Get those edges.
00389                 pairsToSew1 = faces[deadEdge.faceIndexFirst()].getOtherTwoEdges (deadEdgePtr);
00390         if (*pairsToSew1.first == *pairsToSew1.second)
00391         {
00392             (*pairsToSew1.first).printDebugString();
00393             (*pairsToSew1.second).printDebugString();
00394         }
00395                 assert (pairsToSew1.first != pairsToSew1.second);
00396                 assert (faces.faceIsVisible (deadEdge.faceIndexFirst()));
00397         }       
00398 
00399     // same thing, for second neighbor
00400     if (deadEdge.hasSecondNeighbor())
00401         { 
00402                 pairsToSew2 = faces[deadEdge.faceIndexSecond()].getOtherTwoEdges (edges.begin());
00403         if (*pairsToSew2.first == *pairsToSew2.second)
00404         {
00405             (*pairsToSew2.first).printDebugString();
00406             (*pairsToSew2.second).printDebugString();
00407         }
00408                 assert (pairsToSew2.first != pairsToSew2.second);
00409                 assert (faces.faceIsVisible (deadEdge.faceIndexSecond()));
00410         }
00411 /* 
00412  * Usually, collapses result in two dead faces. But if we're trying to
00413  * collapse a tetrahedron, we might end up with a non-manifold
00414  * surface, which we can't handle.  So here, we ask each of our dead
00415  * faces: "Are you part of a tetrahedron?" If they answer yes, then we
00416  * put off the collapse.  
00417  */
00418 
00419     if (deadEdge.hasFirstNeighbor())
00420     {
00421         if (willDegenerate(pairsToSew1.first, pairsToSew1.second))
00422         {
00423             deadEdge.setCachedCost (deadEdge.getCachedCost() + 10);
00424             edges.sort (&vertices);
00425             MTRACE("ProgMesh::doOneCollapse(): degeneracy averted\n");
00426             return;
00427         }
00428     }    
00429 
00430     if (deadEdge.hasSecondNeighbor())
00431     {
00432         if (willDegenerate(pairsToSew2.first, pairsToSew2.second))
00433         {
00434             deadEdge.setCachedCost (deadEdge.getCachedCost() + 10);
00435             edges.sort (&vertices);
00436             MTRACE("ProgMesh::doOneCollapse(): degeneracy averted\n");
00437             return;
00438         }
00439     }
00440 /* 
00441  * When an edge collapses to an optimum point, two vertices die, and a
00442  * new one is born. Anything that pointed to the old vertices, now has
00443  * to point to the new one. Face::swapVertexIndex recursively
00444  * propagates the changes to the surrounding faces. (Note: our primary
00445  * bottleneck is is the incremental sorting routing in
00446  * Face::swapVertexIndex) 
00447  */
00448     if (deadEdge.hasFirstNeighbor())
00449         faces.swapVertexIndex (oldV1, oldV2, newVertexIndex, deadEdge.faceIndexFirst(), edges);
00450 
00451     if (deadEdge.hasSecondNeighbor())
00452         faces.swapVertexIndex (oldV1, oldV2, newVertexIndex, deadEdge.faceIndexSecond(), edges);
00453 /*
00454  * Update edge <--> face connectivity, part 2
00455  */
00456     if (deadEdge.hasFirstNeighbor())
00457     {
00458                 consolidateEdges (pairsToSew1.first, pairsToSew1.second, deadEdge.faceIndexFirst());
00459     }
00460         
00461         if (deadEdge.hasSecondNeighbor())
00462     {
00463                 consolidateEdges (pairsToSew2.first, pairsToSew2.second, deadEdge.faceIndexSecond());
00464     }
00465 
00466     if (deadEdge.hasFirstNeighbor())
00467     {
00468         faces.hideFace (deadEdge.faceIndexFirst());
00469     }
00470         
00471         if (deadEdge.hasSecondNeighbor())
00472     {
00473         faces.hideFace (deadEdge.faceIndexSecond());
00474     }
00475 
00476     // finally, we pop the dead edge off the edge list
00477     edges.pop_front(); 
00478 
00479     // hand-sorting is performed in FaceArray::swapVertexIndex, so
00480     // there's no need to do a full sort here
00481     // edges.sort(&vertices, &faces);
00482 }
00483 
00484 
00485 
00486 void ProgMesh::clear() 
00487 {
00488         faces.clear();
00489         edges.clear();
00490         vertices.clear();
00491         vertexPairEdgeMap.clear();
00492 }
00493 
00494 
00495 
00496 void ProgMesh::draw() const
00497 {
00498         int numFaces = faces.size();
00499 
00500         for (int i = 0; i < numFaces; i++)
00501         {
00502                 if (!faces.faceIsVisible(i))
00503                         continue;
00504 
00505                 faces[i].draw(i, vertices, &colorMap);
00506         }       
00507 
00508 #if 0 // little ball, to help us see where collapses take place
00509     if (vertices.size() > 0)
00510     {
00511         glPushMatrix();
00512             glTranslatef(
00513                 vertices[vertices.size()-1][0],
00514                 vertices[vertices.size()-1][1],
00515                 vertices[vertices.size()-1][2]);
00516             glutSolidSphere(.01, 20, 20);
00517         glPopMatrix();
00518     }
00519 #endif
00520 
00521 }
00522 
00523 
00524 void ProgMesh::presize (int nTriangles, int nVertices) 
00525 {
00526         faces.reserve (nTriangles);
00527         vertices.reserve (nVertices);
00528 }
00529 
00530 void ProgMesh::addFace (int v1, int v2, int v3) 
00531 { 
00532         if (v1 == v2 || v2 == v3 || v1 == v3)
00533                 throw RuntimeError("ProgMesh::addFace(): degenerate faces not accepted.");
00534 
00535 
00536     // this is only here to throw an exception, if we've
00537     // received a degenerate face.
00538     this->vertices[v1].getUnitLengthNormalWith(
00539         vertices[v2], vertices[v3]);
00540 
00541     int faceIndex = faces.size();
00542 
00543         EdgePtr e1 = addEdge (faceIndex, 0, v1, v2);
00544         EdgePtr e2 = addEdge (faceIndex, 1, v2, v3);
00545         EdgePtr e3 = addEdge (faceIndex, 2, v3, v1);
00546         Face newFace (e1,e2,e3);
00547 
00548         faces.push_back (newFace);
00549 }
00550 
00551 
00552 
00553 int ProgMesh::addVertex (mfloat x, mfloat y, mfloat z)
00554 {
00555         VertexQuadric v(x,y,z);
00556         vertices.push_back (v);
00557         return (vertices.size() - 1);
00558 }
00559 
00560 
00561 
00562 int ProgMesh::addVertexWithColor (mfloat x, mfloat y, mfloat z, const RgbByte &color)
00563 {
00564     int index = addVertex(x,y,z);
00565 
00566     colorMap[index] = color;
00567 
00568     return index;
00569 }

Generated on Tue May 21 03:34:51 2002 for Archimedes by doxygen1.2.15