@displayxr/inline3d 1.12.1 → 1.13.1

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.12.1",
3
+ "version": "1.13.1",
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",
@@ -50,6 +50,7 @@
50
50
  "js/inline3d-splat-rig.js",
51
51
  "js/inline3d-splat-playcanvas.js",
52
52
  "js/inline3d-splat-shared.js",
53
+ "js/inline3d-splat-effects.js",
53
54
  "js/inline3d-model.js",
54
55
  "js/inline3d-model-entry.js",
55
56
  "js/inline3d-model-playcanvas.js",
@@ -61,7 +62,8 @@
61
62
  "model.d.ts",
62
63
  "CHANGELOG.md",
63
64
  "README.md",
64
- "LICENSE"
65
+ "LICENSE",
66
+ "THIRD_PARTY_NOTICES.md"
65
67
  ],
66
68
  "keywords": [
67
69
  "displayxr",
package/splat.d.ts CHANGED
@@ -327,6 +327,133 @@ export interface SplatOptions {
327
327
  * caught and warned once; frames keep rendering.
328
328
  */
329
329
  onBeforeFrame?: (frame: SplatFrameInfo) => void;
330
+ /**
331
+ * `engine: 'playcanvas'` only (throws on Spark): play a reveal on the first asset. Installed at
332
+ * its start state before the first frame, played once `firstWoven` settles (at once in 2D) —
333
+ * docs/splat-effects.md. A name uses that effect's defaults; an object overrides them.
334
+ */
335
+ reveal?: SplatRevealName | false | SplatRevealSpec;
336
+ }
337
+
338
+ /** The transition effects `reveal` accepts. */
339
+ export type SplatRevealName = 'inflate' | 'sweep' | 'dissolve' | 'fade';
340
+
341
+ /** Named easings; a function `(x) => y` on [0, 1] also works. */
342
+ export type SplatEasing =
343
+ | 'linear'
344
+ | 'easeInQuad'
345
+ | 'easeOutQuad'
346
+ | 'easeInOutQuad'
347
+ | 'easeInCubic'
348
+ | 'easeOutCubic'
349
+ | 'easeInOutCubic'
350
+ | 'easeInOutSine'
351
+ | ((x: number) => number);
352
+
353
+ /**
354
+ * Where an effect is centred: `'focus'` (the focus point), `'eyes'` (the eyes' midpoint — the
355
+ * inflate default), a point in the splat's OWN space `[x, y, z]`, or a canvas point
356
+ * `[clientX, clientY]` / `{ clientX, clientY }` resolved with `pick()` when the effect starts.
357
+ * All effects are keyed on world position and time only, so both eyes agree.
358
+ */
359
+ export type SplatEffectOrigin = 'focus' | 'eyes' | [number, number, number] | [number, number] | { clientX: number; clientY: number };
360
+
361
+ /** Options every played effect takes. */
362
+ export interface SplatEffectTiming {
363
+ durationMs?: number;
364
+ /** Wait this long at the start state before the clock runs. */
365
+ holdMs?: number;
366
+ easing?: SplatEasing;
367
+ origin?: SplatEffectOrigin;
368
+ /** `'in'` (default): arrive, then the effect is removed. `'out'`: leave; the end state is held until `stopEffect`. */
369
+ direction?: 'in' | 'out';
370
+ /** `'tile'` (default, every splat of the tile, render time) or `'entity'` (the current asset only, work buffer). */
371
+ scope?: 'tile' | 'entity';
372
+ }
373
+
374
+ /** `reveal: { type, … }`. */
375
+ export interface SplatRevealSpec extends Omit<SplatEffectTiming, 'direction' | 'scope'> {
376
+ type: SplatRevealName;
377
+ [param: string]: unknown;
378
+ }
379
+
380
+ /** A custom effect's GLSL (PlayCanvas-shaped) — docs/splat-effects.md §custom. */
381
+ export interface SplatCustomEffect extends SplatEffectTiming {
382
+ /**
383
+ * Any of `void modifySplatCenter(inout vec3 center)`, `void modifySplatRotationScale(vec3
384
+ * originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale)`, `void
385
+ * modifySplatColor(vec3 center, inout vec4 color)` — world-space centres. `dxrProgress` (0..1)
386
+ * and `dxrTime` (s) are defined; `splat.index` / `splat.uv` are the asset's own file index in
387
+ * `scope: 'entity'`. Screen-space inputs are refused.
388
+ */
389
+ glsl: string;
390
+ /** Tile scope only: `void modifySplatColor(vec2 gaussianUV, inout vec4 color)` per fragment. */
391
+ fragmentGlsl?: string;
392
+ /** Uniform values, set every frame; a function gets the frame time in ms. */
393
+ uniforms?: Record<string, number | number[] | ((tMs: number) => number | number[])>;
394
+ /** `setEffect` only: hold `dxrProgress` here (default 1). */
395
+ progress?: number;
396
+ /** `playEffect` only: keep the effect at its end instead of removing it. */
397
+ hold?: boolean;
398
+ }
399
+
400
+ export type SplatEffectName = 'inflate' | 'deflate' | 'sweep' | 'dissolve' | 'fade' | 'pulse' | 'grade' | 'clip' | 'custom' | `custom:${string}`;
401
+
402
+ /** `setEffect('grade', …)`. */
403
+ export interface SplatGradeParams {
404
+ /** Stops; default 0. */
405
+ exposure?: number;
406
+ contrast?: number;
407
+ saturation?: number;
408
+ tint?: [number, number, number];
409
+ scope?: 'tile' | 'entity';
410
+ }
411
+
412
+ /** `setEffect('clip', …)` — in the splat's own space, like setFocus. Exactly one shape. */
413
+ export interface SplatClipParams {
414
+ box?: { min: [number, number, number]; max: [number, number, number] };
415
+ sphere?: { center: [number, number, number]; radius: number };
416
+ /** Keep the outside instead. */
417
+ invert?: boolean;
418
+ scope?: 'tile' | 'entity';
419
+ }
420
+
421
+ /** One row of `handle.effects()`. */
422
+ export interface SplatEffectState {
423
+ name: string;
424
+ scope: 'tile' | 'entity';
425
+ stage: 'grade' | 'clip' | 'reveal' | 'pulse' | 'custom';
426
+ /** Played (has a clock) vs set (held). */
427
+ playing: boolean;
428
+ /** Held at its start state until a gate settles (a reveal waiting for `firstWoven`). */
429
+ waiting: boolean;
430
+ /** 0..1, before easing. */
431
+ progress: number;
432
+ }
433
+
434
+ /** `setSource`'s options. */
435
+ export interface SplatSourceOptions {
436
+ /** > 0 = a crossfade of this length (the 1.10 option; same as `transition: 'crossfade'`). */
437
+ fadeMs?: number;
438
+ resetPose?: boolean;
439
+ /**
440
+ * `'cut'` (default), `'crossfade'` (images lerp, see setSource), `'flip'` (the old photo
441
+ * flattens to its convergence plane — zero disparity — the swap happens there, the new one
442
+ * inflates out of its own; default 2200 ms), `'wavefront'` (a soft front crosses left → right
443
+ * with a thin depth ridge riding it; default 2000 ms, ease-in-out; falls back to the crossfade
444
+ * in a hidden tab). For photo slideshows: `crossfade` or `wavefront`.
445
+ */
446
+ transition?: 'cut' | 'crossfade' | 'flip' | 'wavefront';
447
+ durationMs?: number;
448
+ easing?: SplatEasing;
449
+ /** `cut`/`crossfade` only: reveal the INCOMING asset (entity scope) while the old one fades. */
450
+ reveal?: SplatRevealName | false | SplatRevealSpec;
451
+ /** `wavefront`: the soft band, a fraction of the picture width (default 0.18). */
452
+ band?: number;
453
+ /** `wavefront`: the ridge's pull toward the eyes, world units (default 0.03 — 3 cm on a metric photo). */
454
+ ridge?: number;
455
+ /** `wavefront`: the ridge's disparity cap, a fraction of the eye view's width (default 0.004, max 0.05). */
456
+ ridgeMaxDisparity?: number;
330
457
  }
331
458
 
332
459
  /** What `onBeforeFrame` receives. */
@@ -473,10 +600,28 @@ export interface SplatHandle {
473
600
  * `resetPose`. A newer call supersedes an older one still loading. Resolves once the fade has
474
601
  * finished; rejects if the new asset cannot be loaded (the current one stays on screen).
475
602
  */
476
- setSource(
477
- src: string | Blob | ArrayBuffer | Uint8Array,
478
- opts?: { fadeMs?: number; resetPose?: boolean },
479
- ): Promise<SplatHandle>;
603
+ setSource(src: string | Blob | ArrayBuffer | Uint8Array, opts?: SplatSourceOptions): Promise<SplatHandle>;
604
+ /**
605
+ * `engine: 'playcanvas'` only (throws on Spark). Play a transition/pulse/custom effect;
606
+ * validated at the call, run once the first asset is on screen. Resolves `{ finished }` —
607
+ * false when stopped or replaced. docs/splat-effects.md.
608
+ */
609
+ playEffect(
610
+ name: Exclude<SplatEffectName, 'grade' | 'clip'>,
611
+ opts?: SplatEffectTiming & Record<string, unknown>,
612
+ ): Promise<{ finished: boolean }>;
613
+ /**
614
+ * `engine: 'playcanvas'` only. Set a persistent effect (`grade`, `clip`, `custom`), hold a
615
+ * transition at `{ progress }`, or pass `null` to remove it (the exact baseline).
616
+ */
617
+ setEffect(name: 'grade', params: SplatGradeParams | null): SplatHandle;
618
+ setEffect(name: 'clip', params: SplatClipParams | null): SplatHandle;
619
+ setEffect(name: 'custom' | `custom:${string}`, params: SplatCustomEffect | null): SplatHandle;
620
+ setEffect(name: SplatEffectName, params: (SplatEffectTiming & { progress?: number } & Record<string, unknown>) | null): SplatHandle;
621
+ /** Stop one effect (all with no name): `finish: true` jumps to its end state, else removes it. */
622
+ stopEffect(name?: SplatEffectName, opts?: { finish?: boolean }): SplatHandle;
623
+ /** What is on right now. */
624
+ effects(): SplatEffectState[];
480
625
  /**
481
626
  * Called with the live focus (the splat's own space) whenever it moves — easing included — and
482
627
  * which waterfall step it came from. PlayCanvas backend; assign any time, even before `ready`.