babylon.quarks 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +769 -8
  2. package/README.md +10 -8
  3. package/dist/babylon.quarks.cjs +1 -1
  4. package/dist/babylon.quarks.cjs.map +1 -1
  5. package/dist/babylon.quarks.esm.js +1 -1
  6. package/dist/babylon.quarks.esm.js.map +1 -1
  7. package/dist/babylon.quarks.umd.min.js +1 -1
  8. package/dist/babylon.quarks.umd.min.js.map +1 -1
  9. package/dist/types/index.d.cts +6683 -0
  10. package/dist/types/index.d.ts +1959 -588
  11. package/package.json +12 -6
  12. package/src/BatchedRenderer.ts +204 -101
  13. package/src/EffectPool.ts +4 -1
  14. package/src/MeshSurfaceEmitter.ts +29 -4
  15. package/src/ParticleEmitter.ts +45 -15
  16. package/src/ParticleSystem.ts +909 -2061
  17. package/src/QuarksLoader.ts +421 -67
  18. package/src/QuarksPrefab.ts +91 -4
  19. package/src/QuarksSerializer.ts +484 -0
  20. package/src/QuarksUtil.ts +15 -0
  21. package/src/SpriteBatch.ts +106 -246
  22. package/src/TrailBatch.ts +533 -342
  23. package/src/VFXBatch.ts +236 -136
  24. package/src/batchKey.ts +237 -0
  25. package/src/effectResources.ts +79 -0
  26. package/src/envAtlas.ts +119 -17
  27. package/src/index.ts +32 -33
  28. package/src/nodeId.ts +13 -0
  29. package/src/shaderProgram.ts +325 -0
  30. package/src/shaderVariants.ts +225 -0
  31. package/src/shaders/chunks/fog.ts +4 -2
  32. package/src/shaders/chunks/sheet.ts +111 -0
  33. package/src/shaders/chunks/soft.ts +84 -0
  34. package/src/shaders/chunks/surface.ts +10 -0
  35. package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
  36. package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
  37. package/src/shaders/particle_frag.glsl.ts +3 -22
  38. package/src/shaders/particle_frag.wgsl.ts +3 -23
  39. package/src/shaders/particle_physics_frag.glsl.ts +12 -25
  40. package/src/shaders/particle_physics_frag.wgsl.ts +12 -26
  41. package/src/shaders/particle_vert.glsl.ts +5 -27
  42. package/src/shaders/particle_vert.wgsl.ts +5 -26
  43. package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
  44. package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
  45. package/src/shaders/trail_frag.glsl.ts +11 -1
  46. package/src/shaders/trail_frag.wgsl.ts +11 -0
  47. package/src/shaders/trail_vert.glsl.ts +45 -36
  48. package/src/shaders/trail_vert.wgsl.ts +46 -40
  49. package/src/softDepth.ts +136 -0
  50. package/src/system/BehaviorPlan.ts +85 -0
  51. package/src/system/BoundsTracker.ts +181 -0
  52. package/src/system/EmissionController.ts +360 -0
  53. package/src/system/EventDispatcher.ts +44 -0
  54. package/src/system/ParticleSpawner.ts +274 -0
  55. package/src/system/SimulationClock.ts +130 -0
  56. package/src/system/geometry.ts +111 -0
  57. package/src/system/integrate.ts +428 -0
  58. package/src/system/materialResolve.ts +116 -0
  59. package/src/system/normalize.ts +95 -0
  60. package/src/system/serialization.ts +401 -0
  61. package/src/system/settingsSchema.ts +317 -0
  62. package/src/system/types.ts +410 -0
  63. package/src/textureSource.ts +30 -0
  64. package/src/trailSettings.ts +51 -0
  65. package/src/BatchedParticleRenderer.ts +0 -3
  66. package/src/materials/ParticleMaterials.ts +0 -23
  67. package/src/materials/index.ts +0 -1
  68. package/src/shaders/chunks/index.ts +0 -1
  69. package/src/shaders/chunks/register-shader-chunks.ts +0 -32
  70. package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
  71. package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
  72. package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
  73. package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
  74. package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
  75. package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
  76. package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
  77. package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
  78. package/src/shaders/index.ts +0 -9
  79. package/src/shaders/local_particle_vert.glsl.ts +0 -50
@@ -3,21 +3,42 @@ import {Scene} from '@babylonjs/core/scene';
3
3
  import {IAnimationData, IPrefab} from 'quarks.core';
4
4
  import {BatchedRenderer} from './BatchedRenderer';
5
5
  import {ParticleEmitter} from './ParticleEmitter';
6
+ import {quarksNodeId} from './nodeId';
6
7
 
