# Box3D Continuous Collision Detection: How It Prevents Tunneling for Fast-Moving Objects

> Box3D continuous collision detection calculates exact time-of-impact (TOI) to prevent tunneling for fast objects. Advance simulation to collision fraction, ensuring no objects pass through obstacles.

- Repository: [Erin Catto/box3d](https://github.com/erincatto/box3d)
- Tags: deep-dive
- Published: 2026-08-01

---

**Box3D prevents tunneling by computing the exact time-of-impact (TOI) for fast-moving bodies, then advancing the simulation only up to that collision fraction before applying impulses, ensuring high-velocity objects never pass through obstacles.**

Box3D, Erin Catto's open-source 3D physics engine, implements a robust continuous collision detection (CCD) system to handle fast-moving projectiles and high-velocity rigid bodies. Unlike discrete collision detection which only checks positions at frame boundaries, this pipeline calculates precise collision moments along a body's swept path, eliminating the tunneling artifacts common in bullet-physics simulations.

## Identifying Fast Bodies for CCD

Before the solver runs, Box3D flags bodies that require continuous collision detection. A body is marked with the **`b3_isFast`** or **`b3_isBullet`** flag either explicitly by the user or automatically when its linear velocity exceeds a threshold (typically `b3_LINEAR_SLOP * 20`).

In [`src/physics_world.c`](https://github.com/erincatto/box3d/blob/main/src/physics_world.c) (lines 1400-1415), the simulation step iterates through bodies and sets these flags based on velocity magnitude. The CCD stage then processes only bodies carrying these flags, ensuring computational overhead is spent only on objects that actually need swept collision tests.

## The CCD Pipeline Architecture

The continuous collision detection system runs after the broad-phase but before the discrete impulse solver. It operates through the **`b3SolveContinuous`** function in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c), which orchestrates the following stages:

### Sweep Construction with b3Sweep

For each flagged body, Box3D constructs a **`b3Sweep`** structure that captures the body's transform at the start and end of the time step. As implemented in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c) (lines 886-894), the sweep is re-centered on the body's initial position (`base`) to maintain floating-point precision during calculations. This structure stores the linear and angular displacement, enabling accurate motion interpolation.

### Broad-Phase Query and Collision Filtering

The system calculates an axis-aligned bounding box (AABB) that encompasses the entire sweep path, then queries the dynamic tree for potential colliders. The callback function **`b3ContinuousQueryCallback`** (defined in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c), lines 52-80) performs aggressive filtering to eliminate false positives:

- **Self-collision rejection**: Ignores shapes belonging to the fast body itself
- **Same-body elimination**: Discards shapes attached to the same rigid body
- **Sensor masking**: Filters sensor shapes unless both bodies are sensors
- **Layer masking**: Applies collision filter masks from `b3Filter`

### Time-of-Impact Computation

For each valid shape pair, Box3D invokes **`b3ShapeTimeOfImpact`** in [`src/shape.c`](https://github.com/erincatto/box3d/blob/main/src/shape.c) (lines 2235-2260). This function dispatches to shape-specific swept collision algorithms (supporting spheres, capsules, convex hulls, and meshes) and returns a **`b3TOIOutput`** structure containing:

- **`fraction`**: The normalized time [0,1] when collision occurs
- **`point`**: The contact point in world coordinates
- **`normal`**: The collision normal vector
- **Iteration counters**: Statistics for distance, push-back, and root-finding iterations

### Contact Resolution with Reduced Time Steps

Once the earliest TOI is found, the solver clears the `b3_isFast` flag and records the hit fraction in the **`b3ContinuousContext`** (defined in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c), lines 317-336). The simulation then re-integrates the fast body only up to the collision fraction, inserts the contact, and proceeds with impulse resolution. This guarantees that bodies never penetrate, as the discrete solver handles the collision at the exact sub-step where contact first occurs.

## Core Data Structures

The CCD system relies on three primary structures defined in the solver and shape modules:

- **`b3ContinuousContext`**: Holds the world reference, fast body pointer, current best TOI fraction, sensor hit buffers (up to `B2_MAX_CONTINUOUS_SENSOR_HITS`), and iteration statistics (lines 317-336 in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c))
- **`b3Sweep`**: Represents the linear-angular interpolation of a body's motion, storing initial and final transforms plus the local center offset
- **`b3TOIOutput`**: The standardized result format containing fractional time, contact point, normal, and algorithm-specific iteration counts

## Implementing CCD in Your Project

