Burstable version of GeometryUtility.CalculateBounds

I’ve been using GeometryUtility.CalculateBounds to do some geometry stuff in my game prototype. I’m now moving things over to Jobs and Burst. I am still using GameObjects and am diving into IJobParallelForTransform. I need to do the same thing that this CalculateBounds function does but do it in a Burst compatible function.

I tried to find the source of this function so I could write my own Burst-compatible version but have yet to have luck.

Suggestions?

[BurstCompile]
public static class BurstGeometryUtility {
    public static Bounds CalculateBounds(NativeArray<float3> array, float4x4 trs) {
        if (array.Length == 0) {
            ThrowEmpty();
            // default to zero-sized bounds in player builds - no exception support
            return new Bounds(new Vector3(0, 0, 0), new Vector3(0, 0, 0));
        }
        float3 min = math.transform(trs, array[0]), max = min;
        for (int i = 1; i < array.Length; i++) {
            float3 value = math.transform(trs, array[i]);
            // componentwise min/max works as expected
            min = math.min(min, value);
            max = math.max(max, value);
        }
        return new Bounds((min + max) * 0.5f, max - min);
    }

    [BurstCompile]
    public static void CalculateBounds(ref NativeArray<float3> array, in float4x4 trs, out Bounds bounds) {
        bounds = CalculateBounds(array, trs);
    }

    [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
    private static void ThrowEmpty() {
        throw new ArgumentException("Array cannot be empty");
    }
}

Something along these lines could work. Moving around a bunch of points and taking the localToWorldMatrix of some object for the second parameter, this seems to match up with the GeometryUtility method.

3 Likes

The solution provided by @Anthiese should work, however, if performance is an issue, this could be easily parallelized since Bounds union operator is abelian :slight_smile:

Checkout my implementation for “accumulated product”:

https://github.com/andywiecko/BurstCollections#nativeaccumulatedproductt-op

1 Like

Thanks so much for the replies. Since I’ve also started using the work of @andywiecko , I am converting over from Bounds to AABB since floatN should be better in Burst than VectorN.

Rolled this quick extension:

public static class BoundsExtension
    {
        public static AABB ToAABB(this Bounds bounds)
        {
            return new AABB(
                new float2(bounds.min.x, bounds.min.y),
                new float2(bounds.max.x, bounds.max.y)
            );
        }
    }

I’ll be using this to check collisions when placing some trees in my prototype’s map generation.

var bounds = new NativeArray<AABB>(2, Allocator.TempJob);
bounds[0] = item.trunkBounds.ToAABB();
bounds[1] = item.canopyBounds.ToAABB();

Since those two bounds are actually nAABBs, not axis aligned, I need to align them to the access of the tree’s Transform. Hence the later need for the CalculateBounds function.

8491904--1129847--upload_2022-10-5_10-2-41.png

This is the code I started to write to do the alignment; bounds above is newObjectBounds below. I like the work by @Anthiese and will use it as a basis for a CalculateAABB refactor/fix of my code below, which I am not even sure works.

float4x4 tMatrix = float4x4.TRS(location, transform.rotation, transform.localScale);

for (int vN = 0; vN < newObjectBounds.Length; vN++)
{
     float2 min = math.mul(tMatrix, math.float4(newObjectBounds[vN].Min, 1, 1)).xy;
     float2 max = math.mul(tMatrix, math.float4(newObjectBounds[vN].Max, 1, 1)).xy;
     newObjectBounds[vN] = new AABB(min, max);
}
1 Like

I like the idea of parallel bounds calculation. I have other places where that would be more helpful.

Here’s where I seem to have landed:

[BurstCompile]
public static class BurstGeometryUtility
{
    public static AABB TranslateAABB(AABB bounds, float4x4 trs)
    {
        float2 min = Translate(trs, bounds.Min);
        float2 max = Translate(trs, bounds.Max);

        return new AABB(min, max);
    }

    public static AABB CalculateAABB(NativeArray<float2> array, float4x4 trs)
    {
        if (array.Length == 0)
        {
            ThrowEmpty();
            return default;
        }

        float2 min = Translate(trs, array[0]), max = min;
        for (int i = 0; i < array.Length; i++)
        {
            float2 value = Translate(trs, array[i]);
            min = math.min(min, value);
            max = math.max(max, value);
        }

        return new AABB(min, max);
    }

    private static float2 Translate(float4x4 trs, float2 value)
    {
        return math.transform(trs, math.float3(value, 1)).xy;
    }

    [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
    private static void ThrowEmpty()
    {
        throw new ArgumentException("Array cannot be empty");
    }
}

Unity really should have a more Burst-compatible bounding box of their own.

1 Like