.. default-domain:: cpp Bullet's Collision Algorithm **************************** Overview ======== 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. * :code:`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. * :code:`btDispatcher` foo foo blas blah * :code:`btBroadphaseInterface` * :code:`btConstraintSolver` * :code:`btCollisionConfiguration` blah Concepts ======== Collision Objects ----------------- Collision Shapes ---------------- Broad Phase Collision --------------------- Most collision detection frameworks break up collision detection into two phases, *broadphase* and *narrowphase* Narrow Phase Collision ---------------------- AABB Bounding Box Hierarchy --------------------------- 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. Collision Pairs --------------- Collision Manifold ------------------ 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 :code:`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: * the contact point with deepest penetration is always kept, and it tries to maximuze the area covered by the points * note that some pairs of objects might have more then one contact manifold. Simulation Islands ------------------ 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 :code:`btCollisionAlgorithm::processCollision` method as it's name implies actually processs a collision. The :code:`resoutOut` parameter is often, but not alwasy ingnored by the caller. Basically, the :code:`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 :code:`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. Single Step ----------- m_dynamicsWorld->stepSimulation(deltaTime); int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, btScalar fixedTimeStep) The interesting stuff :: for (int i=0;i 0) { createPredictiveContactsInternal( &m_nonStaticRigidBodies[ 0 ], m_nonStaticRigidBodies.size(), timeStep ); } } .. _coldet: Tracing Through the Collision Detection Phase --------------------------------------------- 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 :code:`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 :code:`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;iprocessOverlap(*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); Bullet Callbacks and Triggers ----------------------------- 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. * btGhostObject 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 'Bullet\Demos\CharacterDemo' and the btKinematicCharacterController (in the recoverFromPenetration method). Contact Text ^^^^^^^^^^^^ 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(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); Contact Pair Test ^^^^^^^^^^^^^^^^^ 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. Contact Callbacks ^^^^^^^^^^^^^^^^^ 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: :: mBody->setCollisionFlags(mBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK); 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. Triggers ^^^^^^^^ 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)); Triggers and the ``btKinematicCharacterController`` ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The stock :code:`btKinematicCharacterController` doesn't appear to properly behave with ghost objects that have :code:`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 btBroadphasePair* collisionPair = &m_ghostObj... line: :: //for trigger filtering if (!static_cast(collisionPair->m_pProxy0->m_clientObject)->hasContactResponse() || !static_cast(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); Classes ======= This section describes the key classes involved with Bullet collision detection. .. _btBroadphasePair: :code:`btBroadphasePair` ------------------------ https://github.com/bulletphysics/bullet3/blob/master/src/BulletCollision/BroadphaseCollision/btBroadphaseProxy.h 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 :code:`btBroadphasePair` When the AABB's of a pair of collision objects overlap, the :code:`btHashedOverlappingPairCache::addOverlappingPair`, in a call to :code:`btHashedOverlappingPairCache::internalAddPair` creates a new :code:`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: ``btCollisionAlgorithm`` ------------------------ https://github.com/bulletphysics/bullet3/blob/master/src/BulletCollision/BroadphaseCollision/btCollisionAlgorithm.h :: 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: ``btPersistentManifold`` ------------------------ https://github.com/bulletphysics/bullet3/blob/master/src/BulletCollision/NarrowPhaseCollision/btPersistentManifold.h#L63 :: 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`` --------------------------- https://github.com/bulletphysics/bullet3/blob/master/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h 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 m_sortedConstraints; InplaceSolverIslandCallback* m_solverIslandCallback; btConstraintSolver* m_constraintSolver; btSimulationIslandManager* m_islandManager; btAlignedObjectArray m_constraints; btAlignedObjectArray 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 m_actions; int m_profileTimings; bool m_latencyMotionStateInterpolation; btAlignedObjectArray 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 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; }