8
+ /**
9
+ * A clip a {@link QuarksPrefab} timeline can drive, such as a Babylon `AnimationGroup`. Every
10
+ * member is optional; the prefab calls only what is there.
11
+ */
7
12
  export interface QuarksTimelineClip {
13
+ /** Names the clip in a saved prefab, so it can be found again on load. */
8
14
  uuid?: string;
15
+ /** Length in seconds, used when the track gives none. */
9
16
  duration?: number;
17
+ /** Called when the timeline enters the track. */
10
18
  play?: () => void;
19
+ /** Called when the prefab pauses. */
11
20
  pause?: () => void;
21
+ /** Called when the timeline leaves the track or the prefab stops. */
12
22
  stop?: () => void;
23
+ /** Called while scrubbing with {@link QuarksPrefab.setTime}, with the time into the track in seconds. */
13
24
  setTime?: (time: number) => void;
14
25
  }
15
26
 
27
+ /** One track on a {@link QuarksPrefab} timeline. */
16
28
  export interface AnimationData extends IAnimationData {
29
+ /**
30
+ * `'ps'` plays a particle system, `'three'` plays a {@link QuarksTimelineClip}. The names are
31
+ * the effect format's, shared with three.quarks, where a `'three'` track plays a three.js
32
+ * `AnimationClip`; here the clip is whatever the host registered on the target node.
33
+ */
17
34
  type: 'three' | 'ps';
35
+ /** The node the track drives: a {@link ParticleEmitter} for `'ps'`, any node for `'three'`. */
18
36
  target: TransformNode;
37
+ /** Restart when the track ends instead of letting it finish. */
19
38
  loop: boolean;
39
+ /** The clip a `'three'` track plays. */
20
40
  clip?: QuarksTimelineClip;
41
+ /** The id of the clip on `target`, written to and read from JSON. */
21
42
  clipUUID?: string;
22
43
  }
23
44
 
@@ -30,12 +51,26 @@ interface AnimationJSON {
30
51
  loop: boolean;
31
52
  }
32
53
 
54
+ /**
55
+ * A node with a timeline that starts and stops the particle systems and clips under it, like a
56
+ * Unity Timeline or a Niagara System's emitter spawn schedule. Each track starts at `startTime`
57
+ * and lasts `duration` seconds: a particle system restarts when its track begins and stops
58
+ * emitting when it ends, a clip plays and stops.
59
+ *
60
+ * Call {@link QuarksPrefab.update} every frame (or {@link QuarksPrefab.setTime} to scrub).
61
+ */
33
62
  export class QuarksPrefab extends TransformNode implements IPrefab {
63
+ /** Always `'QuarksPrefab'`; written to JSON. */
34
64
  type = 'QuarksPrefab';
65
+ /** The tracks, in the order they were added. */
35
66
  animationData: Array<AnimationData> = [];
67
+ /** Whether {@link QuarksPrefab.update} advances the timeline. */
36
68
  isPlaying = false;
69
+ /** Time on the timeline in seconds; just below 0 before the first update, so a track at 0 starts. */
37
70
  currentTime = -0.00001;
71
+ /** Playback speed; 1 is real time. */
38
72
  timeScale = 1;
73
+ /** End of the last track in seconds; the prefab stops when it passes it. */
39
74
  duration = 0;
40
75
 
41
76
  private _lastUpdateTimeMs = Date.now();
@@ -46,10 +81,17 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
46
81
  super(name, scene);
47
82
  }
48
83
 
84
+ /** The renderer that a particle system is added to again when its track starts. */
49
85
  registerBatchedRenderer(renderer: BatchedRenderer): void {
50
86
  this._batchedRenderer = renderer;
51
87
  }
52
88
 
89
+ /**
90
+ * Adds a track that plays `emitter`'s system, and pauses the prefab.
91
+ * @param startTime - Seconds into the timeline.
92
+ * @param duration - Seconds; 0 or less uses the system's own duration.
93
+ * @param loop - Restart the system when the track ends.
94
+ */
53
95
  addParticleSystemAnimation(emitter: ParticleEmitter, startTime = 0, duration = 0, loop = false): AnimationData {
54
96
  const animationDuration = duration > 0 ? duration : emitter.system.duration;
55
97
  const data: AnimationData = {
@@ -65,6 +107,13 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
65
107
  return data;
66
108
  }
67
109
 
110
+ /**
111
+ * Adds a track that plays `clip` on `target`.
112
+ * @param startTime - Seconds into the timeline.
113
+ * @param duration - Seconds; 0 or less uses the clip's duration.
114
+ * @param loop - Play the clip again when the track ends.
115
+ * @param clipUUID - Saved in JSON to find the clip on load; defaults to `clip.uuid`.
116
+ */
68
117
  addThreeAnimation(
69
118
  target: TransformNode,
70
119
  clip?: QuarksTimelineClip,
@@ -88,17 +137,33 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
88
137
  return data;
89
138
  }
90
139
 
140
+ /** Removes the track at `index` in {@link QuarksPrefab.animationData}. */
91
141
  removeAnimation(index: number): void {
92
142
  this.animationData.splice(index, 1);
93
143
  this.updateDuration();
94
144
  }
95
145
 
146
+ /**
147
+ * Starts or resumes the timeline from {@link QuarksPrefab.currentTime}, resuming the systems and
148
+ * clips of the tracks it is inside, which {@link QuarksPrefab.pause} paused.
149
+ */
96
150
  play(): void {
97
151
  if (this.isPlaying) return;
98
152
  this.isPlaying = true;
99
153
  this._lastUpdateTimeMs = Date.now();
154
+ this.animationData.forEach((animation) => {
155
+ const inside =
156
+ this.currentTime >= animation.startTime && this.currentTime <= animation.startTime + animation.duration;
157
+ if (!inside) return;
158
+ if (animation.type === 'ps' && animation.target instanceof ParticleEmitter) {
159
+ animation.target.system.play();
160
+ return;
161
+ }
162
+ animation.clip?.play?.();
163
+ });
100
164
  }
101
165
 
166
+ /** Pauses the timeline and every system and clip on it. */
102
167
  pause(): void {
103
168
  if (!this.isPlaying) return;
104
169
  this.isPlaying = false;
@@ -111,6 +176,7 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
111
176
  });
