private static bool SplitMeshUsingPlane(MeshData meshDataIn, FracturedObject fracturedComponent, SplitOptions splitOptions, Vector3 v3PlaneNormal, Vector3 v3PlaneRight, Vector3 v3PlanePoint, out List<MeshData> listMeshDatasPosOut, out List<MeshData> listMeshDatasNegOut, ProgressDelegate progress = null)
        {
            Plane planeSplit = new Plane(v3PlaneNormal, v3PlanePoint);

            listMeshDatasPosOut = new List<MeshData>();
            listMeshDatasNegOut = new List<MeshData>();

            // Check if the input object already has been split, to get its split closing submesh

            bool bNeedsNewSplitSubMesh = meshDataIn.nSplitCloseSubMesh == -1;
            int  nSplitCloseSubMesh    = meshDataIn.nSplitCloseSubMesh;

            // Here we are going to store our output vertex/index data

            int nCurrentVertexHash = meshDataIn.nCurrentVertexHash; // We will use this to identify vertices with same coordinates but different vertex data. They will share the same vertex hash

            List<VertexData>     listVertexDataPos   = new List<VertexData>();
            List<VertexData>     listVertexDataNeg   = new List<VertexData>();
            List<int>[]          alistIndicesPos     = new List<int>[meshDataIn.nSubMeshCount + (meshDataIn.nSplitCloseSubMesh == -1 ? 1 : 0)];
            List<int>[]          alistIndicesNeg     = new List<int>[meshDataIn.nSubMeshCount + (meshDataIn.nSplitCloseSubMesh == -1 ? 1 : 0)];
            MeshFaceConnectivity faceConnectivityPos = new MeshFaceConnectivity();
            MeshFaceConnectivity faceConnectivityNeg = new MeshFaceConnectivity();
            MeshDataConnectivity meshConnectivityPos = new MeshDataConnectivity();
            MeshDataConnectivity meshConnectivityNeg = new MeshDataConnectivity();

            listVertexDataPos.Capacity = meshDataIn.aVertexData.Length / 2;
            listVertexDataNeg.Capacity = meshDataIn.aVertexData.Length / 2;

            if(bNeedsNewSplitSubMesh)
            {
                // Make room for the split closing submesh

                nSplitCloseSubMesh = meshDataIn.nSubMeshCount;

                alistIndicesPos[nSplitCloseSubMesh] = new List<int>();
                alistIndicesNeg[nSplitCloseSubMesh] = new List<int>();
            }

            // Our vertices that form the clipped cap

            Dictionary<EdgeKeyByHash, int>     dicClipVerticesHash = new Dictionary<EdgeKeyByHash, int>    (new EdgeKeyByHash.EqualityComparer());
            Dictionary<EdgeKeyByHash, CapEdge> dicCapEdges         = new Dictionary<EdgeKeyByHash, CapEdge>(new EdgeKeyByHash.EqualityComparer());

            // A hash table with our clipped edges, to reuse clipped vertices

            Dictionary<EdgeKeyByIndex, ClippedEdge> dicClippedEdgesPos = new Dictionary<EdgeKeyByIndex, ClippedEdge>(new EdgeKeyByIndex.EqualityComparer());
            Dictionary<EdgeKeyByIndex, ClippedEdge> dicClippedEdgesNeg = new Dictionary<EdgeKeyByIndex, ClippedEdge>(new EdgeKeyByIndex.EqualityComparer());

            int nClippedCacheHits   = 0;
            int nClippedCacheMisses = 0;

            // A hash table with the remapped indices, to reuse non-clipped vertices

            Dictionary<int, int> dicRemappedIndicesPos = new Dictionary<int, int>();
            Dictionary<int, int> dicRemappedIndicesNeg = new Dictionary<int, int>();

            for(int nSubMesh = 0; nSubMesh < meshDataIn.nSubMeshCount; nSubMesh++)
            {
                // Index list

                alistIndicesPos[nSubMesh] = new List<int>();
                alistIndicesNeg[nSubMesh] = new List<int>();
                List<int> listIndicesPos = alistIndicesPos[nSubMesh];
                List<int> listIndicesNeg = alistIndicesNeg[nSubMesh];

                alistIndicesPos[nSubMesh].Capacity = meshDataIn.aaIndices[nSubMesh].Length / 2;
                alistIndicesNeg[nSubMesh].Capacity = meshDataIn.aaIndices[nSubMesh].Length / 2;

                // A reference to the output arrays/lists (it will be switching between positive/negative side along the algorithm)

                List<VertexData>     plistVertexData    = listVertexDataPos;
                List<int>            plistObjectIndices = listIndicesPos;
                MeshFaceConnectivity pFaceConnectivity  = faceConnectivityPos;
                MeshDataConnectivity pMeshConnectivity  = meshConnectivityPos;

                Dictionary<EdgeKeyByIndex, ClippedEdge> pdicClippedEdges    = dicClippedEdgesPos;
                Dictionary<int, int>                    pdicRemappedIndices = dicRemappedIndicesPos;

                // Iterate through all submesh faces:

                for(int i = 0; i < meshDataIn.aaIndices[nSubMesh].Length / 3; i++)
	            {
                    plistVertexData     = listVertexDataPos;
                    plistObjectIndices  = listIndicesPos;
                    pFaceConnectivity   = faceConnectivityPos;
                    pMeshConnectivity   = meshConnectivityPos;
                    pdicClippedEdges    = dicClippedEdgesPos;
                    pdicRemappedIndices = dicRemappedIndicesPos;

		            int nIndex1 = meshDataIn.aaIndices[nSubMesh][i * 3 + 0];
		            int nIndex2 = meshDataIn.aaIndices[nSubMesh][i * 3 + 1];
		            int nIndex3 = meshDataIn.aaIndices[nSubMesh][i * 3 + 2];

                    int nHashV1 = meshDataIn.aVertexData[nIndex1].nVertexHash;
                    int nHashV2 = meshDataIn.aVertexData[nIndex2].nVertexHash;
                    int nHashV3 = meshDataIn.aVertexData[nIndex3].nVertexHash;

		            Vector3 v1 = meshDataIn.aVertexData[nIndex1].v3Vertex;
		            Vector3 v2 = meshDataIn.aVertexData[nIndex2].v3Vertex;
		            Vector3 v3 = meshDataIn.aVertexData[nIndex3].v3Vertex;

                    // Classify vertices depending on the side of the plane they lay on, then clip if necessary.

		            float fSide1 = v1.x * planeSplit.normal.x + v1.y * planeSplit.normal.y + v1.z * planeSplit.normal.z + planeSplit.distance;
		            float fSide2 = v2.x * planeSplit.normal.x + v2.y * planeSplit.normal.y + v2.z * planeSplit.normal.z + planeSplit.distance;
		            float fSide3 = v3.x * planeSplit.normal.x + v3.y * planeSplit.normal.y + v3.z * planeSplit.normal.z + planeSplit.distance;

                    bool  bForceSameSide  = false;
                    int   nAlmostInPlane  = 0;
                    bool  bAlmostInPlane1 = false;
                    bool  bAlmostInPlane2 = false;
                    bool  bAlmostInPlane3 = false;
                    float fFurthest       = 0.0f;

                    if(Mathf.Abs(fSide1) < UltimateFracturing.Parameters.EPSILONDISTANCEPLANE) { bAlmostInPlane1 = true; nAlmostInPlane++; }
                    if(Mathf.Abs(fSide2) < UltimateFracturing.Parameters.EPSILONDISTANCEPLANE) { bAlmostInPlane2 = true; nAlmostInPlane++; }
                    if(Mathf.Abs(fSide3) < UltimateFracturing.Parameters.EPSILONDISTANCEPLANE) { bAlmostInPlane3 = true; nAlmostInPlane++; }

                    if(Mathf.Abs(fSide1) > Mathf.Abs(fFurthest)) fFurthest = fSide1;
                    if(Mathf.Abs(fSide2) > Mathf.Abs(fFurthest)) fFurthest = fSide2;
                    if(Mathf.Abs(fSide3) > Mathf.Abs(fFurthest)) fFurthest = fSide3;

                    if(nAlmostInPlane == 1)
                    {
                        // Look if the other two vertices are on the same side. If so, we'll skip the clipping too.
                        if(bAlmostInPlane1 && (fSide2 * fSide3 > 0.0f)) bForceSameSide = true;
                        if(bAlmostInPlane2 && (fSide1 * fSide3 > 0.0f)) bForceSameSide = true;
                        if(bAlmostInPlane3 && (fSide1 * fSide2 > 0.0f)) bForceSameSide = true;
                    }
                    else if(nAlmostInPlane > 1)
                    {
                        bForceSameSide = true;

                        if(nAlmostInPlane == 3)
                        {
                            // Coplanar
                            continue;
                        }
                    }

		            if((fSide1 * fSide2 > 0.0f && fSide2 * fSide3 > 0.0f) || bForceSameSide)
		            {
			            // All on the same side, no clipping needed

			            if(fFurthest < 0.0f)
			            {
				            plistVertexData     = listVertexDataNeg;
				            plistObjectIndices  = listIndicesNeg;
                            pFaceConnectivity   = faceConnectivityNeg;
                            pMeshConnectivity   = meshConnectivityNeg;
                            pdicClippedEdges    = dicClippedEdgesNeg;
                            pdicRemappedIndices = dicRemappedIndicesNeg;
			            }

                        int nNewIndex1 = -1;
                        int nNewIndex2 = -1;
                        int nNewIndex3 = -1;

                        // Find vertices in remapped indices list and add vertex data if not present

                        if(pdicRemappedIndices.ContainsKey(nIndex1))
                        {
                            nNewIndex1 = pdicRemappedIndices[nIndex1];
                        }
                        if(nNewIndex1 == -1)
                        {
                            nNewIndex1 = plistVertexData.Count;
                            plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                            pdicRemappedIndices[nIndex1] = nNewIndex1;
                        }

                        if(pdicRemappedIndices.ContainsKey(nIndex2))
                        {
                            nNewIndex2 = pdicRemappedIndices[nIndex2];
                        }
                        if(nNewIndex2 == -1)
                        {
                            nNewIndex2 = plistVertexData.Count;
                            plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                            pdicRemappedIndices[nIndex2] = nNewIndex2;
                        }

                        if(pdicRemappedIndices.ContainsKey(nIndex3))
                        {
                            nNewIndex3 = pdicRemappedIndices[nIndex3];
                        }
                        if(nNewIndex3 == -1)
                        {
                            nNewIndex3 = plistVertexData.Count;
                            plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                            pdicRemappedIndices[nIndex3] = nNewIndex3;
                        }

                        // Add triangle indices

                        if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                        {
                            pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                        }

                        plistObjectIndices.Add(nNewIndex1);
                        plistObjectIndices.Add(nNewIndex2);
                        plistObjectIndices.Add(nNewIndex3);

                        if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                        {
                            pFaceConnectivity.AddEdge(nSubMesh, v1, v2, nHashV1, nHashV2, nNewIndex1, nNewIndex2);
                            pFaceConnectivity.AddEdge(nSubMesh, v2, v3, nHashV2, nHashV3, nNewIndex2, nNewIndex3);
                            pFaceConnectivity.AddEdge(nSubMesh, v3, v1, nHashV3, nHashV1, nNewIndex3, nNewIndex1);
                        }

                        // Add cap edges only if an edge is lying on the plane

                        if(nAlmostInPlane == 2)
                        {
                            if(fFurthest > 0.0f)
                            {
                                if(bAlmostInPlane1 && bAlmostInPlane2 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV1, nHashV2, v1, v2);
                                if(bAlmostInPlane2 && bAlmostInPlane3 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV2, nHashV3, v2, v3);
                                if(bAlmostInPlane3 && bAlmostInPlane1 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV3, nHashV1, v3, v1);
                            }
                            else
                            {
                                if(bAlmostInPlane1 && bAlmostInPlane2 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV2, nHashV1, v2, v1);
                                if(bAlmostInPlane2 && bAlmostInPlane3 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV3, nHashV2, v3, v2);
                                if(bAlmostInPlane3 && bAlmostInPlane1 && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV1, nHashV3, v1, v3);
                            }
                        }
		            }
                    else if(nAlmostInPlane == 1)
                    {
                        // Special treatment clipping for one vertex laying on the clipping plane and the other 2 on different sides

                        int  nNewIndex1 = -1;
                        int  nNewIndex2 = -1;
                        int  nNewIndex3 = -1;
                        int  nNewIndex4 = -1;
                        int  nHashV4    = -1;
                        bool bEdge      = false;

                        EdgeKeyByIndex clippedEdgeKey;

                        if(bAlmostInPlane1)
                        {
                            // v1 almost on the clipping plane

					        if(fSide2 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
						        plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }

                            clippedEdgeKey = new EdgeKeyByIndex(nIndex2, nIndex3);

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge = true;
                                nNewIndex2 = pdicClippedEdges[clippedEdgeKey].GetFirstIndex(nIndex2);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                            }

                            // Clip if not present in clipped edge list

                            EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV2, nHashV3);

                            if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                            {
                                nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                            }
                            else
                            {
                                nHashV4 = nCurrentVertexHash++;
                                dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                            }

                            VertexData vd4 = new VertexData(nHashV4);

                            if(bEdge == false)
                            {
                                if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex2, nIndex3, v2, v3, planeSplit, ref vd4) == false)
                                {
                                    return false;
                                }
                            }

                            // Add geometry of one side

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex1 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex1))
                                {
                                    nNewIndex1 = pdicRemappedIndices[nIndex1];
                                }
                            }
                            if(nNewIndex1 == -1)
                            {
                                nNewIndex1 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                pdicRemappedIndices[nIndex1] = nNewIndex1;
                            }

                            if(nNewIndex2 == -1)
                            {
                                nNewIndex2 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                pdicRemappedIndices[nIndex2] = nNewIndex2;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex1);
                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex4);

                            Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v1, v2, nHashV1, nHashV2, nNewIndex1, nNewIndex2);
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v4, nHashV2, nHashV4, nNewIndex2, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v1, nHashV4, nHashV1, nNewIndex4, nNewIndex1);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV1, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex1].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex4));

                            // Add geometry of other side

					        if(fSide3 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
                                plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }
					        else
					        {
						        plistVertexData     = listVertexDataPos;
						        plistObjectIndices  = listIndicesPos;
                                pFaceConnectivity   = faceConnectivityPos;
                                pMeshConnectivity   = meshConnectivityPos;
                                pdicClippedEdges    = dicClippedEdgesPos;
                                pdicRemappedIndices = dicRemappedIndicesPos;
					        }

                            nNewIndex1 = -1;
                            nNewIndex2 = -1;
                            nNewIndex3 = -1;
                            nNewIndex4 = -1;
                            bEdge      = false;

                            // Find edges in cache

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge      = true;
                                nNewIndex3 = pdicClippedEdges[clippedEdgeKey].GetSecondIndex(nIndex3);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                            }

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex1 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex1))
                                {
                                    nNewIndex1 = pdicRemappedIndices[nIndex1];
                                }
                            }
                            if(nNewIndex1 == -1)
                            {
                                nNewIndex1 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                pdicRemappedIndices[nIndex1] = nNewIndex1;
                            }

                            if(nNewIndex3 == -1)
                            {
                                nNewIndex3 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                pdicRemappedIndices[nIndex3] = nNewIndex3;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex1);
                            plistObjectIndices.Add(nNewIndex4);
                            plistObjectIndices.Add(nNewIndex3);

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v1, v4, nHashV1, nHashV4, nNewIndex1, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v3, nHashV4, nHashV3, nNewIndex4, nNewIndex3);
                                pFaceConnectivity.AddEdge(nSubMesh, v3, v1, nHashV3, nHashV1, nNewIndex3, nNewIndex1);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV1, nHashV4, plistVertexData[nNewIndex1].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex4));
                        }
                        else if(bAlmostInPlane2)
                        {
                            // v2 almost on the clipping plane

					        if(fSide3 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
						        plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }

                            clippedEdgeKey = new EdgeKeyByIndex(nIndex3, nIndex1);

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge      = true;
                                nNewIndex3 = pdicClippedEdges[clippedEdgeKey].GetFirstIndex(nIndex3);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                            }

                            // Clip if not present in clipped edge list

                            EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV3, nHashV1);

                            if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                            {
                                nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                            }
                            else
                            {
                                nHashV4 = nCurrentVertexHash++;
                                dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                            }

                            VertexData vd4 = new VertexData(nHashV4);

                            if(bEdge == false)
                            {
                                if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex3, nIndex1, v3, v1, planeSplit, ref vd4) == false)
                                {
                                    return false;
                                }
                            }

                            // Add geometry of one side

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex2 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex2))
                                {
                                    nNewIndex2 = pdicRemappedIndices[nIndex2];
                                }
                            }
                            if(nNewIndex2 == -1)
                            {
                                nNewIndex2 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                pdicRemappedIndices[nIndex2] = nNewIndex2;
                            }

                            if(nNewIndex3 == -1)
                            {
                                nNewIndex3 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                pdicRemappedIndices[nIndex3] = nNewIndex3;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex3);
                            plistObjectIndices.Add(nNewIndex4);

                            Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v3, nHashV2, nHashV3, nNewIndex2, nNewIndex3);
                                pFaceConnectivity.AddEdge(nSubMesh, v3, v4, nHashV3, nHashV4, nNewIndex3, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v2, nHashV4, nHashV2, nNewIndex4, nNewIndex2);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV2, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex2].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex3, nIndex1, nNewIndex3, nNewIndex1, nNewIndex4));

                            // Add geometry of other side

					        if(fSide1 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
                                plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }
					        else
					        {
						        plistVertexData     = listVertexDataPos;
						        plistObjectIndices  = listIndicesPos;
                                pFaceConnectivity   = faceConnectivityPos;
                                pMeshConnectivity   = meshConnectivityPos;
                                pdicClippedEdges    = dicClippedEdgesPos;
                                pdicRemappedIndices = dicRemappedIndicesPos;
					        }

                            nNewIndex1 = -1;
                            nNewIndex2 = -1;
                            nNewIndex4 = -1;
                            bEdge      = false;

                            // Find edges in cache

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge      = true;
                                nNewIndex1 = pdicClippedEdges[clippedEdgeKey].GetSecondIndex(nIndex1);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                            }

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex1 == -1)
                            {
                                nNewIndex1 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                pdicRemappedIndices[nIndex1] = nNewIndex1;
                            }

                            if(nNewIndex2 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex2))
                                {
                                    nNewIndex2 = pdicRemappedIndices[nIndex2];
                                }
                            }
                            if(nNewIndex2 == -1)
                            {
                                nNewIndex2 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                pdicRemappedIndices[nIndex2] = nNewIndex2;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex4);
                            plistObjectIndices.Add(nNewIndex1);

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v4, nHashV2, nHashV4, nNewIndex2, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v1, nHashV4, nHashV1, nNewIndex4, nNewIndex1);
                                pFaceConnectivity.AddEdge(nSubMesh, v1, v2, nHashV1, nHashV2, nNewIndex1, nNewIndex2);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV2, nHashV4, plistVertexData[nNewIndex2].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex3, nIndex1, nNewIndex3, nNewIndex1, nNewIndex4));
                        }
                        else if(bAlmostInPlane3)
                        {
                            // v3 almost on the clipping plane

					        if(fSide1 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
						        plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }

                            clippedEdgeKey = new EdgeKeyByIndex(nIndex1, nIndex2);

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge      = true;
                                nNewIndex1 = pdicClippedEdges[clippedEdgeKey].GetFirstIndex(nIndex1);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                            }

                            // Clip if not present in clipped edge list

                            EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV1, nHashV2);

                            if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                            {
                                nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                            }
                            else
                            {
                                nHashV4 = nCurrentVertexHash++;
                                dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                            }

                            VertexData vd4 = new VertexData(nHashV4);

                            if(bEdge == false)
                            {
                                if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex1, nIndex2, v1, v2, planeSplit, ref vd4) == false)
                                {
                                    return false;
                                }
                            }

                            // Add geometry of one side

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex1 == -1)
                            {
                                nNewIndex1 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                pdicRemappedIndices[nIndex1] = nNewIndex1;
                            }

                            if(nNewIndex3 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex3))
                                {
                                    nNewIndex3 = pdicRemappedIndices[nIndex3];
                                }
                            }
                            if(nNewIndex3 == -1)
                            {
                                nNewIndex3 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                pdicRemappedIndices[nIndex3] = nNewIndex3;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex1);
                            plistObjectIndices.Add(nNewIndex4);
                            plistObjectIndices.Add(nNewIndex3);

                            Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v1, v4, nHashV1, nHashV4, nNewIndex1, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v3, nHashV4, nHashV3, nNewIndex4, nNewIndex3);
                                pFaceConnectivity.AddEdge(nSubMesh, v3, v1, nHashV3, nHashV1, nNewIndex3, nNewIndex1);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV3, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex3].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));

                            // Add geometry of other side

					        if(fSide2 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
                                plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }
					        else
					        {
						        plistVertexData     = listVertexDataPos;
						        plistObjectIndices  = listIndicesPos;
                                pFaceConnectivity   = faceConnectivityPos;
                                pMeshConnectivity   = meshConnectivityPos;
                                pdicClippedEdges    = dicClippedEdgesPos;
                                pdicRemappedIndices = dicRemappedIndicesPos;
					        }

                            nNewIndex2 = -1;
                            nNewIndex3 = -1;
                            nNewIndex4 = -1;
                            bEdge      = false;

                            // Find edges in cache

                            if(pdicClippedEdges.ContainsKey(clippedEdgeKey))
                            {
                                nClippedCacheHits++;
                                bEdge      = true;
                                nNewIndex2 = pdicClippedEdges[clippedEdgeKey].GetSecondIndex(nIndex2);
                                nNewIndex4 = pdicClippedEdges[clippedEdgeKey].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                            }

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex2 == -1)
                            {
                                nNewIndex2 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                pdicRemappedIndices[nIndex2] = nNewIndex2;
                            }

                            if(nNewIndex3 == -1)
                            {
                                if(pdicRemappedIndices.ContainsKey(nIndex3))
                                {
                                    nNewIndex3 = pdicRemappedIndices[nIndex3];
                                }
                            }
                            if(nNewIndex3 == -1)
                            {
                                nNewIndex3 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                pdicRemappedIndices[nIndex3] = nNewIndex3;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex3);
                            plistObjectIndices.Add(nNewIndex4);

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v3, nHashV2, nHashV3, nNewIndex2, nNewIndex3);
                                pFaceConnectivity.AddEdge(nSubMesh, v3, v4, nHashV3, nHashV4, nNewIndex3, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v2, nHashV4, nHashV2, nNewIndex4, nNewIndex2);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV3, nHashV4, plistVertexData[nNewIndex3].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                            if(bEdge == false) pdicClippedEdges.Add(clippedEdgeKey, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));
                        }
                    }
		            else
		            {
			            if(fSide1 * fSide2 < 0.0f)
			            {
				            // v1 and v2 on different sides

				            if(fSide2 * fSide3 < 0.0f)
				            {
					            // ... and v3 on same side as v1

					            if(fSide1 < 0.0f)
					            {
						            plistVertexData     = listVertexDataNeg;
						            plistObjectIndices  = listIndicesNeg;
                                    pFaceConnectivity   = faceConnectivityNeg;
                                    pMeshConnectivity   = meshConnectivityNeg;
                                    pdicClippedEdges    = dicClippedEdgesNeg;
                                    pdicRemappedIndices = dicRemappedIndicesNeg;
					            }

                                int  nNewIndex1 = -1;
                                int  nNewIndex2 = -1;
                                int  nNewIndex3 = -1;
                                int  nNewIndex4 = -1;
                                int  nNewIndex5 = -1;
                                int  nHashV4    = -1;
                                int  nHashV5    = -1;
                                bool bEdgeKey1  = false;
                                bool bEdgeKey2  = false;

                                EdgeKeyByIndex edgeKey1 = new EdgeKeyByIndex(nIndex1, nIndex2);
                                EdgeKeyByIndex edgeKey2 = new EdgeKeyByIndex(nIndex2, nIndex3);

                                // Find edges in cache

                                if(pdicClippedEdges.ContainsKey(edgeKey1))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey1  = true;
                                    nNewIndex1 = pdicClippedEdges[edgeKey1].GetFirstIndex(nIndex1);
                                    nNewIndex4 = pdicClippedEdges[edgeKey1].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                                }

                                if(pdicClippedEdges.ContainsKey(edgeKey2))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey2  = true;
                                    nNewIndex3 = pdicClippedEdges[edgeKey2].GetSecondIndex(nIndex3);
                                    nNewIndex5 = pdicClippedEdges[edgeKey2].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                                }

                                // Clip if not present in clipped edge list

                                EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV1, nHashV2);

                                if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                                {
                                    nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                                }
                                else
                                {
                                    nHashV4 = nCurrentVertexHash++;
                                    dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                                }

                                clippedEdgeKeyHash = new EdgeKeyByHash(nHashV2, nHashV3);

                                if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                                {
                                    nHashV5 = dicClipVerticesHash[clippedEdgeKeyHash];
                                }
                                else
                                {
                                    nHashV5 = nCurrentVertexHash++;
                                    dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV5);
                                }

                                VertexData vd4 = new VertexData(nHashV4), vd5 = new VertexData(nHashV5);

                                if(bEdgeKey1 == false)
                                {
                                    if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex1, nIndex2, v1, v2, planeSplit, ref vd4) == false)
                                    {
                                        return false;
                                    }
                                }

                                if(bEdgeKey2 == false)
                                {
                                    if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex2, nIndex3, v2, v3, planeSplit, ref vd5) == false)
                                    {
                                        return false;
                                    }
                                }

					            // Add geometry of one side

                                // Add vertex data for all data not present in remapped list

                                if(nNewIndex1 == -1)
                                {
                                    nNewIndex1 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                    pdicRemappedIndices[nIndex1] = nNewIndex1;
                                }

                                if(nNewIndex3 == -1)
                                {
                                    nNewIndex3 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                    pdicRemappedIndices[nIndex3] = nNewIndex3;
                                }

                                if(nNewIndex4 == -1)
                                {
                                    nNewIndex4 = plistVertexData.Count;
                                    plistVertexData.Add(vd4);
                                }

                                if(nNewIndex5 == -1)
                                {
                                    nNewIndex5 = plistVertexData.Count;
                                    plistVertexData.Add(vd5);
                                }

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex1);
                                plistObjectIndices.Add(nNewIndex4);
                                plistObjectIndices.Add(nNewIndex5);

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex1);
                                plistObjectIndices.Add(nNewIndex5);
                                plistObjectIndices.Add(nNewIndex3);

                                Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;
                                Vector3 v5 = plistVertexData[nNewIndex5].v3Vertex;

                                if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                                {
                                    pFaceConnectivity.AddEdge(nSubMesh, v1, v4, nHashV1, nHashV4, nNewIndex1, nNewIndex4);
                                    pFaceConnectivity.AddEdge(nSubMesh, v4, v5, nHashV4, nHashV5, nNewIndex4, nNewIndex5);
                                    pFaceConnectivity.AddEdge(nSubMesh, v5, v1, nHashV5, nHashV1, nNewIndex5, nNewIndex1);
                                    pFaceConnectivity.AddEdge(nSubMesh, v1, v5, nHashV1, nHashV5, nNewIndex1, nNewIndex5);
                                    pFaceConnectivity.AddEdge(nSubMesh, v5, v3, nHashV5, nHashV3, nNewIndex5, nNewIndex3);
                                    pFaceConnectivity.AddEdge(nSubMesh, v3, v1, nHashV3, nHashV1, nNewIndex3, nNewIndex1);
                                }

                                // Update cap edges and cache

                                if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV5, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex5].v3Vertex);

                                if(pdicClippedEdges.ContainsKey(edgeKey1) == false) pdicClippedEdges.Add(edgeKey1, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));
                                if(pdicClippedEdges.ContainsKey(edgeKey2) == false) pdicClippedEdges.Add(edgeKey2, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex5));

                                // Add geometry of other side

					            if(fSide2 < 0.0f)
					            {
						            plistVertexData     = listVertexDataNeg;
                                    plistObjectIndices  = listIndicesNeg;
                                    pFaceConnectivity   = faceConnectivityNeg;
                                    pMeshConnectivity   = meshConnectivityNeg;
                                    pdicClippedEdges    = dicClippedEdgesNeg;
                                    pdicRemappedIndices = dicRemappedIndicesNeg;
					            }
					            else
					            {
						            plistVertexData     = listVertexDataPos;
						            plistObjectIndices  = listIndicesPos;
                                    pFaceConnectivity   = faceConnectivityPos;
                                    pMeshConnectivity   = meshConnectivityPos;
                                    pdicClippedEdges    = dicClippedEdgesPos;
                                    pdicRemappedIndices = dicRemappedIndicesPos;
					            }

                                nNewIndex1 = -1;
                                nNewIndex2 = -1;
                                nNewIndex3 = -1;
                                nNewIndex4 = -1;
                                nNewIndex5 = -1;
                                bEdgeKey1  = false;
                                bEdgeKey2  = false;

                                // Find edges in cache

                                if(pdicClippedEdges.ContainsKey(edgeKey1))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey1  = true;
                                    nNewIndex2 = pdicClippedEdges[edgeKey1].GetSecondIndex(nIndex2);
                                    nNewIndex4 = pdicClippedEdges[edgeKey1].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                                }

                                if(pdicClippedEdges.ContainsKey(edgeKey2))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey2  = true;
                                    nNewIndex2 = pdicClippedEdges[edgeKey2].GetFirstIndex(nIndex2);
                                    nNewIndex5 = pdicClippedEdges[edgeKey2].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                                }

                                // Add vertex data for all data not present in remapped list

                                if(nNewIndex2 == -1)
                                {
                                    nNewIndex2 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                    pdicRemappedIndices[nIndex2] = nNewIndex2;
                                }

                                if(nNewIndex4 == -1)
                                {
                                    nNewIndex4 = plistVertexData.Count;
                                    plistVertexData.Add(vd4);
                                }

                                if(nNewIndex5 == -1)
                                {
                                    nNewIndex5 = plistVertexData.Count;
                                    plistVertexData.Add(vd5);
                                }

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex4);
                                plistObjectIndices.Add(nNewIndex2);
                                plistObjectIndices.Add(nNewIndex5);

                                if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                                {
                                    pFaceConnectivity.AddEdge(nSubMesh, v4, v2, nHashV4, nHashV2, nNewIndex4, nNewIndex2);
                                    pFaceConnectivity.AddEdge(nSubMesh, v2, v5, nHashV2, nHashV5, nNewIndex2, nNewIndex5);
                                    pFaceConnectivity.AddEdge(nSubMesh, v5, v4, nHashV5, nHashV4, nNewIndex5, nNewIndex4);
                                }

                                // Update cap edges and cache

                                if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV5, nHashV4, plistVertexData[nNewIndex5].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                                if(pdicClippedEdges.ContainsKey(edgeKey1) == false) pdicClippedEdges.Add(edgeKey1, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));
                                if(pdicClippedEdges.ContainsKey(edgeKey2) == false) pdicClippedEdges.Add(edgeKey2, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex5));
				            }
				            else
				            {
					            // ... and v3 on same side as v2

					            if(fSide1 < 0.0f)
					            {
						            plistVertexData     = listVertexDataNeg;
						            plistObjectIndices  = listIndicesNeg;
                                    pFaceConnectivity   = faceConnectivityNeg;
                                    pMeshConnectivity   = meshConnectivityNeg;
                                    pdicClippedEdges    = dicClippedEdgesNeg;
                                    pdicRemappedIndices = dicRemappedIndicesNeg;
					            }

                                int  nNewIndex1 = -1;
                                int  nNewIndex2 = -1;
                                int  nNewIndex3 = -1;
                                int  nNewIndex4 = -1;
                                int  nNewIndex5 = -1;
                                int  nHashV4    = -1;
                                int  nHashV5    = -1;
                                bool bEdgeKey1  = false;
                                bool bEdgeKey3  = false;

                                EdgeKeyByIndex edgeKey1 = new EdgeKeyByIndex(nIndex1, nIndex2);
                                EdgeKeyByIndex edgeKey3 = new EdgeKeyByIndex(nIndex1, nIndex3);

                                // Find edges in cache

                                if(pdicClippedEdges.ContainsKey(edgeKey1))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey1  = true;
                                    nNewIndex1 = pdicClippedEdges[edgeKey1].GetFirstIndex(nIndex1);
                                    nNewIndex4 = pdicClippedEdges[edgeKey1].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                                }

                                if(pdicClippedEdges.ContainsKey(edgeKey3))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey3  = true;
                                    nNewIndex1 = pdicClippedEdges[edgeKey3].GetFirstIndex(nIndex1);
                                    nNewIndex5 = pdicClippedEdges[edgeKey3].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                                }

                                // Clip if not present in clipped edge list

                                EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV1, nHashV2);

                                if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                                {
                                    nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                                }
                                else
                                {
                                    nHashV4 = nCurrentVertexHash++;
                                    dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                                }

                                clippedEdgeKeyHash = new EdgeKeyByHash(nHashV1, nHashV3);

                                if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                                {
                                    nHashV5 = dicClipVerticesHash[clippedEdgeKeyHash];
                                }
                                else
                                {
                                    nHashV5 = nCurrentVertexHash++;
                                    dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV5);
                                }

                                VertexData vd4 = new VertexData(nHashV4), vd5 = new VertexData(nHashV5);

                                if(bEdgeKey1 == false)
                                {
                                    if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex1, nIndex2, v1, v2, planeSplit, ref vd4) == false)
                                    {
                                        return false;
                                    }
                                }

                                if(bEdgeKey3 == false)
                                {
                                    if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex1, nIndex3, v1, v3, planeSplit, ref vd5) == false)
                                    {
                                        return false;
                                    }
                                }

					            // Add geometry of one side

                                // Add vertex data for all data not present in remapped list

                                if(nNewIndex1 == -1)
                                {
                                    nNewIndex1 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                    pdicRemappedIndices[nIndex1] = nNewIndex1;
                                }

                                if(nNewIndex4 == -1)
                                {
                                    nNewIndex4 = plistVertexData.Count;
                                    plistVertexData.Add(vd4);
                                }

                                if(nNewIndex5 == -1)
                                {
                                    nNewIndex5 = plistVertexData.Count;
                                    plistVertexData.Add(vd5);
                                }

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex1);
                                plistObjectIndices.Add(nNewIndex4);
                                plistObjectIndices.Add(nNewIndex5);

                                Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;
                                Vector3 v5 = plistVertexData[nNewIndex5].v3Vertex;

                                if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                                {
                                    pFaceConnectivity.AddEdge(nSubMesh, v1, v4, nHashV1, nHashV4, nNewIndex1, nNewIndex4);
                                    pFaceConnectivity.AddEdge(nSubMesh, v4, v5, nHashV4, nHashV5, nNewIndex4, nNewIndex5);
                                    pFaceConnectivity.AddEdge(nSubMesh, v5, v1, nHashV5, nHashV1, nNewIndex5, nNewIndex1);
                                }

                                // Update cap edges and cache

                                if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV5, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex5].v3Vertex);

                                if(pdicClippedEdges.ContainsKey(edgeKey1) == false) pdicClippedEdges.Add(edgeKey1, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));
                                if(pdicClippedEdges.ContainsKey(edgeKey3) == false) pdicClippedEdges.Add(edgeKey3, new ClippedEdge(nIndex1, nIndex3, nNewIndex1, nNewIndex3, nNewIndex5));

                                // Add geometry of other side

					            if(fSide2 < 0.0f)
					            {
						            plistVertexData     = listVertexDataNeg;
                                    plistObjectIndices  = listIndicesNeg;
                                    pFaceConnectivity   = faceConnectivityNeg;
                                    pMeshConnectivity   = meshConnectivityNeg;
                                    pdicClippedEdges    = dicClippedEdgesNeg;
                                    pdicRemappedIndices = dicRemappedIndicesNeg;
					            }
					            else
					            {
						            plistVertexData     = listVertexDataPos;
						            plistObjectIndices  = listIndicesPos;
                                    pFaceConnectivity   = faceConnectivityPos;
                                    pMeshConnectivity   = meshConnectivityPos;
                                    pdicClippedEdges    = dicClippedEdgesPos;
                                    pdicRemappedIndices = dicRemappedIndicesPos;
					            }

                                nNewIndex1 = -1;
                                nNewIndex2 = -1;
                                nNewIndex3 = -1;
                                nNewIndex4 = -1;
                                nNewIndex5 = -1;
                                bEdgeKey1  = false;
                                bEdgeKey3  = false;

                                // Find edges in cache

                                if(pdicClippedEdges.ContainsKey(edgeKey1))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey1  = true;
                                    nNewIndex2 = pdicClippedEdges[edgeKey1].GetSecondIndex(nIndex2);
                                    nNewIndex4 = pdicClippedEdges[edgeKey1].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                                }

                                if(pdicClippedEdges.ContainsKey(edgeKey3))
                                {
                                    nClippedCacheHits++;
                                    bEdgeKey3  = true;
                                    nNewIndex3 = pdicClippedEdges[edgeKey3].GetSecondIndex(nIndex3);
                                    nNewIndex5 = pdicClippedEdges[edgeKey3].nClippedIndex;
                                }
                                else
                                {
                                    nClippedCacheMisses++;
                                    if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                                }

                                // Add vertex data for all data not present in remapped list

                                if(nNewIndex2 == -1)
                                {
                                    nNewIndex2 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                    pdicRemappedIndices[nIndex2] = nNewIndex2;
                                }

                                if(nNewIndex3 == -1)
                                {
                                    nNewIndex3 = plistVertexData.Count;
                                    plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                    pdicRemappedIndices[nIndex3] = nNewIndex3;
                                }

                                if(nNewIndex4 == -1)
                                {
                                    nNewIndex4 = plistVertexData.Count;
                                    plistVertexData.Add(vd4);
                                }

                                if(nNewIndex5 == -1)
                                {
                                    nNewIndex5 = plistVertexData.Count;
                                    plistVertexData.Add(vd5);
                                }

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex4);
                                plistObjectIndices.Add(nNewIndex2);
                                plistObjectIndices.Add(nNewIndex3);

                                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                                {
                                    pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                                }

                                plistObjectIndices.Add(nNewIndex4);
                                plistObjectIndices.Add(nNewIndex3);
                                plistObjectIndices.Add(nNewIndex5);

                                if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                                {
                                    pFaceConnectivity.AddEdge(nSubMesh, v4, v2, nHashV4, nHashV2, nNewIndex4, nNewIndex2);
                                    pFaceConnectivity.AddEdge(nSubMesh, v2, v3, nHashV2, nHashV3, nNewIndex2, nNewIndex3);
                                    pFaceConnectivity.AddEdge(nSubMesh, v3, v4, nHashV3, nHashV4, nNewIndex3, nNewIndex4);
                                    pFaceConnectivity.AddEdge(nSubMesh, v4, v3, nHashV4, nHashV3, nNewIndex4, nNewIndex3);
                                    pFaceConnectivity.AddEdge(nSubMesh, v3, v5, nHashV3, nHashV5, nNewIndex3, nNewIndex5);
                                    pFaceConnectivity.AddEdge(nSubMesh, v5, v4, nHashV5, nHashV4, nNewIndex5, nNewIndex4);
                                }

                                // Update cap edges and cache

                                if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV5, nHashV4, plistVertexData[nNewIndex5].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                                if(pdicClippedEdges.ContainsKey(edgeKey1) == false) pdicClippedEdges.Add(edgeKey1, new ClippedEdge(nIndex1, nIndex2, nNewIndex1, nNewIndex2, nNewIndex4));
                                if(pdicClippedEdges.ContainsKey(edgeKey3) == false) pdicClippedEdges.Add(edgeKey3, new ClippedEdge(nIndex1, nIndex3, nNewIndex1, nNewIndex3, nNewIndex5));
				            }
			            }
			            else if(fSide2 * fSide3 < 0.0f)
			            {
				            // v1 and v2 on same side, and v3 on different side

					        if(fSide1 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
						        plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }

                            int  nNewIndex1 = -1;
                            int  nNewIndex2 = -1;
                            int  nNewIndex3 = -1;
                            int  nNewIndex4 = -1;
                            int  nNewIndex5 = -1;
                            int  nHashV4    = -1;
                            int  nHashV5    = -1;
                            bool bEdgeKey2  = false;
                            bool bEdgeKey3  = false;

                            EdgeKeyByIndex edgeKey2 = new EdgeKeyByIndex(nIndex2, nIndex3);
                            EdgeKeyByIndex edgeKey3 = new EdgeKeyByIndex(nIndex1, nIndex3);

                            // Find edges in cache

                            if(pdicClippedEdges.ContainsKey(edgeKey2))
                            {
                                nClippedCacheHits++;
                                bEdgeKey2  = true;
                                nNewIndex2 = pdicClippedEdges[edgeKey2].GetFirstIndex(nIndex2);
                                nNewIndex5 = pdicClippedEdges[edgeKey2].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex2)) nNewIndex2 = pdicRemappedIndices[nIndex2];
                            }

                            if(pdicClippedEdges.ContainsKey(edgeKey3))
                            {
                                nClippedCacheHits++;
                                bEdgeKey3  = true;
                                nNewIndex1 = pdicClippedEdges[edgeKey3].GetFirstIndex(nIndex1);
                                nNewIndex4 = pdicClippedEdges[edgeKey3].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex1)) nNewIndex1 = pdicRemappedIndices[nIndex1];
                            }

                            // Clip if not present in clipped edge list

                            EdgeKeyByHash clippedEdgeKeyHash = new EdgeKeyByHash(nHashV1, nHashV3);

                            if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                            {
                                nHashV4 = dicClipVerticesHash[clippedEdgeKeyHash];
                            }
                            else
                            {
                                nHashV4 = nCurrentVertexHash++;
                                dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV4);
                            }

                            clippedEdgeKeyHash = new EdgeKeyByHash(nHashV2, nHashV3);

                            if(dicClipVerticesHash.ContainsKey(clippedEdgeKeyHash))
                            {
                                nHashV5 = dicClipVerticesHash[clippedEdgeKeyHash];
                            }
                            else
                            {
                                nHashV5 = nCurrentVertexHash++;
                                dicClipVerticesHash.Add(clippedEdgeKeyHash, nHashV5);
                            }

                            VertexData vd4 = new VertexData(nHashV4), vd5 = new VertexData(nHashV5);

                            if(bEdgeKey2 == false)
                            {
                                if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex2, nIndex3, v2, v3, planeSplit, ref vd5) == false)
                                {
                                    return false;
                                }
                            }

                            if(bEdgeKey3 == false)
                            {
                                if(VertexData.ClipAgainstPlane(meshDataIn.aVertexData, nIndex1, nIndex3, v1, v3, planeSplit, ref vd4) == false)
                                {
                                    return false;
                                }
                            }

					        // Add geometry of one side

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex1 == -1)
                            {
                                nNewIndex1 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex1].Copy());
                                pdicRemappedIndices[nIndex1] = nNewIndex1;
                            }

                            if(nNewIndex2 == -1)
                            {
                                nNewIndex2 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex2].Copy());
                                pdicRemappedIndices[nIndex2] = nNewIndex2;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(nNewIndex5 == -1)
                            {
                                nNewIndex5 = plistVertexData.Count;
                                plistVertexData.Add(vd5);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex5);
                            plistObjectIndices.Add(nNewIndex4);

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex2);
                            plistObjectIndices.Add(nNewIndex4);
                            plistObjectIndices.Add(nNewIndex1);

                            Vector3 v4 = plistVertexData[nNewIndex4].v3Vertex;
                            Vector3 v5 = plistVertexData[nNewIndex5].v3Vertex;

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v5, nHashV2, nHashV5, nNewIndex2, nNewIndex5);
                                pFaceConnectivity.AddEdge(nSubMesh, v5, v4, nHashV5, nHashV4, nNewIndex5, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v2, nHashV4, nHashV2, nNewIndex4, nNewIndex2);
                                pFaceConnectivity.AddEdge(nSubMesh, v2, v4, nHashV2, nHashV4, nNewIndex2, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v1, nHashV4, nHashV1, nNewIndex4, nNewIndex1);
                                pFaceConnectivity.AddEdge(nSubMesh, v1, v2, nHashV1, nHashV2, nNewIndex1, nNewIndex2);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV5, nHashV4, plistVertexData[nNewIndex5].v3Vertex, plistVertexData[nNewIndex4].v3Vertex);

                            if(pdicClippedEdges.ContainsKey(edgeKey2) == false) pdicClippedEdges.Add(edgeKey2, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex5));
                            if(pdicClippedEdges.ContainsKey(edgeKey3) == false) pdicClippedEdges.Add(edgeKey3, new ClippedEdge(nIndex1, nIndex3, nNewIndex1, nNewIndex3, nNewIndex4));

                            // Add geometry of other side

					        if(fSide3 < 0.0f)
					        {
						        plistVertexData     = listVertexDataNeg;
                                plistObjectIndices  = listIndicesNeg;
                                pFaceConnectivity   = faceConnectivityNeg;
                                pMeshConnectivity   = meshConnectivityNeg;
                                pdicClippedEdges    = dicClippedEdgesNeg;
                                pdicRemappedIndices = dicRemappedIndicesNeg;
					        }
					        else
					        {
						        plistVertexData     = listVertexDataPos;
						        plistObjectIndices  = listIndicesPos;
                                pFaceConnectivity   = faceConnectivityPos;
                                pMeshConnectivity   = meshConnectivityPos;
                                pdicClippedEdges    = dicClippedEdgesPos;
                                pdicRemappedIndices = dicRemappedIndicesPos;
					        }

                            nNewIndex1 = -1;
                            nNewIndex2 = -1;
                            nNewIndex3 = -1;
                            nNewIndex4 = -1;
                            nNewIndex5 = -1;
                            bEdgeKey2  = false;
                            bEdgeKey3  = false;

                            // Find edges in cache

                            if(pdicClippedEdges.ContainsKey(edgeKey2))
                            {
                                nClippedCacheHits++;
                                bEdgeKey2  = true;
                                nNewIndex3 = pdicClippedEdges[edgeKey2].GetSecondIndex(nIndex3);
                                nNewIndex5 = pdicClippedEdges[edgeKey2].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                            }

                            if(pdicClippedEdges.ContainsKey(edgeKey3))
                            {
                                nClippedCacheHits++;
                                bEdgeKey3  = true;
                                nNewIndex3 = pdicClippedEdges[edgeKey3].GetSecondIndex(nIndex3);
                                nNewIndex4 = pdicClippedEdges[edgeKey3].nClippedIndex;
                            }
                            else
                            {
                                nClippedCacheMisses++;
                                if(pdicRemappedIndices.ContainsKey(nIndex3)) nNewIndex3 = pdicRemappedIndices[nIndex3];
                            }

                            // Add vertex data for all data not present in remapped list

                            if(nNewIndex3 == -1)
                            {
                                nNewIndex3 = plistVertexData.Count;
                                plistVertexData.Add(meshDataIn.aVertexData[nIndex3].Copy());
                                pdicRemappedIndices[nIndex3] = nNewIndex3;
                            }

                            if(nNewIndex4 == -1)
                            {
                                nNewIndex4 = plistVertexData.Count;
                                plistVertexData.Add(vd4);
                            }

                            if(nNewIndex5 == -1)
                            {
                                nNewIndex5 = plistVertexData.Count;
                                plistVertexData.Add(vd5);
                            }

                            if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoChunkConnectionInfo == false)
                            {
                                pMeshConnectivity.NotifyNewClippedFace(meshDataIn, nSubMesh, i, nSubMesh, plistObjectIndices.Count / 3);
                            }

                            plistObjectIndices.Add(nNewIndex5);
                            plistObjectIndices.Add(nNewIndex3);
                            plistObjectIndices.Add(nNewIndex4);

                            if(fracturedComponent.GenerateIslands && splitOptions.bForceNoIslandGeneration == false)
                            {
                                pFaceConnectivity.AddEdge(nSubMesh, v5, v3, nHashV5, nHashV3, nNewIndex5, nNewIndex3);
                                pFaceConnectivity.AddEdge(nSubMesh, v3, v4, nHashV3, nHashV4, nNewIndex3, nNewIndex4);
                                pFaceConnectivity.AddEdge(nSubMesh, v4, v5, nHashV4, nHashV5, nNewIndex4, nNewIndex5);
                            }

                            // Update cap edges and cache

                            if(plistVertexData == listVertexDataPos && splitOptions.bForceNoCap == false) AddCapEdge(dicCapEdges, nHashV4, nHashV5, plistVertexData[nNewIndex4].v3Vertex, plistVertexData[nNewIndex5].v3Vertex);

                            if(pdicClippedEdges.ContainsKey(edgeKey2) == false) pdicClippedEdges.Add(edgeKey2, new ClippedEdge(nIndex2, nIndex3, nNewIndex2, nNewIndex3, nNewIndex5));
                            if(pdicClippedEdges.ContainsKey(edgeKey3) == false) pdicClippedEdges.Add(edgeKey3, new ClippedEdge(nIndex1, nIndex3, nNewIndex1, nNewIndex3, nNewIndex4));
			            }
		            }
	            }
            }

