What Is a Convex Hull Shape in Ammo.js

This article explains the concept of a convex hull collision shape in 3D physics and provides a step-by-step guide on how to generate one natively using the ammo.js physics engine. You will learn what makes convex hulls ideal for performance-friendly collision detection and how to feed 3D mesh vertex data into ammo.js to construct these shapes programmatically.

What is a Convex Hull Shape?

In 3D physics simulation, a convex hull is the smallest convex boundary that entirely encloses a given set of points. To visualize this, imagine wrapping a complex 3D object, such as a hand or a detailed vase, tightly in plastic stretch wrap. The resulting outer boundary, which contains no indentations, valleys, or hollows, is the convex hull.

In physics engines like ammo.js (a JavaScript port of the Bullet Physics engine), using a convex hull is a highly efficient way to handle collisions for complex, non-primitive shapes. It offers a middle ground between highly optimized primitive shapes (like boxes, spheres, and cylinders) and computationally expensive triangle meshes (btBvhTriangleMeshShape). Because a convex hull guaranteed to have no concave features, the mathematical algorithms used to calculate collisions (such as GJK) can run much faster, ensuring smooth performance even with multiple interacting objects.

Generating a Convex Hull Natively in Ammo.js

To generate a convex hull natively in ammo.js, you use the btConvexHullShape class. This shape is constructed by adding individual vertices from your 3D graphical model into the ammo.js shape object.

Here is the step-by-step process to generate the shape natively:

First, instantiate the empty convex hull shape:

const convexHullShape = new Ammo.btConvexHullShape();

Next, iterate through the vertices of your 3D mesh. For each vertex, assign its coordinates to a temporary ammo.js vector object (btVector3) and add it to the convex hull shape using the addPoint method.

// Example: Extracting vertices from a hypothetical 3D geometry array
const vertices = meshGeometry.positionArray; 
const tempVector = new Ammo.btVector3();

for (let i = 0; i < vertices.length; i += 3) {
    const x = vertices[i];
    const y = vertices[i + 1];
    const z = vertices[i + 2];

    tempVector.setValue(x, y, z);

    // The second parameter controls whether to recalculate the local AABB.
    // Pass false during the loop and true on the last point for optimization.
    const isLastPoint = (i === vertices.length - 3);
    convexHullShape.addPoint(tempVector, isLastPoint);
}

// Free the temporary vector from memory
Ammo.destroy(tempVector);

By setting the second parameter of addPoint to false during the iteration and only setting it to true on the very last vertex, you prevent ammo.js from recalculating the shape’s local bounding box on every single iteration. This optimization drastically reduces CPU overhead during initialization. Once populated, this convexHullShape can be directly assigned to an ammo.js rigid body construction info object to participate in the physics simulation.