Physics Simulation Forum

 

All times are UTC




Post new topic Reply to topic  [ 6 posts ] 
Author Message
PostPosted: Thu Dec 29, 2011 10:13 pm 
Offline

Joined: Fri Oct 20, 2006 12:08 am
Posts: 3
For my purposes, I want two very simple collision callbacks: CollisionStarted(objA, objB) and CollisionEnded(objA, objB). I got tired of Bullet's seemingly convoluted, quirky methods for detecting what collisions have happened. Iterating over the manifolds doesn't tell you whether a collision is new or not, and can't yield the correct events as a result. Contact processing is exposed in a very confusing way, and ghost objects are a hassle. So I dropped into the source and wrote up a new set of callbacks that work the way I want. The modifications are trivial, yet immensely helpful IMO.

This is how it looks in client code:
Code:
   //set the callbacks somewhere
   gCollisionStartedCallback = CollisionStarted;
   gCollisionEndedCallback = CollisionEnded;

static void CollisionStarted(btPersistentManifold* manifold)
{
   btCollisionObject* obA = static_cast<btCollisionObject*>(manifold->getBody0());
   btCollisionObject* obB = static_cast<btCollisionObject*>(manifold->getBody1());
   PhysicsComponent* phyA = (PhysicsComponent*) obA->getUserPointer();
   PhysicsComponent* phyB = (PhysicsComponent*) obB->getUserPointer();
   if(!phyA || !phyB)
      return;
   
   printf("Collision started between: %s, %s.\n", phyA->_parent->Name.c_str(), phyB->_parent->Name.c_str());
}

static void CollisionEnded(btPersistentManifold* manifold)
{
   btCollisionObject* obA = static_cast<btCollisionObject*>(manifold->getBody0());
   btCollisionObject* obB = static_cast<btCollisionObject*>(manifold->getBody1());
   PhysicsComponent* phyA = (PhysicsComponent*) obA->getUserPointer();
   PhysicsComponent* phyB = (PhysicsComponent*) obB->getUserPointer();
   if(!phyA || !phyB)
      return;
   
   printf("Collision ended between: %s, %s.\n", phyA->_parent->Name.c_str(), phyB->_parent->Name.c_str());   
}

That's it. You get a single notification when a collision starts, no matter what is happening with the contacts. You get another notification when the collision ends. In my code I walk back into my own object hierarchy, but you could conceivably do whatever since the whole manifold is present and persistent.

The changes to Bullet are a mere handful of lines, and attached to this post as a patch. The logic is dead simple: If a contact is added to a manifold with zero contacts, trigger gCollisionStarted. If a contact is removed leaving a manifold with zero contacts, trigger gCollisionEnded. Personally I think this is what most people want. This may not work well if you need all of the contact points, as the collision started callback triggers as soon as a single point is available.

I'm posting this here first in case I did something incredibly stupid or unnecessary, but all of the stuff I've seen in the forums and documentation is waaaay more of a PITA than what I've put together here. If this is something useful and productive, I'll probably publish on GameDev and my blog, dunno if it's appropriate for inclusion into Bullet main-line.


Attachments:
File comment: Promit's custom patch to bullet for collision callbacks.
promit-bulletcollisions.txt [2.52 KiB]
Downloaded 162 times
Top
 Profile  
 
PostPosted: Sun Jan 01, 2012 9:43 am 
Offline

Joined: Tue Dec 25, 2007 1:06 pm
Posts: 316
Short version:
I think these callbacks can be useful in many cases, and I would appreciate their introduction in the Bullet library if it doesn't break something existent.
I would add a CF_MANIFOLD_CALLBACK collision flag (analogous to CF_CUSTOM_MATERIAL_CALLBACK) to filter the objects that need it, and probably change the name of the callbacks so that it's clear that they provide access to a persistent manifold (something like: gManifoldStartedCallback, gCollisionManifoldStartedCallback?).

Long version:
I've read that you're going to publish something about this. Could you please help me to understand the topic a little deeper ?
This is what I've understood about it so far (based mainly on what I think it is, not on what I'm sure it is, since I've tested only about 50% of what I'm going to describe here...).

As far as I can understand with your contribution there are 3 ways commonly used to get collision information in Bullet:

