Ammo.js btBvhTriangleMeshShape Physics Tunneling
In physics simulations using Ammo.js (the JavaScript port of the
Bullet physics engine), fast-moving rigid bodies can sometimes pass
completely through static 3D environments. This phenomenon is known as
physics tunneling. When using a highly detailed
btBvhTriangleMeshShape for your collision geometry, this
issue is exacerbated. This article explains the technical reasons why
highly detailed triangle meshes trigger physics tunneling and outlines
how to prevent it.
1. Zero-Thickness Geometry
Unlike primitive shapes such as boxes, spheres, or capsules, a
btBvhTriangleMeshShape is composed of individual,
infinitely thin triangles. It lacks a solid volume or “inside.” When a
dynamic rigid body collides with a primitive shape, the engine can
detect penetration because the object is inside the shape’s volume and
can push it back out. With a triangle mesh, if a fast-moving object
passes completely through a triangle within a single physics step, there
is no volume to detect that the object is “inside” the mesh, resulting
in a missed collision.
2. Discrete Collision Detection Limits
By default, Ammo.js uses discrete collision detection. In this mode,
the engine updates object positions at discrete time steps (frames). If
an object is traveling at a high velocity, its position in Frame A might
be in front of the highly detailed mesh, and its position in Frame B
might be completely behind it. Because the engine only checks for
collisions at these specific snapshot intervals, it never registers a
contact point against the thin triangles of the
btBvhTriangleMeshShape.
3. High Density of Triangles and BVH Traversal
A highly detailed mesh contains thousands of tiny triangles, resulting in a dense Bounding Volume Hierarchy (BVH) tree. To resolve collisions, the engine must traverse this tree to find which triangles are overlapping with the moving rigid body. * Narrow-phase budget limitations: Under heavy CPU loads, or when the physics step time is constrained, the engine may struggle to process complex contact manifolds. * Jitter and mathematical inaccuracy: When an object collides with a highly dense area of tiny triangles, the engine may generate multiple conflicting contact points. This can confuse the constraint solver, causing the moving object to “glitch” or be pushed through the mesh rather than bouncing off it.
4. How to Prevent Tunneling in Ammo.js
To resolve tunneling issues when using
btBvhTriangleMeshShape, implement the following
strategies:
Enable Continuous Collision Detection (CCD): For fast-moving dynamic objects, enable CCD to sweep a sphere along the object’s movement path between frames. You can set this up using:
body.setCcdMotionThreshold(threshold); // e.g., the radius of the object body.setCcdSweptSphereRadius(radius); // e.g., slightly smaller than the object's radiusUse Simplified Collision Meshes: Never use your highly detailed visual rendering mesh for physics collisions. Create a separate, low-polygon proxy mesh (a “collider”) with fewer, larger triangles for the
btBvhTriangleMeshShape.Increase Physics Substeps: Configure your physics world step to use smaller time intervals and more substeps, giving the engine more opportunities to catch collisions:
physicsWorld.stepSimulation(deltaTime, maxSubSteps, fixedTimeStep);Add Thickness to the Mesh: Where possible, design your environment meshes to have physical thickness rather than single-sided, flat planes.