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

    Module scene/lod-group-registry

    Per-frame LOD-group selector.

    Tracks every lod_group scene-graph node currently loaded. For each one, every frame:

    1. Fold each child's raw nD positionBounds into a cached per-entry local-space :type:BoundingBox, mapping nD axes onto X/Y/Z through the current displayDims; when any child publishes robust lodBounds, fold a second (metric) box the same way, falling back per child to positionBounds (outliers stay visible and resident).
    2. Lift the complete-geometry box to world space through the group's matrixWorld (:func:transformBoundingBox). Off-screen gate: a world box entirely outside the camera frustum holds the group at its coarsest READY level. Eviction uses the same box.
    3. Otherwise measure the metric box ON SCREEN: its LOCAL corners go through projView × matrixWorld (so a rotated group is sized by its own box, not by its inflated world AABB), in the units the entry's selector names:
      • 'screen-area' (what every derived ladder stamps): the viewport fraction covered by the box's INSCRIBED ellipsoid, projected exactly and sized at its near depth (projectBoxAreaFraction). Viewport-clipped, tops out at 1.0;
      • 'coverage' (legacy): the pixel diagonal of the projected corners' AABB ÷ FILL_FACTOR × fittedAxisPx (min(viewport.width, viewport.height), the extent calculateCameraDistance fits). Both saturate to +Infinity (finest) with the camera INSIDE the box. The session lodBias then scales the metric in area units.
    4. For a screen-area ladder whose every level carries a median_footprint stamp, the projected-FOOTPRINT pick decides instead: the coarsest level whose median splat stays under config.lod.maxMedianFootprintPx — except while the metric is saturated, where the finest level stands.
    5. Otherwise pick the finest child whose coverage_fraction threshold the metric satisfies, with 10% asymmetric hysteresis on the downgrade direction (and, during playback, capped at the finest level whose measured load time fits the period).
    6. If the desired child differs from the current active one, swap visibility — a dissolve for blendable groups — gated by the never-downgrade display gate: a fresh aspiration whose additive ladder is still streaming is not shown while the previously-displayed level looks strictly better (shouldHoldPreviousDisplay in lod-display-gate.ts).

    The atomic-swap invariant on initial load is realized by loadLodGroupNode (sequential awaits + visible=false after attach + a single register() call at the end) — no per-child ready gate is needed in the registry.

    Partition frustum gating (registerPartition): a kind=partition group's parts are frustum- and slice-culled every frame, and a part that re-enters the frustum is resynced — see partition-gate.ts, which the registry drives before and after its LOD pass.

    Collaborators. The registry is the orchestrator; the stateful pieces live in their own modules, each stating until when it needs the loop to keep ticking (tick-demand.ts), which evaluatePerFrame folds into one requestTick:

    • lod-dissolve.ts — the time-driven level dissolve (state machine);
    • playback-aspiration.ts — reload timing and the playback level cap;
    • partition-gate.ts — partition culling, resync and lazy activation;
    • capture-quiescence.ts — the offline-capture "frame is final" test;
    • lod-selector-math.ts / lod-display-gate.ts / lod-fade.ts / lod-eviction.ts — the pure metric, display, opacity and eviction policies.

    The bbox infrastructure is shared with the scene-bounds cache and camera framing — projectBoundsToDisplayDims / transformBoundingBox live in scene-manager/clipping/bounds-math.ts and are reused here.

    Wiring: the SceneLoader instantiates one registry per scene; the pipeline hooks evaluatePerFrame into AnimationController alongside the dynamic-clipping callback. Manual override (setSelectorMode(path, { lockLevel: i })) bypasses the auto selector — driven by the layers-panel dropdown.

    LODGroupRegistry
    PreloadVisit
    LODGroupChild
    LODGroupEntry
    EntryFrame
    LODFrameChanges
    LODGroupRegistryDeps
    FILL_FACTOR
    SCREEN_FILL_DIAGONAL_RATIO
    LOD_FRAME_UNCHANGED
    LazyPartState → LazyPartState
    PartitionChildCache → PartitionChildCache
    PartitionGroupChild → PartitionGroupChild
    PartitionGroupEntry → PartitionGroupEntry
    PartitionPartRef → PartitionPartRef
    pickChildWithHysteresis → pickChildWithHysteresis
    projectBoxAreaFraction → projectBoxAreaFraction
    projectBoxDiagonalPx → projectBoxDiagonalPx