ODE Collision Event Sample

cute demo!
FYI I’m experiencing this error from console:

:audio(error): _channel->setVolume(): The specified channel has been reused to play another sound.

but I hear the sounds nonetheless.
I read somewhere you hinting to shift to OpenAL though - how can I check and shift to it just in case?

Oh, I just didn’t bother to do anything about that. It just means more sounds are playing at the same time than the sound system can handle.

To switch, edit Config.prc file, replace “p3fmod_audio” with “p3openal_audio”, but I don’t think it will fix anything really.

Yes, it does.

I use only one hash space. I just find that if I use Simple Space, the performance is much better. Do I make a wrong assumption about using hash space ?

Dunno. Maybe you should consider using more than one space. You can reparent spaces to spaces, did you know that? Kind of like octreefying.

as you said it changed nothing
maybe the ‘error’ tagging is a little misleading and should be lowered to just a ‘warning’ I guess
anyhow now I switched to openal

Thank you for the tips. I will experiment more later to see where is the bottleneck.

Ok, just finished pyweek and I used the collision event callback, I was however struggling to get a reasonable framerate, and this callback was consuming about 25ms of frame time. (Compared to 10ms for physics and another 30ms for AI. Rendering was negligible. As you can work out, my framerate sucked.) As you can appreciate, this was a major problem - I spent the last few hours trying to optimise it all, and still uploaded a horrifically slow game. Thing is, I only did anything with the collision event in a few cases, so few in fact that it had no effect on framerate when such scenarios were happening. Put simply there needs to be a way of making the callback only happen between classes of objects - I’ld add an integer to each geom and allow you to specify a callback for each combo of integers. (i.e. I support what clcheung has said.) (At the c++ level - the time taken per callback in python even if it does nothing is silly.)

Other points:

The ability to get the id for a body/geom is essential. The only way to find out what an object is and hence if you care about its collision is to look them up in a dictionary via str(body). Doing that for every collision event is horrific. In fact, the best optimisation I achieved for my game was to encode the classes of objects using get/setAutoDisableLinearThreshold (With the auto disable system off of course.), so I could test if I cared about a collision whilst avoiding that dictionary lookup.

Calling getBody*() on the object given to the event can return a body with id 0, which is bad, yet there is no way of testing if you have a bad body.

The getData()/setData() methods of OdeBody are broken, or at least don’t do what I had hopped - I had assumed I could store something in there so I could find out what my object type is. OdeGeom really needs something similar as well.

Isn’t that what the category and collide masks are for?

All right, you’ve convinced me. There always has been a get_id method, but it returns a dGeomID and is thus not exposed to Python. I’ll look into it.

Not true. There are two ways (I believe 1.6.1 and later). (1) An OdeBody will evaluate to False if its invalid and (2) body.isEmpty() will return True.
So:

body = methodThatReturnsABody()
if body:
  print "valid body"
else:
  print "invalid body"

getData/setData just stores a PyObject pointer on the underlying dGeomID. If there’s no function on OdeBody for that, it’s ODE’s fault - we can’t do anything about that.

I think it is similar to my previous question. For example, my previous ODE car demo:
youtube.com/watch?v=a5Gy578goIQ

I want to know when the collision happens:

  1. The car body hit the wall
  2. The car body hit the boxes, spheres

But I don’t want to know the details

  1. The car wheels hit the ground
  2. The box and spheres hit the ground

There are too many events fed back and a filter mechanism may help the situation a bit ?

I want a way of specifying that things collide but don’t create a callback event when they do - category/collide masks decide if a geom collides, but I want the ability to create objects that collide without creating a callback event. With that I wouldn’t need a lite version of my pyweek entry, and could probably have even more protesters as it would save 25ms per frame! (I know this is probably not possible in the current ode wrapper, but seeing as the wrapper needs to change anyway when it does support for this would be valuable, and is just one more reason why it needs re-factoring.)

Ah, missed those OdeBody methods - must of been too tired at the time. Have been a few late nights these last few days:-)

