babylon.quarks 0.28.0 → 0.29.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 (39) hide show
  1. package/CHANGELOG.md +160 -0
  2. package/dist/babylon.quarks.cjs +1 -1
  3. package/dist/babylon.quarks.cjs.map +1 -1
  4. package/dist/babylon.quarks.esm.js +1 -1
  5. package/dist/babylon.quarks.esm.js.map +1 -1
  6. package/dist/babylon.quarks.umd.min.js +1 -1
  7. package/dist/babylon.quarks.umd.min.js.map +1 -1
  8. package/dist/types/index.d.cts +602 -190
  9. package/dist/types/index.d.ts +602 -190
  10. package/package.json +1 -1
  11. package/src/BatchedRenderer.ts +13 -1
  12. package/src/EffectPool.ts +5 -4
  13. package/src/ParticleEmitter.ts +89 -24
  14. package/src/ParticleSystem.ts +213 -30
  15. package/src/QuarksLoader.ts +9 -0
  16. package/src/QuarksSerializer.ts +1 -1
  17. package/src/QuarksUtil.ts +24 -2
  18. package/src/SpriteBatch.ts +3 -0
  19. package/src/TrailBatch.ts +8 -1
  20. package/src/VFXBatch.ts +112 -0
  21. package/src/batchKey.ts +2 -0
  22. package/src/batchSettings.ts +2 -0
  23. package/src/index.ts +2 -0
  24. package/src/shaderProgram.ts +103 -0
  25. package/src/shaderVariants.ts +102 -5
  26. package/src/shaders/chunks/blend.ts +15 -7
  27. package/src/shaders/chunks/imageProcessing.ts +143 -0
  28. package/src/shaders/particle_frag.glsl.ts +2 -0
  29. package/src/shaders/particle_frag.wgsl.ts +2 -0
  30. package/src/shaders/particle_physics_frag.glsl.ts +2 -0
  31. package/src/shaders/particle_physics_frag.wgsl.ts +2 -0
  32. package/src/shaders/trail_frag.glsl.ts +2 -0
  33. package/src/shaders/trail_frag.wgsl.ts +2 -0
  34. package/src/system/EmissionController.ts +21 -16
  35. package/src/system/ParticleSpawner.ts +96 -19
  36. package/src/system/integrate.ts +61 -4
  37. package/src/system/normalize.ts +28 -1
  38. package/src/system/settingsSchema.ts +25 -7
  39. package/src/system/types.ts +83 -14
@@ -23,8 +23,12 @@ import {
23
23
  DEFAULT_SOFT_FAR_FADE,
24
24
  normalizeBursts,
25
25
  validCullingMode,
26
+ validLoopRange,
26
27
  validRenderFace,
28
+ validRingBufferMode,
29
+ validScalingMode,
27
30
  validSortMode,
31
+ validStopAction,
28
32
  validTileCount,
29
33
  } from './normalize';
30
34
  import type {
@@ -142,7 +146,7 @@ function vectorJSON(value: unknown): number[] | undefined {
142
146
 
143
147
  /** Every plain and generator setting, in the order `toJSON` writes them. */
144
148
  export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
145
- plain('autoDestroy', (s) => s.autoDestroy, false, boolean),
149
+ plain('stopAction', (s) => s.stopAction, 'none', validStopAction),
146
150
  plain('looping', (s) => s.looping, true, boolean),
147
151
  plain('prewarm', (s) => s.prewarm, false, boolean),
148
152
  plain('prewarmTime', (s) => s.prewarmTime, 0, number),
@@ -228,6 +232,15 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
228
232
  clone: (value) => (value as BurstParameters[]).map((burst) => ({...burst, count: burst.count.clone()})),
229
233
  },
230
234
  plain('maxParticles', (s) => s.maxParticles, undefined, number),
235
+ plain('ringBufferMode', (s) => s.ringBufferMode, 'disabled', validRingBufferMode),
236
+ {
237
+ param: 'ringBufferLoopRange',
238
+ get: (s) => s.ringBufferLoopRange,
239
+ isDefault: (value) => (value as Vector2).x === 0 && (value as Vector2).y === 1,
240
+ toJSON: vectorJSON,
241
+ fromJSON: (json) => (Array.isArray(json) ? validLoopRange(new Vector2(json[0], json[1])) : undefined),
242
+ clone: (value) => (value as Vector2).clone(),
243
+ },
231
244
  plain('onlyUsedByOther', (s) => s.onlyUsedByOther, false, boolean),
232
245
  plain('renderMode', (s) => s.renderMode, RenderMode.BillBoard, number),
233
246
  plain('renderOrder', (s) => s.renderOrder, 0, number),
@@ -238,14 +251,15 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
238
251
  true,
239
252
  (json) => json !== false
240
253
  ),