### Enabling Continuous Collision Detection

To force CCD on a specific body, set the fast body flag manually:

```c
// In src/physics_world.c, bodies are flagged automatically based on velocity,
// or you can manually enable CCD:
b3BodySim* bodySim = b3GetBodySim(world, body);
bodySim->flags |= b3_isFast;  // or b3_isBullet for bullet-style detection

```

The solver asserts that flagged bodies are processed in `b3SolveContinuous` via `B3_ASSERT(fastBodySim->flags & b3_isFast)`.

### Automatic Velocity-Based Triggering

Box3D automatically flags bodies when velocity exceeds safety thresholds:

```c
b3Vec3 highVelocity = {50.0f, 0.0f, 0.0f};  // Fast-moving projectile
b3SetLinearVelocity(world, bodyId, highVelocity);
// Automatically triggers CCD in physics_world.c if |v| > b3_LINEAR_SLOP * 20

```

### Retrieving Sensor Hits

CCD supports sensor detection for triggers and detectors:

```c
// After b3World_Step:
b3ContinuousContext* ctx = &fastBodySim->continuousContext;
for (int i = 0; i < ctx->sensorCount; ++i) {
    b3SensorHit* hit = &ctx->sensorHits[i];
    float fraction = ctx->sensorFractions[i];
    // Process sensor trigger at time fraction
}

```

## Key Source Files

The CCD implementation spans these critical files in the `erincatto/box3d` repository:

| File | Function | Role |
|------|----------|------|
| [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c) | `b3SolveContinuous`, `b3ContinuousQueryCallback` | Main CCD pipeline and context management |
| [`src/shape.c`](https://github.com/erincatto/box3d/blob/main/src/shape.c) | `b3ShapeTimeOfImpact` | Shape-specific swept collision algorithms |
| [`src/physics_world.c`](https://github.com/erincatto/box3d/blob/main/src/physics_world.c) | Body flagging logic | Velocity threshold checking and CCD integration |
| [`src/dynamic_tree.c`](https://github.com/erincatto/box3d/blob/main/src/dynamic_tree.c) | Tree queries | Broad-phase AABB queries for sweep paths |

## Summary

- **Box3D CCD** runs between broad-phase and discrete solving, computing exact collision times for fast-moving bodies
- **b3_isFast/b3_isBullet** flags trigger the sweep-based collision detection pipeline
- **b3Sweep** structures capture motion paths with precision-safe re-centering
- **Time-of-impact** calculations determine the exact fractional time when shapes first contact
- **Sub-step integration** advances bodies only to the collision point, preventing tunneling completely
- **Sensor support** allows CCD to capture trigger events without generating contact impulses

## Frequently Asked Questions

### What is the difference between b3_isFast and b3_isBullet in Box3D?

Both flags trigger continuous collision detection, but **`b3_isBullet`** typically indicates extremely high-velocity projectiles that require special handling, while **`b3_isFast`** marks general high-velocity bodies. The CCD pipeline processes both identically in `b3SolveContinuous`, but bullet bodies may have different default thresholds or filtering behaviors depending on the specific simulation configuration in [`physics_world.c`](https://github.com/erincatto/box3d/blob/main/physics_world.c).

### How does Box3D handle CCD for sensor shapes versus solid bodies?

The **`b3ContinuousQueryCallback`** stores sensor hits separately from solid contacts in the `b3ContinuousContext` structure. While solid hits immediately reduce the simulation time step and generate contact constraints, sensor hits populate a dedicated buffer (up to `B2_MAX_CONTINUOUS_SENSOR_HITS`) that developers can query after the step. This allows triggers to fire without affecting the physics resolution of the fast body.

### Can Box3D CCD handle collisions between two fast-moving dynamic bodies?

Yes. The pipeline queries all dynamic tree proxies, including other fast bodies. When two flagged bodies approach each other, each runs its own CCD sweep against the other. The solver computes TOI for both perspectives and handles the earliest collision first, then potentially re-runs CCD for the remaining time step if additional collisions occur.

### Why does Box3D re-center sweeps on the body's base position?

**Floating-point precision** degrades when calculating swept collisions far from the origin. By re-centering the **`b3Sweep`** on the body's initial position (`base`) as seen in [`src/solver.c`](https://github.com/erincatto/box3d/blob/main/src/solver.c) (lines 886-894), Box3D ensures that relative motion calculations maintain numerical stability, particularly important for small objects moving at high speeds across large world coordinates.