babylon.quarks 0.27.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 (55) hide show
  1. package/CHANGELOG.md +585 -0
  2. package/README.md +3 -1
  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 +4811 -974
  10. package/dist/types/index.d.ts +4811 -974
  11. package/package.json +7 -2
  12. package/src/BatchedRenderer.ts +106 -79
  13. package/src/EffectPool.ts +96 -16
  14. package/src/MeshSurfaceEmitter.ts +24 -13
  15. package/src/ParticleEmitter.ts +119 -26
  16. package/src/ParticleSystem.ts +305 -55
  17. package/src/QuarksLoader.ts +298 -171
  18. package/src/QuarksPrefab.ts +301 -94
  19. package/src/QuarksSerializer.ts +44 -16
  20. package/src/QuarksUtil.ts +88 -11
  21. package/src/SpriteBatch.ts +24 -6
  22. package/src/TrailBatch.ts +20 -7
  23. package/src/VFXBatch.ts +182 -528
  24. package/src/batchKey.ts +6 -2
  25. package/src/batchSettings.ts +548 -0
  26. package/src/effectDocumentJSON.ts +261 -0
  27. package/src/effectResources.ts +21 -24
  28. package/src/envAtlas.ts +8 -2
  29. package/src/geometryUtil.ts +1 -1
  30. package/src/index.ts +41 -4
  31. package/src/nodeId.ts +25 -2
  32. package/src/shaderProgram.ts +213 -16
  33. package/src/shaderVariants.ts +124 -7
  34. package/src/shaders/chunks/blend.ts +39 -0
  35. package/src/shaders/chunks/fog.ts +8 -3
  36. package/src/shaders/chunks/imageProcessing.ts +143 -0
  37. package/src/shaders/chunks/soft.ts +6 -5
  38. package/src/shaders/chunks/surface.ts +2 -2
  39. package/src/shaders/particle_frag.glsl.ts +5 -1
  40. package/src/shaders/particle_frag.wgsl.ts +4 -0
  41. package/src/shaders/particle_physics_frag.glsl.ts +7 -2
  42. package/src/shaders/particle_physics_frag.wgsl.ts +4 -0
  43. package/src/shaders/trail_frag.glsl.ts +6 -1
  44. package/src/shaders/trail_frag.wgsl.ts +4 -0
  45. package/src/system/EmissionController.ts +23 -20
  46. package/src/system/EventDispatcher.ts +26 -10
  47. package/src/system/ParticleSpawner.ts +96 -19
  48. package/src/system/geometry.ts +22 -29
  49. package/src/system/integrate.ts +61 -4
  50. package/src/system/materialResolve.ts +56 -7
  51. package/src/system/normalize.ts +34 -2
  52. package/src/system/serialization.ts +54 -33
  53. package/src/system/settingsSchema.ts +57 -17
  54. package/src/system/types.ts +150 -42
  55. package/src/trailSettings.ts +9 -6
@@ -1,4 +1,5 @@
1
1
  import {Constants} from '@babylonjs/core/Engines/constants';
