Open ghost opened 10 years ago
I would also be interested in this functionality.
I would also like to see this feature or some documentation how to do it!
Since you are asking for a workaround, maybe my simple trigger is interessting for you. There is no End-Overlapping-detection Im just resetting with setTimeout(). Maybe this could be done with a second trigger object surrounding the actual trigger. Live example Weapon pickup, ammo refill (please dont judge my code :hamster: )
if ( manifold.getBody0() === fps.body.a && manifold.getBody1() === trigger1.a ) {
if ( onTrigger === false ) {
sound3.play();
onTrigger = true;
magazine++;
setTimeout(function(){ onTrigger = false; }, 1000 );
}
}
What's wrong with using btCollisionWorld::contactPairTest?
Anyway, I don't think it's possible to use these callbacks in Ammo as there's no way (that I know of) to export global variables to js. You could add some code to bullet though, do your processing in c++ and only export the results. Here's how I managed to (crudely) use gContactAddedCallback in my branch:
https://github.com/Kuang11/ammo.js/commit/d8478a9a261cb7ede563ab8771ca7cd2710111cc
My current workaround: Flag all JS collision objects as separated. Check Ammo for current collisions. If they are still connected, flag them as not separated. If a new collision is detected, create a JS collision object for those two bodies, call the 'collision begin' function on the objects, passing in the JS collision. Any collisions which are still flagged as separated, call their 'collision end' function and delete the JS collision object.
It is kind of like a 'stay connected' ping in netoworking.
It works ok, but fails in situations like if a ball is rolling on a ground, there are constant collision begin / ends.
I haven't looked at contactPairTest, will look into it now. Perhaps I could use that after the initial collision, to determine if they are still colliding.
I don't suppose there is a way to show how to use the webidl bindings approach to override/write custom callbacks, since the "customizeVTable stuff is tricky and deprecated". Is there an example I am overlooking?
Awesome, I can see how to do it now, but what about global scope functions, which aren't part of a class? Is it the 'GlobalObject' class? We would just add an interface for GlobalObject and add gContactProcessed / DestroyedCallback as functions? I'm talking about something like thecplusplusguy does here: http://youtu.be/YweNArzAHs4?t=7m40s
Not sure what is the relevant part of the video - is there a text code sample of this?
I guess we're supposed to be able to create a ContactAddedCallback function, something like:
function callbackFunction (manifoldPoint, bodyA, idA, indexA, bodyB, idB, indexB){ // do something with manifoldPoint, bodies, etc here. }
Then to use this callback function, we need to set it to a global variable:
gContactAddedCallback = callbackFunction;
So if one object hits another, it will call that callback function, this way we don't need to iterate over all the contact manifolds and do it manually. For example, if objectA has its CF_CUSTOM_MATERIAL_CALLBACK flag set, and it collides with another object, the 'callbackFunction' will be called when those two objects collide.
Supposedly there are two more callbacks:ContactDestroyedCallback and ContactProcessedCallback.
http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?p=7417&f=&t=#p7417
I'm just not sure how to add this to the IDL.
Are you saying bullet has a global variable that you need to set? That seems like an odd API for them to use...
The way to support this here is probably to add a static method to some class,
class btBulletSomething {
static setCallback(.. callbackFunction) {
gContactAddedCallback = callbackFunction;
}
}
then add that method in idl.
can anyone share implemented gContactProcessedCallback ?
was this solved? Is there a way/example of using gContactProcessedCallback ?
Hey everyone. I just wanted to share a pretty solid solution I have. I can't send a pull request, because you need to make a small change in the Bullet code. This info assumes you have a working build environment, and can recompile Ammo.js from the Bullet sources.
In Bullet, in the file btDiscreteDynamicsWorld.h add
void setContactAddedCallback(unsigned long callbackFunction) {
gContactAddedCallback = (ContactAddedCallback)callbackFunction;
}
void setContactProcessedCallback(unsigned long callbackFunction) {
gContactProcessedCallback = (ContactProcessedCallback)callbackFunction;
}
void setContactDestroyedCallback(unsigned long callbackFunction) {
gContactDestroyedCallback = (ContactDestroyedCallback)callbackFunction;
}
In Ammo, in the ammo.idl file add
interface btDiscreteDynamicsWorld {
void setContactAddedCallback(long funcpointer);
void setContactProcessedCallback(long funcpointer);
void setContactDestroyedCallback(long funcpointer);
...
}
In make.py, add -s RESERVED_FUNCTION_POINTERS=20 to the array _emccargs. In my project, that's on line 29, but might have changed since I forked.
Go ahead and build Ammo.
Finally, there are a few hoops to jump through in the JS. Note that you'll have to manually cast the incoming pointers to the proper type. I've made an example here for gContactProcessedCallback, and you can probably figure out the others from the Bullet docs.
function collisionCallbackFunc( cp,colObj0,colObj1)
{
colObj0 = Ammo.wrapPointer(colObj0, Ammo.btRigidBody);
colObj1 = Ammo.wrapPointer(colObj1, Ammo.btRigidBody);
cp = Ammo.wrapPointer(cp, Ammo.btManifoldPoint);
//trigger your events.
}
var collisionCallbackPointer = Ammo.Runtime.addFunction( collisionCallbackFunc);
var dynamicsWorld = new Ammo.btDiscreteDynamicsWorld(...);
dynamicsWorld.setContactProcessedCallback(collisionCallbackPointer);
Finally, don't forget to set CF_CUSTOM_MATERIAL_CALLBACK on all bodies. We have hard coded this like so, wherever we set the flags
this.body.setCollisionFlags(this.collisionFlags | CF_CUSTOM_MATERIAL_CALLBACK);
FYI the value of CF_CUSTOM_MATERIAL_CALLBACK is 8.
This solution is working great at Virtulous. Before, the only way to get consistent collision notifications was to test the manifolds after each simulation step. If there were more than a single substep, you could end up missing contacts that were created then destroyed within a single step. So, we had to choose between physics accuracy and getting the callbacks.
I wanted to reach out to determine if this functionality has been added to the latest AmmoJS build before going through the previously mentioned process. Does anyone know if this has been added?
glad I'm not the only one interested in that feature :+1:.
I tried recently to apply what was said by @Virtulous and failed to make it work.
I believe I did every steps in the right order, multiple unsuccessful attempts so far.
my final issue is that dynamicsWorld.setContactProcessedCallback
stay undefined, even if I'm able to find it in the ammo.js generated.
I'd gladly contribute to the repo if I ever get it to work (spoiler: I would need assistance to do that).
in the meantime, if anyone have a clue why it might fail on my side so far? I believe it should work :|
Hey, I'm the Virtulous guy. Sorry that the process did not work. I tried to document as carefully as possible, but it was a few years ago and I can't say if the same steps work with current builds. If you email me, I can give you my compiled version of the engine with this feature enabled..
Hey, cheers for the answer :).
tbh I ended up making it works a while ago (mid-May or after).
The problem was the emscripten sdk installed on my machine, slightly outdated, there was a few bugs present with this old version that would not make it work at all if I remember correctly (I think it was addFunction that misbehaved).
Anyway, I updated the emscripten sdk and also had to refresh a bit how to proceed with the advices you kindly shared in here, I think the whole method to achieve this changed quite a bit with the latest version I decided to go for.
I believe to have noted the steps to achieve this somewhere.
Should anyone else be struggling for this I can always try to: -> dig it up, make it work again and share the steps here. -> then I should ideally repeat it with the latest emscripten sdk released and share the result as well.
I'll do my best to help if it's needed. I also won't lie about the limited amount of free time I can spend on this (life's too short).
I've implemented the fix and I have ContactAddedCallback working, but ContactDestroyedCallback never calls. I've tried to figure out any possibility, but I'm unable to. It's being done the same way as ContactAdded everywhere, so I don't see any reason why it shouldn't work unless there's some other issue. The code I'm using is below:
function contactDestroyedCallback(cp_ptr, bodyA_ptr, bodyB_ptr) {
const bodyA = Ammo.wrapPointer(bodyA_ptr, Ammo.btRigidBody);
const bodyB = Ammo.wrapPointer(bodyB_ptr, Ammo.btRigidBody);
const cp = Ammo.wrapPointer(cp_ptr, Ammo.btManifoldPoint);
console.log('Collision end', bodyA, bodyB, cp);
}
const contactDestroyedCallback_ptr = Ammo.addFunction(contactDestroyedCallback);
this.world.setContactDestroyedCallback(contactDestroyedCallback_ptr);
@GuillaumeBouchetEpitech did you manage to get this callback working? If so, how/what am I doing wrong?
@MrSonicMaster It worked for me.
I think I see where your problem might be, I solved in C++ and dispatched event in JS from it.
Let me try to explain:
by default bullet physic in C++ possess 3 global callbacks:
gContactAddedCallback:
gContactProcessedCallback
gContactDestroyedCallback
When gContactProcessedCallback is called: 1: check if the callback a "begin" or an "updated" one 2: to do so check the value of "contactPoint.m_userPersistentData" 3: BEGIN COLLISION -- 1: if "contactPoint.m_userPersistentData"is NULL then it's a "begin" collision -- 2: you must set the value (of m_userPersistentData) to something useful -- 3: the value should be something you can use later to recognise the collision -- 4: ideally the value would also be stored in a container to look it up (I used a "Map"). -- 5: beyond that I just dispatched a JS event "beginContact" to the related bodies 4: UPDATED COLLISION -- 1: if "contactPoint.m_userPersistentData"is NOT NULL then it's an "updated" -- 2: you must use the value (of m_userPersistentData) to identify which collision is concerned -- 3: this callback is called often so I had to check if the position and normal changed a bit ---- 1: the position is held in "contactPoint.m_positionWorldOnB" ---- 2: the normal is held in "contactPoint.m_normalWorldOnB" -- 4: beyond that I just dispatched a JS event "updateContact" to the related bodies
When gContactDestroyedCallback is called: 1: you must use the value (of m_userPersistentData) to identify which collision is concerned 2: here it's a good idea to clear the collision from the container to avoid a memory leak (the "Map") 3: beyond that I just dispatched a JS event "endContact" to the related bodies
It might help you for now, I will try to see if I can share some code at a later date, I might be able to make a contribution to ammo.js as well if it's not a problem, will see.
Hello! Thanks for the response. I've added m_userPersistentData to the webidl bindings and was able to get destroy to correctly be called by calling point.set_m_userPersistentData()
. That being said, the value of m_userPersistentData
is always 0 the collision processed callbacks. I'm unable to detect a 'begin' vs an 'update.' Any ideas why that might be and how I might go about solving this?
It mght depend on how you are seeting the value of m_userPersistentData
.
I'm not sure I can help a lot on this predictable (frustrating) issue as I made it work in C++.
Meaning that in my case:
=> the value of m_userPersistentData
was a C++ pointer and not a Js reference
=> the pointer was (void*) was litteraly a casted integer and I just used the value as an collisionID
=> then I setup a std::map to store a custom structure holding the collsion data as value and the collisionID as key
=> from here I just call the Js callback (your addFunction part)
NOTES:
the difficult part here might very well be that gContactProcessedCallback
is a global callback from bullet so you can't use attributes from a class unless those are static attributes, meaning the std::map I mentionned need to be either: a globale, a locale variable, a (private?) static attribute; All fr the sake of it being accessible from the inside of the callbacks, same for the addFunction
pointers, those need to be accessible from inside the callback as it's where you may want to use them.
hopefully it help.... if not feel free to ask.
I simply added
attribute any m_userPersistentData;
to the WebIDL bindings for the btManifoldPoint
interface. I was then able to use the auto-generated set_m_userPersistentData()
and get_m_userPersistentData()
functions in the javascript gContactProcessedCallback. Using the set_m_userPersistentData
in the contactprocessed callback, I was able to receive gContactDestroyed callbacks, with the only argument being the value of m_userPersistentData
that I set. All in all, that part seemed to work except for the fact that m_userPersistentData
was always 0 in the ContactProcessed callback. I just decided to create a map in my Javascript code and
This solution seems to work for my purposes. Thanks for the assistance.
I'm experiencing the same issue described here with triangle edges when using a btBvhTriangleMeshShape. The fix described there requires use of gContactAddedCallback to modify the contact normal. I'm having issues with the 2 collision objects. Only one of the pointers is resolved to the correct object, the other one is not. Code is as follows:
function contactAddedCallback(cp_ptr, colObj0_ptr, partId0, index0, colObj1_ptr, partId1, index1) {
const colObj0 = Ammo.wrapPointer(colObj0_ptr, Ammo.btCollisionObject);
const colObj1 = Ammo.wrapPointer(colObj1_ptr, Ammo.btCollisionObject);
const point = Ammo.wrapPointer(cp_ptr, Ammo.btManifoldPoint);
console.log(cp_ptr, colObj0_ptr, partId0, index0, colObj1_ptr, partId1, index1);
this.fixCollision(point, colObj0);
this.fixCollision(point, colObj1);
return true;
}
const contactAddedCallback_ptr = Ammo.addFunction(contactAddedCallback.bind(this));
this.world.setContactAddedCallback(contactAddedCallback_ptr);
in fixCollision, I'm doing the logs console.log(shape, shape.getShapeType());
and it logs the correct shape and shape type for the 1st one, but a null pointer shape for the 2nd one. The shape type for the 2nd one should be 21. Any ideas why?
I also logged the pointers for each and nothing looked out of the ordinary to me - 5271572 28216 1121641784 1111238624 28416 0 14811
Glad it worked for you as well then.
On this latest issue of yours, I'm afraid it's beyond my current knowledges. I can try to help if you provide a simple example that help reproduce it but it would mean sharing (part of) your code. I'd welcome any update on your progress on the matter othwerwise ;).
Ah! Maybe I am not experiencing your latest issue as I might use a different version of bullet as well and probably not the one shipped with ammo.js.
That's what I used last time I needed bullet's latest source code
git clone https://github.com/bulletphysics/bullet3
cd ./bullet3/
git checkout tags/2.87
I assume you updated the webidl bindings yourself as the bindings shipped with this repsitory don't work with bullet3 yet. I'll update them when I get the time, I've been busy working on more important parts of my project before I get to fixing the triangle edge issue I talked about. Too bad I've got no way of knowing if bullet3 will fix my issue or not.
I do not wrap bullet but use it with C++ and got some kind of custom wrapper above it (some form of ammojs duplication that expose a few bullet "hidden" and "challenging" features). It's still not ready to be shown but looks promising (as far as I can tell anyway).
I'm happy to help but you will need to provide a working source code that reproduce your issue ;).
I have been searching for the last couple days, and can't seem to find out how to tap into these two Bullet callback functions.
I am trying to create some 'collisionBegin / collisionEnd' and 'triggerEnter / triggerLeave' type functions, and so far I have only been able to get the initial contact to work using the dispatcher manifolds.
Is there any way to determine if two objects, which were previously colliding, is no longer colliding? From what it looks like, I should be able to set a global 'ContactProcessedCallback' to detect when two objects collide, and a global 'ContactDestroyedCallback' for when the separate, but there is really no information on how to do this with Ammo.
If there is no way to do it with these callbacks, I would really be interested in a 'work around' on how to do it otherwise.