Different kind of reactions with different kind of objects

Post Reply
ThunderZap
Posts: 1
Joined: Mon Jan 15, 2007 5:18 pm

Different kind of reactions with different kind of objects

Post by ThunderZap »

I dont speak fluent english, so please excuse any grammar mistakes. I'm pretty newb what comes to physics engines but I need little guiding with bullet. :P
I'm creating a game where players are not having collisions with each other only with terrain. I'm just wondering how to do this with bullet. And there's another question also. If I have one type of objects and then I would create sphere how would I get only one type of objects that collided with the sphere.

Feel free to not answer if my questions were too inaccurate, but please specify what part was inaccurate

Thanks in advance[/quote]
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Different kind of reactions with different kind of objec

Post by Erwin Coumans »

There are several ways of filtering collisions between different groups of objects. The easiest is to use the collision filter flags.

Instead of adding a rigidbody to the world using 'addRigidBody', you can use 'addCollisionObject', and pass the collisionFilterGroup and collisionFilterMask. The collisionFilterGroup is an integer bitfield, where each bit represents a group. The mask selects which other objects a collision is performed. Before performing a pair-wise collision between two objects, Bullet will first perform an 'AND' operation using the group of one objects against the mask of another.

See btOverlappingPairCache.h:

Code: Select all

	inline bool needsBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const
	{
		bool collides = (proxy0->m_collisionFilterGroup & proxy1->m_collisionFilterMask) != 0;
		collides = collides && (proxy1->m_collisionFilterGroup & proxy0->m_collisionFilterMask);
		
		return collides;
	}
By default, Bullet uses this to disable collisions between static objects:

Code: Select all

void	btDiscreteDynamicsWorld::addRigidBody(btRigidBody* body)
{
	if (!body->isStaticOrKinematicObject())
	{
		body->setGravity(m_gravity);
	}

	if (body->getCollisionShape())
	{
		bool isDynamic = !(body->isStaticObject() || body->isKinematicObject());
		short collisionFilterGroup = isDynamic? btBroadphaseProxy::DefaultFilter : btBroadphaseProxy::StaticFilter;
		short collisionFilterMask = isDynamic? 	btBroadphaseProxy::AllFilter : 	btBroadphaseProxy::AllFilter ^ btBroadphaseProxy::StaticFilter;

		addCollisionObject(body,collisionFilterGroup,collisionFilterMask);
	}
}

Another option is to override the 'nearCallback' and manage all collisions yourself. This is harder, and no examples are available yet.

Hope this helps,
Erwin

ThunderZap wrote:I dont speak fluent english, so please excuse any grammar mistakes. I'm pretty newb what comes to physics engines but I need little guiding with bullet. :P
I'm creating a game where players are not having collisions with each other only with terrain. I'm just wondering how to do this with bullet. And there's another question also. If I have one type of objects and then I would create sphere how would I get only one type of objects that collided with the sphere.

Feel free to not answer if my questions were too inaccurate, but please specify what part was inaccurate

Thanks in advance
[/quote]
jackskelyton
Posts: 32
Joined: Mon Nov 05, 2007 3:52 pm

Re: Different kind of reactions with different kind of objects

Post by jackskelyton »

So, for example: how would you use this mask and filter to cause an object (a character for example) to interact with some dynamic objects but not others? I notice there are enumerated flags to represent dynamic, kinematic, and static objects. If you had a game where you wanted all the rockets to collide with zombies but not with ghosts, and all three (rockets, zombies, ghosts) were dynamic objects, how would you set that up?
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: Different kind of reactions with different kind of objects

Post by AlexSilverman »

Hello,

In a few weeks, I will need to solve the problem Jack mentions as well. I haven't tried this yet, but could you use a custom nearcallback function, and after the call to processCollision, check the manifold and if it indicates a collision has occurred then you remove it (if the collision filters says so) and do whatever game processing needs to be done.

Does that sound like it'll work?

- Alex
User avatar
Erwin Coumans
Site Admin
Posts: 4221
Joined: Sun Jun 26, 2005 6:43 pm
Location: California, USA
Contact:

Re: Different kind of reactions with different kind of objects

Post by Erwin Coumans »

