/// <summary> /// Batch update. Reconstruct all meshes needed by running GPU based Marching Cubes /// </summary> public void BatchUpdate() { for (int x = 0; x < width / blockSize; x++) { for (int y = 0; y < height / blockSize; y++) { for (int z = 0; z < length / blockSize; z++) { if (_blocks[x, y, z] == null) { continue; } if (_blocks[x, y, z].GetComponent <MeshFilter>().mesh != null) { _blocks[x, y, z].GetComponent <MeshFilter>().mesh.Clear(); } //if (_blocks[x, y, z].GetComponent<MeshCollider>().sharedMesh != null) // _blocks[x, y, z].GetComponent<MeshCollider>().sharedMesh.Clear(); } } } shaderSample.SetBuffer(_kernelCollectSurfaceBlock, "_Particles", sphSolver._bufferParticles); _bufferSufaceBlocks.SetData(_surfaceBlocksInit); shaderSample.SetBuffer(_kernelCollectSurfaceBlock, "_SurfaceMCBlocks", _bufferSufaceBlocks); shaderSample.Dispatch(_kernelCollectSurfaceBlock, sphSolver._sphthreadGroupNum, 1, 1); _bufferSufaceBlocks.GetData(_surfaceBlocksContainer); _nextUpdateblocks.Clear(); for (int i = 0; i < _surfaceBlocksContainer.Length; ++i) { if (_surfaceBlocksContainer[i] == 1) { int x = i / (blockDimY * blockDimZ); int y = (i - x * (blockDimY * blockDimZ)) / blockDimZ; int z = i - x * (blockDimY * blockDimZ) - y * blockDimZ; _nextUpdateblocks.Add(new Int3(x, y, z)); } } //for (int x = 0; x < blockDimX; ++x) // for (int y = 0; y < blockDimY; ++y) // for (int z = 0; z < blockDimZ; ++z) // _nextUpdateblocks.Add(new Int3(x, y, z)); if (_nextUpdateblocks.Count == 0) { return; } //GPU sampling ComputeBuffer bufferBlocks = new ComputeBuffer(_nextUpdateblocks.Count, sizeof(int) * 3); bufferBlocks.SetData(_nextUpdateblocks.ToArray()); //List<CSParticle> particlesCopy = new List<CSParticle>(); //int[] particlesStartIdx = new int[sphSolver.gridCountXYZ + 1]; //int startIdx = 0; //for (int x = 0; x < sphSolver.gridSize._x; ++x) // for (int y = 0; y < sphSolver.gridSize._y; ++y) // for (int z = 0; z < sphSolver.gridSize._z; ++z) // { // int idx = x * sphSolver.gridCountYZ + y * sphSolver.gridSize._z + z; // foreach (var particle in sphSolver.grid[idx].particles) // { // particlesCopy.Add(new CSParticle((float)particle.mass, 1f / (float)particle.density, // new Vector3((float)particle.position.x, (float)particle.position.y, (float)particle.position.z))); // } // particlesStartIdx[idx] = startIdx; // startIdx += sphSolver.grid[idx].particles.Count; // } //particlesStartIdx[particlesStartIdx.Length - 1] = sphSolver.currParticleNum; //_bufferParticlesStartIndex.SetData(particlesStartIdx); //_bufferParticles.SetData(particlesCopy.ToArray()); ComputeBuffer bufferSamples = new ComputeBuffer(_nextUpdateblocks.Count * (ag1BlockSize) * (ag1BlockSize) * (ag1BlockSize), sizeof(float)); ComputeBuffer bufferNormals = new ComputeBuffer(_nextUpdateblocks.Count * (ag1BlockSize) * (ag1BlockSize) * (ag1BlockSize), sizeof(float) * 3); shaderSample.SetBuffer(sampleKernel, "_Blocks", bufferBlocks); shaderSample.SetBuffer(sampleKernel, "_ParticleCellNumPrefixSum", sphSolver._bufferParticleNumPerCell); shaderSample.SetBuffer(sampleKernel, "_Particles", sphSolver._bufferParticles); shaderSample.SetBuffer(sampleKernel, "_Samples", bufferSamples); shaderSample.SetBuffer(sampleKernel, "_Normals", bufferNormals); shaderSample.Dispatch(sampleKernel, _nextUpdateblocks.Count, 1, 1); bufferBlocks.Release(); bufferBlocks = null; //marching-cube //STAGE I: collect triangle number ComputeBuffer bufferTriNum = new ComputeBuffer(1, sizeof(int)); bufferTriNum.SetData(new int[] { 0 }); ComputeBuffer bufferCornerFlags = new ComputeBuffer(_nextUpdateblocks.Count * blockSize * blockSize * blockSize, sizeof(int)); shaderCollectTriNum.SetBuffer(ctnKernel, "_Samples", bufferSamples); shaderCollectTriNum.SetBuffer(ctnKernel, "_CornerToTriNumTable", _bufferCornerToTriNumTable); shaderCollectTriNum.SetBuffer(ctnKernel, "_TriNum", bufferTriNum); shaderCollectTriNum.SetBuffer(ctnKernel, "_CornerFlags", bufferCornerFlags); shaderCollectTriNum.Dispatch(ctnKernel, _nextUpdateblocks.Count, 1, 1); int[] triNum = new int[1]; bufferTriNum.GetData(triNum); if (triNum[0] == 0) { //no triangles, early exit bufferNormals.Release(); bufferSamples.Release(); bufferTriNum.Release(); bufferCornerFlags.Release(); return; } //#if UNITY_EDITOR // //Debug.Log("triangles count " + triNum[0]); //#endif //STAGE II: do marching cube ComputeBuffer bufferMeshes = new ComputeBuffer(triNum[0], CSTriangle.stride); ComputeBuffer bufferTriEndIndex = new ComputeBuffer(1, sizeof(int)); bufferTriEndIndex.SetData(new int[] { 0 }); ComputeBuffer bufferTriBlockNum = new ComputeBuffer(_nextUpdateblocks.Count, sizeof(int)); int[] triBlockNum = new int[_nextUpdateblocks.Count]; bufferTriBlockNum.SetData(triBlockNum); shaderMarchingCube.SetBuffer(mcKernel, "_Samples", bufferSamples); shaderMarchingCube.SetBuffer(mcKernel, "_Normals", bufferNormals); shaderMarchingCube.SetBuffer(mcKernel, "_CornerFlags", bufferCornerFlags); shaderMarchingCube.SetBuffer(mcKernel, "_CornerToEdgeTable", _bufferCornerToEdgeTable); shaderMarchingCube.SetBuffer(mcKernel, "_CornerToVertTable", _bufferCornerToVertTable); shaderMarchingCube.SetBuffer(mcKernel, "_Meshes", bufferMeshes); shaderMarchingCube.SetBuffer(mcKernel, "_TriEndIndex", bufferTriEndIndex); shaderMarchingCube.SetBuffer(mcKernel, "_TriBlockNum", bufferTriBlockNum); //dispatch compute shader shaderMarchingCube.Dispatch(mcKernel, _nextUpdateblocks.Count, 1, 1); //split bufferMeshes to meshes for individual blocks CSTriangle[] csTriangles = new CSTriangle[triNum[0]];//triNum[0] is the counter bufferMeshes.GetData(csTriangles); bufferTriBlockNum.GetData(triBlockNum); int[] triBlockCounter = new int[triBlockNum.Length]; Vector3[][] vertices = new Vector3[_nextUpdateblocks.Count][]; Vector3[][] normals = new Vector3[_nextUpdateblocks.Count][]; for (int i = 0; i < _nextUpdateblocks.Count; i++) { vertices[i] = new Vector3[3 * triBlockNum[i]]; normals[i] = new Vector3[3 * triBlockNum[i]]; } foreach (var vt in csTriangles) { vertices[vt.block][triBlockCounter[vt.block]] = vt.position0 * engineScale; normals[vt.block][triBlockCounter[vt.block]] = vt.normal0; triBlockCounter[vt.block]++; vertices[vt.block][triBlockCounter[vt.block]] = vt.position1 * engineScale; normals[vt.block][triBlockCounter[vt.block]] = vt.normal1; triBlockCounter[vt.block]++; vertices[vt.block][triBlockCounter[vt.block]] = vt.position2 * engineScale; normals[vt.block][triBlockCounter[vt.block]] = vt.normal2; triBlockCounter[vt.block]++; } for (int i = 0; i < _nextUpdateblocks.Count; i++) { var x = _nextUpdateblocks[i]._x; var y = _nextUpdateblocks[i]._y; var z = _nextUpdateblocks[i]._z; _blocks[x, y, z].GetComponent <MeshFilter>().mesh.Clear(false); var mesh = _blocks[x, y, z].GetComponent <MeshFilter>().mesh; mesh.vertices = vertices[i]; int[] idx = new int[vertices[i].Length]; Buffer.BlockCopy(_incSequence, 0, idx, 0, vertices[i].Length * sizeof(int)); mesh.SetTriangles(idx, 0); mesh.normals = normals[i]; mesh.Optimize(); mesh.RecalculateBounds(); // _blocks[x, y, z].GetComponent<MeshFilter>().mesh = mesh; _blocks[x, y, z].GetComponent <MeshRenderer>().material = material; //_blocks[x, y, z].GetComponent<MeshCollider>().sharedMesh = mesh; } //Debug.Log("Time taken: " + (Time.realtimeSinceStartup - startTime) * 1000.0f); //#if UNITY_EDITOR // print("Time taken: " + (Time.realtimeSinceStartup - startTime) * 1000.0f); //#endif bufferNormals.Release(); bufferSamples.Release(); bufferTriNum.Release(); bufferCornerFlags.Release(); bufferMeshes.Release(); bufferTriEndIndex.Release(); bufferTriBlockNum.Release(); GC.Collect(); }
/// <summary> /// Batch update. Reconstruct all meshes needed by running GPU based Marching Cubes /// </summary> private void BatchUpdate() { if (_nextUpdateblocks.Count == 0) { return; } //In order to also recompute the normals of vertices, we need to also sample "one more layer" in adjacent cells //"argumented block size" int ag1BlockSize = blockSize + 1; int ag2BlockSize = blockSize + 2; //the array that hold samples in order to send them to GPU float[] samples = new float[_nextUpdateblocks.Count * (ag2BlockSize) * (ag2BlockSize) * (ag2BlockSize)]; for (int blockNum = 0; blockNum < _nextUpdateblocks.Count; blockNum++) { var blockIdx = _nextUpdateblocks[blockNum]; int x = blockIdx._x; int y = blockIdx._y; int z = blockIdx._z; for (int ix = 0; ix < ag2BlockSize; ix++) { for (int iy = 0; iy < ag2BlockSize; iy++) { for (int iz = 0; iz < ag2BlockSize; iz++) { var queryX = x * blockSize + ix; var queryY = y * blockSize + iy; var queryZ = z * blockSize + iz; //store value samples[ix + iy * (ag2BlockSize) + iz * (ag2BlockSize) * (ag2BlockSize) + blockNum * (ag2BlockSize) * (ag2BlockSize) * (ag2BlockSize)] = _voxelSamples[queryX, queryY, queryZ]; } } } } //float startTime = Time.realtimeSinceStartup; ////rebuild normals int nrmKernel = _shaderNormal.FindKernel("SampleNormal"); if (nrmKernel < 0) { throw new UnityException("Fail to find kernel of shader: " + _shaderNormal.name); } ComputeBuffer bufferSamples = new ComputeBuffer(_nextUpdateblocks.Count * (ag2BlockSize) * (ag2BlockSize) * (ag2BlockSize), sizeof(float)); bufferSamples.SetData(samples); _shaderNormal.SetBuffer(nrmKernel, "_Samples", bufferSamples); ComputeBuffer bufferNormals = new ComputeBuffer(_nextUpdateblocks.Count * (ag1BlockSize) * (ag1BlockSize) * (ag1BlockSize), sizeof(float) * 3); _shaderNormal.SetBuffer(nrmKernel, "_Normals", bufferNormals); //dispatch compute shader _shaderNormal.Dispatch(nrmKernel, _nextUpdateblocks.Count, 1, 1); //marching-cube //STAGE I: collect triangle number int ctnKernel = _shaderCollectTriNum.FindKernel("CollectTriNum"); if (ctnKernel < 0) { throw new UnityException("Fail to find kernel of shader: " + _shaderCollectTriNum.name); } ComputeBuffer bufferTriNum = new ComputeBuffer(1, sizeof(int)); bufferTriNum.SetData(new int[] { 0 }); ComputeBuffer bufferCornerFlags = new ComputeBuffer(_nextUpdateblocks.Count * blockSize * blockSize * blockSize, sizeof(int)); _shaderCollectTriNum.SetBuffer(ctnKernel, "_Samples", bufferSamples); _shaderCollectTriNum.SetBuffer(ctnKernel, "_CornerToTriNumTable", _bufferCornerToTriNumTable); _shaderCollectTriNum.SetBuffer(ctnKernel, "_TriNum", bufferTriNum); _shaderCollectTriNum.SetBuffer(ctnKernel, "_CornerFlags", bufferCornerFlags); _shaderCollectTriNum.Dispatch(ctnKernel, _nextUpdateblocks.Count, 1, 1); int[] triNum = new int[1]; bufferTriNum.GetData(triNum); if (triNum[0] == 0) { //no triangles, early exit bufferNormals.Release(); bufferSamples.Release(); bufferTriNum.Release(); bufferCornerFlags.Release(); return; } //Debug.Log("triangles count " + triNum[0]); //STAGE II: do marching cube int mcKernel = _shaderMarchingCube.FindKernel("MarchingCube"); if (mcKernel < 0) { throw new UnityException("Fail to find kernel of shader: " + _shaderMarchingCube.name); } ComputeBuffer bufferMeshes = new ComputeBuffer(triNum[0], CSTriangle.stride); ComputeBuffer bufferTriEndIndex = new ComputeBuffer(1, sizeof(int)); bufferTriEndIndex.SetData(new int[] { 0 }); _shaderMarchingCube.SetBuffer(mcKernel, "_Samples", bufferSamples); _shaderMarchingCube.SetBuffer(mcKernel, "_Normals", bufferNormals); _shaderMarchingCube.SetBuffer(mcKernel, "_CornerFlags", bufferCornerFlags); _shaderMarchingCube.SetBuffer(mcKernel, "_CornerToEdgeTable", _bufferCornerToEdgeTable); _shaderMarchingCube.SetBuffer(mcKernel, "_CornerToVertTable", _bufferCornerToVertTable); _shaderMarchingCube.SetBuffer(mcKernel, "_Meshes", bufferMeshes); _shaderMarchingCube.SetBuffer(mcKernel, "_TriEndIndex", bufferTriEndIndex); //dispatch compute shader _shaderMarchingCube.Dispatch(mcKernel, _nextUpdateblocks.Count, 1, 1); //split bufferMeshes to meshes for individual blocks CSTriangle[] csTriangles = new CSTriangle[triNum[0]];//triNum[0] is the counter bufferMeshes.GetData(csTriangles); List <Vector3>[] vertices = new List <Vector3> [_nextUpdateblocks.Count]; List <Vector3>[] normals = new List <Vector3> [_nextUpdateblocks.Count]; for (int i = 0; i < _nextUpdateblocks.Count; i++) { vertices[i] = new List <Vector3>(); normals[i] = new List <Vector3>(); } foreach (var vt in csTriangles) { vertices[vt._block].Add(vt._position0 * _voxelScale); vertices[vt._block].Add(vt._position1 * _voxelScale); vertices[vt._block].Add(vt._position2 * _voxelScale); normals[vt._block].Add(vt._normal0); normals[vt._block].Add(vt._normal1); normals[vt._block].Add(vt._normal2); } for (int i = 0; i < _nextUpdateblocks.Count; i++) { var x = _nextUpdateblocks[i]._x; var y = _nextUpdateblocks[i]._y; var z = _nextUpdateblocks[i]._z; _blocks[x, y, z].GetComponent <MeshFilter>().mesh.Clear(); var mesh = new Mesh(); mesh.vertices = vertices[i].ToArray(); var idx = Enumerable.Range(0, vertices[i].Count).ToArray(); mesh.SetTriangles(idx, 0); mesh.normals = normals[i].ToArray(); mesh.Optimize(); mesh.RecalculateBounds(); _blocks[x, y, z].GetComponent <MeshFilter>().mesh = mesh; _blocks[x, y, z].GetComponent <MeshRenderer>().material = _material; _blocks[x, y, z].GetComponent <MeshCollider>().sharedMesh = mesh; } //Debug.Log("Time taken: " + (Time.realtimeSinceStartup - startTime) * 1000.0f); bufferNormals.Release(); bufferSamples.Release(); bufferTriNum.Release(); bufferCornerFlags.Release(); bufferMeshes.Release(); bufferTriEndIndex.Release(); }