Create btMatrix3x3 from Column Vectors in Ammo.js

This article explains how to construct and populate a btMatrix3x3 rotation matrix using three individual btVector3 column vectors in ammo.js (the WebAssembly/Emscripten port of the Bullet physics engine). You will learn the most memory-efficient way to map column vector components to the row-major structure of btMatrix3x3 without causing WebAssembly memory leaks.

In ammo.js, btMatrix3x3 is represented internally in row-major order. When you have three column vectors representing the basis of a coordinate system (such as the Right, Up, and Forward vectors), they must be transposed into the rows of the matrix.

The most efficient way to achieve this—without creating temporary row vectors that require manual memory cleanup—is by using the setValue method.

Step-by-Step Implementation

Assume you have three btVector3 objects representing your columns: col0, col1, and col2.

// 1. Define your three column vectors (e.g., Right, Up, Forward)
const col0 = new Ammo.btVector3(1.0, 0.0, 0.0); // Column 0
const col1 = new Ammo.btVector3(0.0, 1.0, 0.0); // Column 1
const col2 = new Ammo.btVector3(0.0, 0.0, 1.0); // Column 2

// 2. Instantiate an empty btMatrix3x3
const matrix = new Ammo.btMatrix3x3();

// 3. Map column elements to row elements using setValue
matrix.setValue(
    col0.x(), col1.x(), col2.x(), // Row 0: X components of columns 0, 1, 2
    col0.y(), col1.y(), col2.y(), // Row 1: Y components of columns 0, 1, 2
    col0.z(), col1.z(), col2.z()  // Row 2: Z components of columns 0, 1, 2
);

How It Works

The setValue method of btMatrix3x3 accepts nine scalar values in row-major order:

\[\begin{bmatrix} xx & xy & xz \\ yx & yy & yz \\ zx & zy & zz \end{bmatrix}\]

To construct the matrix from column vectors \(C_0, C_1, C_2\), we assign the vector components to the matrix coordinates as follows: * Row 0 receives the X-coordinates of all three columns: (col0.x, col1.x, col2.x) * Row 1 receives the Y-coordinates of all three columns: (col0.y, col1.y, col2.y) * Row 2 receives the Z-coordinates of all three columns: (col0.z, col1.z, col2.z)

Why Use setValue Instead of setRow?

While you can construct a matrix by creating three temporary btVector3 row vectors and calling matrix.setRow(index, vector), doing so requires you to allocate new WebAssembly memory for each row vector. Because ammo.js does not have automatic garbage collection for WebAssembly objects, you would have to manually call Ammo.destroy(rowVector) on each temporary row to avoid memory leaks.

Using setValue extracts the float values directly and passes them to the matrix, making it the fastest and safest approach.