Luxar Viewer API Documentation - v2026.9.22
    Preparing search index...

    Tracks loaded lod_group and kind=partition nodes in a scene; evaluates per-frame to pick the active LOD and frustum-visible parts.

    Index
    entries: Map<string, LODGroupEntry> = ...
    partitionEntries: Map<string, PartitionGroupEntry> = ...
    partitionCaches: Map<
        string,
        {
            children: {
                source: PartitionGroupEntry;
                localBoxScratch: BoundingBox;
                worldBoxOptions: WorldBoxOptions;
                footprintBox: Box3;
                footprintMatrixWorld: number[];
                footprintDirty: boolean;
            }[];
        },
    > = ...
    caches: Map<string, LODGroupEntryCache> = ...

    Per-entry register-time cache (parallel to entries by path). Populated by :meth:register. Stored on the side so the public LODGroupEntry interface stays test-friendly (callers don't have to compute thresholds or allocate scratch boxes).

    matrixScratch: number[] = ...

    Reused per frame to feed transformBoundingBox's matrix arg.

    tick: number = 0

    Monotonic per-frame counter. Each frame the active (visible) child of every entry is stamped with the current tick; the resident-byte eviction LRU evicts the lowest-tick (coldest) loaded levels first.

    warnedNoReadyChild: Set<string> = ...

    Entry paths already warned about a missing-ready-child invariant break in coarsestReadyIndex. The off-screen gate calls that method every frame, so without a dedupe a genuinely-stuck group (eager default failed to attach) would log at frame rate. One warning per entry surfaces the break without the flood.

    warnedEmptyLevel: Set<string> = ...

    Entry paths already warned about the fresh-but-empty display guard firing (see evaluateEntry). The guard is evaluated every frame, so without a dedupe a persistently inconsistent dataset would warn at frame rate.

    settleTracker: SettleTracker = ...

    Tracks when the global view-update version last changed (in ticks) so the selector can defer a stale fine level's reload until the scrub settles — the debounce behind maybeKickReload. See scene/lod-freshness.ts.

    fadeWasManaged: boolean = false

    Whether fade management (cross-fade and/or energy compensation) was ON during the previous evaluatePerFrame. Falling-edge detector for the one-shot residual-opacity restore in evaluateEntry: toggling BOTH anti-popping flags off MID-fade would otherwise strand a half-faded level's opacity forever (manageFade === false skips the per-frame restore branch). Updated once per frame after all entries are evaluated; one restore pass on the edge keeps the both-flags-off steady state byte-identical (no material writes, no subtree traversal).

    partitionResyncPending: Map<string, Set<string>> = ...

    Partition rising edges waiting for their wrapper to be visible and the loader to be idle: wrapper path → the re-entering PART paths. A set that contains the wrapper path itself means "resync the whole partition" (a pathless part). Coalesced across frames; flushed as ONE requestReprocess(paths) call.

    • Mark the owning partition part's rendered footprint stale after a geometry commit.

      SceneLoader.updatePointsGeometry and the three commit*Geometry methods are the complete geometry-attach funnels, including lazy LOD children and additive rungs. They dirty the part before writing, so a commit that hands off geometry and then throws cannot leave the previous footprint cached. registerPartition starts every part dirty, which also covers a commit that races registration. A path under a partition that matches no registered child dirties the whole partition conservatively rather than allowing an under-covering stale box. Footprints are cached in world space; the per-frame gate separately detects transform changes.

      Parameters

      • nodePath: string

      Returns void

    • Drop an lod_group from the registry (called on scene teardown).

      Parameters

      • path: string

      Returns void

    • Failure reason for a latched lazy level, if that path is still failed.

      Parameters

      • path: string

      Returns string | undefined

    • Whether every lod_group and partition part that contributes pixels to the CURRENT view is already at final committed quality — i.e. one more frame of waiting would not improve what is on screen.

      Why this exists. An offline turntable capture (OfflineCaptureStrategy) takes exactly one requestAnimationFrame per exported frame. Since the rAF loop runs for the whole sweep, the auto-selector is live and frustum-aware, so a tile that leaves the frustum mid-orbit is demoted to its coarsest ready level and the resident-byte budget may release its fine one. When it swings back into view the fine level reloads ASYNCHRONOUSLY — and without a wait those frames go into the ZIP/MP4 at the coarse level and pop back a few frames later. The capture loop therefore drains on this predicate (bounded) before grabbing each frame. Forcing finest instead was deliberately rejected: a capture visits the whole scene, so peak residency would be the entire dataset.

      Partition parts block while a visible rising edge is pending, while any load pass is queued or committing and any part is visible, or while a visible stamped leaf is stale / still climbing its additive ladder. Hidden or re-culled parts are excluded because they contribute no pixels. Unstamped leaves carry no freshness or ladder signal and remain non-blocking, matching the lod-group subtree fold below.

      • A latched archive fault skips all work that could start or wait for new loads, but still waits for loads already in flight to finish committing. The latch prevents any further automatic kick, so every other unmet condition would be permanently false until an explicit or connectivity retry clears it. An already-started load still clears loading in its finally block and may commit geometry, so releasing the capture frame before that transition would allow a one-frame pop.
      • A partition leaf load failure that does not latch an archive fault has no success commit to refresh its stale stamp. The predicate remains false; the capture drain's bounded timeout and consecutive-timeout latch are the escape hatch for that failed part.

      Per entry, in order:

      • Off-screen entries are skipped entirely. offScreen means the selector is deliberately holding the group coarse because it draws nothing this frame — blocking on it would wait for a level that will never be selected while it is culled.
      • Entries that are not EFFECTIVELY VISIBLE are skipped too (a layer toggled off in the panel, or authored visible=false, anywhere up the ancestor chain). They draw nothing, and — decisively — kickDeferredLoadIfVisible refuses to START a deferred load while the group is hidden, whereas the selector's frustum test is purely geometric and still records a fine desiredChildIndex for it. Without this skip such an entry has desired !== active with nothing ever loading, failing or becoming ready, so the predicate would be PERMANENTLY false and every capture frame would burn the full drain budget before giving up.
      • An entry with no child at the aspiration index is skipped — children can legitimately be EMPTY (every level failed its getObjectByName attach in load-lod-group-node, which warns and carries on). Nothing at a non-existent index can ever become ready, so blocking on it is the permanently-false trap again: the drain would burn its whole budget on every frame and then report a degraded-LOD verdict the scene never earned. An out-of-range desiredChildIndex is the same shape but is NOT skipped — the rest of the entry is still checked and only the missing desired is let through (see its bullet below).
      • displayed !== activeChildIndex ⇒ not quiescent. A stale slice fallback or a never-downgrade hold is on screen instead of the aspiration, so what renders is not what the selector settled on.
      • desired !== activeChildIndex ⇒ not quiescent — UNLESS that desired child is failed, or absent (an out-of-range desired, handled right here rather than by skipping the entry). The aspiration only advances onto a READY level, so this is the one-frame window after a lazy load lands but before the next selector pass swaps (see LODGroupEntry.desiredChildIndex); it is also the whole in-flight load. A failed level can never become ready this frame, so blocking on it only buys a timeout — treat it as the best available and keep checking the rest.
      • The aspiration must be isReady.
      • When freshness is tracked (getViewVersion wired), the aspiration must be FRESH for the current view version — via the group-aware childFreshAndCount, not the leaf-only isFresh, so a deferred kind=partition subtree stamped for an older slice counts as stale.
      • The aspiration's additive ladder must be complete, on BOTH available signals. A lazy child's live hasMoreLODs() thunk answers first: still true means only a prefix of the level has committed. Then childFreshAndCount's subtreeLadderComplete — the commit-time committedLadderComplete stamp, taken from the child itself for a tracked LEAF and folded over the visible stamped leaves of a deferred GROUP child (a nested kind=partition / kind=lod subtree — the overview recipe's fine branch). Neither alone is enough: a group child carries no thunk, so without the fold the drain released the frame the moment such a branch became ready, with its part leaves at chunk-1 by construction; and only the DEFERRED path gets a thunk, so without the stamp the eagerly-loaded default level — still climbing its ladder under the sweep-driven refinement loop — read as complete. Anything with no stamp at all (never committed, or a non-progressive loader) carries no signal and counts as complete.
      • No child of the entry may be loading — an in-flight commit can change what renders on a later frame.

      An empty registry (and an entry-free scene) is quiescent: there is nothing to wait for.

      Called from the capture drain — once per drain rAF, so up to the drain's own frame cap (LOD_SETTLE_MAX_FRAMES, which is itself INCLUSIVE of the mandatory catch-up tick) plus one for the strategy's opening tri-state probe, per exported frame in the worst case; twice on a scene that is already settled (probe + one poll), and once on a latched frame (the re-arm probe alone). Either way NOT the rAF hot path, so unlike the rest of this file it does not avoid allocation: it walks the entry Map with for…of and resolves freshness through childFreshAndCount, which returns a fresh object per entry. That cost is genuinely irrelevant here.

      Returns boolean

    • Whether visible partition parts have no pending resync or incomplete commit. Unlike hasVisiblePendingPartitionResync, pending resyncs count here only while their specific parts contribute pixels to the capture frame.

      Parameters

      • version: number | null

      Returns boolean

    • Whether any lazy LOD-group level has an ensureLoaded fetch in flight. These promotions run outside every updateView cycle, so neither isUpdateInProgress() nor getState().isLoading sees them; the perf snapshot's isSettled does. (Deferred partition parts load through updateView and are covered by the update lock instead.)

      Returns boolean

    • Whether a visible partition has a rising-edge resync waiting for the owning loader to become idle. Unlike pendingPartitionResyncsQuiescent, this deliberately mirrors flushPartitionResyncs's wrapper-level visibility gate: every queued part under a visible wrapper is dispatched, even if that part re-exits before the flush. Pending work retained under a hidden wrapper is not actionable and must not keep wide settledness false indefinitely; neither can work when no resync dispatcher is wired.

      Returns boolean

    • Retry a LAZY lod_group level by its authored scene-node path. The path is stored beside the placeholder in LODGroupChild so anonymous deferred GROUP placeholders remain addressable without duplicating scene identity onto the THREE object.

      Clears the failure cooldown (failed/failedTick) through the shared kickDeferredLoad gate, which owns setting loading before firing ensureLoaded (the thunk itself never sets loading — only the registry does; keep that invariant here).

      Returns true when a retry was kicked OR one is already in flight (loading), false when no retryable lazy child with that path exists. Explicit retries bypass the owning loader's automatic archive-fault gate; the per-child marker keeps concurrent failed branches independently targetable. Fire-and-forget semantics: true means "retry started", not "retry succeeded" — the thunk owns the ready/failed outcome, and a repeat failure re-enters the normal cooldown cycle.

      Parameters

      • path: string

      Returns boolean

    • Update an lod_group's selector mode. 'auto' re-enables view-driven selection; { lockLevel: i } pins the lod_group to child index i (0-based in coarsest→finest order). An out-of-range lockLevel is clamped into [0, n-1] with a warning — throwing here would force every UI caller to guard against stale registry state.

      Visibility is not swapped synchronously — the next evaluatePerFrame() call will pick the new desired child. (This matches the per-frame contract for the auto path; a synchronous swap would diverge.)

      Parameters

      Returns void

    • Per-frame evaluation. Wired through AnimationController.addPerFrameCallback by the app pipeline.

      Returns true when at least one lod_group swapped its active child this frame, so the caller can refresh anything that depends on which level renders (e.g. the data-monitor's visible-element tally). Returns false on a no-op frame (the common case), which keeps the per-frame cost to the projection math alone.

      Returns boolean

    • Rising edge (rare): remember WHICH parts of wrapperPath came back so the resync can be targeted at their loaders instead of re-sweeping the whole scene. Coalesces with parts already pending for the same wrapper.

      Parameters

      • wrapperPath: string
      • parts: ReadonlySet<string>

      Returns void

    • Hand every pending rising edge whose wrapper is visible to the loader as ONE requestReprocess(paths) call; hidden wrappers stay pending, dropped wrappers are forgotten. A set that contains its own wrapper path (a pathless part) collapses to the wrapper path alone — it already covers every part.

      Returns void

    • Frustum-gate one partition's parts. Returns the PARTITION_* bit flags; parts that re-entered this frame are added to risingParts by node path (child.path), or as the WRAPPER path when a part has none so the caller resyncs the whole partition rather than missing it.

      Parameters

      • entry: PartitionGroupEntry
      • displayDims: readonly number[]
      • frustum: Frustum
      • risingParts: Set<string>

      Returns number

    • Returns true if this entry's displayed child changed.

      Parameters

      • entry: LODGroupEntry
      • camera: Camera
      • viewport: { width: number; height: number }
      • displayDims: readonly number[]
      • frustum: Frustum
      • settled: boolean

      Returns boolean

    • Fold an entry's children nD bounds into one world-space :type:BoundingBox (see computeEntryWorldBox in lod-selector-math.ts for the math). The default uses raw positionBounds for frustum gating and eviction; useLodBounds uses robust bounds with a per-child raw fallback for selector metrics. This wrapper supplies per-entry local/world scratch boxes and the registry's matrixScratch. Raw and robust metric bounds use distinct world boxes because both remain live during one selector evaluation.

      Parameters

      • entry: LODGroupEntry
      • displayDims: readonly number[]
      • useLodBounds: boolean = false

      Returns BoundingBox | null

    • Freshness + committed element count of a child, resolving a GROUP-typed LOD child (a deferred kind=partition / nested lod subtree — the overview recipe) through subtreeDisplayProgress rather than the leaf-only stamps. A bare THREE.Group carries no leaf nodeType, so isFresh would report it unconditionally fresh and visibleElementCount would return null — hiding a stale re-slice and defeating the empty guard. Mirrors the never-downgrade gate's sideProgress so both paths agree on what "fresh" means for a group. Leaf children (a direct count stamp) keep the exact pre-existing behaviour.

      fresh implies ready for every child shape: the leaf branch's isFresh is ready-gated, and a NOT-ready group child (a deferred placeholder whose subtree never committed, or a released level awaiting reload) reports fresh: false regardless of any stamps its subtree may retain — it cannot draw, so no display path (slice-aware fallback, empty-guard redirect, blend pairing) may ever elect it. A READY group with no stamped leaf (nested group with no slice-dependent geometry) carries no per-slice staleness signal and reports fresh: true, count: null.

      subtreeLadderComplete is the third answer, folded from the same walk (SubtreeDisplayProgress.complete): false when any visible stamped leaf under the subtree has committed only a prefix of its additive ladder. A tracked LEAF has no subtree to fold, so it answers with its OWN committedLadderComplete stamp.

      That stamp rather than the child's hasMoreLODs() thunk, because the thunk does not exist on every leaf: load-lod-group-node attaches it only on the DEFERRED path, so the eagerly-loaded default level — whose ladder is advanced by the sweep-driven background refinement loop — has none, and reporting an unconditional true here declared a still- streaming coarse level complete. The stamp is also the safer of the two where both exist (see lod-display-gate's "committed state only" note: a live getter flips when the last fetch resolves, frames before the commit lands). Callers still read hasMoreLODs() directly on top of this, since it is what re-fires ensureLoaded to advance a lazy ladder. Only isCaptureQuiescent consults this field; the display paths ignore it.

      version === null means no view-version tracking is wired: the per-slice staleness test is skipped and every READY child reads fresh — which is exactly what the version != null guards at the display call sites already assume, so those are unaffected.

      Parameters

      Returns { fresh: boolean; count: number | null; subtreeLadderComplete: boolean }

    • Index of the coarsest child strictly before beforeIndex that is fresh for version AND has a non-zero committed element count — or -1 when none qualifies. The group-aware counterpart of the empty-level display guard's fallback: it resolves each child through childFreshAndCount, so a fresh-but-empty GROUP child (a deferred kind=partition subtree whose visible leaves all committed 0) is correctly skipped rather than treated as non-empty (a bare THREE.Group has no leaf count stamp). A READY child with an UNTRACKED count (null — group with no stamped leaf) is accepted: the guard only redirects away from KNOWN-empty levels. The caller bounds the search at the chosen display level or the selector's aspiration, whichever is finer, so no finer level can override it. Because childFreshAndCount's fresh implies ready, a NOT-ready placeholder can never be returned — the guard must only redirect to a level that can actually draw. When nothing qualifies (-1) the caller keeps the fresh-but-empty current level: an empty-but-real level beats a blank placeholder.

      Parameters

      Returns number

    • Apply the per-leaf LOD anti-popping opacity (cross-fade weight × streaming 1/e(k) energy compensation) to a child's leaf materials, or restore the authored opacity — see applyLodFade (lod-fade.ts) for the full mechanics. This wrapper supplies the registry's registerMaterial dep so a clone-on-first-fade material keeps receiving per-frame camera-uniform updates.

      Parameters

      Returns void

    • Coarsest child that is ready AND fresh for version, falling back to the coarsest READY level when none is fresh yet (the ≤1-frame window right after a re-slice) so the group shows stale-but-ready geometry rather than going blank. GROUP-AWARE: each child resolves through childFreshAndCount, the same freshness the sibling display paths (aspiration check, empty guard, blend pairing) use — so a ready GROUP child whose subtree leaves are stamped for an older slice is correctly skipped. The leaf-only coarsestFreshIndex it replaced treated any non-leaf as unconditionally fresh, which displayed the OLD slice from a stale partition/overview branch after a re-slice even while a genuinely fresh level was resident.

      Parameters

      Returns number

    • Should a STALE previously-displayed level be kept on screen for a few more frames instead of dropping to fallbackIdx, the coarsest fresh level?

      Returns the index to hold, or undefined to take the fallback.

      The slice-aware fallback exists so a scrub shows the new slice immediately at low detail. It becomes a defect when the aspiration is only a few frames behind: stepping a 4D timelapse one timepoint made the display drop from the finest level to the coarsest and climb back within ~70 ms, every step — a flash to 1.6% of the geometry while the finest level's data was already cached and decoding. What is on screen is the PREVIOUS slice, which for a timelapse step is the previous frame: the same thing a video player leaves up while the next frame decodes, and far closer to the truth than 108 of 6,900 splats.

      Held only when ALL of:

      • the previous display is still ready and is FINER than the fallback (holding something coarser than the fallback would be a downgrade),
      • the fallback is a SEVERE downgrade — below STALE_HOLD_MIN_RATIO of the held level's committed count — so a fallback that is nearly as good is taken immediately (it is fresh, and freshness wins whenever quality is comparable),
      • the hold has not exhausted its STALE_HOLD_MS budget.

      The budget is deliberately spent from when the hold STARTS and is not refreshed by later version bumps, and once exhausted it latches until the aspiration commits fresh. So a continuous drag degrades to exactly the pre-existing behaviour after STALE_HOLD_MS, rather than freezing on one frame for as long as the user keeps dragging.

      Parameters

      Returns number | undefined

    • Index of the coarsest currently-ready child. Children are stored coarsest→finest, so the first ready index is the coarsest available (resident) level. Used by the off-screen gate to hold a culled group on geometry that is already loaded — never a not-ready lazy level, so it cannot kick a load. Falls back to the current active index when nothing is ready (shouldn't happen: the eager default level is always ready).

      Parameters

      Returns number

    • Kick a lazy child's deferred loader if eligible: not ready, has an ensureLoaded thunk, not already loading, and not inside an un-expired failure cooldown. Centralises the lazy-load gate used by both the desired-target swap path and the active-child self-heal so the failure/cooldown logic lives in exactly one place.

      A freshly-failed child is stamped with the current tick; once FAILED_RETRY_FRAMES elapse the failed flag is cleared and the load retried — recovering a level that failed on reload (after a successful load + byte-eviction), which the old "failed until released" behaviour left permanently stuck (a failed child is never an eviction candidate).

      Parameters

      Returns void

    • Reload a READY-but-STALE lazy fine level for the current view — the sibling of maybeKickLoad for a child whose geometry is committed but reflects an older slice/displayDims version. A fine level leaves the per-slice sweep once loaded (see load-lod-group-node.ts), so the registry — not the sweep — drives its reload, gated on settle by the caller. Re-fires ensureLoaded, which re-runs the expensive loader (overwrites the geometry in place, commits independently, re-stamps loadedViewVersion fresh). Unlike maybeKickLoad it does NOT early-return on isReady — refreshing a ready level is the whole point. The stale level stays hidden behind the coarse fallback meanwhile (the display pass), so this never blanks the screen, and the shared loading/cooldown guards make re-calling it every settled frame safe.

      Parameters

      Returns void

    • Effective-visibility gate — the single place the per-frame paths (maybeKickLoad / maybeKickReload) decide whether a deferred load is worth STARTING at all.

      A layer authored visible=false (or toggled off in the layers panel) hides the LAYER object; the lod_group and its levels underneath keep their own visible flags, so the selector happily kept aspiring to — and lazily loading — fine levels that cannot be drawn. Those loads compete for the shared fetch gate, the worker pool, and VRAM with the layer the user is actually looking at (measured: a hidden 9.75M-point level finished FIRST, roughly doubling scene load time). So: no group visible ⇒ no new loads. The same gate refuses every automatic kick while the owning loader has a latched archive fault; explicit retry remains the only bypass.

      Scope is deliberately narrow — this only stops STARTING work:

      • it never hides or unloads anything already resident (a hidden layer draws nothing anyway, and retention keeps a re-show free);
      • the eager default_level is loaded by loadLodGroupNode, not from here, so a hidden layer still has its cheap coarse level ready to display the instant the panel toggles it on;
      • the walk is ancestor-aware via entry.groupObject (the hidden flag usually sits on an ANCESTOR layer/group, not on the lod_group itself);
      • the gate is re-evaluated every frame, so toggling the layer back on (LayerApplyEngine.applyVisibility → requestRender → AnimationController → evaluatePerFrame) resumes loading on the very next frame with no extra wiring.

      retryLazyChildByNodePath (an explicit user retry of a FAILED level) deliberately bypasses this and calls kickDeferredLoad directly: an explicit request for a retryable child is honoured whatever the layer's visibility or the current archive-fault latch.

      Parameters

      Returns void

    • Shared lazy-load gate for maybeKickLoad (initial load of a not-ready level) and maybeKickReload (refresh of a ready-but-stale level): fire ensureLoaded unless already loading or inside the failure cooldown. A freshly-failed child is stamped with the current tick; once FAILED_RETRY_FRAMES elapse the failed flag clears and the load retries — recovering a level that failed on reload (after a successful load

      • byte-eviction), which the old "failed until released" behaviour left stuck. The automatic caller separately gates loader-level archive faults. The per-child latch keeps concurrent failed branches individually addressable by Retry; an explicit retry clears that selected branch as it starts.

      Parameters

      Returns boolean

    • Bound resident LOD geometry to the GPU-pool byte budget — see enforceResidentByteBudget (lod-eviction.ts) for the full policy. This wrapper supplies the registry's entries, the pool-accounting deps, and the raw per-entry world-box fold, so eviction matches the selector's frustum gate rather than its optional robust metric bounds.

      Parameters

      • camera: Camera
      • frustum: Frustum
      • displayDims: readonly number[]

      Returns void