public static void Voxelize(Mesh mesh, int resolution, out List <Voxel_t> voxels, out float unit, bool surfaceOnly = false)
        {
            mesh.RecalculateBounds();
            var bounds = mesh.bounds;

            // From the specified resolution, calculate the unit length of one voxel
            float maxLength = Mathf.Max(bounds.size.x, Mathf.Max(bounds.size.y, bounds.size.z));

            unit = maxLength / resolution;

            // half of the unit length
            var hunit = unit * 0.5f;

            // The bounds extended by "half of the unit length constituting one voxel" is defined as the scope of voxelization
            var start = bounds.min - new Vector3(hunit, hunit, hunit);  // Minimum bounds to voxel
            var end   = bounds.max + new Vector3(hunit, hunit, hunit);  // Maximum bounds to voxel
            var size  = end - start;                                    // Size of bounds to voxel

            // The size of three-dimensional voxel data is determined based on the unit length of the voxel and the scope of voxelization
            var width  = Mathf.CeilToInt(size.x / unit);
            var height = Mathf.CeilToInt(size.y / unit);
            var depth  = Mathf.CeilToInt(size.z / unit);
            var volume = new Voxel_t[width, height, depth];

            // In the subsequent processing,
            // in order to refer to the position and size of each voxel data, generate an AABB array.
            var boxes         = new Bounds[width, height, depth];
            var voxelUnitSize = Vector3.one * unit;

            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {
                        var p    = new Vector3(x, y, z) * unit + start;
                        var aabb = new Bounds(p, voxelUnitSize);
                        boxes[x, y, z] = aabb;
                    }
                }
            }

            var vertices = mesh.vertices;
            var indices  = mesh.triangles;

            // direction to fill the voxels
            var direction = Vector3.forward;

            for (int i = 0, n = indices.Length; i < n; i += 3)
            {
                // a target triangle
                var tri = new Triangle(
                    vertices[indices[i]],
                    vertices[indices[i + 1]],
                    vertices[indices[i + 2]],
                    direction
                    );

                // calculate a AABB of a triangle
                var min = tri.bounds.min - start;
                var max = tri.bounds.max - start;
                int iminX = Mathf.RoundToInt(min.x / unit), iminY = Mathf.RoundToInt(min.y / unit), iminZ = Mathf.RoundToInt(min.z / unit);
                int imaxX = Mathf.RoundToInt(max.x / unit), imaxY = Mathf.RoundToInt(max.y / unit), imaxZ = Mathf.RoundToInt(max.z / unit);
                iminX = Mathf.Clamp(iminX, 0, width - 1);
                iminY = Mathf.Clamp(iminY, 0, height - 1);
                iminZ = Mathf.Clamp(iminZ, 0, depth - 1);
                imaxX = Mathf.Clamp(imaxX, 0, width - 1);
                imaxY = Mathf.Clamp(imaxY, 0, height - 1);
                imaxZ = Mathf.Clamp(imaxZ, 0, depth - 1);

                uint front = (uint)(tri.frontFacing ? 1 : 0);

                // inside AABB of a triangle,
                // check intersections a triangle and voxels
                for (int x = iminX; x <= imaxX; x++)
                {
                    for (int y = iminY; y <= imaxY; y++)
                    {
                        for (int z = iminZ; z <= imaxZ; z++)
                        {
                            if (Intersects(tri, boxes[x, y, z]))
                            {
                                var voxel = volume[x, y, z];
                                voxel.position = boxes[x, y, z].center;
                                if ((voxel.front & 1) == 0)
                                {
                                    voxel.front = front;
                                }
                                else
                                {
                                    voxel.front = voxel.front & front;
                                }
                                voxel.fill      = 1;
                                volume[x, y, z] = voxel;
                            }
                        }
                    }
                }
            }

            if (!surfaceOnly)
            {
                // fill inside of a mesh
                for (int x = 0; x < width; x++)
                {
                    for (int y = 0; y < height; y++)
                    {
                        // fill inside of a mesh from z-nearest side (x, y, 0)
                        for (int z = 0; z < depth; z++)
                        {
                            // continue if (x, y, z) is empty
                            if (volume[x, y, z].IsEmpty())
                            {
                                continue;
                            }

                            // step forward to front face
                            int ifront = z;
                            for (; ifront < depth && volume[x, y, ifront].IsFrontFace(); ifront++)
                            {
                            }

                            // break if position is out of bounds
                            if (ifront >= depth)
                            {
                                break;
                            }

                            int iback = ifront;

                            // step forward to empty
                            for (; iback < depth && volume[x, y, iback].IsEmpty(); iback++)
                            {
                            }

                            if (iback >= depth)
                            {
                                break;
                            }

                            // check if iback is back voxel
                            if (volume[x, y, iback].IsBackFace())
                            {
                                // step forward to back face
                                for (; iback < depth && volume[x, y, iback].IsBackFace(); iback++)
                                {
                                }
                            }

                            // fill from ifront to iback
                            for (int z2 = ifront; z2 < iback; z2++)
                            {
                                var p     = boxes[x, y, z2].center;
                                var voxel = volume[x, y, z2];
                                voxel.position   = p;
                                voxel.fill       = 1;
                                volume[x, y, z2] = voxel;
                            }

                            // advance loop to (x, y, iback)
                            z = iback;
                        }
                    }
                }
            }

            // get non-empty voxels
            voxels = new List <Voxel_t>();
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    for (int z = 0; z < depth; z++)
                    {
                        if (!volume[x, y, z].IsEmpty())
                        {
                            voxels.Add(volume[x, y, z]);
                        }
                    }
                }
            }
        }
