编程文汇

bullet物理引擎(3) 碰撞检测过滤(选择性碰撞)

Bullet提供了三种简单的方法来筛选需要检测碰撞的物体:mask,broadphase过滤器回调和Nearcallbacks。mask的性能成本要远远低于回调。简而言之,如果mask足以满足您的需求,使用它们;他们表现更好,使用起来也更简单。当然,也不要仅仅因为他们性能稍好,就把明显不合适的功能塞给mask来做。callback的性能也只是稍差而已。

使用掩码 mask

Bullet支持位操作掩码(int32,最多32类物体),来决定物体是否应该和别的物体碰撞,或者接收碰撞。

int myGroup = 1;
int collideMask = 4;
world->addCollisionObject(object,myGroup,collideMask);

在粗略检测阶段,只有掩码和另一类型物体的group匹配时,才会将重叠的物体算作碰撞(在needsBroadphaseCollision方法中)。从代码看,这是双向的。

bool collides = 
  (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
collides = collides 
  && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);

如果您的对象类型多于掩码中可用的32位,或者基于其他因素来启用或禁用碰撞,那么可以通过几个方法注册回调函数,实现自定义过滤逻辑,在函数中决定哪些类型的物体需要碰撞:

碰撞过滤之 Broadphase Filter Callback

粗略检测阶段利用回调过滤碰撞也是很有效的,可以在碰撞检测的早期阶段就过滤掉不必要的物体。

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;
  }
};

然后,创建这个过滤器的对象,并且注册到world对象里。

btOverlapFilterCallback * filterCallback = new YourOwnFilterCallback();
dynamicsWorld->getPairCache()->setOverlapFilterCallback(filterCallback);

碰撞过滤之 :NearCallback

与上一节类似,只不过这个阶段发生在NarrowPhase(在粗略之后)。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);

碰撞过滤之终极杀器:自定义btCollisionDispatcher

如果需要更细粒度的碰撞派发,可以集成btCollisionDispatcher,并实现以下方法:

virtual bool needsCollision(btCollisionObject* body0,btCollisionObject* body1);
virtual bool needsResponse(btCollisionObject* body0,btCollisionObject* body1);
virtual void dispatchAllCollisionPairs(btOverlappingPairCache* pairCache,const
btDispatcherInfo& dispatchInfo,btDispatcher* dispatcher) ;

最后

对于我们而言,掩码和粗略检测时的回调,已经足够了,没必要自定义btCollisionDispatcher。