Some trouble with btPairCachingGhostObject

User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Some trouble with btPairCachingGhostObject

Post by TomorrowPlusX »

In the interest of being notified of when two objects touch, I've given a stab at btPairCachingGhostObject; the good news is that it works as advertised, and I get the notifications I want. The bad news is, I can no longer use raycasts to 'pick' objects in my scene that have ghost objects.

Now, it's entirely possible I'm not using ghost objects correctly, or I'm doing something else wonky.

So, here's how I stitched it together:

1) My objects have both a btRigidBody and a btPairCachingGhostObject; I manually sync the transform of the ghost object to match the rigid body in my motion state subclass.

2) After each step, objects with a ghost object run code derived from the wiki ghost object example (http://www.bulletphysics.com/mediawiki- ... d_Triggers) -- the collisions get forwarded as needed -- it works as expected.

3) Raycasts to objects with ghost objects don't work any more.

Here's some code. Note: I'm implementing bullet behind an abstract general purpose physics interface since I currently use ODE and am evaluating bullet.

First, here's where I create the ghost object:

Code: Select all

void 
BulletKinematicBody::setNotifyOnContact( bool noc )
{
	KinematicBody::setNotifyOnContact( noc );

	if ( notifyOnContact() )
	{
		_ghost = new btPairCachingGhostObject();
		_ghost->setCollisionShape( bulletShape() );
		_ghost->setUserPointer( this );
		_ghost->setCollisionFlags( btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE );
		_dynamicsWorld->addCollisionObject( _ghost );
	}
	else if ( _ghost )
	{
		_dynamicsWorld->removeCollisionObject( _ghost );
		delete _ghost;		
		_ghost = NULL;
	}

	becomeSteppingIfNecessary();
}
Here's the function that's called from KinematicBody::step when it has a ghost object:

Code: Select all

void 
BulletKinematicBody::checkForContacts( void )
{
	//
	//	Adapted from Bullet wiki:
	//	http://www.bulletphysics.com/mediawiki-1.5.8/index.php?title=Collision_Callbacks_and_Triggers
	//

	btManifoldArray   manifoldArray;
	btBroadphasePairArray& pairArray = _ghost->getOverlappingPairCache()->getOverlappingPairArray();
	int numPairs = pairArray.size();
		
	for ( int i=0; i < numPairs; i++ )
	{
		manifoldArray.clear();
		
		const btBroadphasePair& pair = pairArray[i];
		
		//unless we manually perform collision detection on this pair, the contacts are in the dynamics world paircache:
		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];
			
			BulletKinematicBody *kb0 = BulletKinematicBody::kinematicBodyForBulletCollisionObject( static_cast<btCollisionObject*>(manifold->getBody0()) );
			BulletKinematicBody *kb1 = BulletKinematicBody::kinematicBodyForBulletCollisionObject( static_cast<btCollisionObject*>(manifold->getBody1()) );
			
			if ( kb0 == this && kb1 == this ) continue;

			BulletKinematicBody *other = kb0 == this ? kb1 : kb0;

			for ( int p = 0; p < manifold->getNumContacts(); p++ )
			{
				const btManifoldPoint &pt = manifold->getContactPoint(p);
				if ( pt.getDistance() < 0.f )
				{
					contact( other->kinematicBodyID(), BTS( pt.getPositionWorldOnB() ), BTS( pt.m_normalWorldOnB ), -pt.getDistance() );
				}
			}
		}
	}
}
And finally here's my btMotionState::setWorldTransform implementation which syncs my rendering as well as the ghost object:

Code: Select all

virtual void setWorldTransform( const btTransform& centerOfMassWorldTrans )
{
	btDefaultMotionState::setWorldTransform( centerOfMassWorldTrans );

	mat4 m;
	this->m_graphicsWorldTrans.getOpenGLMatrix( m.mat );

	_body->_transform = m;
	_body->updateAABB();
	_body->transformChanged.run( _body->kinematicBodyID(), m );
			
	if ( _body->_ghost )
	{
		_body->_ghost->setWorldTransform( centerOfMassWorldTrans );
	}			
}
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA

Re: Some trouble with btPairCachingGhostObject

Post by Erwin Coumans »

The raycast uses the same collision flags to exclude objects, so

Code: Select all

   _ghost->setCollisionFlags( btCollisionObject::CF_KINEMATIC_OBJECT | btCollisionObject::CF_NO_CONTACT_RESPONSE );
will prevent ray casts (due to logic around line 669 of Bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp).

The best is to implement a custom rayResultCallback, and re-implement your own 'needsCollision', that doesn't skip ghosts due to filter flags:

Code: Select all

virtual bool RayResultCallback::needsCollision(btBroadphaseProxy* proxy0) const
Hope this helps,
Erwin
User avatar
TomorrowPlusX
Posts: 14
Joined: Wed Apr 16, 2008 11:45 am

Re: Some trouble with btPairCachingGhostObject

Post by TomorrowPlusX »

Thanks, Erwin

I'll give it a try!

Also, I'm really impressed with Bullet's performance so far -- I've done some stress tests which would have destroyed ODE. I'm converted.

FOLOWUP: Worked perfectly, thanks!