[Wiki/HOWTO] Oriented Bounding Box in Unity ... finally ? eek !

4082560--356218--Unity_2019-01-10_02-58-28.png

Hi everyone !

I’ve been looking for an oriented bounding box to use from within a custom editor I’m developing in Unity but I couldn’t find any, except for flaming debates about the laziness of Unity team to bring us one … anyway, today I’m sharing this with you:

https://github.com/aybe/MathGeoLib.Exports

This is a C# wrapper around the outstanding MathGeoLib library, I haven’t implemented everything because it’s really huge, however, I brought the necessary stuff for a decent OBB from within Unity (see the picture above).

It’s pretty fast at OBB’ing and easy to use,

The thing I absolutely wanted to add was the ability to serialize it since it’s naturally not a cheap operation on huge meshes. Personally I didn’t need more than getting its position, extent and axes to be able to draw bounds in my editor, however, I felt it needed a few more methods for querying against it (e.g. point inside, distance, etc) so I’ve implemented quite a few of them but not all since there’s really a lot of them which would imply porting even more types, i.e. snowball effect :hushed:.

So there you are, OBB from within Unity, grab the zip release at the first link I’ve posted and see the instructions. Basically you drag and drop native plugins and drop the C# files into your project, things should go smoothly then.

Many thanks to the original author of the library :).

Hope you’ll like it, share your comments/feelings/reviews/whatever !

PS I didn’t add XML comments but the methods and parameters names are evocative enough for a natural usage, in doubt, see the original author documentation in the header (4th link I’ve posted); my wrapper does nothing funny in between, it just passes data back and forth between managed and native worlds.

PS The only change I made to the native library was to enable static linking, DLL is a bit bigger but you won’t need any extra Visual C++ redistributable to drag along with your project (a good thing).

Cheers :smile:

8 Likes

I haven’t had a need for an OBB yet, but I’m curious about the process of obtaining one.

I’m also curious why you reached for that MathGeoLib package. Was it for better native performance over massive meshes?

Not having made an OBB routine myself, I would speculate that the way it is done is to:

  1. iterate all tris to get all used verts in local space:
  • generate a “grow as needed” bounds around that (six axis-facing plane equations, in x, y, z)
  1. Apply GameObject’s rotation/translation/scaling to the six planes to get the final OBB

Am I missing something essential?

In any case, I am glad you solved your problem, and props to you for sharing it here. Never know who it might help!

This is cool looking project.
I was working on my own OBB / AAB other day, but with ECS in mind.
However, there is bounds in GameObject, which automatically provides Bounding box of mesh, when applying box collider for example.

So my question is, out of curiosity, what are your motives, to implement own OBB?

Also, wouldn’t be worth, trying move into ECS, or at least jobify OOB calculations?

I used this library because it’s the one that worked and was fast. GeometricToolsEngine was very slow, couldn’t built ApproxMVBB with its chain of dependencies. What’s not trivial is finding the orientation, then you might get something providing you have the skills but will it run fast ?

A cheap and easy way to OBB via a GO’s transform seems weird; its orientation don’t necessarily mean the real orientation, so you might be coincidentally close but not always. For ECS and stuff, it looks nice but as said before, it’s re-inventing the wheel.

I needed this because I was bored to each time use Alt+click when framing an object in scene to rotate and see it from the front, now I can save many mouse clicks and time in the end :smile:.

Fair enough, since you found way to make it work for you :slight_smile:

Well not really reinventing. Is just converting, to bust performance. If you really need it any faster of course. Otherwise, probably is not worth it at this point.

You know what, I was actually tempted to try do it first like, ‘sounds simple, right ?’ or ‘challenge accepted’ . But just look at the code, it is FMA, AVX and SSE enabled, the author is definitely in charge !

And guess what, even though I’ve got **GeometricToolsEngine **to build, it under-performed by magnitudes in Release build for the same set of points even though I’ve set its ‘maxThreads’ parameter to my value of ‘std::thread::hardware_concurrency’. You’d see a small CPU burst but then nothing; I’ve paused debugging and it was stuck in filling vectors … OTOH, in MathGeoLib this sorting phase happens instantly, even in a Debug build.

