FreeFOAM The Cross-Platform CFD Toolkit
Hosted by SourceForge:
Get FreeFOAM at SourceForge.net.
            Fast, secure and Free Open Source software downloads

progmesh.C

Go to the documentation of this file.
00001 /*
00002  *  Progressive Mesh type Polygon Reduction Algorithm
00003  *  by Stan Melax (c) 1998
00004  *  Permission to use any of this code wherever you want is granted..
00005  *  Although, please do acknowledge authorship if appropriate.
00006  *
00007  *  See the header file progmesh.h for a description of this module
00008  */
00009 
00010 #include <stdio.h>
00011 #include <math.h>
00012 #include <stdlib.h>
00013 #include <assert.h>
00014 //#include <windows.h>
00015 
00016 #include "vector.h"
00017 #include "list.h"
00018 #include "progmesh.h"
00019 
00020 #define min(x,y) (((x) <= (y)) ? (x) : (y))
00021 #define max(x,y) (((x) >= (y)) ? (x) : (y))
00022 
00023 
00024 /*
00025  *  For the polygon reduction algorithm we use data structures
00026  *  that contain a little bit more information than the usual
00027  *  indexed face set type of data structure.
00028  *  From a vertex we wish to be able to quickly get the
00029  *  neighboring faces and vertices.
00030  */
00031 class Triangle;
00032 class Vertex;
00033 
00034 class Triangle {
00035   public:
00036     Vertex *         vertex[3]; // the 3 points that make this tri
00037     Vector           normal;    // unit vector othogonal to this face
00038                      Triangle(Vertex *v0,Vertex *v1,Vertex *v2);
00039                      ~Triangle();
00040     void             ComputeNormal();
00041     void             ReplaceVertex(Vertex *vold,Vertex *vnew);
00042     int              HasVertex(Vertex *v);
00043 };
00044 class Vertex {
00045   public:
00046     Vector           position; // location of point in euclidean space
00047     int              id;       // place of vertex in original list
00048     List<Vertex *>   neighbor; // adjacent vertices
00049     List<Triangle *> face;     // adjacent triangles
00050     float            objdist;  // cached cost of collapsing edge
00051     Vertex *         collapse; // candidate vertex for collapse
00052                      Vertex(Vector v,int _id);
00053                      ~Vertex();
00054     void             RemoveIfNonNeighbor(Vertex *n);
00055 };
00056 List<Vertex *>   vertices;
00057 List<Triangle *> triangles;
00058 
00059 
00060 Triangle::Triangle(Vertex *v0,Vertex *v1,Vertex *v2){
00061     assert(v0!=v1 && v1!=v2 && v2!=v0);
00062     vertex[0]=v0;
00063     vertex[1]=v1;
00064     vertex[2]=v2;
00065     ComputeNormal();
00066     triangles.Add(this);
00067     for(int i=0;i<3;i++) {
00068         vertex[i]->face.Add(this);
00069         for(int j=0;j<3;j++) if(i!=j) {
00070             vertex[i]->neighbor.AddUnique(vertex[j]);
00071         }
00072     }
00073 }
00074 Triangle::~Triangle(){
00075     int i;
00076     triangles.Remove(this);
00077     for(i=0;i<3;i++) {
00078         if(vertex[i]) vertex[i]->face.Remove(this);
00079     }
00080     for(i=0;i<3;i++) {
00081         int i2 = (i+1)%3;
00082         if(!vertex[i] || !vertex[i2]) continue;
00083         vertex[i ]->RemoveIfNonNeighbor(vertex[i2]);
00084         vertex[i2]->RemoveIfNonNeighbor(vertex[i ]);
00085     }
00086 }
00087 int Triangle::HasVertex(Vertex *v) {
00088     return (v==vertex[0] ||v==vertex[1] || v==vertex[2]);
00089 }
00090 void Triangle::ComputeNormal(){
00091     Vector v0=vertex[0]->position;
00092     Vector v1=vertex[1]->position;
00093     Vector v2=vertex[2]->position;
00094     normal = (v1-v0)*(v2-v1);
00095     if(magnitude(normal)==0)return;
00096     normal = normalize(normal);
00097 }
00098 void Triangle::ReplaceVertex(Vertex *vold,Vertex *vnew) {
00099     assert(vold && vnew);
00100     assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]);
00101     assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]);
00102     if(vold==vertex[0]){
00103         vertex[0]=vnew;
00104     }
00105     else if(vold==vertex[1]){
00106         vertex[1]=vnew;
00107     }
00108     else {
00109         assert(vold==vertex[2]);
00110         vertex[2]=vnew;
00111     }
00112     int i;
00113     vold->face.Remove(this);
00114     assert(!vnew->face.Contains(this));
00115     vnew->face.Add(this);
00116     for(i=0;i<3;i++) {
00117         vold->RemoveIfNonNeighbor(vertex[i]);
00118         vertex[i]->RemoveIfNonNeighbor(vold);
00119     }
00120     for(i=0;i<3;i++) {
00121         assert(vertex[i]->face.Contains(this)==1);
00122         for(int j=0;j<3;j++) if(i!=j) {
00123             vertex[i]->neighbor.AddUnique(vertex[j]);
00124         }
00125     }
00126     ComputeNormal();
00127 }
00128 
00129 Vertex::Vertex(Vector v,int _id) {
00130     position =v;
00131     id=_id;
00132     vertices.Add(this);
00133 }
00134 
00135 Vertex::~Vertex(){
00136     assert(face.num==0);
00137     while(neighbor.num) {
00138         neighbor[0]->neighbor.Remove(this);
00139         neighbor.Remove(neighbor[0]);
00140     }
00141     vertices.Remove(this);
00142 }
00143 void Vertex::RemoveIfNonNeighbor(Vertex *n) {
00144     // removes n from neighbor list if n isn't a neighbor.
00145     if(!neighbor.Contains(n)) return;
00146     for(int i=0;i<face.num;i++) {
00147         if(face[i]->HasVertex(n)) return;
00148     }
00149     neighbor.Remove(n);
00150 }
00151 
00152 
00153 float ComputeEdgeCollapseCost(Vertex *u,Vertex *v) {
00154     // if we collapse edge uv by moving u to v then how
00155     // much different will the model change, i.e. how much "error".
00156     // Texture, vertex normal, and border vertex code was removed
00157     // to keep this demo as simple as possible.
00158     // The method of determining cost was designed in order
00159     // to exploit small and coplanar regions for
00160     // effective polygon reduction.
00161     // Is is possible to add some checks here to see if "folds"
00162     // would be generated.  i.e. normal of a remaining face gets
00163     // flipped.  I never seemed to run into this problem and
00164     // therefore never added code to detect this case.
00165     int i;
00166     float edgelength = magnitude(v->position - u->position);
00167     float curvature=0;
00168 
00169     // find the "sides" triangles that are on the edge uv
00170     List<Triangle *> sides;
00171     for(i=0;i<u->face.num;i++) {
00172         if(u->face[i]->HasVertex(v)){
00173             sides.Add(u->face[i]);
00174         }
00175     }
00176     // use the triangle facing most away from the sides
00177     // to determine our curvature term
00178     for(i=0;i<u->face.num;i++) {
00179         float mincurv=1; // curve for face i and closer side to it
00180         for(int j=0;j<sides.num;j++) {
00181             // use dot product of face normals. '^' defined in vector
00182             float dotprod = u->face[i]->normal ^ sides[j]->normal;
00183             mincurv = min(mincurv,(1-dotprod)/2.0f);
00184         }
00185         curvature = max(curvature,mincurv);
00186     }
00187     // the more coplanar the lower the curvature term
00188     return edgelength * curvature;
00189 }
00190 
00191 void ComputeEdgeCostAtVertex(Vertex *v) {
00192     // compute the edge collapse cost for all edges that start
00193     // from vertex v.  Since we are only interested in reducing
00194     // the object by selecting the min cost edge at each step, we
00195     // only cache the cost of the least cost edge at this vertex
00196     // (in member variable collapse) as well as the value of the
00197     // cost (in member variable objdist).
00198     if(v->neighbor.num==0) {
00199         // v doesn't have neighbors so it costs nothing to collapse
00200         v->collapse=NULL;
00201         v->objdist=-0.01f;
00202         return;
00203     }
00204     v->objdist = 1000000;
00205     v->collapse=NULL;
00206     // search all neighboring edges for "least cost" edge
00207     for(int i=0;i<v->neighbor.num;i++) {
00208         float dist;
00209         dist = ComputeEdgeCollapseCost(v,v->neighbor[i]);
00210         if(dist<v->objdist) {
00211             v->collapse=v->neighbor[i];  // candidate for edge collapse
00212             v->objdist=dist;             // cost of the collapse
00213         }
00214     }
00215 }
00216 void ComputeAllEdgeCollapseCosts() {
00217     // For all the edges, compute the difference it would make
00218     // to the model if it was collapsed.  The least of these
00219     // per vertex is cached in each vertex object.
00220     for(int i=0;i<vertices.num;i++) {
00221         ComputeEdgeCostAtVertex(vertices[i]);
00222     }
00223 }
00224 
00225 void Collapse(Vertex *u,Vertex *v){
00226     // Collapse the edge uv by moving vertex u onto v
00227     // Actually remove tris on uv, then update tris that
00228     // have u to have v, and then remove u.
00229     if(!v) {
00230         // u is a vertex all by itself so just delete it
00231         delete u;
00232         return;
00233     }
00234     int i;
00235     List<Vertex *>tmp;
00236     // make tmp a list of all the neighbors of u
00237     for(i=0;i<u->neighbor.num;i++) {
00238         tmp.Add(u->neighbor[i]);
00239     }
00240     // delete triangles on edge uv:
00241     for(i=u->face.num-1;i>=0;i--) {
00242         if(u->face[i]->HasVertex(v)) {
00243             delete(u->face[i]);
00244         }
00245     }
00246     // update remaining triangles to have v instead of u
00247     for(i=u->face.num-1;i>=0;i--) {
00248         u->face[i]->ReplaceVertex(u,v);
00249     }
00250     delete u;
00251     // recompute the edge collapse costs for neighboring vertices
00252     for(i=0;i<tmp.num;i++) {
00253         ComputeEdgeCostAtVertex(tmp[i]);
00254     }
00255 }
00256 
00257 void AddVertex(List<Vector> &vert){
00258     for(int i=0;i<vert.num;i++) {
00259         new Vertex(vert[i],i);
00260     }
00261 }
00262 void AddFaces(List<tridata> &tri){
00263     for(int i=0;i<tri.num;i++) {
00264         new Triangle(
00265             vertices[tri[i].v[0]],
00266             vertices[tri[i].v[1]],
00267             vertices[tri[i].v[2]] );
00268     }
00269 }
00270 
00271 Vertex *MinimumCostEdge(){
00272     // Find the edge that when collapsed will affect model the least.
00273     // This funtion actually returns a Vertex, the second vertex
00274     // of the edge (collapse candidate) is stored in the vertex data.
00275     // Serious optimization opportunity here: this function currently
00276     // does a sequential search through an unsorted list :-(
00277     // Our algorithm could be O(n*lg(n)) instead of O(n*n)
00278     Vertex *mn=vertices[0];
00279     for(int i=0;i<vertices.num;i++) {
00280         if(vertices[i]->objdist < mn->objdist) {
00281             mn = vertices[i];
00282         }
00283     }
00284     return mn;
00285 }
00286 
00287 void ProgressiveMesh(List<Vector> &vert, List<tridata> &tri,
00288                      List<int> &map, List<int> &permutation)
00289 {
00290     AddVertex(vert);  // put input data into our data structures
00291     AddFaces(tri);
00292     ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs
00293     permutation.SetSize(vertices.num);  // allocate space
00294     map.SetSize(vertices.num);          // allocate space
00295     // reduce the object down to nothing:
00296     while(vertices.num > 0) {
00297         // get the next vertex to collapse
00298         Vertex *mn = MinimumCostEdge();
00299         // keep track of this vertex, i.e. the collapse ordering
00300         permutation[mn->id]=vertices.num-1;
00301         // keep track of vertex to which we collapse to
00302         map[vertices.num-1] = (mn->collapse)?mn->collapse->id:-1;
00303         // Collapse this edge
00304         Collapse(mn,mn->collapse);
00305     }
00306     // reorder the map list based on the collapse ordering
00307     for(int i=0;i<map.num;i++) {
00308         map[i] = (map[i]==-1)?0:permutation[map[i]];
00309     }
00310     // The caller of this function should reorder their vertices
00311     // according to the returned "permutation".
00312 }
00313 
00314 
00315 // ************************ vim: set sw=4 sts=4 et: ************************ //
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines