1
\$\begingroup\$

I have a method that transforms a point cloud and aligns it with its gameObject.transform. My method works well, but i have to rotate the cloud, and then I have to use an approach (taking from 2d bitmap rotation) where i rotate the destination grid in inverse and check points in the source cloud, in order to get a good quality rendering. If I just do one or the other, I end up with holes. So my question is: Is there a cleaner simpler way to get a good result?

private void AlignPointCloudToTransform(PointCloudTransform pCT, ref Dictionary<Vector3Int, Color> alignedCloud)
        {
            // FORWARDS - rotating and translating cloud to destination space
            Quaternion localRot = Quaternion.Inverse(objectRootT.transform.rotation) * pCT.transform.rotation;
            Vector3 localPos = objectRootT.InverseTransformPoint(pCT.transform.position);
            Matrix4x4 matrix = Matrix4x4.TRS(localPos, localRot, Vector3.one);

            Bounds bounds = new Bounds();
            bool boundsSet = false;

            foreach (var point in sourceCloud)
            {
                Vector3 transformedPos = matrix.MultiplyPoint3x4(point.Key);
                Vector3Int pos = VoxelUtil.RoundVector3(transformedPos);

                if (!boundsSet)
                {
                    bounds = new Bounds(pos, Vector3.zero);
                    boundsSet = true;
                }
                else bounds.Encapsulate(pos);

                alignedCloud[pos] = point.Value;
            }

            // + INVERSE transforming destination space and checking from there if each point exists
            Matrix4x4 invMatrix = matrix.inverse;
            Vector3Int dest = Vector3Int.zero;
            Vector3Int bMin = VoxelUtil.RoundVector3(bounds.min);
            Vector3Int bMax = VoxelUtil.RoundVector3(bounds.max);

            for (dest.x = bMin.x; dest.x < bMax.x; dest.x++)
            {
                for (dest.y = bMin.y; dest.y < bMax.y; dest.y++)
                {
                    for (dest.z = bMin.z; dest.z < bMax.z; dest.z++)
                    {
                        Vector3 testPosFloat = invMatrix.MultiplyPoint3x4(dest);
                        Vector3Int testPos = VoxelUtil.RoundVector3(testPosFloat);

                        if (sourceCloud.TryGetValue(testPos, out Color color))
                        {
                            alignedCloud[dest] = color;
                        }
                    }
                }
            }
        }

Edit: I tried a 'Rotate by shearing' approach as suggested by DMGregory, and I do like how it's fairly straight-forward, but it is very sensitive to rotation order and the quality of the output is only ~80%. It may be possible to improve quality by setting a unique rotation order for each object. And it may be more efficient than the other approach, but I haven't tested performance. Here's some of that code:

foreach (var point in sourceCloud)
            {
                Vector3 transed = point.Key;// + localPos;

                // Rotate Z
                transed = ShearA(transed, zero, z, x, y, eulers.z); // Z-A
                transed = ShearB(transed, zero, z, y, x, eulers.z); // Z-B
                transed = ShearA(transed, zero, z, x, y, eulers.z); // Z-C

                // Rotate X
                transed = ShearA(transed, zero, x, y, z, eulers.x); // X-A
                transed = ShearB(transed, zero, x, z, y, eulers.x); // X-B
                transed = ShearA(transed, zero, x, y, z, eulers.x); // X-C

                // Rotate Y
                transed = ShearA(transed, zero, y, z, x, eulers.y); // Y-A
                transed = ShearB(transed, zero, y, x, z, eulers.y); // Y-B
                transed = ShearA(transed, zero, y, z, x, eulers.y); // Y-C

                transed += localPos;

                var rounded = VoxelUtil.RoundVector3(transed);

                alignedCloud[rounded] = point.Value;
            }
\$\endgroup\$
4
  • \$\begingroup\$ In 2D / when your rotation axis is perpendicular to two grid directions, you can use rotation by shearing as an area-preserving discrete rotation that does not introduce holes. I wonder if there's a way to extend that technique to handle non-axis-aligned rotations in 3D... \$\endgroup\$
    – DMGregory
    Jul 14, 2023 at 8:37
  • \$\begingroup\$ @DMGregory That's an interesting idea. I'm going to explore it. Thanks! \$\endgroup\$
    – Josh
    Jul 14, 2023 at 19:26
  • \$\begingroup\$ I suppose in the worst case, any 3D rotation can be decomposed into three sequential axis-aligned rotations using Euler / Tait-Bryan angles, and each of those can be performed using 2D-style rotation by shearing. But each rotation introduces some rounding error, so chaining three of them might accumulate that error into noticeable artifacts. Plus, doing 9 shears might not perform substantially better than the two-pass version you're doing now (though it would at least let you eliminate the hash table overhead and just do a single sequential pass over an array/list instead). \$\endgroup\$
    – DMGregory
    Jul 14, 2023 at 19:34
  • \$\begingroup\$ I thought I might be able to reduce the impact of rotation order by grouping all the first shear steps together, but that broke things. If there is a way to do such a thing in a way that doesn't require 3 shears by 3 axes, I would be curious to see how that works, but the math is way beyond me. \$\endgroup\$
    – Josh
    Jul 15, 2023 at 7:08

0

You must log in to answer this question.

Browse other questions tagged .