diff --git a/examples/src/examples/gaussian-splatting/lod-streaming.example.mjs b/examples/src/examples/gaussian-splatting/lod-streaming.example.mjs index 83c9ad81333..6d1203872e1 100644 --- a/examples/src/examples/gaussian-splatting/lod-streaming.example.mjs +++ b/examples/src/examples/gaussian-splatting/lod-streaming.example.mjs @@ -173,6 +173,25 @@ assetListLoader.load(() => { }; applyPreset(); + + // Start with lowest LOD only for fast initial load + const lodLevels = gs.resource?.octree?.lodLevels; + if (lodLevels) { + const worstLod = lodLevels - 1; + app.scene.gsplat.lodRangeMin = worstLod; + app.scene.gsplat.lodRangeMax = worstLod; + } + + // When lowest LOD is fully loaded, switch to the normal quality range + const gsplatSystem = /** @type {any} */ (app.systems.gsplat); + const onFrameReady = (/** @type {any} */ camera, /** @type {any} */ layer, /** @type {boolean} */ ready, /** @type {number} */ loadingCount) => { + if (ready && loadingCount === 0) { + gsplatSystem.off('frame:ready', onFrameReady); + applyPreset(); + } + }; + gsplatSystem.on('frame:ready', onFrameReady); + data.on('lodPreset:set', applyPreset); const applySplatBudget = () => { diff --git a/src/scene/gsplat-unified/gsplat-manager.js b/src/scene/gsplat-unified/gsplat-manager.js index ad1ef4a92c6..938a15bc306 100644 --- a/src/scene/gsplat-unified/gsplat-manager.js +++ b/src/scene/gsplat-unified/gsplat-manager.js @@ -1530,6 +1530,13 @@ class GSplatManager { // fire frame:ready event this.fireFrameReadyEvent(); + // If event listeners dirtied params (e.g. changed LOD range), ensure LOD is re-evaluated + if (this.scene.gsplat.dirty) { + for (const [, inst] of this.octreeInstances) { + inst.needsLodUpdate = true; + } + } + // return the number of active splats for stats return sortedState ? sortedState.totalActiveSplats : 0; }