private static void ComputeEdgeCostAtVertex(ProgMeshVertex v) { // compute the edge collapse cost for all edges that start // from vertex v. Since we are only interested in reducing // the object by selecting the min cost edge at each step, we // only cache the cost of the least cost edge at this vertex // (in member variable collapse) as well as the value of the // cost (in member variable ObjDist). if (v.Neighbor.Count == 0) { // v doesn't have neighbors so it costs nothing to collapse v.Collapse = null; v.ObjDist = -0.01f; return; } v.ObjDist = Constants.FloatMax; v.Collapse = null; // search all neighboring edges for "least cost" edge foreach (var neighbor in v.Neighbor) { var dist = ComputeEdgeCollapseCost(v, neighbor); if (dist < v.ObjDist) { v.Collapse = neighbor; // candidate for edge collapse v.ObjDist = dist; // cost of the collapse } } }
private static float ComputeEdgeCollapseCost(ProgMeshVertex u, ProgMeshVertex v) { // if we collapse edge uv by moving u to v then how // much different will the model change, i.e. how much "error". // Texture, vertex normal, and border vertex code was removed // to keep this demo as simple as possible. // The method of determining cost was designed in order // to exploit small and coplanar regions for // effective polygon reduction. // Is is possible to add some checks here to see if "folds" // would be generated. i.e. normal of a remaining face gets // flipped. I never seemed to run into this problem and // therefore never added code to detect this case. // find the "sides" triangles that are on the edge uv var sides = new List <ProgMeshTriangle>(u.Face.Count); foreach (var face in u.Face) { if (face.HasVertex(v)) { sides.Add(face); } } // use the triangle facing most away from the sides // to determine our curvature term var curvature = 0f; foreach (var face in u.Face) { var minCurve = 1f; // curve for face i and closer side to it foreach (var side in sides) { // use dot product of face normals. var dotProd = face.Normal.Dot(side.Normal); minCurve = MathF.Min(minCurve, (1f - dotProd) * 0.5f); } curvature = MathF.Max(curvature, minCurve); } // the more coplanar the lower the curvature term var edgeLength = v.Position.Sub(u.Position).Magnitude(); return(edgeLength * curvature); }
public void RemoveIfNonNeighbor(ProgMeshVertex n) { // removes n from neighbor Array if n isn't a neighbor. if (!Neighbor.Contains(n)) { return; } foreach (var face in Face) { if (face.HasVertex(n)) { return; } } ProgMeshUtil.RemoveFillWithBack(Neighbor, n); }
private void Collapse(ProgMeshVertex u, ProgMeshVertex v) { int i; // Collapse the edge uv by moving vertex u onto v // Actually remove tris on uv, then update tris that // have u to have v, and then remove u. if (v == null) { // u is a vertex all by itself so just delete it u.Dispose(this); return; } var tmp = new ProgMeshVertex[u.Neighbor.Count]; // make tmp a Array of all the neighbors of u for (i = 0; i < tmp.Length; i++) { tmp[i] = u.Neighbor[i]; } // delete triangles on edge uv: i = u.Face.Count; while (i-- > 0) { if (u.Face[i].HasVertex(v)) { u.Face[i].Dispose(this); } } // update remaining triangles to have v instead of u i = u.Face.Count; while (i-- > 0) { u.Face[i].ReplaceVertex(u, v); } u.Dispose(this); // recompute the edge collapse costs for neighboring vertices foreach (var t in tmp) { ComputeEdgeCostAtVertex(t); } }
public void ReplaceVertex(ProgMeshVertex vOld, ProgMeshVertex vNew) { Debug.Assert(vOld != null && vNew != null, "[ProgMeshTriangle.ReplaceVertex] Arguments must not be null."); Debug.Assert(vOld == _vertex[0] || vOld == _vertex[1] || vOld == _vertex[2], "[ProgMeshTriangle.replaceVertex] vOld must not be included in this.vertex."); Debug.Assert(vNew != _vertex[0] && vNew != _vertex[1] && vNew != _vertex[2], "[ProgMeshTriangle.replaceVertex] vNew must not be included in this.vertex."); if (vOld == _vertex[0]) { _vertex[0] = vNew; } else if (vOld == _vertex[1]) { _vertex[1] = vNew; } else { Debug.Assert(vOld == _vertex[2], "[ProgMeshTriangle.ReplaceVertex] vOld == vertex[2]"); _vertex[2] = vNew; } ProgMeshUtil.RemoveFillWithBack(vOld.Face, this); Debug.Assert(!vNew.Face.Contains(this), "[ProgMeshTriangle.ReplaceVertex] !Contains(vNew->face, this)"); vNew.Face.Add(this); for (var i = 0; i < 3; i++) { vOld.RemoveIfNonNeighbor(_vertex[i]); _vertex[i].RemoveIfNonNeighbor(vOld); } for (var i = 0; i < 3; i++) { Debug.Assert(_vertex[i].Face.Count(f => f == this) == 1, "[ProgMeshTriangle.replaceVertex] Contains(vertex[i]->face, this) == 1"); for (var j = 0; j < 3; j++) { if (i != j) { ProgMeshUtil.AddUnique(_vertex[i].Neighbor, _vertex[j]); } } } ComputeNormal(); }
public ProgMeshTriangle(ProgMeshVertex v0, ProgMeshVertex v1, ProgMeshVertex v2) { Debug.Assert(v0 != null && v1 != null && v2 != null, "[ProgMeshTriangle] Vertices must not be null."); Debug.Assert(v0 != v1 && v1 != v2 && v2 != v0, "[ProgMeshTriangle] Vertices must be different."); _vertex = new[] { v0, v1, v2 }; ComputeNormal(); for (var i = 0; i < 3; i++) { _vertex[i].Face.Add(this); for (var j = 0; j < 3; j++) { if (i != j) { ProgMeshUtil.AddUnique(_vertex[i].Neighbor, _vertex[j]); } } } }
public bool HasVertex(ProgMeshVertex v) { return(v == _vertex[0] || v == _vertex[1] || v == _vertex[2]); }