Number of objects currently indexed. Triggers a rebuild if dirty.
Tears down event subscriptions and releases the BVH plus the per-mesh AABB cache.
Streams AABB hits to a visitor. Stops when cb returns false.
Streams frustum hits to a visitor. Stops when cb returns false.
Streams ray hits to a visitor callback. Stops when cb returns false.
Hits are NOT sorted — they're emitted in the BVH's traversal order, which is roughly near-to-far but not guaranteed. If you need strictly sorted hits, use intersectRay or sort the collected hits yourself.
Optionaloptions: SceneCollisionRayOptionsCombined world-space AABB of the listed objects, or null when none
of them are indexed. Triggers a rebuild if dirty.
Returns a fresh AABB instance — safe to mutate without affecting cached state.
World-space AABB of one indexed mesh. Lazily computed and cached; recomputed on the next call after the mesh moves or its matrix is changed.
Independent of the BVH — does not trigger a tree rebuild.
The mesh to query.
The mesh's world-space AABB. The returned buffer is owned by the index; do not mutate.
World-space AABB of one indexed object by ID, or null when the
object isn't in the scene (or has no usable geometry). Lookup is O(1)
via the build-time id→leaf index. Triggers a rebuild if dirty.
World-space AABB of the entire indexed scene (the BVH root's bound),
or null when the scene is empty. Triggers a rebuild if dirty.
World-space center of the entire indexed scene's AABB. Triggers a BVH rebuild if dirty. Returns the zero vector when the scene is empty.
The returned buffer is owned by the index; do not mutate.
Returns every object whose world AABB overlaps the given query AABB.
Uses the same inside/intersect/outside fast paths as the frustum walk: if the query AABB fully contains a node's AABB, we collect every leaf below it without further tests.
Returns every object whose world AABB intersects the given frustum.
Skips both fully-outside branches and tests within fully-inside ones — a node entirely inside the frustum has every descendant inside too, so we collect its leaves without per-AABB testing.
Returns every object whose world AABB is hit by the ray, sorted by
tEnter ascending (nearest first).
For "find first hit" patterns, prefer forEachInRay — it stops at the caller's discretion and avoids allocating an array.
Ray origin in world space.
Ray direction in world space. Does not need to be normalised
— tEnter/tExit are returned in dir-multiples either way.
Optionaloptions: SceneCollisionRayOptionsOptional tMin/tMax clip bounds (defaults: 0 /
Infinity).
Forces an immediate rebuild of the BVH. Normally callers don't need this — the next query rebuilds automatically — but it's useful in benchmarking and in the rare case where you want to amortise build cost outside the query path.
Spatial index for fast ray, frustum, and AABB queries against a Scene.
Maintains its own per-object world-space AABBs and a hierarchical bounding-volume tree on top, so queries can skip whole branches of the scene at once. Self-contained — no dependency on other index helpers.
The index is lazy and self-maintaining: any scene mutation (object created, destroyed, or moved; model destroyed) marks the tree dirty, and the next query rebuilds. Build cost is
O(N log N)and runs at most once between mutations, regardless of how many queries fire after.Why lazy rebuild instead of incremental refit? BIM scenes are typically static once loaded — load triggers a flurry of
onSceneObjectCreatedevents, then the tree settles. Lazy rebuild absorbs all of those in a single tree build at the next query, instead of N separate insert/refit passes. For interactive editing of small numbers of objects, query latency stays bounded because rebuild cost scales with object count, not mutation count.Hierarchy strategy: top-down median-centroid split on the longest axis of the parent's AABB. Leaves stop subdividing at
LEAF_THRESHOLDobjects. This is faster to build than a full SAH BVH and queries within ~5–15% of SAH for the typical near-uniform AABB distribution of building models.Queries:
Each query has a
forEachIn…streaming variant that takes a visitor callback. Returningfalsefrom the callback halts iteration — useful for "find first hit" or "any inside?" patterns where you don't want to pay for the full result set.