private void populateMatrix(List <Face> faces, List <Face>[,,] matrix, Extent size) { Trace.TraceInformation("Partitioning Faces."); int xLength = matrix.GetLength(0); int yLength = matrix.GetLength(1); int zLength = matrix.GetLength(2); // World to cube ratios double xOffset = 0 - size.XMin; double xRatio = xLength / (size.XSize); double yOffset = 0 - size.YMin; double yRatio = yLength / (size.YSize); double zOffset = 0 - size.ZMin; double zRatio = zLength / (size.ZSize); // Initialize matrix SpatialUtilities.EnumerateSpace(xLength, yLength, zLength, (x, y, z) => matrix[x, y, z] = new List <Face>()); foreach (var face in faces) { List <int> used = new List <int>(); for (int i = 0; i < 3; i++) { Vertex vertex = VertexList[face.VertexIndexList[i] - 1]; int x = (int)Math.Floor((vertex.X + xOffset) * xRatio); int y = (int)Math.Floor((vertex.Y + yOffset) * yRatio); int z = (int)Math.Floor((vertex.Z + zOffset) * zRatio); if (x == xLength) { x--; } if (y == yLength) { y--; } if (z == zLength) { z--; } int hash = x * 10000 + y * 100 + z; if (!used.Contains(hash)) { matrix[x, y, z].Add(face); used.Add(hash); } } } // Crop all the cubes SpatialUtilities.EnumerateSpace(xLength, yLength, zLength, (x, y, z) => CropCube(matrix[x, y, z], x, y, z, matrix, size)); }
private void CropCube(List <Face> chunkFaceList, int cubeX, int cubeY, int cubeZ, List <Face>[,,] matrix, Extent size) { double cubeHeight; double cubeWidth; double cubeDepth; cubeHeight = size.YSize / matrix.GetLength(1); cubeWidth = size.XSize / matrix.GetLength(0); cubeDepth = size.ZSize / matrix.GetLength(2); double yOffset = cubeHeight * cubeY; double xOffset = cubeWidth * cubeX; double zOffset = cubeDepth * cubeZ; Extent cubeExtent = new Extent { XMin = size.XMin + xOffset, YMin = size.YMin + yOffset, ZMin = size.ZMin + zOffset, XMax = size.XMin + xOffset + cubeWidth, YMax = size.YMin + yOffset + cubeHeight, ZMax = size.ZMin + zOffset + cubeDepth }; Dictionary <Face, List <Vertex> > facesToRepair = new Dictionary <Face, List <Vertex> >(); // Enumerate vertices for ones crossing bounds foreach (var face in chunkFaceList) { var vertices = FindOutOfBoundVertices(face, cubeExtent); if (vertices.Any()) { facesToRepair.Add(face, vertices); } } foreach (var face in facesToRepair.Keys) { // Type 1 - yields two triangles - 2 of the 3 vertices are in-bounds. if (facesToRepair[face].Count == 1) { Vertex croppedVertex = facesToRepair[face].First(); Vertex[] newVertices = new Vertex[2]; // Find the vertices we are keeping var allVerts = face.VertexIndexList.Select(i => VertexList[i - 1]); Vertex[] homeVertices = allVerts.Except(new List <Vertex> { croppedVertex }).ToArray(); // First triangle, use existing face var intersectionA = SpatialUtilities.CheckLineBox( cubeExtent.MinCorner, cubeExtent.MaxCorner, new Vector3D(croppedVertex), new Vector3D(homeVertices[0])); var intersectionB = SpatialUtilities.CheckLineBox( cubeExtent.MinCorner, cubeExtent.MaxCorner, new Vector3D(croppedVertex), new Vector3D(homeVertices[1])); if (intersectionA != null && intersectionB != null) { // Clone the face before we edit it, to use for the new face var newFaceA = face.Clone(); var newFaceB = face.Clone(); // Update the UVs before the vertices so we can key off the original vertices // New UV for NewVertexA / IntersectionA, which is a new point between homeVertices[0] and croppedVertex var resultA = CalculateNewUV(face, croppedVertex, homeVertices[0], intersectionA); newFaceA.UpdateTextureVertexIndex(resultA.OldIndex, resultA.NewIndex, false); // New UV for NewVertexB / IntersectionB, which is a new point between homeVertices[1] and croppedVertex var resultB = CalculateNewUV(newFaceB, croppedVertex, homeVertices[1], intersectionB); newFaceB.UpdateTextureVertexIndex(resultB.OldIndex, resultB.NewIndex, false); // Now update the vertices // Add a new vertex and update the existing face int length = VertexList.Count(); var NewVertexA = new Vertex { Index = length + 1, X = intersectionA.X, Y = intersectionA.Y, Z = intersectionA.Z }; VertexList.Add(NewVertexA); newFaceA.UpdateVertexIndex(croppedVertex.Index, length + 1, false); // Replace original face with newFaceA chunkFaceList.Add(newFaceA); chunkFaceList.Remove(face); // Add another new vertex for the net-new face length++; var NewVertexB = new Vertex { Index = length + 1, X = intersectionB.X, Y = intersectionB.Y, Z = intersectionB.Z }; VertexList.Add(NewVertexB); // Add the net-new face // TODO: Almost certainly leaving the face and vertex list incorrect for future cubes // Won't really know until I do a run and see what is broken... chunkFaceList.Add(newFaceB); newFaceB.UpdateVertexIndex(homeVertices[0].Index, length, false); newFaceB.UpdateVertexIndex(croppedVertex.Index, length + 1, false); } } // Type 2 - yields single triangle - 1 of the 3 vertices are in-bounds. else if (facesToRepair[face].Count == 2) { Vertex[] croppedVertices = facesToRepair[face].ToArray(); Vertex[] newVertices = new Vertex[2]; // Find the vertex we are keeping var allVerts = face.VertexIndexList.Select(i => VertexList[i - 1]); Vertex homeVertex = allVerts.Except(croppedVertices).First(); // Create new face bool doReplacement = false; var newFace = face.Clone(); for (int i = 0; i < 2; i++) { var croppedVertex = new Vector3D(croppedVertices[i]); // Figure out where this line intersects the cube var intersection = SpatialUtilities.CheckLineBox( cubeExtent.MinCorner, cubeExtent.MaxCorner, new Vector3D(croppedVertices[i]), new Vector3D(homeVertex)); if (intersection != null) { doReplacement = true; var result = CalculateNewUV(face, croppedVertices[i], homeVertex, intersection); newFace.UpdateTextureVertexIndex(result.OldIndex, result.NewIndex, false); // Add the new vertex int length = VertexList.Count(); VertexList.Add(new Vertex { Index = length + 1, X = intersection.X, Y = intersection.Y, Z = intersection.Z }); // Update the new face vertex newFace.UpdateVertexIndex(croppedVertices[i].Index, length + 1, false); } } if (doReplacement) { chunkFaceList.Add(newFace); chunkFaceList.Remove(face); } } } }