babylon.quarks 0.26.0 → 0.28.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 (83) hide show
  1. package/CHANGELOG.md +1194 -8
  2. package/README.md +13 -9
  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 +10108 -0
  10. package/dist/types/index.d.ts +5794 -998
  11. package/package.json +18 -7
  12. package/src/BatchedRenderer.ts +274 -156
  13. package/src/EffectPool.ts +94 -12
  14. package/src/MeshSurfaceEmitter.ts +50 -14
  15. package/src/ParticleEmitter.ts +75 -17
  16. package/src/ParticleSystem.ts +981 -2066
  17. package/src/QuarksLoader.ts +666 -194
  18. package/src/QuarksPrefab.ts +367 -73
  19. package/src/QuarksSerializer.ts +512 -0
  20. package/src/QuarksUtil.ts +77 -7
  21. package/src/SpriteBatch.ts +127 -252
  22. package/src/TrailBatch.ts +545 -348
  23. package/src/VFXBatch.ts +238 -596
  24. package/src/batchKey.ts +239 -0
  25. package/src/batchSettings.ts +546 -0
  26. package/src/effectDocumentJSON.ts +261 -0
  27. package/src/effectResources.ts +76 -0
  28. package/src/envAtlas.ts +126 -18
  29. package/src/geometryUtil.ts +1 -1
  30. package/src/index.ts +69 -35
  31. package/src/nodeId.ts +36 -0
  32. package/src/shaderProgram.ts +419 -0
  33. package/src/shaderVariants.ts +245 -0
  34. package/src/shaders/chunks/blend.ts +31 -0
  35. package/src/shaders/chunks/fog.ts +12 -5
  36. package/src/shaders/chunks/sheet.ts +111 -0
  37. package/src/shaders/chunks/soft.ts +85 -0
  38. package/src/shaders/chunks/surface.ts +12 -2
  39. package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
  40. package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
  41. package/src/shaders/particle_frag.glsl.ts +5 -22
  42. package/src/shaders/particle_frag.wgsl.ts +5 -23
  43. package/src/shaders/particle_physics_frag.glsl.ts +16 -26
  44. package/src/shaders/particle_physics_frag.wgsl.ts +14 -26
  45. package/src/shaders/particle_vert.glsl.ts +5 -27
  46. package/src/shaders/particle_vert.wgsl.ts +5 -26
  47. package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
  48. package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
  49. package/src/shaders/trail_frag.glsl.ts +13 -0
  50. package/src/shaders/trail_frag.wgsl.ts +13 -0
  51. package/src/shaders/trail_vert.glsl.ts +45 -36
  52. package/src/shaders/trail_vert.wgsl.ts +46 -40
  53. package/src/softDepth.ts +136 -0
  54. package/src/system/BehaviorPlan.ts +85 -0
  55. package/src/system/BoundsTracker.ts +181 -0
  56. package/src/system/EmissionController.ts +358 -0
  57. package/src/system/EventDispatcher.ts +60 -0
  58. package/src/system/ParticleSpawner.ts +274 -0
  59. package/src/system/SimulationClock.ts +130 -0
  60. package/src/system/geometry.ts +104 -0
  61. package/src/system/integrate.ts +428 -0
  62. package/src/system/materialResolve.ts +165 -0
  63. package/src/system/normalize.ts +100 -0
  64. package/src/system/serialization.ts +422 -0
  65. package/src/system/settingsSchema.ts +339 -0
  66. package/src/system/types.ts +449 -0
  67. package/src/textureSource.ts +30 -0
  68. package/src/trailSettings.ts +54 -0
  69. package/src/BatchedParticleRenderer.ts +0 -3
  70. package/src/materials/ParticleMaterials.ts +0 -23
  71. package/src/materials/index.ts +0 -1
  72. package/src/shaders/chunks/index.ts +0 -1
  73. package/src/shaders/chunks/register-shader-chunks.ts +0 -32
  74. package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
  75. package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
  76. package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
  77. package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
  78. package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
  79. package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
  80. package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
  81. package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
  82. package/src/shaders/index.ts +0 -9
  83. package/src/shaders/local_particle_vert.glsl.ts +0 -50
