1
\$\begingroup\$

I am trying to implement SAT and it works except for the fact that colliders extend infinitely along the Z axis and I can never get past them. I also have confirmed that vertices values are correct. Seemingly everything checks out except for when it comes to actually calculating the overlap.

Minimal reproducible example To move for 1 unit along the x and z axis simply type +x, -x, +z, -z. The bug can be observed in the example

My collision function:

void Body::CheckCollision(Body* other){

    if(isStatic && other->isStatic){
        return;
    }

    glm::vec3 axis;

    //calculate the normal axis:

    glm::vec3 mtvAxis;
    float overlap = std::numeric_limits<float>::infinity();

    for(uint32_t i = 0; i < COLLIDER_VERTEX_COUNT; i++){
        glm::vec3 curr = vertices[i];
        glm::vec3 edges[2];

        edges[0] = vertices[(i + 1) % COLLIDER_VERTEX_COUNT]- curr;
        edges[1] = vertices[(i + 2) % COLLIDER_VERTEX_COUNT]- curr;

        axis = glm::normalize(glm::cross(edges[1], edges[0]));

        OverlapInfo overlapInfo = CheckOverlap(this, other, axis);
        if(!overlapInfo.isOverlapping){
            return;
        }
        if(overlapInfo.overlap < overlap){
            overlap = overlapInfo.overlap;
            mtvAxis = axis;
        }

        curr = other->vertices[i];
        edges[0] = other->vertices[(i + 1) % COLLIDER_VERTEX_COUNT]- curr;
        edges[1] = other->vertices[(i + 2) % COLLIDER_VERTEX_COUNT]- curr;

        axis = glm::normalize(glm::cross(edges[1], edges[0]));

        overlapInfo = CheckOverlap(this, other, axis);
        if(!overlapInfo.isOverlapping){
            return;
        }
        if(overlapInfo.overlap < overlap){
            overlap = overlapInfo.overlap;
            mtvAxis = axis;
        }

        axis = glm::normalize(glm::cross(vertices[(i + 1) % COLLIDER_VERTEX_COUNT] - curr,
        other->vertices[(i + 1) % COLLIDER_VERTEX_COUNT] - other->vertices[i]));

        overlapInfo = CheckOverlap(this, other, axis);
        if(!overlapInfo.isOverlapping){
            return;
        }
        if(overlapInfo.overlap < overlap){
            overlap = overlapInfo.overlap;
            mtvAxis = axis;
        }

        axis = glm::normalize(glm::cross(vertices[(i + 2) % COLLIDER_VERTEX_COUNT] - curr,
        other->vertices[(i + 2) % COLLIDER_VERTEX_COUNT] - other->vertices[i]));
        
        overlapInfo = CheckOverlap(this, other, axis);
        if(!overlapInfo.isOverlapping){
            return;
        }
        
        if(overlapInfo.overlap < overlap){
            overlap = overlapInfo.overlap;
            mtvAxis = axis;
        }

    }


    mtvAxis.x = static_cast<float>(static_cast<int>(mtvAxis.x * 10)) / 10;//Correct some float precision errors
    mtvAxis.y = static_cast<float>(static_cast<int>(mtvAxis.y * 10)) / 10;
    mtvAxis.z = static_cast<float>(static_cast<int>(mtvAxis.z * 10)) / 10;


    pos -= mtvAxis * overlap;

    vel.y = 0; // Simply for convenience so I dont have to deal with gravity for now
    

float Body::CalculateOverlap(float aMinProj, float aMaxProj, float bMinProj, float bMaxProj){
    return std::min(bMaxProj, aMaxProj) - std::max(aMinProj, bMinProj); 
}

OverlapInfo Body::CheckOverlap(Body* b1, Body* b2, glm::vec3 axis){


    OverlapInfo overlapInfo;

    float aMaxProj = -std::numeric_limits<float>::infinity();
    float aMinProj = std::numeric_limits<float>::infinity();

    float bMaxProj = -std::numeric_limits<float>::infinity();
    float bMinProj = std::numeric_limits<float>::infinity();
    

    for(glm::vec3 p : b1->vertices){
        float proj = glm::dot(axis, p);

        if(proj < aMinProj){
            aMinProj = proj;
        }
        if(proj > aMaxProj){
            aMaxProj = proj;
        }
    }

    for(glm::vec3 p : b2->vertices){
        float proj = glm::dot(axis, p);

        if(proj < bMinProj){
            bMinProj = proj;
        }
        if(proj > bMaxProj){
            bMaxProj = proj;
        }
    }
   OverlapInfo Body::CheckOverlap(Body* b1, Body* b2, glm::vec3 axis){


    OverlapInfo overlapInfo;

    float aMaxProj = -std::numeric_limits<float>::infinity();
    float aMinProj = std::numeric_limits<float>::infinity();

    float bMaxProj = -std::numeric_limits<float>::infinity();
    float bMinProj = std::numeric_limits<float>::infinity();
    

    for(glm::vec3 p : b1->vertices){
        float proj = glm::dot(axis, p);

        if(proj < aMinProj){
            aMinProj = proj;
        }
        if(proj > aMaxProj){
            aMaxProj = proj;
        }
    }

    for(glm::vec3 p : b2->vertices){
        float proj = glm::dot(axis, p);

        if(proj < bMinProj){
            bMinProj = proj;
        }
        if(proj > bMaxProj){
            bMaxProj = proj;
        }
    }
    if(aMaxProj < bMinProj || bMaxProj < aMinProj){
        overlapInfo.isOverlapping = false;
        overlapInfo.overlap = std::numeric_limits<float>::infinity();
        return overlapInfo;
    }

    overlapInfo.isOverlapping = true;
    overlapInfo.overlap = CalculateOverlap(aMinProj, aMaxProj, bMinProj, bMaxProj);
    return overlapInfo;
}
overlapInfo.isOverlapping = true;
    overlapInfo.overlap = CalculateOverlap(aMinProj, aMaxProj, bMinProj, bMaxProj);
    return overlapInfo;
}
\$\endgroup\$
1
  • \$\begingroup\$ Infinite(float.max()) values are rarely useful in most calculations. The only valid operations are (- a positive value, + a negative value, * by a number < (1-float.epsilon), / by a number >= 1 ). They cannot be rotated by length by any value other than a multiple of 90 degrees(pi/4). Your min and max replacements are good examples of proper usage, so long a both are replaced. The cross and dot products depend on the internal order of operations even if the passed values should work. \$\endgroup\$
    – agone
    Dec 17, 2023 at 2:00

0

You must log in to answer this question.