Raycast
CpuMeshBvh is one mesh's tree on the CPU, in the same layout the GPU reads. Raycasts, shapecasts, bvhcasts and
closest points all run one walk and one triangle test. It needs no GPU and no engine; three's types pass where points,
rays, boxes and matrices are asked for.
import { BVH_DOUBLE_SIDE, CpuMeshBvh, type MeshInput } from '@pmndrs/bvh';
const tree = await CpuMeshBvh.create(input, { maxLeafPrimitives: 8 }); // tinybvh, on this thread
Other ways to get one:
CpuMeshBvh.build(input, tinyBvhBuilder, options), synchronously with a builder you loaded;new CpuMeshBvh(layout, { source })over any tree in the layout (a GPU build read back, a file);generateCpuMeshBvh(pool, input)from@pmndrs/bvh/worker, off the main thread;CpuMeshBvh.deserialize(bytes)(Serialization).
Quantized trees are decoded to 64-byte f32 nodes when the tree is made. Triangles are 48-byte records by default
(indexed trees are expanded to them), each a copy of its corners. To keep 16 bytes per triangle instead, pass
corners: 'positions' (to the constructor, build or create; it needs source): the tree keeps indexed triangles
(tinybvh writes them, a records tree is converted) and reads every corner from source.positions by reference. Every
query and refit gives the same answer bit for bit. The corners are then the source's live values, so after editing the
positions call refit() before the next query; serialize() writes an indexed file, which loads with the same
geometry as source. three's BvhBoundsTree does this for indexed trees on its own
(The tree object).
Raycast
const ray = { origin: [0, 0, 5], direction: [0, 0, -1] }; // or a three Ray
const hits = tree.raycast(ray, { side: BVH_DOUBLE_SIDE, near: 0, far: 100 }); // every hit, nearest first
const first = tree.raycastFirst(ray, BVH_DOUBLE_SIDE); // or null
if (first) console.log(first.distance, first.point, first.triangle, first.u, first.v, first.frontFace);
raycast(ray, options)returns every hit, nearest first.raycastFirst(ray, options)returns the nearest or null.side:BVH_FRONT_SIDE(default),BVH_BACK_SIDEorBVH_DOUBLE_SIDE, three's values. A threeMaterialpasses as the side, as in three-mesh-bvh. Side culling is native to the CPU walk; a double-sided raycast is the GPU walk exactly.nearandfarbound the distance (in the ray's units, whatever the direction's length).- A hit has
distance,t,point,triangle(the original index),uandv(weights of corners 1 and 2) andfrontFace.
A layout from a file is checked when the tree is made (children in range, every node reached once, leaves and original indices within the triangles), so a corrupt one throws instead of hanging a walk.
Tree positions and triangles
Triangles are stored in tree order. resolveTriangleIndex(treePosition) maps a position (from traverse or
bvhcast) to the original triangle, and treePositionOf(triangle) back. getTriangle(treePosition) returns the nine
corner floats as the tree stores them. The source geometry is never reordered.
Speed
Raycasts per second, Node 22 on the Apple M5 while other jobs ran (bench/three-raycast.ts --seconds 2, three runs,
median; a 100,000-triangle torus knot, rays from outside, 81% hit; indicative):
Next: Shapecast and bvhcast.
See also
- Shapecast and bvhcast and Closest point: the rest of the CPU queries.
- CPU raycast in three: the same tree behind three's
Raycaster. - Ray batches and the CPU walk: the CPU port of the GPU walk, for checking it.