예제 #2
0
        public static GPUVoxelData Voxelize(ComputeShader voxelizer, Bounds bounds, Mesh mesh, int resolution = 64)
        {
            // From the specified resolution, calculate the unit length of one voxel
            float maxLength = Mathf.Max(bounds.size.x, Mathf.Max(bounds.size.y, bounds.size.z));
            var   unit      = maxLength / resolution;

            // half of the unit length
            var hunit = unit * 0.5f;

            // The bounds extended by "half of the unit length constituting one voxel" is defined as the scope of voxelization
            var start = bounds.min - new Vector3(hunit, hunit, hunit);  // Minimum bounds to voxel
            var end   = bounds.max + new Vector3(hunit, hunit, hunit);  // Maximum bounds to voxel
            var size  = end - start;                                    // Size of bounds to voxel

            // The size of three-dimensional voxel data is determined based on the unit length of the voxel and the scope of voxelization
            int width  = Mathf.CeilToInt(size.x / unit);
            int height = Mathf.CeilToInt(size.y / unit);
            int depth  = Mathf.CeilToInt(size.z / unit);

            // generate ComputeBuffer representing Voxel_t array
            var voxelBuffer = new ComputeBuffer(width * height * depth, Marshal.SizeOf(typeof(Voxel_t)));
            var voxels      = new Voxel_t[voxelBuffer.count];

            voxelBuffer.SetData(voxels); // initialize

            // send voxel data to GPU
            voxelizer.SetVector("_Start", start);
            voxelizer.SetVector("_End", end);
            voxelizer.SetVector("_Size", size);

            voxelizer.SetFloat("_Unit", unit);
            voxelizer.SetFloat("_InvUnit", 1f / unit);
            voxelizer.SetFloat("_HalfUnit", hunit);
            voxelizer.SetInt("_Width", width);
            voxelizer.SetInt("_Height", height);
            voxelizer.SetInt("_Depth", depth);

            // generate ComputeBuffer representing vertex array
            var vertices   = mesh.vertices;
            var vertBuffer = new ComputeBuffer(vertices.Length, Marshal.SizeOf(typeof(Vector3)));

            vertBuffer.SetData(vertices);

            // generate ComputeBuffer representing triangle array
            var triangles = mesh.triangles;
            var triBuffer = new ComputeBuffer(triangles.Length, Marshal.SizeOf(typeof(int)));

            triBuffer.SetData(triangles);

            // send mesh data to GPU kernel "SurfaceFront" and "SurfaceBack"
            var surfaceFrontKer = new Kernel(voxelizer, "SurfaceFront");

            voxelizer.SetBuffer(surfaceFrontKer.Index, "_VoxelBuffer", voxelBuffer);
            voxelizer.SetBuffer(surfaceFrontKer.Index, "_VertBuffer", vertBuffer);
            voxelizer.SetBuffer(surfaceFrontKer.Index, "_TriBuffer", triBuffer);

            // set triangle count in a mesh
            var triangleCount = triBuffer.count / 3;

            voxelizer.SetInt("_TriangleCount", triangleCount);

            // execute surface construction in front triangles
            voxelizer.Dispatch(surfaceFrontKer.Index, triangleCount / (int)surfaceFrontKer.ThreadX + 1, (int)surfaceFrontKer.ThreadY, (int)surfaceFrontKer.ThreadZ);

            // execute surface construction in back triangles
            var surfaceBackKer = new Kernel(voxelizer, "SurfaceBack");

            voxelizer.SetBuffer(surfaceBackKer.Index, "_VoxelBuffer", voxelBuffer);
            voxelizer.SetBuffer(surfaceBackKer.Index, "_VertBuffer", vertBuffer);
            voxelizer.SetBuffer(surfaceBackKer.Index, "_TriBuffer", triBuffer);
            voxelizer.Dispatch(surfaceBackKer.Index, triangleCount / (int)surfaceBackKer.ThreadX + 1, (int)surfaceBackKer.ThreadY, (int)surfaceBackKer.ThreadZ);

            // send voxel data to GPU kernel "Volume"
            var volumeKer = new Kernel(voxelizer, "Volume");

            voxelizer.SetBuffer(volumeKer.Index, "_VoxelBuffer", voxelBuffer);

            // execute to fill voxels inside of a mesh
            voxelizer.Dispatch(volumeKer.Index, width / (int)volumeKer.ThreadX + 1, height / (int)volumeKer.ThreadY + 1, (int)surfaceFrontKer.ThreadZ);

            // dispose unnecessary mesh data
            vertBuffer.Release();
            triBuffer.Release();

            return(new GPUVoxelData(voxelBuffer, width, height, depth, unit));
        }