protected void Invalidate(PointCloudGrid grid)
    {
        var kernel = compute.FindKernel("Invalidate");

        compute.SetBuffer(kernel, "_PointBuffer", grid.PointBuffer);
        compute.SetInt("_InstancesCount", grid.PointBuffer.count);
        GPUHelper.Dispatch1D(compute, kernel, grid.PointBuffer.count);
    }
    protected void RenderGridBounds(PointCloudGrid grid)
    {
        var bounds = grid.LocalBounds;

        GL.PushMatrix();
        GL.MultMatrix(transform.localToWorldMatrix);

        var min = bounds.min;
        var max = bounds.max;
        var p0  = new Vector3(min.x, min.y, min.z);
        var p1  = new Vector3(max.x, min.y, min.z);
        var p2  = new Vector3(max.x, min.y, max.z);
        var p3  = new Vector3(min.x, min.y, max.z);
        var p4  = new Vector3(min.x, max.y, min.z);
        var p5  = new Vector3(max.x, max.y, min.z);
        var p6  = new Vector3(max.x, max.y, max.z);
        var p7  = new Vector3(min.x, max.y, max.z);

        gridMaterial.SetPass(0);

        GL.Begin(GL.LINES);

        GL.Vertex(p0); GL.Vertex(p1);
        GL.Vertex(p1); GL.Vertex(p2);
        GL.Vertex(p2); GL.Vertex(p3);
        GL.Vertex(p3); GL.Vertex(p0);

        GL.Vertex(p4); GL.Vertex(p5);
        GL.Vertex(p5); GL.Vertex(p6);
        GL.Vertex(p6); GL.Vertex(p7);
        GL.Vertex(p7); GL.Vertex(p4);

        GL.Vertex(p0); GL.Vertex(p4);
        GL.Vertex(p1); GL.Vertex(p5);
        GL.Vertex(p2); GL.Vertex(p6);
        GL.Vertex(p3); GL.Vertex(p7);

        GL.End();

        GL.PopMatrix();
    }
    protected List <PointCloudGrid> BuildGrids(PointCloudData cloud, int gridResolution = 10)
    {
        var grids = new List <PointCloudGrid>();

        if (gridResolution <= 1)
        {
            var one = new PointCloudGrid(cloud.LocalBounds);
            one.Add(cloud.Points);
            one.Build();
            grids.Add(one);
            return(grids);
        }

        var points      = cloud.Points.ToList();
        var localBounds = cloud.LocalBounds;
        var min         = localBounds.min;

        gridUnitLength = Mathf.Max(localBounds.size.x, localBounds.size.y, localBounds.size.z) / gridResolution;
        var w = Mathf.CeilToInt(localBounds.size.x / gridUnitLength);
        var h = Mathf.CeilToInt(localBounds.size.y / gridUnitLength);
        var d = Mathf.CeilToInt(localBounds.size.z / gridUnitLength);
        // Debug.Log(string.Format("{0}:{1}:{2}", w, h, d));

        var usize = Vector3.one * gridUnitLength;

        for (int iz = 0; iz < d; iz++)
        {
            float cz = (iz + 0.5f) * gridUnitLength;
            for (int iy = 0; iy < h; iy++)
            {
                float cy = (iy + 0.5f) * gridUnitLength;
                for (int ix = 0; ix < w; ix++)
                {
                    float cx = (ix + 0.5f) * gridUnitLength;
                    var   bb = new Bounds(min + new Vector3(cx, cy, cz), usize);
                    grids.Add(new PointCloudGrid(bb));
                }
            }
        }

        var wxh = w * h;

        foreach (var p in points)
        {
            var position = (p.position - min);
            int ix       = Mathf.FloorToInt(position.x / gridUnitLength);
            int iy       = Mathf.FloorToInt(position.y / gridUnitLength);
            int iz       = Mathf.FloorToInt(position.z / gridUnitLength);
            int index    = iz * wxh + iy * w + ix;
            if (0 <= index && index < grids.Count)
            {
                grids[index].Add(p);
            }
        }

        const int threshold = 64;
        var       valids    = grids.FindAll(g => !g.Empty(threshold));

        valids.ForEach(g => g.Build());
        // Debug.Log("grids :" + valids.Count);
        return(valids);
    }