Пример #1
0
        public int Hash(Vector3 p)
        {
            for (int i = 0; i < count; i++)
            {
                var item = buckets[i];

                float diffX  = p.x - item.x;
                float diffY  = p.y - item.y;
                float diffZ  = p.z - item.z;
                float sqrMag = diffX * diffX + diffY * diffY + diffZ * diffZ;

                if (sqrMag < bucketSize2)
                {
                    return(i);
                }
            }

            if (count >= buckets.Length)
            {
                Exploder2DUtils.Log("Hash out of range: " + count + " " + buckets.Length);
                return(count - 1);
            }

            buckets[count++] = p;
            return(count - 1);
        }
Пример #2
0
        private void ExplodeObject(GameObject gameObject)
        {
            var exploder = Exploder2D.Utils.Exploder2DSingleton.Exploder2DInstance;

            exploder.transform.position = Exploder2DUtils.GetCentroid(gameObject);
            exploder.Radius             = 1.0f;
            exploder.Explode();
        }
Пример #3
0
        public void Remove(int key)
        {
            Exploder2DUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size);
            Exploder2DUtils.Assert(dictionary[key].valid == true, "Key does not exist!");
            dictionary[key].valid = false;

            Count--;
        }
Пример #4
0
 private void explode()
 {
     // Blow up comet
     Exploder2DUtils.SetActive(exploder.gameObject, true);
     exploder.transform.position = Exploder2DUtils.GetCentroid(gameObject);
     GameObject.FindGameObjectWithTag(Constants.EXPLOSION_PLAYER).GetComponent <AudioSource>().Play();
     exploder.Explode();
     Invoke("destroy", DESTROY_DELAY);
 }
Пример #5
0
 private void explode()
 {
     Exploder2DUtils.SetActive(exploder.gameObject, true);
     exploder.Radius             = 0.1f;
     exploder.Force              = 1.0f;
     exploder.TargetFragments    = 50;
     exploder.transform.position = Exploder2DUtils.GetCentroid(gameObject);
     exploder.Explode();
 }
Пример #6
0
        public void Add(int key, T data)
        {
            Exploder2DUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size);
            Exploder2DUtils.Assert(dictionary[key].valid == false, "Key already exists!");

            dictionary[key].valid = true;
            dictionary[key].data  = data;

            Count++;
        }
Пример #7
0
        public T this[int key]
        {
            get
            {
                Exploder2DUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size);
                Exploder2DUtils.Assert(dictionary[key].valid == true, "Key does not exist!");
                return(dictionary[key].data);
            }

            set
            {
                Exploder2DUtils.Assert(dictionary[key].valid == true, "Key does not exist!");
                dictionary[key].data = value;
            }
        }
Пример #8
0
        public T GetFirstValue()
        {
            for (int i = 0; i < Size; i++)
            {
                var item = dictionary[i];

                if (item.valid)
                {
                    return(item.data);
                }
            }

            Exploder2DUtils.Assert(false, "No valid key!");
            return(default(T));
        }
Пример #9
0
        public bool TryGetValue(int key, out T value)
        {
            Exploder2DUtils.Assert(key < Size, "Key index out of range! " + key + " maxSize: " + Size);

            var item = dictionary[key];

            if (item.valid)
            {
                value = item.data;
                return(true);
            }

            value = default(T);
            return(false);
        }
Пример #10
0
        void ExplodeList()
        {
            if (index >= sprites.Count)
            {
                return;
            }

            var exploder = Exploder2DSingleton.Exploder2DInstance;

            // move exploder object to the same position
            exploder.transform.position = Exploder2DUtils.GetCentroid(sprites[index]);

            // decrease the radius so the exploder is not interfering other objects
            exploder.Radius = 1.0f;

            exploder.Explode(OnExplosion);
        }
Пример #11
0
 private void OnTriggerEnter2D(Collider2D other)
 {
     if (!other.CompareTag("Player"))
     {
         return;
     }
     FindObjectOfType <CameraController>().StopFollowingPlayer();
     other.gameObject.tag = Constants.EXPLODER_2D;
     Exploder2DUtils.SetActive(exploder.gameObject, true);
     exploder.transform.position = Exploder2DUtils.GetCentroid(other.gameObject);
     exploder.Radius             = 1.0f;
     exploder.Force           = 5.0f;
     exploder.TargetFragments = 50;
     exploder.Explode();
     GetComponent <AudioSource>().Play();
     Invoke("fadeOut", FADE_DELAY);
 }
Пример #12
0
        void ExplodeObject(GameObject obj)
        {
            // activate exploder
            Exploder2DUtils.SetActive(exploder.gameObject, true);

            // move exploder object to the same position
            exploder.transform.position = Exploder2DUtils.GetCentroid(obj);

            // decrease the radius so the exploder is not interfering other objects
            exploder.Radius = 0.1f;

            // DONE!
#if ENABLE_CRACK_AND_EXPLODE
            exploder.Crack(OnCracked);
#else
            exploder.Explode(OnExplosion);
#endif
        }