I’d rather have a finely-tuned, single-threaded process, than a bells and whistles that performs abysmally ;).

PS I’m not saying that GeometricToolsEngine is bad, it’s an excellent showcase library but to me it’s clear that MathGeoLib outperforms it in terms of speed even though it’s single-threaded.

1 Like

Thats fine. No stress about it :wink:
What works best. In the end, no one want spend forever, on writing scripts :smile:

My motto would be ‘always stay simple when it comes to coding’ since it naturally becomes complex, but more often than not I forget to apply this principle to myself and get stuck into gory details :).

This time I didn’t fall on the trap of ‘you can do it bro, it’s only a 3d box (but remember, one that’s rotated !)’ You must delegate things out somehow if you want to advance on your personal coding projects, otherwise you’ll be still here after the Big Crunch :smile:.

1 Like

Thx for offer, but I will stay with what we got so far in Unity :sunglasses:
I got plenty for now, to type anyway, so I wont get bored.

That’s really neat!

Oriented bounding boxes are great for fast, approximate checking for overlap. Once it’s calculated, it’s almost as fast as an AABB (just need to transform the point you’re checking to the oriented BB’s space), and it gives a much better approximation

1 Like

That’s great! Thank you.

I was just looking at implementing this https://pdfs.semanticscholar.org/a76f/7da5f8bae7b1fb4e85a65bd3812920c6d142.pdf
and was not looking forward to it.

nice stuff!
How do you use it in a project in Unity?

Hey! Nice stuff, I’ve been looking at getting something like this for a while, but never got around to actually writing it.

I’m a bit confused though: Why are you defining the rotation of the bounding box as 3 separate Vector3? Wouldn’t it be simpler to define the rotation euler angles?

What do the Axis1, 2 and 3 represent?

Thanks!

Hey! Looks nice, but take a look at my code below, maybe I am doing something wrong, because I can’t get any time gain when using this - compared to Unity Bounds:

for (int i = 0; i < meshes.Length; i++)
{
    Mesh ms = meshes[i].sharedMesh;
    int vc = ms.vertexCount;
    Debug.Log("ms.vertexCount " + ms.vertexCount.ToString());
    for (int j = 0; j < vc; j++)
    {
        if (i == 0 && j == 0)
        {
            if (_useMathGeo)
            {
                obb = new OrientedBoundingBox(meshes[i].transform.TransformPoint(ms.vertices[j]), Vector3.zero, go.transform.right, go.transform.up, go.transform.forward);
            }
            else
            {
                bounds = new Bounds(meshes[i].transform.TransformPoint(ms.vertices[j]), Vector3.zero);
            }
        }
        else
        {
            if (_useMathGeo)
            {
                obb.Enclose(meshes[i].transform.TransformPoint(ms.vertices[j]));
            }
            else
            {
                bounds.Encapsulate(meshes[i].transform.TransformPoint(ms.vertices[j]));
            }
        }
        if (j > 150) break;//for testing, otherwise it would take ages
    }
}
if (_useMathGeo) bounds = new Bounds(obb.Center, 2 * obb.Extent);

It takes exactly the same amount of time no matter if using OrientedBoundingBox or Unity’s Bounds.
I was actually trying to get it to work with gameobjects with around 40 meshes, from 0.2 to 24k vertices each.

The goal of an oriented bounding box is not to be faster than an AABB, it’s to give a more accurate representation of the original geometry.

If it’s actually as fast, then that’s great, because it’s fundamentally having to do more work.

2 Likes

However it’s also fast.
But after replacing transform and transformPoint from the above code - with matrix4x4 and MultiplyPoint3x4 - the calculation time has dropped in my case by orders of magnitude, from hours to seconds.
Moreover I have moved it from the main thread so the bound box calculation is not a bottleneck in my project any more. Thanks @aybeone for sharing this.

