@displayxr/inline3d 1.7.0 → 1.8.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/splat.d.ts CHANGED
@@ -34,6 +34,36 @@ export interface SplatPerfOptions {
34
34
  lodSplatCount?: number;
35
35
  /** Minimum on-screen splat size multiplier (needs `lod`); up to ~5 is often invisible. */
36
36
  lodRenderScale?: number;
37
+ /**
38
+ * `engine: 'playcanvas'` only — engine-native knobs, passed straight to `app.scene.gsplat`
39
+ * (and winning over the Spark-knob mapping). `splatBudget` is a global splat count per tile.
40
+ */
41
+ splatBudget?: number;
42
+ /** `engine: 'playcanvas'` only: cull splats whose quad DIAMETER is under this many px. */
43
+ minPixelSize?: number;
44
+ /** `engine: 'playcanvas'` only: the forward-pass alpha floor (engine default 1/255). */
45
+ alphaClipForward?: number;
46
+ /** `engine: 'playcanvas'` only: the engine's AA compensation, for AA-trained assets. */
47
+ antiAlias?: boolean;
48
+ }
49
+
50
+ /**
51
+ * The PlayCanvas backend's viewer (`engine: 'playcanvas'`): the SceneViewer pose surface without
52
+ * three. Not field-compatible with SceneViewer — see docs/playcanvas-adapter.md.
53
+ */
54
+ export interface PlayCanvasSplatViewer {
55
+ idleSpin: number;
56
+ readonly is3D: boolean;
57
+ depthOffset: number;
58
+ /** The engine's `AppBase`, once booted. */
59
+ readonly app: unknown;
60
+ fitTo(center: number[], extent: number[]): void;
61
+ setPose(pose?: OrbitPose): void;
62
+ getPose(opts?: { target?: boolean }): Required<OrbitPose>;
63
+ resetPose(): void;
64
+ getSubjectBounds(): SubjectBounds & { front: number; back: number; scale: number };
65
+ setFocus(point: number[] | { x: number; y: number; z: number } | null, opts?: { snap?: boolean; recentre?: boolean }): PlayCanvasSplatViewer;
66
+ getFocus(opts?: { target?: boolean }): { x: number; y: number; z: number };
37
67
  }
38
68
 
39
69
  /** Camera intrinsics for ONE eye, in pixels, OpenCV convention. */
