Collision Callbacks and Triggers
From Physics Simulation Wiki
Contents |
Contact Information
The best way to determine if collisions happened, is to iterate over all contact manifolds. This should be done during a simulation tick (substep) callback, because contacts might be added and removed during several substeps of a single stepSimulation call.
A contact manifold is a cache that contains all contact points between pairs of collision objects. A good way is to iterate over all pairs of objects in the entire collision/dynamics world:
int numManifolds = world->getDispatcher()->getNumManifolds();
for (i=0;i<numManifolds;i++)
{
btPersistentManifold* contactManifold = collisionWorld->getDispatcher()->getManifoldByIndexInternal(i);
btCollisionObject* obA = static_cast<btCollisionObject*>(contactManifold->getBody0());
btCollisionObject* obB = static_cast<btCollisionObject*>(contactManifold->getBody1());
int numContacts = contactManifold->getNumContacts();
for (int j=0;j<numContacts;j++)
{
btManifoldPoint& pt = contactManifold->getContactPoint(j);
if (pt.getDistance()<0.f)
{
const btVector3& ptA = pt.getPositionWorldOnA();
const btVector3& ptB = pt.getPositionWorldOnB();
const btVector3& normalOnB = pt.m_normalWorldOnB;
}
}
}
See Bullet/Demos/CollisionInterfaceDemo for a sample implementation.
btGhostObject
A more efficient way is to iterate only over the pairs of objects that you are interested in. This can be done using a btGhostObject. A btGhostObject keeps track of its own overlapping pairs:
btManifoldArray manifoldArray;
btBroadphasePairArray& pairArray = ghostObject->getOverlappingPairCache()->getOverlappingPairArray();
int numPairs = pairArray.size();
for (int i=0;i<numPairs;i++)
{
manifoldArray.clear();
const btBroadphasePair& pair = pairArray[i];
//unless we manually perform collision detection on this pair, the contacts are in the dynamics world paircache:
btBroadphasePair* collisionPair = dynamicsWorld->getPairCache()->findPair(pair.m_pProxy0,pair.m_pProxy1);
if (!collisionPair)
continue;
if (collisionPair->m_algorithm)
collisionPair->m_algorithm->getAllContactManifolds(manifoldArray);
for (int j=0;j<manifoldArray.size();j++)
{
btPersistentManifold* manifold = manifoldArray[j];
btScalar directionSign = manifold->getBody0() == m_ghostObject ? btScalar(-1.0) : btScalar(1.0);
for (int p=0;p<manifold->getNumContacts();p++)
{
const btManifoldPoint&pt = manifold->getContactPoint(p);
if (pt.getDistance()<0.f)
{
const btVector3& ptA = pt.getPositionWorldOnA();
const btVector3& ptB = pt.getPositionWorldOnB();
const btVector3& normalOnB = pt.m_normalWorldOnB;
/// work here
}
}
}
}
See Bullet\Demos\CharacterDemo\CharacterDemo.cpp and src\BulletDynamics\Character\btKinematicCharacterController.cpp (btKinematicCharacterController::recoverFromPenetration) for a sample implementation.
Contact Callbacks
Be careful when using contact callbacks. They might be called too frequent for your purpose. Bullet supports custom callbacks at various points in the collision system. The callbacks themselves are very simply implemented as global variables that you set to point at appropriate functions. Before you can expect them to be called you must set an appropriate flag in your rigid body:
mBody->setCollisionFlags(mBody->getCollisionFlags() |
btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);
There are three collision callbacks:
gContactAddedCallback
This is called whenever a contact is added. From here, you can modify some properties [eg friction] of the contact point
typedef bool (*ContactAddedCallback)(
btManifoldPoint& cp,
const btCollisionObject* colObj0,
int partId0,
int index0,
const btCollisionObject* colObj1,
int partId1,
int index1);
If your function returns false, then Bullet will assume that you did not modify the contact point properties at all.
gContactProcessedCallback
This is called immediately after the collision has been actually processed
typedef bool (*ContactProcessedCallback)(
btManifoldPoint& cp,
void* body0,void* body1);
gContactDestroyedCallback
This is called immediately after the contact point is destroyed.
typedef bool (*ContactDestroyedCallback)(
void* userPersistentData);
Trigger
Collision objects with a callback still have collision response with dynamic rigid bodies. In order to use collision objects as trigger, you have to disable the collision response.
mBody->setCollisionFlags(mBody->getCollisionFlags() |
btCollisionObject::CF_NO_CONTACT_RESPONSE));