I need to be clearer on the get/setData() methods - they don’t exist in Python - there in the c++ source as clear as day, and in the manual, but not exported to be used - something going wrong with interrogate I guess, probably these method definitions not being something it knows how to export. But they would be very useful and at least mitigate some of the other problems.

Edit: Clcheung is exactly right - I am just repeating his request for the same reasons.

Another thought - the current version of the API Reference on the website is out of date due to some changes you (pro-rsoft) made to ode between 1.6.0 and 1.6.1 - don’t how much effort it is to regenerate but it would be convenient:-)

Hmm, any thoughts how such a filter system would work, then?

I’ll look into the setData functions - remind me if I forget, have so much things on my mind. If you have time, consider adding a bug report on launchpad, that is easier for me to keep track of.

I simply forgot to update the API reference. I’m uploading it right now.
Remember that functions returning python-lists (like OdeCollisionEntry.getContactGeoms and NodePath.getChildren) don’t show up because of a bug in the API generator.

From an interface point of view the way I would do it is to assign a number to each geom, then you would have a register callback method where you specify the two numbers as well as the event to send, i.e. have
geom.setCallbackIdent(int) (Plus a get)
space.setCollisionEvent(ident1,ident2,event name)
and probably
space.disableCollisionEvent(ident1,ident2)

(ident1 and 2 could be the same of course, and you could register multiple combos with one event name.)
(This behaviour isn’t that different from the material properties system that already exists for ode, except there you specify the largest number you will have, presumably so its hash table can be a simple 2D array. The same could be done here - would reduce flexibility a little but make implementation easier and execution slightly faster. Its not like you would typically have more than a few anyway.)

Implementing it internally would then involve having the C++ callback code lookup which event name to use given the idents of the geoms involved, and not creating an event if no name is set; problem is that you need to store the ident for each geom and the ident pair to event name hash. I presume each space is already being mirrored by some Panda object, so you probably already have somewhere to store the hash table.

The geom number is bit harder however, but I observe that dGeomSetData and dGeomGetData are not exposed by the Panda API (Unlike their cousins for the body.), nor does it matter so much if they aren’t exposed, as you only really care about that for bodies and will have a backup once you can get the id numbers. So you could turn that integer into a void* and store it there. Of course, at some point in the future I image the ode code being revamped, so every ode object is mirrored by an object in Panda, at which point that variable can be moved over and stored properly.

P.S. Thanks for updating the API reference. I’ll add a bug report to launch pad.

Hmm. I think adding an ident number like that would make it even more confusing.
What about storing geoms? setCollisionEvent(geom1, geom2, eventname) ?
Panda’s native CollisionHandlerEvent works by specifying a pattern for events, e.g. into-%s-%s, but I can’t do that here since geoms have no name.

Would it speed up anything if I only threw events when the object started colliding and stopped colliding? (the latter would be kinda hard though)

Also, does the performance drop also occur when nobody is receiving the event?

About the dGeomSetData/GetData methods, I know why they aren’t there.
There’s already OdeTriMeshGeom.setData which sets the trimesh data - so if I added OdeGeom.setData, it would be overridden with something entirely different. So I don’t think it’s a good idea to add it there until we rename the OdeTriMeshGeom function.

Even if an object rest on the ground, the collisions still happening regularly. What do you mean by “starting colliding and stopped colliding” ?

Yes.

How about:

If I want to know all collisions happens to geom1 (e.g. car body):
setCollisionEvent(geom1, None, eventname)

If I want to know collisions between geom1 and geom2 (e.g. car body and the wall):
setCollisionEvent(geom1, geom2, eventname)

But it will be a problem if it is for a pool game. I want to know all collisions between balls, but not collisions between ball and the pool table.
If there are 20 balls, I have to make 400 calls…although it can be easily done in program, would it be any better implementation ?

How about using collidebits / categories ?

