public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; DTProfilerMarkers.Triangulation.Begin(); DTMesh triMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); DTConvexPolygroup triPolygroup = triMesh.ToPolygroup(); // Collider from polygon DTProfilerMarkers.HertelMehlhorn.Begin(); DTConvexPolygroup hmPolygroup = HertelMehlhorn.PolyPartitionHM.Instance.ExecuteToPolygroup(triPolygroup); DTProfilerMarkers.HertelMehlhorn.End(); DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(hmPolygroup); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon ApplyRenderMesh(triMesh); }
public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; DTProfilerMarkers.Triangulation.Begin(); DTMesh dtMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); // Collider from polygon DTProfilerMarkers.HertelMehlhorn.Begin(); DTMesh hmMesh = HertelMehlhorn.CustomHM.Instance.ExecuteToMesh(dtMesh); DTProfilerMarkers.HertelMehlhorn.End(); DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(hmMesh); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon ApplyRenderMesh(dtMesh); }
public DTMesh PolygonToMesh(DTPolygon subject) { // Mark any unmarked holes in the contour, otherwise Triangle.NET won't handle them properly subject = DTUtility.IdentifyHoles(subject); if (subject.Contour.Count < 3) { return(new DTMesh()); } // Don't triangulate if this is already a triangle else if (subject.Contour.Count == 3 && subject.Holes.Count == 0) { return(new DTMesh(subject.Contour, new List <List <int> >() { new List <int>() { 0, 1, 2 } })); } // If the polygon is convex, use simple fanning technique to triangulate else if (subject.IsConvex()) { DTMesh mesh = new DTMesh(subject.Contour, new List <List <int> >()); for (int i = 1; i < subject.Contour.Count - 1; i++) { mesh.Partitions.Add(new List <int>() { 0, i, i + 1 }); } return(mesh); } // Format polygon input and execute Polygon polygon = new Polygon(); polygon.Add(new Contour(subject.Contour.ToVertexList()), false); foreach (var hole in subject.Holes) { if (hole.Count >= 3) { try { polygon.Add(new Contour(hole.ToVertexList()), true); } catch (Exception) { } } } DTProfilerMarkers.TriangleNet.Begin(); IMesh triangleNetOutput = polygon.Triangulate(); ++callCount; DTProfilerMarkers.TriangleNet.End(); // Convert Triangle.NET output into DTMesh List <Vector2> vertices = triangleNetOutput.Vertices.ToVector2List(); List <List <int> > triangles = triangleNetOutput.Triangles.ToPartitionList(); return(new DTMesh(vertices, triangles)); }
public static DTConvexPolygroup ToPolygroup(this DTMesh mesh) { DTProfilerMarkers.MeshToPolygroup.Begin(); DTConvexPolygroup polygroup = new DTConvexPolygroup( mesh.Partitions.Select(part => part.Select(i => mesh.Vertices[i]).ToList()).ToList()); DTProfilerMarkers.MeshToPolygroup.End(); return(polygroup); }
protected void ApplyRenderMesh(DTMesh dtMesh) { if (dtRenderMesh == dtMesh) { return; } dtRenderMesh = dtMesh; MeshFilter mf = GetComponent <MeshFilter>(); mf.sharedMesh = new Mesh() { vertices = dtMesh.Vertices.Select(v => new Vector3(v.x, v.y)).ToArray(), uv = dtMesh.Vertices.ToArray(), triangles = dtMesh.Partitions.SelectMany(t => t.GetRange(0, 3).AsEnumerable().Reverse()).ToArray() }; }
protected void ApplyCollider(DTMesh dtMesh) { PolygonCollider2D polygonCollider = GetComponent <PolygonCollider2D>(); polygonCollider.pathCount = dtMesh.Partitions.Count; int i = 0; foreach (var partition in dtMesh.Partitions) { polygonCollider.SetPath(i++, partition.Select(vIndex => dtMesh.Vertices[vIndex]).ToArray()); } if (GetComponent <Rigidbody2D>().mass < MassCutoff) { Destroy(gameObject); } }
public override void ApplyPolygonList(List <DTPolygon> dtPolygonList) { if (dtPolygon == dtPolygonList[0]) { return; } dtPolygon = dtPolygonList[0]; // Collider from polygon DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(dtPolygon); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon DTProfilerMarkers.Triangulation.Begin(); DTMesh dtMesh = GetTriangulator().PolygonToMesh(dtPolygon); DTProfilerMarkers.Triangulation.End(); ApplyRenderMesh(dtMesh); }
public static DTMesh ToMesh(this DTConvexPolygroup polygroup) { DTProfilerMarkers.PolygroupToMesh.Begin(); // List of unique vertices for the mesh List <Vector2> vertices = new List <Vector2>(); // Maps each vertex to its index in the vertices list Dictionary <Vector2, int> vertexMap = new Dictionary <Vector2, int>(new ApproximateVector2Comparer()); // List of partitions, each of which is a list of vertex indices List <List <int> > partitions = new List <List <int> >(polygroup.Count); foreach (var poly in polygroup) { // Indices of the vertices in this partition List <int> indices = new List <int>(); foreach (var v in poly) { // Get the mapped index of the vertex. If the vertex is not yet mapped, map it to the index at the end // of the vertices list if (!vertexMap.TryGetValue(v, out int index)) { index = vertices.Count; vertexMap.Add(v, index); vertices.Add(v); } indices.Add(index); } partitions.Add(indices); } DTMesh mesh = new DTMesh(vertices, partitions); DTProfilerMarkers.PolygroupToMesh.End(); return(mesh); }
public override void ApplyPolygonList(List <DTPolygon> clippedPolygonList) { // The clipped polygons could potentially be concave or have holes, so we will triangulate each one before applying DTProfilerMarkers.Triangulation.Begin(); DTConvexPolygroup triangleList = DTUtility.TriangulateAll(clippedPolygonList, GetTriangulator()); DTProfilerMarkers.Triangulation.End(); // Our Hertel-Mehlhorn implementation takes a DTMesh, so convert before instead of after DTMesh triangulatedMesh = triangleList.ToMesh(); // Collider from polygon DTProfilerMarkers.HertelMehlhorn.Begin(); hmMesh = HertelMehlhorn.CustomHM.Instance.ExecuteToMesh(triangulatedMesh); DTProfilerMarkers.HertelMehlhorn.End(); // Collider from polygon DTProfilerMarkers.ApplyCollider.Begin(); ApplyCollider(hmMesh); DTProfilerMarkers.ApplyCollider.End(); // Create mesh from triangulated polygon ApplyRenderMesh(triangulatedMesh); }
public DTConvexPolygroup ExecuteToPolygroup(DTMesh input) { return(ExecuteToPolygroup(input.ToPolygroup())); }
public DTMesh ExecuteToMesh(DTMesh input) { return(ExecuteToMesh(input.ToPolygroup())); }
// Note: allows output to have consecutive colinear edges in a partition public DTMesh ExecuteToMesh(DTMesh input) { List <int> clearedPolys = new List <int>(); Dictionary <SimplifiedEdge, Edge> realEdges = new Dictionary <SimplifiedEdge, Edge>(); List <int>[] tempPartitions = new List <int> [input.Partitions.Count]; int[] polyRemapper = new int[input.Partitions.Count]; // Local function that gets the remapped polygon index for the given original index int getRemappedIndex(int i) { Stack <int> collapseStack = new Stack <int>(); while (i != polyRemapper[i]) { collapseStack.Push(i); i = polyRemapper[i]; } // Lazily collapse remapping chains (entries that remap multiple times should remap only once) while (collapseStack.Count > 0) { polyRemapper[collapseStack.Pop()] = i; } return(i); } // Determine internal edges and map them to their connected polygons for (int polyIndex = 0; polyIndex < input.Partitions.Count; polyIndex++) { List <int> poly = input.Partitions[polyIndex]; tempPartitions[polyIndex] = new List <int>(poly); polyRemapper[polyIndex] = polyIndex; // Add all the initial partitions (triangles if the input was a triangulation) for (int vertexIndex = 0; vertexIndex < poly.Count; vertexIndex++) { int v = poly[vertexIndex]; int vNext = poly.GetCircular(vertexIndex + 1); SimplifiedEdge e = new SimplifiedEdge(v, vNext); if (!realEdges.ContainsKey(e)) { // If this is the first time visiting this edge, make a new entry and add the right polygon realEdges[e] = new Edge(e, polyIndex); } else { // If we have already visited this edge, add the left polygon and make the edge bidirectional realEdges[e].IsBidirectional = true; realEdges[e].LeftPoly = polyIndex; } } } // Iterate internal edges and check if they can be removed without creating concave partitions foreach (var internalEdgePair in realEdges.Where(edgePair => edgePair.Value.IsBidirectional)) { SimplifiedEdge simpleEdge = internalEdgePair.Key; Edge realEdge = internalEdgePair.Value; // Find the index within the right polygon where the shared edge ends (P1) int rightPolyIndex = getRemappedIndex(realEdge.RightPoly); List <int> rightPoly = tempPartitions[rightPolyIndex]; int rightPolyStart = -1; for (int i = 0; i < rightPoly.Count; i++) { int p = rightPoly[i]; if (p == simpleEdge.P1) { rightPolyStart = i; break; } } // Find the index within the left polygon where the shared edge ends (P0) int leftPolyIndex = getRemappedIndex(realEdge.LeftPoly); List <int> leftPoly = tempPartitions[leftPolyIndex]; int leftPolyStart = -1; for (int i = 0; i < leftPoly.Count; i++) { int p = leftPoly[i]; if (p == simpleEdge.P0) { leftPolyStart = i; break; } } // Determine if the angle at the edge's P0 would become reflex if this edge were removed Vector2 rightPolyLastEdge = input.Vertices[rightPoly.GetCircular(rightPolyStart - 1)] - input.Vertices[rightPoly.GetCircular(rightPolyStart - 2)]; Vector2 leftPolyFirstEdge = input.Vertices[leftPoly.GetCircular(leftPolyStart + 1)] - input.Vertices[leftPoly.GetCircular(leftPolyStart)]; bool p0Reflex = Vector3.Cross(rightPolyLastEdge, leftPolyFirstEdge).z > 0; // Determine if the angle at the edge's P1 would become reflex if this edge were removed Vector2 leftPolyLastEdge = input.Vertices[leftPoly.GetCircular(leftPolyStart - 1)] - input.Vertices[leftPoly.GetCircular(leftPolyStart - 2)]; Vector2 rightPolyFirstEdge = input.Vertices[rightPoly.GetCircular(rightPolyStart + 1)] - input.Vertices[rightPoly.GetCircular(rightPolyStart)]; bool p1Reflex = Vector3.Cross(leftPolyLastEdge, rightPolyFirstEdge).z > 0; if (!p0Reflex && !p1Reflex) { // Remove the edge and merge the two polygons since the result will be convex // Add points from rightPoly in CW order from the removed edge's P1 to P0, including P1 but not P0 List <int> newPoly = rightPoly.GetRange(rightPolyStart, rightPoly.Count - rightPolyStart); newPoly.AddRange(rightPoly.GetRange(0, rightPolyStart)); newPoly.RemoveAt(newPoly.Count - 1); // Add points from left in CW order from the removed edge's P0 to P1, including P0 but not P1 newPoly.AddRange(leftPoly.GetRange(leftPolyStart, leftPoly.Count - leftPolyStart)); newPoly.AddRange(leftPoly.GetRange(0, leftPolyStart)); newPoly.RemoveAt(newPoly.Count - 1); // Modify the right polygon rightPoly.Clear(); rightPoly.AddRange(newPoly); // Clear the left polygon leftPoly.Clear(); clearedPolys.Add(leftPolyIndex); // Remap the left polygon index to match the right polyRemapper[leftPolyIndex] = rightPolyIndex; } } // Return a new mesh that copies the vertices of the original, but uses the merged partitions // (ignores the left partitions that we cleared) return(new DTMesh(new List <Vector2>(input.Vertices), tempPartitions.Where(partition => partition.Count > 0).ToList())); }