For simple collision filter rules, you should be able to create a 2D matrix, and fill in the combinations manually. If you use 1 and 0, you can take the rows as bitfield for the collision mask. If this is not clear, we need some sample code, any volunteers?

If collision filter rules get more complicated, it is recommended to implement your own version of btOverlapFilterCallback, and register it to the btOverlappingPairCache, using btOverlappingPairCache::setOverlapFilterCallback(...). This gets called for each new pair, and you get the chance to decide to collide or not by yourself.

Code: Select all

struct btOverlapFilterCallback
{
	virtual ~btOverlapFilterCallback()
	{}
	// return true when pairs need collision
	virtual bool	needBroadphaseCollision(btBroadphaseProxy* proxy0,btBroadphaseProxy* proxy1) const = 0;
};
AlexSilverman wrote: In a few weeks, I will need to solve the problem Jack mentions as well. I haven't tried this yet, but could you use a custom nearcallback function, and after the call to processCollision, check the manifold and if it indicates a collision has occurred then you remove it (if the collision filters says so) and do whatever game processing needs to be done.
This would work, but is a bit more complicated.

Let us know if we need different functionality.
Hope this helps,
Erwin
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: Different kind of reactions with different kind of objects

Post by AlexSilverman »

Erwin,
Erwin Coumans wrote:This would work, but is a bit more complicated.
Yeah, I can see that. I'm definitely going to be working on the btOverlapFilterCallback. That seems much more straightforward.

- Alex
Flix
Posts: 456
Joined: Tue Dec 25, 2007 1:06 pm

Re: Different kind of reactions with different kind of objects

Post by Flix »

I've made some quick experiments yesterday ( so I didn't test them too much, and if the code I'm about to post doesn't work please forgive me ). Anyway by extending a bit the collision filter groups I've managed to get more control on the collisions logic.

I don't know if this solution is reliable for a more robust "collision group system"; anyway if somebody is interested what I did is:

1) Define at the global scope a (backward compatible) more complete set of collision flags:

Code: Select all

	enum MyCollisionFilterGroups
	{
	    CFG_DEFAULT 		= 1,
	    CFG_STATIC 			= 2,
	    CFG_KINEMATIC 		= 4,
	    CFG_DEBRIS 			= 8,
		CFG_SENSORTRIGGER 	= 16,
		CFG_NOCOLLISION 	= 32,
		CFG_GROUP0 			= 64,
		CFG_GROUP1 			= 128,
		CFG_GROUP2 			= 256,
		CFG_GROUP3 			= 512,
		CFG_GROUP4 			= 1024,
		CFG_GROUP5 			= 2048,
		CFG_GROUP6 			= 4096,
		CFG_GROUP7 			= 8192,
	    CFG_ALLFILTER = CFG_DEFAULT | CFG_STATIC | CFG_KINEMATIC | CFG_DEBRIS | CFG_SENSORTRIGGER |
				CFG_GROUP0 | CFG_GROUP1 | CFG_GROUP2 | CFG_GROUP3 | CFG_GROUP4 | CFG_GROUP5 | CFG_GROUP6 | CFG_GROUP7
	};
2) Subclass btDiscreteDynamicsWorld to change the "plain" addRigidBody(...) method:

Code: Select all

class MyBtDiscreteDynamicsWorld : public btDiscreteDynamicsWorld
{
public:
[...] // Constructor Here And Other Stuff

inline virtual void	addRigidBody(btRigidBody* body) {
	if (!body->isStaticOrKinematicObject())	{
		body->setGravity(m_gravity);
	}
	if (body->getCollisionShape())	{
		/*
		bool isDynamic = !(body->isStaticObject() || body->isKinematicObject());
		short collisionFilterGroup = isDynamic? short(CFG_DEFAULT) : short(CFG_STATIC);
		short collisionFilterMask = isDynamic? 	short(CFG_ALLFILTER) : 	short(CFG_ALLFILTER ^ CFG_STATIC);
		*/
		bool isKinematic = body->isKinematicObject();
		bool isStatic = body->isStaticObject();
		//bool isDynamic = !(isStatic || isKinematic);
		short collisionFilterGroup = CFG_DEFAULT;//isDynamic? short(CFG_DEFAULT) : short(CFG_STATIC);
		short collisionFilterMask = CFG_ALLFILTER;//isDynamic? 	short(CFG_ALLFILTER) : 	short(CFG_ALLFILTER ^ CFG_STATIC);
		if (isKinematic) {collisionFilterGroup = CFG_KINEMATIC;collisionFilterMask = CFG_ALLFILTER^(CFG_STATIC | CFG_KINEMATIC);}
		else if (isStatic) {collisionFilterGroup = CFG_STATIC;collisionFilterMask = CFG_ALLFILTER^(CFG_STATIC | CFG_KINEMATIC);}
		addCollisionObject(body,collisionFilterGroup,collisionFilterMask);
	}
}
};
3) Now the rigid bodies can be added to the subclassed world like this ( code pasted in a hurry, hope it works ):

