Bullet 学习笔记之 btCollisionWorld
对于 Bullet 物理引擎中的碰撞检测部分,并不太关心它的实现细节,更想知道的是物体的存储方式/数据存放、碰撞结果在哪儿,大致的步骤等
1、碰撞场景类 btCollisionWorld
的结构
btCollisionWorld
是 Bullet 物理引擎中的碰撞场景类,并由此派生出了 btDynamicsWorld
以及进一步派生出 btDiscreteDynamicsWorld
btSoftRigidDynamicsWorld
等。
先从 btCollisionWorld
的成员变量/函数分析它的碰撞算法/数据存储:
btAlignedObjectArray<btCollisionObject*> m_collisionObjects;
btDispatcher* m_dispatcher1;
btDispatcherInfo m_dispatchInfo;
btBroadphaseInterface* m_broadphasePairCache;
主要成员变量有这么四个。其中,m_collisionObjects
是存放场景中的物体。m_dispatcher1
是用于 narrowphase collision detection 的,即,用于精确碰撞检测,(其中,通过 btDispatcher
类可知,最终的碰撞检测结果,也是存放在 dispatcher 中的 btPersistentManifold
或者 user callbacks
。)另,m_dispatchInfo
是精确碰撞检测的一些配置信息。m_broadphasePairCache
则应该是用于 broadphase collision detection 的,最终形成可能的碰撞对 overlappinng pairs 。
2、碰撞场景 btCollisionWorld
的初始化/搭建
接下来看,btCollisionWorld
类的初始化过程,以及在 Basic Example 中,都是怎么配置 btCollisionWorld
的。
在构造函数中,分别对 m_dispatcher1
和 m_broadphasePairCache
进行了初始化。参见 BasicDemo
中的例子,初始化阶段:
m_dispatcher = new btCollisionDispatcher(m_collisionConfiguration); // --> m_dispatcher1
m_broadphase = new btDbvtBroadphase(); // --> m_broadphasePairCache
那么,broad phase 是由 btDbvtBroadphase
类完成的,并将所检测到的 overlapping pairs 存储在其当中; narrow phase 则是由 btCollisionDispatcher
类完成的,并将最终检测到的碰撞点信息存放在 btPersistentManifold
中。
此外,对于场景中的碰撞对象的添加、删除、访问,可以通过以下函数实现:
virtual void addCollisionObject(btCollisionObject* collisionObject, int collisionFilterGroup = btBroadphaseProxy::DefaultFilter, int collisionFilterMask = btBroadphaseProxy::AllFilter);
virtual void removeCollisionObject(btCollisionObject* collisionObject);
btCollisionObjectArray& getCollisionObjectArray()
{
return m_collisionObjects;
}
const btCollisionObjectArray& getCollisionObjectArray() const
{
return m_collisionObjects;
}
3、碰撞检测方式/过程
接下来,大致看一下碰撞检测过程,以及碰撞的到的结果(),是存放在什么地方,又是如何被进一步调用的。
在文章 Ron Ngai:[Bullet3]三种碰撞检测及实现 中提到,Bullet 物理引擎可以实现三种方式的碰撞检测:(1)btCollisionWorld::contactTest
检测指定对象是否与场景发生碰撞;(2)btCollisionWorld::performDiscreteCollisionDetection
检测场景中所有物体的碰撞;(3)btCollisionWorld::rayTest
对场景中的所有物体进行射线碰撞检测。
也可以这么说,btCollisionWorld
类可以实现以下几种碰撞检测方式:
-
3.1、对场景中所有物体进行碰撞
具体来说,是通过调用函数btCollisionWorld::performDiscreteCollisionDetection()
实现。 -
3.2、对场景中所有物体进行射线碰撞检测
调用函数btCollisionWorld::rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback)
完成。对于检测到的结果,可以分为 first hit, all hits, any hit 等,通过调用resultCallback
可以对结果进行处理。(其实还有一个,可以对一个给定的射线、给定的对像,进行碰撞检测。)具体函数为:
/// rayTest performs a raycast on all objects in the btCollisionWorld, and calls the resultCallback
/// This allows for several queries: first hit, all hits, any hit, dependent on the value returned by the callback.
virtual void rayTest(const btVector3& rayFromWorld, const btVector3& rayToWorld, RayResultCallback& resultCallback) const;
/// rayTestSingle performs a raycast call and calls the resultCallback. It is used internally by rayTest.
/// In a future implementation, we consider moving the ray test as a virtual method in btCollisionShape.
/// This allows more customization.
static void rayTestSingle(const btTransform& rayFromTrans, const btTransform& rayToTrans,
btCollisionObject* collisionObject,
const btCollisionShape* collisionShape,
const btTransform& colObjWorldTransform,
RayResultCallback& resultCallback);
- 3.3、给定对象与场景中所有对象的碰撞检测
包括了两个函数,具体为:
///contactTest performs a discrete collision test between colObj against all objects in the btCollisionWorld, and calls the resultCallback.
///it reports one or more contact points for every overlapping object (including the one with deepest penetration)
void contactTest(btCollisionObject* colObj, ContactResultCallback& resultCallback);
///contactTest performs a discrete collision test between two collision objects and calls the resultCallback if overlap if detected.
///it reports one or more contact points (including the one with deepest penetration)
void contactPairTest(btCollisionObject* colObjA, btCollisionObject* colObjB, ContactResultCallback& resultCallback);
- 3.4、Convex Sweep Test
感觉应该是,给定一个 convex 对象,在场景中从from
运动至to
,该过程中会发生哪些碰撞,结果包括 first hit, all hits, any hit。碰撞结果可以由resultCallback
访问并处理。具体函数为:
/// convexTest performs a swept convex cast on all objects in the btCollisionWorld, and calls the resultCallback
/// This allows for several queries: first hit, all hits, any hit, dependent on the value return by the callback.
void convexSweepTest(const btConvexShape* castShape, const btTransform& from, const btTransform& to, ConvexResultCallback& resultCallback, btScalar allowedCcdPenetration = btScalar(0.)) const;
4、碰撞结果存储及处理
对于 btCollisionWorld
中不同的碰撞检测方式(如3.1 - 3.4所述),其结果的存放/处理也不尽相同。就逐个分析一下吧
-
4.1 对场景中所有物体进行碰撞 - 碰撞结果存放及处理
-
4.2 对场景中所有物体进行射线碰撞检测 - 碰撞检测结果存放及处理
-
4.3、给定对象与场景中所有对象的碰撞检测 - 碰撞结果存放及处理
-
4.4、Convex Sweep Test - 碰撞结果存放及处理
5、结语
xxx。