@displayxr/inline3d 1.45.1 → 1.47.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/CHANGELOG.md +108 -0
- package/README.md +3 -0
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/cursor-depth.d.ts +41 -0
- package/depth.d.ts +184 -0
- package/dist/call.js +6 -6
- package/docs/making-3d-look-right.md +49 -0
- package/docs/woven-canvas-rules.md +378 -0
- package/index.d.ts +53 -3
- package/js/camera/pair-cursor.js +27 -1
- package/js/inline3d-cursor-depth-playcanvas.js +20 -2
- package/js/inline3d-cursor-depth.js +160 -1
- package/js/inline3d-depth-budget.js +342 -0
- package/js/inline3d-model-entry.js +15 -1
- package/js/inline3d-model-playcanvas.js +27 -2
- package/js/inline3d-model.js +22 -2
- package/js/inline3d-splat-deferred.js +2 -0
- package/js/inline3d-splat-pc-entry.js +2 -1
- package/js/inline3d-splat-playcanvas.js +46 -43
- package/js/inline3d-splat-shared.js +311 -0
- package/js/inline3d-splat.js +31 -3
- package/js/inline3d-three.js +75 -12
- package/js/inline3d-viewer.js +15 -1
- package/js/inline3d.js +71 -0
- package/llms.txt +21 -0
- package/model.d.ts +53 -0
- package/package.json +10 -1
- package/splat.d.ts +33 -0
- package/three.d.ts +19 -4
- package/viewer.d.ts +11 -0
|
@@ -38,7 +38,8 @@
|
|
|
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, declareViewRig } from './inline3d-splat-shared.js';
|
|
41
|
+
import { resolveControls, normalizeCameraPose, now, MAX_DT_S, declareViewRig, attachModelStereo, attachComfortDepth, resolveDepthOption, framedSubjectBounds } from './inline3d-splat-shared.js';
|
|
42
|
+
import { displayRig } from './inline3d-three.js';
|
|
42
43
|
import {
|
|
43
44
|
NEUTRAL_STUDIO,
|
|
44
45
|
neutralStudioRadiance,
|
|
@@ -440,6 +441,7 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
440
441
|
} = opts;
|
|
441
442
|
const ctl = validatePlayCanvasModelOptions(opts);
|
|
442
443
|
const pageMode = ctl.page;
|
|
444
|
+
const depthOption = resolveDepthOption(opts.depth, '@displayxr/inline3d/model');
|
|
443
445
|
if (pageMode && ctl.ignored.length) {
|
|
444
446
|
console.info(`${TAG} controls:'page' — the page owns the camera, so ${ctl.ignored.join(', ')} ${ctl.ignored.length === 1 ? 'is' : 'are'} ignored.`);
|
|
445
447
|
}
|
|
@@ -468,6 +470,8 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
468
470
|
sky: false,
|
|
469
471
|
viewerEase: opts.viewerEase,
|
|
470
472
|
cursor: opts.cursor,
|
|
473
|
+
// Opt-in; undefined = the viewer's own default (a transparent clear).
|
|
474
|
+
clearColor: opts.clearColor,
|
|
471
475
|
// Each environment's own look: 'room' is three's (untonemapped), the rest the Sample Viewer's.
|
|
472
476
|
toneMapping: envMap ? 'neutral' : ENVIRONMENT_TONE_MAPPING[environment] || 'neutral',
|
|
473
477
|
});
|
|
@@ -510,6 +514,7 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
510
514
|
},
|
|
511
515
|
remove() {
|
|
512
516
|
removed = true;
|
|
517
|
+
depth.dispose();
|
|
513
518
|
handle?.remove();
|
|
514
519
|
viewer.dispose(); // app.destroy(): the model, its textures and any page entities go with it
|
|
515
520
|
},
|
|
@@ -530,6 +535,21 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
530
535
|
Object.defineProperty(out, 'renderScale', { get: () => viewer.effectiveRenderScale, enumerable: true, configurable: true });
|
|
531
536
|
Object.defineProperty(out, 'renderScaleRequested', { get: () => viewer.renderScale, enumerable: true, configurable: true });
|
|
532
537
|
|
|
538
|
+
// handle.setStereo / handle.rig (a depth slider; nothing is declared until it is called).
|
|
539
|
+
const stereo = attachModelStereo(out, { getTile: () => handle, vH: virtualDisplayHeight, makeDisplayRig: displayRig, page: pageMode });
|
|
540
|
+
// `depth: 'auto'` (opt-in): the comfort factor under setStereo's, on the same display rig.
|
|
541
|
+
// Without it, inert: `depthFactor` reads 1 and nothing is measured or declared.
|
|
542
|
+
const depth = attachComfortDepth(out, {
|
|
543
|
+
option: depthOption,
|
|
544
|
+
canvas,
|
|
545
|
+
getTile: () => handle,
|
|
546
|
+
getBounds: () => (out.frame ? framedSubjectBounds(viewer) : null),
|
|
547
|
+
getVH: () => virtualDisplayHeight,
|
|
548
|
+
apply: (k) => stereo.setBase(k),
|
|
549
|
+
tag: TAG,
|
|
550
|
+
});
|
|
551
|
+
if (pageMode) depth.ignore("controls:'page' (the page's camera rig)");
|
|
552
|
+
|
|
533
553
|
if (wall && wall.supported) {
|
|
534
554
|
handle = wall.addScene(canvas, viewer.onFrame, {
|
|
535
555
|
...(pageMode
|
|
@@ -574,7 +594,11 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
574
594
|
const dm = (c[0] - ax.origin[0]) * ax.forward[0] + (c[1] - ax.origin[1]) * ax.forward[1] + (c[2] - ax.origin[2]) * ax.forward[2];
|
|
575
595
|
if (dm > 0) d = dm / (ax.scale || 1);
|
|
576
596
|
}
|
|
577
|
-
|
|
597
|
+
// setStereo's values, ABSOLUTE on this camera rig (1 and 1 until it is called: as before).
|
|
598
|
+
out.viewRig = pageViewRig(
|
|
599
|
+
{ verticalFovDeg: viewer.page.fov, convergence: d ?? 2, comfortDepth: ctl.comfortDepth, ipdFactor: out.rig.ipdFactor, parallaxFactor: out.rig.parallaxFactor },
|
|
600
|
+
out.viewRig || {},
|
|
601
|
+
);
|
|
578
602
|
declareViewRig(handle, out.viewRig);
|
|
579
603
|
};
|
|
580
604
|
if (typeof opts.onBeforeFrame === 'function') {
|
|
@@ -666,6 +690,7 @@ export function attachPlayCanvasModel(out, wall, canvas, src, opts, pending = []
|
|
|
666
690
|
if (bounds) {
|
|
667
691
|
out.frame = bounds;
|
|
668
692
|
viewer.fitTo(bounds.center, bounds.extent);
|
|
693
|
+
depth.refresh();
|
|
669
694
|
}
|
|
670
695
|
return out;
|
|
671
696
|
})();
|
package/js/inline3d-model.js
CHANGED
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
// your own origin (`/draco/`, `/basis/`) and `opts.decoderPath` moves that. See
|
|
39
39
|
// docs/authoring-inline-3d.md#compressed-gltf.
|
|
40
40
|
|
|
41
|
-
import { guardedAttach } from './inline3d-splat-shared.js';
|
|
41
|
+
import { guardedAttach, attachModelStereo, attachComfortDepth, resolveDepthOption, framedSubjectBounds } from './inline3d-splat-shared.js';
|
|
42
42
|
import * as THREE from 'three';
|
|
43
|
-
import { EyeCamera, EdgeFeather, DepthCursor, raycastHitTest } from './inline3d-three.js';
|
|
43
|
+
import { EyeCamera, EdgeFeather, DepthCursor, raycastHitTest, displayRig } from './inline3d-three.js';
|
|
44
44
|
import { resolveCursorOption } from './inline3d-cursor-depth.js';
|
|
45
45
|
import { CURSOR_MODEL_RAYS_PER_FRAME } from './inline3d-cursor-perf.js';
|
|
46
46
|
import { SceneViewer } from './inline3d-viewer.js';
|
|
@@ -403,6 +403,8 @@ function addModelUnguarded(wall, canvas, src, opts = {}) {
|
|
|
403
403
|
|
|
404
404
|
const paths = normalizeDecoderPath(decoderPath);
|
|
405
405
|
const injected = { draco: injectedDraco, ktx2: injectedKtx2, meshopt: injectedMeshopt };
|
|
406
|
+
// Opt-in comfort depth; a bad value throws here, at the call.
|
|
407
|
+
const depthOption = resolveDepthOption(opts.depth, '@displayxr/inline3d/model');
|
|
406
408
|
|
|
407
409
|
const viewer = new SceneViewer(THREE, canvas, {
|
|
408
410
|
virtualDisplayHeight,
|
|
@@ -416,6 +418,8 @@ function addModelUnguarded(wall, canvas, src, opts = {}) {
|
|
|
416
418
|
feather,
|
|
417
419
|
logTag: '[inline3d/model]',
|
|
418
420
|
viewerEase: opts.viewerEase,
|
|
421
|
+
// Opt-in; undefined = the viewer's own default (a transparent clear).
|
|
422
|
+
clearColor: opts.clearColor,
|
|
419
423
|
}).useEyeCamera(EyeCamera, EdgeFeather);
|
|
420
424
|
const cursorOpts = resolveCursorOption(opts.cursor, '@displayxr/inline3d/model');
|
|
421
425
|
// Amortised pick (a few rays a frame): the sprite follows the newest pointer position every
|
|
@@ -436,6 +440,7 @@ function addModelUnguarded(wall, canvas, src, opts = {}) {
|
|
|
436
440
|
setPose: (p) => viewer.setPose(p),
|
|
437
441
|
resetPose: () => viewer.resetPose(),
|
|
438
442
|
remove() {
|
|
443
|
+
depth.dispose();
|
|
439
444
|
handle?.remove();
|
|
440
445
|
viewer.dispose();
|
|
441
446
|
if (out.model) disposeTree(out.model);
|
|
@@ -450,6 +455,20 @@ function addModelUnguarded(wall, canvas, src, opts = {}) {
|
|
|
450
455
|
Object.defineProperty(out, 'renderScale', { get: () => viewer.effectiveRenderScale, enumerable: true, configurable: true });
|
|
451
456
|
Object.defineProperty(out, 'renderScaleRequested', { get: () => viewer.renderScale, enumerable: true, configurable: true });
|
|
452
457
|
|
|
458
|
+
// handle.setStereo / handle.rig (a depth slider; nothing is declared until it is called).
|
|
459
|
+
const stereo = attachModelStereo(out, { getTile: () => handle, vH: virtualDisplayHeight, makeDisplayRig: displayRig });
|
|
460
|
+
// `depth: 'auto'` (opt-in): the comfort factor under setStereo's, on the same display rig.
|
|
461
|
+
// Without it, inert: `depthFactor` reads 1 and nothing is measured or declared.
|
|
462
|
+
const depth = attachComfortDepth(out, {
|
|
463
|
+
option: depthOption,
|
|
464
|
+
canvas,
|
|
465
|
+
getTile: () => handle,
|
|
466
|
+
getBounds: () => (out.frame ? framedSubjectBounds(viewer) : null),
|
|
467
|
+
getVH: () => virtualDisplayHeight,
|
|
468
|
+
apply: (k) => stereo.setBase(k),
|
|
469
|
+
tag: '[inline3d/model]',
|
|
470
|
+
});
|
|
471
|
+
|
|
453
472
|
// Window first, content when it lands — same reasoning as ./splat: a grid should not appear
|
|
454
473
|
// one tile at a time in download order.
|
|
455
474
|
let handle = null;
|
|
@@ -528,6 +547,7 @@ function addModelUnguarded(wall, canvas, src, opts = {}) {
|
|
|
528
547
|
if (bounds) {
|
|
529
548
|
out.frame = bounds;
|
|
530
549
|
viewer.fitTo(bounds.center, bounds.extent);
|
|
550
|
+
depth.refresh();
|
|
531
551
|
}
|
|
532
552
|
return out;
|
|
533
553
|
})().catch((err) => {
|
|
@@ -53,6 +53,8 @@ export function addSplatDeferred(wall, canvas, src, opts) {
|
|
|
53
53
|
frame: null,
|
|
54
54
|
camera: null,
|
|
55
55
|
rig: null,
|
|
56
|
+
// The `depth` option's comfort factor in force (1 until measured, and always without it).
|
|
57
|
+
depthFactor: 1,
|
|
56
58
|
perf: null,
|
|
57
59
|
setPose: page ? pageOnly('setPose') : queue('setPose'),
|
|
58
60
|
resetPose: page ? pageOnly('resetPose') : queue('resetPose'),
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
// Not here: measureSplatBounds (it measures a Spark mesh — import it from ./splat).
|
|
17
17
|
|
|
18
18
|
import { resolveRevealOption } from './inline3d-splat-effects.js';
|
|
19
|
-
import { CAPTURE_FITS, guardedAttach, playcanvasCannotRead, resolveControls } from './inline3d-splat-shared.js';
|
|
19
|
+
import { CAPTURE_FITS, guardedAttach, playcanvasCannotRead, resolveControls, resolveDepthOption } from './inline3d-splat-shared.js';
|
|
20
20
|
import { addSplatDeferred } from './inline3d-splat-deferred.js';
|
|
21
21
|
|
|
22
22
|
export { applySplatPerf, SPLAT_PERF_PRESETS } from './inline3d-splat-perf.js';
|
|
@@ -44,6 +44,7 @@ function addSplatUnguarded(wall, canvas, src, opts = {}) {
|
|
|
44
44
|
);
|
|
45
45
|
}
|
|
46
46
|
resolveControls(opts);
|
|
47
|
+
resolveDepthOption(opts.depth);
|
|
47
48
|
resolveRevealOption(opts.reveal);
|
|
48
49
|
const why = playcanvasCannotRead(src, opts);
|
|
49
50
|
if (why) throw new Error(`@displayxr/inline3d/splat: ${why}`);
|
|
@@ -119,6 +119,12 @@ import {
|
|
|
119
119
|
normalizeCameraPose,
|
|
120
120
|
coverageExponent,
|
|
121
121
|
declareViewRig,
|
|
122
|
+
parseClearColor,
|
|
123
|
+
validateSetStereo,
|
|
124
|
+
stereoScalarOf,
|
|
125
|
+
attachComfortDepth,
|
|
126
|
+
resolveDepthOption,
|
|
127
|
+
framedSubjectBounds,
|
|
122
128
|
} from './inline3d-splat-shared.js';
|
|
123
129
|
import { resolveCursorOption } from './inline3d-cursor-option.js';
|
|
124
130
|
|
|
@@ -827,6 +833,10 @@ export class PlayCanvasSplatViewer {
|
|
|
827
833
|
cursor = null,
|
|
828
834
|
} = opts;
|
|
829
835
|
this.canvas = canvas;
|
|
836
|
+
// `clearColor` (opt-in): the eye cameras' clear, [r,g,b,a] in 0..1, or null = the transparent
|
|
837
|
+
// clear every tile had before it existed. An OPAQUE clear avoids stale / black pixels where the
|
|
838
|
+
// weave reads an alpha-0 hole (web#198).
|
|
839
|
+
this.clearColor = parseClearColor(opts.clearColor, opts.logTag || '[inline3d/splat]');
|
|
830
840
|
// `cursor: 'depth'` (ADR-046): the depth-aware cursor. OPT-IN AND FREE OTHERWISE — its module
|
|
831
841
|
// is imported only when asked for (attachEngine), and every per-frame hook is an optional call
|
|
832
842
|
// on `cursorDepth`, which stays null without it.
|
|
@@ -1306,6 +1316,8 @@ export class PlayCanvasSplatViewer {
|
|
|
1306
1316
|
onFrame(views, layer, frame) {
|
|
1307
1317
|
if (this._disposed) return;
|
|
1308
1318
|
if (this._mode !== '3d') this.stopMono();
|
|
1319
|
+
// cursor: 'depth' hands its depth to this layer where the runtime draws the web cursor (ADR-046 Amendment 4).
|
|
1320
|
+
this._frameLayer = layer || null;
|
|
1309
1321
|
// The rig these views were located with: Blink chained the rig declared BEFORE this callback,
|
|
1310
1322
|
// and the tick below may declare a new one (a focus ease) for the NEXT locate.
|
|
1311
1323
|
if (this.layerRigs?.active) {
|
|
@@ -1348,6 +1360,7 @@ export class PlayCanvasSplatViewer {
|
|
|
1348
1360
|
this._monoRaf = requestAnimationFrame(loop);
|
|
1349
1361
|
this._beforeFrame?.(null);
|
|
1350
1362
|
if (this._disposed || this._mode !== 'mono') return; // the callback removed or re-wove us
|
|
1363
|
+
this._frameLayer = null;
|
|
1351
1364
|
this.cursorDepth?.inactive(); // 2D: the normal cursor
|
|
1352
1365
|
this._tick();
|
|
1353
1366
|
this._drawMono();
|
|
@@ -1981,7 +1994,7 @@ export class PlayCanvasSplatViewer {
|
|
|
1981
1994
|
const pc = this.pc;
|
|
1982
1995
|
const e = new pc.Entity(name, this.app);
|
|
1983
1996
|
e.addComponent('camera', {
|
|
1984
|
-
clearColor: new pc.Color(0, 0, 0, 0),
|
|
1997
|
+
clearColor: this.clearColor ? new pc.Color(this.clearColor[0], this.clearColor[1], this.clearColor[2], this.clearColor[3]) : new pc.Color(0, 0, 0, 0),
|
|
1985
1998
|
nearClip: MONO_NEAR,
|
|
1986
1999
|
farClip: MONO_FAR,
|
|
1987
2000
|
fov: MONO_FOV,
|
|
@@ -2221,7 +2234,7 @@ export class PlayCanvasSplatViewer {
|
|
|
2221
2234
|
// handle.setVideo's plane: size, eye split, and a new frame's upload (./inline3d-splat-video.js).
|
|
2222
2235
|
this._videoPlane?.beforeDraw(entries, rect);
|
|
2223
2236
|
// cursor: 'depth' — place the sprite and queue its lines for the tick below (no-op unless opted in).
|
|
2224
|
-
this.cursorDepth?.frame(entries);
|
|
2237
|
+
this.cursorDepth?.frame(entries, this._frameLayer);
|
|
2225
2238
|
app.tick(now());
|
|
2226
2239
|
this._afterTick();
|
|
2227
2240
|
return true;
|
|
@@ -3122,41 +3135,9 @@ export function validateWarmEffects(list) {
|
|
|
3122
3135
|
});
|
|
3123
3136
|
}
|
|
3124
3137
|
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
*/
|
|
3129
|
-
export function validateSetStereo(o, prev) {
|
|
3130
|
-
if (o === null) return null;
|
|
3131
|
-
if (typeof o !== 'object') throw new TypeError('@displayxr/inline3d/splat: setStereo takes an object or null.');
|
|
3132
|
-
// Absolute (…Factor) or RELATIVE to the asset's own (…Scale); per scalar, the last one given wins.
|
|
3133
|
-
const pairs = [
|
|
3134
|
-
['ipdFactor', 'ipdScale'],
|
|
3135
|
-
['parallaxFactor', 'parallaxScale'],
|
|
3136
|
-
];
|
|
3137
|
-
const keys = pairs.flat();
|
|
3138
|
-
const unknown = Object.keys(o).filter((k) => !keys.includes(k));
|
|
3139
|
-
if (unknown.length) throw new Error(`@displayxr/inline3d/splat: setStereo — unknown option(s) ${unknown.join(', ')}.`);
|
|
3140
|
-
for (const [a, b] of pairs) {
|
|
3141
|
-
if (o[a] != null && o[b] != null) throw new Error(`@displayxr/inline3d/splat: setStereo — ${a} and ${b} are exclusive.`);
|
|
3142
|
-
}
|
|
3143
|
-
const next = { ...(prev || {}) };
|
|
3144
|
-
for (const [a, b] of pairs) {
|
|
3145
|
-
for (const k of [a, b]) {
|
|
3146
|
-
if (!(k in o) || o[k] === undefined) continue;
|
|
3147
|
-
if (o[k] === null) {
|
|
3148
|
-
delete next[k];
|
|
3149
|
-
continue;
|
|
3150
|
-
}
|
|
3151
|
-
if (typeof o[k] !== 'number' || !Number.isFinite(o[k]) || o[k] < 0) {
|
|
3152
|
-
throw new RangeError(`@displayxr/inline3d/splat: setStereo — ${k} must be a finite number >= 0 (got ${o[k]}).`);
|
|
3153
|
-
}
|
|
3154
|
-
next[k] = o[k];
|
|
3155
|
-
delete next[k === a ? b : a];
|
|
3156
|
-
}
|
|
3157
|
-
}
|
|
3158
|
-
return Object.keys(next).length ? next : null;
|
|
3159
|
-
}
|
|
3138
|
+
// validateSetStereo lives in ./inline3d-splat-shared.js now (./model's setStereo shares it); it is
|
|
3139
|
+
// re-exported from here so every name this module exported before still resolves.
|
|
3140
|
+
export { validateSetStereo };
|
|
3160
3141
|
|
|
3161
3142
|
/**
|
|
3162
3143
|
* handle.setRig's arguments, validated and resolved (throws at the call, before anything runs).
|
|
@@ -3400,6 +3381,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3400
3381
|
// an advanced caller may reach this function directly.
|
|
3401
3382
|
const ctl = resolveControls(opts);
|
|
3402
3383
|
const pageMode = ctl.page;
|
|
3384
|
+
const depthOption = resolveDepthOption(opts.depth);
|
|
3403
3385
|
if (pageMode && ctl.ignored.length) {
|
|
3404
3386
|
console.info(
|
|
3405
3387
|
`[inline3d/splat] controls:'page' — the page owns the camera, so ${ctl.ignored.join(', ')} ` +
|
|
@@ -3431,10 +3413,27 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3431
3413
|
sky: opts.sky,
|
|
3432
3414
|
viewerEase: opts.viewerEase,
|
|
3433
3415
|
cursor: opts.cursor,
|
|
3416
|
+
// Opt-in; undefined = the viewer's own default (a transparent clear).
|
|
3417
|
+
clearColor: opts.clearColor,
|
|
3434
3418
|
});
|
|
3435
3419
|
|
|
3436
3420
|
let handle = null;
|
|
3437
3421
|
let unbindFocusInput = null;
|
|
3422
|
+
// `depth: 'auto'` (opt-in): the comfort factor, multiplied under setStereo's on the DISPLAY rig
|
|
3423
|
+
// declareDisplay sends (camera rigs and setVideo's own rig never carry it). Without the option:
|
|
3424
|
+
// inert, `depthFactor` 1, nothing measured, nothing declared.
|
|
3425
|
+
const depth = attachComfortDepth(out, {
|
|
3426
|
+
option: depthOption,
|
|
3427
|
+
canvas,
|
|
3428
|
+
getTile: () => handle,
|
|
3429
|
+
getBounds: () => (out.rig && out.rig.type !== 'camera' && !vid?.on ? framedSubjectBounds(viewer) : null),
|
|
3430
|
+
getVH: () => viewer.vH,
|
|
3431
|
+
apply: () => {
|
|
3432
|
+
if (!pageMode && !vid?.on && out.rig?.type !== 'camera' && lastDisplayBase) declareDisplay(lastDisplayBase);
|
|
3433
|
+
},
|
|
3434
|
+
tag: '[inline3d/splat]',
|
|
3435
|
+
});
|
|
3436
|
+
if (pageMode) depth.ignore("controls:'page' (the page's camera rig)");
|
|
3438
3437
|
|
|
3439
3438
|
// ── diagnostics (diag / ?dxrdiag — ./inline3d-splat-diag.js) ──
|
|
3440
3439
|
const diagCfg = resolveDiag(opts.diag);
|
|
@@ -3702,6 +3701,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3702
3701
|
effects: () => (fx ? fx.list() : []),
|
|
3703
3702
|
remove() {
|
|
3704
3703
|
removed = true;
|
|
3704
|
+
depth.dispose();
|
|
3705
3705
|
dropVideo(); // before the app goes: the plane's rVFC, and a <video> this handle made
|
|
3706
3706
|
current = null;
|
|
3707
3707
|
for (const p of [...livePrepared]) p.dispose();
|
|
@@ -4366,9 +4366,11 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4366
4366
|
// raw: setVideo's own display rig — not the page's rig, so neither setStereo's factors nor
|
|
4367
4367
|
// the base setStereo re-declares from.
|
|
4368
4368
|
if (!raw) lastDisplayBase = base;
|
|
4369
|
+
// The `depth` option's comfort factor (1 without it), × setStereo's: never on setVideo's rig.
|
|
4370
|
+
const kd = raw ? 1 : depth.factor;
|
|
4369
4371
|
const f =
|
|
4370
|
-
stereoOverride && !raw
|
|
4371
|
-
? { ...base, ipdFactor: stereoScalar(base.ipdFactor ?? 1, 'ipd'), parallaxFactor: stereoScalar(base.parallaxFactor ?? 1, 'parallax') }
|
|
4372
|
+
(stereoOverride || kd !== 1) && !raw
|
|
4373
|
+
? { ...base, ipdFactor: stereoScalar(base.ipdFactor ?? 1, 'ipd') * kd, parallaxFactor: stereoScalar(base.parallaxFactor ?? 1, 'parallax') * kd }
|
|
4372
4374
|
: base;
|
|
4373
4375
|
const key = `${f.vH}|${f.ipdFactor ?? 1}|${f.parallaxFactor ?? 1}|${f.perspectiveFactor ?? 1}`;
|
|
4374
4376
|
if (declaredDisplay === key) return;
|
|
@@ -4404,6 +4406,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4404
4406
|
viewer.setFocus(modelToContent(resolved.focus), { snap: true, recentre: false });
|
|
4405
4407
|
lastConvergence = Number.NaN;
|
|
4406
4408
|
pushViewRig(true);
|
|
4409
|
+
depth.ignore('a camera rig');
|
|
4407
4410
|
return;
|
|
4408
4411
|
}
|
|
4409
4412
|
if (viewer.mono.capture) viewer.useDisplayCamera();
|
|
@@ -4422,6 +4425,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4422
4425
|
resolved.focusDefaultSource = 'frame';
|
|
4423
4426
|
resolved.frame = target;
|
|
4424
4427
|
declareDisplay(disp);
|
|
4428
|
+
depth.resume();
|
|
4425
4429
|
return;
|
|
4426
4430
|
}
|
|
4427
4431
|
// The splat itself, as a display-rig addSplat frames it (the load-time path).
|
|
@@ -4434,6 +4438,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4434
4438
|
}
|
|
4435
4439
|
if (disp) resolved.frame = bounds ? { center: bounds.center.slice(), extent: bounds.extent.slice(), source: 'splat' } : null;
|
|
4436
4440
|
declareDisplay(disp || { vH: bootFraming.vH });
|
|
4441
|
+
if (bounds) depth.resume();
|
|
4437
4442
|
}
|
|
4438
4443
|
|
|
4439
4444
|
// ── handle.setStereo: live stereo strength on whatever rig is declared ──
|
|
@@ -4449,10 +4454,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4449
4454
|
}
|
|
4450
4455
|
/** One scalar under setStereo: its absolute value, else `base` × its scale, else `base`. */
|
|
4451
4456
|
function stereoScalar(base, k) {
|
|
4452
|
-
|
|
4453
|
-
if (o?.[`${k}Factor`] != null) return o[`${k}Factor`];
|
|
4454
|
-
if (o?.[`${k}Scale`] != null) return base * o[`${k}Scale`];
|
|
4455
|
-
return base;
|
|
4457
|
+
return stereoScalarOf(stereoOverride, base, k);
|
|
4456
4458
|
}
|
|
4457
4459
|
|
|
4458
4460
|
/**
|
|
@@ -4940,6 +4942,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4940
4942
|
lastConvergence = s.lastConvergence;
|
|
4941
4943
|
state.on = false;
|
|
4942
4944
|
if (stereoOverride !== state.stereoAtEnter) redeclareStereo(); // a setStereo made while the video held the rig
|
|
4945
|
+
depth.refresh(); // the `depth` option holds still under a video; measure again (no-op without it)
|
|
4943
4946
|
|
|
4944
4947
|
}
|
|
4945
4948
|
|