Separating axis theorem implementation results in limitless extending colliders along the Z axis

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;
}

• 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. Dec 17, 2023 at 2:00