btTransform and btRigidBody Relationship in ammo.js
This article explains the mathematical and structural relationship
between btTransform and btRigidBody within the
ammo.js physics engine. It covers how a rigid body uses transforms to
represent its position and orientation in 3D space, the underlying
matrix mathematics, and how to programmatically sync these states with
3D graphics libraries.
The Mathematical Representation
At its core, a btRigidBody represents a physical object
with mass, velocity, and physical forces. However, to exist in a 3D
simulation, it must have a spatial state. This state is defined by a
btTransform.
Mathematically, a btTransform represents an affine
transformation consisting of a translation vector and a rotation matrix.
It maps local coordinates of the rigid body (\(X_{local}\)) to world coordinates (\(X_{world}\)):
\[X_{world} = R \cdot X_{local} + T\]
Where: * \(R\) is a
\(3 \times 3\) rotation matrix
(represented inside btTransform as a
btMatrix3x3 or set via a btQuaternion). *
\(T\) is a \(3\)-element translation vector (represented
as a btVector3).
In homogeneous coordinates, this relationship is expressed as a single \(4 \times 4\) matrix multiplication:
\[\begin{bmatrix} X_{world} \\ 1 \end{bmatrix} = \begin{bmatrix} R & T \\ 0 & 1 \end{bmatrix} \begin{bmatrix} X_{local} \\ 1 \end{bmatrix}\]
The btRigidBody stores this matrix as its “Center of
Mass” transform.
How btRigidBody Uses btTransform
In ammo.js, btRigidBody does not store position and
rotation as separate, loose variables. Instead, it encapsulates them
within a btTransform instance.
- Center of Mass: The
btTransformof abtRigidBodyspecifically defines the position and orientation of the body’s center of mass, not necessarily its local origin (though they are often the same). - State Updates: Every time the physics simulation
steps (
world.stepSimulation()), ammo.js calculates new linear and angular velocities, applies forces, and updates thebtTransformof thebtRigidBodyaccordingly.
Accessing and Modifying the Transform
To get or set the spatial state of a btRigidBody in
ammo.js, you interact directly with its transform.
Reading the Transform
To retrieve the current position and rotation of a rigid body:
// Create a transform object to hold the output
let transform = new Ammo.btTransform();
rigidBody.getMotionState().getWorldTransform(transform);
// Extract translation (position)
let origin = transform.getOrigin();
let x = origin.x();
let y = origin.y();
let z = origin.z();
// Extract rotation (quaternion)
let rotation = transform.getRotation();
let qx = rotation.x();
let qy = rotation.y();
let qz = rotation.z();
let qw = rotation.w();Writing the Transform
To manually override the position and orientation of a rigid body,
you must construct a new btTransform and apply it:
let transform = new Ammo.btTransform();
// Set translation
let position = new Ammo.btVector3(0, 10, 0);
transform.setOrigin(position);
// Set rotation using a quaternion
let quaternion = new Ammo.btQuaternion(0, 0, 0, 1);
transform.setRotation(quaternion);
// Apply to the rigid body
rigidBody.setWorldTransform(transform);
rigidBody.getMotionState().setWorldTransform(transform);The Role of Motion States
While you can access the transform directly from the
btRigidBody using getWorldTransform(), the
mathematically correct and performant way to sync physics with rendering
is through a btMotionState.
The motion state acts as a buffer. It allows ammo.js to interpolate
the btTransform between physics steps, ensuring smooth
visual updates even if the physics simulation runs at a different frame
rate than the rendering loop.