@displayxr/inline3d 1.19.0 → 1.19.2
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 +52 -0
- package/README.md +1 -0
- package/js/inline3d-model-playcanvas.js +33 -478
- package/js/inline3d-pc-look.js +497 -0
- package/js/inline3d-splat-effects.js +88 -5
- package/js/inline3d-splat-playcanvas.js +191 -48
- package/package.json +2 -1
|
@@ -693,6 +693,8 @@ export class PlayCanvasSplatViewer {
|
|
|
693
693
|
this._snap = null;
|
|
694
694
|
/** setSource's live outgoing asset (./inline3d-splat-live.js), made on first use. */
|
|
695
695
|
this._live = null;
|
|
696
|
+
/** (entries, rect) => void: once per drawn frame, after the views are set, before the engine renders. */
|
|
697
|
+
this.onBeforeRender = null;
|
|
696
698
|
this.vH = virtualDisplayHeight;
|
|
697
699
|
this.fit = fit;
|
|
698
700
|
this.margin = margin;
|
|
@@ -1447,6 +1449,34 @@ export class PlayCanvasSplatViewer {
|
|
|
1447
1449
|
return { rig: this.rigMatrix(), pose, c, t: Math.tan((this.mono.fov * DEG) / 2) };
|
|
1448
1450
|
}
|
|
1449
1451
|
|
|
1452
|
+
/**
|
|
1453
|
+
* One camera's eye views as the engine composes them this frame (RenderView.updateTransforms:
|
|
1454
|
+
* the camera node's PARENT world transform · each view's pose), packed for the wavefront's cull
|
|
1455
|
+
* (EFFECTS.wipecull): V = the view matrix, X / W = rows 0 and 3 of proj · V, K = (the engine's
|
|
1456
|
+
* focal f = viewport width · proj[0], 1 / viewport width, the view's squared scale). `into` is
|
|
1457
|
+
* reused frame to frame. Null when a matrix is singular.
|
|
1458
|
+
*/
|
|
1459
|
+
cullViews(cam, entries, rect, into = []) {
|
|
1460
|
+
const parent = cam?.parent?.getWorldTransform?.()?.data ?? null;
|
|
1461
|
+
into.length = entries.length;
|
|
1462
|
+
for (let i = 0; i < entries.length; i++) {
|
|
1463
|
+
const e = entries[i];
|
|
1464
|
+
const V = mat4Invert(parent ? mat4Mul(parent, e.pose) : e.pose);
|
|
1465
|
+
if (!V) return null;
|
|
1466
|
+
const C = mat4Mul(e.proj, V);
|
|
1467
|
+
const w = Math.max(1, rect(e)[2]);
|
|
1468
|
+
const o = (into[i] ||= { V: new Float32Array(16), X: new Float32Array(4), W: new Float32Array(4), K: new Float32Array(4) });
|
|
1469
|
+
o.V.set(V);
|
|
1470
|
+
o.X[0] = C[0]; o.X[1] = C[4]; o.X[2] = C[8]; o.X[3] = C[12];
|
|
1471
|
+
o.W[0] = C[3]; o.W[1] = C[7]; o.W[2] = C[11]; o.W[3] = C[15];
|
|
1472
|
+
o.K[0] = w * e.proj[0];
|
|
1473
|
+
o.K[1] = 1 / w;
|
|
1474
|
+
o.K[2] = V[0] * V[0] + V[1] * V[1] + V[2] * V[2];
|
|
1475
|
+
o.K[3] = 0;
|
|
1476
|
+
}
|
|
1477
|
+
return into;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1450
1480
|
/** Can this tile render a live outgoing asset (the single-camera RenderView path)? */
|
|
1451
1481
|
get canLiveOutgoing() {
|
|
1452
1482
|
return LiveOutgoing.supported(this);
|
|
@@ -1643,6 +1673,8 @@ export class PlayCanvasSplatViewer {
|
|
|
1643
1673
|
placeNode(this.eye, entries[0].pose);
|
|
1644
1674
|
// setSource's live outgoing: the same views on its own camera, into its own target.
|
|
1645
1675
|
if (this._live?.active) this._live.sync(entries, rect, f);
|
|
1676
|
+
// setSource's wavefront: this frame's eye views, as the engine is about to compose them.
|
|
1677
|
+
this.onBeforeRender?.(entries, rect);
|
|
1646
1678
|
} else {
|
|
1647
1679
|
// Fallback: one camera per view, `rect` + `calculateProjection`.
|
|
1648
1680
|
const cams = this._views;
|
|
@@ -3223,7 +3255,8 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3223
3255
|
let rigSeq = 0;
|
|
3224
3256
|
/** The IBL setRig('display') installed ({ kind, atlas, prev }), and what it replaced — or null. */
|
|
3225
3257
|
let rigEnv = null;
|
|
3226
|
-
/** ./inline3d-
|
|
3258
|
+
/** ./inline3d-pc-look.js once a display rig has loaded it (transmission, environments). Never the
|
|
3259
|
+
* model module: that one holds the optional meshoptimizer import, which ./splat must not reach. */
|
|
3227
3260
|
let rigModelModule = null;
|
|
3228
3261
|
/** Mesh instances / materials prepareTransmission has handled, and whether it turned the grab on. */
|
|
3229
3262
|
const rigTransmissionSeen = new WeakSet();
|
|
@@ -3322,7 +3355,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3322
3355
|
|
|
3323
3356
|
/** Install addModel's IBL (`disp.environment`) for the display rig, if the page has none of its own. */
|
|
3324
3357
|
async function ensureRigEnvironment(disp, seq) {
|
|
3325
|
-
const m = (rigModelModule ||= await import('./inline3d-
|
|
3358
|
+
const m = (rigModelModule ||= await import('./inline3d-pc-look.js'));
|
|
3326
3359
|
if (removed || seq !== rigSeq || rigOverride?.type !== 'display') return;
|
|
3327
3360
|
rigTick = 0; // transmission check on the next tick
|
|
3328
3361
|
const kind = disp.environment;
|
|
@@ -3679,7 +3712,7 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3679
3712
|
const loaded = await loadOne(pcModule, app, src, { background: true });
|
|
3680
3713
|
perfSpan('prepareSource', t0);
|
|
3681
3714
|
// The transition the page declared: compile its shader now, in the dwell, not on its first frame.
|
|
3682
|
-
|
|
3715
|
+
await prewarmTransition(warm); // null (no transition declared): the overlay only
|
|
3683
3716
|
const entry = { loaded, state: 'ready', dispose: null };
|
|
3684
3717
|
const prepared = {
|
|
3685
3718
|
[PREPARED_TAG]: true,
|
|
@@ -3720,46 +3753,61 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3720
3753
|
* engine whose internals differ just compiles on the transition's first frame, as before.
|
|
3721
3754
|
*/
|
|
3722
3755
|
async function prewarmTransition(plan) {
|
|
3723
|
-
|
|
3724
|
-
|
|
3756
|
+
// The chunk variant the transition installs (particles, wavefront), and the overlay's two quads
|
|
3757
|
+
// (crossfade, wavefront and the particles all composite through them; created on the first
|
|
3758
|
+
// capture otherwise, and compiled on its draw: 30–180 ms on the first frames of the window).
|
|
3759
|
+
const chunkKey = plan?.particles
|
|
3760
|
+
? `${plan.particles.in.effect}|${plan.particles.in.opts.order ?? ''}`
|
|
3761
|
+
: plan?.transition === 'wavefront'
|
|
3762
|
+
? 'wavefront'
|
|
3763
|
+
: null;
|
|
3764
|
+
const wantChunk = !!(fx && chunkKey && !prewarmed.has(chunkKey));
|
|
3765
|
+
const wantOverlay = !prewarmed.has('overlay');
|
|
3766
|
+
if (!wantChunk && !wantOverlay) return;
|
|
3725
3767
|
await yieldIdle();
|
|
3726
|
-
if (removed
|
|
3768
|
+
if (removed) return;
|
|
3727
3769
|
try {
|
|
3728
3770
|
const pc = pcModule;
|
|
3729
|
-
const cams = [viewer.eye, viewer._live?.cam].filter(Boolean);
|
|
3730
|
-
const code = fx.sharedChunkCode('transition', plan.particles.in.effect, { order: plan.particles.in.opts.order });
|
|
3731
3771
|
const made = [];
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
const
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
|
|
3754
|
-
|
|
3755
|
-
|
|
3772
|
+
if (wantChunk && fx) {
|
|
3773
|
+
const cams = [viewer.eye, viewer._live?.cam].filter(Boolean);
|
|
3774
|
+
const code = plan.particles
|
|
3775
|
+
? fx.sharedChunkCode('transition', plan.particles.in.effect, { order: plan.particles.in.opts.order })
|
|
3776
|
+
: fx.sharedChunkCodeFor([
|
|
3777
|
+
['transition', 'wavefront', {}],
|
|
3778
|
+
['transition-cull', 'wipecull', {}],
|
|
3779
|
+
]);
|
|
3780
|
+
let issued = 0;
|
|
3781
|
+
for (const cam of cams) {
|
|
3782
|
+
const mi = managerMi(cam, null);
|
|
3783
|
+
const src = mi?.material;
|
|
3784
|
+
const camera = cam.camera?.camera;
|
|
3785
|
+
if (!src?.shaderDesc || !camera?.shaderParams || typeof src.getShaderVariant !== 'function') continue;
|
|
3786
|
+
const m = new pc.ShaderMaterial(src.shaderDesc);
|
|
3787
|
+
src.defines.forEach((v, k) => m.setDefine(k, v));
|
|
3788
|
+
m.shaderChunks.copy(src.shaderChunks);
|
|
3789
|
+
m.getShaderChunks(pc.SHADERLANGUAGE_GLSL).set('gsplatModifyVS', code);
|
|
3790
|
+
m.blendState = src.blendState;
|
|
3791
|
+
made.push(m.getShaderVariant({
|
|
3792
|
+
device: viewer.app.graphicsDevice,
|
|
3793
|
+
scene: viewer.app.scene,
|
|
3794
|
+
objDefs: mi._shaderDefs,
|
|
3795
|
+
cameraShaderParams: camera.shaderParams,
|
|
3796
|
+
pass: 0, // SHADER_FORWARD
|
|
3797
|
+
sortedLights: [],
|
|
3798
|
+
viewUniformFormat: viewer.app.renderer?.viewUniformFormat ?? null,
|
|
3799
|
+
vertexFormat: mi.mesh?.vertexBuffer?.format,
|
|
3800
|
+
}));
|
|
3801
|
+
issued++;
|
|
3802
|
+
}
|
|
3803
|
+
if (issued) prewarmed.add(chunkKey);
|
|
3756
3804
|
}
|
|
3757
|
-
// the overlay's two quads (created on the first capture otherwise, and compiled on its draw)
|
|
3758
3805
|
const cam = viewer.eye?.camera?.camera;
|
|
3759
|
-
if (cam?.shaderParams) {
|
|
3806
|
+
if (wantOverlay && cam?.shaderParams) {
|
|
3760
3807
|
viewer._ensureSnapshotOverlay();
|
|
3808
|
+
let issued = 0;
|
|
3761
3809
|
for (const p of viewer._snap?.parts || []) {
|
|
3762
|
-
|
|
3810
|
+
const v = p.mat.getShaderVariant?.({
|
|
3763
3811
|
device: viewer.app.graphicsDevice,
|
|
3764
3812
|
scene: viewer.app.scene,
|
|
3765
3813
|
objDefs: p.mi._shaderDefs,
|
|
@@ -3768,16 +3816,18 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3768
3816
|
sortedLights: [],
|
|
3769
3817
|
viewUniformFormat: viewer.app.renderer?.viewUniformFormat ?? null,
|
|
3770
3818
|
vertexFormat: p.mi.mesh?.vertexBuffer?.format,
|
|
3771
|
-
})
|
|
3819
|
+
});
|
|
3820
|
+
if (v) (made.push(v), issued++);
|
|
3772
3821
|
}
|
|
3822
|
+
if (issued) prewarmed.add('overlay');
|
|
3773
3823
|
}
|
|
3774
|
-
if (issued) prewarmed.add(key);
|
|
3775
3824
|
await finalizeWhenLinked(made.filter(Boolean));
|
|
3776
3825
|
} catch (err) {
|
|
3777
3826
|
console.info('[inline3d/splat] transition shader pre-warm skipped', err);
|
|
3778
3827
|
}
|
|
3779
3828
|
}
|
|
3780
3829
|
|
|
3830
|
+
|
|
3781
3831
|
/**
|
|
3782
3832
|
* Creating a program only ISSUES its compile + link; the browser resolves the link when the
|
|
3783
3833
|
* program is first queried — the engine's first draw, blocking (the 35–50 ms GetProgramiv the
|
|
@@ -3844,6 +3894,9 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3844
3894
|
await first.catch(() => null); // a failed first asset may be replaced
|
|
3845
3895
|
if (!app || removed) return out;
|
|
3846
3896
|
const pc = pcModule;
|
|
3897
|
+
// Not prepared: compile what the transition draws with (its chunk, the overlay) while the asset
|
|
3898
|
+
// loads, rather than on the window's first frames. Best effort, not awaited.
|
|
3899
|
+
if (!prep && current && plan.transition !== 'cut') prewarmTransition(plan);
|
|
3847
3900
|
const loaded = prep ? prep.loaded : await loadOne(pc, app, next);
|
|
3848
3901
|
if (removed || gen !== sourceGen) {
|
|
3849
3902
|
app.assets.remove(loaded.asset);
|
|
@@ -3976,15 +4029,24 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
3976
4029
|
});
|
|
3977
4030
|
} else release(prev); // the snapshot shows it from here on
|
|
3978
4031
|
const band = plan.band;
|
|
4032
|
+
finishers.push(() => {
|
|
4033
|
+
viewer._transitionState = null;
|
|
4034
|
+
viewer.setSnapshotAlpha(0);
|
|
4035
|
+
viewer.releaseSnapshot();
|
|
4036
|
+
});
|
|
4037
|
+
if (fx && managerMi(viewer.eye, null)) {
|
|
4038
|
+
playWavefrontRender({ plan, entity, live, finishers, finish, isFinished: () => finished, pumpLive });
|
|
4039
|
+
await done;
|
|
4040
|
+
return out;
|
|
4041
|
+
}
|
|
4042
|
+
// FALLBACK (an engine whose managers are not reachable): the ridge as an entity-scope
|
|
4043
|
+
// modifier — correct, but it rewrites the new photo's work buffer and re-sorts it every frame.
|
|
4044
|
+
viewer._transitionPath = 'entity';
|
|
3979
4045
|
const ridge = fx.play(
|
|
3980
4046
|
'wavefront',
|
|
3981
4047
|
{ durationMs: plan.durationMs, easing: plan.easing, band, ridge: plan.ridge, ridgeMaxDisparity: plan.ridgeMaxDisparity, scope: 'entity' },
|
|
3982
4048
|
{ entity, gate: afterTicks(2), internal: true },
|
|
3983
4049
|
);
|
|
3984
|
-
finishers.push(() => {
|
|
3985
|
-
viewer.setSnapshotAlpha(0);
|
|
3986
|
-
viewer.releaseSnapshot();
|
|
3987
|
-
});
|
|
3988
4050
|
viewer._hooks.push(() => {
|
|
3989
4051
|
if (finished || removed) return false;
|
|
3990
4052
|
if (live && !live.fits) {
|
|
@@ -4063,6 +4125,94 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4063
4125
|
return out;
|
|
4064
4126
|
}
|
|
4065
4127
|
|
|
4128
|
+
/** The mesh instance of the gsplat manager that `cam` draws `layer` with (any layer: null). */
|
|
4129
|
+
const managerMi = (cam, layer) => {
|
|
4130
|
+
const cd = viewer.app?.renderer?.gsplatDirector?.camerasMap?.get?.(cam?.camera?.camera);
|
|
4131
|
+
if (!cd?.layersMap) return null;
|
|
4132
|
+
if (layer) return cd.layersMap.get(layer)?.gsplatManager?.renderer?.meshInstance ?? null;
|
|
4133
|
+
for (const ld of cd.layersMap.values()) if (ld?.gsplatManager?.renderer?.meshInstance) return ld.gsplatManager.renderer.meshInstance;
|
|
4134
|
+
return null;
|
|
4135
|
+
};
|
|
4136
|
+
|
|
4137
|
+
/**
|
|
4138
|
+
* setSource's wavefront, RENDER-TIME (the default): the ridge is a tile-scope body whose values
|
|
4139
|
+
* sit on the eye camera's manager (the incoming photo), as the particle transitions do — no
|
|
4140
|
+
* work-buffer rewrite and no re-sort per frame. Each photo is drawn only on its side of the
|
|
4141
|
+
* front (EFFECTS.wipecull, the last stage): the incoming one left of where its commit starts,
|
|
4142
|
+
* the outgoing one (on the live camera) right of where it ends, so the two share only the
|
|
4143
|
+
* `band`. The overlay still wipes the same two live images on the same lt; the frame is the
|
|
4144
|
+
* one the full draws give, bit for bit. The clock starts on the second tick (the incoming
|
|
4145
|
+
* asset's first frame builds its work buffer), as the crossfade's does.
|
|
4146
|
+
*/
|
|
4147
|
+
function playWavefrontRender({ plan, entity, live, finishers, finish, isFinished, pumpLive }) {
|
|
4148
|
+
const band = plan.band;
|
|
4149
|
+
const ease = typeof plan.easing === 'function' ? plan.easing : EASINGS[plan.easing] || EASINGS.linear;
|
|
4150
|
+
const eyeMi = () => managerMi(viewer.eye, null);
|
|
4151
|
+
const liveMi = () => (live?.active ? managerMi(live.cam, live.layer) : null);
|
|
4152
|
+
const ridgeFx = fx.driveShared('transition', 'wavefront', {});
|
|
4153
|
+
const cullFx = fx.driveShared('transition-cull', 'wipecull', {});
|
|
4154
|
+
const ridgeIn = ridgeFx.side(eyeMi, { band, ridge: plan.ridge, ridgeMaxDisparity: plan.ridgeMaxDisparity });
|
|
4155
|
+
const cull = { in: null, out: null };
|
|
4156
|
+
const cullIn = cullFx.side(eyeMi, { cull: () => cull.in });
|
|
4157
|
+
const cullOut = live ? cullFx.side(liveMi, { cull: () => cull.out }) : null;
|
|
4158
|
+
const views = { in: [], out: [] };
|
|
4159
|
+
let amount = 0;
|
|
4160
|
+
ridgeIn.set(0);
|
|
4161
|
+
viewer._transitionPath = 'render';
|
|
4162
|
+
// Where each photo still shows, in each eye's viewport-relative NDC (setSnapshotAlpha's wipe:
|
|
4163
|
+
// column u has committed lt = clamp((t − u·(1 − band)) / band, 0, 1) to the new photo).
|
|
4164
|
+
const clampEdge = (x) => Math.min(3, Math.max(-3, x));
|
|
4165
|
+
viewer.onBeforeRender = (entries, rect) => {
|
|
4166
|
+
if (isFinished()) return;
|
|
4167
|
+
const span = 1 - band;
|
|
4168
|
+
// band 1: the whole picture is the band — draw both in full. `_wipeCull = false`: the
|
|
4169
|
+
// diagnostics switch the exactness gate compares against (docs §Gates).
|
|
4170
|
+
if (!(span > 1e-6) || viewer._wipeCull === false) {
|
|
4171
|
+
cull.in = cull.out = null;
|
|
4172
|
+
cullIn.set(amount);
|
|
4173
|
+
cullOut?.set(amount);
|
|
4174
|
+
return;
|
|
4175
|
+
}
|
|
4176
|
+
const vin = viewer.cullViews(viewer.eye, entries, rect, views.in);
|
|
4177
|
+
cull.in = vin ? { side: -1, edge: clampEdge((2 * amount) / span - 1), views: vin } : null;
|
|
4178
|
+
cullIn.set(amount);
|
|
4179
|
+
if (cullOut) {
|
|
4180
|
+
const vout = viewer.cullViews(live.cam, entries, rect, views.out);
|
|
4181
|
+
cull.out = vout ? { side: 1, edge: clampEdge((2 * (amount - band)) / span - 1), views: vout } : null;
|
|
4182
|
+
cullOut.set(amount);
|
|
4183
|
+
}
|
|
4184
|
+
};
|
|
4185
|
+
finishers.push(() => {
|
|
4186
|
+
if (viewer.onBeforeRender) viewer.onBeforeRender = null;
|
|
4187
|
+
ridgeFx.remove();
|
|
4188
|
+
cullFx.remove();
|
|
4189
|
+
});
|
|
4190
|
+
let t0 = null;
|
|
4191
|
+
let ticks = 0;
|
|
4192
|
+
viewer._hooks.push((t) => {
|
|
4193
|
+
if (removed) return (finish(), false);
|
|
4194
|
+
if (isFinished()) return false;
|
|
4195
|
+
if (live && !live.fits) {
|
|
4196
|
+
finish(); // a resize or a 2D/3D switch mid-window: end now
|
|
4197
|
+
return false;
|
|
4198
|
+
}
|
|
4199
|
+
pumpLive();
|
|
4200
|
+
if (++ticks >= 2 && t0 === null) {
|
|
4201
|
+
t0 = t;
|
|
4202
|
+
ridgeIn.restart(); // the eyes' frame, now the new rig and first frames are in
|
|
4203
|
+
}
|
|
4204
|
+
const raw = t0 === null ? 0 : plan.durationMs > 0 ? Math.min(1, Math.max(0, (t - t0) / plan.durationMs)) : 1;
|
|
4205
|
+
amount = ease(raw);
|
|
4206
|
+
viewer._transitionState = { raw, live: !!live }; // diagnostics
|
|
4207
|
+
ridgeIn.set(amount);
|
|
4208
|
+
if (!viewer.setSnapshotAlpha(1, { t: amount, band, views: viewer.viewsAcross }) || raw >= 1) {
|
|
4209
|
+
finish();
|
|
4210
|
+
return false;
|
|
4211
|
+
}
|
|
4212
|
+
return true;
|
|
4213
|
+
});
|
|
4214
|
+
}
|
|
4215
|
+
|
|
4066
4216
|
/**
|
|
4067
4217
|
* setSource's particle transitions (swarm, burst, shimmer-cross, dust), once the frame snapshot
|
|
4068
4218
|
* is up. The OUTGOING asset plays its effect in reverse, on the live camera, keyed on its own
|
|
@@ -4082,13 +4232,6 @@ export function attachPlayCanvasSplat(out, wall, canvas, src, opts, pending = []
|
|
|
4082
4232
|
// (outgoing). No work-buffer rewrite and no re-sort per frame. An engine that does not expose
|
|
4083
4233
|
// the managers' mesh instances gets the entity-scope modifiers instead (a full work-buffer
|
|
4084
4234
|
// rewrite + a re-sort per asset per frame: correct, but it hitches on a 1.18M photo).
|
|
4085
|
-
const managerMi = (cam, layer) => {
|
|
4086
|
-
const cd = viewer.app?.renderer?.gsplatDirector?.camerasMap?.get?.(cam?.camera?.camera);
|
|
4087
|
-
if (!cd?.layersMap) return null;
|
|
4088
|
-
if (layer) return cd.layersMap.get(layer)?.gsplatManager?.renderer?.meshInstance ?? null;
|
|
4089
|
-
for (const ld of cd.layersMap.values()) if (ld?.gsplatManager?.renderer?.meshInstance) return ld.gsplatManager.renderer.meshInstance;
|
|
4090
|
-
return null;
|
|
4091
|
-
};
|
|
4092
4235
|
const eyeMi = () => managerMi(viewer.eye, null);
|
|
4093
4236
|
const liveMi = () => (live?.active ? managerMi(live.cam, live.layer) : null);
|
|
4094
4237
|
const shared = particle.out.effect === particle.in.effect && eyeMi() ? fx.driveShared('transition', particle.in.effect, { order: particle.in.opts.order }) : null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@displayxr/inline3d",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.2",
|
|
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",
|
|
@@ -56,6 +56,7 @@
|
|
|
56
56
|
"js/inline3d-model.js",
|
|
57
57
|
"js/inline3d-model-entry.js",
|
|
58
58
|
"js/inline3d-model-playcanvas.js",
|
|
59
|
+
"js/inline3d-pc-look.js",
|
|
59
60
|
"js/inline3d-playcanvas-engine.js",
|
|
60
61
|
"index.d.ts",
|
|
61
62
|
"three.d.ts",
|