Why is Ammo.js CCD Computationally Expensive?
Continuous Collision Detection (CCD) is a critical feature in physics engines like ammo.js used to prevent fast-moving objects from passing through other geometry, a glitch known as “tunneling.” While highly accurate, enabling CCD on a large number of objects quickly degrades performance and causes frame rate drops. This article explains the underlying technical reasons why CCD is so computationally expensive in ammo.js, focusing on swept volume calculations, broadphase pollution, and iterative time-of-impact solvers.
1. Mathematical Complexity of Swept Volumes
In standard Discrete Collision Detection (DCD), ammo.js only checks for overlaps at frozen moments in time (at each physics tick). This involves static mathematical checks, such as testing if two spheres or boxes overlap.
CCD, however, must calculate collisions over a continuous interval of time. To do this, ammo.js calculates a “swept volume”—the total 3D space an object occupies as it moves from its start position to its end position during a single frame. Determining if two moving swept volumes intersect requires solving complex, multi-dimensional algebraic equations and running iterative root-finding algorithms (like Conservative Advancement) to find the exact Time of Impact (TOI). This is mathematically far more demanding than simple static overlap tests.
2. Broadphase Optimization Breakdown
Physics engines use a two-phase system to detect collisions efficiently: * Broadphase: Quickly discards pairs of objects that are nowhere near each other using simple bounding boxes (AABBs). * Narrowphase: Performs precise, expensive collision detection on the remaining pairs.
When CCD is enabled, an object’s bounding box must be expanded to encapsulate its entire swept path. If an object is moving fast, its bounding box becomes extremely large. When many objects have expanded bounding boxes, they begin to overlap constantly in the broadphase. This “pollutes” the broadphase, forcing ammo.js to pass far too many object pairs to the computationally heavy narrowphase, defeating the purpose of the optimization.
3. Iterative Time-of-Impact (TOI) Solvers
When ammo.js detects a potential CCD collision, it cannot simply push the objects apart at the end of the frame. Doing so would cause realistic physics to break. Instead, the engine must determine the exact fraction of the timestep at which the collision occurred.
This requires an iterative solver that repeatedly checks sub-steps of the frame. If a collision is found at \(t = 0.4\) of the frame, the engine must: 1. Roll back the fast-moving object to that exact sub-step. 2. Resolve the collision (calculate new velocities and angles). 3. Simulate the remaining \(0.6\) of the frame.
If multiple objects are undergoing CCD checks simultaneously, these rollbacks and sub-steps multiply exponentially, creating a massive CPU bottleneck.
4. WebAssembly and CPU-Javascript Overhead
Ammo.js is a port of the C++ Bullet Physics library compiled into WebAssembly (Wasm) or JavaScript via Emscripten.
Because CCD requires constant, high-frequency memory access and complex floating-point math, it puts a heavy burden on the WebAssembly runtime. While Wasm is fast, passing large amounts of transform data back and forth between the JavaScript rendering loop (like Three.js) and the compiled ammo.js physics world adds overhead. When applied to dozens or hundreds of active CCD bodies, this communication bottleneck combined with raw CPU calculations quickly exhausts the browser’s single-threaded execution budget.