1) gContactAddedCallback/gContactDestroyedCallback (and maybe gContactProcessedCallback?).
USAGE:
a) CF_CUSTOM_MATERIAL_CALLBACK enables gContactAddedCallback.
b) Inside gContactAddedCallback set cp.m_userPersistentData!=NULL.
c) Now gContactDestroyedCallback is called.
PROS:
-> Point (a) on its own is used in tons of demos for custom friction/restitution, for the internal edge problem fix and for detecting the index/partId of the colliding parts of the meshes/shapes.
-> Extra flexibility due to cp.m_userPersistentData.
CONS:
-> Plenty of forum posts state that the cp.m_userPersistentData trick is not reliable enough or works only with some special solver configurations (just search the forum and see it).
-> In some cases users are interested not much in the creation and destruction of single manifold points, but in the overall collision evolution between two btCollisionObjects (I mean: when a btManifoldPoint is created it does not mean that two bodies were not colliding before, and when it's destroyed it does not mean the objects are not in contact anymore). (*)
-> Some parameters of the btManifoldPoint (like m_appliedImpulse) are not usable here.

2) Your approach (I'd extend it with a collision flag to filter which object needs these callbacks).
USAGE:
-> CF_MANIFOLD_CALLBACK enables gCollisionStartedCallback/gCollisionEndedCallback (I'd call them something like: gManifoldStartedCallback or gCollisionManifoldStartedCallback and so on...).
PROS:
-> In the cases described above at point (*), this solution improves the contact results, so that in most cases these callbacks can be considered the 'correct' callbacks to use for handling total collision start / end events between two objects.
-> Easy to implement.
CONS:
-> Needs modification to the Bullet source code.
-> More work is needed if the user needs access to manifold points (but if the manifolds are persistent, maybe the user can store them somewhere after the start callback and keep using them until the end callback... or not?).
-> As far as I understand (but comments are welcome) the problem at point (*) is not completely solved with this approach: If an object is compound shaped and collides with another object with MORE than one of its child shapes, two contacts manifolds are created for the same pair of objects (each can contain a maximum of 4 contact points).

3) Manually iterating all the contact manifolds.
USAGE: Basic code
Code:
const int numManifolds = btDynamicsWorld->getDispatcher()->getNumManifolds();
   for (int i=0;i<numManifolds;i++)
   {
       btPersistentManifold* contactManifold = btWorld->getDispatcher()->getManifoldByIndexInternal(i);
         btCollisionObject* objA = static_cast <btCollisionObject*> (contactManifold->getBody0());
         btCollisionObject* objB = static_cast <btCollisionObject*> (contactManifold->getBody1());   
         for (int j=0;j<tcontactManifold->getNumContacts();j++)   {
           btManifoldPoint& pt = contactManifold->getContactPoint(j); 
                if (pt.m_distance1<0) {
                // Valid contact point
                }
        }
  }
         

PROS:
-> You got all the possible collision data available in Bullet and you can build a system to solve problem: (*).
-> Flexibility: you can use only the pieces you need and discard the others.
-> I believe this solution is recommended in the Bullet docs.
CONS:
-> It's difficult to build an automatic way to fire events with this approach and such a system can't be very fast (I posted one in an old post on this forum, but it's a bit slow (I calculated an 'average contact point' every frame to feed the callbacks and I had to mantain two lists of colliding object pairs and to compare them to fire started/ended events)).

I hope somebody can correct possible mistakes.
PS: I know that technically an event is something different from a callback, but I've used these terms as synonyms above.


Top
 Profile  
 
PostPosted: Sun Jan 01, 2012 9:54 pm 
Offline

Joined: Fri Oct 20, 2006 12:08 am
Posts: 3
That's all consistent with my understanding, at least, but I am not an expert here. I do agree that a flag would be useful to filter the callbacks.


Top
 Profile  
 
PostPosted: Tue Jan 17, 2012 11:45 pm 
Offline

Joined: Wed Apr 26, 2006 6:22 pm
Posts: 13
Promit,

Nice elegant modification. I was about to write something similar and thought I would check to see if anyone had solved this issue. Your solution to this problem works very well. The most useful being that constantly embedded objects don't spam the collision events, something that I had going on with objects interacting with landscapes but still needing events.

thank you

-andi


Top
 Profile  
 
PostPosted: Tue Feb 14, 2012 11:31 pm 
Offline

Joined: Tue Feb 07, 2012 8:02 pm
Posts: 1
Thanks Promit! This has been fantastic for me, as has the SDK which is awesome after being locked out of a competitor's source. Thanks loads all involved :-)


Top
 Profile  
 
PostPosted: Sat Sep 29, 2012 9:31 am 
Offline

Joined: Sat Sep 29, 2012 9:26 am
Posts: 1
clearManifold() in btPersistentManifold.h must be also changed to catch all "end collision" events:

Code:
   SIMD_FORCE_INLINE   void   clearManifold()
   {
      int i;
      for (i=0;i<m_cachedPoints;i++)
      {
         clearUserCache(m_pointCache[i]);
      }
      
      if(gCollisionEndedCallback && m_cachedPoints)
      {
         gCollisionEndedCallback(this);
      }

      m_cachedPoints = 0;
   }


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 6 posts ] 

All times are UTC


Who is online

Users browsing this forum: xexuxjy and 7 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group