static void March(float[, ,] voxels, Fragment minMax, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, KDTree surface, List <Vector3> parentVerts, List <Vector3> verts, List <int> index) { Dictionary <string, int> indices = new Dictionary <string, int>(); float[] cube = new float[8]; for (int x = minMax.minX - 1; x <= minMax.maxX + 1; x++) { for (int y = minMax.minY - 1; y <= minMax.maxY + 1; y++) { for (int z = minMax.minZ - 1; z <= minMax.maxZ + 1; z++) { if (x < 0 || y < 0 || z < 0) { continue; } if (x >= voxels.GetLength(0) - 1 || y >= voxels.GetLength(1) - 1 || z >= voxels.GetLength(2) - 1) { continue; } //Get the values in the 8 neighbours which make up a cube FillCube(x, y, z, voxels, cube); //Perform algorithm Mode_Func(new Vector3(x, y, z), cube, verts, index, grid, indices, surface, parentVerts); } } } }
static public MeshInfo CreateMeshClamp(float[, ,] voxels, Fragment minMax, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, KDTree surface, List <Vector3> parentVerts) { List <Vector3> verts = new List <Vector3>(); List <int> index = new List <int>(); March(voxels, minMax, grid, surface, parentVerts, verts, index); MeshInfo mesh = new MeshInfo(verts.ToArray(), index.ToArray(), minMax); return(mesh); }
static public MeshInfo CreateMesh(float[, ,] voxels, Fragment minMax, ThreadSafeVoxelisation.Voxelization.AABCGrid grid) { List <Vector3> verts = new List <Vector3>(); List <int> index = new List <int>(); March(voxels, minMax, grid, null, null, verts, index); MeshInfo mesh = new MeshInfo(verts.ToArray(), index.ToArray(), minMax); return(mesh); }
public MeshInfo StartMarching(short[, ,] colouredVoxels, Fragment minMax, ThreadSafeVoxelisation.Voxelization.AABCGrid grid) { PrepareMarch(); float[, ,] voxels = new float[colouredVoxels.GetLength(0), colouredVoxels.GetLength(1), colouredVoxels.GetLength(2)]; PrepareGrid(colouredVoxels, voxels, minMax); MeshInfo mesh = ThreadSafeMarchingCubes.CreateMesh(voxels, minMax, grid); return(mesh); }
public Dictionary <short, Fragment> Fragment(ThreadSafeVoxelisation.Voxelization.AABCGrid grid, Vector3 hitPoint, float hitForce, PhysicalProperties physicalProperties) { this.aabcGrid = grid; this.hitPoint = hitPoint; this.hitForce = hitForce; this.physicalProperties = physicalProperties; findHitVoxel(hitPoint); generateVoronoiPoints(calcRadius(hitForce), calcNumberOfPoints(hitForce)); colourVoxels(); FindIslands(); return(fragmentExtents); }
public MeshInfo StartMarchingClamp(short[, ,] colouredVoxels, Fragment minMax, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, KDTree surface, List <Vector3> parentVerts) { PrepareMarch(); float[, ,] voxels = new float[colouredVoxels.GetLength(0), colouredVoxels.GetLength(1), colouredVoxels.GetLength(2)]; PrepareGrid(colouredVoxels, voxels, minMax); foreach (Vector3 v in minMax.vertices) { voxels[(int)v.x, (int)v.y, (int)v.z] = -999f; } MeshInfo mesh = ThreadSafeMarchingCubes.CreateMeshClamp(voxels, minMax, grid, surface, parentVerts); return(mesh); }
public void SplitDestroy(Vector3 hitPoint, float hitForce, PhysicalProperties physicalProperties) { messages.Add("Starting"); float time = Time.realtimeSinceStartup; float startTime = time; voxelisationDriver.StartVoxelise(gameObject); messages.Add("Voxelisation: " + (Time.realtimeSinceStartup - time)); csvList.Add((Time.realtimeSinceStartup - time) + " "); time = Time.realtimeSinceStartup; grid = voxelisationDriver.GetGrid(); fragments = destruction.Fragment(grid, hitPoint, hitForce, physicalProperties); messages.Add("Destruction: " + (Time.realtimeSinceStartup - time)); csvList.Add((Time.realtimeSinceStartup - time) + " "); time = Time.realtimeSinceStartup; colouring = destruction.getVoronoiDiagram(); foreach (Fragment c in fragments.Values) { c.vertices.Clear(); } short[, ,] borderColouring = new short[colouring.GetLength(0), colouring.GetLength(1), colouring.GetLength(2)]; List <Vector3> vectors = new List <Vector3>(); for (int i = 0; i < colouring.GetLength(0); i++) { for (int j = 0; j < colouring.GetLength(1); j++) { for (int k = 0; k < colouring.GetLength(2); k++) { short c = colouring[i, j, k]; bool neighbours = false; bool exterior = false; if (c == 0) { continue; } for (int x = -1; x <= 1; x++) { for (int y = -1; y <= 1; y++) { for (int z = -1; z <= 1; z++) { if (i + x < 0 || j + y < 0 || k + z < 0) { continue; } if (i + x >= colouring.GetLength(0) || j + y >= colouring.GetLength(1) || k + z >= colouring.GetLength(2)) { continue; } if (colouring[i + x, j + y, k + z] != c && colouring[i + x, j + y, k + z] != 0) { neighbours = true; } if (colouring[i + x, j + y, k + z] == 0) { exterior = true; } } } } if (exterior) { vectors.Add(new Vector3(i, j, k)); } if (neighbours) { borderColouring[i, j, k] = c; } if (neighbours && exterior) { Fragment colour; if (fragments.TryGetValue(c, out colour)) { colour.vertices.Add(new Vector3(i, j, k)); } } } } } KDTree tree = KDTree.MakeFromPoints(vectors.ToArray()); messages.Add("KDTree: " + (Time.realtimeSinceStartup - time)); time = Time.realtimeSinceStartup; MeshInfo original = new MeshInfo(gameObject.GetComponent <MeshFilter>().mesh.vertices, gameObject.GetComponent <MeshFilter>().mesh.triangles, new Fragment(0)); Dictionary <short, MeshInfo> meshes = splitMesh.Split(original, tree, vectors, colouring, grid.GetSize()); messages.Add("Split: " + (Time.realtimeSinceStartup - time)); csvList.Add((Time.realtimeSinceStartup - time) + " "); time = Time.realtimeSinceStartup; if (!hollow) { csvList.Add("Meshing "); foreach (Fragment colour in fragments.Values) { if (colour == null) { continue; } MeshInfo meshinfo, march; MeshInfo parent; bool found = meshes.TryGetValue(colour.colour, out parent); colors.Add(colour.colour, new Color(Random.value, Random.value, Random.value)); if (found) { List <Vector3> edges = parent.colour.vertices; List <Vector3> exterior = new List <Vector3>(); foreach (Vector3 v in colour.vertices) { exterior.Add(toWorldSpace(v)); } Vector3 voxelMid = new Vector3((colour.maxX - colour.minX) / 2, (colour.maxY - colour.minY) / 2, (colour.maxZ - colour.minZ) / 2); voxelMid.x = voxelMid.x / grid.GetSize().x; voxelMid.y = voxelMid.y / grid.GetSize().y; voxelMid.z = voxelMid.z / grid.GetSize().z; voxelMid *= 2; KDTree surface = KDTree.MakeFromPoints(edges.ToArray()); //meshinfo = holefill.Stitch(edges, colour, exterior); meshinfo = marchingDriver.StartMarchingClamp(borderColouring, colour, grid, surface, edges); } else { meshinfo = marchingDriver.StartMarching(borderColouring, colour, grid); march = marchingDriver.StartMarching(borderColouring, colour, grid); } //meshinfo = convexDriver.StartMeshing(colour); /*for (int c = 0; c < meshinfo.verts.Length; c++) { * meshinfo.verts[c] = new Vector3(meshinfo.verts[c].x / transform.lossyScale.x, meshinfo.verts[c].y / transform.lossyScale.y, meshinfo.verts[c].z / transform.lossyScale.z); * }*/ messages.Add("Meshing " + colour.colour + ": " + (Time.realtimeSinceStartup - time)); csvList.Add((Time.realtimeSinceStartup - time) + " "); time = Time.realtimeSinceStartup; if (colour.colour == 0) { continue; } else { if (found) { List <Vector3> verts = new List <Vector3>(parent.verts); int count = verts.Count; verts.AddRange(meshinfo.verts); List <int> indices = new List <int>(parent.index); foreach (int i in meshinfo.index) { indices.Add(i + count); } /*count = verts.Count; * verts.AddRange(march.verts); * * foreach (int i in march.index) { * indices.Add(i + count); * }*/ parent.verts = verts.ToArray(); parent.index = indices.ToArray(); parent.colour.mass = meshinfo.colour.mass; } else { meshes.Add(colour.colour, meshinfo); } } } } csvList.Add("Building "); foreach (MeshInfo meshinfo in meshes.Values) { Mesh mesh = new Mesh(); Fragment coloured = meshinfo.colour; fragments.TryGetValue(coloured.colour, out coloured); if (coloured.colour == 0) { continue; } mesh.vertices = meshinfo.verts; mesh.triangles = meshinfo.index; //The diffuse shader wants uvs so just fill with a empty array, they're not actually used mesh.uv = new Vector2[mesh.vertices.Length]; mesh.RecalculateNormals(); mesh.RecalculateBounds(); Mesh mesh1 = new Mesh(); if (hollow) { List <int> indices = new List <int>(meshinfo.index); for (int i = 0; i < meshinfo.index.Length; i += 3) { indices.Add(meshinfo.index[i + 2]); indices.Add(meshinfo.index[i + 1]); indices.Add(meshinfo.index[i + 0]); } meshinfo.index = indices.ToArray(); /*List<Vector3> normals = new List<Vector3>(mesh.normals); * for (int i = 0; i < mesh.normals.Length; i++) * normals.Add(-normals[i]);*/ mesh1.vertices = meshinfo.verts; mesh1.triangles = meshinfo.index; //The diffuse shader wants uvs so just fill with a empty array, they're not actually used mesh1.uv = new Vector2[mesh.vertices.Length]; mesh1.normals = mesh.normals; } GameObject m_mesh = new GameObject("Fragment" + coloured.colour); m_mesh.AddComponent <MeshFilter>(); m_mesh.AddComponent <MeshRenderer>(); //MeshCollider col1 = m_mesh.AddComponent<MeshCollider>(); MeshCollider col2 = m_mesh.AddComponent <MeshCollider>(); m_mesh.AddComponent <Rigidbody>(); //m_mesh.rigidbody.isKinematic = true; m_mesh.rigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic; m_mesh.rigidbody.mass = coloured.mass; m_mesh.renderer.material = m_material; //col1.sharedMesh = mesh; col2.sharedMesh = mesh; col2.convex = true; m_mesh.GetComponent <MeshFilter>().mesh = hollow ? mesh1 : mesh; m_mesh.transform.localScale = transform.localScale; messages.Add("Built " + coloured.colour + ": " + (Time.realtimeSinceStartup - time)); csvList.Add((Time.realtimeSinceStartup - time) + " "); time = Time.realtimeSinceStartup; } messages.Add("Done:" + (Time.realtimeSinceStartup - startTime)); string csv = ""; foreach (string str in csvList) { csv += str; } foreach (string str in messages) { Debug.Log(str); } //Debug.Log(csv); done = true; GameObject.Destroy(gameObject); if (breakP) { Debug.Break(); } }
//MarchCubeTetrahedron performs the Marching Tetrahedrons algorithm on a single cube static void MarchCubeTetrahedron(Vector3 pos, float[] cube, List <Vector3> vertList, List <int> indexList, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, Dictionary <string, int> indices, KDTree surface, List <Vector3> parentVerts) { int i, j, vertexInACube; Vector3[] cubePosition = new Vector3[8]; Vector3[] tetrahedronPosition = new Vector3[4]; float[] tetrahedronValue = new float[4]; //Make a local copy of the cube's corner positions for (i = 0; i < 8; i++) { cubePosition[i] = new Vector3(pos.x + vertexOffset[i, 0], pos.y + vertexOffset[i, 1], pos.z + vertexOffset[i, 2]); } for (i = 0; i < 6; i++) { for (j = 0; j < 4; j++) { vertexInACube = tetrahedronsInACube[i, j]; tetrahedronPosition[j] = cubePosition[vertexInACube]; tetrahedronValue[j] = cube[vertexInACube]; } MarchTetrahedron(tetrahedronPosition, tetrahedronValue, vertList, indexList, grid, indices, surface, parentVerts); } }
//MarchTetrahedron performs the Marching Tetrahedrons algorithm on a single tetrahedron static void MarchTetrahedron(Vector3[] tetrahedronPosition, float[] tetrahedronValue, List <Vector3> vertList, List <int> indexList, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, Dictionary <string, int> indices, KDTree surface, List <Vector3> parentVerts) { ThreadSafeVoxelisation.GridSize gridSize = grid.GetSize(); int i, j, vert, vert0, vert1, idx; int flagIndex = 0, edgeFlags; float offset, invOffset; bool boundary = false; Vector3[] edgeVertex = new Vector3[6]; //Find which vertices are inside of the surface and which are outside for (i = 0; i < 4; i++) { if (tetrahedronValue[i] == -999f) { boundary = true; } if (tetrahedronValue[i] <= target) { flagIndex |= 1 << i; } } //Find which edges are intersected by the surface edgeFlags = tetrahedronEdgeFlags[flagIndex]; //If the tetrahedron is entirely inside or outside of the surface, then there will be no intersections if (edgeFlags == 0) { return; } //Find the point of intersection of the surface with each edge for (i = 0; i < 6; i++) { //if there is an intersection on this edge if ((edgeFlags & (1 << i)) != 0) { vert0 = tetrahedronEdgeConnection[i, 0]; vert1 = tetrahedronEdgeConnection[i, 1]; offset = GetOffset(tetrahedronValue[vert0], tetrahedronValue[vert1]); invOffset = 1.0f - offset; edgeVertex[i].x = ((invOffset * tetrahedronPosition[vert0].x + offset * tetrahedronPosition[vert1].x) - (gridSize.x * 0.5f)) * gridSize.side / 2.1f; edgeVertex[i].y = ((invOffset * tetrahedronPosition[vert0].y + offset * tetrahedronPosition[vert1].y) - (gridSize.y * 0.5f)) * gridSize.side / 2.1f; edgeVertex[i].z = ((invOffset * tetrahedronPosition[vert0].z + offset * tetrahedronPosition[vert1].z) - (gridSize.z * 0.5f)) * gridSize.side / 2.1f; } } Vector3[] oEdgeVertex = new Vector3[6]; if (boundary) { for (int c = 0; c < edgeVertex.Length; c++) { oEdgeVertex[c] = edgeVertex[c]; int index = surface.FindNearest(edgeVertex[c]); edgeVertex[c] = parentVerts[index]; } } else { for (int c = 0; c < edgeVertex.Length; c++) { oEdgeVertex[c] = edgeVertex[c]; Vector3 random = Random.insideUnitSphere; random *= 1f / 4f; random.x = (random.x) * gridSize.side / 2.1f; random.y = (random.y) * gridSize.side / 2.1f; random.z = (random.z) * gridSize.side / 2.1f; edgeVertex[c] = edgeVertex[c] + random; } } //Save the triangles that were found. There can be up to 2 per tetrahedron for (i = 0; i < 2; i++) { if (tetrahedronTriangles[flagIndex, 3 * i] < 0) { break; } for (j = 0; j < 3; j++) { idx = vertList.Count; vert = tetrahedronTriangles[flagIndex, 3 * i + windingOrder[j]]; Vector3 vertex = edgeVertex[vert]; string points = oEdgeVertex[vert].x + "," + oEdgeVertex[vert].y + "," + oEdgeVertex[vert].z; int index = -1; bool found = indices.TryGetValue(points, out index); if (found) { indexList.Add(index); } else { indexList.Add(idx); vertList.Add(edgeVertex[vert]); indices.Add(points, idx); } } } }
//MarchCube performs the Marching Cubes algorithm on a single cube static void MarchCube(Vector3 pos, float[] cube, List <Vector3> vertList, List <int> indexList, ThreadSafeVoxelisation.Voxelization.AABCGrid grid, Dictionary <string, int> indices, KDTree surface, List <Vector3> parentVerts) { int i, j, vert, idx; int flagIndex = 0; float offset = 0.0f; ThreadSafeVoxelisation.GridSize gridSize = grid.GetSize(); Vector3[] edgeVertex = new Vector3[12]; //Find which vertices are inside of the surface and which are outside for (i = 0; i < 8; i++) { if (cube[i] <= target) { flagIndex |= 1 << i; } } //Find which edges are intersected by the surface int edgeFlags = cubeEdgeFlags[flagIndex]; //If the cube is entirely inside or outside of the surface, then there will be no intersections if (edgeFlags == 0) { return; } //Find the point of intersection of the surface with each edge for (i = 0; i < 12; i++) { //if there is an intersection on this edge if ((edgeFlags & (1 << i)) != 0) { offset = GetOffset(cube[edgeConnection[i, 0]], cube[edgeConnection[i, 1]]); edgeVertex[i].x = pos.x + (vertexOffset[edgeConnection[i, 0], 0] + offset * edgeDirection[i, 0]); edgeVertex[i].y = pos.y + (vertexOffset[edgeConnection[i, 0], 1] + offset * edgeDirection[i, 1]); edgeVertex[i].z = pos.z + (vertexOffset[edgeConnection[i, 0], 2] + offset * edgeDirection[i, 2]); } } //Save the triangles that were found. There can be up to five per cube /*for (i = 0; i < 5; i++) { * if (triangleConnectionTable[flagIndex, 3 * i] < 0) break; * * for (j = 0; j < 3; j++) { * idx = vertList.Count; * vert = triangleConnectionTable[flagIndex, 3 * i + windingOrder[j]]; * * Vector3 vertex = edgeVertex[vert]; * string points = vertex.x + "" + vertex.y + "" + vertex.z; * int index = -1; * * bool found = indices.TryGetValue(points, out index); * * if (found) { * indexList.Add(index); * } else { * indexList.Add(idx); * vertList.Add(edgeVertex[vert]); * indices.Add(points, idx); * } * } * * }*/ for (i = 0; i < 5; i++) { if (triangleConnectionTable[flagIndex, 3 * i] < 0) { break; } idx = vertList.Count; for (j = 0; j < 3; j++) { vert = triangleConnectionTable[flagIndex, 3 * i + j]; indexList.Add(idx + windingOrder[j]); vertList.Add(edgeVertex[vert]); } } }