public void CreateSprite(Core.SpriteMesh spriteMesh, Sprite parentSprite, Transform parentTransform, string layer, int order, Color color) { var fragmentSprite = Sprite.Instantiate(parentSprite, parentTransform.position, parentTransform.rotation) as Sprite; var min = parentSprite.bounds.min; var max = parentSprite.bounds.max; var verts = new Vector2[spriteMesh.vertices.Length]; for (int i = 0; i < spriteMesh.vertices.Length; i++) { var v = spriteMesh.vertices[i]; var nx = (v.x - min.x) / (max.x - min.x); var ny = (v.y - min.y) / (max.y - min.y); nx = Mathf.Clamp01(nx); ny = Mathf.Clamp01(ny); verts[i] = new Vector2(nx * parentSprite.rect.width, ny * parentSprite.rect.height); } fragmentSprite.OverrideGeometry(verts, spriteMesh.uTriangles); spriteRenderer.sprite = fragmentSprite; spriteRenderer.sortingLayerName = layer; spriteRenderer.sortingOrder = order; spriteRenderer.color = color; }
/// <summary> /// generate collider path based on mesh vertices /// </summary> public static void GeneratePolygonCollider(PolygonCollider2D collider, SpriteMesh mesh) { if (mesh != null && collider) { var vertices = mesh.vertices; var path = new Vector2[vertices.Length]; for (int i = 0; i < vertices.Length; i++) { path[i] = vertices[i]; } var hull = Utils.Hull2D.ChainHull2D(path); collider.SetPath(0, hull); } }
/// <summary> /// find and isolate independent (not connecting) parts in a mesh /// </summary> public static List <CutterMesh> IsolateMeshIslands(SpriteMesh mesh) { var triangles = mesh.triangles; var vertexCount = mesh.vertices.Length; // cache mesh data var trianglesNum = mesh.triangles.Length; var vertices = mesh.vertices; if (trianglesNum <= 3) { return(null); } Exploder2DUtils.Assert(trianglesNum > 3, "IsolateMeshIslands error: " + trianglesNum); var lsHash = new LSHash(0.1f, vertexCount); var vertHash = new int[trianglesNum]; for (int i = 0; i < trianglesNum; i++) { vertHash[i] = lsHash.Hash(vertices[triangles[i]]); } var islands = new List <HashSet <int> > { new HashSet <int> { vertHash[0], vertHash[1], vertHash[2] } }; var islandsIdx = new List <List <int> > { new List <int>(trianglesNum) { 0, 1, 2 } }; var triVisited = new bool[trianglesNum]; triVisited[0] = true; triVisited[1] = true; triVisited[2] = true; var currIsland = islands[0]; var currIslandIdx = islandsIdx[0]; var counter = 3; var lastInvalidIdx = -1; var loopCounter = 0; while (true) { var foundIsland = false; for (int j = 3; j < trianglesNum; j += 3) { if (triVisited[j]) { continue; } if (currIsland.Contains(vertHash[j]) || currIsland.Contains(vertHash[j + 1]) || currIsland.Contains(vertHash[j + 2])) { currIsland.Add(vertHash[j]); currIsland.Add(vertHash[j + 1]); currIsland.Add(vertHash[j + 2]); currIslandIdx.Add(j); currIslandIdx.Add(j + 1); currIslandIdx.Add(j + 2); triVisited[j] = true; triVisited[j + 1] = true; triVisited[j + 2] = true; counter += 3; foundIsland = true; } else { lastInvalidIdx = j; } } if (counter == trianglesNum) { break; } if (!foundIsland) { // create new island currIsland = new HashSet <int> { vertHash[lastInvalidIdx], vertHash[lastInvalidIdx + 1], vertHash[lastInvalidIdx + 2] }; currIslandIdx = new List <int>(trianglesNum / 2) { lastInvalidIdx, lastInvalidIdx + 1, lastInvalidIdx + 2 }; islands.Add(currIsland); islandsIdx.Add(currIslandIdx); } loopCounter++; if (loopCounter > 100) { Exploder2DUtils.Log("10000 loop exceeded, islands: " + islands.Count); break; } } var islandNum = islands.Count; Exploder2DUtils.Assert(islandNum >= 1, "No island found!"); // no more than one islands if (islandNum == 1) { return(null); } var result = new List <CutterMesh>(islands.Count); foreach (var island in islandsIdx) { var cutterMesh = new CutterMesh { mesh = new SpriteMesh() }; var triCount = island.Count; var m = cutterMesh.mesh; var tt = new List <int>(triCount); var vs = new List <Vector2>(triCount); var triCache = new Dictionary <int, int>(trianglesNum); var centroid = Vector2.zero; var centroidCounter = 0; var triCounter = 0; foreach (var i in island) { var tri = triangles[i]; var id = 0; if (triCache.TryGetValue(tri, out id)) { tt.Add(id); continue; } tt.Add(triCounter); triCache.Add(tri, triCounter); triCounter++; centroid += vertices[tri]; centroidCounter++; vs.Add(vertices[tri]); } m.vertices = vs.ToArray(); m.triangles = tt.ToArray(); cutterMesh.centroidLocal = centroid / centroidCounter; result.Add(cutterMesh); } return(result); }
/// <summary> /// cut mesh by plane /// </summary> /// <param name="spriteMesh">mesh to cut</param> /// <param name="meshTransform">transformation of the mesh</param> /// <param name="line2D">cutting plane</param> /// <returns>processing time</returns> public float Cut(SpriteMesh spriteMesh, Transform meshTransform, Math.Line2D line2D, ref List <CutterMesh> meshes) { var stopWatch = new Stopwatch(); stopWatch.Start(); #if PROFILING MeasureIt.Begin("CutAllocations"); #endif // cache mesh data var trianglesNum = spriteMesh.triangles.Length; var verticesNum = spriteMesh.vertices.Length; var meshTriangles = spriteMesh.triangles; var meshVertices = spriteMesh.vertices; // preallocate buffers AllocateBuffers(trianglesNum, verticesNum); CutterMesh mesh0, mesh1; #if PROFILING MeasureIt.End("CutAllocations"); MeasureIt.Begin("CutCycleFirstPass"); #endif // inverse transform cutting plane // line2D.InverseTransform(meshTransform); // first pass - find complete triangles on both sides of the plane for (int i = 0; i < trianglesNum; i += 3) { // get triangle points var v0 = meshVertices[meshTriangles[i]]; var v1 = meshVertices[meshTriangles[i + 1]]; var v2 = meshVertices[meshTriangles[i + 2]]; var side0 = line2D.GetSideFix(ref v0); var side1 = line2D.GetSideFix(ref v1); var side2 = line2D.GetSideFix(ref v2); meshVertices[meshTriangles[i]] = v0; meshVertices[meshTriangles[i + 1]] = v1; meshVertices[meshTriangles[i + 2]] = v2; // Utils.Log(plane.Pnt + " " + v0 + " " + v1 + " " + " " + v2); // all points on one side if (side0 == side1 && side1 == side2) { var idx = side0 ? 0 : 1; if (meshTriangles[i] >= triCache.Length) { Exploder2DUtils.Log("TriCacheError " + meshTriangles[i] + " " + triCache.Length + " " + meshVertices.Length); } if (triCache[meshTriangles[i]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i]]); centroid[idx] += meshVertices[meshTriangles[i]]; triCache[meshTriangles[i]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i]] - 1); } if (triCache[meshTriangles[i + 1]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 1]]); centroid[idx] += meshVertices[meshTriangles[i + 1]]; triCache[meshTriangles[i + 1]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 1]] - 1); } if (triCache[meshTriangles[i + 2]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 2]]); centroid[idx] += meshVertices[meshTriangles[i + 2]]; triCache[meshTriangles[i + 2]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 2]] - 1); } } else { // intersection triangles add to list and process it in second pass cutTris.Add(i); } } if (vertices[0].Count == 0) { centroid[0] = meshVertices[0]; } else { centroid[0] /= vertices[0].Count; } if (vertices[1].Count == 0) { centroid[1] = meshVertices[1]; } else { centroid[1] /= vertices[1].Count; } #if PROFILING MeasureIt.End("CutCycleFirstPass"); MeasureIt.Begin("CutCycleSecondPass"); #endif mesh0.centroidLocal = centroid[0]; mesh1.centroidLocal = centroid[1]; mesh0.mesh = null; mesh1.mesh = null; if (cutTris.Count < 1) { stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } AllocateContours(cutTris.Count); // second pass - cut intersecting triangles in half foreach (var cutTri in cutTris) { var triangle = new Triangle { ids = new[] { meshTriangles[cutTri + 0], meshTriangles[cutTri + 1], meshTriangles[cutTri + 2] }, pos = new[] { meshVertices[meshTriangles[cutTri + 0]], meshVertices[meshTriangles[cutTri + 1]], meshVertices[meshTriangles[cutTri + 2]] }, }; // check points with a plane var side0 = line2D.GetSide(triangle.pos[0]); var side1 = line2D.GetSide(triangle.pos[1]); var side2 = line2D.GetSide(triangle.pos[2]); float t0, t1; Vector2 s0 = Vector2.zero, s1 = Vector2.zero; var idxLeft = side0 ? 0 : 1; var idxRight = 1 - idxLeft; if (side0 == side1) { var a = line2D.IntersectSegment(triangle.pos[2], triangle.pos[0], out t0, ref s0); var b = line2D.IntersectSegment(triangle.pos[2], triangle.pos[1], out t1, ref s1); Exploder2DUtils.Assert(a && b, "!!!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle.ids[2], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft]); var s1Left = AddIntersectionPoint(s1, triangle.ids[2], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft]); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft]); var v1Left = AddTrianglePoint(triangle.pos[1], triangle.ids[1], triCache, cornerVertCache[idxLeft], vertices[idxLeft]); // Triangle (s0, v0, s1) triangles[idxLeft].Add(s0Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // Triangle (s1, v0, v1) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(v1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle.ids[2], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight]); var s1Right = AddIntersectionPoint(s1, triangle.ids[2], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight]); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight]); // Triangle (v2, s0, s1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); } else if (side0 == side2) { var a = line2D.IntersectSegment(triangle.pos[1], triangle.pos[0], out t0, ref s1); var b = line2D.IntersectSegment(triangle.pos[1], triangle.pos[2], out t1, ref s0); Exploder2DUtils.Assert(a && b, "!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle.ids[1], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft]); var s1Left = AddIntersectionPoint(s1, triangle.ids[1], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft]); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft]); var v2Left = AddTrianglePoint(triangle.pos[2], triangle.ids[2], triCache, cornerVertCache[idxLeft], vertices[idxLeft]); // Triangle (v2, s1, s0) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(s0Left); // Triangle (v2, v0, s1) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle.ids[1], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight]); var s1Right = AddIntersectionPoint(s1, triangle.ids[1], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight]); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight]); // Triangle (s0, s1, v1) triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); } else { var a = line2D.IntersectSegment(triangle.pos[0], triangle.pos[1], out t0, ref s0); var b = line2D.IntersectSegment(triangle.pos[0], triangle.pos[2], out t1, ref s1); Exploder2DUtils.Assert(a && b, "!!!!!!!!!!!!!"); // right side ... 2 triangles var s0Right = AddIntersectionPoint(s0, triangle.ids[0], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight]); var s1Right = AddIntersectionPoint(s1, triangle.ids[0], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight]); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight]); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight]); // Triangle (v2, s1, v1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // Triangle (s1, s0, v1) triangles[idxRight].Add(s1Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(v1Right); // left side ... 1 triangle var s0Left = AddIntersectionPoint(s0, triangle.ids[0], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft]); var s1Left = AddIntersectionPoint(s1, triangle.ids[0], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft]); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft]); // Triangle (s1, v0, s0) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s0Left); } } #if PROFILING MeasureIt.End("CutCycleSecondPass"); #endif List <int>[] trianglesCut = null; if (vertices[0].Count > 3 && vertices[1].Count > 3) { #if PROFILING MeasureIt.Begin("CutEndCopyBack"); #endif mesh0.mesh = new SpriteMesh(); mesh1.mesh = new SpriteMesh(); var verticesArray0 = vertices[0].ToArray(); var verticesArray1 = vertices[1].ToArray(); mesh0.mesh.vertices = verticesArray0; mesh1.mesh.vertices = verticesArray1; if (trianglesCut != null && trianglesCut[0].Count > 3) { triangles[0].AddRange(trianglesCut[0]); triangles[1].AddRange(trianglesCut[1]); } mesh0.mesh.triangles = triangles[0].ToArray(); mesh1.mesh.triangles = triangles[1].ToArray(); mesh0.centroidLocal = Vector2.zero; mesh1.centroidLocal = Vector2.zero; foreach (var p in vertices[0]) { mesh0.centroidLocal += p; } mesh0.centroidLocal /= vertices[0].Count; foreach (var p in vertices[1]) { mesh1.centroidLocal += p; } mesh1.centroidLocal /= vertices[1].Count; #if PROFILING MeasureIt.End("CutEndCopyBack"); #endif meshes = new List <CutterMesh> { mesh0, mesh1 }; stopWatch.Stop(); return(stopWatch.ElapsedMilliseconds); } stopWatch.Stop(); // UnityEngine.Debug.Log("Empty cut! " + vertices[0].Count + " " + vertices[1].Count); return(stopWatch.ElapsedMilliseconds); }