@displayxr/inline3d 1.19.2 → 1.19.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,29 @@ entry points (`.`, `./three`) are frozen for 1.x, while the **scene subpaths** (
5
5
  `./splat`, `./model`) are a preview tier whose options may change in any release. Entries below say
6
6
  which tier they touch, because that is what tells you whether an upgrade can move your pixels.
7
7
 
8
+ ## 1.19.3 — 2026-09-24
9
+
10
+ Touches the **preview tier** (`./splat`, `./model`). No API change and no pixel change on a core that
11
+ has view rigs.
12
+
13
+ ### Fixed
14
+
15
+ - **A view rig the wall cannot take is no longer dropped silently** (#36).
16
+ - Every scene backend (`./splat` Spark and PlayCanvas, `./model` PlayCanvas) declared its rig
17
+ with `handle?.setViewRig?.(rig)`. On a handle from a core that predates view rigs, that call
18
+ does nothing.
19
+ - A page that vendors an older `inline3d.js` and hands that wall to `./splat` therefore had its
20
+ CAMERA rig thrown away. The wall wove the metric photo scene on the `addScene` display-rig
21
+ shorthand (`virtualDisplayHeight` 0.24): way off in convergence and scale in 3D, correct in 2D.
22
+ That is how the gallery's Spatial View wall regressed; the rig this SDK declared was the same
23
+ from 1.8.0 to 1.19.2.
24
+ - Now it logs one `console.warn` per page ("the view rig was dropped: your inline3d core
25
+ predates view rigs …") carrying the dropped rig.
26
+ - The Spark path used `handle?.setViewRig(…)` without the second `?.`, so it would have THROWN
27
+ there. It now takes the same warning path.
28
+ - Regression tests pin the declared rig for a camera-rig asset with `zoom {1, 2, relax}` and the
29
+ `assemble` reveal, at rest and after the reveal, on the fake engine.
30
+
8
31
  ## 1.19.2 — 2026-09-24
9
32
 
10
33
  Touches the **preview tier** (`./splat`, `engine: 'playcanvas'` only). No API change and no pixel
package/README.md CHANGED
@@ -55,6 +55,8 @@ its own half, with no second canvas ([§setVideo](docs/playcanvas-adapter.md#set
55
55
  optional peer `playcanvas` (`>=2.22.3 <3`) and reads `.sog`, `.ply` and a Streamed-SOG
56
56
  `lod-meta.json`. **Spark stays the default**; a page that never passes `engine` never loads
57
57
  `playcanvas`. What differs, and why: [`docs/playcanvas-adapter.md`](docs/playcanvas-adapter.md).
58
+ The repo's splat sample (`samples/splat/`) renders with PlayCanvas and keeps `?engine=spark` as
59
+ its A/B switch.
58
60
  Bundlers: the engine is reached through a named-import module (`js/inline3d-playcanvas-engine.js`,
59
61
  so the unused ~40% of the engine tree-shakes away); its sort workers are Blob URLs (CSP
60
62
  `worker-src blob:`), and esbuild needs `node:worker_threads` marked external.
@@ -160,7 +162,7 @@ samples/
160
162
  convergence + comfort, the attach pattern, C to A/B a display rig
161
163
  windows/ mixed 3D windows — still photos + a live video + a real-time three.js scene,
162
164
  each woven with one SDK call, all on one session
163
- splat/ a 3D Gaussian splat in a tile, auto-framed, with a 2D price plate over it
165
+ splat/ a 3D Gaussian splat in a tile (PlayCanvas; ?engine=spark), with a 2D plate over it
164
166
  model/ a glTF mesh, a mesh+splat scene, and a Draco-COMPRESSED glTF (PlayCanvas; ?engine=three)
165
167
  composition/ the 14-case 2D/3D overlap matrix — demo AND standing hardware regression
166
168
  surface; red cases ship red (see samples/README.md)
@@ -74,3 +74,15 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
74
74
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
75
75
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
76
76
  THE SOFTWARE.
77
+
78
+ ## Sample assets (repo samples only, not in the npm package)
79
+
80
+ The pages under `samples/` show these assets. None is in the published `@displayxr/inline3d`
81
+ package.
82
+
83
+ | Asset | Used by | Source | Credit and licence |
84
+ |---|---|---|---|
85
+ | Damaged Helmet (`DamagedHelmet.glb`) | `samples/model/` (tiles A and B), loaded at run time, not vendored | [KhronosGroup/glTF-Sample-Assets](https://github.com/KhronosGroup/glTF-Sample-Assets/tree/c6a6bd13ab2b3c685c7903d03561b8a9392f38b8/Models/DamagedHelmet) at commit `c6a6bd13ab2b3c685c7903d03561b8a9392f38b8`, via jsDelivr, unmodified | © 2018 ctxwing (rebuild and conversion to glTF 2.0), [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/legalcode). Earlier version of the model ("Battle Damaged Sci-fi Helmet") © 2016 theblueturtle_, [CC BY-NC 4.0](https://creativecommons.org/licenses/by-nc/4.0/legalcode). As stated in the asset's `README.md` and `LICENSE.md`. |
86
+ | Duck, Draco variant (`samples/model/assets/glTF-Draco/`) | `samples/model/` (tile C) | Khronos glTF-Sample-Assets | © Sony, [CC0 1.0](https://creativecommons.org/publicdomain/zero/1.0/). |
87
+ | Butterfly splat (`samples/splat/assets/butterfly.sog`) | `samples/splat/`, `samples/model/` (tile B) | [Spark](https://sparkjs.dev/) `assets/splats/butterfly.spz`, converted to SOG with PlayCanvas splat-transform (177,132 gaussians, 3 SH bands kept) | Butterfly splat © World Labs (from Spark, sparkjs.dev), used with permission; converted to SOG. |
88
+ | Draco decoder (`vendor/draco/`) | `samples/model/` | three.js `examples/jsm/libs/draco/` | © The Draco Authors, Apache-2.0. |
@@ -38,7 +38,7 @@
38
38
  // └── eye camera, N RenderViews, TONEMAP_NEUTRAL, no sky layer
39
39
 
40
40
  import { PlayCanvasSplatViewer, pageRigTRS, pageViewAxis, pageViewRig } from './inline3d-splat-playcanvas.js';
41
- import { resolveControls, normalizeCameraPose, now, MAX_DT_S } from './inline3d-splat-shared.js';
41
+ import { resolveControls, normalizeCameraPose, now, MAX_DT_S, declareViewRig } from './inline3d-splat-shared.js';
42
42
  import {
43
43
  NEUTRAL_STUDIO,
44
44
  neutralStudioRadiance,
@@ -551,7 +551,7 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
551
551
  if (dm > 0) d = dm / (ax.scale || 1);
552
552
  }
553
553
  out.viewRig = pageViewRig({ verticalFovDeg: viewer.page.fov, convergence: d ?? 2, comfortDepth: ctl.comfortDepth }, out.viewRig || {});
554
- handle?.setViewRig?.(out.viewRig);
554
+ declareViewRig(handle, out.viewRig);
555
555
  };
556
556
  if (typeof opts.onBeforeFrame === 'function') {
557
557
  const cb = opts.onBeforeFrame;
@@ -99,6 +99,7 @@ import {
99
99
  resolveControls,
100
100
  normalizeCameraPose,
101
101
  coverageExponent,
102
+ declareViewRig,
102
103
  } from './inline3d-splat-shared.js';
103
104
 
104
105
  /**
@@ -2348,7 +2349,6 @@ const SET_RIG_ENV_TONE_MAPPING = Object.freeze({ neutral: 'neutral', room: 'none
2348
2349
  export const SET_RIG_TYPES = Object.freeze(['display', 'camera', 'auto']);
2349
2350
  const SET_RIG_FITS = ['contain', 'cover', 'height', 'none'];
2350
2351
  let warnedSetRigKeys = false;
2351
-
2352
2352
  const VIDEO_BUSY = (what) =>
2353
2353
  `@displayxr/inline3d/splat: ${what}() while a video is on — call handle.setVideo(null) first ` +
2354
2354
  '(the video holds the display rig and hides the splat until it exits).';
@@ -2832,7 +2832,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
2832
2832
  },
2833
2833
  out.viewRig || {},
2834
2834
  );
2835
- handle?.setViewRig?.(out.viewRig);
2835
+ declareViewRig(handle, out.viewRig);
2836
2836
  if (!out.rig) return;
2837
2837
  out.rig.convergence = d;
2838
2838
  if (Math.abs(d - pageFocus.fired) > 1e-3 || source !== pageFocus.source) {
@@ -2905,7 +2905,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
2905
2905
  out: camRigObj,
2906
2906
  },
2907
2907
  );
2908
- handle?.setViewRig?.(out.viewRig);
2908
+ declareViewRig(handle, out.viewRig);
2909
2909
  }
2910
2910
  viewer.onFocusChange = (f) => {
2911
2911
  pushViewRig(false);
@@ -3195,7 +3195,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
3195
3195
  parallaxFactor: f.parallaxFactor ?? 1,
3196
3196
  perspectiveFactor: f.perspectiveFactor ?? 1,
3197
3197
  });
3198
- handle?.setViewRig?.(out.viewRig);
3198
+ declareViewRig(handle, out.viewRig);
3199
3199
  }
3200
3200
 
3201
3201
  /**
@@ -3601,7 +3601,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
3601
3601
  } else {
3602
3602
  declaredDisplay = s.declaredDisplay;
3603
3603
  out.viewRig = s.viewRig;
3604
- handle?.setViewRig?.(out.viewRig);
3604
+ declareViewRig(handle, out.viewRig);
3605
3605
  }
3606
3606
  } else out.viewRig = s.viewRig;
3607
3607
  lastConvergence = s.lastConvergence;
@@ -514,3 +514,35 @@ export function coverageExponent(c, L = FADE_OPTICAL_DEPTH) {
514
514
  if (c >= 1) return 1;
515
515
  return -Math.log(1 - c * (1 - Math.exp(-L))) / L;
516
516
  }
517
+
518
+ // ── view-rig declaration (all three backends) ────────────────────────────────────────────────
519
+
520
+ let warnedNoViewRig = false;
521
+
522
+ /**
523
+ * Declare a view rig on the scene handle. A handle without `setViewRig` is a core that predates
524
+ * view rigs (inline3d.js before the view-rig commit — e.g. a page that vendors an older core and
525
+ * hands its wall to this adapter): the rig cannot reach the browser, and the wall weaves on the
526
+ * addScene display-rig shorthand instead. For a CAMERA rig that is a metric photo scene on a
527
+ * 0.24 m display rig — convergence and scale way off while 2D looks right — so it is said out
528
+ * loud, once per page, rather than dropped the way `handle?.setViewRig?.()` used to drop it.
529
+ * Returns whether the handle took the rig.
530
+ */
531
+ export function declareViewRig(handle, rig) {
532
+ if (!handle) return false; // no wall (mono): nothing to declare to
533
+ if (typeof handle.setViewRig === 'function') {
534
+ handle.setViewRig(rig);
535
+ return true;
536
+ }
537
+ if (rig?.type === 'camera' && !warnedNoViewRig) {
538
+ warnedNoViewRig = true;
539
+ console.warn(
540
+ '[inline3d/splat] the view rig was dropped: your inline3d core predates view rigs (its ' +
541
+ 'addScene handle has no setViewRig), so the wall will use the addScene display-rig ' +
542
+ 'shorthand, not this camera rig — the scene will be mis-converged and mis-scaled in 3D. ' +
543
+ 'Update the core (js/inline3d.js) that created the wall to the same SDK version as ./splat.',
544
+ rig,
545
+ );
546
+ }
547
+ return false;
548
+ }
@@ -37,6 +37,7 @@ import {
37
37
  STREAMED_NEEDS_PLAYCANVAS,
38
38
  resolveControls,
39
39
  normalizeCameraPose,
40
+ declareViewRig,
40
41
  } from './inline3d-splat-shared.js';
41
42
  import {
42
43
  resolveRig,
@@ -486,7 +487,7 @@ export function addSplat(wall, canvas, src, opts = {}) {
486
487
  parallaxFactor: out.rig.parallaxFactor,
487
488
  out: out.viewRig || {},
488
489
  });
489
- handle?.setViewRig(out.viewRig);
490
+ declareViewRig(handle, out.viewRig);
490
491
  }
491
492
  viewer.onFocusChange = () => {
492
493
  pushViewRig(false);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displayxr/inline3d",
3
- "version": "1.19.2",
3
+ "version": "1.19.3",
4
4
  "description": "Turn any HTML <canvas> into a glasses-free-3D window on a DisplayXR display, inside an ordinary web page. Dependency-free; progressive enhancement (falls back to plain 2D everywhere else).",
5
5
  "type": "module",
6
6
  "types": "./index.d.ts",