//          Debug.Log("Clipped cache hits " + nClippedCacheHits + " clipped cache misses " + nClippedCacheMisses);

            // Compute transforms

            Vector3 v3CenterPos = Vector3.zero;

            if(listVertexDataPos.Count > 0)
            {
                Vector3 v3Min = Vector3.zero, v3Max = Vector3.zero;
                MeshData.ComputeMinMax(listVertexDataPos, ref v3Min, ref v3Max);
                v3CenterPos = (v3Min + v3Max) * 0.5f;
            }

            Matrix4x4 mtxToLocalPos = Matrix4x4.TRS(v3CenterPos, meshDataIn.qRotation, meshDataIn.v3Scale).inverse;

            if(splitOptions.bVerticesAreLocal)
            {
                mtxToLocalPos = Matrix4x4.TRS(v3CenterPos, Quaternion.identity, Vector3.one).inverse;
            }

            Vector3 v3CenterNeg = Vector3.zero;

            if(listVertexDataNeg.Count > 0)
            {
                Vector3 v3Min = Vector3.zero, v3Max = Vector3.zero;
                MeshData.ComputeMinMax(listVertexDataNeg, ref v3Min, ref v3Max);
                v3CenterNeg = (v3Min + v3Max) * 0.5f;
            }

            Matrix4x4 mtxToLocalNeg = Matrix4x4.TRS(v3CenterNeg, meshDataIn.qRotation, meshDataIn.v3Scale).inverse;

            if(splitOptions.bVerticesAreLocal)
            {
                mtxToLocalNeg = Matrix4x4.TRS(v3CenterNeg, Quaternion.identity, Vector3.one).inverse;
            }

            // Resolve cap outline and add its geometry

            List<List<Vector3>> listlistResolvedCapVertices   = new List<List<Vector3>>();
            List<List<int>>     listlistResolvedCapHashValues = new List<List<int>>();

            bool bNeedsConnectivityPostprocess = false;

            Matrix4x4 mtxPlane = Matrix4x4.TRS(v3PlanePoint, Quaternion.LookRotation(Vector3.Cross(v3PlaneNormal, v3PlaneRight), v3PlaneNormal), Vector3.one);

            if(dicCapEdges.Count > 0 && splitOptions.bForceNoCap == false)
            {
                if(ResolveCap(dicCapEdges, listlistResolvedCapVertices, listlistResolvedCapHashValues, fracturedComponent))
	            {
                    if(listlistResolvedCapVertices.Count > 1)
                    {
                        // There's more than one closed cap. We need to postprocess the mesh because there may be more than one object on a side of the plane as a result of the clipping.
                        bNeedsConnectivityPostprocess = (fracturedComponent.GenerateIslands && (splitOptions.bForceNoIslandGeneration == false)) ? true : false;
                    }

                    TriangulateConstrainedDelaunay( listlistResolvedCapVertices, listlistResolvedCapHashValues, splitOptions.bForceCapVertexSoup, fracturedComponent, bNeedsConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg,
                                                    meshConnectivityPos, meshConnectivityNeg, splitOptions.nForceMeshConnectivityHash, nSplitCloseSubMesh,
                                                    mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                                    alistIndicesPos, listVertexDataPos, alistIndicesNeg, listVertexDataNeg);
                }
                else
                {
                    if(fracturedComponent.Verbose) Debug.LogWarning("Error resolving cap");
                }
            }

            // Postprocess if necessary
            if(bNeedsConnectivityPostprocess)
            {
                // Search for multiple objects inside each meshes

                List<MeshData> listIslandsPos = MeshData.PostProcessConnectivity(meshDataIn, faceConnectivityPos, meshConnectivityPos, alistIndicesPos, listVertexDataPos, nSplitCloseSubMesh, nCurrentVertexHash, false);
                List<MeshData> listIslandsNeg = new List<MeshData>();

                if(splitOptions.bIgnoreNegativeSide == false)
                {
                    listIslandsNeg = MeshData.PostProcessConnectivity(meshDataIn, faceConnectivityNeg, meshConnectivityNeg, alistIndicesNeg, listVertexDataNeg, nSplitCloseSubMesh, nCurrentVertexHash, false);
                }

                // Sometimes we are feed a mesh with multiple islands as input. If this is the case, compute connectivity between islands at this point.

                List<MeshData> listTotalIslands = new List<MeshData>();
                listTotalIslands.AddRange(listIslandsPos);
                listTotalIslands.AddRange(listIslandsNeg);

                if(fracturedComponent.GenerateChunkConnectionInfo && splitOptions.bForceNoIslandConnectionInfo == false)
                {
                    for(int i = 0; i < listTotalIslands.Count; i++)
                    {
                        if(progress != null && listTotalIslands.Count > 10 && splitOptions.bForceNoProgressInfo == false)
                        {
                            progress("Fracturing", "Processing island connectivity...", i / (float)listTotalIslands.Count);
                            if(Fracturer.IsFracturingCancelled()) return false;
                        }

                        for(int j = 0; j < listTotalIslands.Count; j++)
                        {
                            if(i != j)
                            {
                                ComputeIslandsMeshDataConnectivity(fracturedComponent, splitOptions.bVerticesAreLocal, listTotalIslands[i], listTotalIslands[j]);
                            }
                        }
                    }
                }

                listMeshDatasPosOut.AddRange(listIslandsPos);
                listMeshDatasNegOut.AddRange(listIslandsNeg);
            }
            else
            {
                // Create new MeshDatas

                if(listVertexDataPos.Count > 0 && alistIndicesPos.Length > 0)
                {
                    MeshData newMeshData = new MeshData(meshDataIn.aMaterials, alistIndicesPos, listVertexDataPos, nSplitCloseSubMesh, v3CenterPos, meshDataIn.qRotation, meshDataIn.v3Scale, mtxToLocalPos, false, false);
                    newMeshData.meshDataConnectivity = meshConnectivityPos;
                    newMeshData.nCurrentVertexHash   = nCurrentVertexHash;
                    listMeshDatasPosOut.Add(newMeshData);
                }

                if(listVertexDataNeg.Count > 0 && alistIndicesNeg.Length > 0 && splitOptions.bIgnoreNegativeSide == false)
                {
                    MeshData newMeshData = new MeshData(meshDataIn.aMaterials, alistIndicesNeg, listVertexDataNeg, nSplitCloseSubMesh, v3CenterNeg, meshDataIn.qRotation, meshDataIn.v3Scale, mtxToLocalNeg, false, false);
                    newMeshData.meshDataConnectivity = meshConnectivityNeg;
                    newMeshData.nCurrentVertexHash   = nCurrentVertexHash;
                    listMeshDatasNegOut.Add(newMeshData);
                }
            }

            return true;
        }
