@pmndrs/bvh
    Preparing search index...

    Class SceneTrees

    Index
    device: GPUDevice
    gpuTreeOutputs: GpuTreeOutputsMode

    The scene's node format, its nodes' unit, and the slots before each root (the frame slot when quantized).

    positionLayout: PositionLayout
    topLevelPath: "gpu" | "cpu"

    'gpu' (the GPU top level, refit) on a device with subgroups; 'cpu' otherwise or when asked.

    triangleLeaves: GpuLayoutTriangleLeaves

    The scene's triangle format, its triangles' unit, and (indexed) the positions and their layout.

    • get buffers(): {
          nodes: GPUBuffer;
          positions: GPUBuffer
          | null;
          topLevel: GPUBuffer;
          triangles: GPUBuffer;
      }

      The buffers traceModuleWgsl binds (bvhTraceNodes, bvhTraceTriangles, bvhTraceTopLevel, and with triangleLeaves: 'indexed' the fourth, bvhTracePositions; null for records).

      Returns {
          nodes: GPUBuffer;
          positions: GPUBuffer | null;
          topLevel: GPUBuffer;
          triangles: GPUBuffer;
      }

    • get building(): boolean

      True while a mesh with instances waits for its first tree, or (GPU top level) a shape is being built.

      Returns boolean

    • get gpuFirstTrees(): { enabled: boolean; reason: string | null }

      Whether first trees come from the GPU, and if not, why.

      Returns { enabled: boolean; reason: string | null }

    • get refining(): boolean

      True while tinybvh trees are still being built (each replaces a GPU tree, or is a mesh's first).

      Returns boolean

    • get topLevelInstanceCount(): number

      Instances in the current top level (traced, or with the GPU top level also hidden ones in its shape).

      Returns number

    • get version(): number

      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).

      Returns number

    • A deforming mesh's latest refit cost over its built tree's (NaN before a sample, or without subgroups).

      Parameters

      • mesh: number

      Returns number

    • A merged group's tree, by group index (0 to counts.mergedGroups - 1).

      Parameters

      • group: number

      Returns SceneMeshStatus & { sourceInstances: readonly number[] }

    • 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 })).

      Parameters

      Returns void

    • 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).

      Parameters

      • mesh: number

      Returns boolean

    • Parameters

      • Optionalwhich: (mesh: number) => boolean

      Returns void

    • Removes an instance. Throws for a merged static instance.

      Parameters

      • instance: number

      Returns void

    • Removes a mesh no instance uses. Its tree's range is freed; a build still running is cancelled or ignored.

      Parameters

      • mesh: number

      Returns void

    • 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.

      Parameters

      • mesh: number
      • bounds: ArrayLike<number>

      Returns void

    • Turns holdRefinements on or off from now on (off releases every held tree at the next update).

      Parameters

      • hold: boolean

      Returns void

    • 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.

      Parameters

      Returns void

    • 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.

      Parameters

      • mesh: number
      • positions: Float32Array

      Returns void

    • Moves an instance. Throws for a merged static instance.

      Parameters

      • instance: number
      • objectToWorld: Mat4

      Returns void

    • 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.

      Returns void

    • 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:

      • Before recording anything: a tree that needs the node or triangle buffer past a device limit (both growths are checked before either happens; the trees stay queued, so the next update throws again), or a tinybvh build that failed (reported once). The encoder is untouched and the scene is as it was: the caller may drop the frame.
      • After recording the frame (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.

      Parameters

      Returns void

    • 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.

      Returns Promise<void>