Separating Axis Theorom - false positive on vertical axis

152 Views Asked by At

I've implemented a collision detecting using SAT and I get false positives if another bbox is above or below the bounding box.

I'm projecting each (for now) axis aligned face of the bounding box against its own and the other bbox corners.

The collision data returned is correct (depth, stepheight), but the fact that it returns a collision for each object that is above or below the bounding box is ofc false.

I check my results against a simple aabb collision check which works.

Here is the code:

//------------------------------
BoundingBoxIntersectionResult BoundingBox::IntersectionSAT(BoundingBox & other)
{

    // check shortest edge
    f32 distances[6] = {
        (other.mMaxVec.x - this->mMinVec.x),
        (this->mMaxVec.x - other.mMinVec.x),
        (other.mMaxVec.y - this->mMinVec.y),
        (this->mMaxVec.y - other.mMinVec.y),
        (other.mMaxVec.z - this->mMinVec.z),
        (this->mMaxVec.z - other.mMinVec.z)
    };

    i32 faceIndex = 0;
    Vec3 faceNormal;
    f32 collisionDepth = 0.0f;

    // for each face normal, get the minimum and maximum extens of the projection 
    // of all corner points of both shapes
    // if they dont overlap, there is no intersection

    // check each normal
    for (ui32 i = 0; i < 6; i++)
    {
        // CornerPointsWorld represents the world space corner positions
        SATReturn ret = this->SATTest(this->mNormals[i], this->mCornerPointsWorld);
        SATReturn ret2 = this->SATTest(this->mNormals[i], other.mCornerPointsWorld);

        float d1 = ret.minAlong - ret2.maxAlong;
        float d2 = ret2.minAlong - ret.maxAlong;
        if ((d1 > 0.0f) || (d2> 0.0f))
        {
            // return a false collision event, because we got a seperating axis
            return { false, 0.0f, 0.0f, Vec3(), BBOX_SIDE_LEFT };
        }
    }


    // check each normal of the other bbox
    for (ui32 i = 0; i < 6; i++)
    {
        SATReturn ret = this->SATTest(other.mNormals[i], this->mCornerPointsWorld);
        SATReturn ret2 = this->SATTest(other.mNormals[i], other.mCornerPointsWorld);

        float d1 = ret.minAlong - ret2.maxAlong;
        float d2 = ret2.minAlong - ret.maxAlong;
        if ((d1 > 0.0f) || (d2> 0.0f))
        {
            // return a false collision event, because we got a seperating axis
            return { false, 0.0f, 0.0f, Vec3(), BBOX_SIDE_LEFT };
        }

        // get collision data
        if (i == 0 || distances[i] < collisionDepth)
        {
            faceIndex = i;
            faceNormal = this->mNormals[i];
            collisionDepth = distances[i];
        }
    }

    // get step height needed to climb this object
    f32 stepHeight = other.mMaxVec.y - this->mMinVec.y;

    return { true, collisionDepth, stepHeight, faceNormal, BoundingBoxSide(faceIndex) };
}


//------------------------------
SATReturn BoundingBox::SATTest(Vec3& normal, Vector<Vec3>& corners)
{
    SATReturn ret;
    ret.maxAlong = MIN_FLOAT;
    ret.minAlong = MAX_FLOAT;

    // for each point
    for (ui32 i = 0; i < corners.GetSize(); i++)
    {
        f32 dot = Vec3::Dot(corners[i], normal);
        if (dot < ret.minAlong) ret.minAlong = dot;
        if (dot > ret.maxAlong) ret.maxAlong = dot;
    }

    return ret;
}

where face normals are defined as:

Vec3 mNormals[6] =
    {
        Vec3(1,  0,  0), // left
        Vec3(-1,  0,  0), // right
        Vec3(0,  1,  0), // up
        Vec3(0,  -1,  0), // down
        Vec3(0,  0,  1), //back
        Vec3(0,  0,  -1), // front
    };

I've added a screenshot to display the problem:

sat false positive

So the problem is:

It returns false positive for all objects below or above the bounding box.

0

There are 0 best solutions below