@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.
Files changed (64) hide show
  1. package/dist/animationsV2/ClipsRuntime.d.ts.map +1 -1
  2. package/dist/animationsV2/ClipsRuntime.js +11 -1
  3. package/dist/animationsV2/animationLive.d.ts +7 -0
  4. package/dist/animationsV2/animationLive.d.ts.map +1 -0
  5. package/dist/animationsV2/animationLive.js +26 -0
  6. package/dist/animationsV2/appliers.d.ts +8 -0
  7. package/dist/animationsV2/appliers.d.ts.map +1 -1
  8. package/dist/animationsV2/appliers.js +61 -0
  9. package/dist/animationsV2/index.d.ts +1 -0
  10. package/dist/animationsV2/index.d.ts.map +1 -1
  11. package/dist/animationsV2/index.js +1 -0
  12. package/dist/components/AnimatedObject.d.ts.map +1 -1
  13. package/dist/components/AnimatedObject.js +2 -0
  14. package/dist/components/CameraController.d.ts.map +1 -1
  15. package/dist/components/CameraController.js +9 -0
  16. package/dist/components/CreatedObject.d.ts.map +1 -1
  17. package/dist/components/CreatedObject.js +4 -1
  18. package/dist/components/GroupMaterialContext.d.ts +25 -0
  19. package/dist/components/GroupMaterialContext.d.ts.map +1 -0
  20. package/dist/components/GroupMaterialContext.js +21 -0
  21. package/dist/components/ObjectNode.d.ts.map +1 -1
  22. package/dist/components/ObjectNode.js +13 -2
  23. package/dist/components/ObjectsRenderer.d.ts.map +1 -1
  24. package/dist/components/ObjectsRenderer.js +26 -19
  25. package/dist/components/SceneBuilder.d.ts.map +1 -1
  26. package/dist/components/SceneBuilder.js +7 -4
  27. package/dist/components/land/LandPatch.d.ts.map +1 -1
  28. package/dist/components/land/LandPatch.js +15 -1
  29. package/dist/hooks/useContinuousEffects.d.ts.map +1 -1
  30. package/dist/hooks/useContinuousEffects.js +24 -3
  31. package/dist/hooks/useMaterialApplication.d.ts.map +1 -1
  32. package/dist/hooks/useMaterialApplication.js +2 -1
  33. package/dist/hooks/useMaterialUniforms.d.ts +2 -8
  34. package/dist/hooks/useMaterialUniforms.d.ts.map +1 -1
  35. package/dist/hooks/useMaterialUniforms.js +18 -6
  36. package/dist/hooks/useOrbitTargetFollow.d.ts.map +1 -1
  37. package/dist/hooks/useOrbitTargetFollow.js +6 -4
  38. package/dist/operations/bake.d.ts +36 -0
  39. package/dist/operations/bake.d.ts.map +1 -0
  40. package/dist/operations/bake.js +113 -0
  41. package/dist/operations/catalogue/physicalHeld.d.ts.map +1 -1
  42. package/dist/operations/catalogue/physicalHeld.js +6 -0
  43. package/dist/operations/catalogue/shaderLayers.js +2 -2
  44. package/dist/operations/catalogue/warmup.d.ts +23 -1
  45. package/dist/operations/catalogue/warmup.d.ts.map +1 -1
  46. package/dist/operations/catalogue/warmup.js +200 -12
  47. package/dist/operations/index.d.ts +3 -1
  48. package/dist/operations/index.d.ts.map +1 -1
  49. package/dist/operations/index.js +3 -1
  50. package/dist/operations/objectEffects.d.ts +15 -1
  51. package/dist/operations/objectEffects.d.ts.map +1 -1
  52. package/dist/operations/objectEffects.js +49 -1
  53. package/dist/operations/runtime.d.ts +17 -9
  54. package/dist/operations/runtime.d.ts.map +1 -1
  55. package/dist/operations/runtime.js +71 -18
  56. package/dist/operations/types.d.ts +5 -0
  57. package/dist/operations/types.d.ts.map +1 -1
  58. package/dist/triggers/readiness.d.ts +2 -0
  59. package/dist/triggers/readiness.d.ts.map +1 -1
  60. package/dist/triggers/readiness.js +28 -3
  61. package/dist/utils/stallLog.d.ts +4 -0
  62. package/dist/utils/stallLog.d.ts.map +1 -0
  63. package/dist/utils/stallLog.js +88 -0
  64. 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) => { if (c.material)
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;AAC1O,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,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"}
@@ -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":"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"}
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
- * Compile an effect's shaders ahead of time, without showing anything, so its
93
- * first real play starts instantly. Visual effects only (the others have no
94
- * shaders). Runs one at a time, in the background; each (effect, target) once.
95
- * Used when an effect is picked in the editor and, for every effect a scene's
96
- * interactions and actions use, when the scene loads.
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 prewarmEffect(effect: OperationEffect, self?: Object3D | null): Promise<void>;
100
+ export declare function warmEffectOn(def: OperationDefinition, targets: Object3D[], params: OperationParams, self?: Object3D | null): Promise<void>;
99
101
  /**
100
- * A warm-only apply: stages and compiles what the operation needs on the target and
101
- * animates nothing. It is left staged at its start — apply it for real next, or revert it.
102
- * Used to get an effect ready while its object is still hidden (see objectEffects, Visible).
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":"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"}
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
- runs.set(key, finished(def.apply(target, params, ctx), params));
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
- * Compile an effect's shaders ahead of time, without showing anything, so its
355
- * first real play starts instantly. Visual effects only (the others have no
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
- export function prewarmEffect(effect, self) {
361
- const def = getOperation(effect.operation);
362
- if (!def || def.channel !== 'visual' || !def.revert || NO_SHADERS.has(def.value))
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 params = resolveParams(def, effect.params);
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 targets = resolveEffectTargets(effect, self);
370
- for (const t of def.multi ? targets : targets.slice(0, 1)) {
371
- const k = `${def.value}:${t.uuid}`;
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 the user started it meanwhile
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
- * A warm-only apply: stages and compiles what the operation needs on the target and
395
- * animates nothing. It is left staged at its start — apply it for real next, or revert it.
396
- * Used to get an effect ready while its object is still hidden (see objectEffects, Visible).
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();