I am trying to implement collision detection in 3D between OBB/AABB and sphere. For this i'm using sat/separating axis theorem. For sat to work in 3D i need face normals of obb (sphere to face), each vertex of obb - sphereCenter (sphere to vertex) and edge normals to sphereCenter (sphere to edge). The last i dont know how to calculate. First picture is working fine(sphere to vertex) and other two cases are giving false positives. Any other way to find minimum translation vector and distance of sphere and aabb would also be appreciated.
Box vs Sphere:
void Physics::colliderBoxVsSphere(BoxCollider* c1, SphereCollider* c2, glm::vec3& mtvAxis, float& mtvDist){
auto spPos = c2->getPosition();
std::vector<glm::vec3> obb;
c1->getOBB(obb);
auto mid = midPos2(obb);
// (1) identity axes
std::vector<glm::vec3> axes;
c1->getEntity()->transform()->appendIdentiyAxes(axes);
// (2) vertices
std::vector<glm::vec3> verts;
for (auto& vert : obb) {
auto axisVec = glm::normalize(spPos - vert);
verts.push_back(axisVec);
}
// (3) edges
//TODO
axes.insert(axes.end(), verts.begin(), verts.end());
CollisionMath::instance()->OBBCollideSphere(obb, spPos, c2->getRadius(), axes, mtvAxis, mtvDist);
}
SAT:
bool CollisionMath::OBBCollideSphere(const std::vector<glm::vec3>& obb, const glm::vec3& spherePos, float sphereRad, const std::vector<glm::vec3>& axes, glm::vec3& mtvAxisRet, float& mtvDistanceRet){
glm::vec3 pos1 = midPos(obb);
glm::vec3 pos2 = spherePos;
float mtvDist = std::numeric_limits<float>::max();
glm::vec3 mtvAxis = glm::vec3(0, 0, 0);
for (auto& axis : axes) {
if (axis == glm::vec3(0, 0, 0)) continue;
glm::vec2 p1 = Math::instance()->projectOnAxis(obb, axis);
glm::vec2 p2 = Math::instance()->projectSphereOnAxis(spherePos, sphereRad, axis);
float d1 = glm::dot(pos1, axis);
float d2 = glm::dot(pos2, axis);
if (d1 > d2) {
if (p1.x > p2.y)
return false;
else { //intersect get depth
float depth = p2.y - p1.x;
if (depth < mtvDist) {
mtvDist = depth;
mtvAxis = axis;
}
}
}
else {
if (p2.x > p1.y)
return false;
else { //intersect get depth
float depth = p1.y - p2.x;
if (depth < mtvDist) {
mtvDist = depth;
mtvAxis = -axis;
}
}
}
}
mtvAxisRet = mtvAxis;
mtvDistanceRet = mtvDist;
return true;
}
Project on axis:
float min = glm::dot(verts[0], axis);
float max = min;
for (unsigned i = 1; i < verts.size(); ++i) {
float d = glm::dot(verts[i], axis);
if (d > max) max = d;
if (d < min) min = d;
}
return glm::vec2(min, max);
project sphere on axis:
glm::vec3 A = center + axis * rad;
glm::vec3 B = center - axis * rad;
float min = glm::dot(B,axis);
float max = glm::dot(A,axis);
return glm::vec2(min, max);