Can you tell me how to plug in this OBB in a unity project? Not very sure what I should do.

You can find everything in the first post in this thread and in the links inside it.

@tomekkie2 mind sharing how you used the matrix? Modified the sample code in this thread like so and it instead increased the time!

Matrix4x4 m;

for (int i = 0; i < meshes.Length; i++)
{
    Mesh ms = meshes[i].sharedMesh;
    int vc = ms.vertexCount;
    m = Matrix4x4.TRS(meshes[i].transform.position,meshes[i].transform.rotation,meshes[i].transform.localScale);
    Debug.Log("ms.vertexCount " + ms.vertexCount.ToString());
    for (int j = 0; j < vc; j++)
    {
        if (i == 0 && j == 0)
        {
            if (_useMathGeo)
            {
                obb = new OrientedBoundingBox(m.MultiplyPoint3x4(ms.vertices[j]), Vector3.zero, go.transform.right, go.transform.up, go.transform.forward);
            }
            else
            {
                bounds = new Bounds(meshes[i].transform.TransformPoint(ms.vertices[j]), Vector3.zero);
            }
        }
        else
        {
            if (_useMathGeo)
            {
                obb.Enclose(m.MultiplyPoint3x4(ms.vertices[j]));
            }
            else
            {
                bounds.Encapsulate(meshes[i].transform.TransformPoint(ms.vertices[j]));
            }
        }
        if (j > 150) break;//for testing, otherwise it would take ages
    }
}
if (_useMathGeo) bounds = new Bounds(obb.Center, 2 * obb.Extent);

How can I integrate the oriented bounding box? I am trying to get the vertices from a Plane that intersect with the bounds of a rotated cube. I am trying to get the points from a Plane that intersect with the boundaries of a cube. The problem is, the calculation is based on the AABB and is therefore useless for rotated cubes. For this calculation I need the oriented bounding box. Can someone help me?

On the first picture you see a cube with an axis aligned bounding box (AABB)

And this is my target:

Here is my code that I used to calculate it:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class Intersection : MonoBehaviour
{
    public GameObject m_MyObject, m_NewObject;
    Collider m_Collider, m_Collider2;
    private List<Vector3> inboundsVertices = new List<Vector3>();
    private bool drawVertGizmos = false;

    void Start()
    {
        //Check that the first GameObject exists in the Inspector and fetch the Collider
        if (m_MyObject != null)
            m_Collider = m_MyObject.GetComponent<Collider>();

        //Check that the second GameObject exists in the Inspector and fetch the Collider
        if (m_NewObject != null)
            m_Collider2 = m_NewObject.GetComponent<Collider>();
    }

    void Update()
    {
        //If the first GameObject's Bounds enters the second GameObject's Bounds, output the message
        if (m_Collider.bounds.Intersects(m_Collider2.bounds))
        {
            CheckVertices(m_MyObject, m_Collider2.bounds);
            drawVertGizmos = true;
        }
    }

    void OnDrawGizmos()
    {
        if (drawVertGizmos)
        {
            DrawVertexGizmos();
            DrawBoundingBox();
        }
    }

    void DrawBoundingBox()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireCube(m_NewObject.transform.position, m_Collider2.bounds.size);
    }

    void DrawVertexGizmos()
    {
        foreach (Vector3 vertex in inboundsVertices)
        {
            Gizmos.DrawSphere(vertex, 0.04f);
        }
    }

    void CheckVertices(GameObject obj, Bounds bounds)
    {
        inboundsVertices.Clear();
        if (obj == null)
            return;
        MeshFilter mf = obj.GetComponent<MeshFilter>();
        if (mf == null)
            return;
        Vector3[] verticesToCheck = obj.GetComponent<MeshFilter>().mesh.vertices;
        foreach (Vector3 vertex in verticesToCheck)
        {
            Vector3 pos = obj.transform.TransformPoint(vertex);
            if (bounds.Contains(pos))
            {
                inboundsVertices.Add(pos);
            }
        }
    }
}