float Cut(Mesh mesh, Transform meshTransform, Math.Plane plane, bool triangulateHoles, bool allowOpenMesh, ref List<CutterMesh> meshes) { var stopWatch = new Stopwatch(); stopWatch.Start(); #if PROFILING MeasureIt.Begin("CutAllocations"); #endif // cache mesh data var trianglesNum = mesh.triangles.Length; var verticesNum = mesh.vertices.Length; var meshTriangles = mesh.triangles; var meshTangents = mesh.tangents; var meshColors = mesh.colors32; var meshVertices = mesh.vertices; var meshNormals = mesh.normals; var meshUV = mesh.uv; var useMeshTangents = meshTangents != null && meshTangents.Length > 0; var useVertexColors = meshColors != null && meshColors.Length > 0; var useNormals = meshNormals != null && meshNormals.Length > 0; // preallocate buffers AllocateBuffers(trianglesNum, verticesNum, useMeshTangents, useVertexColors); CutterMesh mesh0, mesh1; #if PROFILING MeasureIt.End("CutAllocations"); MeasureIt.Begin("CutCycleFirstPass"); #endif // inverse transform cutting plane plane.InverseTransform(meshTransform); // first pass - find complete triangles on both sides of the plane for (int i = 0; i < trianglesNum; i += 3) { // get triangle points var v0 = meshVertices[meshTriangles[i]]; var v1 = meshVertices[meshTriangles[i + 1]]; var v2 = meshVertices[meshTriangles[i + 2]]; var side0 = plane.GetSideFix(ref v0); var side1 = plane.GetSideFix(ref v1); var side2 = plane.GetSideFix(ref v2); meshVertices[meshTriangles[i]] = v0; meshVertices[meshTriangles[i + 1]] = v1; meshVertices[meshTriangles[i + 2]] = v2; // Utils.Log(plane.Pnt + " " + v0 + " " + v1 + " " + " " + v2); // all points on one side if (side0 == side1 && side1 == side2) { var idx = side0 ? 0 : 1; if (meshTriangles[i] >= triCache.Length) { ExploderUtils.Log("TriCacheError " + meshTriangles[i] + " " + triCache.Length + " " + meshVertices.Length); } if (triCache[meshTriangles[i]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i]]); uvs[idx].Add(meshUV[meshTriangles[i]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i]]); } centroid[idx] += meshVertices[meshTriangles[i]]; triCache[meshTriangles[i]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i]] - 1); } if (triCache[meshTriangles[i + 1]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 1]]); uvs[idx].Add(meshUV[meshTriangles[i + 1]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 1]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i+1]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i+1]]); } centroid[idx] += meshVertices[meshTriangles[i + 1]]; triCache[meshTriangles[i + 1]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 1]] - 1); } if (triCache[meshTriangles[i + 2]] == 0) { triangles[idx].Add(triCounter[idx]); vertices[idx].Add(meshVertices[meshTriangles[i + 2]]); uvs[idx].Add(meshUV[meshTriangles[i + 2]]); if (useNormals) { normals[idx].Add(meshNormals[meshTriangles[i + 2]]); } if (useMeshTangents) { tangents[idx].Add(meshTangents[meshTriangles[i+2]]); } if (useVertexColors) { vertexColors[idx].Add(meshColors[meshTriangles[i+2]]); } centroid[idx] += meshVertices[meshTriangles[i + 2]]; triCache[meshTriangles[i + 2]] = triCounter[idx] + 1; triCounter[idx]++; } else { triangles[idx].Add(triCache[meshTriangles[i + 2]] - 1); } } else { // intersection triangles add to list and process it in second pass cutTris.Add(i); } } if (vertices[0].Count == 0) { centroid[0] = meshVertices[0]; } else { centroid[0] /= vertices[0].Count; } if (vertices[1].Count == 0) { centroid[1] = meshVertices[1]; } else { centroid[1] /= vertices[1].Count; } #if PROFILING MeasureIt.End("CutCycleFirstPass"); MeasureIt.Begin("CutCycleSecondPass"); #endif mesh0.centroid = centroid[0]; mesh1.centroid = centroid[1]; mesh0.mesh = null; mesh1.mesh = null; if (cutTris.Count < 1) { stopWatch.Stop(); return stopWatch.ElapsedMilliseconds; } AllocateContours(cutTris.Count); // second pass - cut intersecting triangles in half foreach (var cutTri in cutTris) { var triangle = new Triangle { ids = new[] { meshTriangles[cutTri + 0], meshTriangles[cutTri + 1], meshTriangles[cutTri + 2] }, pos = new[] { meshVertices[meshTriangles[cutTri + 0]], meshVertices[meshTriangles[cutTri + 1]], meshVertices[meshTriangles[cutTri + 2]] }, normal = useNormals ? new[] { meshNormals[meshTriangles[cutTri + 0]], meshNormals[meshTriangles[cutTri + 1]], meshNormals[meshTriangles[cutTri + 2]] } : new[] { Vector3.zero, Vector3.zero, Vector3.zero }, uvs = new[] { meshUV[meshTriangles[cutTri + 0]], meshUV[meshTriangles[cutTri + 1]], meshUV[meshTriangles[cutTri + 2]] }, tangents = useMeshTangents ? new[] { meshTangents[meshTriangles[cutTri + 0]], meshTangents[meshTriangles[cutTri + 1]], meshTangents[meshTriangles[cutTri + 2]]} : new []{ Vector4.zero, Vector4.zero, Vector4.zero }, colors = useVertexColors ? new[] { meshColors[meshTriangles[cutTri + 0]], meshColors[meshTriangles[cutTri + 1]], meshColors[meshTriangles[cutTri + 2]] } : new Color32[] { Color.white, Color.white, Color.white }, }; // check points with a plane var side0 = plane.GetSide(triangle.pos[0]); var side1 = plane.GetSide(triangle.pos[1]); var side2 = plane.GetSide(triangle.pos[2]); float t0, t1; Vector3 s0 = Vector3.zero, s1 = Vector3.zero; var idxLeft = side0 ? 0 : 1; var idxRight = 1 - idxLeft; if (side0 == side1) { var a = plane.IntersectSegment(triangle.pos[2], triangle.pos[0], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[2], triangle.pos[1], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v1Left = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, v0, s1) triangles[idxLeft].Add(s0Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // Triangle (s1, v0, v1) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(v1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[2], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[2], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s0, s1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else if (side0 == side2) { var a = plane.IntersectSegment(triangle.pos[1], triangle.pos[0], out t0, ref s1); var b = plane.IntersectSegment(triangle.pos[1], triangle.pos[2], out t1, ref s0); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // left side ... 2 triangles var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v2Left = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, s0) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(s0Left); // Triangle (v2, v0, s1) triangles[idxLeft].Add(v2Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s1Left); // right side ... 1 triangle var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[1], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[1], triangle.ids[0], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (s0, s1, v1) triangles[idxRight].Add(s0Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } else { var a = plane.IntersectSegment(triangle.pos[0], triangle.pos[1], out t0, ref s0); var b = plane.IntersectSegment(triangle.pos[0], triangle.pos[2], out t1, ref s1); ExploderUtils.Assert(a && b, "!!!!!!!!!!!!!"); // right side ... 2 triangles var s0Right = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var s1Right = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v1Right = AddTrianglePoint(triangle.pos[1], triangle.normal[1], triangle.uvs[1], triangle.tangents[1], triangle.colors[1], triangle.ids[1], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); var v2Right = AddTrianglePoint(triangle.pos[2], triangle.normal[2], triangle.uvs[2], triangle.tangents[2], triangle.colors[2], triangle.ids[2], triCache, cornerVertCache[idxRight], vertices[idxRight], normals[idxRight], uvs[idxRight], tangents[idxRight], vertexColors[idxRight], useMeshTangents, useVertexColors, useNormals); // Triangle (v2, s1, v1) triangles[idxRight].Add(v2Right); triangles[idxRight].Add(s1Right); triangles[idxRight].Add(v1Right); // Triangle (s1, s0, v1) triangles[idxRight].Add(s1Right); triangles[idxRight].Add(s0Right); triangles[idxRight].Add(v1Right); // left side ... 1 triangle var s0Left = AddIntersectionPoint(s0, triangle, triangle.ids[0], triangle.ids[1], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var s1Left = AddIntersectionPoint(s1, triangle, triangle.ids[0], triangle.ids[2], cutVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); var v0Left = AddTrianglePoint(triangle.pos[0], triangle.normal[0], triangle.uvs[0], triangle.tangents[0], triangle.colors[0], triangle.ids[0], triCache, cornerVertCache[idxLeft], vertices[idxLeft], normals[idxLeft], uvs[idxLeft], tangents[idxLeft], vertexColors[idxLeft], useMeshTangents, useVertexColors, useNormals); // Triangle (s1, v0, s0) triangles[idxLeft].Add(s1Left); triangles[idxLeft].Add(v0Left); triangles[idxLeft].Add(s0Left); // buffer intersection vertices for triangulation if (triangulateHoles) { if (idxLeft == 0) { contour.AddTriangle(cutTri, s0Left, s1Left, s0, s1); } else { contour.AddTriangle(cutTri, s0Right, s1Right, s0, s1); } } } } #if PROFILING MeasureIt.End("CutCycleSecondPass"); #endif if (triangulateHoles) { #if PROFILING MeasureIt.Begin("FindContours"); #endif contour.FindContours(); if (contour.contour.Count == 0 || contour.contour[0].Count < 3) { if (allowOpenMesh) { triangulateHoles = false; } else { stopWatch.Stop(); return stopWatch.ElapsedMilliseconds; } } #if PROFILING MeasureIt.End("FindContours"); #endif } List<int>[] trianglesCut = null; if (triangulateHoles) { #if PROFILING MeasureIt.Begin("Triangulate"); #endif trianglesCut = new List<int>[2] {new List<int>(contour.MidPointsCount), new List<int>(contour.MidPointsCount)}; Triangulate(contour.contour, plane, vertices, normals, uvs, tangents, vertexColors, trianglesCut, true, useMeshTangents, useVertexColors, useNormals); #if PROFILING MeasureIt.End("Triangulate"); #endif } if (vertices[0].Count > 3 && vertices[1].Count > 3) { #if PROFILING MeasureIt.Begin("CutEndCopyBack"); #endif mesh0.mesh = new Mesh(); mesh1.mesh = new Mesh(); var verticesArray0 = vertices[0].ToArray(); var verticesArray1 = vertices[1].ToArray(); mesh0.mesh.vertices = verticesArray0; mesh0.mesh.uv = uvs[0].ToArray(); mesh1.mesh.vertices = verticesArray1; mesh1.mesh.uv = uvs[1].ToArray(); if (useNormals) { mesh0.mesh.normals = normals[0].ToArray(); mesh1.mesh.normals = normals[1].ToArray(); } if (useMeshTangents) { mesh0.mesh.tangents = tangents[0].ToArray(); mesh1.mesh.tangents = tangents[1].ToArray(); } if (useVertexColors) { mesh0.mesh.colors32 = vertexColors[0].ToArray(); mesh1.mesh.colors32 = vertexColors[1].ToArray(); } if (trianglesCut != null && trianglesCut[0].Count > 3) { triangles[0].AddRange(trianglesCut[0]); triangles[1].AddRange(trianglesCut[1]); } mesh0.mesh.triangles = triangles[0].ToArray(); mesh1.mesh.triangles = triangles[1].ToArray(); if (!triangulateHoles) { // don't triangulate holes means the mesh is a 2d plane // it is better to recalculate centroid to get precise center not just an approximation mesh0.centroid = Vector3.zero; mesh1.centroid = Vector3.zero; foreach (var p in vertices[0]) { mesh0.centroid += p; } mesh0.centroid /= vertices[0].Count; foreach (var p in vertices[1]) { mesh1.centroid += p; } mesh1.centroid /= vertices[1].Count; } #if PROFILING MeasureIt.End("CutEndCopyBack"); #endif meshes = new List<CutterMesh> { mesh0, mesh1 }; stopWatch.Stop(); return stopWatch.ElapsedMilliseconds; } stopWatch.Stop(); // UnityEngine.Debug.Log("Empty cut! " + vertices[0].Count + " " + vertices[1].Count); return stopWatch.ElapsedMilliseconds; }