void March(IList <float> voxels, Vector3Int dimensions) { MarchingTertrahedron marching = new MarchingTertrahedron(); List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); marching.Generate(voxels, dimensions.x, dimensions.y, dimensions.z, verts, indices); Mesh.indexFormat = IndexFormat.UInt32; Mesh.SetVertices(verts); Mesh.SetTriangles(indices, 0); Mesh.RecalculateBounds(); Mesh.RecalculateNormals(); }
public override bool Calculate() { Meshes = new List <GameObject>(); VoxelBlock <Voxel> block = input.GetValue <VoxelBlock <Voxel> >(); Marching marching = null; if (mode == MarchingMode.Tetrahedron) { marching = new MarchingTertrahedron(); } else { marching = new MarchingCubes(); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. // //This should be accesible by an input marching.Surface = 0.5f; //The size of voxel array. int width = block.Width; int height = block.Height; int length = block.Length; float[] voxels = new float[width * height * length]; for (int y = 0; y < height; y++) { Voxel[,] voxelLayer = block.Layers[y].Layer; for (int x = 0; x < width; x++) { for (int z = 0; z < length; z++) { int idx = x + y * width + z * width * height; voxels[idx] = voxelLayer[x, z].GetValue(); } } } List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); //The mesh produced is not optimal. There is one vert for each index. //Would need to weld vertices for better quality mesh. marching.Generate(voxels, width, height, length, verts, indices); int generatedVerts = verts.Count; int generatedIndices = indices.Count; weldVertices(verts, indices); Debug.Log("Vertex Welding: " + generatedVerts + "=>" + verts.Count); Debug.Log("Vertex Welding indices: " + generatedIndices + "=>" + indices.Count); Debug.LogError("Out of Bounds: " + indices.Find(i => i >= verts.Count)); //A mesh in unity can only be made up of 65000 verts. //Need to split the verts between multiple meshes. int maxVertsPerMesh = 30000; //must be divisible by 3, ie 3 verts == 1 triangle int numMeshes = verts.Count / maxVertsPerMesh + 1; for (int i = 0; i < numMeshes; i++) { List <Vector3> splitVerts = new List <Vector3>(); List <int> splitIndices = new List <int>(); for (int j = 0; j < maxVertsPerMesh; j++) { int idx = i * maxVertsPerMesh + j; if (idx < verts.Count) { splitVerts.Add(verts[idx]); splitIndices.Add(j); } } if (splitVerts.Count == 0) { continue; } splitVerts = verts; splitIndices = indices; Mesh mesh = new Mesh(); mesh.SetVertices(splitVerts); mesh.SetTriangles(splitIndices, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); GameObject go = new GameObject("Mesh"); // go.transform.parent = transform; go.AddComponent <MeshFilter>(); go.AddComponent <MeshRenderer>(); // go.GetComponent<Renderer>().material = m_material; go.GetComponent <MeshFilter>().mesh = mesh; go.transform.localPosition = new Vector3(-width / 2, -height / 2, -length / 2); go.transform.localScale = new Vector3(1.0f, 1.0f, 1.0f); Meshes.Add(go); } VertexDisplay.RenderNewMeshes(Meshes); return(true); }
private void createStructure(CloudFrame frame) { //Set the mode used to create the mesh. //Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface. Marching marching = null; if (mode == MARCHING_MODE.TETRAHEDRON) { marching = new MarchingTertrahedron(); } else { marching = new MarchingCubes(); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. marching.Surface = surface; //nothing at 1 CloudFrame lowResFrame = frame.getLowResCloudFrame(30000); //The size of voxel array. int width = lowResFrame.width; int height = lowResFrame.height; int length = 500; float[] voxels = setupVoxelArray(new float[width * height * length]); float[] points = lowResFrame.GetpointCloud();; //Fill voxels with values. I take the pointcloud and populate the voxel with it. List <float> t = new List <float>(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { float _z = points[x + y * width]; int z = 0; if (_z >= 0) //we are removing garbage value(s)... { z = (int)Math.Floor(_z * length); if (z < 1) { t.Add(z); } //we know that _z is normalized to 1 so we multiply //by the length to get the length in voxels we subtract 1 because index is 0-9 but //the index could be 0-10 so we subtract 1 and clamp to 0 //clamp z at 9 if (z >= length - 1) { z = length - 1; } int idx = x + y * width + z * width * height; try { voxels[idx] = 0; //put a 1 where the point is because thats where the surface is } catch (Exception e) { Debug.Log("Opps"); return; } } else { continue; } } } //test the voxel //testVoxel(voxels, width, height, length); List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); //The mesh produced is not optimal. There is one vert for each index. //Would need to weld vertices for better quality mesh. marching.Generate(voxels, width, height, length, verts, indices); //A mesh in unity can only be made up of 65000 verts. //Need to split the verts between multiple meshes. int maxVertsPerMesh = 30000; //must be divisible by 3, ie 3 verts == 1 triangle int numMeshes = verts.Count / maxVertsPerMesh + 1; for (int i = 0; i < numMeshes; i++) { List <Vector3> splitVerts = new List <Vector3>(); List <int> splitIndices = new List <int>(); for (int j = 0; j < maxVertsPerMesh; j++) { int idx = i * maxVertsPerMesh + j; if (idx < verts.Count) { splitVerts.Add(verts[idx]); splitIndices.Add(j); } } if (splitVerts.Count == 0) { continue; } Mesh mesh = new Mesh(); mesh.SetVertices(splitVerts); mesh.SetTriangles(splitIndices, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); GameObject go = new GameObject("Mesh"); go.transform.parent = transform; go.AddComponent <MeshFilter>(); go.AddComponent <MeshRenderer>(); go.GetComponent <Renderer>().material = m_material; go.GetComponent <MeshFilter>().mesh = mesh; go.transform.localPosition = new Vector3(-width / 2, -height / 2, -length / 2); meshes.Add(go); } transform.Rotate(new Vector3(0, 0, -90)); }
void DrawIsoSurface() { float[] levels = new float[] { 5, 10, 20, 30, 40, 50 }; foreach (Transform child in transform) { Destroy(child.gameObject); } //Set the mode used to create the mesh. //Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface. Marching marching = null; if (mode == MARCHING_MODE.TETRAHEDRON) { marching = new MarchingTertrahedron(); } else { marching = new MarchingCubes(); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. for (int l = 0; l < levels.Length; l++) { marching.Surface = levels[l]; m_material.color = Color.Lerp(new Color(1F, 0F, 0F, 0.1F), new Color(0F, 0F, 1F, 0.1F), Map(levels[l], 0F, 30F, 0F, 1F)); //The size of voxel array. int width = interpVals.GetLength(1); int height = interpVals.GetLength(0); int depth = interpVals.GetLength(2); float[] voxels = new float[width * height * depth]; InterpolateAllLayers(frame); //Fill voxels with values. Im using perlin noise but any method to create voxels will work. for (int x = 0; x < height; x++) { for (int y = 0; y < width; y++) { for (int z = 0; z < depth; z++) { voxels[x + y * height + z * width * height] = interpVals[x, y, z]; //Random.Range(-1F, 1F); } } } List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); //The mesh produced is not optimal. There is one vert for each index. //Would need to weld vertices for better quality mesh. marching.Generate(voxels, width, height, depth, verts, indices); //A mesh in unity can only be made up of 65000 verts. //Need to split the verts between multiple meshes. int maxVertsPerMesh = 30000; //must be divisible by 3, ie 3 verts == 1 triangle int numMeshes = verts.Count / maxVertsPerMesh + 1; for (int i = 0; i < numMeshes; i++) { List <Vector3> splitVerts = new List <Vector3>(); List <int> splitIndices = new List <int>(); for (int j = 0; j < maxVertsPerMesh; j++) { int idx = i * maxVertsPerMesh + j; if (idx < verts.Count) { splitVerts.Add(verts[idx]); splitIndices.Add(j); } } if (splitVerts.Count == 0) { continue; } Mesh mesh = new Mesh(); mesh.SetVertices(TransformVerts(splitVerts)); mesh.SetTriangles(splitIndices, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); GameObject go = new GameObject("Mesh"); go.transform.parent = transform; go.AddComponent <MeshFilter>(); go.AddComponent <MeshRenderer>(); go.GetComponent <Renderer>().material = new Material(m_material); go.GetComponent <MeshFilter>().mesh = mesh; //go.transform.localPosition = new Vector3(-width / 2, -height / 2, -depth / 2); } } }
public void ConvertToMesh(float cutoff = 0.5f) { //Set the mode used to create the mesh. //Cubes is faster and creates less verts, tetrahedrons is slower and creates more verts but better represents the mesh surface. Marching marching = null; if (mode == MARCHING_MODE.TETRAHEDRON) { marching = new MarchingTertrahedron(); } else { marching = new MarchingCubes(); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. marching.Surface = cutoff; VolumeFileLoader vfl = GetComponent <VolumeFileLoader>(); //The size of voxel array. int width = vfl.dataVolume.nx; int height = vfl.dataVolume.ny; int length = vfl.dataVolume.nz; float[] voxels = new float[width * height * length]; Color[] pixels = vfl.dataVolume.texture.GetPixels(); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { for (int z = 0; z < length; z++) { float fx = x / (width - 1.0f); float fy = y / (height - 1.0f); float fz = z / (length - 1.0f); int idx = x + y * width + z * width * height; Color pixel = pixels[idx]; if (pixel.a > 0f) { voxels[idx] = pixel.a; } } } } List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); Debug.Log("Starting marching cubes..."); //The mesh produced is not optimal. There is one vert for each index. //Would need to weld vertices for better quality mesh. marching.Generate(voxels, width, height, length, verts, indices); /*A mesh in unity can only be made up of 65000 verts, | and need to split the verts between multiple meshes. */ int maxVertsPerMesh = 30000; //must be divisible by 3, ie 3 verts == 1 triangle int numMeshes = verts.Count / maxVertsPerMesh + 1; Debug.Log(verts.Count + " vertices scanned."); GameObject marchingObject = new GameObject("MarchingObject"); marchingObject.transform.parent = transform; for (int i = 0; i < numMeshes; i++) { List <Vector3> splitVerts = new List <Vector3>(); List <int> splitIndices = new List <int>(); for (int j = 0; j < maxVertsPerMesh; j++) { int idx = i * maxVertsPerMesh + j; if (idx < verts.Count) { splitVerts.Add(verts[idx]); splitIndices.Add(j); } } if (splitVerts.Count == 0) { continue; } /* | Creates mesh with default vertices and edges */ Mesh mesh = new Mesh(); mesh.SetVertices(splitVerts); mesh.SetTriangles(splitIndices, 0); mesh.RecalculateBounds(); mesh.RecalculateNormals(); /* | */ GameObject go = new GameObject("Mesh"); go.transform.parent = marchingObject.transform; go.AddComponent <MeshFilter>(); go.AddComponent <MeshRenderer>(); go.GetComponent <Renderer>().material = m_material; go.GetComponent <MeshFilter>().mesh = mesh; go.transform.localRotation = Quaternion.Euler(Vector3.zero); go.transform.localPosition = new Vector3((float)-width / 2, (float)-height / 2, (float)-length / 2); meshes.Add(go); } marchingObject.transform.localPosition = Vector3.zero; marchingObject.transform.localRotation = Quaternion.Euler(Vector3.zero); marchingObject.transform.localScale = new Vector3(1f / width, 1f / height, 1f / length); Debug.Log("Finishing marching cubes..."); GetComponent <MeshRenderer>().enabled = false; }
void GenerateTerrain(Vector3Int terrainOffset, bool scu) { Marching marching = null; if (mode == MARCHING_MODE.TETRAHEDRON) { marching = new MarchingTertrahedron(); } else { marching = new MarchingCubes(); } marching.Surface = surface; // bool newChunk = false; Voxel[] chunkVoxels; if (!voxels.ContainsKey(terrainOffset)) { //newChunk = true; chunkVoxels = generation.Voxels(/*,s*/ terrainOffset, activeOres); voxels.Add(terrainOffset, chunkVoxels); } else { chunkVoxels = voxels[terrainOffset]; //chunkVoxels = new Voxel[cS*cS*cS]; // for(int i = 0; i<chunkVoxels.Length; i++){ // chunkVoxels[i] = voxels[terrainOffset][i]; // } } float[] chunkvoxs = new float[chunkVoxels.Length]; for (int i = 0; i < chunkVoxels.Length; i++) { chunkvoxs[i] = chunkVoxels[i].value; } List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); //The mesh produced is not optimal. There is one vert for each index. //Would need to weld vertices for better quality mesh. marching.Generate(chunkvoxs, cS, cS, cS, verts, indices); indices = null; //indices.Reverse(); List <List <int> > subIndices = new List <List <int> >(); for (int i = 0; i < materials.materialList.Count; i++) { subIndices.Add(new List <int>()); } //System.Random random = new System.Random(); //spawnObject.spawn(terrainOffset,terrainOffset,-1); bool canCC = false; for (int i = 0; i < verts.Count; i = i + 3) { //bool normal = true; Vector3 triPos = (verts[i] + verts[i + 1] + verts[i + 2]) / 3; Vector3Int voxeloV = Vector3Int.RoundToInt(triPos); int idx = voxeloV.x + voxeloV.y * cS + voxeloV.z * cS * cS; int mat = chunkVoxels[idx].material; if (!canCC) { int zeroDiffs = 0; if (triPos.x == verts[i].x) { zeroDiffs += 1; } if (triPos.y == verts[i].y) { zeroDiffs += 1; } if (triPos.z == verts[i].z) { zeroDiffs += 1; } if (zeroDiffs < 2) { canCC = true; } } subIndices[mat].Add(i + 2); subIndices[mat].Add(i + 1); subIndices[mat].Add(i); //System.Random random = new System.Random(); // if(newChunk && random.NextDouble() < spawnProbability){ // spawnObject.spawn(triPos+terrainOffset,terrainOffset,-1); // }// spawn objects } for (int i = 0; i < verts.Count; i++) { verts[i] = verts[i] / resolution; //Indices.Add(i); } Action generateMesh; if (verts.Count == 0 || !canCC) { generateMesh = () => { if (meshes.ContainsKey(terrainOffset)) { Destroy(meshes[terrainOffset]); } }; } else { generateMesh = () => { GameObject go = meshGeneration.genMesh(verts, subIndices, materials.materialList, transform); go.transform.localPosition = terrainOffset; if (meshes.ContainsKey(terrainOffset)) { Destroy(meshes[terrainOffset]); meshes[terrainOffset] = go; } else { meshes.Add(terrainOffset, go); } }; } if (scu) { FunctionsToRunInMainThread2.Add(generateMesh); } else { FunctionsToRunInMainThread.Add(generateMesh); } }
public override bool Calculate() { bool isPMB = false; if (!input.connected()) { return(false); } VoxelBlock <Voxel> block = input.GetValue <VoxelBlock <Voxel> >(); if (surfaceConnection.connected()) { surface = surfaceConnection.GetValue <float>(); } Marching marching = null; switch (mode) { case VerteGenerationMode.Tetrahedron: marching = new MarchingTertrahedron(); break; case VerteGenerationMode.Cubes: marching = new MarchingCubes(); break; case VerteGenerationMode.Voxel: marching = new VoxelGeneration(); break; case VerteGenerationMode.PMB: isPMB = true; break; } //The size of voxel array. Vector3Int count = block.VoxelCount; int width = count.x; int height = count.y; int length = count.z; float[] voxels = new float[width * height * length]; for (int y = 0; y < height; y++) { Voxel[,] voxelLayer = block.Layers[y].Layer; for (int x = 0; x < width; x++) { for (int z = 0; z < length; z++) { int idx = x + y * width + z * width * height; voxels[idx] = voxelLayer[x, z].GetValue(); } } } if (isPMB) { var pmbTask = MainThreadHelper.instance().scheduleOnMainThread(() => { pmb.ReInit(block); Stopwatch pmbWatch = Stopwatch.StartNew(); buffers = pmb.calculate(voxels, width, height, length, surface); pmbWatch.Stop(); UnityEngine.Debug.LogFormat("PMB took {0}ms\n\t{1} voxels\n\t{2} triangles", pmbWatch.ElapsedMilliseconds, voxels.Count(), buffers.indexBuffer.count / 3); }); pmbTask.wait(); if (!pmbTask.completed) { return(false); } Block = block; return(true); } //Surface is the value that represents the surface of mesh //For example the perlin noise has a range of -1 to 1 so the mid point is where we want the surface to cut through. //The target value does not have to be the mid point it can be any value with in the range. // //This should be accesible by an input marching.Surface = surface; List <Vector3> verts = new List <Vector3>(); List <int> indices = new List <int>(); List <Vector3> normals = new List <Vector3>(); Stopwatch sw = Stopwatch.StartNew(); marching.Generate(voxels, width, height, length, verts, indices, normals); sw.Stop(); UnityEngine.Debug.LogFormat("Marching took {0}ms\n\t{1} vertices; {2} triangles", sw.ElapsedMilliseconds, verts.Count(), indices.Count() / 3); sw.Restart(); weldVertices(verts, indices, normals); sw.Stop(); UnityEngine.Debug.LogFormat("Vertex welding took {0}ms\n\t {1} vertices left", sw.ElapsedMilliseconds, verts.Count()); var task = MainThreadHelper.instance().scheduleOnMainThread(() => { buffers = new RenderBuffers { vertexBuffer = new ComputeBuffer(verts.Count, sizeof(float) * 3), indexBuffer = new ComputeBuffer(indices.Count, sizeof(int)), normalBuffer = new ComputeBuffer(normals.Count, sizeof(float) * 3), argsBuffer = new ComputeBuffer(4, sizeof(int), ComputeBufferType.IndirectArguments), }; buffers.vertexBuffer.SetData(verts); buffers.indexBuffer.SetData(indices); buffers.normalBuffer.SetData(normals); buffers.argsBuffer.SetData(new int[] { indices.Count, 1, 0, 0 }); }); task.wait(); if (!task.completed) { return(false); } Block = block; return(true); }