Initial-load path for a single lod_group scene-graph node.
Mirrors the generic-group branch in load-scene-nodes.ts — creates
a THREE.Group, applies the transform, recurses into children —
with two additions:
Each child carries a coverage_fraction attribute (a per-child
threshold whose UNITS the group's selector attr names: literal
screen-area fractions under 'screen-area' — derived whole-object
ladders span [0, 1/2], a partition tile anchors at 1.0 — or the legacy
diagonal metric under 'coverage', up to 4.0 ==
SCREEN_FILL_DIAGONAL_RATIO / FILL_FACTOR)
plus its own position_bounds (the raw nD AABB) and optional
lod_bounds (a robust selector-only AABB). All are read from the
child's zarr attrs.
Legacy (pre-v3.2) datasets that still carry min_pixel_size /
selector 'pixel_size' are auto-adapted with a warning
(see resolveCoverageFractions).
Once children are loaded, an entry is registered with the
:class:LODGroupRegistry so the per-frame selector can pick
which child renders.
The lod_group walks the scene-graph child order (which the
Python writer guarantees is coarsest→finest); the registry stores
children in that same order for consistent threshold comparisons.
Lazy loading: only the default level's geometry is loaded eagerly.
Every other gsplats, points, lines or mesh level is cheap-attached
(placeholder + loader, no array fetch) with an ensureLoaded thunk; the
registry fires the thunk on demand the first time the per-frame selector wants
to show that level. This is what keeps a scene of many lod_groups from loading
every level of every group up front — distant groups stay coarse and
their fine levels are never fetched. Deferring the points/lines/mesh level
matters for their substitutive ladders, whose finest child is the full
cloud / line set / full-resolution surface (eager-loading it would defeat
progressive loading). The selector math needs
only the per-child coverage_fraction / position_bounds / optional
lod_bounds attrs (read here), not loaded geometry, so deferral is fully
correct.
Sibling of data/scene-loader/nodes/load-scene-nodes.ts (dispatch),
data/scene-loader/nodes/load-gsplats-node.ts +
data/scene-loader/nodes/load-points-node.ts (cheap/expensive splits),
and scene/lod-group-registry.ts (per-frame eval + lazy trigger).
Initial-load path for a single
lod_groupscene-graph node.Mirrors the generic-group branch in
load-scene-nodes.ts— creates aTHREE.Group, applies the transform, recurses into children — with two additions:coverage_fractionattribute (a per-child threshold whose UNITS the group'sselectorattr names: literal screen-area fractions under'screen-area'— derived whole-object ladders span [0, 1/2], a partition tile anchors at 1.0 — or the legacy diagonal metric under'coverage', up to 4.0 ==SCREEN_FILL_DIAGONAL_RATIO / FILL_FACTOR) plus its ownposition_bounds(the raw nD AABB) and optionallod_bounds(a robust selector-only AABB). All are read from the child's zarr attrs. Legacy (pre-v3.2) datasets that still carrymin_pixel_size/ selector'pixel_size'are auto-adapted with a warning (seeresolveCoverageFractions).LODGroupRegistryso the per-frame selector can pick which child renders.The lod_group walks the scene-graph child order (which the Python writer guarantees is coarsest→finest); the registry stores children in that same order for consistent threshold comparisons.
Lazy loading: only the default level's geometry is loaded eagerly. Every other gsplats, points, lines or mesh level is cheap-attached (placeholder + loader, no array fetch) with an
ensureLoadedthunk; the registry fires the thunk on demand the first time the per-frame selector wants to show that level. This is what keeps a scene of many lod_groups from loading every level of every group up front — distant groups stay coarse and their fine levels are never fetched. Deferring the points/lines/mesh level matters for their substitutive ladders, whose finest child is the full cloud / line set / full-resolution surface (eager-loading it would defeat progressive loading). The selector math needs only the per-childcoverage_fraction/position_bounds/ optionallod_boundsattrs (read here), not loaded geometry, so deferral is fully correct.Sibling of
data/scene-loader/nodes/load-scene-nodes.ts(dispatch),data/scene-loader/nodes/load-gsplats-node.ts+data/scene-loader/nodes/load-points-node.ts(cheap/expensive splits), andscene/lod-group-registry.ts(per-frame eval + lazy trigger).