Code: Select all

btWorld->addRigidBody(body,CFG_NOCOLLISION);	// CFG_NOCOLLISION means that this object cannot collide with anything (use it with static or kinematic bodies, or set gravity to zero). Good stuff for spacial triggers probably here...

btWorld->addRigidBody(body,CFG_GROUP0,CFG_ALLFILTER^CFG_GROUP0);	// this object is put in group0 and can collide with everything except objects belonging to group0 (by default objects are put in CFG_DEFAULT if dynamics)

btWorld->addRigidBody(body,CFG_GROUP0,CFG_ALLFILTER^(CFG_GROUP0 | CFG_GROUP1));	// this object is put in group0 and can collide with everything except objects belonging to group0 and group1

Hope it works in cases when one needs just a few collision groups...

Note that in the Bullet code kinematic objects were given the "static" group by default, I've changed it so that it's possible to create bodies that can collide with static objects but not with kinematic ones.

Hope it helps...
User avatar
Dragonlord
Posts: 198
Joined: Mon Sep 04, 2006 5:31 pm
Location: Switzerland
Contact:

Re: Different kind of reactions with different kind of objects

Post by Dragonlord »

Don't know if this helps or if I strangle here Bullet but I'm doing custom collisions like this:

Code: Select all

class debpCollisionDispatcher : public btCollisionDispatcher{
public:
	debpCollisionDispatcher( btCollisionConfiguration *collisionConfiguration );
	~debpCollisionDispatcher();
	
	virtual bool needsCollision( btCollisionObject *body0, btCollisionObject *body1 );
	virtual bool needsResponse( btCollisionObject *body0, btCollisionObject *body1 );
};

debpCollisionDispatcher::debpCollisionDispatcher( btCollisionConfiguration *collisionConfiguration ) : btCollisionDispatcher( collisionConfiguration ){ }
debpCollisionDispatcher::~debpCollisionDispatcher(){ }

bool debpCollisionDispatcher::needsCollision( btCollisionObject *body0, btCollisionObject *body1 ){
	if( ! btCollisionDispatcher::needsCollision( body0, body1 ) ) return false;
	debpPhysicsBody *phyBody1 = ( debpPhysicsBody* )body0->getUserPointer();
	debpPhysicsBody *phyBody2 = ( debpPhysicsBody* )body1->getUserPointer();
	// determine with your method if phyBody1 and phyBody2 can collide
}
bool debpCollisionDispatcher::needsResponse( btCollisionObject *body0, btCollisionObject *body1 ){
	if( ! btCollisionDispatcher::needsResponse( body0, body1 ) ) return false;
	debpPhysicsBody *phyBody1 = ( debpPhysicsBody* )body0->getUserPointer();
	debpPhysicsBody *phyBody2 = ( debpPhysicsBody* )body1->getUserPointer();
	// determine with your method if phyBody1 and phyBody2 can collide
}
This way I got any kind of complex collisions ( even if one object A is supposed to not hit another object B of the same class although usually it would ). I don't know though what exactly needsResponse is doing. So far I just rewrote needsCollision and this seems to work. You still have to test for collisions that happened but this way you should only get collisions that you want.

Nearly forgot. You gave to put your original game object into the user pointer of the body. This way you do not have to manipulate the collision filters nor do you have to tamper with the addRigidBody function.
jackskelyton
Posts: 32
Joined: Mon Nov 05, 2007 3:52 pm

Re: Different kind of reactions with different kind of objects

Post by jackskelyton »

I implemented a simply 2D matrix with bitwise integers for filtering and that worked fine -- no need to subclass or do custom implementations of any bullet code. However, what we need is a bit more complex, and in pursuing it I noticed something interesting:

