From 2d7a54d4c39340435fc6ca145ff1466a33e7e6f5 Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Thu, 25 Jun 2026 22:47:49 +0100 Subject: [PATCH 1/4] Expose lod-range-min and lod-range-max attributes on pc-gsplat Add lodRangeMin/lodRangeMax to the pc-gsplat element, completing the LOD control surface alongside the existing lod-base-distance/lod-multiplier. These clamp the LOD index selected by distance (min = finest allowed, max = coarsest allowed); raising the min avoids downloading the largest, highest-quality LOD files. The engine's GSplatComponentSystem.initializeComponentData only applies a fixed allow-list of properties and ignores lodRangeMin/lodRangeMax, so the values are applied through the initComponent() hook after the component exists rather than via getInitialComponentData(). Co-Authored-By: Claude Opus 4.8 --- src/components/gsplat-component.ts | 66 +++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/src/components/gsplat-component.ts b/src/components/gsplat-component.ts index 20f1da9..df43315 100644 --- a/src/components/gsplat-component.ts +++ b/src/components/gsplat-component.ts @@ -20,6 +20,10 @@ class GSplatComponentElement extends ComponentElement { private _lodMultiplier = 3; + private _lodRangeMin = 0; + + private _lodRangeMax = 99; + /** @ignore */ constructor() { super('gsplat'); @@ -34,6 +38,15 @@ class GSplatComponentElement extends ComponentElement { }; } + initComponent() { + // The engine's gsplat component system does not include lodRangeMin/lodRangeMax in the set + // of properties it applies from initialization data, so they are ignored when passed via + // getInitialComponentData(). Apply them here, once the component exists, through the setters + // so the values reach the underlying GSplatComponent. + this.lodRangeMin = this._lodRangeMin; + this.lodRangeMax = this._lodRangeMax; + } + /** * Gets the underlying PlayCanvas gsplat component. * @returns The gsplat component. @@ -126,13 +139,58 @@ class GSplatComponentElement extends ComponentElement { return this._lodMultiplier; } + /** + * Sets the minimum allowed LOD index (inclusive). The LOD selected by distance is clamped so it + * never goes finer (lower index) than this value. Raising it avoids downloading the highest + * quality (largest) LOD files. Defaults to 0. Only affects assets that contain LOD levels (e.g. + * `.lod-meta.json`). + * @param value - The minimum LOD index. + */ + set lodRangeMin(value: number) { + this._lodRangeMin = value; + if (this.component) { + this.component.lodRangeMin = value; + } + } + + /** + * Gets the minimum allowed LOD index. + * @returns The minimum LOD index. + */ + get lodRangeMin() { + return this._lodRangeMin; + } + + /** + * Sets the maximum allowed LOD index (inclusive). The LOD selected by distance is clamped so it + * never goes coarser (higher index) than this value. The default of 99 effectively means "no + * cap". Defaults to 99. Only affects assets that contain LOD levels (e.g. `.lod-meta.json`). + * @param value - The maximum LOD index. + */ + set lodRangeMax(value: number) { + this._lodRangeMax = value; + if (this.component) { + this.component.lodRangeMax = value; + } + } + + /** + * Gets the maximum allowed LOD index. + * @returns The maximum LOD index. + */ + get lodRangeMax() { + return this._lodRangeMax; + } + static get observedAttributes() { return [ ...super.observedAttributes, 'asset', 'cast-shadows', 'lod-base-distance', - 'lod-multiplier' + 'lod-multiplier', + 'lod-range-min', + 'lod-range-max' ]; } @@ -152,6 +210,12 @@ class GSplatComponentElement extends ComponentElement { case 'lod-multiplier': this.lodMultiplier = Number(newValue); break; + case 'lod-range-min': + this.lodRangeMin = Number(newValue); + break; + case 'lod-range-max': + this.lodRangeMax = Number(newValue); + break; } } } From 9d4d0dffc06b30ee44b69eb5450405eb4e9d1714 Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Fri, 26 Jun 2026 09:48:53 +0100 Subject: [PATCH 2/4] Reference upstream engine fix in initComponent workaround comment playcanvas/engine#8968 (merged) adds lodRangeMin/lodRangeMax to the gsplat component system's _properties list, fixing the root cause at the engine level. Note this in the comment so the initComponent() override can be removed once web-components' minimum supported engine version includes the fix. No behavior change. Co-Authored-By: Claude Opus 4.8 --- src/components/gsplat-component.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/gsplat-component.ts b/src/components/gsplat-component.ts index df43315..65fb0b1 100644 --- a/src/components/gsplat-component.ts +++ b/src/components/gsplat-component.ts @@ -39,10 +39,13 @@ class GSplatComponentElement extends ComponentElement { } initComponent() { - // The engine's gsplat component system does not include lodRangeMin/lodRangeMax in the set - // of properties it applies from initialization data, so they are ignored when passed via - // getInitialComponentData(). Apply them here, once the component exists, through the setters - // so the values reach the underlying GSplatComponent. + // The gsplat component system in engine 2.20.0 and earlier does not include + // lodRangeMin/lodRangeMax in the `_properties` list it applies from initialization data, so + // they are ignored when passed via getInitialComponentData(). Apply them here, once the + // component exists, through the setters so the values reach the underlying GSplatComponent. + // The engine root cause is fixed by playcanvas/engine#8968; once the minimum supported + // engine version includes that fix, these can move into getInitialComponentData() and this + // override can be removed. this.lodRangeMin = this._lodRangeMin; this.lodRangeMax = this._lodRangeMax; } From dd22586403a72ca721fe7ceab3b2e07819b58e79 Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Mon, 29 Jun 2026 13:59:11 +0100 Subject: [PATCH 3/4] Correct initComponent comment: only engine 2.20.0 is affected lodRangeMin/lodRangeMax were first added to GSplatComponent in engine 2.20.0, so versions earlier than 2.20.0 do not have them at all. The gsplat system _properties allow-list omitted them only in 2.20.0; the fix (playcanvas/engine#8968) shipped in 2.20.1. Reword the comment so it no longer implies "2.20.0 and earlier" are affected. Co-Authored-By: Claude Opus 4.8 --- src/components/gsplat-component.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/gsplat-component.ts b/src/components/gsplat-component.ts index 65fb0b1..72468cc 100644 --- a/src/components/gsplat-component.ts +++ b/src/components/gsplat-component.ts @@ -39,13 +39,13 @@ class GSplatComponentElement extends ComponentElement { } initComponent() { - // The gsplat component system in engine 2.20.0 and earlier does not include - // lodRangeMin/lodRangeMax in the `_properties` list it applies from initialization data, so - // they are ignored when passed via getInitialComponentData(). Apply them here, once the - // component exists, through the setters so the values reach the underlying GSplatComponent. - // The engine root cause is fixed by playcanvas/engine#8968; once the minimum supported - // engine version includes that fix, these can move into getInitialComponentData() and this - // override can be removed. + // Engine 2.20.0 first added lodRangeMin/lodRangeMax to GSplatComponent, but its gsplat + // component system omits them from the `_properties` list it applies from initialization + // data, so they are ignored when passed via getInitialComponentData(). Apply them here, + // once the component exists, through the setters so the values reach the underlying + // GSplatComponent. The engine root cause is fixed by playcanvas/engine#8968 (shipped in + // 2.20.1); once the minimum supported engine version includes that fix, these can move into + // getInitialComponentData() and this override can be removed. this.lodRangeMin = this._lodRangeMin; this.lodRangeMax = this._lodRangeMax; } From 78e6440c1a539ccaec3232bb96cd48fa8c9013fa Mon Sep 17 00:00:00 2001 From: Will Eastcott Date: Mon, 29 Jun 2026 14:04:15 +0100 Subject: [PATCH 4/4] Drop initComponent workaround for lodRangeMin/lodRangeMax Engine 2.20.1 (playcanvas/engine#8968) adds lodRangeMin/lodRangeMax to the gsplat component system's _properties allow-list, so they now apply correctly via getInitialComponentData() like the other attributes. Move them there and remove the initComponent() override. Bump the playcanvas peer dependency to ^2.20.1 so consumers can't land on 2.20.0, where these props exist on the component but are dropped from initialization data. Co-Authored-By: Claude Opus 4.8 --- package.json | 2 +- src/components/gsplat-component.ts | 16 +++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index ac20b51..9c71e76 100644 --- a/package.json +++ b/package.json @@ -45,7 +45,7 @@ "watch": "rollup -c -w" }, "peerDependencies": { - "playcanvas": "^2.19.0" + "playcanvas": "^2.20.1" }, "devDependencies": { "@mediapipe/tasks-vision": "0.10.35", diff --git a/src/components/gsplat-component.ts b/src/components/gsplat-component.ts index 72468cc..5830c28 100644 --- a/src/components/gsplat-component.ts +++ b/src/components/gsplat-component.ts @@ -34,22 +34,12 @@ class GSplatComponentElement extends ComponentElement { asset: AssetElement.get(this._asset), castShadows: this._castShadows, lodBaseDistance: this._lodBaseDistance, - lodMultiplier: this._lodMultiplier + lodMultiplier: this._lodMultiplier, + lodRangeMin: this._lodRangeMin, + lodRangeMax: this._lodRangeMax }; } - initComponent() { - // Engine 2.20.0 first added lodRangeMin/lodRangeMax to GSplatComponent, but its gsplat - // component system omits them from the `_properties` list it applies from initialization - // data, so they are ignored when passed via getInitialComponentData(). Apply them here, - // once the component exists, through the setters so the values reach the underlying - // GSplatComponent. The engine root cause is fixed by playcanvas/engine#8968 (shipped in - // 2.20.1); once the minimum supported engine version includes that fix, these can move into - // getInitialComponentData() and this override can be removed. - this.lodRangeMin = this._lodRangeMin; - this.lodRangeMax = this._lodRangeMax; - } - /** * Gets the underlying PlayCanvas gsplat component. * @returns The gsplat component.