0
\$\begingroup\$

I'm using the IntersectMovingAABBAABB implementation from the book Realtime Collision Detection and I'm having a little bit of an issue. When I have two AABBs next to each other (not touching), A being stationnary and B moving up or down, I'm getting a result saying there's a collision. Obviously this is wrong.

 _______     _______
|       |   |       |
|   A   |   |   B   |  B moving up/down
|_______|   |_______|

I've rechecked my code multiple times to make sure I had the same thing as in the book. Stepping through the code, I realized that the check for v[i] < 0 and v[i] > 0 will never be true for either X or Z axis when moving straight up or down. Therefor, no checks for intersections on these axis will be done. I find that quite weird, and I was wondering if I was missing something, or if this code is simply bad.

Here's my implementation for reference:

public static bool IntersectMovingAABBAABB(AABB a, AABB b, Vector3 va, Vector3 vb, out float first, out float last, out BlockFace face)
{
    face = BlockFace.None;
    if (TestAABB(a, b))
    {
        first = last = 0;
        return true;
    }

    Vector3 v = vb - va;
    float current;

    first = 0;
    last = 1;

    for (int i = 0; i < 3; i++)
    {
        if (v[i] < 0)
        {
            if (b.Max[i] < a.Min[i]) return false;
            if (a.Max[i] < b.Min[i]) if ((first = Math.Max(current= (a.Max[i] - b.Min[i]) / v[i], first)) == current) face = (BlockFace)(1 << (i));
            if (b.Max[i] > a.Min[i]) last = Math.Min((a.Min[i] - b.Max[i]) / v[i], last);
        }

        if (v[i] > 0)
        {
            if (a.Max[i] < b.Min[i]) return false;
            if (b.Max[i] < a.Min[i]) if ((first = Math.Max(current = (a.Min[i] - b.Max[i]) / v[i], first)) == current) face = (BlockFace)(1 << (i + 3));
            if (a.Max[i] > b.Min[i]) last = Math.Min((a.Max[i] - b.Min[i]) / v[i], last);
        }

        if (first > last) return false;
    }

    return true;
}
\$\endgroup\$
1
\$\begingroup\$

Realtime collision detection is a great book, but that code is clearly wrong. If the relative velocity was zero, it would always return true, which is clearly incorrect. I believe the first test in each of the if (v[i] ...) blocks needs to be done if the velocity is <= 0 and >= 0 respectively. Note that you can't just change those tests though, because you could then end up dividing by zero. There may be a more elegant way, but I think this should work:

    if (v[i] < 0)
    {
        if (b.Max[i] < a.Min[i]) return false;
        if (a.Max[i] < b.Min[i]) if ((first = Math.Max(current= (a.Max[i] - b.Min[i]) / v[i], first)) == current) face = (BlockFace)(1 << (i));
        if (b.Max[i] > a.Min[i]) last = Math.Min((a.Min[i] - b.Max[i]) / v[i], last);
    }
    else if (v[i] > 0)
    {
        if (a.Max[i] < b.Min[i]) return false;
        if (b.Max[i] < a.Min[i]) if ((first = Math.Max(current = (a.Min[i] - b.Max[i]) / v[i], first)) == current) face = (BlockFace)(1 << (i + 3));
        if (a.Max[i] > b.Min[i]) last = Math.Min((a.Max[i] - b.Min[i]) / v[i], last);
    }
    else
    {
        if (b.Max[i] < a.Min[i] || a.Max[i] < b.Min[i]) return false;
    }
\$\endgroup\$
  • \$\begingroup\$ Amazing! Thanks a lot! I find it kind of weird that such a good book wouldn't have a working implementation. I checked the errata, nothing there! \$\endgroup\$ – Philippe Paré Jul 16 '17 at 18:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.