Collision Filtering
From Physics Simulation Wiki
Contents |
Collision Filtering
Selective collisions
Bullet provides three easy ways to ensure that only certain objects collide with each other: masks, broadphase filter callbacks and nearcallbacks.
It is worth noting that mask-based collision selection happens a lot further up the toolchain than the callback do. In short, if masks are sufficient for your purposes, use them; they perform better and are a lot simpler to use.
Of course, don't try to shoehorn something into a mask-based selection system that clearly doesn't fit there just because performance may be a little better. caveat emptor
Filtering collisions using masks
Bullet supports bitwise masks as a way of deciding whether or not things should collide with other things, or receive collisions.
For example, in a spaceship game, you could have your spaceships ignore collisions with other spaceships [the spaceships would just fly through each other], but always collide with walls [the spaceships always bounce off walls].
Your spaceship needs a callback when it collides with a wall [for example, to produce a “plink” sound], but the walls do nothing when you collide with them so they do not need to receive callbacks.
A third type of object, “powerup”, collides with walls and spaceships. Spaceships do not receive collisions from them, since we don't want the trajectory of the spaceship changed by collecting a powerup. The powerup object modifies the spaceship from its own collision callback.
In order to do this, you need a bit mask for the walls, spaceships, and powerups:
#define BIT(x) (1<<(x))
enum collisiontypes {
COL_NOTHING = 0, //<Collide with nothing
COL_SHIP = BIT(1), //<Collide with ships
COL_WALL = BIT(2), //<Collide with walls
COL_POWERUP = BIT(3) //<Collide with powerups
}
int shipCollidesWith = COL_WALL;
int wallCollidesWith = COL_NOTHING;
int powerupCollidesWith = COL_SHIP | COL_WALL;
After setting these up, simply add your body objects to the world as normal, except as the second and third parameters pass your collision type for that body, and the collision mask.
If you are not using a recent enough version of Bullet, you may find this overload of addRigidBody does not exist. In this case, use btDynamicsWorld::addCollisionObject instead of the more usual btDynamicsWorld::addRigidBody. You may have to set the gravity on the rigid body manually in this case.
btRigidBody ship; // Set up the other ship stuff btRigidBody wall; // Set up the other wall stuff btRigidBody powerup; // Set up the other powerup stuff mWorld->addRigidBody(ship, COL_SHIP, shipCollidesWith); mWorld->addRigidBody(wall, COL_WALL, wallCollidesWith); mWorld->addRigidBody(powerup, COL_POWERUP, powerupCollidesWith);
Example: If we want to see if a powerup collides with a ship, we need to know the powerup's mask, and the ship's group. Powerup mask: 00000011 (3 in decimal - this was achieved by performing a bitwise OR between the Ship and the Wall groups) Ship group: 00000001
Before checking for a collision, bullet performs an AND between object's A mask, and object's B group. If the result is anything but 0, Bullet will perform collision response. In this example, we'll perform an AND between 00000011 and 00000001 and the result is 00000001. Since that is not 0, Bullet will consider this a positive result and continue on to collision response.
For more information about bitwise OR and AND: Wikipedia's article on Bitwise operations
Note: As of 16/03/10, Bullet uses a signed short int to store the collision groups and the collision masks. This means you can have up to 15 different groups, and 15 different masks.
Custom collision filtering
It's worth noting that if you are using masks, and they're sufficient for your needs, then you do not need a custom collision filtering.
If you have more types of objects than bits available to you in the masks above, or some collisions are enabled or disabled based on other factors, then there are several ways to register callbacks to that implements custom logic and only passes on collisions that are the ones you want:
Filtering Collisions Using a Broadphase Filter Callback
One efficient way is to register a broadphase filter callback. This callback is called at a very early stage in the collision pipeline, and prevents collision pairs from being generated.
struct YourOwnFilterCallback : public btOverlapFilterCallback
{
// return true when pairs need collision
virtual bool needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const
{
bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);
//add some additional logic here that modified 'collides'
return collides;
}
};
And then create an object of this class and register this callback using:
btOverlapFilterCallback * filterCallback = new YourOwnFilterCallback(); dynamicsWorld->getPairCache()->setOverlapFilterCallback(filterCallback);
Filtering Collisions Using a Custom NearCallback
Another callback can be registered during the narrowphase, when all pairs are generated by the broadphase. The btCollisionDispatcher::dispatchAllCollisionPairs calls this narrowphase nearcallback for each pair that passes the 'btCollisionDispatcher::needsCollision' test. You can customize this nearcallback:
void MyNearCallback(btBroadphasePair& collisionPair,
btCollisionDispatcher& dispatcher, btDispatcherInfo& dispatchInfo) {
// Do your collision logic here
// Only dispatch the Bullet collision information if you want the physics to continue
dispatcher.defaultNearCallback(collisionPair, dispatcher, dispatchInfo);
}
mDispatcher->setNearCallback(MyNearCallback);
Deriving your own class from btCollisionDispatcher
For even more fine grain control over the collision dispatch, you can derive your own class from btCollisionDispatcher and override one or more of the following methods:
virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1); virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1); virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) ;
