2 * Progressive Mesh type Polygon Reduction Algorithm
\r
3 * by Stan Melax (c) 1998
\r
4 * Permission to use any of this code wherever you want is granted..
\r
5 * Although, please do acknowledge authorship if appropriate.
\r
7 * See the header file progmesh.h for a description of this module
\r
14 //#include <windows.h>
\r
18 #include "progmesh.h"
\r
20 #define min(x,y) (((x) <= (y)) ? (x) : (y))
\r
21 #define max(x,y) (((x) >= (y)) ? (x) : (y))
\r
25 * For the polygon reduction algorithm we use data structures
\r
26 * that contain a little bit more information than the usual
\r
27 * indexed face set type of data structure.
\r
28 * From a vertex we wish to be able to quickly get the
\r
29 * neighboring faces and vertices.
\r
36 Vertex * vertex[3]; // the 3 points that make this tri
\r
37 Vector normal; // unit vector othogonal to this face
\r
38 Triangle(Vertex *v0,Vertex *v1,Vertex *v2);
\r
40 void ComputeNormal();
\r
41 void ReplaceVertex(Vertex *vold,Vertex *vnew);
\r
42 int HasVertex(Vertex *v);
\r
46 Vector position; // location of point in euclidean space
\r
47 int id; // place of vertex in original list
\r
48 List<Vertex *> neighbor; // adjacent vertices
\r
49 List<Triangle *> face; // adjacent triangles
\r
50 float objdist; // cached cost of collapsing edge
\r
51 Vertex * collapse; // candidate vertex for collapse
\r
52 Vertex(Vector v,int _id);
\r
54 void RemoveIfNonNeighbor(Vertex *n);
\r
56 List<Vertex *> vertices;
\r
57 List<Triangle *> triangles;
\r
60 Triangle::Triangle(Vertex *v0,Vertex *v1,Vertex *v2){
\r
61 assert(v0!=v1 && v1!=v2 && v2!=v0);
\r
66 triangles.Add(this);
\r
67 for(int i=0;i<3;i++) {
\r
68 vertex[i]->face.Add(this);
\r
69 for(int j=0;j<3;j++) if(i!=j) {
\r
70 vertex[i]->neighbor.AddUnique(vertex[j]);
\r
74 Triangle::~Triangle(){
\r
76 triangles.Remove(this);
\r
78 if(vertex[i]) vertex[i]->face.Remove(this);
\r
82 if(!vertex[i] || !vertex[i2]) continue;
\r
83 vertex[i ]->RemoveIfNonNeighbor(vertex[i2]);
\r
84 vertex[i2]->RemoveIfNonNeighbor(vertex[i ]);
\r
87 int Triangle::HasVertex(Vertex *v) {
\r
88 return (v==vertex[0] ||v==vertex[1] || v==vertex[2]);
\r
90 void Triangle::ComputeNormal(){
\r
91 Vector v0=vertex[0]->position;
\r
92 Vector v1=vertex[1]->position;
\r
93 Vector v2=vertex[2]->position;
\r
94 normal = (v1-v0)*(v2-v1);
\r
95 if(magnitude(normal)==0)return;
\r
96 normal = normalize(normal);
\r
98 void Triangle::ReplaceVertex(Vertex *vold,Vertex *vnew) {
\r
99 assert(vold && vnew);
\r
100 assert(vold==vertex[0] || vold==vertex[1] || vold==vertex[2]);
\r
101 assert(vnew!=vertex[0] && vnew!=vertex[1] && vnew!=vertex[2]);
\r
102 if(vold==vertex[0]){
\r
105 else if(vold==vertex[1]){
\r
109 assert(vold==vertex[2]);
\r
113 vold->face.Remove(this);
\r
114 assert(!vnew->face.Contains(this));
\r
115 vnew->face.Add(this);
\r
117 vold->RemoveIfNonNeighbor(vertex[i]);
\r
118 vertex[i]->RemoveIfNonNeighbor(vold);
\r
121 assert(vertex[i]->face.Contains(this)==1);
\r
122 for(int j=0;j<3;j++) if(i!=j) {
\r
123 vertex[i]->neighbor.AddUnique(vertex[j]);
\r
129 Vertex::Vertex(Vector v,int _id) {
\r
132 vertices.Add(this);
\r
136 assert(face.num==0);
\r
137 while(neighbor.num) {
\r
138 neighbor[0]->neighbor.Remove(this);
\r
139 neighbor.Remove(neighbor[0]);
\r
141 vertices.Remove(this);
\r
143 void Vertex::RemoveIfNonNeighbor(Vertex *n) {
\r
144 // removes n from neighbor list if n isn't a neighbor.
\r
145 if(!neighbor.Contains(n)) return;
\r
146 for(int i=0;i<face.num;i++) {
\r
147 if(face[i]->HasVertex(n)) return;
\r
149 neighbor.Remove(n);
\r
153 float ComputeEdgeCollapseCost(Vertex *u,Vertex *v) {
\r
154 // if we collapse edge uv by moving u to v then how
\r
155 // much different will the model change, i.e. how much "error".
\r
156 // Texture, vertex normal, and border vertex code was removed
\r
157 // to keep this demo as simple as possible.
\r
158 // The method of determining cost was designed in order
\r
159 // to exploit small and coplanar regions for
\r
160 // effective polygon reduction.
\r
161 // Is is possible to add some checks here to see if "folds"
\r
162 // would be generated. i.e. normal of a remaining face gets
\r
163 // flipped. I never seemed to run into this problem and
\r
164 // therefore never added code to detect this case.
\r
166 float edgelength = magnitude(v->position - u->position);
\r
169 // find the "sides" triangles that are on the edge uv
\r
170 List<Triangle *> sides;
\r
171 for(i=0;i<u->face.num;i++) {
\r
172 if(u->face[i]->HasVertex(v)){
\r
173 sides.Add(u->face[i]);
\r
176 // use the triangle facing most away from the sides
\r
177 // to determine our curvature term
\r
178 for(i=0;i<u->face.num;i++) {
\r
179 float mincurv=1; // curve for face i and closer side to it
\r
180 for(int j=0;j<sides.num;j++) {
\r
181 // use dot product of face normals. '^' defined in vector
\r
182 float dotprod = u->face[i]->normal ^ sides[j]->normal;
\r
183 mincurv = min(mincurv,(1-dotprod)/2.0f);
\r
185 curvature = max(curvature,mincurv);
\r
187 // the more coplanar the lower the curvature term
\r
188 return edgelength * curvature;
\r
191 void ComputeEdgeCostAtVertex(Vertex *v) {
\r
192 // compute the edge collapse cost for all edges that start
\r
193 // from vertex v. Since we are only interested in reducing
\r
194 // the object by selecting the min cost edge at each step, we
\r
195 // only cache the cost of the least cost edge at this vertex
\r
196 // (in member variable collapse) as well as the value of the
\r
197 // cost (in member variable objdist).
\r
198 if(v->neighbor.num==0) {
\r
199 // v doesn't have neighbors so it costs nothing to collapse
\r
204 v->objdist = 1000000;
\r
206 // search all neighboring edges for "least cost" edge
\r
207 for(int i=0;i<v->neighbor.num;i++) {
\r
209 dist = ComputeEdgeCollapseCost(v,v->neighbor[i]);
\r
210 if(dist<v->objdist) {
\r
211 v->collapse=v->neighbor[i]; // candidate for edge collapse
\r
212 v->objdist=dist; // cost of the collapse
\r
216 void ComputeAllEdgeCollapseCosts() {
\r
217 // For all the edges, compute the difference it would make
\r
218 // to the model if it was collapsed. The least of these
\r
219 // per vertex is cached in each vertex object.
\r
220 for(int i=0;i<vertices.num;i++) {
\r
221 ComputeEdgeCostAtVertex(vertices[i]);
\r
225 void Collapse(Vertex *u,Vertex *v){
\r
226 // Collapse the edge uv by moving vertex u onto v
\r
227 // Actually remove tris on uv, then update tris that
\r
228 // have u to have v, and then remove u.
\r
230 // u is a vertex all by itself so just delete it
\r
236 // make tmp a list of all the neighbors of u
\r
237 for(i=0;i<u->neighbor.num;i++) {
\r
238 tmp.Add(u->neighbor[i]);
\r
240 // delete triangles on edge uv:
\r
241 for(i=u->face.num-1;i>=0;i--) {
\r
242 if(u->face[i]->HasVertex(v)) {
\r
243 delete(u->face[i]);
\r
246 // update remaining triangles to have v instead of u
\r
247 for(i=u->face.num-1;i>=0;i--) {
\r
248 u->face[i]->ReplaceVertex(u,v);
\r
251 // recompute the edge collapse costs for neighboring vertices
\r
252 for(i=0;i<tmp.num;i++) {
\r
253 ComputeEdgeCostAtVertex(tmp[i]);
\r
257 void AddVertex(List<Vector> &vert){
\r
258 for(int i=0;i<vert.num;i++) {
\r
259 new Vertex(vert[i],i);
\r
262 void AddFaces(List<tridata> &tri){
\r
263 for(int i=0;i<tri.num;i++) {
\r
265 vertices[tri[i].v[0]],
\r
266 vertices[tri[i].v[1]],
\r
267 vertices[tri[i].v[2]] );
\r
271 Vertex *MinimumCostEdge(){
\r
272 // Find the edge that when collapsed will affect model the least.
\r
273 // This funtion actually returns a Vertex, the second vertex
\r
274 // of the edge (collapse candidate) is stored in the vertex data.
\r
275 // Serious optimization opportunity here: this function currently
\r
276 // does a sequential search through an unsorted list :-(
\r
277 // Our algorithm could be O(n*lg(n)) instead of O(n*n)
\r
278 Vertex *mn=vertices[0];
\r
279 for(int i=0;i<vertices.num;i++) {
\r
280 if(vertices[i]->objdist < mn->objdist) {
\r
287 void ProgressiveMesh(List<Vector> &vert, List<tridata> &tri,
\r
288 List<int> &map, List<int> &permutation)
\r
290 AddVertex(vert); // put input data into our data structures
\r
292 ComputeAllEdgeCollapseCosts(); // cache all edge collapse costs
\r
293 permutation.SetSize(vertices.num); // allocate space
\r
294 map.SetSize(vertices.num); // allocate space
\r
295 // reduce the object down to nothing:
\r
296 while(vertices.num > 0) {
\r
297 // get the next vertex to collapse
\r
298 Vertex *mn = MinimumCostEdge();
\r
299 // keep track of this vertex, i.e. the collapse ordering
\r
300 permutation[mn->id]=vertices.num-1;
\r
301 // keep track of vertex to which we collapse to
\r
302 map[vertices.num-1] = (mn->collapse)?mn->collapse->id:-1;
\r
303 // Collapse this edge
\r
304 Collapse(mn,mn->collapse);
\r
306 // reorder the map list based on the collapse ordering
\r
307 for(int i=0;i<map.num;i++) {
\r
308 map[i] = (map[i]==-1)?0:permutation[map[i]];
\r
310 // The caller of this function should reorder their vertices
\r
311 // according to the returned "permutation".
\r