voxolith

voxelRaycast()

CPU voxel raycast (Amanatides-Woo DDA), the same walk shaders/trace.wesl does on the GPU minus the coarse skip. dir need not be normalised; t is in units of dir (so origin + dir t is the hit point). Cells count as solid when data[i] !== 0 unless a solid predicate is given. Returns null when...

function voxelRaycast(
   size: object, 
   data: Uint8Array, 
   origin: Vec3, 
   dir: Vec3, 
   maxT: number, 
   solid?: (value: number, x: number, y: number, z: number) => boolean
): VoxelHit | null;

Defined in: ray.ts:102

CPU voxel raycast (Amanatides-Woo DDA), the same walk shaders/trace.wesl does on the GPU minus the coarse skip. dir need not be normalised; t is in units of dir (so origin + dir * t is the hit point). Cells count as solid when data[i] !== 0 unless a solid predicate is given. Returns null when nothing is hit within maxT.

Headless and cheap (at most one step per cell crossed), so it suits per-frame collision tests as well as picking. It walks one dense grid; a scene held only as bricks has no such array to pass.

Parameters

ParameterTypeDescription
size{ x: number; y: number; z: number; }Grid extent in voxels.
size.xnumber-
size.ynumber-
size.znumber-
dataUint8ArrayDense grid, x + y*sx + z*sx*sy.
originVec3Ray start in grid space; it may lie outside the grid.
dirVec3Ray direction; its length sets the unit of t and maxT.
maxTnumberGive up beyond origin + dir * maxT.
solid?(value: number, x: number, y: number, z: number) => booleanNarrows which non-zero cells stop the ray (water, glass, the shooter's own voxels).

Returns

VoxelHit | null

The first solid cell and its entry face, or null.

Example

import { voxelRaycast, type Vec3 } from "@voxolith/renderer/core";

// Will a ball at `p` moving by `move` this step hit anything?
const hit = voxelRaycast(size, data, p, move, 1);
if (hit) {
  const at: Vec3 = [p[0] + move[0] * hit.t, p[1] + move[1] * hit.t, p[2] + move[2] * hit.t];
  bounce(at, hit.normal);
}

On this page