public static void RemoveDuplicates(List <Vertex> poly) { for (var i = 0; i < poly.Count; ++i) { if (poly[i].ApproximatelyEquals(poly[MeshUtils.LoopIndex(i + 1, poly.Count)])) { poly.RemoveAt(i--); } if (poly.Count <= 3) { break; } } }
public static List <List <Vertex> > GetEdgeVerticesIgnoreHoles(Mesh mesh, string debugName = null) { var edgeDict = new Dictionary <Tuple <int, int>, bool>(); var verts = mesh.vertices; var indices = mesh.triangles; for (var i = 0; i < indices.Length; i += 3) { for (var j = 0; j < 3; ++j) { var index = indices[i + j]; var nextIndex = indices[i + LoopIndex(j + 1, 3)]; edgeDict[new Tuple <int, int>(index, nextIndex)] = true; } } for (var i = 0; i < indices.Length; i += 3) { for (var j = 0; j < 3; ++j) { var index = indices[i + j]; var nextIndex = indices[i + LoopIndex(j + 1, 3)]; var key = new Tuple <int, int>(nextIndex, index); if (edgeDict.ContainsKey(key)) { edgeDict[key] = false; } } } var result = new List <List <Vertex> >(); var newVerts = new List <Vertex>(); var usedIndices = new List <int>(); bool TryFindBestUnusedVertex(out KeyValuePair <Tuple <int, int>, bool> unusedVert) { var found = false; KeyValuePair <Tuple <int, int>, bool> firstKvp; foreach (var kvp in edgeDict) { if (!kvp.Value || usedIndices.Contains(kvp.Key.Item1)) { continue; } var thisVert = verts[kvp.Key.Item1]; var pointIsInside = false; foreach (var finishedPoly in result) { if (IsPointInPolygon(finishedPoly, thisVert)) { usedIndices.Add(kvp.Key.Item1); pointIsInside = true; break; } } if (pointIsInside) { continue; } if (!found) { found = true; firstKvp = kvp; continue; } var firstVert = verts[firstKvp.Key.Item1]; if (thisVert.x < firstVert.x || Mathf.Approximately(thisVert.x, firstVert.x) && thisVert.z < firstVert.z) { firstKvp = kvp; } } unusedVert = found ? firstKvp : default; return(found); } var usedEdges = new List <KeyValuePair <Tuple <int, int>, bool> >(); while (TryFindBestUnusedVertex(out var bestKvp)) { var firstIndex = bestKvp.Key.Item1; var currentIndex = bestKvp.Key.Item2; var iteration = 0; newVerts.Add(new Vertex(verts[currentIndex])); usedIndices.Add(currentIndex); while (currentIndex != firstIndex) { var nextItem = edgeDict.FirstOrDefault(x => x.Value && x.Key.Item1 == currentIndex && !usedEdges.Contains(x)); var def = default(KeyValuePair <Tuple <int, int>, bool>); if (nextItem.Equals(def)) { // TODO: zero-surface vertices welded together break this, review var warningMsg = "Unsupported mesh shape found, falling back to convex hull."; if (!string.IsNullOrEmpty(debugName)) { warningMsg += $" ({debugName})"; } Debug.LogWarning(warningMsg); result.Clear(); var allVerts = verts.Select(x => new Vertex(x)).ToList(); result.Add(MeshUtils.GetConvexHull(allVerts)); return(result); } currentIndex = nextItem.Key.Item2; newVerts.Add(new Vertex(verts[currentIndex])); usedIndices.Add(currentIndex); usedEdges.Add(nextItem); if (++iteration > 65536) { throw new Exception("Infinite loop encountered when looking for external edges."); } } newVerts.Reverse(); result.Add(new List <Vertex>(newVerts)); newVerts.Clear(); } return(result); }
public static List <Triangle> TriangulateEarClipping(List <Vertex> vertices, string debugName = null, bool recursiveCall = false) { var tris = new List <Triangle>(); var volatileVertices = new List <Vertex>(vertices); if (volatileVertices.Count == 3) { tris.Add(new Triangle(volatileVertices[0], volatileVertices[1], volatileVertices[2])); return(tris); } for (var i = 0; i < volatileVertices.Count; i++) { volatileVertices[i].previous = volatileVertices[MeshUtils.LoopIndex(i - 1, volatileVertices.Count)]; volatileVertices[i].next = volatileVertices[MeshUtils.LoopIndex(i + 1, volatileVertices.Count)]; } var ears = new List <Vertex>(); foreach (var vert in volatileVertices) { MarkIfReflex(vert); MeshUtils.AddToListIfEar(vert, volatileVertices, ears); } while (true) { if (volatileVertices.Count == 3) { tris.Add(new Triangle(volatileVertices[0], volatileVertices[0].previous, volatileVertices[0].next)); break; } var index = -1; var minDist = float.MaxValue; for (var i = 0; i < ears.Count; ++i) { var point = ears[i]; var d01 = Vector3.Distance(point.Position, point.previous.Position); var d02 = Vector3.Distance(point.Position, point.next.Position); var d12 = Vector3.Distance(point.previous.Position, point.next.Position); var localMax = Mathf.Max(d01, Mathf.Max(d02, d12)); if (localMax < minDist) { minDist = localMax; index = i; } } if (ears.Count == 0 || index == -1) { if (recursiveCall) { var exceptionMsg = "Can't triangulate poly or its convex hull - terminating."; if (!string.IsNullOrEmpty(debugName)) { exceptionMsg += $" ({debugName})"; } throw new Exception(exceptionMsg); } var warningMsg = "Unsupported mesh shape found, falling back to convex hull."; if (!string.IsNullOrEmpty(debugName)) { warningMsg += $" ({debugName})"; } Debug.LogWarning(warningMsg); // Poly is invalid - create convex hull and return it instead var hull = MeshUtils.GetConvexHull(vertices); return(hull == null ? null : TriangulateEarClipping(hull, debugName, true)); } var current = ears[index]; var prev = current.previous; var next = current.next; tris.Add(new Triangle(current, prev, next)); ears.Remove(current); volatileVertices.Remove(current); prev.next = next; next.previous = prev; MarkIfReflex(prev); MarkIfReflex(next); ears.Remove(prev); ears.Remove(next); MeshUtils.AddToListIfEar(prev, volatileVertices, ears); MeshUtils.AddToListIfEar(next, volatileVertices, ears); } return(tris); }
private static void MarkIfReflex(Vertex v) { v.isReflex = MeshUtils.IsTriangleClockwise(v.previous, v, v.next); }