Access Collision Manifold Points in Ammo.js

In physics simulations using ammo.js (the Emscripten port of Bullet Physics), detecting and processing collisions requires accessing the contact manifolds generated by the physics dispatcher. This article provides a direct, step-by-step guide on how to iterate through active collision manifold points in ammo.js to retrieve detailed contact data—such as impact points, surface normals, penetration depth, and applied impulse forces—for use in your game development and simulation workflows.

The Collision Iteration Process

Ammo.js processes collisions during the physics step and stores the results in “contact manifolds.” A manifold is a cache that contains contact points between pairs of overlapping objects. To inspect these collisions, you must query the physics world’s dispatcher after updating the simulation.

Here is the step-by-step implementation to access this data.

Step 1: Get the Dispatcher

To start, you need to access the collision dispatcher from your btDiscreteDynamicsWorld instance.

// Assuming 'physicsWorld' is your Ammo.btDiscreteDynamicsWorld instance
const dispatcher = physicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();

Step 2: Loop Through the Manifolds

The dispatcher holds a list of all active manifolds. You must iterate through this list to inspect each pair of colliding objects.

for (let i = 0; i < numManifolds; i++) {
    const manifold = dispatcher.getManifoldByIndexInternal(i);
    
    // Skip if there are no actual contact points in this manifold
    const numContacts = manifold.getNumContacts();
    if (numContacts === 0) continue;

    // Retrieve the two colliding collision objects
    const obj0 = manifold.getBody0();
    const obj1 = manifold.getBody1();

    // Cast them to btRigidBody if you need rigid body specific properties
    const body0 = Ammo.castObject(obj0, Ammo.btRigidBody);
    const body1 = Ammo.castObject(obj1, Ammo.btRigidBody);

    // Step 3: Iterate through individual contact points
    for (let j = 0; j < numContacts; j++) {
        const pt = manifold.getContactPoint(j);
        
        // Process contact point data here
    }
}

Step 3: Extract Contact Point Data

Once you have access to the btManifoldPoint (represented by pt in the loop above), you can extract physical properties of the collision:

// Check the distance to ensure the objects are actually touching/penetrating
const distance = pt.getDistance();

if (distance < 0.0) {
    // Collision point on Body A (World Space)
    const localPtA = pt.getPositionWorldOnA(); 
    const worldPtA = [localPtA.x(), localPtA.y(), localPtA.z()];

    // Collision point on Body B (World Space)
    const localPtB = pt.getPositionWorldOnB();
    const worldPtB = [localPtB.x(), localPtB.y(), localPtB.z()];

    // Normal vector on Body B (pointing towards Body A)
    const normal = pt.m_normalWorldOnB;
    const normalVec = [normal.x(), normal.y(), normal.z()];

    // The force magnitude of the impact
    const appliedImpulse = pt.getAppliedImpulse();

    // Use this data for gameplay logic (e.g., playing sounds, spawning particles)
    console.log(`Collision detected with impulse: ${appliedImpulse}`);
}

Key Considerations for Performance