We need to be able to tell when two objects are colliding, but not have the collision simulated by bullet. For example: if an enemy runs into the character, they should pass through each other but the collision should still be detected so we can catch it and run logic (like take damage).

What I found when using the simple collision groups and masks was that collisions that were filtered out no longer appeared in the btPersistentManifold -- they simply did not occur at all. I tried implementing my own btDispatcher class, like Dragonlord, but I found the culling occurred before the needsCollision or needsResponse methods -- no help.

My question, then, is where in the process do the masks get calculated and collisions filtered out, so I can get to it and implement some sort of callback to tell me when that is occurring?
AlexSilverman
Posts: 141
Joined: Mon Jul 02, 2007 5:12 pm

Re: Different kind of reactions with different kind of objects

Post by AlexSilverman »

Jack,

I think the simpler solution is to derive your own dispatcher as you have, but to place anything that needs to collide (or at least have collisions detected) in the same collision group, then just put your own culling code into the dispatcher needsResponse. To me, this seems preferable to implementing a new callback type, or inserting game specific code into Bullet. This may necessitate the addition of another flag or a specific object type check inside your custom dispatcher, since you can't use the Bullet to cull these collisions anymore. I'm doing this same thing, as I need to see if objects collide, but don't necessarily want them to act as though they have.

Another thing to note, and I'm not sure, but my thought is that the Bullet filtering occurs at a higher level (closer to the broadphase than the narrowphase) so if you simply send a notification when an enemy and player collision is filtered and act as though they had collided, you may get some false positives. Again, I'm not positive on this, but it stands to reason, as least to me.

- Alex
jackskelyton
Posts: 32
Joined: Mon Nov 05, 2007 3:52 pm

Re: Different kind of reactions with different kind of objects

Post by jackskelyton »

Alex -- thank you for the suggestion. As it turned out, your idea plus the 2D matrix turned out to be the perfect solution. My custom needsResponse method now looks like this (simplified):

Code: Select all

bool CustomCollisionDispatcher::needsResponse( btCollisionObject *body0, btCollisionObject *body1 ){
   Actor* actor1 = (Actor*)body0->getUserPointer();
   Actor* actor2 = (Actor*)body1->getUserPointer();
   int actor1group = actor1->GetPhysicsObject()->GetFilterGroup();
   int actor1mask = actor1->GetPhysicsObject()->GetFilterMask();
   int actor2group = actor2->GetPhysicsObject()->GetFilterGroup();
   int actor2mask = actor2->GetPhysicsObject()->GetFilterMask();

   if (!((actor1group & actor2mask) == actor2group)) {
      // if actors are not supposed to collide, here's where the code will catch it. put logic here.
      return false;
   }

   if(!btCollisionDispatcher::needsResponse(body0, body1)) {
      return false;
   }
   return true;
}
I make use of the same bitfield comparison that the bullet code does, but rather than registering the groups and masks with the btDynamicsWorld and letting bullet process the collisions, I follow your (Alex's) suggestion and store them as member variables inside my "actor" class, and do the comparison myself in this method. The result is that the objects collide as they should according to bullet, but I can catch pairs that should not simulate collision and break out of the needsResponse method after I do whatever callback or logic I need to on them. Thanks, Alex and Dragonlord!
Oogst
Posts: 25
Joined: Thu Apr 10, 2008 11:19 am
Location: Utrecht, Netherlands
Contact:

Re: Different kind of reactions with different kind of objects

Post by Oogst »

I am wondering a bit about the implications of what I read here for performance. Do I get it right that if two objects are in different groups, they are checked for collision in the broad phase and are ignored only in the narrow phase? That seems potentially inefficient in comparison to what ODE does there. In ODE, there are dSpace collision object containers. The user can make as many dSpaces as he wants. The user than requests ODE to do collision detection within a dSpace internally, or between two dSpaces (so each object from dSpace A is collided with each object from dSpace B, but objects from A are not collided with other objects from A).

Potentially, this seems a lot faster, as objects that cannot collide do not take any performance at all, but I do not know how important it would be in practice.

Also, this allows for any number of groups, instead of the maximum of 32 groups allowed by integer bit masks.
Post Reply