# How to Perform Spatial Queries Using Ray Casts and Shape Casts in Box3D's Broad-Phase

> Learn how to perform spatial queries with ray casts and shape casts using Box3D's broad-phase. Discover efficient AABB overlap testing for faster intersection routines.

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

---

**Box3D's broad-phase accelerates spatial queries by walking three dynamic trees, testing AABB overlaps before invoking shape-specific intersection routines for both ray casts and shape casts.**

The erincatto/box3d physics engine provides high-performance spatial query capabilities through its dynamic tree-based broad-phase architecture. Understanding how to perform spatial queries using ray casts and shape casts in Box3D's broad-phase is essential for implementing gameplay mechanics like weapon traces, visibility checks, and predictive collision sweeps.

## Ray Cast Queries

Ray casts test intersection between a line segment and the world geometry. Box3D implements this by first pruning candidates using the broad-phase dynamic trees, then performing precise shape-level intersection tests.

### World-Level Entry Points

The primary API for ray casting is `b3World_CastRay` and its convenience variant `b3World_CastRayClosest`, both defined in [`src/physics_world.c`](https://github.com/erincatto/box3d/blob/main/src/physics_world.c) at lines 2892 and 2973 respectively. These functions accept a ray origin, direction vector, and collision filter, then return intersection results containing the hit fraction, normal, and shape identifier.

### Broad-Phase Traversal

When `b3World_CastRay` executes, it constructs a `b3RayCastInput` structure containing the origin, direction, and maximum fraction. The function then invokes `b3DynamicTree_RayCast` on each of the three internal dynamic trees (one per body type). 

The callback function `RayCastCallback`, located around line 2841 in [`src/physics_world.c`](https://github.com/erincatto/box3d/blob/main/src/physics_world.c), receives every proxy whose AABB overlaps the ray. This early pruning prevents expensive shape intersection tests on distant objects.

### Shape-Level Dispatch

Inside `RayCastCallback`, the engine creates a local copy of the ray input and dispatches to `b3RayCastShape` at [`src/shape.c`](https://github.com/erincatto/box3d/blob/main/src/shape.c) line 794. This dispatcher selects the appropriate intersection routine based on the shape type (sphere, capsule, convex hull, or mesh).

### Per-Shape Implementation

Each concrete shape implements its own ray intersection routine:

- **Sphere**: `b3RayCastSphere` in [`src/sphere.c`](https://github.com/erincatto/box3d/blob/main/src/sphere.c) at line 60
- **Capsule**: `b3RayCastCapsule` in [`src/capsule.c`](https://github.com/erincatto/box3d/blob/main/src/capsule.c) at line 99
- **Convex Hull**: `b3RayCastHull` in [`src/hull.c`](https://github.com/erincatto/box3d/blob/main/src/hull.c)
- **Mesh**: `b3RayCastMesh` in [`src/mesh.c`](https://github.com/erincatto/box3d/blob/main/src/mesh.c)

These routines perform exact ray-versus-shape intersection tests and return the hit fraction and surface normal.

### Result Handling

The `b3World_CastRay` function calls a user-provided callback for every hit encountered along the ray, while `b3World_CastRayClosest` returns only the nearest intersection. Results include the hit point, surface normal, fraction along the ray, and the `b3ShapeId` of the colliding shape.

## Shape Cast Queries

Shape casts (also called sweep tests or box casts) determine whether a moving shape collides with the world. Unlike ray casts, shape casts account for the volume of the querying geometry.

### World-Level Entry Point

Shape casts are exposed via `b3World_ShapeCast`, implemented at line 3029 in [`src/physics_world.c`](https://github.com/erincatto/box3d/blob/main/src/physics_world.c). This function uses the `WorldShapeCastContext` structure and the `b3ShapeCastCallback` internal callback to process potential intersections.

### Broad-Phase Traversal

The world constructs a `b3BoxCastInput` containing the initial transform, sweep translation, and maximum fraction. It then calls `b3DynamicTree_BoxCast` on each dynamic tree. The callback receives proxies whose AABBs overlap the swept shape's bounding volume.

### Shape-Level Dispatch

The callback creates a local copy of the cast input and forwards it to `b3ShapeCastShape` at [`src/shape.c`](https://github.com/erincatto/box3d/blob/main/src/shape.c) line 830. This dispatcher prepares the cast shape and static shape pair for intersection testing.

### Per-Shape Implementation

Each shape type implements a specialized shape-cast routine that reuses the generic `b3Shape` collision framework:

- **Capsule**: `b3ShapeCastCapsule` in [`src/capsule.c`](https://github.com/erincatto/box3d/blob/main/src/capsule.c) at line 342
- **Convex Hull**: `b3ShapeCastHull` in [`src/hull.c`](https://github.com/erincatto/box3d/blob/main/src/hull.c) at line 2616
- **Sphere**: `b3ShapeCastSphere` in [`src/sphere.c`](https://github.com/erincatto/box3d/blob/main/src/sphere.c)

These functions internally create a pair of the static target shape and the moving cast shape, then invoke the appropriate contact generation routine to determine the time of impact.

### Result Handling

Shape casts return a `b3CastOutput` structure containing the hit fraction, contact normal, contact point, and the identifier of the struck shape. A fraction less than 1.0 indicates a collision occurred before the sweep completed.

## Broad-Phase Architecture Benefits

Box3D maintains three separate dynamic trees—one for static bodies, one for kinematic bodies, and one for dynamic bodies—implemented in [`src/dynamic_tree.c`](https://github.com/erincatto/box3d/blob/main/src/dynamic_tree.c) and managed by [`src/broad_phase.c`](https://github.com/erincatto/box3d/blob/main/src/broad_phase.c). 

The functions `b3DynamicTree_RayCast` and `b3DynamicTree_BoxCast` perform conservative AABB tests before invoking expensive per-shape intersection routines. This hierarchical pruning reduces the query complexity from O(n) to O(log n) in sparse worlds, making large simulations feasible.

Additionally, the broad-phase automatically filters queries using `categoryBits` and `maskBits` stored in each proxy, ensuring spatial queries respect collision layer configurations without additional user-level filtering.

## Practical Implementation Examples

The following examples demonstrate how to perform spatial queries against a Box3D world.

### Ray Cast Closest Hit

```c
// Cast a ray downward from (0,5,0) to find the nearest intersection
b3WorldId world = b3World_Create();
b3Pos origin = { .x = 0.0f, .y = 5.0f, .z = 0.0f };
b3Vec3 direction = { .x = 0.0f, .y = -1.0f, .z = 0.0f };

b3QueryFilter filter = {
    .maskBits = 0xffffffffu,
    .categoryBits = 0xffffffffu
};

b3RayResult hit = b3World_CastRayClosest(world, origin, direction, filter);

if (hit.fraction < 1.0f) {
    printf("Hit shape %d at distance %.3f\n", hit.shapeId, hit.fraction);
}

```

### Capsule Shape Cast

```c
// Sweep a capsule downward to detect collision before moving
b3Capsule capsule = { .radius = 0.5f, .halfHeight = 2.0f };
b3ShapeId capsuleId = b3World_CreateCapsuleShape(world, &capsule);

b3ShapeCastInput sweep = {
    .shape = capsuleId,
    .transform = b3Transform_Translate(b3Pos_zero, (b3Vec3){0,0,0}),
    .translation = (b3Vec3){0, -3, 0},
    .maxFraction = 1.0f
};

b3CastOutput result = b3World_ShapeCast(world, &sweep, filter, NULL, NULL);

if (result.fraction < 1.0f) {
    printf("Capsule hit shape %d after moving %.2f units\n",
           result.shapeId, 
           result.fraction * b3Vec3_Length(sweep.translation));
}

```

## Summary

- **Box3D's broad-phase** uses three dynamic trees to accelerate spatial queries, separating static, kinematic, and dynamic bodies for optimal traversal.
- **Ray casts** enter through `b3World_CastRay` or `b3World_CastRayClosest`, traverse trees via `b3DynamicTree_RayCast`, and dispatch to shape-specific routines like `b3RayCastSphere`.
- **Shape casts** use `b3World_ShapeCast`, traverse via `b3DynamicTree_BoxCast`, and delegate to implementations such as `b3ShapeCastCapsule` for precise swept-volume tests.
- **AABB pruning** in the broad-phase prevents expensive shape intersection tests on distant objects, maintaining O(log n) query performance.
- **Collision filtering** is automatically applied using proxy category and mask bits during tree traversal.

## Frequently Asked Questions

### What is the difference between b3World_CastRay and b3World_CastRayClosest?

`b3World_CastRay` reports every intersection along the ray to a user-provided callback function, allowing you to process multiple hits. `b3World_CastRayClosest` returns only the nearest intersection and is optimized for queries where you only need the first collision point, such as ground checks or line-of-sight tests.

### How does Box3D filter which objects are tested during spatial queries?

Box3D uses bit masks stored in each broad-phase proxy to filter collisions efficiently. The `b3QueryFilter` structure contains `categoryBits` (what layers the query belongs to) and `maskBits` (what layers the query can hit). During tree traversal in `b3DynamicTree_RayCast` and `b3DynamicTree_BoxCast`, the engine skips proxies that fail the bitwise layer check, preventing unnecessary intersection tests against filtered bodies.

### Can I perform shape casts with any shape type in Box3D?

Yes, Box3D supports shape casts for all primitive types. Each shape implements a specific routine—such as `b3ShapeCastCapsule` in [`src/capsule.c`](https://github.com/erincatto/box3d/blob/main/src/capsule.c) and `b3ShapeCastHull` in [`src/hull.c`](https://github.com/erincatto/box3d/blob/main/src/hull.c)—that handles the swept-volume intersection. The dispatcher `b3ShapeCastShape` in [`src/shape.c`](https://github.com/erincatto/box3d/blob/main/src/shape.c) automatically routes the query to the appropriate implementation based on the shape type identifier.

### Why does Box3D use three separate dynamic trees for the broad-phase?

Box3D maintains separate dynamic trees for static, kinematic, and dynamic bodies to optimize query patterns. Static bodies rarely move, allowing their tree to be highly optimized and rarely rebalanced. Dynamic bodies move frequently and require frequent updates. This separation allows ray casts against static geometry to traverse a stable, optimized structure while dynamic objects are handled separately, improving cache coherence and traversal speed.