示例#2
0
文件: Triangulate.cs 项目: wHo2/TMC
        static void TriangulateConstrainedDelaunay( List<List<Vector3>> listlistPointsConstrainedDelaunay, List<List<int>> listlistHashValuesConstrainedDelaunay, bool bForceVertexSoup, FracturedObject fracturedComponent,
                                                    bool bConnectivityPostprocess, MeshFaceConnectivity faceConnectivityPos, MeshFaceConnectivity faceConnectivityNeg, MeshDataConnectivity meshConnectivityPos, MeshDataConnectivity meshConnectivityNeg, int nForceMeshConnectivityHash,
                                                    int nSplitCloseSubMesh, Matrix4x4 mtxPlane, Matrix4x4 mtxToLocalPos, Matrix4x4 mtxToLocalNeg, Vector3 v3CenterPos, Vector3 v3CenterNeg,
                                                    List<int>[] aListIndicesPosInOut, List<VertexData> listVertexDataPosInOut, List<int>[] aListIndicesNegInOut, List<VertexData> listVertexDataNegInOut)
        {
            // Pass to two dimensional plane:

            Matrix4x4 mtxPlaneInverse = mtxPlane.inverse;
            List<List<Poly2Tri.Point2D>>      listlistCapPoints        = new List<List<Poly2Tri.Point2D>>();
            List<List<Poly2Tri.PolygonPoint>> listlistCapPolygonPoints = new List<List<Poly2Tri.PolygonPoint>>();
            List<Poly2Tri.Polygon>            listCapPolygons          = new List<Poly2Tri.Polygon>();

            for(int i = 0; i < listlistPointsConstrainedDelaunay.Count; i++)
            {
                List<Poly2Tri.Point2D>      listCapPoints         = new List<Poly2Tri.Point2D>();
                List<Poly2Tri.PolygonPoint> listCapPolygonsPoints = new List<Poly2Tri.PolygonPoint>();

                foreach(Vector3 v3Vertex in listlistPointsConstrainedDelaunay[i])
                {
                    Vector3 v3VertexInPlane = mtxPlaneInverse.MultiplyPoint3x4(v3Vertex);
                    listCapPoints.Add        (new Poly2Tri.Point2D     (v3VertexInPlane.x, v3VertexInPlane.z));
                    listCapPolygonsPoints.Add(new Poly2Tri.PolygonPoint(v3VertexInPlane.x, v3VertexInPlane.z));
                }

                listlistCapPoints.Add(listCapPoints);
                listlistCapPolygonPoints.Add(listCapPolygonsPoints);
                listCapPolygons.Add(new Poly2Tri.Polygon(listCapPolygonsPoints));
            }

            // Remove close vertices

            float fPrecisionFix = Mathf.Max(Parameters.EPSILONCAPPRECISIONMIN, fracturedComponent.CapPrecisionFix);

            if(fPrecisionFix > 0.0f)
            {
                for(int nCap = 0; nCap < listlistCapPolygonPoints.Count; nCap++)
                {
                    double lastX = listlistCapPolygonPoints[nCap][listlistCapPolygonPoints[nCap].Count - 1].X;
                    double lastY = listlistCapPolygonPoints[nCap][listlistCapPolygonPoints[nCap].Count - 1].Y;

                    bool bDeleteCap = false;

                    for(int nVertex = 0; nVertex < listlistCapPolygonPoints[nCap].Count; nVertex++)
                    {
                        double vecX = listlistCapPolygonPoints[nCap][nVertex].X - lastX;
                        double vecY = listlistCapPolygonPoints[nCap][nVertex].Y - lastY;

                        if(System.Math.Sqrt(vecX * vecX + vecY * vecY) < fPrecisionFix)
                        {
                            listlistCapPolygonPoints[nCap].RemoveAt(nVertex);
                            nVertex--;

                            if(listlistCapPolygonPoints[nCap].Count < 3)
                            {
                                bDeleteCap = true;
                                break;
                            }
                        }
                        else
                        {
                            lastX = listlistCapPolygonPoints[nCap][nVertex].X;
                            lastY = listlistCapPolygonPoints[nCap][nVertex].Y;
                        }
                    }

                    if(bDeleteCap)
                    {
                        listlistCapPolygonPoints.RemoveAt(nCap);
                        nCap--;
                    }
                }
            }

            if(listlistCapPolygonPoints.Count == 0)
            {
                return;
            }

            // Search if one of the caps is contained in the other. If this happens we will mark the big one as the polygon and the rest as holes

            int nSuperPolygon = -1;
            Poly2Tri.Polygon polygonContainer = null;

            if(bForceVertexSoup == false)
            {
                for(int i = 0; i < listlistCapPolygonPoints.Count; i++)
                {
                    for(int j = 0; j < listlistCapPolygonPoints.Count; j++)
                    {
                        if(i != j && listlistCapPoints[i].Count >= 3 && listlistCapPoints[j].Count >= 3)
                        {
                            if(Poly2Tri.PolygonUtil.PolygonContainsPolygon(listlistCapPoints[i], listCapPolygons[i].Bounds, listlistCapPoints[j], listCapPolygons[j].Bounds, true))
                            {
                                nSuperPolygon = i;
                                break;
                            }
                            else if(Poly2Tri.PolygonUtil.PolygonContainsPolygon(listlistCapPoints[j], listCapPolygons[j].Bounds, listlistCapPoints[i], listCapPolygons[i].Bounds, true))
                            {
                                nSuperPolygon = j;
                                break;
                            }
                        }
                    }
                }

                // Add holes if this is a cap with holes

                if(nSuperPolygon != -1)
                {
                    polygonContainer = listCapPolygons[nSuperPolygon];

                    for(int i = 0; i < listlistCapPolygonPoints.Count; i++)
                    {
                        if(i != nSuperPolygon && listCapPolygons[i].Count >= 3)
                        {
                            polygonContainer.AddHole(listCapPolygons[i]);
                        }
                    }
                }
            }

            // Triangulate

            bool bTriangulatedWithHoles = false;

            if(polygonContainer != null && bForceVertexSoup == false)
            {
                // Polygon with holes

                try
                {
                    Poly2Tri.P2T.Triangulate(polygonContainer);

                    if(polygonContainer.Triangles != null)
                    {
                        List<Vector3> listMeshVertices = new List<Vector3>();
                        List<int>     listMeshIndices  = new List<int>();

                        CreateIndexedMesh(polygonContainer.Triangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                        Triangulate(    listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                        bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                        nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                        aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                    }

                    bTriangulatedWithHoles = true;
                }
                catch(System.Exception e)
                {
                    if(fracturedComponent.Verbose) Debug.LogWarning("Exception (" + e.GetType() + ") using hole triangulation (holes = " + listlistCapPolygonPoints.Count + "). Trying to use constrained delaunay.");
                    bTriangulatedWithHoles = false;
                }
            }

            if(bTriangulatedWithHoles == false)
            {
                if(bForceVertexSoup)
                {
                    // Vertex soup

                    List<Poly2Tri.TriangulationPoint> listPoints = new List<Poly2Tri.TriangulationPoint>();

                    if(listlistCapPolygonPoints.Count > 0)
                    {
                        foreach(Poly2Tri.PolygonPoint polyPoint in listlistCapPolygonPoints[0])
                        {
                            listPoints.Add(polyPoint);
                        }

                        try
                        {
                            if(listPoints.Count >= 3)
                            {
                                Poly2Tri.PointSet ps = new Poly2Tri.PointSet(listPoints);
                                Poly2Tri.P2T.Triangulate(ps);

                                if(ps.Triangles != null)
                                {
                                    List<Vector3> listMeshVertices = new List<Vector3>();
                                    List<int>     listMeshIndices  = new List<int>();

                                    CreateIndexedMesh(ps.Triangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                                    Triangulate(    listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                                    bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                                    nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                                    aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                                }
                            }
                        }
                        catch(System.Exception e)
                        {
                            if(fracturedComponent.Verbose) Debug.LogWarning("Exception (" + e.GetType() + ") using vertex soup triangulation.");
                        }
                    }
                }
                else
                {
                    // Use constrained delaunay triangulation

                    int nPoly = 0;

                    foreach(List<Poly2Tri.PolygonPoint> listPolyPoints in listlistCapPolygonPoints)
                    {
                        IList<Poly2Tri.DelaunayTriangle> listTriangles = null;

                        Poly2Tri.Polygon polygon = null;

                        try
                        {
                            if(listPolyPoints.Count >= 3)
                            {
                                polygon = new Poly2Tri.Polygon(listPolyPoints);
                                Poly2Tri.P2T.Triangulate(polygon);
                                listTriangles = polygon.Triangles;
                            }
                        }
                        catch(System.Exception e)
                        {
                            if(fracturedComponent.Verbose) Debug.LogWarning("Exception (" + e.GetType() + ") using polygon triangulation of cap polygon " + nPoly + ". Trying to use non constrained");
                            listTriangles = null;
                        }

                        if(listTriangles == null)
                        {
                            List<Poly2Tri.TriangulationPoint> listPoints = new List<Poly2Tri.TriangulationPoint>();

                            foreach(Poly2Tri.PolygonPoint polyPoint in listPolyPoints)
                            {
                                listPoints.Add(polyPoint);
                            }

                            try
                            {
                                if(listPoints.Count >= 3)
                                {
                                    Poly2Tri.PointSet ps = new Poly2Tri.PointSet(listPoints);
                                    Poly2Tri.P2T.Triangulate(ps);
                                    listTriangles = ps.Triangles;
                                }
                            }
                            catch(System.Exception e)
                            {
                                if(fracturedComponent.Verbose) Debug.LogWarning("Exception (" + e.GetType() + ") using non constrained triangulation of cap polygon " + nPoly + ". Skipping");
                            }
                        }

                        if(listTriangles != null)
                        {
                            List<Vector3> listMeshVertices = new List<Vector3>();
                            List<int>     listMeshIndices  = new List<int>();

                            CreateIndexedMesh(listTriangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                            Triangulate(    listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                            bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                            nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                            aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                        }

                        nPoly++;
                    }
                }
            }
        }
示例#3
0
        static void TriangulateConstrainedDelaunay(List <List <Vector3> > listlistPointsConstrainedDelaunay, List <List <int> > listlistHashValuesConstrainedDelaunay, bool bForceVertexSoup, FracturedObject fracturedComponent,
                                                   bool bConnectivityPostprocess, MeshFaceConnectivity faceConnectivityPos, MeshFaceConnectivity faceConnectivityNeg, MeshDataConnectivity meshConnectivityPos, MeshDataConnectivity meshConnectivityNeg, int nForceMeshConnectivityHash,
                                                   int nSplitCloseSubMesh, Matrix4x4 mtxPlane, Matrix4x4 mtxToLocalPos, Matrix4x4 mtxToLocalNeg, Vector3 v3CenterPos, Vector3 v3CenterNeg,
                                                   List <int>[] aListIndicesPosInOut, List <VertexData> listVertexDataPosInOut, List <int>[] aListIndicesNegInOut, List <VertexData> listVertexDataNegInOut)
        {
            // Pass to two dimensional plane:

            Matrix4x4 mtxPlaneInverse = mtxPlane.inverse;
            List <List <Poly2Tri.Point2D> >      listlistCapPoints        = new List <List <Poly2Tri.Point2D> >();
            List <List <Poly2Tri.PolygonPoint> > listlistCapPolygonPoints = new List <List <Poly2Tri.PolygonPoint> >();
            List <Poly2Tri.Polygon> listCapPolygons = new List <Poly2Tri.Polygon>();

            for (int i = 0; i < listlistPointsConstrainedDelaunay.Count; i++)
            {
                List <Poly2Tri.Point2D>      listCapPoints         = new List <Poly2Tri.Point2D>();
                List <Poly2Tri.PolygonPoint> listCapPolygonsPoints = new List <Poly2Tri.PolygonPoint>();

                foreach (Vector3 v3Vertex in listlistPointsConstrainedDelaunay[i])
                {
                    Vector3 v3VertexInPlane = mtxPlaneInverse.MultiplyPoint3x4(v3Vertex);
                    listCapPoints.Add(new Poly2Tri.Point2D(v3VertexInPlane.x, v3VertexInPlane.z));
                    listCapPolygonsPoints.Add(new Poly2Tri.PolygonPoint(v3VertexInPlane.x, v3VertexInPlane.z));
                }

                listlistCapPoints.Add(listCapPoints);
                listlistCapPolygonPoints.Add(listCapPolygonsPoints);
                listCapPolygons.Add(new Poly2Tri.Polygon(listCapPolygonsPoints));
            }

            // Remove close vertices

            float fPrecisionFix = Mathf.Max(Parameters.EPSILONCAPPRECISIONMIN, fracturedComponent.CapPrecisionFix);

            if (fPrecisionFix > 0.0f)
            {
                for (int nCap = 0; nCap < listlistCapPolygonPoints.Count; nCap++)
                {
                    double lastX = listlistCapPolygonPoints[nCap][listlistCapPolygonPoints[nCap].Count - 1].X;
                    double lastY = listlistCapPolygonPoints[nCap][listlistCapPolygonPoints[nCap].Count - 1].Y;

                    bool bDeleteCap = false;

                    for (int nVertex = 0; nVertex < listlistCapPolygonPoints[nCap].Count; nVertex++)
                    {
                        double vecX = listlistCapPolygonPoints[nCap][nVertex].X - lastX;
                        double vecY = listlistCapPolygonPoints[nCap][nVertex].Y - lastY;

                        if (System.Math.Sqrt(vecX * vecX + vecY * vecY) < fPrecisionFix)
                        {
                            listlistCapPolygonPoints[nCap].RemoveAt(nVertex);
                            nVertex--;

                            if (listlistCapPolygonPoints[nCap].Count < 3)
                            {
                                bDeleteCap = true;
                                break;
                            }
                        }
                        else
                        {
                            lastX = listlistCapPolygonPoints[nCap][nVertex].X;
                            lastY = listlistCapPolygonPoints[nCap][nVertex].Y;
                        }
                    }

                    if (bDeleteCap)
                    {
                        listlistCapPolygonPoints.RemoveAt(nCap);
                        nCap--;
                    }
                }
            }

            if (listlistCapPolygonPoints.Count == 0)
            {
                return;
            }

            // Search if one of the caps is contained in the other. If this happens we will mark the big one as the polygon and the rest as holes

            int nSuperPolygon = -1;

            Poly2Tri.Polygon polygonContainer = null;

            if (bForceVertexSoup == false)
            {
                for (int i = 0; i < listlistCapPolygonPoints.Count; i++)
                {
                    for (int j = 0; j < listlistCapPolygonPoints.Count; j++)
                    {
                        if (i != j && listlistCapPoints[i].Count >= 3 && listlistCapPoints[j].Count >= 3)
                        {
                            if (Poly2Tri.PolygonUtil.PolygonContainsPolygon(listlistCapPoints[i], listCapPolygons[i].Bounds, listlistCapPoints[j], listCapPolygons[j].Bounds, true))
                            {
                                nSuperPolygon = i;
                                break;
                            }
                            else if (Poly2Tri.PolygonUtil.PolygonContainsPolygon(listlistCapPoints[j], listCapPolygons[j].Bounds, listlistCapPoints[i], listCapPolygons[i].Bounds, true))
                            {
                                nSuperPolygon = j;
                                break;
                            }
                        }
                    }
                }

                // Add holes if this is a cap with holes

                if (nSuperPolygon != -1)
                {
                    polygonContainer = listCapPolygons[nSuperPolygon];

                    for (int i = 0; i < listlistCapPolygonPoints.Count; i++)
                    {
                        if (i != nSuperPolygon && listCapPolygons[i].Count >= 3)
                        {
                            polygonContainer.AddHole(listCapPolygons[i]);
                        }
                    }
                }
            }

            // Triangulate

            bool bTriangulatedWithHoles = false;

            if (polygonContainer != null && bForceVertexSoup == false)
            {
                // Polygon with holes

                try
                {
                    Poly2Tri.P2T.Triangulate(polygonContainer);

                    if (polygonContainer.Triangles != null)
                    {
                        List <Vector3> listMeshVertices = new List <Vector3>();
                        List <int>     listMeshIndices  = new List <int>();

                        CreateIndexedMesh(polygonContainer.Triangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                        Triangulate(listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                    bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                    nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                    aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                    }

                    bTriangulatedWithHoles = true;
                }
                catch (System.Exception e)
                {
                    if (fracturedComponent.Verbose)
                    {
                        Debug.LogWarning("Exception (" + e.GetType() + ") using hole triangulation (holes = " + listlistCapPolygonPoints.Count + "). Trying to use constrained delaunay.");
                    }
                    bTriangulatedWithHoles = false;
                }
            }

            if (bTriangulatedWithHoles == false)
            {
                if (bForceVertexSoup)
                {
                    // Vertex soup

                    List <Poly2Tri.TriangulationPoint> listPoints = new List <Poly2Tri.TriangulationPoint>();

                    if (listlistCapPolygonPoints.Count > 0)
                    {
                        foreach (Poly2Tri.PolygonPoint polyPoint in listlistCapPolygonPoints[0])
                        {
                            listPoints.Add(polyPoint);
                        }

                        try
                        {
                            if (listPoints.Count >= 3)
                            {
                                Poly2Tri.PointSet ps = new Poly2Tri.PointSet(listPoints);
                                Poly2Tri.P2T.Triangulate(ps);

                                if (ps.Triangles != null)
                                {
                                    List <Vector3> listMeshVertices = new List <Vector3>();
                                    List <int>     listMeshIndices  = new List <int>();

                                    CreateIndexedMesh(ps.Triangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                                    Triangulate(listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                                bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                                nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                                aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                                }
                            }
                        }
                        catch (System.Exception e)
                        {
                            if (fracturedComponent.Verbose)
                            {
                                Debug.LogWarning("Exception (" + e.GetType() + ") using vertex soup triangulation.");
                            }
                        }
                    }
                }
                else
                {
                    // Use constrained delaunay triangulation

                    int nPoly = 0;

                    foreach (List <Poly2Tri.PolygonPoint> listPolyPoints in listlistCapPolygonPoints)
                    {
                        IList <Poly2Tri.DelaunayTriangle> listTriangles = null;

                        Poly2Tri.Polygon polygon = null;

                        try
                        {
                            if (listPolyPoints.Count >= 3)
                            {
                                polygon = new Poly2Tri.Polygon(listPolyPoints);
                                Poly2Tri.P2T.Triangulate(polygon);
                                listTriangles = polygon.Triangles;
                            }
                        }
                        catch (System.Exception e)
                        {
                            if (fracturedComponent.Verbose)
                            {
                                Debug.LogWarning("Exception (" + e.GetType() + ") using polygon triangulation of cap polygon " + nPoly + ". Trying to use non constrained");
                            }
                            listTriangles = null;
                        }

                        if (listTriangles == null)
                        {
                            List <Poly2Tri.TriangulationPoint> listPoints = new List <Poly2Tri.TriangulationPoint>();

                            foreach (Poly2Tri.PolygonPoint polyPoint in listPolyPoints)
                            {
                                listPoints.Add(polyPoint);
                            }

                            try
                            {
                                if (listPoints.Count >= 3)
                                {
                                    Poly2Tri.PointSet ps = new Poly2Tri.PointSet(listPoints);
                                    Poly2Tri.P2T.Triangulate(ps);
                                    listTriangles = ps.Triangles;
                                }
                            }
                            catch (System.Exception e)
                            {
                                if (fracturedComponent.Verbose)
                                {
                                    Debug.LogWarning("Exception (" + e.GetType() + ") using non constrained triangulation of cap polygon " + nPoly + ". Skipping");
                                }
                            }
                        }

                        if (listTriangles != null)
                        {
                            List <Vector3> listMeshVertices = new List <Vector3>();
                            List <int>     listMeshIndices  = new List <int>();

                            CreateIndexedMesh(listTriangles, listMeshVertices, listMeshIndices, mtxPlane, true);

                            Triangulate(listMeshVertices, listMeshIndices, fracturedComponent, listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay,
                                        bConnectivityPostprocess, faceConnectivityPos, faceConnectivityNeg, meshConnectivityPos, meshConnectivityNeg, nForceMeshConnectivityHash,
                                        nSplitCloseSubMesh, mtxPlane, mtxToLocalPos, mtxToLocalNeg, v3CenterPos, v3CenterNeg,
                                        aListIndicesPosInOut, listVertexDataPosInOut, aListIndicesNegInOut, listVertexDataNegInOut);
                        }

                        nPoly++;
                    }
                }
            }
        }
示例#4
0
文件: Triangulate.cs 项目: wHo2/TMC
        static void Triangulate(    List<Vector3> listVertices, List<int> listIndices, FracturedObject fracturedComponent, List<List<Vector3>> listlistPointsConstrainedDelaunay, List<List<int>> listlistHashValuesConstrainedDelaunay,
                                    bool bConnectivityPostprocess, MeshFaceConnectivity faceConnectivityPos, MeshFaceConnectivity faceConnectivityNeg, MeshDataConnectivity meshConnectivityPos, MeshDataConnectivity meshConnectivityNeg, int nForceMeshConnectivityHash,
                                    int nSplitCloseSubMesh, Matrix4x4 mtxPlane, Matrix4x4 mtxToLocalPos, Matrix4x4 mtxToLocalNeg, Vector3 v3CenterPos, Vector3 v3CenterNeg,
                                    List<int>[] aListIndicesPosInOut, List<VertexData> listVertexDataPosInOut, List<int>[] aListIndicesNegInOut, List<VertexData> listVertexDataNegInOut)
        {
            int nPositiveSideIndexStart = listVertexDataPosInOut.Count;
		    int nNegativeSideIndexStart = listVertexDataNegInOut.Count;

            if(listVertexDataPosInOut.Count < 1 || listVertexDataNegInOut.Count < 1)
            {
                return;
            }

            // Add vertex data

            VertexData[] aVtxDataCapPos = new VertexData[listVertices.Count];
            VertexData[] aVtxDataCapNeg = new VertexData[listVertices.Count];

            Vector3 v3PlaneNormal = mtxPlane.MultiplyVector(Vector3.up);

            float fReversedCapNormals = fracturedComponent.InvertCapNormals ? -1.0f : 1.0f;

            Vector3 v3CapNormalLocalPos = mtxToLocalPos.MultiplyVector(-v3PlaneNormal * fReversedCapNormals);
            Vector3 v3CapNormalLocalNeg = mtxToLocalNeg.MultiplyVector( v3PlaneNormal * fReversedCapNormals);

            Vector3 v3TangentPos = Vector3.right;
            v3TangentPos = mtxPlane.MultiplyVector(v3TangentPos);
            v3TangentPos = mtxToLocalPos.MultiplyVector(v3TangentPos);

            Vector3 v3TangentNeg = Vector3.right;
            v3TangentNeg = mtxPlane.MultiplyVector(v3TangentNeg);
            v3TangentNeg = mtxToLocalNeg.MultiplyVector(v3TangentNeg);

            Matrix4x4 mtxPlaneInverse = mtxPlane.inverse;

            Color32 colWhite  = new Color32(255, 255, 255, 255);
            Vector3 v2Mapping = Vector2.zero;

            for(int i = 0; i < listVertices.Count; i++)
            {
                Vector3 v3Local   = mtxPlaneInverse.MultiplyPoint3x4(listVertices[i]);
                v2Mapping.x = v3Local.x * fracturedComponent.SplitMappingTileU;
                v2Mapping.y = v3Local.z * fracturedComponent.SplitMappingTileV;

                int nVertexHash = ComputeVertexHash(listVertices[i], listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay);

                aVtxDataCapPos[i] = new VertexData(nVertexHash, listVertices[i], v3CapNormalLocalPos, v3TangentPos, colWhite, v2Mapping, v2Mapping, true, true, listVertexDataPosInOut[0].bHasColor32, listVertexDataPosInOut[0].bHasMapping1, listVertexDataPosInOut[0].bHasMapping2);
                aVtxDataCapNeg[i] = new VertexData(nVertexHash, listVertices[i], v3CapNormalLocalNeg, v3TangentNeg, colWhite, v2Mapping, v2Mapping, true, true, listVertexDataNegInOut[0].bHasColor32, listVertexDataNegInOut[0].bHasMapping1, listVertexDataNegInOut[0].bHasMapping2);
            }

            listVertexDataPosInOut.AddRange(aVtxDataCapPos);
            listVertexDataNegInOut.AddRange(aVtxDataCapNeg);

            // Add indices

            for(int i = 0; i < listIndices.Count / 3; i++)
		    {
                int nTriangleA = listIndices[i * 3 + 0];
                int nTriangleB = listIndices[i * 3 + 1];
                int nTriangleC = listIndices[i * 3 + 2];

                int nHashPosA  = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleA].nVertexHash;
                int nHashPosB  = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleB].nVertexHash;
                int nHashPosC  = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleC].nVertexHash;

                int nHashNegA  = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleA].nVertexHash;
                int nHashNegB  = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleB].nVertexHash;
                int nHashNegC  = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleC].nVertexHash;

                if(nHashPosA != -1 && nHashPosB != -1 && nHashPosC != -1 && nHashNegA != -1 && nHashNegB != -1 && nHashNegC != -1)
                {
                    int nMeshConnectivityHash = nForceMeshConnectivityHash == -1 ? MeshDataConnectivity.GetNewHash() : nForceMeshConnectivityHash; // New hash value to identify the 2 shared faces

                    if(fracturedComponent.GenerateChunkConnectionInfo)
                    {
                        meshConnectivityPos.NotifyNewCapFace(nMeshConnectivityHash, nSplitCloseSubMesh, aListIndicesPosInOut[nSplitCloseSubMesh].Count / 3);
                    }

                    aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleA);
			        aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleB);
			        aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleC);

                    if(bConnectivityPostprocess)
                    {
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleA], listVertices[nTriangleB], nHashPosA, nHashPosB, nPositiveSideIndexStart + nTriangleA, nPositiveSideIndexStart + nTriangleB);
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleB], listVertices[nTriangleC], nHashPosB, nHashPosC, nPositiveSideIndexStart + nTriangleB, nPositiveSideIndexStart + nTriangleC);
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleC], listVertices[nTriangleA], nHashPosC, nHashPosA, nPositiveSideIndexStart + nTriangleC, nPositiveSideIndexStart + nTriangleA);
                    }

                    if(fracturedComponent.GenerateChunkConnectionInfo)
                    {
                        meshConnectivityNeg.NotifyNewCapFace(nMeshConnectivityHash, nSplitCloseSubMesh, aListIndicesNegInOut[nSplitCloseSubMesh].Count / 3);
                    }

			        aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleA);
			        aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleC);
			        aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleB);

                    if(bConnectivityPostprocess)
                    {
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleA], listVertices[nTriangleC], nHashNegA, nHashNegC, nNegativeSideIndexStart + nTriangleA, nNegativeSideIndexStart + nTriangleC);
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleC], listVertices[nTriangleB], nHashNegC, nHashNegB, nNegativeSideIndexStart + nTriangleC, nNegativeSideIndexStart + nTriangleB);
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleB], listVertices[nTriangleA], nHashNegB, nHashNegA, nNegativeSideIndexStart + nTriangleB, nNegativeSideIndexStart + nTriangleA);
                    }
                }
            }
        }
