Detecting collisions with btHeightfieldTerrainShape

Post Reply
fandango
Posts: 3
Joined: Tue Apr 26, 2022 5:08 pm

Detecting collisions with btHeightfieldTerrainShape

Post by fandango »

Hi everyone. I have a 256x256 heightfield, which is currently rendering correctly in Bullet's Debug Draw handler, so I believe it is set up correctly. It has it's own filter group (8) and I'm wanting to detect collisions with game rigidbodies (btBoxShape) whose filter group is (1).

What I'm finding is that my callback

Code: Select all

void CollisionCallback(btBroadphasePair &collisionPair, btCollisionDispatcher &dispatcher, const btDispatcherInfo &dispatchInfo) {
        SDL_Log(
            "Collision detected ( %d | %d )",
            collisionPair.m_pProxy0->m_collisionFilterGroup,
            collisionPair.m_pProxy1->m_collisionFilterGroup);
}

collisionDispatcher->setNearCallback(CollisionCallback);
is triggering as soon as a rigidBody's AABB box enters the terrain AABB box. (e.g., so as they fly above the highest mountain there are no interactions, but as soon as the bounding box for the entire terrain is entered, the callback is triggered).

My understanding was that these callbacks would trigger when the actual triangle mesh of the terrain was crossed.

I feel like I'm close, but I'm entirely sure how I should be handling this. The included Bullet Heightfield example doesn't use any callbacks, but seems to do manual raycasting for all rigidbodies in the world. Is that what I'm meant to be doing in this callback, a manual raycast? Or should I not be using this callback at all and every tick do manual raycasts for all rigidbodies in my world?
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Detecting collisions with btHeightfieldTerrainShape

Post by drleviathan »

The way collision works in Bullet is...

The "broad-phase" provides collision candidates (e.g. pairs bodies with overlapping bounding boxes) to the "narrow-phase" which computes the details (contact manifold) of the collision. Your problem is... you are tapping into the output of the broad-phase: when objects are "near enough to warrant a more expensive narrow-phase check".

Yes, btHeightfieldTerrainShape keeps the data in a regular grid and builds btTriangleShape instances as necessary (during the narrow-phase check) to collide against other shapes.

If you want to log all collisions then maybe something like this after the stepSimulation():

Code: Select all

    // list all touching objects
    int num_manifolds = collision_dispatcher->getNumManifolds();
    for (int i = 0; i < num_manifolds; ++i) {
        btPersistentManifold* contact_manifold =  collision_dispatcher->getManifoldByIndexInternal(i);
        if (contact_manifold->getNumContacts() > 0) {
            const btCollisionObject* object_a = contact_manifold->getBody0();
            const btCollisionObject* object_b = contact_manifold->getBody1();
            SDL_Log("Collision detected ( %d | %d )", object_a->getWorldArrayIndex(), object_b->getWorldArrayIndex());
        }
    }
fandango
Posts: 3
Joined: Tue Apr 26, 2022 5:08 pm

Re: Detecting collisions with btHeightfieldTerrainShape

Post by fandango »

Thanks drleviathan, somewhat ironically I had that exact code commented out in my PhysicsManager. I think the problem is I don't really know how best to handle this.

I have a couple of hundred small flying entities over a triangle terrain and want to detect if they crash into the terrain. Ideally they should have an opportunity to change course.

I think what I need to do is use their velocity (with some look-ahead) to perform a ray check on the terrain mesh. With regard to checking if they are inside the landscape, I'd be as well doing a cheap heightfield lookup with the lower resolution grid.
User avatar
drleviathan
Posts: 849
Joined: Tue Sep 30, 2014 6:03 pm
Location: San Francisco

Re: Detecting collisions with btHeightfieldTerrainShape

Post by drleviathan »

Yes, if you want to prevent the collisions in the first place you would need to look ahead, with ray-trace or some other mechanism. If you're running several hundred at once then the ray-trace seems like the cheapest approach. Rumor has it the btHeightfieldTerrainShape can get expensive if you make long mostly horizontal ray traces against it: where the ray-trace test must generate large numbers of btTriangleShape's to test against. If you find your ray-trace tests start to bog the system down you might consider reducing LOD of the terrain and using a btBvhTriangleMeshShape instead which uses a Bounding Volume Hierarchy data structure to speed up spatial queries against it.

Meanwhile, there might be another way to do it. It is possible to set a callback for when contact points are created, but before they have been integrated into the final contact manifold. This provides an opportunity to examine the details of the contact and to change them before they are actually handled. I've seen this used to disable collisions between objects on a pairwise/temporary basis instead of using collision groups/filters.

The way I could imagine this being used would be to add a second (identical? lower LOD?) btHeightfieldTerrainShape that is raised above the real one, and which configures to only collides against missiles. It would serve only as a proximity detector: you would catch all contact points, check to see if they are against this heightfield, and disable them if necessary. The effect would be: the missiles never actually collide physically, but the creation of contact points could be used to communicate real-terrain proximity info to each corresponding missile. Dunno what is the CPU cost trade-off here but I figured I would mention the idea.

Good luck.
fandango
Posts: 3
Joined: Tue Apr 26, 2022 5:08 pm

Re: Detecting collisions with btHeightfieldTerrainShape

Post by fandango »

Thank you for the information, that's fantastic! I saw some posts about the contact points callback that you mentioned and will look into that a bit more to see what's possible.

I experimented with just having a short raycast based on heading per entity and the speed seemed to be fine! So I might go down that avenue. However, what I think I will do is actually implement some path-finding for the terrain (i.e., waypoints that are above the terrain) and avoid the physics entirely and leave that for detecting objects like buildings that have strongly defined AABBs and ignore the terrain for that pass. Also I check entities that are under the terrain in different groups spread across frames. Sometimes it actually looks better if they go into the ground for a few frames before exploding. :lol:

So far performance seems to be very good, but I do still have hopes of a raspberry pi port of my game so we'll see how that goes. Thanks again for your help drleviathan, it's really appreciated. :)
Post Reply