Well, doing it by the geom wouldn’t work - just take my pyweek game - 250 police, 192 protestors - I’ld have to call that method 48000 times, resulting in 48000 entries in a hash table. I think the number approach is the only reasonable solution, especially as ode already uses the exact same interface for handling materials - each geom is assigned a material number, you then provide friction/bounce etc properties for each possible pairing of material numbers.

Of course, numbers are basically just a limited form of name, and you could have hash tables from number to name and name to number and hide this internal representation, but I honestly think that anyone doing physics programming is going to get the idea of using numbers this way. (Plus you could do that abstraction of converting the names to numbers in python to abstract it - that is exactly what I’ve done for materials in that project of ours, so you can use names in the config files.)

I don’t know about the performance drop, not in a position to test it right now - it certainly happens if the event handler is empty. I’ll get back to you on that latter, but I expect the answer is yes - there is defiantly a large overhead with using it, and it feels too large to be the event system alone.

When an object rests on the ground, it’s still colliding, since with ODE, objects in rest are always a tiny bit inside each other, with a distance that is defined by the timestep (that’s the reason why there’s jitter if the timestep is nonconstant).
So, I could make it throw an event when the object falls on the ground (starts colliding) and one when it stops colliding (no longer lying on the ground). How about that?

Yeah, I’m leaning towards having a setCollideEventMask, and it will only throw the event if the mask set here matches the collided object’s masks.
lethe, how about that?

I’m not so sure about detecting when objects start/stop colliding - its defiantly something that you want to be optional at any rate. Imagine an object sparking as its dragged along the ground for instance - you will need to know all collision points every frame to instance the sparks. (Plus it could cause problems with non-standard collision setups - for instance I intend to create an automatic AI node creation algorithm for that project which I won’t name that runs the physics system repeatedly in entirely separate simulations to find all the possible nodes in a level, before pruning. You would effectively need some kind of reset method. There is also an issue if you have saved a level by storing all the velocities etc. for each object and then re-initialise the system - every object that was colliding before will now get an event which wouldn’t of existed had you not saved, resulting in divergent behaviour.)

With masks that could work - it depends on the specifics of implementation - you will want to support groups of objects which don’t throw events when they collide with each other but do throw events when they collide with another group. (Though how that is simpler than id numbers I don’t know…) The reason I discounted that solution is you would want to be consistent with the rest of ode - and that uses two masks in a symmetric arrangement - problem is your storage in a geom is limited to one bitmasks worth (Size of a void*), unless you point at a block of memory you allocate for each geom… except you don’t know when each geom is destroyed, and hence cant deallocate that block.

Well, there’s a big problem with making a new data member: where would we store it? We could store it using setData (and store a struct that stores both the collide-event-id and the PyObject*) but that would be hacky and complicated.
So that’s why I prefer to let it use an existing ODE data member.

Thoughts?

I don’t think there is an existing data member you can use except for that void* pointer - that is 32bits of data per geom (64 on a 64 bit computer of course.), hence why I’m leaning towards solutions where that is enough. Its a shame that ode doesn’t have something like this built in to be honest, but then ode is usually accessed via c++, and so its not so much of an issue.

Well, we can cast that void pointer into any pointer we want, so we can store as much as we need, not just 32/64 bits. :slight_smile:
But still, this makes the user unable to set data himself. Unless we make a struct containing both the collide-id and the PyObject*, and storing a pointer to that struct in that void*. This is kinda hacky though.
Or I could store a lookup table on the odeSpace with the dGeomID pointers and corresponding collide-ids.

So I’m still leaning towards using the collide/category masks. We can create a special event-mask on the space. Then, on a collision, we take the result of the mask check (I think thats category1 & collide2 | category2 & collide1 or so) and match that against the event-mask.

Still, I’m really wondering why the event system is so slow. I thought throw_event was a pretty fast operation. Maybe copying dContactGeoms around is slow. I’ll look into making that operation faster.

PS. Note that theres still OdeSpace.collide which accepts a near_callback function pointer, which is probably faster for advanced use.