This document attempts to better document the Bullet physics library, much of it based direclty on the Bullet Wiki
The bullet collision detection algorithm proceeds similarly to most rigid body / physics simulation engines. This document will cover the basic Bullet collision algorithm using discrete rigid objects. Deformable (soft body) straight-forward extension. There are a number of key objects that comprise a Bullet simulation.
btDiscreteDynamicsWorld
The top level object that performs the main time stepping and connects the other pieces. Other
kinds of Bullet simulations such as soft body employ dynamics world objects that inherit from the
discrete dynamics world. Users must create the following objects and pass them to the discrete
dynamics world constructor.
btDispatcher
foo foo
blas blah
btBroadphaseInterface
btConstraintSolver
btCollisionConfiguration
blah
Most collision detection frameworks break up collision detection into two phases, broadphase and narrowphase
This is implemented by the btDbvtBroadphase in Bullet. As the name suggests, this is a dynamic AABB tree. One useful feature of this broadphase is that the structure adapts dynamically to the dimensions of the world and its contents. It is very well optimized and a very good general purpose broadphase. It handles dynamic worlds where many objects are in motion, and object addition and removal is faster than SAP.
In collision detection, a manifold defines a contact area between two objects. The set of intersection points is referred to as the contact set or contact manifold, the latter term appropriate when the intersection set is not a finite set but a continuum of points. For example, the intersection set of a box sitting on a table is the set of points on a face of the box. When the contact set is desired, I refer to this as a find-intersection query. As you would expect, in most cases a find intersection query is more expensive than a test intersection query for a given pair of objects.
Every actual, verified collision will result in an additional collision manifold. The collision
algorithms add a collision manifold to the dispatcher if a collision occurs. New contact manifolds
are always created by the btCollisionDispatcher::getNewManifold
, which all of the collision
algorithms call.
btPersistentManifold is a contact point cache, it stays persistent as long as objects are overlapping in the broadphase. Those contact points are created by the collision narrow phase. The cache can be empty, or hold 1,2,3 or 4 points. Some collision algorithms (GJK) might only add one point at a time. updates/refreshes old contact points, and throw them away if necessary (distance becomes too large) reduces the cache to 4 points, when more then 4 points are added, using following rules:
Collision in general has a number of phases. Project the object forward. Figure out what collided, then adjust and compensate for the collision.
The btCollisionObjectWrapper class ‘wraps’ a btCollisionShape, btCollisionObject, along with a worldTransform.
The btCollisionAlgorithm::processCollision
method as it’s name implies actually processs a
collision. The resoutOut
parameter is often, but not alwasy ingnored by the
caller. Basically, the btManifoldResult
objct holds onto a btPersistentManifold object. The
collision algoritm usually sets the btManifoldResult’s m_manifoldPtr to point to the persistent
manifold that’s owned by the collision algorithm.
The dispatcher, btCollisionDispatcher maintains a matrix of collision algoriths,
m_doubleDispatchContactPoints. The collision shape type is used to index this matrix and get the
collision algorithm for the appropriate pair in the btCollisionDispatcher::findAlgorithm
method. This method creates a new collision algorithm for each potential collision. Each new
algorithm typically creates a new collision manifold from the dispatcher in the algorithm’s
constructor.
m_dynamicsWorld->stepSimulation(deltaTime);
int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep)
The interesting stuff
for (int i=0;i<clampedSimulationSteps;i++)
{
internalSingleStepSimulation(fixedTimeStep);
synchronizeMotionStates();
}
The list of contact manifolds is created in, and owned by the dispatcher. The dispatcher is created before the world, and handed to the world in it’s ctor.
All of the collision algorithms call btDispatcher::releaseManifold
in their destructors to
tell the dispatcher to remove this manifold.
The AABB tree gets updated in the btCollisionWorld::computeOverlappingPairs
method. This
method in turn calls btDbvtBroadphase::calculateOverlappingPairs
, which in turn calls
btDbvtBroadphase::collide
.
New broadphase collision pairs typically get created by the addOverlappingPair
The collision algorithm attached to a collision pair gets destroyed via a call to
btHashedOverlappingPairCache::cleanOverlappingPair
, and the collision pair gets destroyed
via a call to btHashedOverlappingPairCache::removeOverlappingPair
. This method gets called
when a pair of AABBs formerly in contact no longer contact each other. Hence, that’s why this method
gets a dispatcher as it’s last argument, as the dispatcher is needed to destroy the algorithm
attached to the collision pair.
The call sequence for updating the AAPP tree, and removing pairs that are no longer colliding is:
btCollisionWorld::performDiscreteCollisionDetection
btCollisionWorld::computeOverlappingPairs
btDbvtBroadphase::calculateOverlappingPairs
btDbvtBroadphase::collide
btHashedOverlappingPairCache::removeOverlappingPair
The btDiscreteDynamicsWorld::internalSingleStepSimulation
is the main time step method. Each
time step consists of the following phases:
void btDiscreteDynamicsWorld::internalSingleStepSimulation(btScalar timeStep)
{
if(0 != m_internalPreTickCallback) {
(*m_internalPreTickCallback)(this, timeStep);
}
///apply gravity, predict motion
predictUnconstraintMotion(timeStep);
// just grab the dispatch info
btDispatcherInfo& dispatchInfo = getDispatchInfo();
dispatchInfo.m_timeStep = timeStep;
dispatchInfo.m_stepCount = 0;
dispatchInfo.m_debugDraw = getDebugDrawer();
createPredictiveContacts(timeStep);
///perform collision detection
performDiscreteCollisionDetection();
calculateSimulationIslands();
getSolverInfo().m_timeStep = timeStep;
///solve contact and other joint constraints
solveConstraints(getSolverInfo());
///CallbackTriggers();
///integrate transforms
integrateTransforms(timeStep);
///update vehicle simulation
updateActions(timeStep);
updateActivationState( timeStep );
if(0 != m_internalTickCallback) {
( * m_internalTickCallback) (this, timeStep);
}
}
void btDiscreteDynamicsWorld::createPredictiveContacts(btScalar timeStep)
{
releasePredictiveContacts();
if (m_nonStaticRigidBodies.size() > 0)
{
createPredictiveContactsInternal( &m_nonStaticRigidBodies[ 0 ], m_nonStaticRigidBodies.size(), timeStep );
}
}
Bullet makes extensive use of callback function, so it can be rather dificult to manually trace
through an execution pass. This section follows a discrete collision that occured between a pair of
rigid boxes. This section follows a call starting at
btDiscreteDynamicsWorld::internalSingleStepSimulation
and proceeding through the following
steps.
btDiscreteDynamicsWorld::internalSingleStepSimulation
btCollisionWorld::performDiscreteCollisionDetection
btCollisionDispatcher::dispatchAllCollisionPairs
btHashedOverlappingPairCache::processAllOverlappingPairs
btCollisionPairCallback::processOverlap
btCollisionDispatcher::defaultNearCallback
btBoxBoxCollisionAlgorithm::processCollision
The collision detection phase occurs after the unconstrained motion has been calculated. The
btCollisionWorld::performDiscreteCollisionDetection
first updates the dynamic AABB bounding
volume tree, computes the overlapping collision pairs, then performs the dispatch collision pairs
step.
void btCollisionWorld::performDiscreteCollisionDetection()
{
btDispatcherInfo& dispatchInfo = getDispatchInfo();
updateAabbs();
computeOverlappingPairs();
btDispatcher* dispatcher = getDispatcher();
dispatcher->dispatchAllCollisionPairs(
m_broadphasePairCache->getOverlappingPairCache(),dispatchInfo,m_dispatcher1);
}
void btCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,
const btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher)
{
btCollisionPairCallback collisionCallback(dispatchInfo,this);
pairCache->processAllOverlappingPairs(&collisionCallback,dispatcher);
}
void btHashedOverlappingPairCache::processAllOverlappingPairs(
btOverlapCallback* callback,btDispatcher* dispatcher)
{
int i;
for (i=0;i<m_overlappingPairArray.size();)
{
btBroadphasePair* pair = &m_overlappingPairArray[i];
if (callback->processOverlap(*pair))
{
removeOverlappingPair(pair->m_pProxy0,pair->m_pProxy1,dispatcher);
gOverlappingPairs--;
} else
{
i++;
}
}
}
The btOverlapCallback defaults to btCollisionPairCallback, who’s processOverlap method looks like
virtual bool processOverlap(btBroadphasePair& pair)
{
(*m_dispatcher->getNearCallback())(pair,*m_dispatcher,m_dispatchInfo);
return false;
}
This calls the default btCollisionDispatcher::defaultNearCallback callback. Looks like every pair in btHashedOverlappingPairCache::m_overlappingPairArray gets processed by the collision callback.
The btCollisionDispatcher::dispatchAllCollisionPairs calls this narrowphase nearcallback for each pair that passes the ‘btCollisionDispatcher::needsCollision’ test. You can customize this nearcallback
First checks if objects really really can collide. If so, uses the dispatcher’s findAlgorithm as a double dispatch (this is the matrix of collision algorithms) to find the correct algorithm for the colliding pair.
//by default, Bullet will use this near callback
void btCollisionDispatcher::defaultNearCallback(btBroadphasePair& collisionPair,
btCollisionDispatcher& dispatcher, const btDispatcherInfo& dispatchInfo)
{
btCollisionObject* colObj0 = (btCollisionObject*)collisionPair.m_pProxy0->m_clientObject;
btCollisionObject* colObj1 = (btCollisionObject*)collisionPair.m_pProxy1->m_clientObject;
if (dispatcher.needsCollision(colObj0,colObj1))
{
btCollisionObjectWrapper obj0Wrap(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform(),-1,-1);
btCollisionObjectWrapper obj1Wrap(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform(),-1,-1);
//dispatcher will keep algorithms persistent in the collision pair
if (!collisionPair.m_algorithm)
{
collisionPair.m_algorithm = dispatcher.findAlgorithm(&obj0Wrap,&obj1Wrap,0, BT_CONTACT_POINT_ALGORITHMS);
}
if (collisionPair.m_algorithm)
{
btManifoldResult contactPointResult(&obj0Wrap,&obj1Wrap);
if (dispatchInfo.m_dispatchFunc == btDispatcherInfo::DISPATCH_DISCRETE)
{
//discrete collision detection query
collisionPair.m_algorithm->processCollision(&obj0Wrap,&obj1Wrap,dispatchInfo,&contactPointResult);
} else
{
//continuous collision detection query, time of impact (toi)
btScalar toi = collisionPair.m_algorithm->calculateTimeOfImpact(colObj0,colObj1,dispatchInfo,&contactPointResult);
if (dispatchInfo.m_timeOfImpact > toi)
dispatchInfo.m_timeOfImpact = toi;
}
}
}
}
Provides a way to check if complex collision objects really do collide with other objects, checks to see if these objects are kinematic or static. If so, they don’t need to collide, and don’t get processed above
bool btCollisionDispatcher::needsCollision(const btCollisionObject* body0,const
btCollisionObject* body1)
All of the collision algorithm concrete implementations contain a btPersistentManifold* m_manifoldPtr; pointer. This is a pointer to a persistant manifold created and owned by the dispatcher. The algorithm’s job is to process collisions, determine the collision points, and add them to this persistant manifold.
btCollisionAlgorithm is an collision interface that is compatible with the Broadphase and btDispatcher. It is persistent over frames
The collision algorithm shares a pointer to a persistent manifold, which is created by the collision dispatcher’s getNewManifold method. The dispatcher retains a pointer to this manifold. All of the collision algorithms call this method to get a pointer to a new shared manifold.
btPersistentManifold* btCollisionDispatcher::getNewManifold(const btCollisionObject*
body0,const btCollisionObject* body1);
The best way to determine if collisions happened between existing objects in the world, is to iterate over all contact manifolds. This should be done during a [http://www.bulletphysics.com/mediawiki-1.5.8/index.php?title=Stepping_The_World 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:
//Assume world->stepSimulation or world->performDiscreteCollisionDetection has been called
int numManifolds = world->getDispatcher()->getNumManifolds();
for (int i = 0; i < numManifolds; i++)
{
btPersistentManifold* contactManifold = world->getDispatcher()->getManifoldByIndexInternal(i);
const btCollisionObject* obA = contactManifold->getBody0();
const btCollisionObject* obB = 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.
This type of collision object will keep track of its own overlapping pairs. This is much more efficient than iterating through everything. For this example, we’ll use a btPairCachingGhostObject since we want easy access to the pair cache of the ghost object. A regular btGhostObject can be used for things like triggers where the details of the overlap don’t matter as much.
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];
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];
bool isFirstBody = manifold->getBody0() == ghostObject;
btScalar direction = isFirstBody ? 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;
// handle collisions here
}
}
}
}
For the ghost object to work correctly, we need to add a callback to our world.
btDiscreteDynamicsWorld* dynamicsWorld = CreateDiscreteDynamicsWorld();
dynamicsWorld->getPairCache()->setInternalGhostPairCallback(
new btGhostPairCallback());
Implementations of this concept can be found in the ‘BulletDemosCharacterDemo’ and the btKinematicCharacterController (in the recoverFromPenetration method).
Bullet 2.76 onwards let you perform an instant query on the world (btCollisionWorld or btDiscreteDynamicsWorld) using the contactTest query. The contactTest query will peform a collision test against all overlapping objects in the world, and produces the results using a callback. The query object doesn’t need to be part of the world. In order for an efficient query on large worlds, it is important that the broadphase aabbTest is accelerated, for example using the btDbvtBroadphase or btAxisSweep3 broadphase.
An advantage of this method is that you can perform collision tests at a reduced temporal resolution if you do not need collision tests at every physics tic. It is also convenient to use with a pre-existing object in the world, whereas btGhostObject would require synchronizing with the target object. However, a downside is that collision detection is being duplicated for the target object (if it already exists in the world), so frequent or widespread collision tests may become less efficient than iterating over previously generated collision pairs.
struct ContactSensorCallback : public btCollisionWorld::ContactResultCallback {
//! Constructor, pass whatever context you want to have available when processing contacts
/*! You may also want to set m_collisionFilterGroup and m_collisionFilterMask
* (supplied by the superclass) for needsCollision() */
ContactSensorCallback(btRigidBody& tgtBody , YourContext& context /*, ... */)
: btCollisionWorld::ContactResultCallback(), body(tgtBody), ctxt(context) { }
btRigidBody& body; //!< The body the sensor is monitoring
YourContext& ctxt; //!< External information for contact processing
//! If you don't want to consider collisions where the bodies are joined by a constraint, override needsCollision:
/*! However, if you use a btCollisionObject for #body instead of a btRigidBody,
* then this is unnecessary—checkCollideWithOverride isn't available */
virtual bool needsCollision(btBroadphaseProxy* proxy) const {
// superclass will check m_collisionFilterGroup and m_collisionFilterMask
if(!btCollisionWorld::ContactResultCallback::needsCollision(proxy))
return false;
// if passed filters, may also want to avoid contacts between constraints
return body.checkCollideWithOverride(static_cast<btCollisionObject*>(proxy->m_clientObject));
}
//! Called with each contact for your own processing (e.g. test if contacts fall in within sensor parameters)
virtual btScalar addSingleResult(btManifoldPoint& cp,
const btCollisionObjectWrapper* colObj0,int partId0,int index0,
const btCollisionObjectWrapper* colObj1,int partId1,int index1) {
btVector3 pt; // will be set to point of collision relative to body
if(colObj0->m_collisionObject==&body) {
pt = cp.m_localPointA;
} else {
assert(colObj1->m_collisionObject==&body && "body does not match either collision object");
pt = cp.m_localPointB;
}
// do stuff with the collision point
return 0; // There was a planned purpose for the return value of addSingleResult, but it is not used so you can ignore it.
}
};
// USAGE:
btRigidBody* tgtBody /* = ... */;
YourContext foo;
ContactSensorCallback callback(*tgtBody, foo);
world->contactTest(tgtBody,callback);
Bullet 2.76 onwards provides the contactPairTest to perform collision detection between two specific collision objects only. Contact results are passed on using the provided callback. They don’t need to be inserted in the world. See btCollisionWorld::contactPairTest in Bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h for implementation details.
Be careful when using contact callbacks: They may be called too frequently for your purposes. 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:
There are three collision callbacks:
gContactAddedCallback
This is called whenever a contact is added (note that the same contact may be added multiple times before it is processed). From here, you can modify some properties (e.g. friction) of the contact point. ‘’‘Note:’‘’ gContactAddedCallback does not appear to work when using multithreaded solvers.
typedef bool (*ContactAddedCallback)(
btManifoldPoint& cp,
const btCollisionObject* colObj0,
int partId0,
int index0,
const btCollisionObject* colObj1,
int partId1,
int index1);
As of the current implementation of Bullet (2.82), the return value of this function is ignored.
==gContactProcessedCallback==
This is called immediately after the collision has actually been processed.
typedef bool (*ContactProcessedCallback)(
btManifoldPoint& cp,
void* body0,void* body1);
Note that ‘’body0’’ and ‘’body1’’ are pointers to the same btCollisionObjects as ‘’colObj0’’ and ‘’colObj1’’ in the ‘’gContactAddedCallback’’ (exactly why this function prototype is declared differently is unclear).
As of the current implementation of Bullet (2.82), the return value of this function is ignored.
gContactDestroyedCallback
This is called immediately after the contact point is destroyed.
typedef bool (*ContactDestroyedCallback)(
void* userPersistentData);
The passed userPersistentData
argument is the value of the ‘’m_userPersistentData’’ member of
the btManifoldPoint
which has been destroyed (this can be set in ‘’gContactAddedCallback’’ or
‘’gContactProcessedCallback’‘).
‘’‘Note that gContactDestroyedCallback will not be called for any contact point unless ‘’cp.m_userPersistentData’’ is set!’‘’ You must set this (to some value other than NULL) in a prior contact added/processed callback in order to receive a destroyed callback.
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));
btKinematicCharacterController
The stock btKinematicCharacterController
doesn’t appear to properly behave with ghost
objects that have CF_NO_CONTACT_RESPONSE
set. It seems to ignore that flag and act as
if the objects don’t have that flag set.
The solution is to create a custom character controller based on the btKinematicCharacterController class, and make a few changes, as detailed in this forum post:
http://bulletphysics.org/Bullet/phpBB3/viewtopic.php?f=9&t=5684
Add this IF into the method ‘’‘btKinematicCharacterController::recoverFromPenetration’‘’, under the <code>btBroadphasePair* collisionPair = &m_ghostObj...</code> line:
//for trigger filtering
if (!static_cast<btCollisionObject*>(collisionPair->m_pProxy0->m_clientObject)->hasContactResponse() ||
!static_cast<btCollisionObject*>(collisionPair->m_pProxy1->m_clientObject)->hasContactResponse())
continue;
And add this IF to the beginning of ‘’‘btKinematicClosestNotMeConvexResultCallback::addSingleResult’‘’:
//for trigger filtering
if (!convexResult.m_hitCollisionObject->hasContactResponse())
return btScalar(1.0);
This section describes the key classes involved with Bullet collision detection.
btBroadphasePair
The btBroadphasePair
object represents a pair of pair of AABB-overlapping collision objects. The
btBroadphasePair
exists as long as this pair of collision objects have operlappng AABB volumes.
A btDispatcher
can search a btCollisionAlgorithm
that performs exact/narrowphase collision
detection on the actual collision shapes.
The btBroadphasePair
holds a reference to a collision algorithm. The collision algorithm
lifetime is the same as the broadphase pair. The btBroadphasePair
When the AABB’s of a pair of collision objects overlap, the btHashedOverlappingPairCache::addOverlappingPair
, in a call to
btHashedOverlappingPairCache::internalAddPair
creates a new btBroadphasePair
. This
new object is added to, and managed by a btOverlappingPairCache
. The btBroadphasePair
‘s m_algorithm
pointer is initially NULL.
The collision pair’s algorithm is created in the
dispatcher’s findAlgorithm
method. The basic idea is that the collision algorithm exists as
long as a broadphase collision pair exists, the collision algorithm exists as long as a pair of
objects remain in AABB contact.
A simplified btBroadphasePair
struct btBroadphasePair {
btBroadphaseProxy* m_pProxy0;
btBroadphaseProxy* m_pProxy1;
mutable btCollisionAlgorithm* m_algorithm;
//don't use this data, it will be removed in future version.
union { void* m_internalInfo1; int m_internalTmpValue;};
}
some more stuff
btCollisionAlgorithm
class btCollisionAlgorithm {
protected:
btDispatcher* m_dispatcher;
public:
btCollisionAlgorithm() {};
btCollisionAlgorithm(const btCollisionAlgorithmConstructionInfo& ci);
virtual ~btCollisionAlgorithm() {};
virtual void processCollision (const btCollisionObjectWrapper* body0Wrap,
const btCollisionObjectWrapper* body1Wrap,
const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) = 0;
virtual btScalar calculateTimeOfImpact(btCollisionObject* body0,
btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,
btManifoldResult* resultOut) = 0;
virtual void getAllContactManifolds(btManifoldArray& manifoldArray) = 0;
};
btPersistentManifold
class btPersistentManifold : public btTypedObject {
btManifoldPoint m_pointCache[MANIFOLD_CACHE_SIZE];
/// this two body pointers can point to the physics rigidbody class.
const btCollisionObject* m_body0;
const btCollisionObject* m_body1;
int m_cachedPoints;
btScalar m_contactBreakingThreshold;
btScalar m_contactProcessingThreshold;
public:
int m_companionIdA;
int m_companionIdB;
int m_index1a;
btPersistentManifold();
btPersistentManifold(const btCollisionObject* body0, const btCollisionObject*
body1,int , btScalar contactBreakingThreshold,
btScalar contactProcessingThreshold);
const btCollisionObject* getBody0() const { return m_body0;}
const btCollisionObject* getBody1() const { return m_body1;}
void setBodies(const btCollisionObject* body0,const btCollisionObject* body1);
void clearUserCache(btManifoldPoint& pt);
int getNumContacts();
/// the setNumContacts API is usually not used, except when you gather/fill all contacts manually
void setNumContacts(int cachedPoints);
const btManifoldPoint& getContactPoint(int index) const;
btManifoldPoint& getContactPoint(int index);
///@todo: get this margin from the current physics / collision environment
btScalar getContactBreakingThreshold() const;
btScalar getContactProcessingThreshold() const;
void setContactBreakingThreshold(btScalar contactBreakingThreshold);
void setContactProcessingThreshold(btScalar contactProcessingThreshold);
int getCacheEntry(const btManifoldPoint& newPoint) const;
int addManifoldPoint( const btManifoldPoint& newPoint, bool isPredictive=false);
void removeContactPoint (int index);
void replaceContactPoint(const btManifoldPoint& newPoint,int insertIndex);
bool validContactDistance(const btManifoldPoint& pt) const;
/// calculated new worldspace coordinates and depth, and reject points that exceed the collision margin
void refreshContactPoints( const btTransform& trA,const btTransform& trB);
void clearManifold();
}
btDiscreteDynamicsWorld
The btDiscreteDynamicsWorld
is the main way to create discrete rigid body simulations. It inherits
from btDynamicsWorld
, which in turn inherits from the base btCollisionWorld
. The
btDiscreteDynamicsWorld determines the response to the identified collisions.
class btDiscreteDynamicsWorld : public btDynamicsWorld {
btAlignedObjectArray<btTypedConstraint*> m_sortedConstraints;
InplaceSolverIslandCallback* m_solverIslandCallback;
btConstraintSolver* m_constraintSolver;
btSimulationIslandManager* m_islandManager;
btAlignedObjectArray<btTypedConstraint*> m_constraints;
btAlignedObjectArray<btRigidBody*> m_nonStaticRigidBodies;
btVector3 m_gravity;
//for variable timesteps
btScalar m_localTime;
btScalar m_fixedTimeStep;
//for variable timesteps
bool m_ownsIslandManager;
bool m_ownsConstraintSolver;
bool m_synchronizeAllMotionStates;
bool m_applySpeculativeContactRestitution;
btAlignedObjectArray<btActionInterface*> m_actions;
int m_profileTimings;
bool m_latencyMotionStateInterpolation;
btAlignedObjectArray<btPersistentManifold*> m_predictiveManifolds;
// used to synchronize threads creating predictive contacts
btSpinMutex m_predictiveManifoldsMutex;
}
btDynamicsWorld
The btDynamicsWorld is the interface class for several dynamics implementation, basic, discrete, parallel, and continuous etc.
class btDynamicsWorld : public btCollisionWorld {
btInternalTickCallback m_internalTickCallback;
btInternalTickCallback m_internalPreTickCallback;
void* m_worldUserInfo;
btContactSolverInfo m_solverInfo;
}
btCollisionWorld
The btCollisionWorld sets up the basic collision framework, it manages the broadphase and dispatcher.
class btCollisionWorld {
btAlignedObjectArray<btCollisionObject*> m_collisionObjects;
btDispatcher* m_dispatcher1;
btDispatcherInfo m_dispatchInfo;
btBroadphaseInterface* m_broadphasePairCache;
btIDebugDraw* m_debugDrawer;
///m_forceUpdateAllAabbs can be set to false as an optimization to only update active object AABBs
///it is true by default, because it is error-prone (setting the position of static objects wouldn't update their AABB)
bool m_forceUpdateAllAabbs;
}