Пример #13
0
 private void startBlackHole()
 {
     if (sunExploded)
     {
         return;
     }
     FindObjectOfType <LevelManager>().PlayEvilBGM();
     sun.tag = Constants.EXPLODER_2D;
     Exploder2DUtils.SetActive(sun, true);
     exploder.transform.position = Exploder2DUtils.GetCentroid(sun);
     exploder.Radius             = 100.0f;
     exploder.Force           = 32.0f;
     exploder.TargetFragments = 420;
     sunExploded = true;
     exploder.Explode();
     blackHoleIsActive = true;
     blackHole.SetActive(true);
 }
Пример #14
0
        public T[] ToArray()
        {
            var array = new T[Count];
            var idx   = 0;

            for (int i = 0; i < Size; i++)
            {
                if (dictionary[i].valid)
                {
                    array[idx++] = dictionary[i].data;

                    if (idx == Count)
                    {
                        return(array);
                    }
                }
            }

            Exploder2DUtils.Assert(false, "ToArray failed, Count is wrong!");
            return(null);
        }
Пример #15
0
        /// <summary>
        /// Compute intersection between a segment line (a, b) and a plane (p)
        /// from Real-Time Collision Detection Book by Christer Ericson
        /// </summary>
        /// <param name="a">first point of a segment</param>
        /// <param name="b">second point of a segment</param>
        /// <param name="t">normalized distance of intersection point on vector (ab)</param>
        /// <param name="q">point in intersection</param>
        /// <returns>true if there is an intersection</returns>
        public bool IntersectSegment(Vector2 a, Vector2 b, out float t, ref Vector2 q)
        {
            var abx = b.x - a.x;
            var aby = b.y - a.y;

            var dot0 = Normal.x * a.x + Normal.y * a.y;
            var dot1 = Normal.x * abx + Normal.y * aby;

            t = (Distance - dot0) / dot1;

            if (t >= 0.0f - epsylon && t <= 1.0f + epsylon)
            {
                q.x = a.x + t * abx;
                q.y = a.y + t * aby;

                return(true);
            }

            Exploder2DUtils.Log("IntersectSegment failed: " + t);
            q = Vector2.zero;
            return(false);
        }
Пример #16
0
        /// <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);
        }
Пример #17
0
        public void Intersect(Vector2[] path)
        {
            int v1 = 0, h1 = 0;

            for (var i = 0; i < path.Length - 1; i++)
            {
                var p0 = path[i] - min;
                var p1 = path[i + 1] - min;

                int seg = (int)((p0 - p1).sqrMagnitude / (resolution * 0.3 * resolution * 0.3f)) + 1;

                for (int j = 0; j <= seg; j++)
                {
                    var t = (float)j / seg;
                    var p = p0 * (1.0f - t) + p1 * t;

                    var h0 = (short)((p.x + resolution * 1.5f) / resolution);
                    var v0 = (short)((p.y + resolution * 1.5f) / resolution);

                    Exploder2DUtils.Assert(v0 < vCount);
                    Exploder2DUtils.Assert(h0 < hCount);

                    grid[v0][h0].type = CellType.Edge;

                    if (j == 0 || j == seg)
                    {
                        grid[v0][h0].LineIntersection(p0 + min, p1 + min, v0, h0, intersections);
                        grid[v0][h0].PointIntersection(p + min, v0, h0, intersections);
                        grid[v0][h0].AddPnt(p + min);
                    }
                    else if (v0 != v1 || h0 != h1)
                    {
                        grid[v0][h0].LineIntersection(p0 + min, p1 + min, v0, h0, intersections);
                    }

                    v1 = v0;
                    h1 = h0;
                }
            }

            for (var i = 0; i < vCount; i++)
            {
                int leftEdge  = -1;
                int rightEdge = -1;

                for (var j = 0; j < hCount; j++)
                {
                    if (leftEdge == -1 && grid[i][j].type == CellType.Edge)
                    {
                        leftEdge = j;
                    }

                    if (rightEdge == -1 && grid[i][hCount - 1 - j].type == CellType.Edge)
                    {
                        rightEdge = hCount - 1 - j;
                    }

                    if (leftEdge != -1 && rightEdge != -1)
                    {
                        break;
                    }
                }

                if (leftEdge != -1 && rightEdge != -1)
                {
                    for (int j = leftEdge + 1; j < rightEdge; j++)
                    {
                        if (grid[i][j].type != CellType.Edge)
                        {
                            grid[i][j].type = CellType.In;
                        }
                    }
                }
            }

            foreach (var inter in intersections)
            {
                var cell0 = (int)inter.Key;
                var cell1 = (int)(inter.Key >> 32);

                var hp0 = (short)cell0;
                var vp0 = (short)(cell0 >> 16);

                var hp1 = (short)cell1;
                var vp1 = (short)(cell1 >> 16);

                grid[vp0][hp0].AddPnt(inter.Value);
                grid[vp1][hp1].AddPnt(inter.Value);
            }
        }
Пример #18
0
        /// <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);
        }