@@ -0,0 +1,339 @@
1
+ import {Constants} from '@babylonjs/core/Engines/constants';
2
+ import type {ColorGeneratorJSON, RotationGeneratorJSON, ValueGeneratorJSON, Vector3GeneratorJSON} from 'quarks.core';
3
+ import {
4
+ ColorGenerator,
5
+ ColorGeneratorFromJSON,
6
+ ConstantColor,
7
+ ConstantValue,
8
+ FunctionColorGenerator,
9
+ FunctionValueGenerator,
10
+ GeneratorFromJSON,
11
+ readFormatPart,
12
+ RotationGenerator,
13
+ ValueGenerator,
14
+ ValueGeneratorFromJSON,
15
+ Vector2,
16
+ Vector3,
17
+ Vector3Generator,
18
+ Vector4,
19
+ } from 'quarks.core';
20
+ import type {ParticleSystem} from '../ParticleSystem';
21
+ import {RenderMode} from '../VFXBatch';
22
+ import {
23
+ DEFAULT_SOFT_FAR_FADE,
24
+ normalizeBursts,
25
+ validCullingMode,
26
+ validRenderFace,
27
+ validSortMode,
28
+ validTileCount,
29
+ } from './normalize';
30
+ import type {
31
+ BurstParameters,
32
+ BurstParametersJSON,
33
+ ParticleSystemJSONParameters,
34
+ ParticleSystemParameters,
35
+ } from './types';
36
+
37
+ /**
38
+ * One setting of a {@link ParticleSystem} that is a plain value or a generator: how to read it
39
+ * from a system, what its default is, and how it goes to and from JSON and into a copy. The
40
+ * table below drives `toJSON` (which writes only values that differ from the default),
41
+ * `fromJSON` and `clone`, so a setting cannot be added to one and forgotten in another.
42
+ *
43
+ * Settings that live in the document's meta (geometry, material, texture and the texture
44
+ * effects), the render mode's own settings, the shape and the behaviors are handled by the
45
+ * callers, since they need the meta or the system itself.
46
+ */
47
+ export interface SettingDescriptor {
48
+ /** The constructor parameter. */
49
+ readonly param: keyof ParticleSystemParameters;
50
+ /** The JSON field, when it is named differently from the parameter. */
51
+ readonly json?: keyof ParticleSystemJSONParameters;
52
+ /** The current value on a system. */
53
+ get(system: ParticleSystem): unknown;
54
+ /** Whether `value` is what the constructor gives a system that leaves the setting out. */
55
+ isDefault(value: unknown): boolean;
56
+ /**
57
+ * Written even when it is the default, because the loaders of releases up to 0.27 fail on a
58
+ * file without it.
59
+ */
60
+ readonly always?: boolean;
61
+ /** The value as JSON. */
62
+ toJSON(value: unknown): unknown;
63
+ /** The constructor parameter for a JSON value that is present; undefined takes the default. */
64
+ fromJSON(json: unknown): unknown;
65
+ /** A copy that shares no mutable state with `value`. */
66
+ clone(value: unknown): unknown;
67
+ }
68
+
69
+ type Generator = {toJSON(): unknown; clone(): unknown};
70
+
71
+ /** A burst as files hold it, any field left out. */
72
+ type BurstJSON = Partial<BurstParametersJSON>;
73
+
74
+ /** A number, boolean or string compared by value. */
75
+ function plain<T>(
76
+ param: keyof ParticleSystemParameters,
77
+ get: (system: ParticleSystem) => T,
78
+ defaultValue: T,
79
+ read: (json: unknown) => T | undefined,
80
+ options: {json?: keyof ParticleSystemJSONParameters} = {}
81
+ ): SettingDescriptor {
82
+ return {
83
+ param,
84
+ json: options.json,
85
+ get,
86
+ isDefault: (value) => value === defaultValue,
87
+ toJSON: (value) => value,
88
+ fromJSON: read,
89
+ clone: (value) => value,
90
+ };
91
+ }
92
+
93
+ function number(json: unknown): number | undefined {
94
+ return typeof json === 'number' ? json : undefined;
95
+ }
96
+
97
+ /** A flipbook count: a whole number of at least 1, see {@link validTileCount}. */
98
+ function tileCount(json: unknown): number | undefined {
99
+ return typeof json === 'number' ? validTileCount(json) : undefined;
100
+ }
101
+
102
+ /** A soft fade distance: finite and at least 0, as `validSoftFade`; an invalid one takes the default. */
103
+ function softFade(json: unknown): number | undefined {
104
+ return typeof json === 'number' && Number.isFinite(json) && json >= 0 ? json : undefined;
105
+ }
106
+
107
+ /** A boolean; a number counts as true unless it is 0, as files with 0/1 flags meant it. */
108
+ function boolean(json: unknown): boolean | undefined {
109
+ if (typeof json === 'number') return json !== 0;
110
+ return typeof json === 'boolean' ? json : undefined;
111
+ }
112
+
113
+ /**
114
+ * A generator, compared with its default through its JSON. `read` turns the JSON back into one;
115
+ * a bare number is read as a constant.
116
+ */
117
+ function generator<J>(
118
+ param: keyof ParticleSystemParameters,
119
+ get: (system: ParticleSystem) => Generator,
120
+ defaultValue: () => Generator,
121
+ read: (json: J) => unknown,
122
+ always = true
123
+ ): SettingDescriptor {
124
+ const defaultJSON = JSON.stringify(defaultValue().toJSON());
125
+ return {
126
+ param,
127
+ always,
128
+ get,
129
+ isDefault: (value) => JSON.stringify((value as Generator).toJSON()) === defaultJSON,
130
+ toJSON: (value) => (value as Generator).toJSON(),
131
+ // The generator readers check the JSON they are given, and warn about what they cannot read.
132
+ fromJSON: (json) => (typeof json === 'number' ? new ConstantValue(json) : read(json as J)),
133
+ clone: (value) => (value as Generator).clone(),
134
+ };
135
+ }
136
+
137
+ function vectorJSON(value: unknown): number[] | undefined {
138
+ const v = value as {x: number; y: number; z?: number} | undefined;
139
+ if (v === undefined) return undefined;
140
+ return v.z === undefined ? [v.x, v.y] : [v.x, v.y, v.z];
141
+ }
142
+
143
+ /** Every plain and generator setting, in the order `toJSON` writes them. */
144
+ export const SETTINGS: ReadonlyArray<SettingDescriptor> = [
145
+ plain('autoDestroy', (s) => s.autoDestroy, false, boolean),
146
+ plain('looping', (s) => s.looping, true, boolean),
147
+ plain('prewarm', (s) => s.prewarm, false, boolean),
148
+ plain('prewarmTime', (s) => s.prewarmTime, 0, number),
149
+ plain('duration', (s) => s.duration, 1, number),
150
+ generator(
151
+ 'startDelay',
152
+ (s) => s.startDelay,
153
+ () => new ConstantValue(0),
154
+ ValueGeneratorFromJSON,
155
+ false
156
+ ),
157
+ generator(
158
+ 'startLife',
159
+ (s) => s.startLife,
160
+ () => new ConstantValue(5),
161
+ ValueGeneratorFromJSON
162
+ ),
163
+ generator(
164
+ 'startSpeed',
165
+ (s) => s.startSpeed,
166
+ () => new ConstantValue(0),
167
+ ValueGeneratorFromJSON
168
+ ),
169
+ generator(
170
+ 'startRotation',
171
+ (s) => s.startRotation,
172
+ () => new ConstantValue(0),
173
+ (json: ValueGeneratorJSON | RotationGeneratorJSON) =>
174
+ GeneratorFromJSON(json) as RotationGenerator | ValueGenerator | FunctionValueGenerator
175
+ ),
176
+ generator(
177
+ 'startSize',
178
+ (s) => s.startSize,
179
+ () => new ConstantValue(1),
180
+ (json: ValueGeneratorJSON | Vector3GeneratorJSON) =>
181
+ GeneratorFromJSON(json) as Vector3Generator | ValueGenerator | FunctionValueGenerator
182
+ ),
183
+ generator(
184
+ 'startColor',
185
+ (s) => s.startColor,
186
+ () => new ConstantColor(new Vector4(1, 1, 1, 1)),
187
+ (json: ColorGeneratorJSON) => ColorGeneratorFromJSON(json) as ColorGenerator | FunctionColorGenerator
188
+ ),
189
+ generator(
190
+ 'emissionOverTime',
191
+ (s) => s.emissionOverTime,
192
+ () => new ConstantValue(10),
193
+ ValueGeneratorFromJSON
194
+ ),
195
+ generator(
196
+ 'emissionOverDistance',
197
+ (s) => s.emissionOverDistance,
198
+ () => new ConstantValue(0),
199
+ ValueGeneratorFromJSON
200
+ ),
201
+ {
202
+ param: 'emissionBursts',
203
+ get: (s) => s.emissionBursts,
204
+ isDefault: (value) => (value as BurstParameters[]).length === 0,
205
+ toJSON: (value) =>
206
+ (value as BurstParameters[]).map((burst) => ({
207
+ time: burst.time,
208
+ count: burst.count.toJSON(),
209
+ probability: burst.probability,
210
+ interval: burst.interval,
211
+ cycle: burst.cycle,
212
+ })),
213
+ fromJSON: (json) =>
214
+ Array.isArray(json)
215
+ ? normalizeBursts(
216
+ json.map((burst: BurstJSON) => ({
217
+ time: burst.time,
218
+ count:
219
+ typeof burst.count === 'number'
220
+ ? new ConstantValue(burst.count)
221
+ : ValueGeneratorFromJSON(burst.count),
222
+ probability: burst.probability,
223
+ interval: burst.interval,
224
+ cycle: burst.cycle ?? burst.cycleCount,
225
+ }))
226
+ )
227
+ : undefined,
228
+ clone: (value) => (value as BurstParameters[]).map((burst) => ({...burst, count: burst.count.clone()})),
229
+ },
230
+ plain('maxParticles', (s) => s.maxParticles, undefined, number),
231
+ plain('onlyUsedByOther', (s) => s.onlyUsedByOther, false, boolean),
232
+ plain('renderMode', (s) => s.renderMode, RenderMode.BillBoard, number),
233
+ plain('renderOrder', (s) => s.renderOrder, 0, number),
234
+ plain('renderingGroupId', (s) => s.renderingGroupId, 0, number),
235
+ plain(
236
+ 'applyFog',
237
+ (s) => s.applyFog,
238
+ true,
239
+ (json) => json !== false
240
+ ),
241
+ plain('playbackSpeed', (s) => s.playbackSpeed, 1, number),
242
+ plain('cullingMode', (s) => s.cullingMode, 'always', validCullingMode),
243
+ plain(
244
+ 'scalingMode',
245
+ (s) => s.scalingMode,
246
+ 'shape',
247
+ (json) => (json === 'hierarchy' ? 'hierarchy' : 'shape')
248
+ ),
249
+ plain('seed', (s) => s.seed, undefined, number),
250
+ plain('sortMode', (s) => s.sortMode, 'none', validSortMode),
251
+ plain('renderFace', (s) => s.renderFace, 'both', validRenderFace),
252
+ plain(
253
+ 'alignToDirection',
254
+ (s) => s.alignToDirection,
255
+ false,
256
+ (json) => json === true
257
+ ),
258
+ plain('minParticleSize', (s) => s.minParticleSize, 0, number),
259
+ plain('maxParticleSize', (s) => s.maxParticleSize, Infinity, number),
260
+ plain('layerMask', (s) => s.layers.mask, 0x0fffffff, number, {json: 'layers'}),
261
+ generator(
262
+ 'startTileIndex',
263
+ (s) => s.startTileIndex,
264
+ () => new ConstantValue(0),
265
+ ValueGeneratorFromJSON
266
+ ),
267
+ plain('uTileCount', (s) => s.uTileCount, 1, tileCount),
268
+ plain('vTileCount', (s) => s.vTileCount, 1, tileCount),
269
+ plain('blendTiles', (s) => s.blendTiles, false, boolean),
270
+ plain('softParticles', (s) => s.softParticles, false, boolean),
271
+ plain('softFarFade', (s) => s.softFarFade, DEFAULT_SOFT_FAR_FADE, softFade),
272
+ plain('softNearFade', (s) => s.softNearFade, 0, softFade),
273
+ plain('blendMode', (s) => s.blending, Constants.ALPHA_ADD, number, {json: 'blending'}),
274
+ plain('transparent', (s) => s.transparent, true, boolean),
275
+ plain('depthTest', (s) => s.depthTest, true, boolean),
276
+ plain('depthWrite', (s) => s.depthWrite, false, boolean),
277
+ plain('alphaTest', (s) => s.alphaTest, 0, number),
278
+ plain('worldSpace', (s) => s.worldSpace, false, boolean),
279
+ {
280
+ param: 'pivot',
281
+ get: (s) => s.pivot,
282
+ isDefault: (value) => value === undefined,
283
+ toJSON: vectorJSON,
284
+ fromJSON: (json) => (Array.isArray(json) ? new Vector3(json[0] ?? 0, json[1] ?? 0, json[2] ?? 0) : undefined),
285
+ clone: (value) => (value as Vector3 | undefined)?.clone(),
286
+ },
287
+ {
288
+ param: 'flip',
289
+ get: (s) => s.flip,
290
+ isDefault: (value) => value === undefined,
291
+ toJSON: vectorJSON,
292
+ fromJSON: (json) => (Array.isArray(json) ? new Vector2(json[0] ?? 0, json[1] ?? 0) : undefined),
293
+ clone: (value) => (value as Vector2 | undefined)?.clone(),
294
+ },
295
+ ];
296
+
297
+ /** The JSON field a setting is written under. */
298
+ export function settingJSONKey(setting: SettingDescriptor): string {
299
+ return setting.json ?? setting.param;
300
+ }
301
+
302
+ /** The settings of `system` that differ from their defaults, as JSON fields. */
303
+ export function settingsToJSON(system: ParticleSystem): Record<string, unknown> {
304
+ const json: Record<string, unknown> = {};
305
+ for (const setting of SETTINGS) {
306
+ const value = setting.get(system);
307
+ if (setting.always || !setting.isDefault(value)) {
308
+ json[settingJSONKey(setting)] = setting.toJSON(value);
309
+ }
310
+ }
311
+ return json;
312
+ }
313
+
314
+ /** Constructor parameters for the settings a document has; missing ones are left out, so they take the default. */
315
+ export function settingsFromJSON(json: ParticleSystemJSONParameters): Partial<ParticleSystemParameters> {
316
+ const parameters: Record<string, unknown> = {};
317
+ const fields = json as unknown as Record<string, unknown>;
318
+ for (const setting of SETTINGS) {
319
+ const value = fields[settingJSONKey(setting)];
320
+ if (value === undefined || value === null) {
321
+ continue;
322
+ }
323
+ const key = settingJSONKey(setting);
324
+ const read = readFormatPart(key, () => setting.fromJSON(value));
325
+ if (read !== undefined) {
326
+ parameters[setting.param] = read;
327
+ }
328
+ }
329
+ return parameters as Partial<ParticleSystemParameters>;
330
+ }
331
+
332
+ /** Constructor parameters that copy every setting of `system`. */
333
+ export function settingsForClone(system: ParticleSystem): Partial<ParticleSystemParameters> {
334
+ const parameters: Record<string, unknown> = {};
335
+ for (const setting of SETTINGS) {
336
+ parameters[setting.param] = setting.clone(setting.get(system));
337
+ }
338
+ return parameters as Partial<ParticleSystemParameters>;
339
+ }