@@ -92,14 +122,26 @@ export interface ResolvedRig {
92
122
  focalEqMm: number;
93
123
  /** The live focus, in the splat's own space. */
94
124
  focus: number[];
125
+ /**
126
+ * Which step answered the focus. The order: `caller` › `caller-convergence` › `block` (a
127
+ * considered block focus) › `nearest-clump` (the nearest substantial disparity clump in the
128
+ * central half of the frame — needs the block's or the caller's lens) › `block-cloud-median` (a
129
+ * block focus a converter computed as a whole-cloud median) › `median-disparity` › `default`.
130
+ */
95
131
  focusSource:
96
132
  | 'caller'
97
133
  | 'caller-convergence'
98
134
  | 'block'
135
+ | 'nearest-clump'
136
+ | 'block-cloud-median'
99
137
  | 'median-disparity'
100
138
  | 'default'
101
139
  | 'picked'
102
140
  | 'set';
141
+ /** The block's own `focus.source` string (e.g. `'convergence'`, `'cloud-median'`), for diagnostics. */
142
+ blockFocusSource: string | null;
143
+ /** Fraction of the central crop's opacity-weighted mass the winning clump carried (`nearest-clump` only). */
144
+ clumpMassFrac: number | null;
103
145
  /** What Space returns to. */
104
146
  focusDefault: number[];
105
147
  focusDefaultSource: string;
@@ -112,6 +154,22 @@ export interface ResolvedRig {
112
154
  }
113
155
 
114
156
  export interface SplatOptions {
157
+ /**
158
+ * Which renderer. `'spark'` (the default) is three.js + Spark. `'playcanvas'` is the PlayCanvas
159
+ * engine (optional peer `playcanvas >=2.22.3 <3`, loaded by dynamic import only when asked):
160
+ * same handle, reads `.sog` / `.ply` / a Streamed-SOG `lod-meta.json`. Anything else throws.
161
+ */
162
+ engine?: 'spark' | 'playcanvas';
163
+ /**
164
+ * `engine: 'playcanvas'` only: the WebGL context's `preserveDrawingBuffer` (default false) —
165
+ * the knob for the weave's zero-copy read race on large canvases.
166
+ */
167
+ preserveDrawingBuffer?: boolean;
168
+ /**
169
+ * `engine: 'playcanvas'` only: the `playcanvas` module namespace to use instead of
170
+ * `import('playcanvas')` — for a page that already bundles its own copy.
171
+ */
172
+ playcanvas?: unknown;
115
173
  /** Metres of world the tile's height spans (default 0.24). */
116
174
  virtualDisplayHeight?: number;
117
175
  /**
@@ -124,7 +182,17 @@ export interface SplatOptions {
124
182
  flipY?: boolean;
125
183
  /** Degrees/second of turntable once idle (default 8). */
126
184
  idleSpin?: number;
185
+ /**
186
+ * Drag + wheel. On the PlayCanvas backend the drag is TILT-AND-RELAX: measured as a fraction of
187
+ * the canvas box from the press, it tilts up to ±`orbitMaxDeg` (a half-width swipe reaches it)
188
+ * and relaxes back to rest on release; `idleSpin` resumes once at rest. On Spark (SceneViewer)
189
+ * it is still the cumulative turntable (a full-width drag = 180°).
190
+ */
127
191
  orbit?: boolean;
192
+ /** PlayCanvas: the largest drag tilt, degrees, either axis (default 15). */
193
+ orbitMaxDeg?: number;
194
+ /** PlayCanvas: easing time constants, seconds — `drag` while held (0.2), `rest` after release (0.6). */
195
+ orbitEase?: { drag?: number; rest?: number };
128
196
  fit?: 'contain' | 'height' | 'cover' | 'none';
129
197
  /** Fraction of the tile the subject may occupy (default 0.8) — width AND height. */
130
198
  margin?: number;
@@ -138,7 +206,11 @@ export interface SplatOptions {
138
206
  /** Per-eye buffer scale; 0.5–0.7 is usually free (default 1). */
139
207
  renderScale?: number;
140
208
  feather?: number;
141
- /** Minimum ms between splat sorts. Defaults to 16 so both eyes share one sort per frame. */
209
+ /**
210
+ * Spark: minimum ms between splat sorts (default 16, so both eyes share one sort per frame).
211
+ * PlayCanvas: accepted and has no effect — the engine re-sorts when the camera ROTATES, with one
212
+ * directional sort serving every view.
213
+ */
142
214
  sortIntervalMs?: number;
143
215
  /**
144
216
  * Cut overdraw. UNSET changes nothing — every Spark default stays where Spark put it, so an
@@ -150,7 +222,7 @@ export interface SplatOptions {
150
222
  * `true`, −5…−20 % measured) and `'aggressive'` (−22 %) tighten the quad extent instead, which
151
223
  * is the axis that actually pays on the web; both move pixels.
152
224
  */
153
- perf?: true | 'exact' | 'balanced' | 'aggressive' | SplatPerfOptions;
225
+ perf?: boolean | 'exact' | 'balanced' | 'aggressive' | SplatPerfOptions;
154
226
  /**
155
227
  * Which view rig. `'auto'` (the default) reads it off the ASSET — a `.sog` carrying a `camera`
156
228
  * block was lifted from a photograph and gets a camera rig that conserves the recording
@@ -158,6 +230,14 @@ export interface SplatOptions {
158
230
  * detectable when `src` is BYTES.
159
231
  */
160
232
  rig?: 'auto' | 'display' | 'camera';
233
+ /**
234
+ * Camera rig only: what gives when the canvas is not the capture's shape. `'height'` (default,
235
+ * the 1.7 behaviour) keeps the capture's vertical extent and widens or narrows the horizontal to
236
+ * the canvas. `'cover'` always fills the tile with photograph: a canvas WIDER than the capture
237
+ * keeps the width and crops top/bottom (a 4:3 capture in a 16:9 tile); a narrower one is
238
+ * `'height'`. Both backends; the 3D rig's vertical FOV follows the crop. Anything else throws.
239
+ */
240
+ captureFit?: 'height' | 'cover';
161
241
  /** Camera rig only: the distance in world metres that sits ON the glass. */
162
242
  convergence?: number;
163
243
  /**
@@ -191,11 +271,38 @@ export interface SplatOptions {
191
271
 
192
272
  /** What {@link addSplat} returns: a TileHandle plus the objects behind it. */
193
273
  export interface SplatHandle {
194
- readonly viewer: SceneViewer;
195
- /** Spark's SplatMesh. */
274
+ /**
275
+ * SceneViewer on Spark; the PlayCanvas backend's own viewer on `engine: 'playcanvas'` (null
276
+ * there until the backend module has loaded — one module fetch after addSplat returns).
277
+ */
278
+ readonly viewer: SceneViewer | PlayCanvasSplatViewer;
279
+ /**
280
+ * Spark's SplatMesh; on `engine: 'playcanvas'` a `{ numSplats, entity, asset, resource }`
281
+ * record of the engine objects. Null until the asset is loaded.
282
+ */
196
283
  readonly mesh: object;
197
- /** Spark's SparkRenderer. */
198
- readonly spark: object;
284
+ /** Spark's SparkRenderer (absent on `engine: 'playcanvas'`). */
285
+ readonly spark?: object;
286
+ /** Which backend is rendering: `'playcanvas'` or `'spark'`; null until it has loaded. */
287
+ readonly backend: 'playcanvas' | 'spark' | null;
288
+ /**
289
+ * ADVANCED — not covered by the semver promise. The renderer objects behind this window, for a
290
+ * page that wants to add its own content. Null until the backend has booted.
291
+ *
292
+ * PlayCanvas: `{ app, root, camera }` — the tile's `pc.AppBase`; the content root entity (the
293
+ * splat's content space — add your own entities under it, e.g. a glTF through the engine's
294
+ * container loader, skinned and animated included); the eye-rig camera entity. `remove()`
295
+ * destroys the app, and everything under `root` with it.
296
+ *
297
+ * Spark: `{ renderer, scene, camera }` — the WebGLRenderer, the scene, and whichever camera draws
298
+ * the current frame.
299
+ */
300
+ readonly engine:
301
+ | { readonly app: unknown; readonly root: unknown; readonly camera: unknown }
302
+ | { readonly renderer: unknown; readonly scene: unknown; readonly camera: unknown }
303
+ | null;
304
+ /** `engine: 'playcanvas'` only: the live focus, in the splat's own space. */
305
+ getFocus?(opts?: { target?: boolean }): number[] | null;
199
306
  /** Bounds actually used for framing; null until `ready` resolves. */
200
307
  frame: SubjectBounds | null;
201
308
  /**
@@ -222,7 +329,30 @@ export interface SplatHandle {
222
329
  point: number[] | { x: number; y: number; z: number } | null,
223
330
  opts?: { snap?: boolean },
224
331
  ): SplatHandle;
225
- /** What is under a point on the canvas, in the splat's own space — the double-click's raycast. */
332
+ /**
333
+ * Swap the asset (URL or bytes) in place. PlayCanvas backend only — throws on Spark.
334
+ *
335
+ * The new file loads BEHIND the current one; then the two crossfade over `fadeMs` (0 = a cut)
336
+ * and the old one is released. The rig waterfall re-runs for the new file (rig, lens, focus and
337
+ * frame update; `onFocusChange` fires). The pose (yaw/pitch/zoom/depth) is kept unless
338
+ * `resetPose`. A newer call supersedes an older one still loading. Resolves once the fade has
339
+ * finished; rejects if the new asset cannot be loaded (the current one stays on screen).
340
+ */
341
+ setSource(
342
+ src: string | Blob | ArrayBuffer | Uint8Array,
343
+ opts?: { fadeMs?: number; resetPose?: boolean },
344
+ ): Promise<SplatHandle>;
345
+ /**
346
+ * Called with the live focus (the splat's own space) whenever it moves — easing included — and
347
+ * which waterfall step it came from. PlayCanvas backend; assign any time, even before `ready`.
348
+ */
349
+ onFocusChange: ((point: number[], info: { focusSource: ResolvedRig['focusSource'] | null }) => void) | null;
350
+ /**
351
+ * What is under a point on the canvas, in the splat's own space — the double-click's pick.
352
+ * PlayCanvas: the nearest gaussian CENTRE to the ray over the FULL centre set (haze under 5 %
353
+ * opacity skipped); on a Streamed SOG, over the chunks currently resident. Spark: its surface
354
+ * raycast, falling back to the nearest centre.
355
+ */
226
356
  pick(clientX: number, clientY: number): number[] | null;
227
357
 
228
358
  /** Close this window and release its GPU resources. */
package/three.d.ts CHANGED
@@ -100,6 +100,23 @@ export function cameraRigFromCamera(
100
100
  opts?: CameraRigOptions,
101
101
  ): XRViewRigInit;
102
102
 
103
+ /**
104
+ * The same CAMERA-rig descriptor as {@link cameraRigFromCamera}, from a plain pose instead of a
105
+ * three.js camera — for a renderer that is not three. Agrees with cameraRigFromCamera to the bit
106
+ * for the same pose.
107
+ */
108
+ export function cameraRigFromPose(
109
+ pose: {
110
+ /** WORLD position. */
111
+ position: { x: number; y: number; z: number };
112
+ /** WORLD orientation quaternion. */
113
+ orientation: { x: number; y: number; z: number; w: number };
114
+ /** FULL vertical angle in DEGREES (three's `camera.fov` convention). */
115
+ fov: number;
116
+ },
117
+ opts?: CameraRigOptions,
118
+ ): XRViewRigInit;
119
+
103
120
  /**
104
121
  * Build a DISPLAY-rig descriptor — the default rig, made explicit and posable: the canvas is a
105
122
  * portal onto a virtual display `virtualDisplayHeight` metres tall. Adds what the scalar