I’m working on an overlap test with Ammo.js, but I can’t get it to work in a PlayCanvas context. I’m trying to write a function that returns an array with all Entities touching or inside a given sphere.
I’ve tried reading the engine source code, to try and understand Ammo.castObject and Ammo.wrapPointer, but I’m a bit stumped. Seems PlayCanvas stores an entity property in its collision objects, I just can’t find them. Perhaps you guys can help me?
This is what I have so far:
const position = new pc.Vec3(10, 0, 0);
const radius = 5;
const ammoVector3$1 = new Ammo.btVector3();
const ammoTransform$1 = new Ammo.btTransform();
ammoVector3$1.setValue(position.x, position.y, position.z);
ammoTransform$1.setOrigin(ammoVector3$1);
const sphereShape = new Ammo.btSphereShape(radius);
const sphereBody = new Ammo.btRigidBody(new Ammo.btRigidBodyConstructionInfo(0, null, sphereShape));
sphereBody.setWorldTransform(ammoTransform$1);
const resultCallback = new Ammo.ConcreteContactResultCallback();
resultCallback.addSingleResult = (cp, colObj0, partId0, index0, colObj1, partId1, index1) => {
// Should I do something like this?...
const body0 = Ammo.wrapPointer(colObj0, Ammo.btCollisionObject);
const pcEntity = body0.entity; // undefined :(
// Or this..?
const rb0 = Ammo.castObject(body0, Ammo.btRigidBody);
const pcEntity = body0.entity; // = undefined, too
};
// this = the app's RigidBodyComponentSystem
this.dynamicsWorld.contactTest(sphereBody, resultCallback);
Agh, hmm, I can’t recall immediately from the top of my head, but I believe you are supposed to cast the collision object to a rigidbody to get the body, which we store in the rigidbody component. Something like:
const body = Ammo.castObject(colObj0, Ammo.btRigidBody);
Edit:
I will check it out later in the evening, if the answer is not found till then.
I have successfully detected the Player and Collision (the floor) entities. Groovy!
Every 2nd entry must be the temporary sphereBody that I add to actually perform the contactTest with. See how its pointer ID is the same in both collision cases.
For good measure, I found another way of doing what I wanted. Just thought I’d share:
pc.RigidBodyComponentSystem.prototype.overlapSphere = function(position: pc.Vec3, radius: number, out: pc.Entity[], collisionFilterMask: CollisionFilters = CollisionFilters.AllFilter): number {
vector3$1.setValue(position.x, position.y, position.z);
transform$1.setIdentity();
transform$1.setOrigin(vector3$1);
const sphereShape = new Ammo.btSphereShape(radius);
const ghostObject = new Ammo.btPairCachingGhostObject();
ghostObject.setCollisionShape(sphereShape);
ghostObject.setWorldTransform(transform$1);
ghostObject.setCollisionFlags(CollisionFlags.CF_NO_CONTACT_RESPONSE);
(this.dynamicsWorld as Ammo.btDynamicsWorld).addCollisionObject(ghostObject, CollisionFilters.AllFilter, collisionFilterMask);
const numOverlaps = Math.min(ghostObject.getNumOverlappingObjects(), out.length);
let outI = 0;
for (let i = 0; i < numOverlaps; i++) {
const obj = ghostObject.getOverlappingObject(i);
const body = Ammo.castObject(obj, Ammo.btRigidBody);
if (body && body.entity) {
out[outI++] = body.entity;
}
}
(this.dynamicsWorld as Ammo.btDynamicsWorld).removeCollisionObject(ghostObject);
Ammo.destroy(ghostObject);
Ammo.destroy(sphereShape);
return outI;
};
btPairCachingGhostObject is from what I can gather, specifically made for this situation - checking for overlaps.
I had attempted a couple of times before to no avail, but in the end it was because I needed to jam this bit into PlayCanvas’ underlying initialization of Ammo.
It requires somekind of internal callback setup. Otherwise the overlap test doesn’t register any bodies.
Maybe once I’m through implementing all these Ammo helper functions, I’ll post them somewhere for people to use. I personally need a bit more than just the two given raycast functions.