@displayxr/inline3d 1.19.3 → 1.21.0

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@displayxr/inline3d",
3
- "version": "1.19.3",
3
+ "version": "1.21.0",
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",
@@ -52,6 +52,7 @@
52
52
  "js/inline3d-splat-shared.js",
53
53
  "js/inline3d-splat-effects.js",
54
54
  "js/inline3d-splat-live.js",
55
+ "js/inline3d-splat-diag.js",
55
56
  "js/inline3d-splat-video.js",
56
57
  "js/inline3d-model.js",
57
58
  "js/inline3d-model-entry.js",
package/splat.d.ts CHANGED
@@ -219,6 +219,23 @@ export interface SplatOptions {
219
219
  * URL with `engine: 'spark'`.
220
220
  */
221
221
  engine?: 'spark' | 'playcanvas';
222
+ /**
223
+ * `engine: 'playcanvas'` only: TRANSITION DIAGNOSTICS (#36). Off by default; `true` (or the page
224
+ * URL's `?dxrdiag=1`) records every woven frame — its interval, whether its eye poses are
225
+ * bit-identical to the previous frame's (a tracking HOLD), whether the transition overlay shows
226
+ * the frozen capture — plus every `setViewRig` push with its values, long tasks, and the
227
+ * setSource phases (`prepare`, `swap`, `window`, `settle`). It shows a small overlay (excluded
228
+ * from the weave), logs `[dxr-diag]` console lines (one summary with a verdict per transition),
229
+ * and exposes `window.__dxrDiag` (`copy(__dxrDiag.dump())` for the JSON).
230
+ *
231
+ * A string / array adds A/B kill switches (also accepted as `?dxrdiag=norig,frozen`): `'norig'`
232
+ * keeps the rig declared before the first setSource (drops every re-declaration), `'frozen'`
233
+ * forces `outgoing: 'frozen'`, `'nowarm'` skips the transition shader pre-warm, `'cold'` skips
234
+ * the live outgoing pre-sort, `'nooverlay'` records without the overlay. The option wins over the
235
+ * URL; `false` turns it off even with `?dxrdiag` present. See docs/playcanvas-adapter.md
236
+ * § Diagnosing transition stalls.
237
+ */
238
+ diag?: boolean | string | ReadonlyArray<'norig' | 'frozen' | 'nowarm' | 'cold' | 'nooverlay' | '1'>;
222
239
  /**
223
240
  * `engine: 'playcanvas'` only: the WebGL context's `preserveDrawingBuffer` (default false) —
224
241
  * the knob for the weave's zero-copy read race on large canvases.
@@ -606,8 +623,17 @@ export interface SplatSourceOptions {
606
623
  * (nothing moves: twinkling points out, twinkling points in) and `'dust'` (drifting dust out,
607
624
  * gathering dust in). 2.6–2.8 s, linear shared clock (each particle eases itself). The old
608
625
  * photo is live, in 2D too; a hidden tab gets the crossfade.
609
- */
610
- transition?: 'cut' | 'crossfade' | 'flip' | 'wavefront' | SplatParticleTransitionName;
626
+ *
627
+ * The SEQUENCE transitions (docs/splat-effects.md §Sequence transitions): ONE photo at a time.
628
+ * `'reassemble'` disperses the current photo (`assemble` backwards), releases it, loads the next
629
+ * one and assembles it (3000 ms: 45 % out, a 10 % empty beat, 45 % in). The general form
630
+ * `{ type: 'sequence', out, in }` runs any two of `assemble`, `dissolve-in`, `converge`,
631
+ * `shimmer`, `sweep`, `fade`. No second camera, overlay or capture: the eye camera renders every
632
+ * frame.
633
+ */
634
+ transition?: 'cut' | 'crossfade' | 'flip' | 'wavefront' | SplatParticleTransitionName | 'reassemble' | SplatSequenceTransition;
635
+ /** Sequence transitions: the empty beat between out and in, a fraction of `durationMs`, 0..0.9 (default 0.1). */
636
+ beat?: number;
611
637
  durationMs?: number;
612
638
  easing?: SplatEasing;
613
639
  /** `cut`/`crossfade` only: reveal the INCOMING asset (entity scope) while the old one fades. */
@@ -639,9 +665,9 @@ export interface SplatSourceOptions {
639
665
  origin?: SplatEffectOrigin;
640
666
  /** Particle transitions: how much of the clock the two photos' spans share, 0..1 (0 = one after the other). */
641
667
  overlap?: number;
642
- /** Particle transitions: option overrides for the OUTGOING photo's effect. */
668
+ /** Particle and sequence transitions: option overrides for the OUTGOING photo's effect. */
643
669
  outgoingFx?: SplatParticleOptions & Record<string, unknown>;
644
- /** Particle transitions: option overrides for the INCOMING photo's effect. */
670
+ /** Particle and sequence transitions: option overrides for the INCOMING photo's effect. */
645
671
  incomingFx?: SplatParticleOptions & Record<string, unknown>;
646
672
  }
647
673
 
@@ -651,18 +677,45 @@ export interface SplatSourceOptions {
651
677
  * first transition of each kind in a page blocks its first frame on the link (tens of ms, hundreds
652
678
  * the first time a machine sees the variant). Validated like setSource's.
653
679
  */
654
- export type SplatPrepareOptions = SplatSourceOptions;
680
+ export type SplatPrepareOptions = SplatSourceOptions & {
681
+ /**
682
+ * Sequence transitions only. Default false: the prepare only FETCHES the file's bytes, so one
683
+ * splat stays resident and the decode + upload run at the swap, in the empty beat. `true`: the
684
+ * full prepare (decoded and uploaded now, not in the scene) — no load at the swap, two assets
685
+ * resident during the dwell.
686
+ */
687
+ resident?: boolean;
688
+ };
655
689
 
656
690
  /** setSource's particle transitions (docs/splat-effects.md §Particle transitions). */
657
691
  export type SplatParticleTransitionName = 'swarm' | 'burst' | 'shimmer-cross' | 'dust';
658
692
 
693
+ /** The reveals a sequence transition can run (each draws nothing at its start). */
694
+ export type SplatSequenceRevealName = 'assemble' | 'dissolve-in' | 'converge' | 'shimmer' | 'sweep' | 'fade';
695
+
696
+ /**
697
+ * setSource's general SEQUENCE transition: `out` backwards on the current photo, the swap, `in`
698
+ * forwards on the next. `durationMs` / `beat` / `easing` here or at the top level of the options
699
+ * (the top level wins).
700
+ */
701
+ export interface SplatSequenceTransition {
702
+ type: 'sequence';
703
+ out: SplatSequenceRevealName;
704
+ in: SplatSequenceRevealName;
705
+ durationMs?: number;
706
+ beat?: number;
707
+ easing?: SplatEasing;
708
+ }
709
+
659
710
  /**
660
711
  * What `prepareSource()` resolves to: an opaque, single-use handle for `setSource`. The asset is
661
- * fully resident (GPU textures + the engine's centre array) until used or disposed.
712
+ * fully resident (GPU textures + the engine's centre array) until used or disposed — except for a
713
+ * sequence transition (`prepareSource(src, { transition: 'reassemble' })`), whose prepare only
714
+ * fetches the file's bytes: nothing reaches the engine until the swap.
662
715
  */
663
716
  export interface SplatPreparedSource {
664
- /** The asset's own count (every splat of a flat source). */
665
- readonly numSplats: number;
717
+ /** The asset's own count (every splat of a flat source); null for a fetch-only (sequence) prepare. */
718
+ readonly numSplats: number | null;
666
719
  /** `'ready'` until `setSource` uses it (`'used'`) or `dispose()` drops it (`'disposed'`). */
667
720
  readonly state: 'ready' | 'used' | 'disposed';
668
721
  /** Release the prepared asset (no-op once used or disposed). */