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
- Caching: The local AABB of a
btBvhTriangleMeshShapeis cached during construction. Calculating the world-space AABB usinggetAabbis computationally cheap because it only transforms the pre-calculated local limits, rather than re-evaluating the mesh vertices. - Deformable Meshes: If you deform or update the
vertices of your custom triangle mesh at runtime, you must explicitly
tell the engine to recalculate the BVH tree and local AABB by calling
refitTree()on the shape, otherwise the bounding box and collision detections will remain outdated.