112
177
  }
113
178
 
179
+ /** Pauses, rewinds to the start, and stops every system and clip on it. */
114
180
  stop(): void {
115
181
  this.pause();
116
182
  this.currentTime = -0.00001;
@@ -123,6 +189,10 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
123
189
  });
124
190
  }
125
191
 
192
+ /**
193
+ * Advances the timeline, starting and ending tracks it crosses. Stops past {@link QuarksPrefab.duration}.
194
+ * @param forceDelta - Seconds to advance; by default the wall-clock time since the last update.
195
+ */
126
196
  update(forceDelta?: number): void {
127
197
  if (!this.isPlaying) return;
128
198
  const now = Date.now();
@@ -169,6 +239,7 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
169
239
  });
170
240
  }
171
241
 
242
+ /** Jumps to `time` seconds, starting and ending tracks it crosses and scrubbing clips to it. */
172
243
  setTime(time: number): void {
173
244
  const previousTime = this.currentTime;
174
245
  this.currentTime = time;
@@ -199,17 +270,27 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
199
270
  });
200
271
  }
201
272
 
273
+ /** Same as {@link QuarksPrefab.duration}. */
202
274
  getDuration(): number {
203
275
  return this.duration;
204
276
  }
205
277
 
278
+ /**
279
+ * Links the tracks read by {@link QuarksPrefab.fromJSON} to the nodes under `root` they name.
280
+ * {@link QuarksLoader} calls it once the whole effect is built; tracks whose node is missing are dropped.
281
+ */
206
282
  resolveReferences(root: TransformNode): void {
207
283
  const nodesMap: {[uuid: string]: TransformNode} = {};
208
284
  const traverse = (node: TransformNode) => {
209
- if ((node as any)._quarksUUID) {
210
- nodesMap[(node as any)._quarksUUID] = node;
285
+ // A file's uuid names the node it was loaded as; a Babylon uniqueId, which older files
286
+ // wrote for nodes made in code, only names a node that has no file uuid, and never
287
+ // takes over an id already claimed.
288
+ const fileId = (node as any)._quarksUUID as string | undefined;
289
+ if (fileId) {
290
+ nodesMap[fileId] = node;
291
+ } else {
292
+ nodesMap[node.uniqueId.toString()] ??= node;
211
293
  }
212
- nodesMap[node.uniqueId.toString()] = node;
213
294
  const children = node.getChildren();
214
295
  for (const child of children) {
215
296
  if (child instanceof TransformNode) {
@@ -246,23 +327,29 @@ export class QuarksPrefab extends TransformNode implements IPrefab {
246
327
  this._tempAnimationJSON = [];
247
328
  }
248
329
 
330
+ /** Writes the prefab's name, time scale (when not 1) and tracks. */
249
331
  toJSON(): any {
250
332
  return {
251
333
  type: this.type,
252
334
  name: this.name,
335
+ ...(this.timeScale !== 1 ? {timeScale: this.timeScale} : {}),
253
336
  animationData: this.animationData.map((animation) => ({
254
337
  startTime: animation.startTime,
255
338
  duration: animation.duration,
256
339
  type: animation.type,
257
- targetUUID: (animation.target as any)._quarksUUID ?? animation.target.uniqueId.toString(),
340
+ targetUUID: quarksNodeId(animation.target),
258
341
  clipUUID: animation.type === 'three' ? (animation.clipUUID ?? animation.clip?.uuid) : undefined,
259
342
  loop: animation.loop,
260
343
  })),
261
344
  };
262
345
  }
263
346
 
347
+ /** Reads a prefab written by {@link QuarksPrefab.toJSON}; call {@link QuarksPrefab.resolveReferences} afterwards. */
264
348
  static fromJSON(json: any, scene?: Scene): QuarksPrefab {
265
349
  const prefab = new QuarksPrefab(json.name || 'QuarksPrefab', scene);
350
+ if (typeof json.timeScale === 'number') {
351
+ prefab.timeScale = json.timeScale;
352
+ }
266
353
  if (Array.isArray(json.animationData)) {
267
354
  prefab._tempAnimationJSON = json.animationData.filter(
268
355
  (item: any) => item?.type === 'ps' || item?.type === 'three'