示例#5
0
        static void Triangulate(List <Vector3> listVertices, List <int> listIndices, FracturedObject fracturedComponent, List <List <Vector3> > listlistPointsConstrainedDelaunay, List <List <int> > listlistHashValuesConstrainedDelaunay,
                                bool bConnectivityPostprocess, MeshFaceConnectivity faceConnectivityPos, MeshFaceConnectivity faceConnectivityNeg, MeshDataConnectivity meshConnectivityPos, MeshDataConnectivity meshConnectivityNeg, int nForceMeshConnectivityHash,
                                int nSplitCloseSubMesh, Matrix4x4 mtxPlane, Matrix4x4 mtxToLocalPos, Matrix4x4 mtxToLocalNeg, Vector3 v3CenterPos, Vector3 v3CenterNeg,
                                List <int>[] aListIndicesPosInOut, List <VertexData> listVertexDataPosInOut, List <int>[] aListIndicesNegInOut, List <VertexData> listVertexDataNegInOut)
        {
            int nPositiveSideIndexStart = listVertexDataPosInOut.Count;
            int nNegativeSideIndexStart = listVertexDataNegInOut.Count;

            if (listVertexDataPosInOut.Count < 1 || listVertexDataNegInOut.Count < 1)
            {
                return;
            }

            // Add vertex data

            VertexData[] aVtxDataCapPos = new VertexData[listVertices.Count];
            VertexData[] aVtxDataCapNeg = new VertexData[listVertices.Count];

            Vector3 v3PlaneNormal = mtxPlane.MultiplyVector(Vector3.up);

            float fReversedCapNormals = fracturedComponent.InvertCapNormals ? -1.0f : 1.0f;

            Vector3 v3CapNormalLocalPos = mtxToLocalPos.MultiplyVector(-v3PlaneNormal * fReversedCapNormals);
            Vector3 v3CapNormalLocalNeg = mtxToLocalNeg.MultiplyVector(v3PlaneNormal * fReversedCapNormals);

            Vector3 v3TangentPos = Vector3.right;

            v3TangentPos = mtxPlane.MultiplyVector(v3TangentPos);
            v3TangentPos = mtxToLocalPos.MultiplyVector(v3TangentPos);

            Vector3 v3TangentNeg = Vector3.right;

            v3TangentNeg = mtxPlane.MultiplyVector(v3TangentNeg);
            v3TangentNeg = mtxToLocalNeg.MultiplyVector(v3TangentNeg);

            Matrix4x4 mtxPlaneInverse = mtxPlane.inverse;

            Color32 colWhite  = new Color32(255, 255, 255, 255);
            Vector3 v2Mapping = Vector2.zero;

            for (int i = 0; i < listVertices.Count; i++)
            {
                Vector3 v3Local = mtxPlaneInverse.MultiplyPoint3x4(listVertices[i]);
                v2Mapping.x = v3Local.x * fracturedComponent.SplitMappingTileU;
                v2Mapping.y = v3Local.z * fracturedComponent.SplitMappingTileV;

                int nVertexHash = ComputeVertexHash(listVertices[i], listlistPointsConstrainedDelaunay, listlistHashValuesConstrainedDelaunay);

                aVtxDataCapPos[i] = new VertexData(nVertexHash, listVertices[i], v3CapNormalLocalPos, v3TangentPos, colWhite, v2Mapping, v2Mapping, true, true, listVertexDataPosInOut[0].bHasColor32, listVertexDataPosInOut[0].bHasMapping1, listVertexDataPosInOut[0].bHasMapping2);
                aVtxDataCapNeg[i] = new VertexData(nVertexHash, listVertices[i], v3CapNormalLocalNeg, v3TangentNeg, colWhite, v2Mapping, v2Mapping, true, true, listVertexDataNegInOut[0].bHasColor32, listVertexDataNegInOut[0].bHasMapping1, listVertexDataNegInOut[0].bHasMapping2);
            }

            listVertexDataPosInOut.AddRange(aVtxDataCapPos);
            listVertexDataNegInOut.AddRange(aVtxDataCapNeg);

            // Add indices

            for (int i = 0; i < listIndices.Count / 3; i++)
            {
                int nTriangleA = listIndices[i * 3 + 0];
                int nTriangleB = listIndices[i * 3 + 1];
                int nTriangleC = listIndices[i * 3 + 2];

                int nHashPosA = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleA].nVertexHash;
                int nHashPosB = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleB].nVertexHash;
                int nHashPosC = listVertexDataPosInOut[nPositiveSideIndexStart + nTriangleC].nVertexHash;

                int nHashNegA = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleA].nVertexHash;
                int nHashNegB = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleB].nVertexHash;
                int nHashNegC = listVertexDataNegInOut[nNegativeSideIndexStart + nTriangleC].nVertexHash;

                if (nHashPosA != -1 && nHashPosB != -1 && nHashPosC != -1 && nHashNegA != -1 && nHashNegB != -1 && nHashNegC != -1)
                {
                    int nMeshConnectivityHash = nForceMeshConnectivityHash == -1 ? MeshDataConnectivity.GetNewHash() : nForceMeshConnectivityHash; // New hash value to identify the 2 shared faces

                    if (fracturedComponent.GenerateChunkConnectionInfo)
                    {
                        meshConnectivityPos.NotifyNewCapFace(nMeshConnectivityHash, nSplitCloseSubMesh, aListIndicesPosInOut[nSplitCloseSubMesh].Count / 3);
                    }

                    aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleA);
                    aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleB);
                    aListIndicesPosInOut[nSplitCloseSubMesh].Add(nPositiveSideIndexStart + nTriangleC);

                    if (bConnectivityPostprocess)
                    {
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleA], listVertices[nTriangleB], nHashPosA, nHashPosB, nPositiveSideIndexStart + nTriangleA, nPositiveSideIndexStart + nTriangleB);
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleB], listVertices[nTriangleC], nHashPosB, nHashPosC, nPositiveSideIndexStart + nTriangleB, nPositiveSideIndexStart + nTriangleC);
                        faceConnectivityPos.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleC], listVertices[nTriangleA], nHashPosC, nHashPosA, nPositiveSideIndexStart + nTriangleC, nPositiveSideIndexStart + nTriangleA);
                    }

                    if (fracturedComponent.GenerateChunkConnectionInfo)
                    {
                        meshConnectivityNeg.NotifyNewCapFace(nMeshConnectivityHash, nSplitCloseSubMesh, aListIndicesNegInOut[nSplitCloseSubMesh].Count / 3);
                    }

                    aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleA);
                    aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleC);
                    aListIndicesNegInOut[nSplitCloseSubMesh].Add(nNegativeSideIndexStart + nTriangleB);

                    if (bConnectivityPostprocess)
                    {
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleA], listVertices[nTriangleC], nHashNegA, nHashNegC, nNegativeSideIndexStart + nTriangleA, nNegativeSideIndexStart + nTriangleC);
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleC], listVertices[nTriangleB], nHashNegC, nHashNegB, nNegativeSideIndexStart + nTriangleC, nNegativeSideIndexStart + nTriangleB);
                        faceConnectivityNeg.AddEdge(nSplitCloseSubMesh, listVertices[nTriangleB], listVertices[nTriangleA], nHashNegB, nHashNegA, nNegativeSideIndexStart + nTriangleB, nNegativeSideIndexStart + nTriangleA);
                    }
                }
            }
        }