Different kind of reactions with different kind of objects
-
- Posts: 1
- Joined: Mon Jan 15, 2007 5:18 pm
Different kind of reactions with different kind of objects
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.
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]
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]
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Different kind of reactions with different kind of objec
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:
By default, Bullet uses this to disable collisions between static objects:
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
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;
}
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
[/quote]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.
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
-
- Posts: 32
- Joined: Mon Nov 05, 2007 3:52 pm
Re: Different kind of reactions with different kind of objects
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?
-
- Posts: 141
- Joined: Mon Jul 02, 2007 5:12 pm
Re: Different kind of reactions with different kind of objects
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
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
-
- Site Admin
- Posts: 4221
- Joined: Sun Jun 26, 2005 6:43 pm
- Location: California, USA
Re: Different kind of reactions with different kind of objects
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.
Let us know if we need different functionality.
Hope this helps,
Erwin
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;
};
This would work, but is a bit more complicated.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.
Let us know if we need different functionality.
Hope this helps,
Erwin
-
- Posts: 141
- Joined: Mon Jul 02, 2007 5:12 pm
Re: Different kind of reactions with different kind of objects
Erwin,
- Alex
Yeah, I can see that. I'm definitely going to be working on the btOverlapFilterCallback. That seems much more straightforward.Erwin Coumans wrote:This would work, but is a bit more complicated.
- Alex
-
- Posts: 456
- Joined: Tue Dec 25, 2007 1:06 pm
Re: Different kind of reactions with different kind of objects
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:
2) Subclass btDiscreteDynamicsWorld to change the "plain" addRigidBody(...) method:
3) Now the rigid bodies can be added to the subclassed world like this ( code pasted in a hurry, hope it works ):
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...
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
};
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);
}
}
};
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
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...
-
- Posts: 198
- Joined: Mon Sep 04, 2006 5:31 pm
- Location: Switzerland
Re: Different kind of reactions with different kind of objects
Don't know if this helps or if I strangle here Bullet but I'm doing custom collisions like this:
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.
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
}
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.
-
- Posts: 32
- Joined: Mon Nov 05, 2007 3:52 pm
Re: Different kind of reactions with different kind of objects
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?
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?
-
- Posts: 141
- Joined: Mon Jul 02, 2007 5:12 pm
Re: Different kind of reactions with different kind of objects
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
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
-
- Posts: 32
- Joined: Mon Nov 05, 2007 3:52 pm
Re: Different kind of reactions with different kind of objects
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):
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!
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;
}
-
- Posts: 25
- Joined: Thu Apr 10, 2008 11:19 am
- Location: Utrecht, Netherlands
Re: Different kind of reactions with different kind of objects
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.
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.