Bullet Physics: Query objects within a volume

I need to preform a query of the game world to find all Game Objects within a volume. Preferably a sphere but any convex shape would be good. I struggled with this for a few hours before realising it’s actually pretty easy. Bullet Physics is amazing but sometimes simple things aren’t that obvious.

My first approach was to use a sphere and btCollsiionWorld::contactTest. This workedtm in that when I tested it a few months ago all was well but actually it only looks for collision shapes that overlap the boundary of the test shape. So a collision shape that was completely inside a test sphere would be missed. I guess the clue was i the name really.

Next up I tried a btCollisionWorld::convexSweepTest where the from and too transforms of the sweep were very close together. This seemed to return basically the same as btCollisionWorld::contactTest and it felt a little dirty as I didn’t really want a sweep test I just wanted to know what was in the volume of the sphere.

Now, I know, that bullet holds this information internally because it uses a tree structure of AABB to optimize it’s collision/physics system so it must have an accelerated way of modifiying this tree and keeping track of things that overlap or collide.

After stepping back for an hour or so I remembered that one of the collision shapes in Bullet is btPairCachingGhostObject and it is specifically designed to cache all the things that overlap it. I use it in the character controller for Smith and Winston. So here is the code is wrote to query a sphere of space for all objects that overlap this volume:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
std::vector<GameObjectShPtr> results;

//[1]
btSphereShape sphere(radius);
btPairCachingGhostObject ghost;
btTransform xform;
xform.setOrigin(pos);
ghost.setCollisionShape(&sphere);
ghost.setWorldTransform(xform);
ObjectColMask *ocm = new ObjectColMask(colflags, ColFlags::kAll);
ghost.setUserPointer(ocm);

//[2]
mDynamicsWorld->addCollisionObject(&ghost);

//[3]
for (int i = 0; i < ghost.getNumOverlappingObjects(); i++){
    btCollisionObject *btco = ghost.getOverlappingObject(i);

    ObjectColMask *ocm = static_cast<ObjectColMask *>(btco->getUserPointer());

    if(ocm){
        ObjectCol *oc = ocm->UpcastToObjectCol();
        if(oc){
            GameObjectShPtr go = GOFromTransform(oc->GetTransform());
            results.push_back(go);
        }
    }
}

//[4]
mDynamicsWorld->removeCollisionObject(&ghost);
delete ocm;

return results;

Step 1

Create a sphere with the disired radius of the volume to test (I believe that any convex collision primitive will do, tri meshes wont work as they have no concept ot inside or outside).

Create a btPairCachingGhostObject set its collison shape to the sphere and set it’s collison flags. I’ve extended the collision flags with my own mechanism called an ObjectColMask.

Step 2

Add the ghost object to the collision world. The act of adding the objects causes it’s overlapping object list to be updated with, you’ve guessed it, all of it’s overlapping objects.

Step 3

Iterate over the overlapping objects doing your own magic to extract what ever information you need.

Step 4

Remove the Ghost Object from the collision world.

That’s it, pretty simple but not too obvious. Hope this helps someone else.