Correct box-box overlap tests

 

 

Many box-box overlap tests found on the internet are wrong. This classical Gamasutra article, in particular, has a bugged box-box function.

 

You need to pay attention to the edge-edge case, and tweak the “fabs matrix” as in the good old RAPID library.

 

The code below demonstrates the bug, and how the fixed code looks like.

 

Pierre Terdiman

 

 

 

// Gamasutra code

static bool Gamasutra_OBBOBBOverlap(const Point& e0, const Point& c0, const Matrix3x3& r0, const Point& e1, const Point& c1, const Matrix3x3& r1)

{

            // Translation, in parent frame

            Point v = c1 - c0;

            // Translation, in A's frame

            Point T(v|r0[0], v|r0[1], v|r0[2]);

 

            // B's basis with respect to A's local frame

            float R[3][3];

            float ra, rb, t;

 

            // Calculate rotation matrix

            for(udword i=0;i<3;i++)

            {

                        for(udword k=0;k<3;k++)

                        {

                                    R[i][k] = r0[i] | r1[k];

                        }

            }

 

            // A's basis vectors

            for(udword i=0;i<3;i++)

            {

                        ra = e0[i];

 

                        rb = e1[0]*fabsf(R[i][0]) + e1[1]*fabsf(R[i][1]) + e1[2]*fabsf(R[i][2]);

 

                        t = fabsf(T[i]);

 

                        if(t > ra + rb)                return false;

            }

 

            // B's basis vectors

            for(udword k=0;k<3;k++)

            {

                        ra = e0[0]*fabsf(R[0][k]) + e0[1]*fabsf(R[1][k]) + e0[2]*fabsf(R[2][k]);

 

                        rb = e1[k];

 

                        t = fabsf(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]);

 

                        if( t > ra + rb )  return false;

            }

 

            if(1)

            {

                        //9 cross products

 

                        //L = A0 x B0

                        ra         = e0[1]*fabsf(R[2][0]) + e0[2]*fabsf(R[1][0]);

                        rb         = e1[1]*fabsf(R[0][2]) + e1[2]*fabsf(R[0][1]);

                        t           = fabsf(T[2]*R[1][0] - T[1]*R[2][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A0 x B1

                        ra         = e0[1]*fabsf(R[2][1]) + e0[2]*fabsf(R[1][1]);

                        rb         = e1[0]*fabsf(R[0][2]) + e1[2]*fabsf(R[0][0]);

                        t           = fabsf(T[2]*R[1][1] - T[1]*R[2][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A0 x B2

                        ra         = e0[1]*fabsf(R[2][2]) + e0[2]*fabsf(R[1][2]);

                        rb         = e1[0]*fabsf(R[0][1]) + e1[1]*fabsf(R[0][0]);

                        t           = fabsf(T[2]*R[1][2] - T[1]*R[2][2]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B0

                        ra         = e0[0]*fabsf(R[2][0]) + e0[2]*fabsf(R[0][0]);

                        rb         = e1[1]*fabsf(R[1][2]) + e1[2]*fabsf(R[1][1]);

                        t           = fabsf(T[0]*R[2][0] - T[2]*R[0][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B1

                        ra         = e0[0]*fabsf(R[2][1]) + e0[2]*fabsf(R[0][1]);

                        rb         = e1[0]*fabsf(R[1][2]) + e1[2]*fabsf(R[1][0]);

                        t           = fabsf(T[0]*R[2][1] - T[2]*R[0][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B2

                        ra         = e0[0]*fabsf(R[2][2]) + e0[2]*fabsf(R[0][2]);

                        rb         = e1[0]*fabsf(R[1][1]) + e1[1]*fabsf(R[1][0]);

                        t           = fabsf(T[0]*R[2][2] - T[2]*R[0][2]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B0

                        ra         = e0[0]*fabsf(R[1][0]) + e0[1]*fabsf(R[0][0]);

                        rb         = e1[1]*fabsf(R[2][2]) + e1[2]*fabsf(R[2][1]);

                        t           = fabsf(T[1]*R[0][0] - T[0]*R[1][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B1

                        ra         = e0[0]*fabsf(R[1][1]) + e0[1]*fabsf(R[0][1]);

                        rb         = e1[0] *fabsf(R[2][2]) + e1[2]*fabsf(R[2][0]);

                        t           = fabsf(T[1]*R[0][1] - T[0]*R[1][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B2

                        ra         = e0[0]*fabsf(R[1][2]) + e0[1]*fabsf(R[0][2]);

                        rb         = e1[0]*fabsf(R[2][1]) + e1[1]*fabsf(R[2][0]);

                        t           = fabsf(T[1]*R[0][2] - T[0]*R[1][2]);

                        if(t > ra + rb)    return false;

            }

            return true;

}

 

// Fixed Gamasutra code

static bool Fixed_Gamasutra_OBBOBBOverlap(const Point& e0, const Point& c0, const Matrix3x3& r0, const Point& e1, const Point& c1, const Matrix3x3& r1)

{

            // Translation, in parent frame

            Point v = c1 - c0;

            // Translation, in A's frame

            Point T(v|r0[0], v|r0[1], v|r0[2]);

 

            // B's basis with respect to A's local frame

            float R[3][3];

            float FR[3][3];

            float ra, rb, t;

 

            // Calculate rotation matrix

            for(udword i=0;i<3;i++)

            {

                        for(udword k=0;k<3;k++)

                        {

                                    R[i][k] = r0[i] | r1[k];

                                    FR[i][k] = 1e-6f + fabsf(R[i][k]);          // Precompute fabs matrix

                        }

            }

 

            // A's basis vectors

            for(udword i=0;i<3;i++)

            {

                        ra = e0[i];

 

                        rb = e1[0]*FR[i][0] + e1[1]*FR[i][1] + e1[2]*FR[i][2];

 

                        t = fabsf(T[i]);

 

                        if(t > ra + rb)                return false;

            }

 

            // B's basis vectors

            for(udword k=0;k<3;k++)

            {

                        ra = e0[0]*FR[0][k] + e0[1]*FR[1][k] + e0[2]*FR[2][k];

 

                        rb = e1[k];

 

                        t = fabsf(T[0]*R[0][k] + T[1]*R[1][k] + T[2]*R[2][k]);

 

                        if( t > ra + rb )  return false;

            }

 

            if(1)

            {

                        //9 cross products

 

                        //L = A0 x B0

                        ra         = e0[1]*FR[2][0] + e0[2]*FR[1][0];

                        rb         = e1[1]*FR[0][2] + e1[2]*FR[0][1];

                        t           = fabsf(T[2]*R[1][0] - T[1]*R[2][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A0 x B1

                        ra         = e0[1]*FR[2][1] + e0[2]*FR[1][1];

                        rb         = e1[0]*FR[0][2] + e1[2]*FR[0][0];

                        t           = fabsf(T[2]*R[1][1] - T[1]*R[2][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A0 x B2

                        ra         = e0[1]*FR[2][2] + e0[2]*FR[1][2];

                        rb         = e1[0]*FR[0][1] + e1[1]*FR[0][0];

                        t           = fabsf(T[2]*R[1][2] - T[1]*R[2][2]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B0

                        ra         = e0[0]*FR[2][0] + e0[2]*FR[0][0];

                        rb         = e1[1]*FR[1][2] + e1[2]*FR[1][1];

                        t           = fabsf(T[0]*R[2][0] - T[2]*R[0][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B1

                        ra         = e0[0]*FR[2][1] + e0[2]*FR[0][1];

                        rb         = e1[0]*FR[1][2] + e1[2]*FR[1][0];

                        t           = fabsf(T[0]*R[2][1] - T[2]*R[0][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A1 x B2

                        ra         = e0[0]*FR[2][2] + e0[2]*FR[0][2];

                        rb         = e1[0]*FR[1][1] + e1[1]*FR[1][0];

                        t           = fabsf(T[0]*R[2][2] - T[2]*R[0][2]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B0

                        ra         = e0[0]*FR[1][0] + e0[1]*FR[0][0];

                        rb         = e1[1]*FR[2][2] + e1[2]*FR[2][1];

                        t           = fabsf(T[1]*R[0][0] - T[0]*R[1][0]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B1

                        ra         = e0[0]*FR[1][1] + e0[1]*FR[0][1];

                        rb         = e1[0] *FR[2][2] + e1[2]*FR[2][0];

                        t           = fabsf(T[1]*R[0][1] - T[0]*R[1][1]);

                        if(t > ra + rb)    return false;

 

                        //L = A2 x B2

                        ra         = e0[0]*FR[1][2] + e0[1]*FR[0][2];

                        rb         = e1[0]*FR[2][1] + e1[1]*FR[2][0];

                        t           = fabsf(T[1]*R[0][2] - T[0]*R[1][2]);

                        if(t > ra + rb)    return false;

            }

            return true;

}

 

 

static void TestEdgeEdgeBadCase()

{

            bool success;

 

            {

                        Matrix3x3 r0;

                        Matrix3x3 r1;

                        r0.SetCol(0, Point(1.000000e+00f, 0.000000e+00f, 0.000000e+00f));

                        r0.SetCol(1, Point(0.000000e+00f, 1.000000e+00f, 0.000000e+00f));

                        r0.SetCol(2, Point(0.000000e+00f, 0.000000e+00f, 1.000000e+00f));

 

                        r1.SetCol(0, Point(0.000000e+00f, 1.000000e+00f, 2.980232e-08f));

                        r1.SetCol(1, Point(2.980232e-08f, 0.000000e+00f, 1.000000e+00f));

                        r1.SetCol(2, Point(1.000000e+00f, 2.980232e-08f, 0.000000e+00f));

 

                        // Should return "0" (that's the bug)

                        success = Gamasutra_OBBOBBOverlap                      (Point (5.000000e-01f, 5.000000e-01f, 5.000000e-01f), Point (0.000000e+00f, 0.000000e+00f, 0.000000e+00f), r0, Point (5.000000e+02f, 5.000000e+01f, 2.000000e+02f), Point (8.792448e-27f, -3.507065e+00f, 3.621201e+02f), r1);

                        // Should return "1"

                        success = Fixed_Gamasutra_OBBOBBOverlap (Point (5.000000e-01f, 5.000000e-01f, 5.000000e-01f), Point (0.000000e+00f, 0.000000e+00f, 0.000000e+00f), r0, Point (5.000000e+02f, 5.000000e+01f, 2.000000e+02f), Point (8.792448e-27f, -3.507065e+00f, 3.621201e+02f), r1);

            }

 

}