241
- plain('playbackSpeed', (s) => s.playbackSpeed, 1, number),
242
- plain('cullingMode', (s) => s.cullingMode, 'always', validCullingMode),
243
254
  plain(
244
- 'scalingMode',
245
- (s) => s.scalingMode,
246
- 'shape',
247
- (json) => (json === 'hierarchy' ? 'hierarchy' : 'shape')
255
+ 'applyImageProcessing',
256
+ (s) => s.applyImageProcessing,
257
+ true,
258
+ (json) => json !== false
248
259
  ),
260
+ plain('playbackSpeed', (s) => s.playbackSpeed, 1, number),
261
+ plain('cullingMode', (s) => s.cullingMode, 'always', validCullingMode),
262
+ plain('scalingMode', (s) => s.scalingMode, 'local', validScalingMode),
249
263
  plain('seed', (s) => s.seed, undefined, number),
250
264
  plain('sortMode', (s) => s.sortMode, 'none', validSortMode),
251
265
  plain('renderFace', (s) => s.renderFace, 'both', validRenderFace),
@@ -326,6 +340,10 @@ export function settingsFromJSON(json: ParticleSystemJSONParameters): Partial<Pa
326
340
  parameters[setting.param] = read;
327
341
  }
328
342
  }
343
+ // Files before 0.29 wrote the stop action as `autoDestroy`.
344
+ if (parameters.stopAction === undefined && fields.autoDestroy === true) {
345
+ parameters.stopAction = 'destroy';
346
+ }
329
347
  return parameters as Partial<ParticleSystemParameters>;
330
348
  }
331
349
 
