Get Collision Normal from Ammo.js Contact Manifold

This article explains how to retrieve the exact collision normal and face information from a contact manifold in Ammo.js. By accessing the physics world’s dispatcher, iterating through active contact manifolds, and querying individual contact points, you can extract the world-space normal vector and identify specific colliding triangle faces.

Step 1: Iterate Through the Contact Manifolds

Ammo.js processes collisions using a dispatcher. To find active collisions, loop through all contact manifolds generated during the physics simulation step:

const dispatcher = dynamicsWorld.getDispatcher();
const numManifolds = dispatcher.getNumManifolds();

for (let i = 0; i < numManifolds; i++) {
    const manifold = dispatcher.getManifoldByIndexInternal(i);
    const numContacts = manifold.getNumContacts();
    
    if (numContacts === 0) continue;

    // Retrieve the colliding bodies
    const body0 = Ammo.castObject(manifold.getBody0(), Ammo.btRigidBody);
    const body1 = Ammo.castObject(manifold.getBody1(), Ammo.btRigidBody);

    processManifoldContacts(manifold, numContacts);
}

Step 2: Extract the Collision Normal from Contact Points

Each manifold contains up to four contact points. You can query these points to get the depth of the collision and the contact normal vector on the second body (m_normalWorldOnB):

function processManifoldContacts(manifold, numContacts) {
    for (let j = 0; j < numContacts; j++) {
        const contactPoint = manifold.getContactPoint(j);
        
        // Only process actual contact/penetration (distance < 0)
        if (contactPoint.getDistance() < 0) {
            const normal = contactPoint.get_m_normalWorldOnB();
            
            const normalX = normal.x();
            const normalY = normal.y();
            const normalZ = normal.z();
            
            console.log(`Collision Normal: X: ${normalX}, Y: ${normalY}, Z: ${normalZ}`);
        }
    }
}

The vector returned by get_m_normalWorldOnB() points from body B towards body A. If you need the normal relative to body A, simply negate the vector components.

Step 3: Identify the Specific Mesh Face (Triangle Index)

If one of the colliding shapes is a complex triangle mesh (such as btBvhTriangleMeshShape or btGImpactMeshShape), Ammo.js stores the index of the specific triangle face that was hit. You can retrieve this index directly from the contact point:

// Retrieve triangle indices for the colliding faces
const triangleIndex0 = contactPoint.get_m_index0(); // Triangle index for Body 0
const triangleIndex1 = contactPoint.get_m_index1(); // Triangle index for Body 1

// Retrieve part IDs if using multi-part meshes
const partId0 = contactPoint.get_m_partId0();
const partId1 = contactPoint.get_m_partId1();

If the shape is a primitive (like a box or sphere), these index values will default to -1. If a mesh is involved, the index maps directly to the face index defined in your original geometry, allowing you to trigger face-specific logic, such as playing distinct impact sounds or applying localized visual effects.