/// <summary> /// Export the HMesh as a number of meshes, split into a number of subregions. /// If the parameter used material is provided, the mesh only contains submeshes with geometry and the submesh index /// is added to the used material. /// Is a mesh does not contain any vertices it is skipped. /// </summary> public List <Mesh> ExportSplit(Vector3i axisSplit, List <List <int> > usedMaterials = null, double sharpEdgeAngle = 360) { var bounds = ComputeBoundsD(); bounds.extents += Vector3D.one * 0.000001; // add delta size to ensure no vertex is on bounds var resList = new List <Mesh>(); // Enumerate vertices for (int i = 0; i < vertices.Count; i++) { vertices[i].label = i; } var maxFaceLabel = 0; foreach (var face in faces) { maxFaceLabel = Mathf.Max(maxFaceLabel, face.label); } int clusterCount = 0; MarkSharpEdges(sharpEdgeAngle); var faceClusters = SeparateMeshByGeometry(out clusterCount); Debug.Log("Face clusters: " + clusterCount); var remap = new int[vertices.Count]; for (var i = 0; i < axisSplit[0]; i++) { double minX = bounds.min.x + i * (bounds.size.x / axisSplit[0]); double maxX = bounds.min.x + (i + 1) * (bounds.size.x / axisSplit[0]); for (var j = 0; j < axisSplit[1]; j++) { double minY = bounds.min.y + j * (bounds.size.y / axisSplit[1]); double maxY = bounds.min.y + (j + 1) * (bounds.size.y / axisSplit[1]); for (var k = 0; k < axisSplit[2]; k++) { double minZ = bounds.min.z + k * (bounds.size.z / axisSplit[2]); double maxZ = bounds.min.z + (k + 1) * (bounds.size.z / axisSplit[2]); // DebugExt.DrawBox(new Vector3D(minX, minY, minZ).ToVector3(), new Vector3D(maxX, maxY, maxZ).ToVector3(),Color.white, 10); var min = new Vector3D(minX, minY, minZ); var max = new Vector3D(maxX, maxY, maxZ); var res = new Mesh(); res.name = "HMesh_" + i + "," + j + "," + k; var vertexArray = new List <Vector3>(); var normalArray = new List <Vector3>(); var uv1 = new List <Vector2>(); var uv2 = new List <Vector2>(); res.subMeshCount = maxFaceLabel + 1; List <Face> facesInRegion = new List <Face>(); foreach (var face in faces) { var center = face.GetCenter(); if (Vector3D.AllLessThan(min, center) && Vector3D.AllLessEqualThan(center, max)) { facesInRegion.Add(face); } } var triangles = new List <List <int> >(); for (int ii = 0; ii <= maxFaceLabel; ii++) { triangles.Add(new List <int>()); } for (int clusterIndex = 0; clusterIndex < clusterCount; clusterIndex++) { // clear remap for (int x = 0; x < remap.Length; x++) { remap[x] = -1; } for (var faceLabel = 0; faceLabel <= maxFaceLabel; faceLabel++) { foreach (var face in facesInRegion) { if (faceClusters[face.id] != clusterIndex) { continue; } if (face.label == faceLabel) { if (face.NoEdges() != 3) { Debug.LogError("Only triangles supported. Was " + face.NoEdges() + "-gon"); continue; } var he = face.halfedge; var first = true; while (he != face.halfedge || first) { var indexOfVertex = he.vert.label; var indexOfVertexRemapped = remap[indexOfVertex]; if (indexOfVertexRemapped == -1) { indexOfVertexRemapped = vertexArray.Count; remap[indexOfVertex] = indexOfVertexRemapped; vertexArray.Add(vertices[indexOfVertex].position); uv1.Add(vertices[indexOfVertex].uv1); uv2.Add(vertices[indexOfVertex].uv2); // compute normal Vector3D n = Vector3D.zero; int count = 0; foreach (var vertHe in he.vert.CirculateAllIngoing()) { if (faceClusters[vertHe.face.id] == clusterIndex) { double angle = Vector3D.Angle(-vertHe.GetDirection(), vertHe.next.GetDirection()); n += angle * vertHe.face.GetNormal(); count++; } } if (n.sqrMagnitude <= 0 || double.IsNaN(Vector3D.Dot(n, n))) { Debug.LogWarning("Cannot compute normal n is " + n.ToString("R") + " edges of vertex " + he.vert.Circulate().Count + " same cluster " + count + " " + he.face.ToString()); foreach (var vertHe in he.vert.CirculateAllIngoing()) { if (faceClusters[vertHe.face.id] == clusterIndex) { Debug.Log(vertHe.face.ToString()); } } n = new Vector3D(0, 1, 0); } else { n.Normalize(); } normalArray.Add(n.ToVector3()); } triangles[faceLabel].Add(indexOfVertexRemapped); he = he.next; first = false; } } } } } if (vertexArray.Count == 0) { // empty mesh - skip continue; } resList.Add(res); if (vertexArray.Count > 65000) { Debug.LogWarning("Vertex count was " + vertexArray.Count); } res.vertices = vertexArray.ToArray(); res.uv = uv1.ToArray(); res.uv2 = uv2.ToArray(); res.normals = normalArray.ToArray(); // add mesh indices // if usedMaterials exists then filter out any empty submesh List <int> materialIndices = null; if (usedMaterials != null) { materialIndices = new List <int>(); usedMaterials.Add(materialIndices); } for (int ii = 0; ii < triangles.Count; ii++) { if (materialIndices != null) { if (triangles[ii].Count > 0) { res.SetIndices(triangles[ii].ToArray(), MeshTopology.Triangles, materialIndices.Count, true); materialIndices.Add(ii); } } else { res.SetIndices(triangles[ii].ToArray(), MeshTopology.Triangles, ii, true); } } res.RecalculateBounds(); } } } return(resList); }