@@ -101,18 +101,68 @@ export interface BurstParameters {
101
101
  export type CullingMode = 'always' | 'pause' | 'pauseAndCatchUp';
102
102
 
103
103
  /**
104
- * What the emitter's world scale does to a world-space system (Unity's
105
- * Scaling Mode, as far as it differs here). A local-space system lives in its
106
- * emitter's space, so its scale reaches everything either way.
104
+ * What a system does once it has finished, its emission over and its last particle dead (Unity's
105
+ * Stop Action):
107
106
  *
108
- * - `'shape'` (default): the scale sizes the emission shape and the
109
- * particles; speeds, forces and speed limits stay in world units, so a
110
- * scaled-down effect moves as far as the full-size one.
111
- * - `'hierarchy'`: the scale also multiplies start speeds (per axis),
112
- * `ForceOverLife` and `LimitSpeedOverLife` (by the mean of the axes), so the
113
- * whole effect, motion included, scales like a model would.
107
+ * - `'none'` (the default): nothing; {@link ParticleSystem.restart} plays it again.
108
+ * - `'disable'`: disables its emitter node (`setEnabled(false)`), and with it the nodes under it.
109
+ * A disabled system is not advanced or drawn; enable the emitter again before playing it.
110
+ * - `'destroy'`: disposes the system and its emitter, as `ParticleSystem.dispose()` does.
111
+ *
112
+ * Unity's Callback is the system's `finished` event, which fires whatever the stop action. The
113
+ * action waits for the system's sub emitters, see {@link ParticleSystem.stopAction}. A disabled
114
+ * emitter is state like any other: saved as not visible, and copied disabled by `clone`.
115
+ */
116
+ export type StopAction = 'none' | 'disable' | 'destroy';
117
+
118
+ /**
119
+ * What a full system does with the particles it emits next (Unity's Ring Buffer Mode). It needs
120
+ * {@link ParticleSystemParameters.maxParticles}; without a limit it is ignored.
121
+ *
122
+ * - `'disabled'` (the default): particles die at the end of their life, and a full system drops
123
+ * what it would emit.
124
+ * - `'pauseUntilReplaced'`: a particle at the end of its life stays where and as it is, frozen,
125
+ * until a new particle replaces it, the oldest first. For footprints, decals and bullet holes.
126
+ * - `'loopUntilReplaced'`: a particle at the end of its life goes back to an earlier point of it
127
+ * and plays that part again, over and over, until a new particle replaces it; see
128
+ * {@link ParticleSystem.ringBufferLoopRange}.
129
+ *
130
+ * Particles are never killed by age, so a system in either mode keeps its particles and does not
131
+ * finish while it has any: a non-looping ring effect never fires `finished`, and an
132
+ * `EffectPool` copy of it returns to the pool only when it is released. A replaced particle
133
+ * does not fire `particleDied`, nor Death sub emitters, which never fire in either mode since
134
+ * no particle dies. Lowering `maxParticles` drops the oldest particles over the new limit, also
135
+ * without `particleDied`. Particles are replaced oldest first in the order they were emitted;
136
+ * switching a mode on in a system whose particles have already died in a different order
137
+ * replaces them in row order the first time round. `'oldestInFront'` and `'youngestInFront'`
138
+ * sorting reads age, so a looping particle sorts by its age in the loop. Applies in every
139
+ * render mode but Trail, whose particles always die at the end of their life.
140
+ */
141
+ export type RingBufferMode = 'disabled' | 'pauseUntilReplaced' | 'loopUntilReplaced';
142
+
143
+ /**
144
+ * What the emitter's scale does to the system, as Unity's Scaling Mode, in world and local space
145
+ * alike. The scale reaches the emission shape, the particles' size, their start speed and, in world
146
+ * space, `ForceOverLife`, `ApplyForce` and `LimitSpeedOverLife` (by the mean of the axes), so a
147
+ * scaled effect looks and moves like a scaled model, except where a mode leaves it out:
148
+ *
149
+ * - `'local'` (default, as in Unity): only the emitter's own `scaling`; its parents' scale is left
150
+ * out, so an effect keeps its size under a scaled character or prop.
151
+ * - `'hierarchy'`: the emitter's whole world scale, its parents' included.
152
+ * - `'shape'`: the world scale sizes the emission shape alone; the particles keep their size and
153
+ * speed, so a bigger emitter spreads the same particles over a larger area.
154
+ *
155
+ * Under every mode the emitter's position, rotation and any mirroring come from the whole
156
+ * hierarchy; only the length of each axis changes. A sub emission starts in its parent particle's
157
+ * frame and takes its size and speed from that, not from the sub emitter's scale; `'shape'` still
158
+ * scales its shape by the sub emitter's world scale. With a non-uniform scale `'shape'` stretches
159
+ * where particles start, not the directions the shape sends them.
160
+ *
161
+ * Known differences from Unity: a world force (`ForceOverLife`, `ApplyForce`, and so the gravity
162
+ * the Unity exporter writes) is scaled with the motion in `'local'` and `'hierarchy'`, where
163
+ * Unity's gravity is not.
114
164
  */
115
- export type ScalingMode = 'shape' | 'hierarchy';
165
+ export type ScalingMode = 'local' | 'hierarchy' | 'shape';
116
166
 
117
167
  /**
118
168
  * Where a {@link RenderMode.Beam} system's quads end: a point in world space,
@@ -145,7 +195,12 @@ export type BeamEnd =
145
195
  * default named on it, the same default `fromJSON` gives a file that leaves it out.
146
196
  */
147
197
  export interface ParticleSystemParameters {
148
- /** See {@link ParticleSystem.autoDestroy}. Default false. */
198
+ /** What the system does once it has finished, see {@link StopAction}. Default `'none'`. */
199
+ stopAction?: StopAction;
200
+ /**
201
+ * `true` is `stopAction: 'destroy'`; read only when `stopAction` is left out.
202
+ * @deprecated Use `stopAction: 'destroy'`.
203
+ */
149
204
  autoDestroy?: boolean;
150
205
  /** See {@link ParticleSystem.looping}. Default true. */
151
206
  looping?: boolean;
@@ -177,6 +232,10 @@ export interface ParticleSystemParameters {
177
232
  emissionBursts?: Array<BurstParameters>;
178
233
  /** Most particles alive at once; emission past it is dropped. Leave unset for no limit. */
179
234
  maxParticles?: number;
235
+ /** What a full system does with new particles, see {@link RingBufferMode}. Default `'disabled'`. */
236
+ ringBufferMode?: RingBufferMode;
237
+ /** See {@link ParticleSystem.ringBufferLoopRange}. Default the whole life, (0, 1). */
238
+ ringBufferLoopRange?: Vector2;
180
239
  /** See {@link ParticleSystem.onlyUsedByOther}. Default false. */
181
240
  onlyUsedByOther?: boolean;
182
241
  /** See {@link ParticleSystem.behaviors}. Default none. */
@@ -263,7 +322,7 @@ export interface ParticleSystemParameters {
263
322
  playbackSpeed?: number;
264
323
  /** What the system does while off screen, see {@link CullingMode}. Default `'always'`. */
265
324
  cullingMode?: CullingMode;
266
- /** What the emitter's scale does to a world-space system, see {@link ScalingMode}. Default `'shape'`. */
325
+ /** What the emitter's scale does to the system, see {@link ScalingMode}. Default `'local'`. */
267
326
  scalingMode?: ScalingMode;
268
327
  /** Seed for this system's own random generator, see {@link ParticleSystem.seed}. Default none. */
269
328
  seed?: number;
@@ -281,6 +340,8 @@ export interface ParticleSystemParameters {
281
340
  renderingGroupId?: number;
282
341
  /** Whether the scene's fog applies, see {@link ParticleSystem.applyFog}. Default true. */
283
342
  applyFog?: boolean;
343
+ /** Whether the scene's image processing applies, see {@link ParticleSystem.applyImageProcessing}. Default true. */
344
+ applyImageProcessing?: boolean;
284
345
  /** Scene the emitter node is created in. Default Babylon's last created scene. */
285
346
  scene?: Scene;
286
347
  }
@@ -314,7 +375,9 @@ export interface BurstParametersJSON {
314
375
  export interface ParticleSystemJSONParameters {
315
376
  /** The quarks.core version line the system was written with. */
316
377
  version: string;
317
- /** See {@link ParticleSystem.autoDestroy}. */
378
+ /** Written only when not `'none'`. */
379
+ stopAction?: StopAction;
380
+ /** Read from older files: `true` is `stopAction: 'destroy'`. No longer written. */
318
381
  autoDestroy?: boolean;
319
382
  /** See {@link ParticleSystem.looping}. */
320
383
  looping?: boolean;
@@ -346,6 +409,10 @@ export interface ParticleSystemJSONParameters {
346
409
  emissionBursts?: Array<BurstParametersJSON>;
347
410
  /** See {@link ParticleSystem.maxParticles}. */
348
411
  maxParticles?: number;
412
+ /** Written only when not `'disabled'`. */
413
+ ringBufferMode?: RingBufferMode;
414
+ /** `[start, end]`; written only when not `[0, 1]`. */
415
+ ringBufferLoopRange?: [number, number];
349
416
  /** See {@link ParticleSystem.onlyUsedByOther}. */
350
417
  onlyUsedByOther?: boolean;
351
418
  /**
@@ -363,11 +430,13 @@ export interface ParticleSystemJSONParameters {
363
430
  renderingGroupId?: number;
364
431
  /** Written only when false. */
365
432
  applyFog?: boolean;
433
+ /** Written only when false. */
434
+ applyImageProcessing?: boolean;
366
435
  /** Written only when not 1. */
367
436
  playbackSpeed?: number;
368
437
  /** Written only when not `'always'`. */
369
438
  cullingMode?: CullingMode;
370
- /** Written only when not `'shape'`. */
439
+ /** Written only when not `'local'`. */
371
440
  scalingMode?: ScalingMode;
372
441
  /** Written only when set. */
373
442
  seed?: number;