ReadonlydeviceReadonlygpuReadonlynodeReadonlypositionReadonlytop'gpu' (the GPU top level, refit) on a device with subgroups; 'cpu' otherwise or when asked.
ReadonlytriangleThe scene's triangle format, its triangles' unit, and (indexed) the positions and their layout.
The buffers traceModuleWgsl binds (bvhTraceNodes, bvhTraceTriangles,
bvhTraceTopLevel, and with triangleLeaves: 'indexed' the fourth,
bvhTracePositions; null for records).
True while a mesh with instances waits for its first tree, or (GPU top level) a shape is being built.
Deforming meshes' refits, drift rebuilds, poses skipped without subgroups, and CPU poses written (setPoseCpu).
Whether first trees come from the GPU, and if not, why.
True while tinybvh trees are still being built (each replaces a GPU tree, or is a mesh's first).
Instances in the current top level (traced, or with the GPU top level also hidden ones in its shape).
The buffers as TraceBatchKernel takes them; the same object until version changes.
Node buffer trims done so far.
Whether tinybvh builds run in workers.
Increments whenever one of buffers is replaced: make bind groups
again then. Usually in an update; with gpuMaxBatchBytes also between
updates, when a capped batch grows the buffers on its own (the old ones
stay valid until the next update).
Adds an instance of a mesh. It becomes visible to rays once its mesh has a tree and (GPU top level) a shape holds it.
Adds a mesh. Its trees are built from the next update. The
geometry is read then (and copied into the worker, unless
transferGeometry). Returns the mesh's handle.
StaticcreateMakes the scene and compiles its kernels (the GPU builder first: the first trees wait on it).
A deforming mesh's latest refit cost over its built tree's (NaN before a sample, or without subgroups).
A merged group's tree, by group index (0 to counts.mergedGroups - 1).
An instance's merge state: mergedInto is the static group it was
merged into at the first update (then it can't move, change or be
removed), or null.
A mesh's tree: which builder made it, why not the GPU, where it is.
Places a tree the app built itself (a worker's own GPU device, a file,
any builder writing the GPU layout) as the mesh's tree from the next
update, in place of whatever it has. It is final, as tinybvh's tree is:
a GPU tree or tinybvh's that comes later for the mesh is dropped, a
tinybvh build still running is cancelled, and (given before the first
update that brings the mesh) no builds start for it at all. A deforming
mesh's drift rebuilds still replace it. onTree reports it with
builtBy: 'app', as do meshStatus and counts.onAppTrees.
The layout must be the mesh's (built from the geometry given to
addMesh, with its triangle flags and materials) in the scene's
triangle format; f32 nodes are quantized here for a quantized scene,
and a quantized tree needs a quantized scene. A deforming mesh needs an
f32 tree (its refit works on one). An indexed scene welds each mesh's
vertices unless weld: false (or the mesh deforms), and an indexed tree
built on the mesh's own vertices doesn't name the welded ones: such a
scene refuses it. Throws a RangeError for these and for a tree deeper
than gpuMaxDepth. The layout's arrays are read at the update; keep them
unchanged until then.
A baked file is placeTree(mesh, deserializeLayout(bytes, { geometry }))
with the geometry given to addMesh: deserializeLayout refuses a file
baked from other geometry when it was written with its own
(serializeLayout(layout, { geometry })).
Rebuilds a deforming mesh's tree now, with the GPU builder (H-PLOC),
from its latest GPU pose (setPose), rather than waiting for the
drift rule: the next update starts it (one rebuild per update, so
several asked at once go one after another), and the update after its
batch comes back places it (as a drift rebuild is: onBuild events with
rebuild: true, deformingCounts.driftRebuilds counts it). Returns
false, and does nothing, when there is nothing to rebuild from: no GPU
builder (no subgroups), no GPU pose yet, or no tree placed yet; true
when it is asked (again, while it is asked or building, too).
Optionalwhich: (mesh: number) => booleanRemoves an instance. Throws for a merged static instance.
Removes a mesh no instance uses. Its tree's range is freed; a build still running is cancelled or ignored.
A deforming mesh's new deform box: the box ([minX, minY, minZ, maxX, maxY, maxZ], object space) every pose from now on stays in, which the
top level holds for its instances. For poses that leave the box given at
addMesh (a skinned character walking: @pmndrs/bvh/three's sync
sets it from the skeleton each update). Every instance of the mesh is
refit into the top level at the next update. Without subgroups
(poses are skipped) it does nothing.
Turns holdRefinements on or off from now on (off releases every held tree at the next update).
Hides, shows, re-masks or re-materials an instance. A merged instance's visibility and mask can't change.
A deforming mesh's new pose: its moved vertices on the GPU (the mesh's
own indices and triangle order, STORAGE usage; COPY_SRC too for drift
rebuilds). The next update refits its tree from them, before anything
traces. Without subgroups the pose is skipped (counted in
deformingCounts.posesSkipped, with one warning) and rays see the pose
the tree was built in: give such devices the pose on the CPU with
setPoseCpu instead.
A deforming mesh's new pose on the CPU: positions holds x, y, z per
vertex in the mesh's own numbering (as many vertices as the geometry it
was added with; its indices and triangle order are kept). The next
update refits the mesh's tree on the CPU (refitLayoutToPose),
writes its nodes (re-quantized with nodeFormat: 'quantized') and its
corners (indexed: its vertices in the positions buffer) through the
update's encoder, and moves the mesh's top-level box to the posed
tree's root box. With or without subgroups: the path for devices
without them, where setPose is skipped, and for poses an app
computes on the CPU. A tree that arrives later (a swap, a rebuild) gets
the latest pose too. It costs a pass over the triangles and an upload
of the mesh's nodes and triangles per pose, so it suits small meshes or
a few poses a second. positions is read at the update; it must not
change before then. With subgroups a later setPose replaces it;
without them setPose is skipped, so this pose (or a later setPoseCpu) stays.
Moves an instance. Throws for a merged static instance.
Trims the node buffer at the next quiet update (no GPU build running, no
tree waiting to be placed, no tinybvh build left, nothing reserved): the
placed trees are packed to the front of a new buffer of their exact size,
in that update's encoder, and the old buffer is freed at the update
after. The growth room goes (and, with gpuTreeOutputs: 'adopt', the
load batch's one node per triangle), so a later tree makes the buffer
grow again. Trees move: version changes (make bind groups
again), and placements read before (meshStatus) are stale. The
scene's deforming meshes follow by themselves. trimWhenRefined asks
for it whenever the scene is quiet and the buffer has room to give.
Brings the buffers up to date: starts builds for meshes added since the
last update, places trees that arrived (writes, and copies recorded in
encoder), and brings the top level up to date. Call once per frame
before anything traces; the caller submits encoder, before the next
update (the next update frees what this frame's commands still read,
and a capped batch stored between updates relies on it: see the file
comment).
Throws in two ways:
RangeErrors of the top level: one too
deep for the trace stack, or past a device limit). The frame's tree
copies, refits and growth copies are in encoder, and the buffers they
replace are destroyed at the next update: submit the encoder anyway,
or the scene's buffers hold empty trees.options.timestampWrites times what the update records (see
SceneTreesUpdateOptions). Its two passes are recorded however
the update ends, a throw included, so the pair is always complete; an
update that throws before recording anything else leaves only them in
the encoder.
Resolves once every mesh with instances has its first tree (or none is coming) and no top-level shape is building. They reach the GPU at the next update.
Builds start in update, not in addMesh. A mesh added since
the last update counts as waiting, so awaiting this with no update to
come never resolves. Call update once after adding meshes (and keep
calling it once per frame), then await:
frame(); // update(encoder) + submit: starts the builds
await trees.whenBuilt(); // every mesh with instances has a tree
frame(); // places them
After that one update it needs no more, with gpuMaxBatchBytes too (its
batches go on between updates). Only when a capped batch has to wait for
an update (see the file comment) do the meshes behind it count once
tinybvh's tree is in. A frame loop that is already running needs nothing
extra: its next update starts the builds.
Resolves once every tinybvh build has finished (held trees still wait for releaseRefinements). Like whenBuilt, it needs an update after addMesh to start them.
The scene's node format, its nodes' unit, and the slots before each root (the frame slot when quantized).