@displayxr/inline3d 1.22.0 → 1.24.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 +38 -0
- package/js/inline3d-splat-diag.js +17 -1
- package/js/inline3d-splat-layer-rig.js +425 -0
- package/js/inline3d-splat-live.js +87 -7
- package/js/inline3d-splat-playcanvas.js +188 -7
- package/js/inline3d-splat-rig-map.js +369 -0
- package/js/inline3d-splat-video.js +97 -0
- package/js/inline3d-splat.js +18 -0
- package/package.json +3 -1
- package/splat.d.ts +70 -2
|
@@ -67,7 +67,9 @@ import {
|
|
|
67
67
|
sequenceSpans,
|
|
68
68
|
} from './inline3d-splat-effects.js';
|
|
69
69
|
import { LiveOutgoing, resolveOutgoingOption, defaultOutgoing, yieldIdle } from './inline3d-splat-live.js';
|
|
70
|
-
import { VideoPlane, validateSetVideo, PAGE_VIDEO_ERROR } from './inline3d-splat-video.js';
|
|
70
|
+
import { VideoPlane, validateSetVideo, PAGE_VIDEO_ERROR, eyeSplit, EYE_SPLIT_UNIFORM, makeSbsMaterial } from './inline3d-splat-video.js';
|
|
71
|
+
import { LayerRigCameras, validateLayerRig } from './inline3d-splat-layer-rig.js';
|
|
72
|
+
import { RigTracker, remapViews, nodePose, sameRig } from './inline3d-splat-rig-map.js';
|
|
71
73
|
import { resolveDiag, DiagRecorder, startDiagLoop, registerDiag, DIAG_SWITCHES } from './inline3d-splat-diag.js';
|
|
72
74
|
import {
|
|
73
75
|
clamp,
|
|
@@ -816,6 +818,23 @@ export class PlayCanvasSplatViewer {
|
|
|
816
818
|
this.inputLocked = false;
|
|
817
819
|
/** handle.setVideo's plane (./inline3d-splat-video.js) while a video is on, else null. */
|
|
818
820
|
this._videoPlane = null;
|
|
821
|
+
/** handle.setLayerRig's per-rig cameras (./inline3d-splat-layer-rig.js), made on first use. */
|
|
822
|
+
this.layerRigs = null;
|
|
823
|
+
/** () => the view-rig descriptor currently declared to the runtime (or null). */
|
|
824
|
+
this.layerRigSource = null;
|
|
825
|
+
/** That descriptor as it stood when THIS frame's views were pulled (snapshotted in onFrame). */
|
|
826
|
+
this._rigSnap = null;
|
|
827
|
+
/**
|
|
828
|
+
* The rigs this tile declared and which one each frame's views were located for
|
|
829
|
+
* (./inline3d-splat-rig-map.js). Null = the kill switch (diag 'oldrig'): views drawn as
|
|
830
|
+
* located, the live outgoing on its pre-1.24 node chain.
|
|
831
|
+
*/
|
|
832
|
+
this.rigTrack = null;
|
|
833
|
+
/** This frame's rig mapping: { located, eye, remapped, entries } (see _mapViews). */
|
|
834
|
+
this.rigFrame = null;
|
|
835
|
+
this._eyeMap = [];
|
|
836
|
+
this._rigAtPull = null;
|
|
837
|
+
this._eyeSplit = NaN;
|
|
819
838
|
this.boxAspect = 1;
|
|
820
839
|
this.featherPx = feather > 0 ? feather : 0;
|
|
821
840
|
this.captureFit = captureFit;
|
|
@@ -1125,6 +1144,11 @@ export class PlayCanvasSplatViewer {
|
|
|
1125
1144
|
onFrame(views, layer) {
|
|
1126
1145
|
if (this._disposed) return;
|
|
1127
1146
|
if (this._mode !== '3d') this.stopMono();
|
|
1147
|
+
// The rig these views were located with: Blink chained the rig declared BEFORE this callback,
|
|
1148
|
+
// and the tick below may declare a new one (a focus ease) for the NEXT locate.
|
|
1149
|
+
if (this.layerRigs?.active) this._rigSnap = snapshotRig(this.layerRigSource?.(), this._rigSnap);
|
|
1150
|
+
// Same moment, for the rig map: the rig these views were most likely located for (a tie-break).
|
|
1151
|
+
this._rigAtPull = this.rigTrack ? this.rigTrack.latest : null;
|
|
1128
1152
|
// BEFORE the tick: a pose the page sets in here is the one this very frame renders.
|
|
1129
1153
|
this._beforeFrame?.(views || null);
|
|
1130
1154
|
this._tick();
|
|
@@ -1185,6 +1209,7 @@ export class PlayCanvasSplatViewer {
|
|
|
1185
1209
|
this._live = null;
|
|
1186
1210
|
this._videoPlane?.destroy();
|
|
1187
1211
|
this._videoPlane = null;
|
|
1212
|
+
this.layerRigs = null; // its cameras go with the app
|
|
1188
1213
|
try {
|
|
1189
1214
|
this.app?.destroy();
|
|
1190
1215
|
} catch (err) {
|
|
@@ -1576,6 +1601,44 @@ export class PlayCanvasSplatViewer {
|
|
|
1576
1601
|
}
|
|
1577
1602
|
}
|
|
1578
1603
|
|
|
1604
|
+
/**
|
|
1605
|
+
* This frame's rig mapping (./inline3d-splat-rig-map.js): which declared rig the views were
|
|
1606
|
+
* located for (`located`), which the current photo wants (`eye`, the last declared), and — only
|
|
1607
|
+
* when those differ and both are camera rigs — the views remapped to `eye` (`entries`: proj,
|
|
1608
|
+
* viewInv, view per view; `cull`: the same as { proj, pose } entries; `node`: the first eye's
|
|
1609
|
+
* rigid pose). Otherwise `remapped` is false and the views are drawn exactly as located.
|
|
1610
|
+
*/
|
|
1611
|
+
_mapViews(entries, prefer = null) {
|
|
1612
|
+
const rt = this.rigTrack;
|
|
1613
|
+
const rf = (this.rigFrame ||= { located: null, eye: null, remapped: false, entries: null, cull: [], node: new Float64Array(16) });
|
|
1614
|
+
rf.remapped = false;
|
|
1615
|
+
rf.located = rf.eye = null;
|
|
1616
|
+
if (!rt || this._mode !== '3d' || entries.length < 2) return rf;
|
|
1617
|
+
rf.located = rt.locate(entries, prefer);
|
|
1618
|
+
rf.eye = rt.latest;
|
|
1619
|
+
const L = rf.located;
|
|
1620
|
+
const T = rf.eye;
|
|
1621
|
+
if (!L || !T?.portal || L === T || sameRig(L.rig, T.rig)) return rf;
|
|
1622
|
+
const r = remapViews(entries, L.portal, T.portal, this._eyeMap);
|
|
1623
|
+
if (!r) return rf;
|
|
1624
|
+
rf.remapped = true;
|
|
1625
|
+
rf.entries = r;
|
|
1626
|
+
rf.cull.length = r.length;
|
|
1627
|
+
for (let i = 0; i < r.length; i++) {
|
|
1628
|
+
const c = (rf.cull[i] ||= {});
|
|
1629
|
+
c.proj = r[i].proj;
|
|
1630
|
+
c.pose = r[i].viewInv;
|
|
1631
|
+
c.x = r[i].x;
|
|
1632
|
+
c.y = r[i].y;
|
|
1633
|
+
c.width = r[i].width;
|
|
1634
|
+
c.height = r[i].height;
|
|
1635
|
+
c.node = null;
|
|
1636
|
+
}
|
|
1637
|
+
nodePose(T.portal, r[0].eye, rf.node);
|
|
1638
|
+
rf.cull[0].node = rf.node; // a rigid pose for a camera NODE (the views are affine)
|
|
1639
|
+
return rf;
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1579
1642
|
/**
|
|
1580
1643
|
* The mono/capture camera as a lens frame, for the live outgoing's rig chain
|
|
1581
1644
|
* (./inline3d-splat-live.js): the rig node's matrix, the camera pose in rig space, the
|
|
@@ -1600,6 +1663,8 @@ export class PlayCanvasSplatViewer {
|
|
|
1600
1663
|
*/
|
|
1601
1664
|
cullViews(cam, entries, rect, into = []) {
|
|
1602
1665
|
const parent = cam?.parent?.getWorldTransform?.()?.data ?? null;
|
|
1666
|
+
// A camera drawing remapped views (./inline3d-splat-rig-map.js) publishes them as _dxrViews.
|
|
1667
|
+
if (cam?._dxrViews?.length === entries.length) entries = cam._dxrViews;
|
|
1603
1668
|
into.length = entries.length;
|
|
1604
1669
|
for (let i = 0; i < entries.length; i++) {
|
|
1605
1670
|
const e = entries[i];
|
|
@@ -1613,7 +1678,8 @@ export class PlayCanvasSplatViewer {
|
|
|
1613
1678
|
o.W[0] = C[3]; o.W[1] = C[7]; o.W[2] = C[11]; o.W[3] = C[15];
|
|
1614
1679
|
o.K[0] = w * e.proj[0];
|
|
1615
1680
|
o.K[1] = 1 / w;
|
|
1616
|
-
|
|
1681
|
+
// The view's largest column scale² (1 for a rigid view; a remapped view is affine).
|
|
1682
|
+
o.K[2] = Math.max(V[0] * V[0] + V[1] * V[1] + V[2] * V[2], V[4] * V[4] + V[5] * V[5] + V[6] * V[6], V[8] * V[8] + V[9] * V[9] + V[10] * V[10]);
|
|
1617
1683
|
o.K[3] = 0;
|
|
1618
1684
|
}
|
|
1619
1685
|
return into;
|
|
@@ -1845,12 +1911,18 @@ export class PlayCanvasSplatViewer {
|
|
|
1845
1911
|
for (let i = 0; i < entries.length; i++) rvs.push(new pc.RenderView());
|
|
1846
1912
|
this.eye.camera.camera.xrViews = rvs.slice();
|
|
1847
1913
|
}
|
|
1914
|
+
// The current photo through ITS rig (the last declared): the views are remapped only while
|
|
1915
|
+
// they were located for another one (a declaration still in flight, ./inline3d-splat-rig-map.js).
|
|
1916
|
+
const rf = this._mapViews(entries, cache ? cache.rigAt : null);
|
|
1917
|
+
const eyeViews = rf.remapped ? rf.entries : null;
|
|
1848
1918
|
for (let i = 0; i < entries.length; i++) {
|
|
1849
1919
|
const e = entries[i];
|
|
1850
|
-
rvs[i].setView(
|
|
1920
|
+
if (eyeViews) rvs[i].setView(eyeViews[i].proj, eyeViews[i].viewInv, eyeViews[i].view);
|
|
1921
|
+
else rvs[i].setView(e.proj, e.pose);
|
|
1851
1922
|
const [x, y, w, h] = rect(e);
|
|
1852
1923
|
rvs[i].setViewport(x, y, w, h);
|
|
1853
1924
|
}
|
|
1925
|
+
this.eye._dxrViews = eyeViews ? rf.cull : null;
|
|
1854
1926
|
// LOD and FOV-compensation read camera.fov/near/far, which under xrViews come from the
|
|
1855
1927
|
// XR properties — the frustum the views actually have, as XrManager does it.
|
|
1856
1928
|
const f = frustumFromProjection(entries[0].proj);
|
|
@@ -1861,11 +1933,16 @@ export class PlayCanvasSplatViewer {
|
|
|
1861
1933
|
}
|
|
1862
1934
|
// The camera NODE drives the sort direction and LOD distance; the views ignore it (they
|
|
1863
1935
|
// compose the node's PARENT with their own pose). Park it on the first eye.
|
|
1864
|
-
placeNode(this.eye, entries[0].pose);
|
|
1865
|
-
// setSource's live outgoing: the same views on its own camera
|
|
1866
|
-
|
|
1936
|
+
placeNode(this.eye, eyeViews ? rf.node : entries[0].pose);
|
|
1937
|
+
// setSource's live outgoing: the same views on its own camera (through ITS photo's rig),
|
|
1938
|
+
// into its own target.
|
|
1939
|
+
if (this._live?.active || this._live?.warming) this._live.sync(entries, rect, f, rf);
|
|
1867
1940
|
// setSource's wavefront: this frame's eye views, as the engine is about to compose them.
|
|
1868
1941
|
this.onBeforeRender?.(entries, rect);
|
|
1942
|
+
// handle.setLayerRig: the display / post run cameras, on the same views (display: rounded).
|
|
1943
|
+
if (this.layerRigs && this.layerRigs.sync()) {
|
|
1944
|
+
this.layerRigs.frame(eyeViews ? rf.cull : entries, rect, f, eyeViews ? rf.eye.rig : rf.located ? rf.located.rig : cache ? cache.rig : null);
|
|
1945
|
+
}
|
|
1869
1946
|
} else {
|
|
1870
1947
|
// Fallback: one camera per view, `rect` + `calculateProjection`.
|
|
1871
1948
|
const cams = this._views;
|
|
@@ -1899,6 +1976,12 @@ export class PlayCanvasSplatViewer {
|
|
|
1899
1976
|
}
|
|
1900
1977
|
}
|
|
1901
1978
|
this._updateFeather(entries[0].width * sx, entries[0].height * sy);
|
|
1979
|
+
// handle.makeSbsMaterial: the eye split, scene-wide (a material only declares the uniform).
|
|
1980
|
+
const split = eyeSplit(entries, rect);
|
|
1981
|
+
if (split !== this._eyeSplit) {
|
|
1982
|
+
this._eyeSplit = split;
|
|
1983
|
+
app.graphicsDevice?.scope?.resolve?.(EYE_SPLIT_UNIFORM)?.setValue(split);
|
|
1984
|
+
}
|
|
1902
1985
|
// handle.setVideo's plane: size, eye split, and a new frame's upload (./inline3d-splat-video.js).
|
|
1903
1986
|
this._videoPlane?.beforeDraw(entries, rect);
|
|
1904
1987
|
app.tick(now());
|
|
@@ -1917,6 +2000,8 @@ export class PlayCanvasSplatViewer {
|
|
|
1917
2000
|
}
|
|
1918
2001
|
g.bufW = el.width || 0;
|
|
1919
2002
|
g.bufH = el.height || 0;
|
|
2003
|
+
g.rig = this._rigSnap ? { ...this._rigSnap } : null; // a replay re-uses the rig of its views
|
|
2004
|
+
g.rigAt = this._rigAtPull ?? null;
|
|
1920
2005
|
for (let i = 0; i < views.length; i++) {
|
|
1921
2006
|
const e = g.entries[i];
|
|
1922
2007
|
const vp = vps[i];
|
|
@@ -2259,6 +2344,23 @@ export class PlayCanvasSplatViewer {
|
|
|
2259
2344
|
}
|
|
2260
2345
|
}
|
|
2261
2346
|
|
|
2347
|
+
/**
|
|
2348
|
+
* A copy of the declared rig's frame fields (the SDK rewrites its camera-rig descriptor in place),
|
|
2349
|
+
* or null. Only what the layer rig reads: kind, pose, convergence, metres-to-virtual.
|
|
2350
|
+
*/
|
|
2351
|
+
export function snapshotRig(rig, into = null) {
|
|
2352
|
+
if (!rig || typeof rig !== 'object') return null;
|
|
2353
|
+
const o = into || { position: {}, orientation: {} };
|
|
2354
|
+
o.type = rig.type;
|
|
2355
|
+
const p = rig.position || {};
|
|
2356
|
+
const q = rig.orientation || {};
|
|
2357
|
+
o.position = { x: p.x || 0, y: p.y || 0, z: p.z || 0 };
|
|
2358
|
+
o.orientation = { x: q.x || 0, y: q.y || 0, z: q.z || 0, w: q.w === undefined ? 1 : q.w };
|
|
2359
|
+
o.convergenceDiopters = rig.convergenceDiopters;
|
|
2360
|
+
o.metersToVirtual = rig.metersToVirtual;
|
|
2361
|
+
return o;
|
|
2362
|
+
}
|
|
2363
|
+
|
|
2262
2364
|
/** Put an engine node at a rigid display-space pose (position + rotation; no scale). */
|
|
2263
2365
|
function placeNode(node, m) {
|
|
2264
2366
|
node.setLocalPosition(m[12], m[13], m[14]);
|
|
@@ -2831,11 +2933,31 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
2831
2933
|
let rigLockArmed = false;
|
|
2832
2934
|
if (diag) {
|
|
2833
2935
|
diag.imageState = () => viewer.diagImageState();
|
|
2936
|
+
diag.rigState = () => {
|
|
2937
|
+
const rf = viewer.rigFrame;
|
|
2938
|
+
if (!viewer.rigTrack || !rf) return null;
|
|
2939
|
+
return {
|
|
2940
|
+
at: rf.located ? rf.located.id : null,
|
|
2941
|
+
in: rf.eye ? `${rf.eye.id}${rf.remapped ? '+' : ''}` : null,
|
|
2942
|
+
out: viewer._live?.active ? viewer._live.path : null,
|
|
2943
|
+
};
|
|
2944
|
+
};
|
|
2834
2945
|
diag.observeLongTasks();
|
|
2835
2946
|
diag.observeLongFrames();
|
|
2836
2947
|
diag.log(`on — switches [${[...diag.switches].join(', ') || 'none'}]; dump: copy(__dxrDiag.dump())`);
|
|
2837
2948
|
}
|
|
2838
2949
|
let removed = false;
|
|
2950
|
+
// handle.setLayerRig: the per-rig cameras and where they read the declared rig from.
|
|
2951
|
+
// Kill switch `nolayerrig`: requests are recorded but every layer stays on the eye camera.
|
|
2952
|
+
let warnedLayerRigPath = false;
|
|
2953
|
+
function layerRigs() {
|
|
2954
|
+
if (!viewer.layerRigs) {
|
|
2955
|
+
viewer.layerRigs = new LayerRigCameras(viewer);
|
|
2956
|
+
viewer.layerRigs.disabled = !!diag?.has('nolayerrig');
|
|
2957
|
+
viewer.layerRigSource = () => out.viewRig || null;
|
|
2958
|
+
}
|
|
2959
|
+
return viewer.layerRigs;
|
|
2960
|
+
}
|
|
2839
2961
|
/** prepareSource: prepared handle → { loaded, state: 'ready' | 'used' | 'disposed', dispose }. */
|
|
2840
2962
|
const preparedAssets = new WeakMap();
|
|
2841
2963
|
const livePrepared = new Set();
|
|
@@ -2916,6 +3038,42 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
2916
3038
|
prepareSource,
|
|
2917
3039
|
setRig,
|
|
2918
3040
|
setVideo,
|
|
3041
|
+
/**
|
|
3042
|
+
* Draw a layer of this tile's engine through the DISPLAY rig (round, physical-depth stage
|
|
3043
|
+
* objects) while the splat and the declared view rig stay on the photo's camera rig — or back
|
|
3044
|
+
* on the camera rig ('camera'). No second full pass: one extra camera over the named layers,
|
|
3045
|
+
* into the same target, in composition order. docs/playcanvas-adapter.md §setLayerRig and
|
|
3046
|
+
* docs/proposals/layer-display-rig.md (the exact mapping, and why it is not Kooima).
|
|
3047
|
+
* `viewerDistance` (m) / `gain` set the rounding for the whole tile (last call wins).
|
|
3048
|
+
*/
|
|
3049
|
+
setLayerRig(layer, rig, o = {}) {
|
|
3050
|
+
const r = validateLayerRig(layer, rig, o);
|
|
3051
|
+
if (viewer._viewPath === 'cameras' && !warnedLayerRigPath) {
|
|
3052
|
+
warnedLayerRigPath = true;
|
|
3053
|
+
console.warn('[inline3d/splat] setLayerRig needs the engine RenderView path; on the N-camera fallback the layer stays on the photo rig.');
|
|
3054
|
+
}
|
|
3055
|
+
layerRigs().set(layer, r.rig, { viewerDistance: r.viewerDistance, gain: r.gain });
|
|
3056
|
+
return out;
|
|
3057
|
+
},
|
|
3058
|
+
/** The layers on the display rig (as the page named them), and what the last frame did. */
|
|
3059
|
+
layerRigState() {
|
|
3060
|
+
const lr = viewer.layerRigs;
|
|
3061
|
+
return {
|
|
3062
|
+
display: lr ? [...lr.requests.keys()] : [],
|
|
3063
|
+
disabled: !!lr?.disabled,
|
|
3064
|
+
rounded: !!lr?.last.rounded,
|
|
3065
|
+
gain: lr?.last.gain ?? null,
|
|
3066
|
+
};
|
|
3067
|
+
},
|
|
3068
|
+
/**
|
|
3069
|
+
* An unlit material showing the left half of `texture` to left-eye views and the right half to
|
|
3070
|
+
* right-eye views (format 'sbs' | 'tb' | 'mono'), on any mesh — mono / 2D: the left half. Needs
|
|
3071
|
+
* the engine (after `ready`). docs/playcanvas-adapter.md §makeSbsMaterial.
|
|
3072
|
+
*/
|
|
3073
|
+
makeSbsMaterial(texture, o = {}) {
|
|
3074
|
+
if (!viewer.app || !viewer.pc) throw new Error('@displayxr/inline3d/splat: makeSbsMaterial() needs the engine — call it after `await handle.ready`.');
|
|
3075
|
+
return makeSbsMaterial(viewer.pc, texture, o);
|
|
3076
|
+
},
|
|
2919
3077
|
/**
|
|
2920
3078
|
* Play a transition effect (inflate, deflate, sweep, dissolve, fade, pulse, custom) — see
|
|
2921
3079
|
* docs/splat-effects.md. Validated now; runs once the first asset is on screen. Resolves
|
|
@@ -3024,6 +3182,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3024
3182
|
// toward `target` at the viewer's FOCUS_EASE unless snapped.
|
|
3025
3183
|
const pageFocus = { d: Number.NaN, target: Number.NaN, fired: Number.NaN, source: null };
|
|
3026
3184
|
|
|
3185
|
+
let initialViewRig = null; // the rig addScene is created with (controls:'page'), else the shorthand
|
|
3027
3186
|
if (wall && wall.supported) {
|
|
3028
3187
|
const onFrame = diag
|
|
3029
3188
|
? (views, layer) => {
|
|
@@ -3036,7 +3195,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3036
3195
|
// controls:'page' starts on a camera rig (attach, provisional FOV/convergence until the page
|
|
3037
3196
|
// and the waterfall say otherwise); the display rig's height means nothing there.
|
|
3038
3197
|
...(pageMode
|
|
3039
|
-
? { viewRig: pageViewRig({ verticalFovDeg: viewer.page.fov, convergence: 2, comfortDepth: ctl.comfortDepth }) }
|
|
3198
|
+
? { viewRig: (initialViewRig = pageViewRig({ verticalFovDeg: viewer.page.fov, convergence: 2, comfortDepth: ctl.comfortDepth })) }
|
|
3040
3199
|
: { virtualDisplayHeight }),
|
|
3041
3200
|
onLayerLost: viewer.onLayerLost,
|
|
3042
3201
|
...(observe ? { observe } : {}),
|
|
@@ -3045,6 +3204,20 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3045
3204
|
} else {
|
|
3046
3205
|
viewer.startMono();
|
|
3047
3206
|
}
|
|
3207
|
+
// Every rig this tile declares, by VALUE (./inline3d-splat-rig-map.js): which one a frame's
|
|
3208
|
+
// views were located for is read off the views, so each photo can be drawn through its own rig
|
|
3209
|
+
// while a declaration is in flight or while a live outgoing photo is still on screen.
|
|
3210
|
+
// `?dxrdiag=oldrig` turns it off (views drawn as located; the live outgoing's node chain).
|
|
3211
|
+
if (handle && typeof handle.setViewRig === 'function' && !diag?.has('oldrig')) {
|
|
3212
|
+
const rt = (viewer.rigTrack = new RigTracker());
|
|
3213
|
+
rt.note(initialViewRig || { type: 'display', virtualDisplayHeight });
|
|
3214
|
+
const push = handle.setViewRig;
|
|
3215
|
+
handle.setViewRig = (rig) => {
|
|
3216
|
+
const r = push.call(handle, rig);
|
|
3217
|
+
rt.note(rig);
|
|
3218
|
+
return r;
|
|
3219
|
+
};
|
|
3220
|
+
}
|
|
3048
3221
|
let diagLoop = null;
|
|
3049
3222
|
if (diag) {
|
|
3050
3223
|
// Every rig push, logged with its values; under `norig`, every push after the first
|
|
@@ -3071,6 +3244,14 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3071
3244
|
delete out._resolveFirstWoven;
|
|
3072
3245
|
}
|
|
3073
3246
|
|
|
3247
|
+
// addSplat's `displayRigLayers` sugar: [layer, …] or { layers: [...], viewerDistance, gain }.
|
|
3248
|
+
if (opts.displayRigLayers !== undefined) {
|
|
3249
|
+
const d = opts.displayRigLayers;
|
|
3250
|
+
const list = Array.isArray(d) ? d : d && Array.isArray(d.layers) ? d.layers : null;
|
|
3251
|
+
if (!list) throw new TypeError('@displayxr/inline3d/splat: displayRigLayers — expected an array of layers, or { layers, viewerDistance?, gain? }.');
|
|
3252
|
+
const o = Array.isArray(d) ? {} : { ...(d.viewerDistance !== undefined ? { viewerDistance: d.viewerDistance } : {}), ...(d.gain !== undefined ? { gain: d.gain } : {}) };
|
|
3253
|
+
for (const l of list) out.setLayerRig(l, 'display', o);
|
|
3254
|
+
}
|
|
3074
3255
|
// Replay what the page did before this module arrived — exclude() above all, which a product
|
|
3075
3256
|
// page calls on the very next line after addSplat.
|
|
3076
3257
|
for (const [name, args] of pending) {
|