Update Ammo.js Collision Shape Size on Scale
This article explains how to dynamically update the collision shape of a rigid body when its corresponding visual object scales up during an active Ammo.js simulation. You will learn the exact steps required to apply local scaling to an existing collision shape, recalculate its inertia, and force the physics world to update the object’s bounding box to prevent rendering desynchronization.
The Challenge with Dynamic Scaling in Ammo.js
In Ammo.js (the JavaScript port of the Bullet physics engine), changing the scale of a 3D mesh (such as a Three.js mesh) does not automatically update its physical collision boundary. If you only scale the visual representation, the physics engine will continue to simulate collisions using the original dimensions. To keep the physics and visuals synchronized, you must explicitly update the rigid body’s collision shape and force Ammo.js to recalculate its bounding volume.
Step-by-Step Implementation
To correctly scale a collision shape during runtime, you must execute the following sequence on your rigid body:
1. Apply Local Scaling to the Shape
First, retrieve the collision shape from the rigid body and apply the
new scale using btVector3.
// Assume 'rigidBody' is your Ammo.btRigidBody instance
// and 'scaleX, scaleY, scaleZ' are the new scale factors
const shape = rigidBody.getCollisionShape();
const ammoScale = new Ammo.btVector3(scaleX, scaleY, scaleZ);
shape.setLocalScaling(ammoScale);2. Recalculate Mass and Inertia
If the object is dynamic (mass > 0), scaling it alters its distribution of mass. You must recalculate the local inertia based on the new scale and apply it to the rigid body.
const mass = 1.0; // Use your object's actual mass
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
rigidBody.setMassProps(mass, localInertia);3. Update the Broadphase Bounding Box (AABB)
The physics world uses Axis-Aligned Bounding Boxes (AABB) for quick collision detection. If you scale a shape, you must force the physics world to update this bounding box immediately; otherwise, the engine will still use the old size for broadphase collision checks.
// Assume 'physicsWorld' is your Ammo.btDiscreteDynamicsWorld instance
physicsWorld.updateSingleAABB(rigidBody);4. Re-activate the Rigid Body
If the rigid body has gone to sleep (deactivated due to lack of movement), the engine will ignore the scale changes. Force the body to activate so the physics engine processes the update on the next frame.
rigidBody.activate(true);5. Clean Up Memory
Ammo.js is a WebAssembly/C++ wrapper, meaning you must manually destroy temporary vector objects to prevent memory leaks in your application loop.
Ammo.destroy(ammoScale);
Ammo.destroy(localInertia);Complete Utility Function
Here is a complete, reusable JavaScript function to scale an Ammo.js rigid body safely:
function scaleRigidBody(physicsWorld, rigidBody, scale, mass = 0) {
const shape = rigidBody.getCollisionShape();
// Create temporary Ammo vectors
const ammoScale = new Ammo.btVector3(scale.x, scale.y, scale.z);
// Apply the scaling
shape.setLocalScaling(ammoScale);
// Recalculate inertia for dynamic bodies
if (mass > 0) {
const localInertia = new Ammo.btVector3(0, 0, 0);
shape.calculateLocalInertia(mass, localInertia);
rigidBody.setMassProps(mass, localInertia);
Ammo.destroy(localInertia);
}
// Update the collision world
physicsWorld.updateSingleAABB(rigidBody);
rigidBody.activate(true);
// Free WebAssembly memory
Ammo.destroy(ammoScale);
}