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

    Manager for SceneLoader instances. Provides centralized access to loader instances without global variables.

    Index
    instance: SceneLoaderManager | null = null
    loaders: Map<string, SceneLoader> = ...
    defaultLoaderId: string | null = null
    profiler: UpdateProfiler

    Update profiler for timing scene updates Singleton owned by the manager, shared with all loaders

    monitorFactory: SceneLoaderMonitorFactory | null = null

    Optional monitor factory injected by core/app.ts so each SceneLoader we create can resolve a UI monitor without the data/ layer importing ui/. Null means "no UI monitor wired up" (tests / embedders) and SceneLoader treats every monitor call as a no-op.

    lodGroupRegistryFactory: SceneLoaderLODGroupRegistryFactory | null = null

    Optional LOD-group registry factory. Same dependency-inversion pattern as monitorFactory — the app pipeline owns the live SceneManager (camera, viewport) and closes over it to build a registry per loader without the data/ layer importing scene/.

    requestRender: (() => void) | null = null

    Optional render-loop wake-up forwarded to every created loader (→ SceneLoader.setRequestRender). Same dependency-inversion pattern as monitorFactory: the app pipeline wires it to AnimationController.startAnimation so late geometry commits (refinement, retries, lazy LOD loads) repaint after the rAF loop has idle-paused. Null (tests / embedders) means commits never wake a loop — SceneLoader treats it as a no-op.

    autoRetryableFailureCallback: (() => void) | null = null
    refinementDensity:
        | { provider: ProjectedDensityProvider
        | null; caps: DensityGateCaps }
        | null = null

    Projected-density provider + caps for the refinement rung gate, forwarded to every created loader (→ SceneLoader.setRefinementDensityProvider). Null provider = bytes-only admission.

    decodeKTX2: KTX2TextureDecoder | null = null
    • Provide the render-loop wake-up callback. Called once at app boot from the init pipeline; forwarded to each subsequently created SceneLoader (see the requestRender field).

      Parameters

      • callback: (() => void) | null

      Returns void

    • Forward transient-failure notifications to every current and future loader.

      Parameters

      • callback: (() => void) | null

      Returns void

    • Create a new SceneLoader instance

      Parameters

      • id: string = 'default'

        Unique identifier for this loader

      • Optionalconfig: LoaderConfig

        Optional loader configuration

      • setAsDefault: boolean = true

        Whether to set this as the default loader

      Returns SceneLoader

      The created SceneLoader instance

    • Create a new SceneLoader, awaiting disposal of any existing loader with the same ID first.

      Unlike createLoader (which fires the previous loader's async dispose without awaiting), this awaits destroyLoaderAsync so caching-store teardown and OPFS metadata flush fully drain before the replacement loader is constructed. Dataset switches must use this path so the new loader never races the old one's late teardown for cache ownership / OPFS metadata.

      Parameters

      • id: string = 'default'

        Unique identifier for this loader

      • Optionalconfig: LoaderConfig

        Optional loader configuration

      • setAsDefault: boolean = true

        Whether to set this as the default loader

      Returns Promise<SceneLoader>

      The created SceneLoader instance

    • Whether ANY registered loader has a LOAD PASS outstanding — an updateView sweep (fetch/decode/upload) up to its geometry commit, or a sweep that is queued and has not started yet. See SceneLoader.isLoadPassInProgress for the exact scope, in particular why the progressive-LOD refinement drain is excluded and why the queued slot counts.

      Consumed by the debug snapshot (__luxarDebug.getState().isLoading, built in core/app/debug/debug-interface.ts), which the E2E "wait for data" helpers poll to decide when a load has settled.

      The answer is "any loader", not "the default loader", because this manager's contract admits several: createLoader/createLoaderAsync take an id, getAllLoaders() returns a map, and the default is merely one elected entry. So the aggregate answers for all of them rather than trusting the default slot to be the only busy one. (Production registers exactly one, under 'default' — a dataset switch disposes the outgoing loader before constructing its replacement, so the two never overlap.)

      Returns boolean

      True if at least one loader is mid-load-pass; false when idle or when no loader is registered.

    • The progressive-refinement BYTE-ceiling stop across every registered loader, or undefined when none of them ever declined a rung.

      MERGE RULE. The FIRST loader (in registration order) that reports a stop supplies reason, residentBytes, budgetBytes and firstPath — those four describe one verdict at one instant and averaging or concatenating them across loaders would describe no verdict at all. declinedPathCount sums, because "how much of the scene stopped short" is genuinely additive, and declinedPaths concatenates in the same order and is re-truncated to RESIDENCY_DECLINED_PATH_SAMPLE so the merged sample cannot grow with the loader count. So on a multi-loader page the reported verdict is the first-registered loader's, while the count covers all of them.

      Aggregated for the same reason as isAnyLoadPassInProgress: the manager's contract admits several loaders even though production registers exactly one.

      Consumed by the debug snapshot (__luxarDebug.getState().refinementResidency), which core/app/debug/capture-readiness.ts refuses a capture on.

      Returns RefinementResidencyStop | undefined

    • The DEFAULT loader's GPU buffer-pool statistics, or undefined when no loader is registered or pooling is disabled.

      Deliberately NOT an aggregate, unlike refinementResidencyStop: PoolStats carries a per-type breakdown and a largestPooledBytes figure that do not sum, and inventing a half-merged shape for a case production never reaches (one loader, under 'default') would be a worse answer than a precisely-scoped one. A second registered loader's pool is therefore NOT represented here.

      Returns PoolStats | undefined

    • Destroy a specific loader (best-effort, non-awaiting).

      Synchronously removes the loader from the manager (so subsequent getLoaderCount() / hasLoader() calls reflect the change) and fires the async dispose without awaiting. Suits the beforeunload path and other call sites that cannot meaningfully await teardown. Callers that need deterministic teardown (e.g. dataset switches) must use destroyLoaderAsync.

      Parameters

      • id: string

        The loader ID to destroy

      Returns void

    • Destroy a specific loader and await its disposal.

      Awaits SceneLoader.dispose so caching-store teardown, prefetcher teardown, and OPFS metadata flush all complete before this method resolves. Use during dataset switches when the next loader's init must see fully-drained state.

      Parameters

      • id: string

      Returns Promise<void>

    • Destroy all loaders concurrently and await every disposal.

      Promise.alls every loader's dispose() so the caller can wait for every prefetcher, caching store, and L0 cache to drain before proceeding. Always clears the loaders map and default-loader id, even if individual disposals reject.

      Returns Promise<void>

    • Synchronously remove a loader from the manager and update the default-loader id. Returns the loader instance for the caller to dispose (sync or async). Centralizes the bookkeeping so the fire-and-forget and awaitable variants stay in sync.

      Default-election contract: when the detached loader was the current default, the next default is the FIRST remaining loader by insertion order — i.e. the oldest loader still registered. This ordering is a stable property of Map and therefore deterministic across reloads for any given sequence of registrations. Callers that need a specific default should call setDefault() explicitly rather than relying on the implicit election.

      Parameters

      • id: string

      Returns SceneLoader | null

    • Check if a loader exists

      Parameters

      • id: string

        The loader ID to check

      Returns boolean

      True if the loader exists

    • Dispose the current instance and clear the singleton slot.

      Used at app shutdown and between tests. The next getInstance() call lazily constructs a fresh manager.

      Returns void