// compact triangles, compute edge error and build reference list void Update_mesh(int iteration) { if (iteration > 0) // compact triangles { int dst = 0; for (int i = 0; i < triangles.Count; i++) { if (triangles[i].deleted == 0) { triangles[dst++] = triangles[i]; } } } // // Init Quadrics by Plane & Edge Errors // // required at the beginning ( iteration == 0 ) // recomputing during the simplification is not required, // but mostly improves the result for closed meshes // if (iteration == 0) { for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; v.q = new SymetricMatrix(0.0); vertices[i] = v; } for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; Vector3 n = new Vector3(); Vector3[] p = new Vector3[3]; for (int j = 0; j < 3; j++) { p[j] = vertices[t.v[j]].p; } n = Vector3.Cross(p[1] - p[0], p[2] - p[0]); n.Normalize(); t.n = n; for (int j = 0; j < 3; j++) { Vertex v = vertices[t.v[j]]; v.q = vertices[t.v[j]].q + new SymetricMatrix(n.X, n.Y, n.Z, -Vector3.Dot(n, p[0])); vertices[t.v[j]] = v; } triangles[i] = t; } for (int i = 0; i < triangles.Count; i++) { // Calc Edge Error Triangle t = triangles[i]; Vector3 p = new Vector3(); for (int j = 0; j < 3; j++) { t.err[j] = Calculate_error(t.v[j], t.v[(j + 1) % 3], ref p); } t.err[3] = Math.Min(t.err[0], Math.Min(t.err[1], t.err[2])); triangles[i] = t; } } // Init Reference ID list for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; v.tstart = 0; v.tcount = 0; vertices[i] = v; } for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; for (int j = 0; j < 3; i++) { Vertex v = vertices[t.v[j]]; v.tcount++; vertices[t.v[j]] = v; } } int tstart = 0; for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; v.tstart = tstart; tstart += v.tcount; v.tcount = 0; vertices[i] = v; } // Write References for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; for (int j = 0; j < 3; j++) { Vertex v = vertices[t.v[j]]; Ref r = refs[v.tstart + v.tcount]; r.tid = i; r.tvertex = j; refs[v.tstart + v.tcount] = r; v.tcount++; vertices[t.v[j]] = v; } triangles[i] = t; } // Identify boundary : vertices[].border=0,1 if (iteration == 0) { ListHelper <int> vcount = new ListHelper <int>(); ListHelper <int> vids = new ListHelper <int>(); for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; v.border = 0; vertices[i] = v; } for (int i = 0; i < vertices.Count; i++) { Vertex v = vertices[i]; vcount.Clear(); vids.Clear(); for (int j = 0; j < v.tcount; j++) { int kb = refs[v.tstart + j].tid; Triangle t = triangles[kb]; for (int k = 0; k < 3; k++) { int ofs = 0, id = t.v[k]; while (ofs < vcount.Count) { if (vids[ofs] == id) { break; } ofs++; } if (ofs == vcount.Count) { vcount.Add(1); vids.Add(id); } else { vcount[ofs]++; } } triangles[kb] = t; } for (int j = 0; j < vcount.Count; j++) { if (vcount[j] == 1) { Vertex vt = vertices[vids[j]]; vt.border = 1; vertices[vids[j]] = vt; } } } } }
// Main simplification function // // target_count : target nr. of triangles // agressiveness : sharpness to increase the threashold. // 5..8 are good numbers // more iterations yield higher quality // void Simplify_mesh(int target_count, double agressiveness = 7) { // main iteration loop int deleted_triangles = 0; ListHelper <int> deleted0 = new ListHelper <int>(); ListHelper <int> deleted1 = new ListHelper <int>(); int triangle_count = triangles.Count; for (int iteration = 0; iteration < 100; iteration++) { // target number of triangles reached ? Then break if (triangle_count - deleted_triangles <= target_count) { break; } // update mesh once in a while if (iteration % 5 == 0) { Update_mesh(iteration); } // clear dirty flag for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; t.dirty = 0; triangles[i] = t; } // // All triangles with edges below the threshold will be removed // // The following numbers works well for most models. // If it does not, try to adjust the 3 parameters // double threshold = 0.000000001 * Math.Pow((double)(iteration + 3), agressiveness); // remove vertices & mark deleted triangles for (int i = 0; i < triangles.Count; i++) { Triangle t = triangles[i]; if (t.err[3] > threshold) { continue; } if (t.deleted != 0) { continue; } if (t.dirty != 0) { continue; } for (int j = 0; j < 3; j++) { if (t.err[j] < threshold) { int i0 = t.v[j]; Vertex v0 = vertices[i0]; int i1 = t.v[(j + 1) % 3]; Vertex v1 = vertices[i1]; // Border check if (v0.border != v1.border) { continue; } // Compute vertex to collapse to Vector3 p = new Vector3(); Calculate_error(i0, i1, ref p); // dont remove if flipped if (Flipped(p, i0, i1, ref v0, ref v1, ref deleted0)) { continue; } if (Flipped(p, i1, i0, ref v1, ref v0, ref deleted1)) { continue; } // not flipped, so remove edge v0.p = p; v0.q = v1.q + v0.q; int tstart = refs.Count; Update_triangles(i0, ref v0, ref deleted0, ref deleted_triangles); Update_triangles(i0, ref v1, ref deleted1, ref deleted_triangles); vertices[i0] = v0; vertices[i1] = v1; int tcount = refs.Count - tstart; if (tcount <= v0.tcount) { // save ram if (tcount != 0) { for (int f = 0; f < tcount; f++) { refs[v0.tstart + f] = refs[tstart]; } } } else { // append v0.tstart = tstart; } v0.tcount = tcount; break; } } triangles[i] = t; // done? if (triangle_count - deleted_triangles <= target_count) { break; } } } // clean up mesh Compact_mesh(); }