private static void Capping(Plane blade, MeshCutSide leftSide, MeshCutSide rightSide, List <Vector3> newVertices)
        {
            try
            {
                List <Vector3> capVertTracker = new List <Vector3>();
                List <Vector3> capVertpolygon = new List <Vector3>();
                int            numNewVertices = newVertices.Count;

                for (int i = 0; i < numNewVertices; i++)
                {
                    if (!capVertTracker.Contains(newVertices[i]))
                    {
                        capVertpolygon.Clear();
                        capVertpolygon.Add(newVertices[i]);
                        capVertpolygon.Add(newVertices[i + 1]);

                        capVertTracker.Add(newVertices[i]);
                        capVertTracker.Add(newVertices[i + 1]);

                        bool isDone = false;

                        while (!isDone)
                        {
                            isDone = true;

                            for (int k = 0; k < numNewVertices; k += 2)
                            {
                                // go through the pairs
                                if (newVertices[k] == capVertpolygon[capVertpolygon.Count - 1] && !capVertTracker.Contains(newVertices[k + 1]))
                                {
                                    // if so add the other
                                    isDone = false;
                                    capVertpolygon.Add(newVertices[k + 1]);
                                    capVertTracker.Add(newVertices[k + 1]);
                                }
                                else if (newVertices[k + 1] == capVertpolygon[capVertpolygon.Count - 1] && !capVertTracker.Contains(newVertices[k]))
                                {
                                    // if so add the other
                                    isDone = false;
                                    capVertpolygon.Add(newVertices[k]);
                                    capVertTracker.Add(newVertices[k]);
                                }
                            }
                        }

                        FillCap(blade, leftSide, rightSide, capVertpolygon);
                    }
                }
            }
            catch (System.Exception e)
            {
                Debug.LogErrorFormat("MeshCutter Capping: Caught exception: {0}", e.Message);
            }
        }
        static void FillCap(Plane blade, MeshCutSide leftSide, MeshCutSide rightSide, List <Vector3> vertices)
        {
            // center of the cap
            Vector3 center      = Vector3.zero;
            int     numVertices = vertices.Count;

            for (int i = 0; i < numVertices; ++i)
            {
                center += vertices[i];
            }

            center = center / vertices.Count;

            // you need an axis based on the cap
            Vector3 upward = Vector3.zero;

            // 90 degree turn
            upward.x = blade.normal.y;
            upward.y = -blade.normal.x;
            upward.z = blade.normal.z;
            Vector3 left = Vector3.Cross(blade.normal, upward);

            Vector3 displacement = Vector3.zero;
            Vector3 newUV1       = Vector3.zero;
            Vector3 newUV2       = Vector3.zero;
            Vector2 half         = new Vector2(0.5f, 0.5f);

            for (int i = 0; i < numVertices; ++i)
            {
                displacement = vertices[i] - center;
                newUV1       = Vector3.zero;
                newUV1.x     = 0.5f + Vector3.Dot(displacement, left);
                newUV1.y     = 0.5f + Vector3.Dot(displacement, upward);
                newUV1.z     = 0.5f + Vector3.Dot(displacement, blade.normal);

                displacement = vertices[(i + 1) % vertices.Count] - center;
                newUV2       = Vector3.zero;
                newUV2.x     = 0.5f + Vector3.Dot(displacement, left);
                newUV2.y     = 0.5f + Vector3.Dot(displacement, upward);
                newUV2.z     = 0.5f + Vector3.Dot(displacement, blade.normal);

                leftSide.AddTriangle(
                    new Vector3[] {
                    vertices[i], vertices[(i + 1) % vertices.Count], center
                },
                    new Vector3[] {
                    -blade.normal, -blade.normal, -blade.normal
                },
                    new Vector2[] {
                    newUV1, newUV2, half
                },
                    -blade.normal,
                    leftSide.subIndices.Count - 1
                    );

                rightSide.AddTriangle(
                    new Vector3[] {
                    vertices[i], vertices[(i + 1) % vertices.Count], center
                },
                    new Vector3[] {
                    blade.normal, blade.normal, blade.normal
                },
                    new Vector2[] {
                    newUV1, newUV2, half
                },
                    blade.normal,
                    rightSide.subIndices.Count - 1
                    );
            }
        }
        private static void CutFace(
            Plane blade,
            MeshCutSide leftSide,
            MeshCutSide rightSide,
            List <Vector3> newVertices,
            Vector3[] targetMeshVertices,
            Vector3[] targetMeshNormals,
            Vector2[] targetMeshUVs,
            int submesh,
            bool[] sides,
            int index1,
            int index2,
            int index3)
        {
            Vector3[] leftPoints   = new Vector3[2];
            Vector3[] leftNormals  = new Vector3[2];
            Vector2[] leftUvs      = new Vector2[2];
            Vector3[] rightPoints  = new Vector3[2];
            Vector3[] rightNormals = new Vector3[2];
            Vector2[] rightUvs     = new Vector2[2];

            bool didset_left  = false;
            bool didset_right = false;

            int p = index1;

            for (int side = 0; side < 3; side++)
            {
                switch (side)
                {
                case 0: p = index1; break;

                case 1: p = index2; break;

                case 2: p = index3; break;
                }

                if (sides[side])
                {
                    if (!didset_left)
                    {
                        didset_left = true;

                        leftPoints[0]  = targetMeshVertices[p];
                        leftPoints[1]  = leftPoints[0];
                        leftUvs[0]     = targetMeshUVs[p];
                        leftUvs[1]     = leftUvs[0];
                        leftNormals[0] = targetMeshNormals[p];
                        leftNormals[1] = leftNormals[0];
                    }
                    else
                    {
                        leftPoints[1]  = targetMeshVertices[p];
                        leftUvs[1]     = targetMeshUVs[p];
                        leftNormals[1] = targetMeshNormals[p];
                    }
                }
                else
                {
                    if (!didset_right)
                    {
                        didset_right = true;

                        rightPoints[0]  = targetMeshVertices[p];
                        rightPoints[1]  = rightPoints[0];
                        rightUvs[0]     = targetMeshUVs[p];
                        rightUvs[1]     = rightUvs[0];
                        rightNormals[0] = targetMeshNormals[p];
                        rightNormals[1] = rightNormals[0];
                    }
                    else
                    {
                        rightPoints[1]  = targetMeshVertices[p];
                        rightUvs[1]     = targetMeshUVs[p];
                        rightNormals[1] = targetMeshNormals[p];
                    }
                }
            }

            float normalizedDistance = 0.0f;
            float distance           = 0;

            blade.Raycast(new Ray(leftPoints[0], (rightPoints[0] - leftPoints[0]).normalized), out distance);

            normalizedDistance = distance / (rightPoints[0] - leftPoints[0]).magnitude;
            Vector3 newVertex1 = Vector3.Lerp(leftPoints[0], rightPoints[0], normalizedDistance);
            Vector2 newUv1     = Vector2.Lerp(leftUvs[0], rightUvs[0], normalizedDistance);
            Vector3 newNormal1 = Vector3.Lerp(leftNormals[0], rightNormals[0], normalizedDistance);

            newVertices.Add(newVertex1);

            blade.Raycast(new Ray(leftPoints[1], (rightPoints[1] - leftPoints[1]).normalized), out distance);

            normalizedDistance = distance / (rightPoints[1] - leftPoints[1]).magnitude;
            Vector3 newVertex2 = Vector3.Lerp(leftPoints[1], rightPoints[1], normalizedDistance);
            Vector2 newUv2     = Vector2.Lerp(leftUvs[1], rightUvs[1], normalizedDistance);
            Vector3 newNormal2 = Vector3.Lerp(leftNormals[1], rightNormals[1], normalizedDistance);

            newVertices.Add(newVertex2);

            leftSide.AddTriangle(new Vector3[] { leftPoints[0], newVertex1, newVertex2 },
                                 new Vector3[] { leftNormals[0], newNormal1, newNormal2 },
                                 new Vector2[] { leftUvs[0], newUv1, newUv2 }, newNormal1,
                                 submesh);

            leftSide.AddTriangle(new Vector3[] { leftPoints[0], leftPoints[1], newVertex2 },
                                 new Vector3[] { leftNormals[0], leftNormals[1], newNormal2 },
                                 new Vector2[] { leftUvs[0], leftUvs[1], newUv2 }, newNormal2,
                                 submesh);

            rightSide.AddTriangle(new Vector3[] { rightPoints[0], newVertex1, newVertex2 },
                                  new Vector3[] { rightNormals[0], newNormal1, newNormal2 },
                                  new Vector2[] { rightUvs[0], newUv1, newUv2 }, newNormal1,
                                  submesh);

            rightSide.AddTriangle(new Vector3[] { rightPoints[0], rightPoints[1], newVertex2 },
                                  new Vector3[] { rightNormals[0], rightNormals[1], newNormal2 },
                                  new Vector2[] { rightUvs[0], rightUvs[1], newUv2 }, newNormal2,
                                  submesh);
        }
        /// <summary>
        /// Cuts the specified target into two pieces and caps the sides with the specified Material.
        /// </summary>
        /// <param name="target">The target that is cut - must have a MeshFilter component.</param>
        /// <param name="anchorPoint">Anchor point of the blade i.e. the point where the blade cuts the object.</param>
        /// <param name="normalDirection">Normal direction of the blade plane.</param>
        /// <param name="capMaterial">Cap material - placed as material on the cutting surface of the two sides.</param>
        /// <param name="copyRigidbody">Should the rigidbody be copied to both of the two new pieces.</param>
        /// <param name="copyRigidbody">Should both of the new pieces have colliders.</param>
        public static IObservable <GameObject[]> Cut(GameObject target, Vector3 anchorPoint, Vector3 normalDirection, Material capMaterial, bool copyRigidbody = true, bool copyCollider = true)
        {
            // Set the blade relative to victim
            Plane blade = new Plane(target.transform.InverseTransformDirection(-normalDirection),
                                    target.transform.InverseTransformPoint(anchorPoint));

            // Get the target mesh
            Mesh targetMesh = target.GetComponent <MeshFilter>().mesh;

            Vector3[] targetMeshVertices = targetMesh.vertices;
            Vector3[] targetMeshNormals  = targetMesh.normals;
            Vector2[] targetMeshUVs      = targetMesh.uv;

            int          subMeshCount   = targetMesh.subMeshCount;
            List <int[]> subMeshIndices = new List <int[]>();

            for (int i = 0; i < subMeshCount; ++i)
            {
                subMeshIndices.Add(targetMesh.GetIndices(i));
            }

            // Material parameters
            Material[] mats            = target.GetComponent <MeshRenderer>().sharedMaterials;
            string[]   matsNames       = mats.Select(m => m.name).ToArray();
            string     capMaterialName = capMaterial.name;

            return(Observable.Start(() =>
            {
                if (targetMesh == null)
                {
                    Debug.LogError("MeshCut Cut: Target did not have a MeshFilter component");
                }

                // Left and right side mesh cuts
                MeshCutSide leftSide = new MeshCutSide(targetMeshVertices, targetMeshNormals, targetMeshUVs);
                MeshCutSide rightSide = new MeshCutSide(targetMeshVertices, targetMeshNormals, targetMeshUVs);

                // New vertices for capping the cutting surface
                List <Vector3> newVertices = new List <Vector3>();

                bool[] sides = new bool[3];
                int p1, p2, p3;

                for (int sub = 0; sub < subMeshCount; ++sub)
                {
                    int[] indices = subMeshIndices[sub];
                    leftSide.subIndices.Add(new List <int>());
                    rightSide.subIndices.Add(new List <int>());

                    for (int i = 0; i < indices.Length; i += 3)
                    {
                        p1 = indices[i];
                        p2 = indices[i + 1];
                        p3 = indices[i + 2];

                        sides[0] = blade.GetSide(targetMeshVertices[p1]);
                        sides[1] = blade.GetSide(targetMeshVertices[p2]);
                        sides[2] = blade.GetSide(targetMeshVertices[p3]);

                        // whole triangle
                        if (sides[0] == sides[1] && sides[0] == sides[2])
                        {
                            if (sides[0])
                            {
                                // left side
                                leftSide.AddTriangle(p1, p2, p3, sub);
                            }
                            else
                            {
                                rightSide.AddTriangle(p1, p2, p3, sub);
                            }
                        }
                        else
                        {
                            // Cut the triangle
                            CutFace(
                                blade,
                                leftSide,
                                rightSide,
                                newVertices,
                                targetMeshVertices,
                                targetMeshNormals,
                                targetMeshUVs,
                                sub,
                                sides,
                                p1,
                                p2,
                                p3
                                );
                        }
                    }
                }

                if (matsNames[mats.Length - 1] != capMaterialName)
                {
                    // Add cap indices
                    leftSide.subIndices.Add(new List <int>());
                    rightSide.subIndices.Add(new List <int>());

                    Material[] newMats = new Material[mats.Length + 1];
                    mats.CopyTo(newMats, 0);
                    newMats[mats.Length] = capMaterial;
                    mats = newMats;
                }

                // Cap the openings
                Capping(blade, leftSide, rightSide, newVertices);

                return new MeshCutSide[] { leftSide, rightSide };
            },
                                    Scheduler.ThreadPool)
                   .ObserveOnMainThread()
                   .Select(results => {
                MeshCutSide leftSide = results[0];
                MeshCutSide rightSide = results[1];

                // Left Mesh
                Mesh leftHalfMesh = new Mesh();
                leftHalfMesh.name = "Split Mesh Left";
                leftHalfMesh.vertices = leftSide.vertices.ToArray();
                leftHalfMesh.triangles = leftSide.triangles.ToArray();
                leftHalfMesh.normals = leftSide.normals.ToArray();
                leftHalfMesh.uv = leftSide.uvs.ToArray();

                leftHalfMesh.subMeshCount = leftSide.subIndices.Count;
                int leftHalfMeshSubMeshCount = leftHalfMesh.subMeshCount;

                for (int i = 0; i < leftHalfMeshSubMeshCount; ++i)
                {
                    leftHalfMesh.SetIndices(leftSide.subIndices[i].ToArray(), MeshTopology.Triangles, i);
                }

                // Right Mesh
                Mesh rightHalfMesh = new Mesh();
                rightHalfMesh.name = "Split Mesh Right";
                rightHalfMesh.vertices = rightSide.vertices.ToArray();
                rightHalfMesh.triangles = rightSide.triangles.ToArray();
                rightHalfMesh.normals = rightSide.normals.ToArray();
                rightHalfMesh.uv = rightSide.uvs.ToArray();

                rightHalfMesh.subMeshCount = rightSide.subIndices.Count;
                int rightHalfSubMeshCount = rightHalfMesh.subMeshCount;

                for (int i = 0; i < rightHalfSubMeshCount; ++i)
                {
                    rightHalfMesh.SetIndices(rightSide.subIndices[i].ToArray(), MeshTopology.Triangles, i);
                }

                // Assign the game objects
                target.GetComponent <MeshFilter>().mesh = leftHalfMesh;
                GameObject leftSideObj = target;

                GameObject rightSideObj = new GameObject(target.name, typeof(MeshFilter), typeof(MeshRenderer));
                rightSideObj.transform.position = target.transform.position;
                rightSideObj.transform.rotation = target.transform.rotation;
                rightSideObj.transform.localScale = target.transform.localScale;
                rightSideObj.GetComponent <MeshFilter>().mesh = rightHalfMesh;

                // Maintain colliders and rigidbodies on both pieces i.e. populate the right piece
                Rigidbody leftSideRigidbody = leftSideObj.GetComponent <Rigidbody>();
                MeshCollider leftSideCollider = leftSideObj.GetComponent <MeshCollider>();

                if (copyRigidbody && leftSideRigidbody != null)
                {
                    Rigidbody rigidbody = rightSideObj.AddComponent <Rigidbody>();
                    rigidbody.useGravity = leftSideRigidbody.useGravity;
                    rigidbody.interpolation = leftSideRigidbody.interpolation;
                    rigidbody.collisionDetectionMode = leftSideRigidbody.collisionDetectionMode;
                }

                if (copyCollider && leftSideCollider != null)
                {
                    MeshCollider meshCollider = rightSideObj.AddComponent <MeshCollider>();
                    meshCollider.convex = true;
                }

                // Assign mats
                leftSideObj.GetComponent <MeshRenderer>().materials = mats;
                rightSideObj.GetComponent <MeshRenderer>().materials = mats;

                return new GameObject[] { leftSideObj, rightSideObj };
            }));
        }