ammo.js btQuaternion: Representing 3D Rotations
This article explores how ammo.js, the JavaScript port
of the Bullet physics engine, natively represents 3D spatial rotations
using the btQuaternion class. We will break down the
mathematical structure of quaternions, demonstrate how to instantiate
and manipulate them in ammo.js, and explain how they
prevent common rotation issues like gimbal lock during physics
simulations.
Understanding the Math Behind btQuaternion
In ammo.js, 3D rotations are represented using
quaternions rather than Euler angles (pitch, yaw, and roll) or 3x3
rotation matrices. A quaternion is a four-dimensional vector expressed
as:
\[q = (x, y, z, w)\]
Where: * \(x, y, z\) represent the vector of the rotation axis, scaled by the sine of half the rotation angle. * \(w\) represents the cosine of half the rotation angle.
By using four values to represent a rotation around an arbitrary
axis, btQuaternion avoids the phenomenon of “gimbal lock”—a
state where two of the three rotational axes align, losing a degree of
freedom. Quaternions also require less memory than 3x3 matrices and are
computationally efficient for spherical linear interpolation
(SLERP).
Instantiating btQuaternion in ammo.js
To use quaternions in ammo.js, you must instantiate the
class through the Ammo namespace. Here is the standard way
to create an identity quaternion (representing no rotation):
// Initialize an identity quaternion (0, 0, 0, 1)
const quaternion = new Ammo.btQuaternion(0, 0, 0, 1);You can also set the values of an existing quaternion using the
setValue method:
quaternion.setValue(x, y, z, w);Because ammo.js is a WebIDL/Emscripten port of C++, you
access the individual components using getter methods rather than direct
properties:
const x = quaternion.x();
const y = quaternion.y();
const z = quaternion.z();
const w = quaternion.w();Creating Rotations from Euler Angles and Axis-Angles
Directly calculating the \(x, y, z,
w\) values of a quaternion manually is highly impractical. The
btQuaternion class provides helper methods to define
rotations using more intuitive systems.
1. Using Euler Angles (Yaw, Pitch, Roll)
You can define a rotation using Euler angles using the
setEulerZYX method. Note that ammo.js expects
these angles to be in radians.
const yaw = Math.PI / 4; // Rotation around Y-axis (45 degrees)
const pitch = 0; // Rotation around X-axis
const roll = 0; // Rotation around Z-axis
const quaternion = new Ammo.btQuaternion();
quaternion.setEulerZYX(yaw, pitch, roll);2. Using Axis-Angle Representation
If you want to rotate an object by a specific angle around a custom
direction vector, use the setRotation method:
const axis = new Ammo.btVector3(0, 1, 0); // Rotate around the Y-axis
const angle = Math.PI / 2; // 90 degrees in radians
const quaternion = new Ammo.btQuaternion();
quaternion.setRotation(axis, angle);Applying Quaternions to Physics Bodies
In ammo.js, the rotation of a rigid body is managed via
its transform (btTransform). To apply a rotation to a
physics object, you pass the btQuaternion to the transform
before updating the body.
const transform = new Ammo.btTransform();
transform.setIdentity();
// Define rotation
const rotation = new Ammo.btQuaternion();
rotation.setEulerZYX(0, Math.PI / 4, 0);
// Apply rotation to transform
transform.setRotation(rotation);
// Apply transform to a rigid body's motion state
const motionState = new Ammo.btDefaultMotionState(transform);To maintain physical accuracy, ensure your quaternions remain normalized (having a length of 1). If a quaternion becomes distorted due to floating-point drift over cumulative operations, you can re-normalize it using:
quaternion.normalize();