@3driseai/3drise-viewer 0.1.32 → 0.1.34
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/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
- package/dist/animationsV2/ClipsRuntime.js +11 -1
- package/dist/animationsV2/animationLive.d.ts +7 -0
- package/dist/animationsV2/animationLive.d.ts.map +1 -0
- package/dist/animationsV2/animationLive.js +26 -0
- package/dist/animationsV2/appliers.d.ts +8 -0
- package/dist/animationsV2/appliers.d.ts.map +1 -1
- package/dist/animationsV2/appliers.js +61 -0
- package/dist/animationsV2/index.d.ts +1 -0
- package/dist/animationsV2/index.d.ts.map +1 -1
- package/dist/animationsV2/index.js +1 -0
- package/dist/components/AnimatedObject.d.ts.map +1 -1
- package/dist/components/AnimatedObject.js +2 -0
- package/dist/components/CameraController.d.ts.map +1 -1
- package/dist/components/CameraController.js +9 -0
- package/dist/components/CreatedObject.d.ts.map +1 -1
- package/dist/components/CreatedObject.js +4 -1
- package/dist/components/GroupMaterialContext.d.ts +25 -0
- package/dist/components/GroupMaterialContext.d.ts.map +1 -0
- package/dist/components/GroupMaterialContext.js +21 -0
- package/dist/components/ObjectNode.d.ts.map +1 -1
- package/dist/components/ObjectNode.js +13 -2
- package/dist/components/ObjectsRenderer.d.ts.map +1 -1
- package/dist/components/ObjectsRenderer.js +26 -19
- package/dist/components/SceneBuilder.d.ts.map +1 -1
- package/dist/components/SceneBuilder.js +7 -4
- package/dist/components/land/LandPatch.d.ts.map +1 -1
- package/dist/components/land/LandPatch.js +15 -1
- package/dist/hooks/useContinuousEffects.d.ts.map +1 -1
- package/dist/hooks/useContinuousEffects.js +24 -3
- package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
- package/dist/hooks/useMaterialApplication.js +2 -1
- package/dist/hooks/useMaterialUniforms.d.ts +2 -8
- package/dist/hooks/useMaterialUniforms.d.ts.map +1 -1
- package/dist/hooks/useMaterialUniforms.js +18 -6
- package/dist/hooks/useOrbitTargetFollow.d.ts.map +1 -1
- package/dist/hooks/useOrbitTargetFollow.js +6 -4
- package/dist/operations/bake.d.ts +36 -0
- package/dist/operations/bake.d.ts.map +1 -0
- package/dist/operations/bake.js +113 -0
- package/dist/operations/catalogue/physicalHeld.d.ts.map +1 -1
- package/dist/operations/catalogue/physicalHeld.js +6 -0
- package/dist/operations/catalogue/shaderLayers.js +2 -2
- package/dist/operations/catalogue/warmup.d.ts +23 -1
- package/dist/operations/catalogue/warmup.d.ts.map +1 -1
- package/dist/operations/catalogue/warmup.js +200 -12
- package/dist/operations/index.d.ts +3 -1
- package/dist/operations/index.d.ts.map +1 -1
- package/dist/operations/index.js +3 -1
- package/dist/operations/objectEffects.d.ts +15 -1
- package/dist/operations/objectEffects.d.ts.map +1 -1
- package/dist/operations/objectEffects.js +49 -1
- package/dist/operations/runtime.d.ts +17 -9
- package/dist/operations/runtime.d.ts.map +1 -1
- package/dist/operations/runtime.js +71 -18
- package/dist/operations/types.d.ts +5 -0
- package/dist/operations/types.d.ts.map +1 -1
- package/dist/triggers/readiness.d.ts +2 -0
- package/dist/triggers/readiness.d.ts.map +1 -1
- package/dist/triggers/readiness.js +28 -3
- package/dist/utils/stallLog.d.ts +4 -0
- package/dist/utils/stallLog.d.ts.map +1 -0
- package/dist/utils/stallLog.js +88 -0
- package/package.json +2 -2
|
@@ -17,32 +17,125 @@
|
|
|
17
17
|
// and reuses it, no compile (see shaderLayers.commitPendingLayers);
|
|
18
18
|
// • effect helpers (particles, threads, rings, beams, instances): their
|
|
19
19
|
// materials start hidden (material.visible = false; three still compiles
|
|
20
|
-
// hidden materials) and are shown once their program is ready
|
|
20
|
+
// hidden materials) and are shown once their program is ready — together with
|
|
21
|
+
// their shadow-pass program when they cast shadows (compileAsync never
|
|
22
|
+
// compiles the shadow pass).
|
|
21
23
|
// Without a renderer / camera (SSR, tests) everything happens immediately.
|
|
22
24
|
//
|
|
25
|
+
// Every warm compile runs AS THE FRAME DRAWS THE SCENE (asInFrame): three bakes tone
|
|
26
|
+
// mapping and the output colour space into a program by the render target bound when
|
|
27
|
+
// it compiles — the screen (null) gets the renderer's tone mapping and sRGB, a render
|
|
28
|
+
// target gets neither. Volumetric clouds and post-processing draw the scene into a
|
|
29
|
+
// render target, so a warm-up compiled against the screen built the wrong program and
|
|
30
|
+
// the first real frame compiled again, in the frame (every effect froze). The target
|
|
31
|
+
// the scene is really drawn into is watched (scene.onBeforeRender) and bound for the
|
|
32
|
+
// compile. Measured in claude/prebake-audit.md.
|
|
33
|
+
//
|
|
23
34
|
import { SceneStore } from '@3driseai/3drise-core';
|
|
24
|
-
import { InstancedMesh, LineSegments, Mesh, Points } from 'three';
|
|
35
|
+
import { Color, InstancedMesh, LineSegments, Mesh, MeshDepthMaterial, MeshDistanceMaterial, Points, RGBADepthPacking, VSMShadowMap, WebGLRenderTarget, } from 'three';
|
|
25
36
|
import cameraManager from '../../utils/CameraSingleton.js';
|
|
26
37
|
export function warmContext() {
|
|
27
38
|
const gl = SceneStore.gl, scene = SceneStore.scene, cam = cameraManager.getCamera?.();
|
|
39
|
+
if (scene)
|
|
40
|
+
watchFrameTarget(scene);
|
|
28
41
|
return gl && scene && cam && typeof gl.compileAsync === 'function' ? { gl, scene, cam } : null;
|
|
29
42
|
}
|
|
43
|
+
// ---------------------------------------------------------------- the frame's render target
|
|
44
|
+
const FRAME_TARGET = '__fxFrameTarget';
|
|
45
|
+
const UNSEEN = 'unseen';
|
|
46
|
+
/** Note, every frame, the render target the scene is drawn into. Installed once per scene. */
|
|
47
|
+
function watchFrameTarget(scene) {
|
|
48
|
+
if (scene.userData[FRAME_TARGET] !== undefined)
|
|
49
|
+
return;
|
|
50
|
+
scene.userData[FRAME_TARGET] = UNSEEN;
|
|
51
|
+
const own = scene.onBeforeRender;
|
|
52
|
+
scene.onBeforeRender = function (renderer, sc, camera, target) {
|
|
53
|
+
scene.userData[FRAME_TARGET] = target ?? null;
|
|
54
|
+
return own?.call(this, renderer, sc, camera, target);
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/** Resolves once the scene has been drawn since the watch started (its target is known), or after a short wait. */
|
|
58
|
+
function frameSeen(ctx) {
|
|
59
|
+
if (ctx.scene.userData[FRAME_TARGET] !== UNSEEN || typeof requestAnimationFrame !== 'function')
|
|
60
|
+
return Promise.resolve();
|
|
61
|
+
return new Promise(resolve => {
|
|
62
|
+
const t0 = performance.now();
|
|
63
|
+
const check = () => (ctx.scene.userData[FRAME_TARGET] !== UNSEEN || performance.now() - t0 > 500 ? resolve() : requestAnimationFrame(check));
|
|
64
|
+
requestAnimationFrame(check);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/** Run a compile with the render target the scene is drawn into bound (see the header). */
|
|
68
|
+
export function asInFrame(ctx, fn) {
|
|
69
|
+
const seen = ctx.scene.userData?.[FRAME_TARGET];
|
|
70
|
+
const target = seen === UNSEEN || seen === undefined ? null : seen;
|
|
71
|
+
const prev = ctx.gl.getRenderTarget();
|
|
72
|
+
if (target === prev)
|
|
73
|
+
return fn();
|
|
74
|
+
ctx.gl.setRenderTarget(target);
|
|
75
|
+
try {
|
|
76
|
+
return fn();
|
|
77
|
+
}
|
|
78
|
+
finally {
|
|
79
|
+
ctx.gl.setRenderTarget(prev);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
30
82
|
/** compileAsync with a safety timeout (a driver that never reports ready must not hang an effect). */
|
|
31
|
-
export function compileInBackground(root, timeoutMs = 8000) {
|
|
32
|
-
const ctx = warmContext();
|
|
83
|
+
export function compileInBackground(root, timeoutMs = 8000, given) {
|
|
84
|
+
const ctx = given ?? warmContext();
|
|
33
85
|
if (!ctx)
|
|
34
86
|
return Promise.resolve();
|
|
35
87
|
let timer;
|
|
36
|
-
return Promise.race([
|
|
37
|
-
Promise.resolve(ctx.gl.compileAsync(root, ctx.cam, ctx.scene)).then(() => undefined, () => undefined),
|
|
88
|
+
return frameSeen(ctx).then(() => Promise.race([
|
|
89
|
+
Promise.resolve(asInFrame(ctx, () => ctx.gl.compileAsync(root, ctx.cam, ctx.scene))).then(() => undefined, () => undefined),
|
|
38
90
|
new Promise(r => { timer = setTimeout(r, timeoutMs); }),
|
|
39
|
-
]).finally(() => clearTimeout(timer));
|
|
91
|
+
])).finally(() => clearTimeout(timer));
|
|
92
|
+
}
|
|
93
|
+
/** The whole scene, as the frame draws it (the published scene's load: see triggers/readiness). */
|
|
94
|
+
export function compileScene(three, timeoutMs = 8000) {
|
|
95
|
+
let ctx = warmContext();
|
|
96
|
+
if (!ctx && three?.gl?.compileAsync && three.scene && three.camera) {
|
|
97
|
+
watchFrameTarget(three.scene);
|
|
98
|
+
ctx = { gl: three.gl, scene: three.scene, cam: three.camera };
|
|
99
|
+
}
|
|
100
|
+
return ctx ? compileInBackground(ctx.scene, timeoutMs, ctx) : Promise.resolve();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Upload every texture of `root` to the GPU now. three uploads a texture the first time
|
|
104
|
+
* something using it is drawn — in that frame: an object hidden at load and shown later
|
|
105
|
+
* (a cue, an action, visibility) would stall the frame it appears in.
|
|
106
|
+
*/
|
|
107
|
+
export function uploadTextures(root) {
|
|
108
|
+
const ctx = warmContext();
|
|
109
|
+
if (!ctx || typeof ctx.gl.initTexture !== 'function')
|
|
110
|
+
return 0;
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
(root ?? ctx.scene).traverse((c) => {
|
|
113
|
+
for (const m of materialsOf1(c)) {
|
|
114
|
+
for (const v of Object.values(m))
|
|
115
|
+
if (v?.isTexture && !v.isRenderTargetTexture)
|
|
116
|
+
seen.add(v);
|
|
117
|
+
const u = m.uniforms;
|
|
118
|
+
if (u)
|
|
119
|
+
for (const x of Object.values(u)) {
|
|
120
|
+
const t = x?.value;
|
|
121
|
+
if (t?.isTexture && !t.isRenderTargetTexture)
|
|
122
|
+
seen.add(t);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
for (const t of seen) {
|
|
127
|
+
try {
|
|
128
|
+
ctx.gl.initTexture(t);
|
|
129
|
+
}
|
|
130
|
+
catch { /* not uploadable yet (image still loading) */ }
|
|
131
|
+
}
|
|
132
|
+
return seen.size;
|
|
40
133
|
}
|
|
41
134
|
const FLAG = '__opFxWarm';
|
|
135
|
+
const materialsOf1 = (c) => (c.material ? (Array.isArray(c.material) ? c.material : [c.material]) : []);
|
|
42
136
|
const materialsOf = (obj) => {
|
|
43
137
|
const out = [];
|
|
44
|
-
obj.traverse((c) => {
|
|
45
|
-
out.push(...(Array.isArray(c.material) ? c.material : [c.material])); });
|
|
138
|
+
obj.traverse((c) => { out.push(...materialsOf1(c)); });
|
|
46
139
|
return out;
|
|
47
140
|
};
|
|
48
141
|
/** Keep a freshly created helper off screen until its shaders are compiled (see warmHelpers). */
|
|
@@ -58,13 +151,105 @@ export function warmHelpers(root) {
|
|
|
58
151
|
const waiting = [];
|
|
59
152
|
root.traverse((c) => { if (c.userData?.[FLAG])
|
|
60
153
|
waiting.push(c); });
|
|
61
|
-
return Promise.all(waiting.map(h => compileInBackground(h).then(() => {
|
|
154
|
+
return Promise.all(waiting.map(h => Promise.all([compileInBackground(h), warmShadowPass(h)]).then(() => {
|
|
62
155
|
for (const m of materialsOf(h))
|
|
63
156
|
m.visible = true;
|
|
64
157
|
delete h.userData[FLAG];
|
|
65
158
|
retainPrograms(h);
|
|
66
159
|
}))).then(() => undefined);
|
|
67
160
|
}
|
|
161
|
+
// ---------------------------------------------------------------- the shadow pass
|
|
162
|
+
let shadowTarget = null;
|
|
163
|
+
// three's shadow pass draws a caster with ITS depth material turned to the caster's shadow side
|
|
164
|
+
// (front faces → back, back → front, double stays) — the side is part of the program
|
|
165
|
+
const SHADOW_SIDE = { 0: 1, 1: 0, 2: 2 };
|
|
166
|
+
const depthMats = new Map();
|
|
167
|
+
function depthFor(kind, side) {
|
|
168
|
+
const key = `${kind}|${side}`;
|
|
169
|
+
let m = depthMats.get(key);
|
|
170
|
+
if (!m) {
|
|
171
|
+
m = kind === 'depth' ? new MeshDepthMaterial({ depthPacking: RGBADepthPacking }) : new MeshDistanceMaterial();
|
|
172
|
+
m.side = side;
|
|
173
|
+
depthMats.set(key, m);
|
|
174
|
+
}
|
|
175
|
+
return m;
|
|
176
|
+
}
|
|
177
|
+
const shadowKept = [];
|
|
178
|
+
const shadowKeys = new Set();
|
|
179
|
+
/**
|
|
180
|
+
* The shadow pass draws a shadow caster with three's own depth material — a program
|
|
181
|
+
* compileAsync never builds, so a new kind of caster (an instanced helper) compiled it
|
|
182
|
+
* in the first frame that drew its shadow. Build it here: a stand-in of the same kind
|
|
183
|
+
* with an equivalent depth material, compiled into a render target as the shadow map is.
|
|
184
|
+
* The stand-ins are kept (never drawn) so the programs stay cached.
|
|
185
|
+
*/
|
|
186
|
+
export function warmShadowPass(root) {
|
|
187
|
+
try {
|
|
188
|
+
return shadowPass(root);
|
|
189
|
+
}
|
|
190
|
+
catch (e) {
|
|
191
|
+
console.warn('[3drise] shadow warm-up failed', e);
|
|
192
|
+
return Promise.resolve();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
function shadowPass(root) {
|
|
196
|
+
const ctx = warmContext();
|
|
197
|
+
if (!ctx || !ctx.gl.shadowMap?.enabled || ctx.gl.shadowMap.type === VSMShadowMap)
|
|
198
|
+
return Promise.resolve();
|
|
199
|
+
let dirOrSpot = false, point = false;
|
|
200
|
+
ctx.scene.traverseVisible((o) => {
|
|
201
|
+
if (!o.isLight || !o.castShadow)
|
|
202
|
+
return;
|
|
203
|
+
if (o.isPointLight)
|
|
204
|
+
point = true;
|
|
205
|
+
else
|
|
206
|
+
dirOrSpot = true;
|
|
207
|
+
});
|
|
208
|
+
if (!dirOrSpot && !point)
|
|
209
|
+
return Promise.resolve();
|
|
210
|
+
const stands = [];
|
|
211
|
+
root.traverse((c) => {
|
|
212
|
+
if (!c.castShadow || !(c.isMesh || c.isPoints || c.isLine))
|
|
213
|
+
return;
|
|
214
|
+
const own = materialsOf1(c)[0];
|
|
215
|
+
const side = own?.shadowSide ?? SHADOW_SIDE[own?.side ?? 0] ?? 1;
|
|
216
|
+
for (const [use, kind] of [[dirOrSpot, 'depth'], [point, 'distance']]) {
|
|
217
|
+
if (!use)
|
|
218
|
+
continue;
|
|
219
|
+
const mat = depthFor(kind, side);
|
|
220
|
+
const key = `${kind}|${side}|${c.isInstancedMesh ? `inst${c.instanceColor ? 'C' : ''}` : c.isSkinnedMesh ? 'skin' : c.type}|${Object.keys(c.geometry?.morphAttributes ?? {}).join(',')}`;
|
|
221
|
+
if (shadowKeys.has(key))
|
|
222
|
+
continue;
|
|
223
|
+
shadowKeys.add(key);
|
|
224
|
+
let s;
|
|
225
|
+
if (c.isInstancedMesh) {
|
|
226
|
+
s = new InstancedMesh(c.geometry, mat, 1);
|
|
227
|
+
if (c.instanceColor)
|
|
228
|
+
s.setColorAt(0, new Color(1, 1, 1));
|
|
229
|
+
}
|
|
230
|
+
else if (c.isMesh)
|
|
231
|
+
s = new Mesh(c.geometry, mat);
|
|
232
|
+
else
|
|
233
|
+
continue;
|
|
234
|
+
s.frustumCulled = false;
|
|
235
|
+
stands.push(s);
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (!stands.length)
|
|
239
|
+
return Promise.resolve();
|
|
240
|
+
shadowTarget ??= new WebGLRenderTarget(1, 1);
|
|
241
|
+
const prev = ctx.gl.getRenderTarget();
|
|
242
|
+
let res;
|
|
243
|
+
ctx.gl.setRenderTarget(shadowTarget);
|
|
244
|
+
try {
|
|
245
|
+
res = Promise.all(stands.map(s => Promise.resolve(ctx.gl.compileAsync(s, ctx.cam, ctx.scene)).catch(() => undefined)));
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
ctx.gl.setRenderTarget(prev);
|
|
249
|
+
}
|
|
250
|
+
shadowKept.push(...stands);
|
|
251
|
+
return Promise.race([res, new Promise(r => setTimeout(r, 8000))]).then(() => undefined);
|
|
252
|
+
}
|
|
68
253
|
/**
|
|
69
254
|
* Warm-only runs (pre-warming): while this is on, an effect's runProgress compiles
|
|
70
255
|
* everything it needs and then stops — nothing animates. See runtime.prewarmEffect.
|
|
@@ -84,7 +269,7 @@ export function retainPrograms(root) {
|
|
|
84
269
|
root.traverse((c) => {
|
|
85
270
|
const mats = c.material ? (Array.isArray(c.material) ? c.material : [c.material]) : [];
|
|
86
271
|
for (const m of mats) {
|
|
87
|
-
const key = `${c.type}|${m.type}|${m.vertexShader?.length ?? ''}|${m.fragmentShader?.length ?? ''}|${m.customProgramCacheKey?.() ?? ''}`;
|
|
272
|
+
const key = `${c.type}${c.instanceColor ? '+color' : ''}|${m.type}|${m.vertexShader?.length ?? ''}|${m.fragmentShader?.length ?? ''}|${m.customProgramCacheKey?.() ?? ''}`;
|
|
88
273
|
if (retainedKeys.has(key))
|
|
89
274
|
continue;
|
|
90
275
|
retainedKeys.add(key);
|
|
@@ -96,8 +281,11 @@ export function retainPrograms(root) {
|
|
|
96
281
|
: c.isLineSegments ? new LineSegments(c.geometry, keep) : c.isMesh ? new Mesh(c.geometry, keep) : null;
|
|
97
282
|
if (!stand)
|
|
98
283
|
continue;
|
|
284
|
+
// instance colours are part of the program: without them the kept program is another one
|
|
285
|
+
if (c.isInstancedMesh && c.instanceColor)
|
|
286
|
+
stand.setColorAt(0, new Color(1, 1, 1));
|
|
99
287
|
try {
|
|
100
|
-
ctx.gl.compile(stand, ctx.cam, ctx.scene);
|
|
288
|
+
asInFrame(ctx, () => ctx.gl.compile(stand, ctx.cam, ctx.scene));
|
|
101
289
|
retained.push(keep);
|
|
102
290
|
}
|
|
103
291
|
catch { /* ignore */ }
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { OPERATIONS, getOperation, operationsFor, resolveParams, channelOf } from './registry';
|
|
3
3
|
export { OPERATION_GROUPS, groupOperations, type OperationGroup } from './groups';
|
|
4
|
+
export { bakeProject, collectBakeJobs, setBakeSource, type BakeSource, type BakeReport } from './bake';
|
|
5
|
+
export { installStallLog, stallLogEnabled } from '../utils/stallLog';
|
|
4
6
|
export { runEffect, runEffectClip, setEffectClipTemplates, getEffectClipTemplate, effectClipFromAsset, EFFECT_CLIP_ASSET, applyOperation, revertOperation, runOf, isOperationOn, resolveEffectTargets, runActionEffects, previewEffect, previewEffects, isPreviewing, prewarmEffect, areActionEffectsOn, subscribeActionEffects, registerOperationTools, } from './runtime';
|
|
5
7
|
export { useOperationsRuntime } from './useOperationsRuntime';
|
|
6
|
-
export { ObjectEffectsController, runningObjectEffects, objectEffectParams, isObjectOperation, surfaceSignature, playObjectEffect, playbackOf, clipFollowsVisibility, setObjectEffectsVisible, currentEffectStep } from './objectEffects';
|
|
8
|
+
export { ObjectEffectsController, runningObjectEffects, objectEffectParams, isObjectOperation, surfaceSignature, playObjectEffect, setObjectEffectParam, playbackOf, clipFollowsVisibility, setObjectEffectsVisible, currentEffectStep } from './objectEffects';
|
|
7
9
|
export { useObjectEffectLayers } from './useObjectEffectLayers';
|
|
8
10
|
export { usePresentedVisibility } from './usePresentedVisibility';
|
|
9
11
|
export { captureFrameImage } from './clipPreview';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/operations/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EACH,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAC5L,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,sBAAsB,GACnJ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/operations/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAC/F,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,UAAU,EAAE,MAAM,QAAQ,CAAC;AACvG,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EACH,SAAS,EAAE,aAAa,EAAE,sBAAsB,EAAE,qBAAqB,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,cAAc,EAAE,eAAe,EAAE,KAAK,EAAE,aAAa,EAAE,oBAAoB,EAC5L,gBAAgB,EAAE,aAAa,EAAE,cAAc,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,sBAAsB,GACnJ,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,UAAU,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAChQ,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/operations/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export { OPERATIONS, getOperation, operationsFor, resolveParams, channelOf } from './registry.js';
|
|
3
3
|
export { OPERATION_GROUPS, groupOperations } from './groups.js';
|
|
4
|
+
export { bakeProject, collectBakeJobs, setBakeSource } from './bake.js';
|
|
5
|
+
export { installStallLog, stallLogEnabled } from '../utils/stallLog.js';
|
|
4
6
|
export { runEffect, runEffectClip, setEffectClipTemplates, getEffectClipTemplate, effectClipFromAsset, EFFECT_CLIP_ASSET, applyOperation, revertOperation, runOf, isOperationOn, resolveEffectTargets, runActionEffects, previewEffect, previewEffects, isPreviewing, prewarmEffect, areActionEffectsOn, subscribeActionEffects, registerOperationTools, } from './runtime.js';
|
|
5
7
|
export { useOperationsRuntime } from './useOperationsRuntime.js';
|
|
6
|
-
export { ObjectEffectsController, runningObjectEffects, objectEffectParams, isObjectOperation, surfaceSignature, playObjectEffect, playbackOf, clipFollowsVisibility, setObjectEffectsVisible, currentEffectStep } from './objectEffects.js';
|
|
8
|
+
export { ObjectEffectsController, runningObjectEffects, objectEffectParams, isObjectOperation, surfaceSignature, playObjectEffect, setObjectEffectParam, playbackOf, clipFollowsVisibility, setObjectEffectsVisible, currentEffectStep } from './objectEffects.js';
|
|
7
9
|
export { useObjectEffectLayers } from './useObjectEffectLayers.js';
|
|
8
10
|
export { usePresentedVisibility } from './usePresentedVisibility.js';
|
|
9
11
|
export { captureFrameImage } from './clipPreview.js';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { Object3D } from 'three';
|
|
2
|
-
import type { EffectPlayback, EffectSpread, EffectStep, EffectSteps, ObjectEffect } from '@3driseai/3drise-core';
|
|
2
|
+
import type { EffectPlayback, EffectSpread, EffectStep, EffectSteps, ObjectEffect, OperationParamValue } from '@3driseai/3drise-core';
|
|
3
3
|
import type { OperationDefinition, OperationParams } from './types';
|
|
4
4
|
/** An operation can be an object effect (one-shots too: they have no way back, and play with loop). */
|
|
5
5
|
export declare const isObjectOperation: (def: OperationDefinition | undefined) => def is OperationDefinition;
|
|
@@ -17,6 +17,12 @@ export declare const objectEffectParams: (def: OperationDefinition, effect: Pick
|
|
|
17
17
|
export declare const playbackOf: (effect: Pick<ObjectEffect, "playback">) => Required<EffectPlayback>;
|
|
18
18
|
/** An object's effects controller; `create` makes one for an object that has no effects of its own yet. */
|
|
19
19
|
export declare function objectEffectsControllerFor(root: Object3D, create?: boolean): ObjectEffectsController | undefined;
|
|
20
|
+
/**
|
|
21
|
+
* A timeline track on one of an object's effects' params (`effects.<effectId>.<param>`): set it on
|
|
22
|
+
* the running effect (undefined = back to the effect's own). False when the object has no controller
|
|
23
|
+
* or no such effect yet (a model still loading).
|
|
24
|
+
*/
|
|
25
|
+
export declare function setObjectEffectParam(root: Object3D | null | undefined, effectId: string, key: string, value: OperationParamValue | undefined): boolean;
|
|
20
26
|
/** The step of an object's effect steps that is on now (-1: none) — for the editor. */
|
|
21
27
|
export declare function currentEffectStep(root: Object3D | null | undefined): number;
|
|
22
28
|
/** An object's own effect clip follows its visibility (`visible`) and has an effect that is on. */
|
|
@@ -76,6 +82,8 @@ export declare class ObjectEffectsController {
|
|
|
76
82
|
private spread;
|
|
77
83
|
/** Added effects that follow visibility: a group's effects on this child, when the group's follow it. */
|
|
78
84
|
private addedFollows;
|
|
85
|
+
/** Timeline-driven params (effect id → param → value), laid over the row's own while a clip drives them. */
|
|
86
|
+
private liveParams;
|
|
79
87
|
constructor(root: Object3D);
|
|
80
88
|
/** The object's targets for an effect: the object, or its named nodes. */
|
|
81
89
|
private targetsOf;
|
|
@@ -176,6 +184,12 @@ export declare class ObjectEffectsController {
|
|
|
176
184
|
/** Loop / yoyo / reverse, sequenced from apply (forward) and revert (backward). Reads r.params each pass. */
|
|
177
185
|
private cycle;
|
|
178
186
|
/** Play an effect out and leave it waiting (it stays in the list, ready to play again). */
|
|
187
|
+
/**
|
|
188
|
+
* A timeline track drives one of the effect's params: lay it over the row's own (undefined puts
|
|
189
|
+
* the row's own back). A running motion takes it at once (its `live` hook); anything else picks
|
|
190
|
+
* it up on its next pass — nothing replays.
|
|
191
|
+
*/
|
|
192
|
+
setLiveParam(effectId: string, key: string, value: OperationParamValue | undefined): boolean;
|
|
179
193
|
stop(effectId: string): boolean;
|
|
180
194
|
/** Play an effect from its start (see playObjectEffect). */
|
|
181
195
|
play(effectId: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"objectEffects.d.ts","sourceRoot":"","sources":["../../src/operations/objectEffects.ts"],"names":[],"mappings":"AAqGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAiB,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"objectEffects.d.ts","sourceRoot":"","sources":["../../src/operations/objectEffects.ts"],"names":[],"mappings":"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"}
|
|
@@ -75,6 +75,14 @@ registerAddedEffects({
|
|
|
75
75
|
removeClip: (root, key) => !!controllers.get(root)?.removeClip(key),
|
|
76
76
|
hasClip: (root, key) => !!controllers.get(root)?.hasClip(key),
|
|
77
77
|
});
|
|
78
|
+
/**
|
|
79
|
+
* A timeline track on one of an object's effects' params (`effects.<effectId>.<param>`): set it on
|
|
80
|
+
* the running effect (undefined = back to the effect's own). False when the object has no controller
|
|
81
|
+
* or no such effect yet (a model still loading).
|
|
82
|
+
*/
|
|
83
|
+
export function setObjectEffectParam(root, effectId, key, value) {
|
|
84
|
+
return !!root && !!controllers.get(root)?.setLiveParam(effectId, key, value);
|
|
85
|
+
}
|
|
78
86
|
/** The step of an object's effect steps that is on now (-1: none) — for the editor. */
|
|
79
87
|
export function currentEffectStep(root) {
|
|
80
88
|
return (root && controllers.get(root)?.step) ?? -1;
|
|
@@ -150,6 +158,8 @@ export class ObjectEffectsController {
|
|
|
150
158
|
spread = {};
|
|
151
159
|
/** Added effects that follow visibility: a group's effects on this child, when the group's follow it. */
|
|
152
160
|
addedFollows = new Set();
|
|
161
|
+
/** Timeline-driven params (effect id → param → value), laid over the row's own while a clip drives them. */
|
|
162
|
+
liveParams = new Map();
|
|
153
163
|
constructor(root) {
|
|
154
164
|
this.root = root;
|
|
155
165
|
controllers.set(root, this);
|
|
@@ -562,7 +572,7 @@ export class ObjectEffectsController {
|
|
|
562
572
|
for (const e of next) {
|
|
563
573
|
const def = getOperation(e.operation);
|
|
564
574
|
const [follows, inStep] = this.visOf(e.id);
|
|
565
|
-
const params = objectEffectParams(def, e, follows, inStep);
|
|
575
|
+
const params = { ...objectEffectParams(def, e, follows, inStep), ...(this.liveParams.get(e.id) ?? {}) };
|
|
566
576
|
const paramsKey = JSON.stringify(params);
|
|
567
577
|
const r = this.running.get(e.id);
|
|
568
578
|
if (r && r.paramsKey === paramsKey)
|
|
@@ -1104,6 +1114,44 @@ export class ObjectEffectsController {
|
|
|
1104
1114
|
r.cycle = undefined;
|
|
1105
1115
|
}
|
|
1106
1116
|
/** Play an effect out and leave it waiting (it stays in the list, ready to play again). */
|
|
1117
|
+
/**
|
|
1118
|
+
* A timeline track drives one of the effect's params: lay it over the row's own (undefined puts
|
|
1119
|
+
* the row's own back). A running motion takes it at once (its `live` hook); anything else picks
|
|
1120
|
+
* it up on its next pass — nothing replays.
|
|
1121
|
+
*/
|
|
1122
|
+
setLiveParam(effectId, key, value) {
|
|
1123
|
+
if (this.disposed)
|
|
1124
|
+
return false;
|
|
1125
|
+
const cur = { ...(this.liveParams.get(effectId) ?? {}) };
|
|
1126
|
+
if (value === undefined)
|
|
1127
|
+
delete cur[key];
|
|
1128
|
+
else
|
|
1129
|
+
cur[key] = value;
|
|
1130
|
+
if (Object.keys(cur).length)
|
|
1131
|
+
this.liveParams.set(effectId, cur);
|
|
1132
|
+
else
|
|
1133
|
+
this.liveParams.delete(effectId);
|
|
1134
|
+
const e = this.effects.find(x => x?.id === effectId);
|
|
1135
|
+
const r = this.running.get(effectId);
|
|
1136
|
+
if (!e)
|
|
1137
|
+
return false;
|
|
1138
|
+
if (!r)
|
|
1139
|
+
return true;
|
|
1140
|
+
const def = getOperation(r.op);
|
|
1141
|
+
if (!def)
|
|
1142
|
+
return true;
|
|
1143
|
+
const [follows, inStep] = this.visOf(effectId);
|
|
1144
|
+
const params = { ...objectEffectParams(def, e, follows, inStep), ...cur };
|
|
1145
|
+
const paramsKey = JSON.stringify(params);
|
|
1146
|
+
if (paramsKey === r.paramsKey)
|
|
1147
|
+
return true;
|
|
1148
|
+
r.params = params;
|
|
1149
|
+
r.paramsKey = paramsKey;
|
|
1150
|
+
if (r.started && def.live)
|
|
1151
|
+
for (const t of r.targets)
|
|
1152
|
+
def.live(t, { ...params, __backward: !!r.cycle?.back });
|
|
1153
|
+
return true;
|
|
1154
|
+
}
|
|
1107
1155
|
stop(effectId) {
|
|
1108
1156
|
const r = this.running.get(effectId);
|
|
1109
1157
|
const def = r && getOperation(r.op);
|
|
@@ -88,19 +88,26 @@ export declare function previewEffect(effect: OperationEffect, self?: Object3D |
|
|
|
88
88
|
/** Preview several effects together (an interaction's "Try it"). */
|
|
89
89
|
export declare function previewEffects(effects: OperationEffect[], self?: Object3D | null): Promise<boolean>;
|
|
90
90
|
export declare const isPreviewing: (effectId: string) => boolean;
|
|
91
|
+
/** Whether a pre-warm can do anything for an operation (visual, undoable, with shaders). */
|
|
92
|
+
export declare const isWarmable: (def: OperationDefinition | undefined) => def is OperationDefinition;
|
|
91
93
|
/**
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
94
|
+
* Warm an operation on these targets: build and compile everything its first real play
|
|
95
|
+
* needs — shaders (in the background), and the CPU work it caches (surface samples,
|
|
96
|
+
* geometry) — showing nothing, then take it off again. Queued, one at a time; each
|
|
97
|
+
* (operation, target, its meshes, parameters) once. Targets with no meshes yet (a model
|
|
98
|
+
* still loading) are skipped and warmed when it has landed.
|
|
97
99
|
*/
|
|
98
|
-
export declare function
|
|
100
|
+
export declare function warmEffectOn(def: OperationDefinition, targets: Object3D[], params: OperationParams, self?: Object3D | null): Promise<void>;
|
|
99
101
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
102
|
+
* Compile an effect's shaders ahead of time, without showing anything, so its
|
|
103
|
+
* first real play starts instantly (see warmEffectOn). Used when an effect is picked
|
|
104
|
+
* in an editor; the project bake (bake.ts) warms everything a scene can play.
|
|
103
105
|
*/
|
|
106
|
+
export declare function prewarmEffect(effect: OperationEffect, self?: Object3D | null): Promise<void>;
|
|
107
|
+
/** Main-thread work an effect did, for the stall log (utils/stallLog). Cheap when it is off. */
|
|
108
|
+
type WorkListener = (label: string, ms: number) => void;
|
|
109
|
+
export declare const setWorkListener: (fn: WorkListener | null) => void;
|
|
110
|
+
export declare function noteWork(label: string, ms: number): void;
|
|
104
111
|
export declare function warmOperation(def: OperationDefinition, target: Object3D, params: OperationParams, self?: Object3D | null, opts?: {
|
|
105
112
|
bare?: boolean;
|
|
106
113
|
}): Promise<void>;
|
|
@@ -112,4 +119,5 @@ export declare function runActionEffects(action: ActionSequence, mode: 'apply' |
|
|
|
112
119
|
export declare const areActionEffectsOn: (actionId: string) => boolean;
|
|
113
120
|
/** Subscribe to action-effects on/off changes (studio badges). */
|
|
114
121
|
export declare function subscribeActionEffects(fn: () => void): () => void;
|
|
122
|
+
export {};
|
|
115
123
|
//# sourceMappingURL=runtime.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/operations/runtime.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAEtC,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,aAAa,EAAE,eAAe,EAAE,aAAa,EAAgB,MAAM,uBAAuB,CAAC;AAIrI,OAAO,KAAK,EAAoB,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAGtG,OAAO,2BAA2B,CAAC;AAwCnC,kEAAkE;AAClE,wBAAgB,sBAAsB,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,IAAI,CAQpE;AAED,wBAAgB,aAAa,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,GAAG,OAAO,CAE1E;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG,IAAI,EAAE,IAAI,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/operations/runtime.ts"],"names":[],"mappings":"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"}
|
|
@@ -75,7 +75,10 @@ export function applyOperation(def, target, params, self, opts) {
|
|
|
75
75
|
}
|
|
76
76
|
slots.set(def.exclusive.group, key);
|
|
77
77
|
}
|
|
78
|
-
|
|
78
|
+
const t0 = performance.now();
|
|
79
|
+
const res = def.apply(target, params, ctx);
|
|
80
|
+
noteWork(def.value, performance.now() - t0);
|
|
81
|
+
runs.set(key, finished(res, params));
|
|
79
82
|
if (def.revert)
|
|
80
83
|
active.set(key, { op: def.value, target, params });
|
|
81
84
|
return true;
|
|
@@ -348,30 +351,69 @@ export const isPreviewing = (effectId) => previews.has(effectId);
|
|
|
348
351
|
// ---------------------------------------------------------------- pre-warm
|
|
349
352
|
/** Visual operations with no shaders of their own — nothing to warm (and a warm-only run would show). */
|
|
350
353
|
const NO_SHADERS = new Set(['glow', 'fade', 'visibility', 'displayImage']);
|
|
354
|
+
/** Whether a pre-warm can do anything for an operation (visual, undoable, with shaders). */
|
|
355
|
+
export const isWarmable = (def) => !!def && def.channel === 'visual' && !!def.revert && !NO_SHADERS.has(def.value);
|
|
351
356
|
const warmed = new Set();
|
|
352
357
|
let warmQueue = Promise.resolve();
|
|
353
358
|
/**
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
* shaders). Runs one at a time, in the background; each (effect, target) once.
|
|
357
|
-
* Used when an effect is picked in the editor and, for every effect a scene's
|
|
358
|
-
* interactions and actions use, when the scene loads.
|
|
359
|
+
* What a warm-up covered on a target: its meshes as they are now. A model that lands
|
|
360
|
+
* after the warm-up (its object's group exists first) has other meshes — it is warmed again.
|
|
359
361
|
*/
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
362
|
+
function meshSignature(t) {
|
|
363
|
+
let count = 0, h = 0;
|
|
364
|
+
t.traverse((c) => {
|
|
365
|
+
if (!c.isMesh || c.userData?.__opFxHelper)
|
|
366
|
+
return;
|
|
367
|
+
count++;
|
|
368
|
+
for (let i = 0; i < c.uuid.length; i++)
|
|
369
|
+
h = (h * 31 + c.uuid.charCodeAt(i)) | 0;
|
|
370
|
+
});
|
|
371
|
+
return { count, sig: `${count}:${h}` };
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* The objects a warm-up runs on for one target: the target itself — or, for a group, its
|
|
375
|
+
* member objects (a group never draws an effect itself: groupFanout hands them out).
|
|
376
|
+
*/
|
|
377
|
+
function warmRoots(t) {
|
|
378
|
+
if (t.userData?.objectType !== 'group')
|
|
379
|
+
return [t];
|
|
380
|
+
const out = [];
|
|
381
|
+
t.traverse((c) => {
|
|
382
|
+
if (c === t || !c.userData?.id || !c.userData.objectType || c.userData.objectType === 'group')
|
|
383
|
+
return;
|
|
384
|
+
if (c.userData.visualFx === false)
|
|
385
|
+
return;
|
|
386
|
+
out.push(c);
|
|
387
|
+
});
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* Warm an operation on these targets: build and compile everything its first real play
|
|
392
|
+
* needs — shaders (in the background), and the CPU work it caches (surface samples,
|
|
393
|
+
* geometry) — showing nothing, then take it off again. Queued, one at a time; each
|
|
394
|
+
* (operation, target, its meshes, parameters) once. Targets with no meshes yet (a model
|
|
395
|
+
* still loading) are skipped and warmed when it has landed.
|
|
396
|
+
*/
|
|
397
|
+
export function warmEffectOn(def, targets, params, self) {
|
|
398
|
+
if (!isWarmable(def))
|
|
363
399
|
return Promise.resolve();
|
|
364
|
-
const
|
|
400
|
+
const paramsKey = JSON.stringify(params);
|
|
365
401
|
const job = async () => {
|
|
366
|
-
const ctx = context(self);
|
|
402
|
+
const ctx = context(self, true);
|
|
367
403
|
if (!ctx)
|
|
368
404
|
return;
|
|
369
|
-
const
|
|
370
|
-
for (const t of def.multi ?
|
|
371
|
-
|
|
405
|
+
const roots = targets.flatMap(warmRoots);
|
|
406
|
+
for (const t of def.multi ? roots : roots.slice(0, 1)) {
|
|
407
|
+
if (!t.parent)
|
|
408
|
+
continue; // left the scene meanwhile
|
|
409
|
+
const { count, sig } = meshSignature(t);
|
|
410
|
+
if (!count)
|
|
411
|
+
continue; // nothing to warm yet (a model still loading)
|
|
412
|
+
const k = `${def.value}:${t.uuid}:${sig}:${paramsKey}`;
|
|
372
413
|
if (warmed.has(k) || isOperationOn(def.value, t))
|
|
373
414
|
continue;
|
|
374
415
|
warmed.add(k);
|
|
416
|
+
const t0 = performance.now();
|
|
375
417
|
// a warm-only run: stages and compiles everything, animates nothing…
|
|
376
418
|
warmMode.on = true;
|
|
377
419
|
let res;
|
|
@@ -381,8 +423,9 @@ export function prewarmEffect(effect, self) {
|
|
|
381
423
|
finally {
|
|
382
424
|
warmMode.on = false;
|
|
383
425
|
}
|
|
426
|
+
noteWork(`warm ${def.value}`, performance.now() - t0);
|
|
384
427
|
await finished(res, { duration: 0 });
|
|
385
|
-
// …then quietly take it off again, unless
|
|
428
|
+
// …then quietly take it off again, unless something started it meanwhile
|
|
386
429
|
if (!isOperationOn(def.value, t))
|
|
387
430
|
await finished(def.revert(t, { ...params, duration: 0.001 }, ctx), { duration: 0 });
|
|
388
431
|
}
|
|
@@ -391,10 +434,20 @@ export function prewarmEffect(effect, self) {
|
|
|
391
434
|
return warmQueue;
|
|
392
435
|
}
|
|
393
436
|
/**
|
|
394
|
-
*
|
|
395
|
-
*
|
|
396
|
-
*
|
|
437
|
+
* Compile an effect's shaders ahead of time, without showing anything, so its
|
|
438
|
+
* first real play starts instantly (see warmEffectOn). Used when an effect is picked
|
|
439
|
+
* in an editor; the project bake (bake.ts) warms everything a scene can play.
|
|
397
440
|
*/
|
|
441
|
+
export function prewarmEffect(effect, self) {
|
|
442
|
+
const def = getOperation(effect.operation);
|
|
443
|
+
if (!isWarmable(def))
|
|
444
|
+
return Promise.resolve();
|
|
445
|
+
return warmEffectOn(def, resolveEffectTargets(effect, self), resolveParams(def, effect.params), self);
|
|
446
|
+
}
|
|
447
|
+
let workListener = null;
|
|
448
|
+
export const setWorkListener = (fn) => { workListener = fn; };
|
|
449
|
+
export function noteWork(label, ms) { if (workListener && ms >= 4)
|
|
450
|
+
workListener(label, ms); }
|
|
398
451
|
export function warmOperation(def, target, params, self, opts) {
|
|
399
452
|
if (def.channel !== 'visual' || !def.revert || NO_SHADERS.has(def.value))
|
|
400
453
|
return Promise.resolve();
|