Ammo.js Bounding Box for Custom Triangle Mesh

This article explains how the ammo.js physics engine calculates the Axis-Aligned Bounding Box (AABB) for custom triangle meshes. It covers the underlying Bullet Physics mechanisms, the role of Bounding Volume Hierarchies (BVH), and how to programmatically retrieve and utilize these bounding boxes in your JavaScript 3D applications.

Mesh Representation in Ammo.js

To handle a custom triangle mesh, ammo.js (a direct Emscripten port of the C++ Bullet Physics engine) requires the vertex and index data to be formatted into a collision shape. Typically, this is achieved using the btTriangleMesh class to interface raw web geometries (like those from Three.js or Babylon.js) with the physics world.

Once the vertex data is added to btTriangleMesh, it is usually wrapped in one of two collision shapes: * btBvhTriangleMeshShape: Used for static, non-moving concave meshes. * btConvexTriangleMeshShape: Used for dynamic or static convex hulls. * btGimpactMeshShape: Used for dynamic, colliding concave meshes.

How the Bounding Box is Calculated

The bounding box of a custom mesh in ammo.js is represented as an Axis-Aligned Bounding Box (AABB) defined by two 3D vectors: the minimum corner (aabbMin) and the maximum corner (aabbMax).

1. Local-Space AABB Generation

When a btBvhTriangleMeshShape is instantiated, the engine parses all the triangles in the btTriangleMesh to build a Quantized Bounding Volume Hierarchy (BVH) tree.

During this initialization phase: * The engine loops through every vertex in the mesh to determine the absolute minimum and maximum X, Y, and Z coordinates. * These limits form the local-space bounding box of the entire mesh. * The BVH tree then recursively splits this space into smaller bounding boxes containing subsets of triangles, which optimizes mid-phase collision detection.

2. World-Space AABB Transformation

Because physics objects move and rotate, the local bounding box must be updated to reflect the object’s current state in world space.

When the simulation step runs, or when queried manually, the engine applies the object’s world transform (translation and rotation) to the local AABB. Because a rotating box no longer aligns perfectly with the world axes, ammo.js calculates a new, slightly larger AABB that completely encloses the rotated local bounding box.

Querying the Bounding Box Programmatically

To retrieve the calculated bounding box of a custom mesh shape in ammo.js, you use the getAabb method available on the collision shape. This method requires a transform matrix and two pre-allocated btVector3 objects to store the output.

Here is the standard implementation pattern:

// Assume 'meshShape' is a btBvhTriangleMeshShape or btConvexTriangleMeshShape
// Assume 'worldTransform' is the current btTransform of your physics body

// 1. Allocate temporary vectors to hold the min and max coordinates
const aabbMin = new Ammo.btVector3();
const aabbMax = new Ammo.btVector3();

// 2. Retrieve the bounding box relative to the provided transform
meshShape.getAabb(worldTransform, aabbMin, aabbMax);

// 3. Extract the coordinate values
const minX = aabbMin.x();
const minY = aabbMin.y();
const minZ = aabbMin.z();

const maxX = aabbMax.x();
const maxY = aabbMax.y();
const maxZ = aabbMax.z();

// 4. Clean up allocated Ammo memory to prevent leaks
Ammo.destroy(aabbMin);
Ammo.destroy(aabbMax);

Performance Considerations