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; }
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++; } } } }
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++; } } } }
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); } } } }
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); } } } }