Hi All,
I've been trying to extend Box2D Lite to 3D OBB. I've managed to detect collision between OBBs and to do contact generation. I'm not sure about the edge-edge though case and at first simulation works fine but later they sink through the ground. It worked fine with AABB. I can't seem to find the issue. Can anyone help me?
This is my arbiter code.
int Arbiter::OBBOBBIntersection(Contact *c, RigidBody *A, RigidBody *B)
{
XMVECTOR mtvFace = XMVectorSet(0, 0, 0, 0);
float mtdFace2 = -FLT_MAX;
int NumContacts = 0;
for(int i=0; i<A->GetOBB()->SupportFaceCount(); i++)
{
if(!TestAxis(A->GetOBB()->SupportFaceDirection(i), mtvFace, mtdFace2))
return 0;
}
for(int i=0; i<B->GetOBB()->SupportFaceCount(); i++)
{
if(!TestAxis(B->GetOBB()->SupportFaceDirection(i), mtvFace, mtdFace2))
return 0;
}
XMVECTOR mtvEdge = XMVectorSet(0, 0, 0, 0);
float mtdEdge2 = -FLT_MAX;
int edgeMin = 10;
for (int i=0; i<A->GetOBB()->SupportEdgeCount(); ++i)
{
XMVECTOR iVec = A->GetOBB()->SupportFaceDirection(i);
for (int j=0; j<B->GetOBB()->SupportEdgeCount(); ++j)
{
XMVECTOR jVec = B->GetOBB()->SupportFaceDirection(j);
XMVECTOR crossedVec = XMVector3Normalize(XMVector3Cross(iVec, jVec));
if(!TestAxis(crossedVec, mtvEdge, mtdEdge2))
return 0;
else
edgeMin = 3*i+j;
}
}
// no intersections were ever tested.
if(mtdFace2 < 0 && mtdEdge2 < 0)
return 0;
float mtdFace = sqrt(mtdFace2);
float mtdEdge = sqrt(mtdEdge2);
if (mtdEdge * .95 > mtdFace + .01)
{ //Edge Collision
XMVECTOR CollisionNormal = mtvEdge;
contacts->normal = CollisionNormal;
int axisEdge1 = edgeMin / 3;
int axisEdge2 = edgeMin % 3;
XMVECTOR edge1[2];
XMVECTOR edge2[2];
getEdge(A->GetOrientation(), *A->GetOBB(), -contacts->normal, edge1, axisEdge1, contacts[0].contactID.inI);
getEdge(B->GetOrientation(), *B->GetOBB(), contacts->normal, edge2, axisEdge2, contacts[0].contactID.inR);
contacts[0].position = computeContactPointEdges(edge1, edge2);
contacts[0].separation = mtdEdge;
contacts[0].contactID.key = 2;
//contacts->normal *= -1;
NumContacts = 1;
}
else
{ // Face Colision
XMVECTOR CollisionNormal = XMVector3Normalize(-mtvFace);
XMFLOAT3 b_vertices[] = { XMFLOAT3(B->GetOBB()->GetCenter().x-B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y-B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z-B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x-B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y+B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z-B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x+B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y-B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z-B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x+B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y+B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z-B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x+B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y-B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z+B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x+B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y+B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z+B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x-B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y-B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z+B->GetOBB()->GetRadius().z),
XMFLOAT3(B->GetOBB()->GetCenter().x-B->GetOBB()->GetRadius().x, B->GetOBB()->GetCenter().y+B->GetOBB()->GetRadius().y, B->GetOBB()->GetCenter().z+B->GetOBB()->GetRadius().z)
};
std::vector<XMFLOAT3> IncidentFace;
if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == -1.0f)
{
IncidentFace.push_back(b_vertices[2]);
IncidentFace.push_back(b_vertices[3]);
IncidentFace.push_back(b_vertices[1]);
IncidentFace.push_back(b_vertices[0]);
}
else
if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 1.0f)
{
IncidentFace.push_back(b_vertices[6]);
IncidentFace.push_back(b_vertices[7]);
IncidentFace.push_back(b_vertices[5]);
IncidentFace.push_back(b_vertices[4]);
}
else
if (CollisionNormal.m128_f32[0] == 1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f)
{
IncidentFace.push_back(b_vertices[4]);
IncidentFace.push_back(b_vertices[5]);
IncidentFace.push_back(b_vertices[3]);
IncidentFace.push_back(b_vertices[2]);
}
else
if (CollisionNormal.m128_f32[0] == -1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f)
{
IncidentFace.push_back(b_vertices[0]);
IncidentFace.push_back(b_vertices[1]);
IncidentFace.push_back(b_vertices[7]);
IncidentFace.push_back(b_vertices[6]);
}
else
if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 1.0f && CollisionNormal.m128_f32[2] == 0.0f)
{
IncidentFace.push_back(b_vertices[3]);
IncidentFace.push_back(b_vertices[5]);
IncidentFace.push_back(b_vertices[7]);
IncidentFace.push_back(b_vertices[1]);
}
else
if (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == -1.0f && CollisionNormal.m128_f32[2] == 0.0f)
{
IncidentFace.push_back(b_vertices[4]);
IncidentFace.push_back(b_vertices[2]);
IncidentFace.push_back(b_vertices[0]);
IncidentFace.push_back(b_vertices[6]);
}
std::vector<Plane> ReferenceFace;
if ((CollisionNormal.m128_f32[0] == 1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f) || (CollisionNormal.m128_f32[0] == -1.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 0.0f))
{
ReferenceFace.push_back(Plane(XMFLOAT3(0,0,-1), -(A->GetOBB()->GetCenter().z-A->GetOBB()->GetRadius().z)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,1,0), (A->GetOBB()->GetCenter().y+A->GetOBB()->GetRadius().y)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,0,1), (A->GetOBB()->GetCenter().z+A->GetOBB()->GetRadius().z)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,-1,0), -(A->GetOBB()->GetCenter().y-A->GetOBB()->GetRadius().y)));
}
else
if ((CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 1.0f && CollisionNormal.m128_f32[2] == 0.0f) || (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == -1.0f && CollisionNormal.m128_f32[2] == 0.0f))
{
ReferenceFace.push_back(Plane(XMFLOAT3(1,0,0), (A->GetOBB()->GetCenter().x+A->GetOBB()->GetRadius().x)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,0,1), (A->GetOBB()->GetCenter().z+A->GetOBB()->GetRadius().z)));
ReferenceFace.push_back(Plane(XMFLOAT3(-1,0,0), -(A->GetOBB()->GetCenter().x-A->GetOBB()->GetRadius().x)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,0,-1), -(A->GetOBB()->GetCenter().z-A->GetOBB()->GetRadius().z)));
}
else
if ((CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == 1.0f) || (CollisionNormal.m128_f32[0] == 0.0f && CollisionNormal.m128_f32[1] == 0.0f && CollisionNormal.m128_f32[2] == -1.0f))
{
ReferenceFace.push_back(Plane(XMFLOAT3(1,0,0), (A->GetOBB()->GetCenter().x+A->GetOBB()->GetRadius().x)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,1,0), (A->GetOBB()->GetCenter().y+A->GetOBB()->GetRadius().y)));
ReferenceFace.push_back(Plane(XMFLOAT3(-1,0,0), -(A->GetOBB()->GetCenter().x-A->GetOBB()->GetRadius().x)));
ReferenceFace.push_back(Plane(XMFLOAT3(0,-1,0), -(A->GetOBB()->GetCenter().y-A->GetOBB()->GetRadius().y)));
}
if (!IncidentFace.empty() && !ReferenceFace.empty())
{
vector<XMFLOAT3> ContactPoints;
ContactPoints = SHClipPolygon(IncidentFace, ReferenceFace[0]);
if (!ContactPoints.empty())
{
NumContacts++;//1
ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[1]);
if (!ContactPoints.empty())
{
NumContacts++;//2
ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[2]);
if (!ContactPoints.empty())
{
NumContacts++;//3
ContactPoints = SHClipPolygon(ContactPoints, ReferenceFace[3]);
if (!ContactPoints.empty())
NumContacts++;//4
}
}
}
IncidentFace.clear();
vector<Plane>().swap(ReferenceFace);
if (ContactPoints.empty())
return 0;
for (int i=0;i<4;i++)
{
c[i].normal = CollisionNormal;
XMVECTOR TMP = XMLoadFloat3(&ContactPoints[i]);
TMP -= CollisionNormal*mtdFace;
XMStoreFloat3(&ContactPoints[i], TMP);
c[i].position = XMLoadFloat3(&ContactPoints[i]);
c[i].separation = mtdFace;
c[i].contactID.key = 1;
}
}
}
return NumContacts;
}
XMVECTOR Arbiter::computeContactPointEdges(XMVECTOR e1[2], XMVECTOR e2[2]){
XMVECTOR d1 = e1[1] - e1[0];
XMVECTOR d2 = e2[1] - e2[0];
XMVECTOR r = e1[0] - e2[0];
float a = XMVector3Dot( d1, d1 ).m128_f32[0];
float e = XMVector3Dot( d2, d2 ).m128_f32[0];
float f = XMVector3Dot( d2, r ).m128_f32[0];
float c = XMVector3Dot( d1, r ).m128_f32[0];
float b = XMVector3Dot( d1, d2 ).m128_f32[0];
float denom = a * e - b * b;
float TA = (b * f - c * e) / denom;
float TB = (b * TA + f) / e;
return ((e1[0] + d1 * TA) + (e2[0] + d2 * TB))*.5f;
}
// Computes the edge involved in a collision given an axis and collision normal by
// Brute forcing through the 4 possible edges to find the best support edge
void Arbiter::getEdge(XMMATRIX orientation, OBB obb, XMVECTOR normal, XMVECTOR outPoints[2], int axisEdge, int &e)
{
normal = XMVector4Transform(normal, XMMatrixInverse(NULL, orientation));
if (axisEdge == 0){
XMVECTOR possiblePoints[] = {
XMVectorSet(obb.mExtents.x, obb.mExtents.y, obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, obb.mExtents.y, -obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, -obb.mExtents.y, -obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, -obb.mExtents.y, obb.mExtents.z, 1.0f)
};
float extremeEdge = -FLT_MAX;
int extremeVert = 5;
for (int x = 0; x < 4; x++){
float temp = XMVector3Dot(possiblePoints[x], normal).m128_f32[0];
if (temp > extremeEdge){
extremeEdge = temp;
extremeVert = x;
}
}
outPoints[0] = possiblePoints[extremeVert];
outPoints[1] = possiblePoints[extremeVert];
outPoints[1].m128_f32[0] *= -1;
e = extremeVert;
}
else if (axisEdge == 1){
XMVECTOR possiblePoints[] = {
XMVectorSet(-obb.mExtents.x, obb.mExtents.y, obb.mExtents.z, 1.0f),
XMVectorSet(-obb.mExtents.x, obb.mExtents.y, -obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, obb.mExtents.y, -obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, obb.mExtents.y, obb.mExtents.z, 1.0f)
};
float extremeEdge = -FLT_MAX;
int extremeVert = 5;
for (int x = 0; x < 4; x++){
float temp = XMVector3Dot(possiblePoints[x], normal).m128_f32[0];
if (temp > extremeEdge){
extremeEdge = temp;
extremeVert = x;
}
}
outPoints[0] = possiblePoints[extremeVert];
outPoints[1] = possiblePoints[extremeVert];
outPoints[1].m128_f32[1] *= -1;
e = extremeVert;
}
else{
XMVECTOR possiblePoints[] = {
XMVectorSet(obb.mExtents.x, obb.mExtents.y, obb.mExtents.z, 1.0f),
XMVectorSet(obb.mExtents.x, -obb.mExtents.y, obb.mExtents.z, 1.0f),
XMVectorSet(-obb.mExtents.x, -obb.mExtents.y, obb.mExtents.z, 1.0f),
XMVectorSet(-obb.mExtents.x, obb.mExtents.y, obb.mExtents.z, 1.0f)
};
float extremeEdge = -FLT_MAX;
int extremeVert = 5;
for (int x = 0; x < 4; x++){
float temp = XMVector3Dot(possiblePoints[x], normal).m128_f32[0];
if (temp > extremeEdge){
extremeEdge = temp;
extremeVert = x;
}
}
outPoints[0] = possiblePoints[extremeVert];
outPoints[1] = possiblePoints[extremeVert];
outPoints[1].m128_f32[2] *= -1;
e = extremeVert;
}
outPoints[0] = XMVector4Transform(outPoints[0], orientation);
outPoints[1] = XMVector4Transform(outPoints[1], orientation);
e += axisEdge * 3;
}
Arbiter::Arbiter()
{
}
Arbiter::Arbiter(RigidBody* b1, RigidBody* b2)
{
if (b1 < b2)
{
body1 = b1;
body2 = b2;
}
else
{
body1 = b2;
body2 = b1;
}
restituition = /*min*/( body1->GetRestitution()* body2->GetRestitution() );
// numContacts = AABBAABBIntersection(contacts, body1, body2);
numContacts = OBBOBBIntersection(contacts, body1, body2);
friction = sqrtf(body1->GetStaticFriction() * body2->GetStaticFriction());
}
void Arbiter::UpdateManifold(Contact* newContacts, int numNewContacts)
{
Contact mergedContacts[4];
for (int i = 0; i < numNewContacts; ++i)
{
Contact* cNew = newContacts + i;
int k = -1;
for (int j = 0; j < numContacts; ++j)
{
Contact* cOld = contacts + j;
if (cNew->contactID.key == cOld->contactID.key)
{
k = j;
break;
}
}
if (k > -1)
{
Contact* c = mergedContacts + i;
Contact* cOld = contacts + k;
*c = *cNew;
c->Pn = cOld->Pn;
c->Pt[0] = cOld->Pt[0];
c->Pt[1] = cOld->Pt[1];
}
else
{
mergedContacts[i] = newContacts[i];
}
}
for (int i = 0; i < numNewContacts; ++i)
contacts[i] = mergedContacts[i];
numContacts = numNewContacts;
}
void Arbiter::PreStep(float inv_dt)
{
XMVECTOR tangent[2];
for (int i = 0; i < numContacts; ++i)
{
Contact* c = contacts + i;
XMVECTOR p = c->position;
XMVECTOR padot = body1->GetVelocityAtPoint(p);
XMVECTOR pbdot = body2->GetVelocityAtPoint(p);
XMVECTOR ra = (p - body1->GetPosition());
XMVECTOR rb = (p - body2->GetPosition());
XMVECTOR n = c->normal;
float C =min(0.0f, c->separation + 0.005f);
c->bias = -inv_dt * C * .1f;
XMVECTOR dv = -padot +pbdot;
float Cdot = XMVector3Dot(dv, n).m128_f32[0];
if (Cdot < -1.0f)
{
c->bias += -restituition * Cdot;
}
float term1 = body1->GetMass()>0.0f ? (1.0f / body1->GetMass()) : 0.0f;
float term2 = body2->GetMass()>0.0f ? (1.0f / body2->GetMass()) : 0.0f;
XMVECTOR rnA = XMVector3Cross(ra, n);
XMVECTOR rnB = XMVector3Cross(rb, n);
float K = term1 + term2 + XMVector3Dot(rnA, XMVector4Transform(rnA, body1->GetIInverse())).m128_f32[0] + XMVector3Dot(rnB, XMVector4Transform(rnB, body2->GetIInverse())).m128_f32[0];
c->massNormal = K > 0.0f ? 1.0f / K : 0.0f;
tangent[0] = XMVector3Orthogonal(n);
tangent[1] = XMVector3Cross(tangent[0], n);;
/* padot = body1->GetVelocityAtPoint(p);
pbdot = body2->GetVelocityAtPoint(p);
ra = (p - body1->GetPosition());
rb = (p - body2->GetPosition());*/
XMVECTOR rt1A = XMVector3Cross(ra, tangent[0]);
XMVECTOR rt1B = XMVector3Cross(rb, tangent[0]);
XMVECTOR rt2A = XMVector3Cross(ra, tangent[1]);
XMVECTOR rt2B = XMVector3Cross(rb, tangent[1]);
float K1 = term1 + term2 + XMVector3Dot(rt1A, XMVector4Transform(rt1A, body1->GetIInverse())).m128_f32[0] + XMVector3Dot(rt1B, XMVector4Transform(rt1B, body2->GetIInverse())).m128_f32[0];
float K2 = term1 + term2 + XMVector3Dot(rt2A, XMVector4Transform(rt2A, body1->GetIInverse())).m128_f32[0] + XMVector3Dot(rt2B, XMVector4Transform(rt2B, body2->GetIInverse())).m128_f32[0];
c->massTangent[0] = K1 > 0.0f ? 1.0f / K1 : 0.0f;
c->massTangent[1] = K2 > 0.0f ? 1.0f / K2 : 0.0f;
XMVECTOR P = c->Pn * n+ c->Pt[0] * tangent[0] + c->Pt[1] * tangent[1];
body1->AddImpulse(-P);
body1->AddImpulsiveTorque(-XMVector3Cross(ra, P));
body2->AddImpulse(P);
body2->AddImpulsiveTorque(XMVector3Cross(rb, P));
}
}
float Clamp(float a, float low, float high)
{
return max(low, min(a, high));
}
void Arbiter::ApplyImpulse()
{
XMVECTOR tangent[2];
for (int i = 0; i < numContacts; ++i)
{
Contact* c = contacts + i;
XMVECTOR p = c->position;
XMVECTOR padot = body1->GetVelocityAtPoint(p);
XMVECTOR pbdot = body2->GetVelocityAtPoint(p);
XMVECTOR ra = (p - body1->GetPosition());
XMVECTOR rb = (p - body2->GetPosition());
XMVECTOR n = c->normal;
XMVECTOR dv = -padot + pbdot;
float Cdot = XMVector3Dot(dv, n).m128_f32[0];
float dPn = c->massNormal * (-Cdot + c->bias);
float Pn0 = c->Pn;
c->Pn = max(Pn0 + dPn, 0.0f);
dPn = c->Pn - Pn0;
XMVECTOR P = dPn * n;
body1->AddImpulse(-P);
body1->AddImpulsiveTorque(-XMVector3Cross(ra, P));
body2->AddImpulse(P);
body2->AddImpulsiveTorque(XMVector3Cross(rb, P));
// p = c->position;
padot = body1->GetVelocityAtPoint(p);
pbdot = body2->GetVelocityAtPoint(p);
/* n = c->normal;
ra = (p - body1->GetPosition());
rb = (p - body2->GetPosition());*/
dv = -padot +pbdot;
tangent[0] = XMVector3Orthogonal(n);
tangent[1] = XMVector3Cross(tangent[0], n);
for (int z=0;z<2;z++)
{
float vt = XMVector3Dot(dv, tangent[z]).m128_f32[0];
float dPt = c->massTangent[z] * (-vt);
// Compute friction impulse
float maxPt = friction * c->Pn;
// Clamp friction
float oldTangentImpulse = c->Pt[z];
c->Pt[z] = Clamp(oldTangentImpulse + dPt, -maxPt, maxPt);
dPt = c->Pt[z] - oldTangentImpulse;
// Apply contact impulse
XMVECTOR Pt = dPt * tangent[z];
body1->AddImpulse(-Pt);
body1->AddImpulsiveTorque(-XMVector3Cross(ra, Pt));
body2->AddImpulse(Pt);
body2->AddImpulsiveTorque(XMVector3Cross(rb, Pt));
}
}
}