Undefined collisions of 3+ bodies in one tick with CCD?

zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

UPDATE: I found a workaround using convexSweepTest() that I posted at the bottom of this thread.

----------

Hi I'm using CCD in a space sim where right now the ships are all spheres and the shots move about 8000 m/s, but I think I've stumbled onto a problem where 2 shots hitting an object in one tick is somewhat undefined, due to their high speed. The shots bounce off the object correctly, but I want the first shot to destroy the object so that the second shot doesn't hit it. The problem is that if I tell Bullet to simulate both shots in the narrow phase, it always bounces both of them, and I have no way to selectively disable the collision of the second shot. So I see the target get destroyed and frequently the second shot shoots off into space even though the target no longer exists.

I AM able to stop the second shot if I do it during the broadphase in a filter callback. Unfortunately this doesn't give enough control, because if I choose which shot is going to hit the object in the broadphase, I don't have enough data to do it accurately, so the result is that I'm able to destroy things by just shooting their bounding box. I could implement my own collision logic in the filter, but I would rather have bullet do it accurately in the narrow phase.

So I think that I have found a bug where if 3 or more objects are going to collide during a tick with CCD, there is no way to selectively decide to let 2 hit but cancel the collision of the third during the narrow phase.

I've spent the last few days reading pages like these:

http://bulletphysics.org/mediawiki-1.5. ... _Filtering
http://www.bulletphysics.org/mediawiki- ... ion_Things
http://www.scribd.com/doc/54304058/45/F ... arCallback
http://bulletphysics.org/Bullet/phpBB3/ ... &view=next
http://www.gamedev.net/topic/497454-cul ... t-working/
http://www.bulletphysics.org/Bullet/php ... f=9&t=2243

I've tried the filter callback with masks, the nearCallback, a contact points check with getManifoldByIndexInternal() in the after tick callback, and even created my own dispatcher class inherited from btCollisionDispatcher to override needsCollision() and needsResponse().

I feel like I am missing something fundamental, because in my mind I think that there should still be a callback or way to override the collision check in the narrow phase, so that I can stop further collisions as soon as I know that 2 objects have touched for certain.

I'm thinking this may be impossible, because the 2 shots may get simulated out of order, so the second one might be given the benefit of the doubt before the first. In that case, I would be ok with making a list of the 3 touching objects and simulating 2 of them hitting, but put the third back by simulating where it would have gone if it hadn't interacted. I'm not sure if Bullet has a way to query where an object "will be" at the end of the tick if it doesn't interact though.

I realize this is all a bit esoteric, but this was an area that I worried wasn't going to work before I even tried Bullet, which seems strange because shooting things is one of the main things that Bullet was designed to do, hence the name...

Thanx for any help you can provide,

--Zack
Last edited by zmorris on Tue Jun 14, 2011 10:26 pm, edited 1 time in total.
zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

OK I found this page about using btGhostObject:

http://bulletphysics.org/mediawiki-1.5. ... d_Triggers

This doesn't seem to affect the callbacks (they always get called):

mBody->setCollisionFlags(mBody->getCollisionFlags() | btCollisionObject::CF_CUSTOM_MATERIAL_CALLBACK);

But I tried setting:

mBody->setCollisionFlags(mBody->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE));

And now the shots go through objects so I can handle their response manually, but unfortunately gContactProcessedCallback only gets called if the shot is lined up so that it's exactly inside an object at the instant a tick occurs. So I hear a series of beeps from the callback periodically, that stop briefly if I shoot continuously and move forward so the shots happen to be outside the object during a tick. This may be a problem with CCD. I think that I need an even more fine-grained callback that would be triggered if the CCD detects that the objects would have collided at any point during the tick.

FYI if I turn off CF_NO_CONTACT_RESPONSE, the shots always bounce off the objects perfectly, due to CCD. The issue is that gContactProcessedCallback only gets called periodically when the shots and objects are lined up perfectly during a tick. Is there an even more fine-grained callback?

Thanx,

--Zack
zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

OK one more update. I was hopeful that gContactAddedCallback or gContactProcessedCallback would work, and they do for high speed CCD bodies that have a contact response. I hear beeps every time a shot bounces off an object.

Unfortunately I just found the bug I was expecting to find, that if you make an object a ghost by setting:

body->setCollisionFlags( body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE );

Then the callbacks go back to just getting called at tick intervals. This means when I turn off contact response, I go back to seeing half the shots slip through objects with no beep, because they evidently stop using CCD.

I'm going to try examining the pair cache at the end of each tick to detect if interaction occurred when contact response is off, but I'm expecting to see the same bug since I'm pretty sure that cache will be based on the flawed gContactAddedCallback logic.

--Zack
zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

Well I thought I was getting somewhere because gContactAddedCallback and gContactProcessedCallback were working perfectly until I called:

body->setCollisionFlags( body->getCollisionFlags() | btCollisionObject::CF_NO_CONTACT_RESPONSE );

No-contact response goes back to just per-tick collision testing, so it's incompatible with CCD. So as far as I can tell, there is no way to use CCD for triggering, which is a real shame and severely limits what Bullet can do.

I even tried:

Code: Select all