2
+ import type {ColorGeneratorJSON, RotationGeneratorJSON, ValueGeneratorJSON, Vector3GeneratorJSON} from 'quarks.core';
2
3
  import {
3
4
  ColorGenerator,
4
5
  ColorGeneratorFromJSON,
@@ -7,6 +8,7 @@ import {
7
8
  FunctionColorGenerator,
8
9
  FunctionValueGenerator,
9
10
  GeneratorFromJSON,
11
+ readFormatPart,
10
12
  RotationGenerator,
11
13
  ValueGenerator,
12
14
  ValueGeneratorFromJSON,
@@ -17,8 +19,24 @@ import {
17
19
  } from 'quarks.core';
18
20
  import type {ParticleSystem} from '../ParticleSystem';
19
21
  import {RenderMode} from '../VFXBatch';
20
- import {DEFAULT_SOFT_FAR_FADE, normalizeBursts, validCullingMode, validSortMode, validTileCount} from './normalize';
21
- import type {BurstParameters, ParticleSystemJSONParameters, ParticleSystemParameters} from './types';
22
+ import {
23
+ DEFAULT_SOFT_FAR_FADE,
24
+ normalizeBursts,
25
+ validCullingMode,
26
+ validLoopRange,
27
+ validRenderFace,
28
+ validRingBufferMode,
29
+ validScalingMode,
30
+ validSortMode,
31
+ validStopAction,
32
+ validTileCount,
33
+ } from './normalize';
34
+ import type {
35
+ BurstParameters,
36
+ BurstParametersJSON,
37
+ ParticleSystemJSONParameters,
38
+ ParticleSystemParameters,
39
+ } from './types';
22
40
 
23
41
  /**
24
42
  * One setting of a {@link ParticleSystem} that is a plain value or a generator: how to read it
@@ -54,6 +72,9 @@ export interface SettingDescriptor {
54
72
 
55
73
  type Generator = {toJSON(): unknown; clone(): unknown};
56
74
 
75
+ /** A burst as files hold it, any field left out. */
76
+ type BurstJSON = Partial<BurstParametersJSON>;
77
+
57
78
  /** A number, boolean or string compared by value. */
58
79
  function plain<T>(
59
80
  param: keyof ParticleSystemParameters,
@@ -97,11 +118,11 @@ function boolean(json: unknown): boolean | undefined {
97
118
  * A generator, compared with its default through its JSON. `read` turns the JSON back into one;
98
119
  * a bare number is read as a constant.
99
120
  */
100
- function generator(
121
+ function generator<J>(
101
122
  param: keyof ParticleSystemParameters,
102
123
  get: (system: ParticleSystem) => Generator,
103
124
  defaultValue: () => Generator,
104
- read: (json: any) => unknown,
125
+ read: (json: J) => unknown,
105
126
  always = true
106
127
  ): SettingDescriptor {
107
128
  const defaultJSON = JSON.stringify(defaultValue().toJSON());
@@ -111,7 +132,8 @@ function generator(
111
132
  get,
112
133
  isDefault: (value) => JSON.stringify((value as Generator).toJSON()) === defaultJSON,
113
134
  toJSON: (value) => (value as Generator).toJSON(),
114
- fromJSON: (json) => (typeof json === 'number' ? new ConstantValue(json) : read(json)),
135
+ // The generator readers check the JSON they are given, and warn about what they cannot read.
136
+ fromJSON: (json) => (typeof json === 'number' ? new ConstantValue(json) : read(json as J)),
115
137
  clone: (value) => (value as Generator).clone(),
116
138
  };
117
139
  }
@@ -124,7 +146,7 @@ function vectorJSON(value: unknown): number[] | undefined {
124
146
 
125
147
  /** Every plain and generator setting, in the order `toJSON` writes them. */
126
148
  export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
127
- plain('autoDestroy', (s) => s.autoDestroy, false, boolean),
149
+ plain('stopAction', (s) => s.stopAction, 'none', validStopAction),
128
150
  plain('looping', (s) => s.looping, true, boolean),
129
151
  plain('prewarm', (s) => s.prewarm, false, boolean),
130
152
  plain('prewarmTime', (s) => s.prewarmTime, 0, number),
@@ -152,19 +174,21 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
152
174
  'startRotation',
153
175
  (s) => s.startRotation,
154
176
  () => new ConstantValue(0),
155
- (json) => GeneratorFromJSON(json) as RotationGenerator | ValueGenerator | FunctionValueGenerator
177
+ (json: ValueGeneratorJSON | RotationGeneratorJSON) =>
178
+ GeneratorFromJSON(json) as RotationGenerator | ValueGenerator | FunctionValueGenerator
156
179
  ),
157
180
  generator(
158
181
  'startSize',
159
182
  (s) => s.startSize,
160
183
  () => new ConstantValue(1),
161
- (json) => GeneratorFromJSON(json) as Vector3Generator | ValueGenerator | FunctionValueGenerator
184
+ (json: ValueGeneratorJSON | Vector3GeneratorJSON) =>
185
+ GeneratorFromJSON(json) as Vector3Generator | ValueGenerator | FunctionValueGenerator
162
186
  ),
163
187
  generator(
164
188
  'startColor',
165
189
  (s) => s.startColor,
166
190
  () => new ConstantColor(new Vector4(1, 1, 1, 1)),
167
- (json) => ColorGeneratorFromJSON(json) as ColorGenerator | FunctionColorGenerator
191
+ (json: ColorGeneratorJSON) => ColorGeneratorFromJSON(json) as ColorGenerator | FunctionColorGenerator
168
192
  ),
169
193
  generator(
170
194
  'emissionOverTime',
@@ -193,7 +217,7 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
193
217
  fromJSON: (json) =>
194
218
  Array.isArray(json)
195
219
  ? normalizeBursts(
196
- json.map((burst: any) => ({
220
+ json.map((burst: BurstJSON) => ({
197
221
  time: burst.time,
198
222
  count:
199
223
  typeof burst.count === 'number'
@@ -208,6 +232,15 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
208
232
  clone: (value) => (value as BurstParameters[]).map((burst) => ({...burst, count: burst.count.clone()})),
209
233
  },
210
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
+ },
211
244
  plain('onlyUsedByOther', (s) => s.onlyUsedByOther, false, boolean),
212
245
  plain('renderMode', (s) => s.renderMode, RenderMode.BillBoard, number),
213
246
  plain('renderOrder', (s) => s.renderOrder, 0, number),
@@ -218,16 +251,18 @@ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
218
251
  true,
219
252
  (json) => json !== false
220
253
  ),
221
- plain('playbackSpeed', (s) => s.playbackSpeed, 1, number),
222
- plain('cullingMode', (s) => s.cullingMode, 'always', validCullingMode),
223
254
  plain(
224
- 'scalingMode',
225
- (s) => s.scalingMode,
226
- 'shape',
227
- (json) => (json === 'hierarchy' ? 'hierarchy' : 'shape')
255
+ 'applyImageProcessing',
256
+ (s) => s.applyImageProcessing,
257
+ true,
258
+ (json) => json !== false
228
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),
229
263
  plain('seed', (s) => s.seed, undefined, number),
230
264
  plain('sortMode', (s) => s.sortMode, 'none', validSortMode),
265
+ plain('renderFace', (s) => s.renderFace, 'both', validRenderFace),
231
266
  plain(
232
267
  'alignToDirection',
233
268
  (s) => s.alignToDirection,
@@ -299,11 +334,16 @@ export function settingsFromJSON(json: ParticleSystemJSONParameters): Partial<Pa
299
334
  if (value === undefined || value === null) {
300
335
  continue;
301
336
  }
302
- const read = setting.fromJSON(value);
337
+ const key = settingJSONKey(setting);
338
+ const read = readFormatPart(key, () => setting.fromJSON(value));
303
339
  if (read !== undefined) {
304
340
  parameters[setting.param] = read;
305
341
  }
306
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
+ }
307
347
  return parameters as Partial<ParticleSystemParameters>;
308
348
  }
309
349
 
@@ -1,18 +1,27 @@
1
+ import type {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
1
2
  import type {Texture} from '@babylonjs/core/Materials/Textures/texture';
2
3
  import type {Scene} from '@babylonjs/core/scene';
3
4
  import type {
4
5
  Behavior,
6
+ BehaviorJSON,
5
7
  ColorGenerator,
8
+ ColorGeneratorJSON,
6
9
  EmitterShape,
7
10
  FunctionColorGenerator,
8
- FunctionJSON,
9
11
  FunctionValueGenerator,
10
12
  RendererEmitterSettings,
11
13
  RotationGenerator,
14
+ RotationGeneratorJSON,
15
+ ShapeJSON,
16
+ StretchedBillBoardSettings,
12
17
  ValueGenerator,
18
+ ValueGeneratorJSON,
13
19
  Vector2,
14
20
  Vector3,
21
+ Vector3ArrayJSON,
15
22
  Vector3Generator,
23
+ Vector3GeneratorJSON,
24
+ Vector3JSON,
16
25
  } from 'quarks.core';
17
26
  import type {
18
27
  MeshLighting,
@@ -20,10 +29,28 @@ import type {
20
29
  ParticleErosionOptions,
21
30
  ParticleGradientMap,
22
31
  ParticleUVDistortionOptions,
32
+ RenderFace,
23
33
  RenderMode,
24
34
  SortMode,
25
35
  TextureUV,
26
36
  } from '../VFXBatch';
37
+ import type {GeometryEntry, QuarksMaterialJSON} from '../effectDocumentJSON';
38
+ import type {TrailOptions} from '../trailSettings';
39
+ import type {ParticleMaterialSource} from './materialResolve';
40
+
41
+ /** A trail's settings in JSON, each only when not its default: the start length as a generator's JSON, the side axis as `[x, y, z]`. */
42
+ export type TrailSettingsJSON = Partial<TrailOptions> & {
43
+ /** See `TrailSettings.startLength`. */
44
+ startLength?: ValueGeneratorJSON;
45
+ /** See `TrailSettings.sideAxis`; files may hold it as `{x, y, z}`. */
46
+ sideAxis?: Vector3ArrayJSON | Vector3JSON;
47
+ };
48
+
49
+ /**
50
+ * A render mode's own settings in JSON, see {@link RendererEmitterSettings}: a trail's or a
51
+ * stretched billboard's. The other render modes have none, and their systems leave the field out.
52
+ */
53
+ export type RendererEmitterSettingsJSON = TrailSettingsJSON | StretchedBillBoardSettings;
27
54
 
28
55
  /**
29
56
  * One burst of a system's emission (Unity's Emission Bursts): `count` particles at `time` seconds
@@ -64,22 +91,78 @@ export interface BurstParameters {
64
91
  * An empty system is never culled, so it can start emitting and grow the
65
92
  * bounds that decide its visibility. A system another one emits into is never
66
93
  * culled either; its parent decides.
94
+ *
95
+ * The cameras tested are the scene's active cameras (`scene.activeCameras`, or
96
+ * `scene.activeCamera`), each only for the systems on a layer it renders
97
+ * (`camera.layerMask & system.layers.mask`). A camera that renders into a
98
+ * render target texture (a mirror, a minimap, a portal) is not one of them: a
99
+ * system seen only through one should keep `'always'`.
67
100
  */
68
101
  export type CullingMode = 'always' | 'pause' | 'pauseAndCatchUp';
69
102
 
70
103
  /**
71
- * What the emitter's world scale does to a world-space system (Unity's
72
- * Scaling Mode, as far as it differs here). A local-space system lives in its
73
- * 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):
106
+ *
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:
74
148
  *
75
- * - `'shape'` (default): the scale sizes the emission shape and the
76
- * particles; speeds, forces and speed limits stay in world units, so a
77
- * scaled-down effect moves as far as the full-size one.
78
- * - `'hierarchy'`: the scale also multiplies start speeds (per axis),
79
- * `ForceOverLife` and `LimitSpeedOverLife` (by the mean of the axes), so the
80
- * whole effect, motion included, scales like a model would.
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.
81
164
  */
82
- export type ScalingMode = 'shape' | 'hierarchy';
165
+ export type ScalingMode = 'local' | 'hierarchy' | 'shape';
83
166
 
84
167
  /**
85
168
  * Where a {@link RenderMode.Beam} system's quads end: a point in world space,
@@ -112,7 +195,12 @@ export type BeamEnd =
112
195
  * default named on it, the same default `fromJSON` gives a file that leaves it out.
113
196
  */
114
197
  export interface ParticleSystemParameters {
115
- /** 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
+ */
116
204
  autoDestroy?: boolean;
117
205
  /** See {@link ParticleSystem.looping}. Default true. */
118
206
  looping?: boolean;
@@ -144,6 +232,10 @@ export interface ParticleSystemParameters {
144
232
  emissionBursts?: Array<BurstParameters>;
145
233
  /** Most particles alive at once; emission past it is dropped. Leave unset for no limit. */
146
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;
147
239
  /** See {@link ParticleSystem.onlyUsedByOther}. Default false. */
148
240
  onlyUsedByOther?: boolean;
149
241
  /** See {@link ParticleSystem.behaviors}. Default none. */
@@ -169,7 +261,7 @@ export interface ParticleSystemParameters {
169
261
  /** @deprecated Set `speedFactor` in {@link rendererEmitterSettings} instead; this one wins when both are set. */
170
262
  speedFactor?: number;
171
263
  /** See {@link ParticleSystem.material}. Default none. */
172
- material?: unknown;
264
+ material?: ParticleMaterialSource | null;
173
265
  /** See {@link ParticleSystem.texture}. Unset takes the material's texture; null draws without one. */
174
266
  texture?: Texture | null;
175
267
  /** See {@link ParticleSystem.startTileIndex}. Default 0. */
@@ -230,12 +322,14 @@ export interface ParticleSystemParameters {
230
322
  playbackSpeed?: number;
231
323
  /** What the system does while off screen, see {@link CullingMode}. Default `'always'`. */
232
324
  cullingMode?: CullingMode;
233
- /** 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'`. */
234
326
  scalingMode?: ScalingMode;
235
327
  /** Seed for this system's own random generator, see {@link ParticleSystem.seed}. Default none. */
236
328
  seed?: number;
237
329
  /** Draw order within the batch, see {@link ParticleSystem.sortMode}. Default `'none'`. */
238
330
  sortMode?: SortMode;
331
+ /** Which faces a mesh particle draws, see {@link ParticleSystem.renderFace}. Default `'both'`. */
332
+ renderFace?: RenderFace;
239
333
  /** Whether particles start turned along their direction of travel, see {@link ParticleSystem.alignToDirection}. Default false. */
240
334
  alignToDirection?: boolean;
241
335
  /** Smallest on-screen size, as a fraction of the viewport height, see {@link ParticleSystem.minParticleSize}. Default 0. */
@@ -246,6 +340,8 @@ export interface ParticleSystemParameters {
246
340
  renderingGroupId?: number;
247
341
  /** Whether the scene's fog applies, see {@link ParticleSystem.applyFog}. Default true. */
248
342
  applyFog?: boolean;
343
+ /** Whether the scene's image processing applies, see {@link ParticleSystem.applyImageProcessing}. Default true. */
344
+ applyImageProcessing?: boolean;
249
345
  /** Scene the emitter node is created in. Default Babylon's last created scene. */
250
346
  scene?: Scene;
251
347
  }
@@ -255,13 +351,15 @@ export interface BurstParametersJSON {
255
351
  /** See {@link BurstParameters.time}. */
256
352
  time: number;
257
353
  /** See {@link BurstParameters.count}. */
258
- count: FunctionJSON | number;
354
+ count: ValueGeneratorJSON | number;
259
355
  /** See {@link BurstParameters.cycle}. */
260
356
  cycle: number;
261
357
  /** See {@link BurstParameters.interval}. */
262
358
  interval: number;
263
359
  /** See {@link BurstParameters.probability}. */
264
360
  probability: number;
361
+ /** `cycle`, as files before it was named wrote it; read when there is no `cycle`. */
362
+ cycleCount?: number;
265
363
  }
266
364
 
267
365
  /**
@@ -277,7 +375,9 @@ export interface BurstParametersJSON {
277
375
  export interface ParticleSystemJSONParameters {
278
376
  /** The quarks.core version line the system was written with. */
279
377
  version: string;
280
- /** 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. */
281
381
  autoDestroy?: boolean;
282
382
  /** See {@link ParticleSystem.looping}. */
283
383
  looping?: boolean;
@@ -288,36 +388,40 @@ export interface ParticleSystemJSONParameters {
288
388
  /** See {@link ParticleSystem.duration}. */
289
389
  duration?: number;
290
390
  /** See {@link ParticleSystem.startDelay}. */
291
- startDelay?: FunctionJSON;
391
+ startDelay?: ValueGeneratorJSON;
292
392
  /** The emitter shape, as its `toJSON` writes it. */
293
- shape?: any;
393
+ shape?: ShapeJSON;
294
394
  /** See {@link ParticleSystem.startLife}. */
295
- startLife?: FunctionJSON;
395
+ startLife?: ValueGeneratorJSON;
296
396
  /** See {@link ParticleSystem.startSpeed}. */
297
- startSpeed?: FunctionJSON;
397
+ startSpeed?: ValueGeneratorJSON;
298
398
  /** See {@link ParticleSystem.startRotation}. */
299
- startRotation?: FunctionJSON;
399
+ startRotation?: ValueGeneratorJSON | RotationGeneratorJSON;
300
400
  /** See {@link ParticleSystem.startSize}. */
301
- startSize?: FunctionJSON;
401
+ startSize?: ValueGeneratorJSON | Vector3GeneratorJSON;
302
402
  /** See {@link ParticleSystem.startColor}. */
303
- startColor?: FunctionJSON;
403
+ startColor?: ColorGeneratorJSON;
304
404
  /** See {@link ParticleSystem.emissionOverTime}. */
305
- emissionOverTime?: FunctionJSON;
405
+ emissionOverTime?: ValueGeneratorJSON;
306
406
  /** See {@link ParticleSystem.emissionOverDistance}. */
307
- emissionOverDistance?: FunctionJSON;
407
+ emissionOverDistance?: ValueGeneratorJSON;
308
408
  /** See {@link ParticleSystem.emissionBursts}. */
309
409
  emissionBursts?: Array<BurstParametersJSON>;
310
410
  /** See {@link ParticleSystem.maxParticles}. */
311
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];
312
416
  /** See {@link ParticleSystem.onlyUsedByOther}. */
313
417
  onlyUsedByOther?: boolean;
314
418
  /**
315
- * Trail, stretched or mesh settings; a trail's `startLength` as `FunctionJSON`. Left out when
419
+ * Trail, stretched or mesh settings; a trail's `startLength` as `ValueGeneratorJSON`. Left out when
316
420
  * the render mode has none.
317
421
  */
318
- rendererEmitterSettings?: RendererEmitterSettings;
319
- /** Uuid of the mesh particles' geometry entry. */
320
- instancingGeometry?: any;
422
+ rendererEmitterSettings?: RendererEmitterSettingsJSON;
423
+ /** Uuid of the mesh particles' geometry entry; a document made in code may hold the entry itself. */
424
+ instancingGeometry?: string | GeometryEntry;
321
425
  /** A {@link RenderMode} value. */
322
426
  renderMode?: number;
323
427
  /** See {@link ParticleSystem.renderOrder}. */
@@ -326,16 +430,20 @@ export interface ParticleSystemJSONParameters {
326
430
  renderingGroupId?: number;
327
431
  /** Written only when false. */
328
432
  applyFog?: boolean;
433
+ /** Written only when false. */
434
+ applyImageProcessing?: boolean;
329
435
  /** Written only when not 1. */
330
436
  playbackSpeed?: number;
331
437
  /** Written only when not `'always'`. */
332
438
  cullingMode?: CullingMode;
333
- /** Written only when not `'shape'`. */
439
+ /** Written only when not `'local'`. */
334
440
  scalingMode?: ScalingMode;
335
441
  /** Written only when set. */
336
442
  seed?: number;
337
443
  /** Written only when not `'none'`. */
338
444
  sortMode?: SortMode;
445
+ /** Written only when not `'both'`. */
446
+ renderFace?: RenderFace;
339
447
  /** Written only when true. */
340
448
  alignToDirection?: boolean;
341
449
  /** Written only when not 0. */
@@ -351,7 +459,7 @@ export interface ParticleSystemJSONParameters {
351
459
  /** The camera layer mask. */
352
460
  layers?: number;
353
461
  /** See {@link ParticleSystem.startTileIndex}. */
354
- startTileIndex?: FunctionJSON | number;
462
+ startTileIndex?: ValueGeneratorJSON | number;
355
463
  /** See {@link ParticleSystem.uTileCount}. */
356
464
  uTileCount?: number;
357
465
  /** See {@link ParticleSystem.vTileCount}. */
@@ -375,7 +483,7 @@ export interface ParticleSystemJSONParameters {
375
483
  /** See {@link ParticleSystemParameters.alphaTest}. */
376
484
  alphaTest?: number;
377
485
  /** Each behavior as its `toJSON` writes it. */
378
- behaviors?: Array<any>;
486
+ behaviors?: BehaviorJSON[];
379
487
  /** See {@link ParticleSystem.worldSpace}. */
380
488
  worldSpace?: boolean;
381
489
  /** `[x, y, z]`; written only when non-zero. */
@@ -392,19 +500,19 @@ export interface ParticleSystemJSONParameters {
392
500
  */
393
501
  export interface BabylonMetaData {
394
502
  /** Textures by uuid; null for one that could not be made. */
395
- textures: {[uuid: string]: Texture | null};
503
+ textures: {[uuid: string]: BaseTexture | null};
396
504
  /** Material entries by uuid. */
397
- materials: {[uuid: string]: any};
505
+ materials: {[uuid: string]: QuarksMaterialJSON};
398
506
  /** Geometry entries by uuid. */
399
- geometries: {[uuid: string]: any};
400
- /** Image entries by uuid. */
401
- images?: {[uuid: string]: any};
507
+ geometries: {[uuid: string]: GeometryEntry};
508
+ /** Not used by babylon.quarks; kept for three.js-shaped documents. */
509
+ images?: {[uuid: string]: unknown};
510
+ /** Not used by babylon.quarks; kept for three.js-shaped documents. */
511
+ shapes?: {[uuid: string]: unknown};
402
512
  /** Not used by babylon.quarks; kept for three.js-shaped documents. */
403
- shapes?: {[uuid: string]: any};
513
+ skeletons?: {[uuid: string]: unknown};
404
514
  /** Not used by babylon.quarks; kept for three.js-shaped documents. */
405
- skeletons?: {[uuid: string]: any};
515
+ animations?: {[uuid: string]: unknown};
406
516
  /** Not used by babylon.quarks; kept for three.js-shaped documents. */
407
- animations?: {[uuid: string]: any};
408
- /** Nodes by uuid, which a mesh surface shape links to. */
409
- nodes?: {[uuid: string]: any};
517
+ nodes?: {[uuid: string]: unknown};
410
518
  }
@@ -1,5 +1,8 @@
1
1
  import type {TrailSettings, TrailTextureMode} from 'quarks.core';
2
2
 
3
+ /** A trail's settings other than its start length and side axis, alike in code and in JSON. */
4
+ export type TrailOptions = Omit<TrailSettings, 'startLength' | 'sideAxis'>;
5
+
3
6
  /**
4
7
  * The normalised values of a trail's optional settings (`TrailSettings`), each read with its
5
8
  * default for a value that is unset or out of range, so the batch, the simulation, JSON and
@@ -10,13 +13,13 @@ import type {TrailSettings, TrailTextureMode} from 'quarks.core';
10
13
  export const MAX_TRAIL_TESSELLATION = 16;
11
14
 
12
15
  /** A trail's tessellation, a whole number from 0 (the default: none) to {@link MAX_TRAIL_TESSELLATION}. */
13
- export function trailTessellation(settings: TrailSettings | undefined): number {
16
+ export function trailTessellation(settings: Partial<TrailOptions> | undefined): number {
14
17
  const value = Math.round(Number(settings?.tessellation) || 0);
15
18
  return Math.min(MAX_TRAIL_TESSELLATION, Math.max(0, value));
16
19
  }
17
20
 
18
21
  /** A trail's curve tension, from 0 (the default: Catmull-Rom) to 1 (straight segments). */
19
- export function trailCurveTension(settings: TrailSettings | undefined): number {
22
+ export function trailCurveTension(settings: Partial<TrailOptions> | undefined): number {
20
23
  const value = Number(settings?.curveTension) || 0;
21
24
  return Math.min(1, Math.max(0, value));
22
25
  }
@@ -24,19 +27,19 @@ export function trailCurveTension(settings: TrailSettings | undefined): number {
24
27
  const TEXTURE_MODES: readonly TrailTextureMode[] = ['stretch', 'tile', 'distributePerSegment', 'repeatPerSegment'];
25
28
 
26
29
  /** A trail's texture mode, `'stretch'` (the default) unless set to one of the others. */
27
- export function trailTextureMode(settings: TrailSettings | undefined): TrailTextureMode {
30
+ export function trailTextureMode(settings: Partial<TrailOptions> | undefined): TrailTextureMode {
28
31
  const mode = settings?.textureMode;
29
32
  return mode !== undefined && TEXTURE_MODES.includes(mode) ? mode : 'stretch';
30
33
  }
31
34
 
32
35
  /** A trail's texture scale: a finite number above 0, else the default 1. */
33
- export function trailTextureScale(settings: TrailSettings | undefined): number {
36
+ export function trailTextureScale(settings: Partial<TrailOptions> | undefined): number {
34
37
  const value = Number(settings?.textureScale);
35
38
  return Number.isFinite(value) && value > 0 ? value : 1;
36
39
  }
37
40
 
38
41
  /** A trail's minimum distance between samples: a finite number of at least 0, else the default 0. */
39
- export function trailMinVertexDistance(settings: TrailSettings | undefined): number {
42
+ export function trailMinVertexDistance(settings: Partial<TrailOptions> | undefined): number {
40
43
  const value = Number(settings?.minVertexDistance);
41
44
  return Number.isFinite(value) && value > 0 ? value : 0;
42
45
  }
@@ -45,7 +48,7 @@ export function trailMinVertexDistance(settings: TrailSettings | undefined): num
45
48
  * How long a trail keeps a sample, in seconds: a finite number above 0, else `Infinity` (the
46
49
  * default, as 0 is: until the history is full).
47
50
  */
48
- export function trailLifetime(settings: TrailSettings | undefined): number {
51
+ export function trailLifetime(settings: Partial<TrailOptions> | undefined): number {
49
52
  const value = Number(settings?.lifetime);
50
53
  return Number.isFinite(value) && value > 0 ? value : Infinity;
51
54
  }