Why Recalculate Inertia After Scaling Ammo.js Shapes
When working with the ammo.js physics engine, adjusting the local scaling of a collision shape alters its physical dimensions. Because a shape’s mass distribution changes with its size, the rigid body’s local inertia tensor becomes outdated. This article explains the relationship between shape scaling and rotational inertia, detailing why you must manually recompute and apply the new inertia to maintain realistic physical simulations.
Understanding the Physics of Inertia
In physics, the moment of inertia represents an object’s resistance to rotational acceleration. Unlike mass, which is a constant scalar value representing resistance to linear acceleration, inertia depends heavily on how that mass is distributed relative to the object’s axis of rotation.
Mathematically, the moment of inertia (\(I\)) for a given axis is proportional to the mass (\(m\)) and the square of the distance (\(r\)) from the rotation axis:
\[I \propto m \cdot r^2\]
When you change the local scaling of a shape in ammo.js (for example, stretching a sphere into an ellipsoid or making a box twice as wide), you are changing the distance (\(r\)) of the mass points from the center of gravity. Even if the total mass of the object remains exactly the same, spreading that mass over a larger or smaller volume fundamentally alters how difficult it is to rotate the object.
How Ammo.js Handles Shape Scaling
Ammo.js (a JavaScript port of the Bullet physics SDK) separates the collision geometry from the dynamic properties of a rigid body:
btCollisionShape: Defines the geometry and dimensions of the object.btRigidBody: Manages the dynamics, including velocity, mass, forces, and local inertia.
When you instantiate a rigid body, you calculate its initial local
inertia based on the shape’s original dimensions and mass using the
calculateLocalInertia method. The rigid body then stores
this inertia vector in its internal state.
If you subsequently call
shape.setLocalScaling(newScale), you instantly modify the
physical dimensions of the collision shape. However, ammo.js does not
automatically update the rigid body’s cached dynamics. The rigid body
continues to use the inertia tensor calculated for the shape’s original,
unscaled dimensions.
The Consequences of Ignoring Recalculation
Failing to recompute inertia after scaling a shape leads to unrealistic and broken physics behaviors:
- Unnatural Rotation Speeds: A box scaled to be ten times larger that retains its original small inertia will spin wildly and unrealistically fast when hit, as if it were still tiny.
- Jittering and Instability: Mismatches between visual bounds, collision bounds, and rotational resistance cause constraints, joints, and collisions to resolve incorrectly, leading to physics glitches and erratic movement.
- Improper Gravity Responses: Rolling objects will roll too quickly or too slowly relative to their visual size.
How to Correctly Update Scale and Inertia
To scale a shape correctly in ammo.js, you must follow a specific sequence of updating the shape, recalculating the inertia, and reapplying those properties to the rigid body.
1. Set the New Scaling
First, apply the new scale vector to the collision shape.
const scaleVector = new Ammo.btVector3(2.0, 2.0, 2.0); // Example: Double the size
collisionShape.setLocalScaling(scaleVector);2. Recalculate Local Inertia
Use the shape to calculate the new inertia vector based on the rigid body’s existing mass.
const mass = 1.0; // The mass of your rigid body
const localInertia = new Ammo.btVector3(0, 0, 0);
collisionShape.calculateLocalInertia(mass, localInertia);3. Apply the New Properties to the Rigid Body
Update the rigid body’s mass and inertia properties using the
setMassProps method.
rigidBody.setMassProps(mass, localInertia);
rigidBody.updateInertiaTensor();4. Activate the Body
If the rigid body was asleep (deactivated), force it to re-evaluate its state within the physics world.
rigidBody.activate(true);