bool	MyContactAddedCallback(
    btManifoldPoint& cp,
    const btCollisionObject* colObj0,
    int partId0,
    int index0,
    const btCollisionObject* colObj1,
    int partId1,
    int index1)
{
	int		numManifolds = world->getDispatcher()->getNumManifolds();
	
	for( int i = 0; i < numManifolds; i++ )
	{
		btPersistentManifold	*contactManifold = mworld->getDispatcher()->getManifoldByIndexInternal(i);
		
		contactManifold->clearManifold();
	}
	
	return( true );
}
Inside of gContactAddedCallback, hoping that I could empty the manifold so that no collision would get a response, but evidently CCD doesn't use the contact manifold, or has already decided what is going to respond regardless of what's in the contact manifold.

At this point I have to throw in the towel, unless anyone else has a way to do triggering with CCD. I just don't understand how Bullet could be so obtuse, when shooting stuff was the very first thing I tried, and I half expected that it would "just work", but here it is 3 days later and with so many free and open source libraries, the effort has ended in failure. Blah.
Last edited by zmorris on Tue Jun 14, 2011 10:27 pm, edited 1 time in total.
zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

OK just wanted to let everyone know that I worked around the issue by doing a convexSweepTest() of the non-interacting projectile in the m_internalPreTickCallback. Luckily Bullet has code built-in to do a prediction of the future location of a body, so all I had to do was plug in the time delta and it "just worked".

Then I made a struct inherited from btCollisionWorld::ConvexResultCallback that overrides addSingleResult(). That gave me a continuous trigger callback that seems to be fairly accurate. This isn't QUITE as accurate as doing a full simulation, because the target objects aren't moving during the ray cast. But, at 60 fps, it's probably "good enough" for most purposes, until they finish the btContinuousDynamicsWorld.

If any Bullet maintainers are reading this, the problem comes down to contacts not being added to the getAllContactManifolds() of the world's getPairCache() in CCD mode when I examine them in the m_internalTickCallback. They only get added with discrete 60 fps accuracy, so the non-interactive projectiles pass right through a target sometimes without reporting contact. As soon as I disable CF_NO_CONTACT_RESPONSE, I see every projectile bounce off perfectly every time, and the manifold is correctly populated. There may very well be a better way to do this, but I just can't spend any more time researching it. Hopefully it wouldn't be too difficult in a future release of Bullet to get all bodies to populate the manifold, whether they are interactive or not.

--Zack

Code: Select all

struct ConvexResultCallbackStruct : public btCollisionWorld::ConvexResultCallback
{
	btCollisionObject	*object;
	
	virtual	btScalar	addSingleResult( btCollisionWorld::LocalConvexResult& convexResult, bool normalInWorldSpace)
	{
		// do your contact trigger logic here
	};
}
localConvexResultCallback;

voidBeforeTickCallback( btDynamicsWorld *world, btScalar timeStep )
{
	for( int i = world->getNumCollisionObjects() - 1; i >= 0; i-- )
		{
			btRigidBody		*body = btRigidBody::upcast( world->getCollisionObjectArray()[i] );
			
			if( body )
				if( body->getCollisionFlags() & btCollisionObject::CF_NO_CONTACT_RESPONSE )	/* as of Bullet 2.78, there is a bug where non-interacting objects don't do continuous collision detection (CCD), so do it manually */
				{
					btMotionState	*motionState = body->getMotionState();
					btTransform		fromTrans = body->getCenterOfMassTransform(), toTrans;
					
					body->predictIntegratedTransform( timeStep, toTrans );
					
					localConvexResultCallback.object = world->getCollisionObjectArray()[i];
					
					world->convexSweepTest( (btConvexShape*) body->getCollisionShape(), fromTrans, toTrans, localConvexResultCallback );
				}
		}
}
Kanttori
Posts: 38
Joined: Thu Jul 08, 2010 12:52 am

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by Kanttori »

I think the CCD implementation in _discrete_ dynamics is an anti-tunneling measure (uses a sphere and not the actual shape) so getting contacts when no collision response is needed is outside it's intended purview. Even if the contacts were added for objects without a contact response, you'd miss collisions with a more complex actual shape and so it wouldn't be a general solution.
zmorris
Posts: 7
Joined: Mon Jun 13, 2011 4:53 pm

Re: Undefined collisions of 3+ bodies in one tick with CCD?

Post by zmorris »

Ya I think you are probably right, so maybe I only thought CCD worked because all my objects are spheres right now. I wonder how hard it would be to generalize the CCD stuff to work with any convex shape? Continuous dynamics shouldn't really be that hard, instead of a 60 fps tick, it would just calculate the time of next collision and interpolate between them (which could end up being quite infrequent). Of course as an analogy, affine transforms are a far cry from OpenGL so there is probably a lot more to it.

The main thing I learned is that I think Bullet would be more powerful if contacts and triggers were more strictly separated from responses. I swear that during testing, I found a bunch of cases where the group mask bits were overriding the result I was returning from the callback(s). I just felt like I didn't have the fine-grained control I really needed for collisions. The test case is to fire 2 high-speed projectiles at a target with CCD so that they both collide during a tick, and have one of them interact with the target (turning both the target and the projectile off) and have the second projectile pass through unhindered. Pretty much every game is going to need this, and without the workaround, it doesn't currently work.

--Zack