Extract Rotation Matrix from Ammo.js Transform

Extracting the rotation matrix from an Ammo.js transform object is crucial for synchronizing physics simulations with 3D rendering engines like Three.js. This article explains how to correctly retrieve this rotation data by accessing the transform’s basis and converting it into a standard 3x3 or 4x4 matrix format.


Understanding the Basis in Ammo.js

In Ammo.js (a WebIDL port of the Bullet Physics library), a btTransform object represents the position and orientation of a rigid body. The orientation is stored internally as a 3x3 rotation matrix, which is referred to as the basis.

To extract the rotation matrix, you must retrieve this basis and read its individual row vectors.

Step-by-Step Extraction

1. Retrieve the Basis

First, call the getBasis() method on your btTransform instance. This returns a btMatrix3x3 object.

// Assuming 'transform' is your Ammo.btTransform instance
const basis = transform.getBasis();

2. Extract Row Vectors

The btMatrix3x3 object consists of three row vectors (btVector3). You can access them using the getRow(index) method, where the index ranges from 0 to 2.

const row0 = basis.getRow(0); // X-axis of the rotation
const row1 = basis.getRow(1); // Y-axis of the rotation
const row2 = basis.getRow(2); // Z-axis of the rotation

To get the numerical values, call the .x(), .y(), and .z() getter functions on each row vector.

const r00 = row0.x(), r01 = row0.y(), r02 = row0.z();
const r10 = row1.x(), r11 = row1.y(), r12 = row1.z();
const r20 = row2.x(), r21 = row2.y(), r22 = row2.z();

Converting to WebGL (Column-Major) Format

Most WebGL-based rendering engines, such as Three.js, use column-major 4x4 matrices. When transferring the 3x3 Ammo.js basis into a 4x4 matrix, you must transpose the elements to switch from row-major to column-major format and set the translation/scale components to their identity defaults.

Here is how to map the extracted basis directly into a flat 16-element float array:

const rotationMatrix = [
    row0.x(), row1.x(), row2.x(), 0, // Column 0
    row0.y(), row1.y(), row2.y(), 0, // Column 1
    row0.z(), row1.z(), row2.z(), 0, // Column 2
    0,        0,        0,        1  // Column 3 (Translation set to 0)
];

Alternative: Extraction via Quaternion

If your rendering engine accepts quaternions, you can extract the rotation as a quaternion instead of a matrix. This is often more performant and less prone to scaling errors.

const quaternion = transform.getRotation();

const qx = quaternion.x();
const qy = quaternion.y();
const qz = quaternion.z();
const qw = quaternion.w();

You can then apply this quaternion directly to your 3D mesh to update its orientation.