Filter Convex Sweep Tests by Layer in Ammo.js

This article explains how to restrict a convex sweep test in Ammo.js to specific collision layers. You will learn how to configure bitwise collision groups and masks, apply them to a ClosestConvexResultCallback, and execute the filtered sweep test to ignore unwanted physics bodies.

Step 1: Define Collision Groups

Bullet Physics (and by extension Ammo.js) uses bitwise flags to handle collision filtering. Define your collision layers as powers of two:

const COLLISION_GROUP_DEFAULT = 1;   // 0001
const COLLISION_GROUP_PLAYER = 2;    // 0010
const COLLISION_GROUP_ENEMY = 4;     // 0100
const COLLISION_GROUP_WALL = 8;      // 1000

When adding rigid bodies to your dynamics world, assign them a group and a mask defining what they can collide with:

// Add a wall that only collides with the player and default objects
dynamicsWorld.addRigidBody(
    wallBody, 
    COLLISION_GROUP_WALL, 
    COLLISION_GROUP_PLAYER | COLLISION_GROUP_DEFAULT
);

Step 2: Configure the Convex Sweep Callback

To filter the sweep test, instantiate a ClosestConvexResultCallback. This object exposes properties to set the collision group of the test itself and the mask of layers it is allowed to hit.

// Define start and end vectors for the sweep
const fromVec = new Ammo.btVector3(0, 5, 0);
const toVec = new Ammo.btVector3(10, 5, 0);

// Create the callback
const callback = new Ammo.ClosestConvexResultCallback(fromVec, toVec);

// Set the sweep test's identity and target layers
// Example: The sweep acts as a "Player" and should only hit "Wall" objects
callback.set_m_collisionFilterGroup(COLLISION_GROUP_PLAYER);
callback.set_m_collisionFilterMask(COLLISION_GROUP_WALL);

Step 3: Execute the Sweep Test

Pass your collision shape, start and end transforms, and the configured callback to the convexSweepTest method of your dynamics world.

const startTransform = new Ammo.btTransform();
startTransform.setIdentity();
startTransform.setOrigin(fromVec);

const endTransform = new Ammo.btTransform();
endTransform.setIdentity();
endTransform.setOrigin(toVec);

// Perform the sweep test
dynamicsWorld.convexSweepTest(
    convexShape, // e.g., Ammo.btBoxShape or Ammo.btCapsuleShape
    startTransform,
    endTransform,
    callback,
    0 // Allowed CCD penetration (usually 0)
);

// Process the results
if (callback.hasHit()) {
    const hitBody = Ammo.castObject(callback.get_m_hitCollisionObject(), Ammo.btCollisionObject);
    const hitPoint = callback.get_m_hitPointWorld();
    const hitNormal = callback.get_m_hitNormalWorld();
    
    console.log("Sweep hit a filtered target at:", hitPoint.x(), hitPoint.y(), hitPoint.z());
} else {
    console.log("No filtered targets detected in sweep path.");
}

Step 4: Clean Up Memory

To avoid memory leaks in the WebAssembly runtime, always destroy the created Ammo objects when they are no longer needed:

Ammo.destroy(fromVec);
Ammo.destroy(toVec);
Ammo.destroy(startTransform);
Ammo.destroy(endTransform);
Ammo.destroy(callback);