[ODE] Strategies for efficient collisions in a large world with few moving bodies

Shamyl Zakariya shamyl at zakariya.net
Wed Jan 18 06:26:58 MST 2006


Thank you for the replies!

With some futzing, I came upon a solution which both works, and  
brings CPU time in the dCollide functions down to about 2.5%, which  
is as far as I'm concerned excellent. I'm going to describe what I  
did, and I hope somebody here can explain to me why it works... as  
opposed to before.

First, I added a new space specifically for the ground trimesh. I'm  
not certain how ODE's quadtree works, but the quadtree visibility  
determination system I wrote for my game allows an object to be in  
only one node at a time ( so objects are only drawn once ), therefore  
nodes will expand to fit their contents. Now, it's possible ODE's  
implementation allows objects to be in multiple nodes simultaneously  
( makes sense in this context ), but I figured I might give a stab  
anyway at taking the ground trimesh out and putting it in its own  
space. Second, I made only the static space a quadtree space, the  
ground and actor spaces are hash spaces. I didn't change my  
nearCallback, but I'll show it below.

So my setup looks like:

	_staticSpace = dQuadTreeSpaceCreate( 0, vec3( 0,0,0 ), vec3 
( _worldSize, _worldSize, _worldSize ), 5 );
	_actorSpace = dHashSpaceCreate( 0 );
	_groundSpace = dHashSpaceCreate( 0 );

Next, in my World::step() method I call dCollide2 twice

	dSpaceCollide2( (dGeomID) _staticSpace, (dGeomID) _actorSpace, this,  
&nearCallback );
	dSpaceCollide2( (dGeomID) _groundSpace, (dGeomID) _actorSpace, this,  
&nearCallback );
	dSpaceCollide( _actorSpace, this, &nearCallback);

	dWorldStep( _world, deltaT );			
	dJointGroupEmpty( _contactGroup );

Now, here's my nearCallback:

void nearCallback (void *data, dGeomID g1, dGeomID g2)
{
	const int maxContacts = 6;
	World *world = (World *) data;
	
	/*
		back out if either doesn't allow collisions with the other
	*/

	Entity *e1 = Entity::entityForGeom( g1 );
	Entity *e2 = Entity::entityForGeom( g2 );

	if ( e1 && e2 )
	{
		if ( !e1->allowCollisionWith( e2 ) || !e2->allowCollisionWith 
( e1 )) return;
	}
	
	dBodyID b1 = dGeomGetBody( g1 );
	dBodyID b2 = dGeomGetBody( g2 );

	/*
		Exit if they have corresponding bodies.
		This, conveniently, discards collisions between statics, since  
their bodies == NULL.
	*/
	if ( b1 == b2 )
	{
		return;
	}

	/*
		exit without doing anything if the two bodies are connected by a joint
		or Connection. Connection implementations may need more complex  
checking than
		just the default joint checking, since some (Servo) use  
intermediate "pinion"
		bodies.
	*/
	
	#warning Removed Connection::connectedBy() from nearCallback
	
	if (
			b1 && b2 &&
			(dAreConnectedExcluding( b1, b2, dJointTypeContact ))
	   )
	{
		return;
	}		
	
	/*
		Determine mu values for b1 and b2
		Note that the geoms for the ground mesh and walls have no bodies, so
		we special case it to use sim->getGroundMu()
	*/

	float defaultMu = world->_defaultMu;
	float g1Mu = e1 ? e1->mu() : defaultMu;
	float g2Mu = e2 ? e2->mu() : defaultMu;
	float combinedMu = 0;

	/*
		This is my preposterous way to determine combined friction for two  
objects:
		product divided by average. Seems "good enough". Allows for a  
sticky object
		to slip if against a frictionless one. Seems reasonable to me :/
	*/
	if ( g1Mu < EPSILON && g2Mu < EPSILON )
	{
		combinedMu = 0;
	}
	else
	{
		combinedMu = ( g1Mu * g2Mu ) / ( (g1Mu + g2Mu) * 0.5f );
	}


	dContact contact[maxContacts];
	for (int i = 0; i < maxContacts; i++)
	{
		contact[i].surface.mode = dContactSoftCFM | dContactSoftERP;
		contact[i].surface.mu = combinedMu;
		contact[i].surface.soft_cfm = 0.0001f;
		contact[i].surface.soft_erp = 0.2f;
	}
	

	if ( int numc = dCollide( g1, g2, maxContacts, &contact[0].geom,  
sizeof(dContact)) )
	{
		for ( int i = 0; i < numc; i++ )
		{
			dJointID c = dJointCreateContact( world->_world, world- 
 >_contactGroup, &(contact[i]) );
			dJointAttach( c, b1, b2 );
						
			if ( e1 )
			{
				e1->collision( e2, contact[i].geom.pos, contact[i].geom.normal,  
contact[i].geom.depth );
			}
			
			if ( e2 )
			{
				e2->collision( e1, contact[i].geom.pos, contact[i].geom.normal,  
contact[i].geom.depth );
			}						
		}
	}
}


shamyl zakariya
     "obviously, you're not a golfer"
         -- the Dude

On Jan 17, 2006, at 8:47 PM, Chris Ledwith wrote:

> No collisions? Very strange. Can we see what your near callback  
> function looks like?
>
> By the way, once you do get collision with quadtree spaces working,  
> make sure that it is oriented correctly, that is, you may need to  
> modify the AXIS0, AXIS1, and UP #defines at the top of  
> collision_quadtreespace.cpp.
>
> -C
>



More information about the ODE mailing list