babylon.quarks 0.27.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.
- package/CHANGELOG.md +425 -0
- package/README.md +3 -1
- package/dist/babylon.quarks.cjs +1 -1
- package/dist/babylon.quarks.cjs.map +1 -1
- package/dist/babylon.quarks.esm.js +1 -1
- package/dist/babylon.quarks.esm.js.map +1 -1
- package/dist/babylon.quarks.umd.min.js +1 -1
- package/dist/babylon.quarks.umd.min.js.map +1 -1
- package/dist/types/index.d.cts +4256 -831
- package/dist/types/index.d.ts +4256 -831
- package/package.json +7 -2
- package/src/BatchedRenderer.ts +93 -78
- package/src/EffectPool.ts +91 -12
- package/src/MeshSurfaceEmitter.ts +24 -13
- package/src/ParticleEmitter.ts +31 -3
- package/src/ParticleSystem.ts +92 -25
- package/src/QuarksLoader.ts +289 -171
- package/src/QuarksPrefab.ts +301 -94
- package/src/QuarksSerializer.ts +43 -15
- package/src/QuarksUtil.ts +64 -9
- package/src/SpriteBatch.ts +21 -6
- package/src/TrailBatch.ts +12 -6
- package/src/VFXBatch.ts +70 -528
- package/src/batchKey.ts +4 -2
- package/src/batchSettings.ts +546 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +21 -24
- package/src/envAtlas.ts +8 -2
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +39 -4
- package/src/nodeId.ts +25 -2
- package/src/shaderProgram.ts +110 -16
- package/src/shaderVariants.ts +24 -4
- package/src/shaders/chunks/blend.ts +31 -0
- package/src/shaders/chunks/fog.ts +8 -3
- package/src/shaders/chunks/soft.ts +6 -5
- package/src/shaders/chunks/surface.ts +2 -2
- package/src/shaders/particle_frag.glsl.ts +3 -1
- package/src/shaders/particle_frag.wgsl.ts +2 -0
- package/src/shaders/particle_physics_frag.glsl.ts +5 -2
- package/src/shaders/particle_physics_frag.wgsl.ts +2 -0
- package/src/shaders/trail_frag.glsl.ts +4 -1
- package/src/shaders/trail_frag.wgsl.ts +2 -0
- package/src/system/EmissionController.ts +2 -4
- package/src/system/EventDispatcher.ts +26 -10
- package/src/system/geometry.ts +22 -29
- package/src/system/materialResolve.ts +56 -7
- package/src/system/normalize.ts +6 -1
- package/src/system/serialization.ts +54 -33
- package/src/system/settingsSchema.ts +32 -10
- package/src/system/types.ts +67 -28
- package/src/trailSettings.ts +9 -6
package/src/ParticleEmitter.ts
CHANGED
|
@@ -2,6 +2,29 @@ import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
|
2
2
|
import {Scene} from '@babylonjs/core/scene';
|
|
3
3
|
import {IParticleSystem, Matrix4, SerializationOptions} from 'quarks.core';
|
|
4
4
|
import {quarksNodeId} from './nodeId';
|
|
5
|
+
import type {BabylonMetaData} from './system/types';
|
|
6
|
+
|
|
7
|
+
/** A {@link ParticleEmitter} as its `toJSON` writes it: the node, its transform and its system. */
|
|
8
|
+
export interface ParticleEmitterJSON {
|
|
9
|
+
/** The node's id in the effect. */
|
|
10
|
+
uuid: string;
|
|
11
|
+
/** Always `'ParticleEmitter'`. */
|
|
12
|
+
type: 'ParticleEmitter';
|
|
13
|
+
/** The node's name. */
|
|
14
|
+
name: string;
|
|
15
|
+
/** `[x, y, z]`. */
|
|
16
|
+
position: number[];
|
|
17
|
+
/** Euler angles `[x, y, z]` in radians. */
|
|
18
|
+
rotation: number[];
|
|
19
|
+
/** `[x, y, z, w]`, when the node has a rotation quaternion. */
|
|
20
|
+
quaternion?: number[];
|
|
21
|
+
/** `[x, y, z]`. */
|
|
22
|
+
scale: number[];
|
|
23
|
+
/** The node's own enabled flag. */
|
|
24
|
+
visible: boolean;
|
|
25
|
+
/** The system's JSON, as its `toJSON` writes it. */
|
|
26
|
+
ps: unknown;
|
|
27
|
+
}
|
|
5
28
|
|
|
6
29
|
/**
|
|
7
30
|
* Above 0 while a particle system disposes its own emitter (`ParticleSystem.dispose()`, which
|
|
@@ -117,8 +140,13 @@ export class ParticleEmitter extends TransformNode {
|
|
|
117
140
|
return clonedEmitter;
|
|
118
141
|
}
|
|
119
142
|
|
|
120
|
-
/**
|
|
121
|
-
|
|
143
|
+
/**
|
|
144
|
+
* The node and its system in effect-file form: one entry of an effect file's `object` tree,
|
|
145
|
+
* without its children. Geometry, materials and textures go into `meta` when it is given.
|
|
146
|
+
* `visible` is the node's own enabled flag, not the one it inherits from its parents. To write
|
|
147
|
+
* a whole effect, with its children and resources, use `serializeEffect`.
|
|
148
|
+
*/
|
|
149
|
+
toJSON(meta?: BabylonMetaData, options: SerializationOptions = {}): ParticleEmitterJSON {
|
|
122
150
|
return {
|
|
123
151
|
uuid: quarksNodeId(this),
|
|
124
152
|
type: 'ParticleEmitter',
|
|
@@ -134,7 +162,7 @@ export class ParticleEmitter extends TransformNode {
|
|
|
134
162
|
]
|
|
135
163
|
: undefined,
|
|
136
164
|
scale: [this.scaling.x, this.scaling.y, this.scaling.z],
|
|
137
|
-
visible: this.isEnabled(),
|
|
165
|
+
visible: this.isEnabled(false),
|
|
138
166
|
ps: this.system.toJSON(meta, options),
|
|
139
167
|
};
|
|
140
168
|
}
|
package/src/ParticleSystem.ts
CHANGED
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
ParticleSystemEventType,
|
|
24
24
|
Quaternion,
|
|
25
25
|
RandomSource,
|
|
26
|
+
readFormatPart,
|
|
26
27
|
RendererEmitterSettings,
|
|
27
28
|
RotationGenerator,
|
|
28
29
|
SerializationOptions,
|
|
@@ -63,7 +64,7 @@ import {
|
|
|
63
64
|
runBehaviorSteps,
|
|
64
65
|
snapshotPreviousState,
|
|
65
66
|
} from './system/integrate';
|
|
66
|
-
import {liveAtlas, MaterialOverrides, resolveMaterialSettings} from './system/materialResolve';
|
|
67
|
+
import {liveAtlas, MaterialOverrides, ParticleMaterialSource, resolveMaterialSettings} from './system/materialResolve';
|
|
67
68
|
import {
|
|
68
69
|
DEFAULT_SOFT_FAR_FADE,
|
|
69
70
|
nonZeroFlip,
|
|
@@ -74,6 +75,7 @@ import {
|
|
|
74
75
|
validCullingMode,
|
|
75
76
|
validLighting,
|
|
76
77
|
validMaxParticles,
|
|
78
|
+
validRenderFace,
|
|
77
79
|
validSoftFade,
|
|
78
80
|
validSortMode,
|
|
79
81
|
validTileCount,
|
|
@@ -101,6 +103,7 @@ import {
|
|
|
101
103
|
ParticleGradientMap,
|
|
102
104
|
ParticleUVDistortion,
|
|
103
105
|
ParticleUVDistortionOptions,
|
|
106
|
+
RenderFace,
|
|
104
107
|
RenderMode,
|
|
105
108
|
SortMode,
|
|
106
109
|
TextureUV,
|
|
@@ -295,7 +298,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
295
298
|
private readonly emission = new EmissionController(this, this.spawner);
|
|
296
299
|
private readonly events = new EventDispatcher();
|
|
297
300
|
private readonly layerMaskProxy: {mask: number};
|
|
298
|
-
private materialRef:
|
|
301
|
+
private materialRef: ParticleMaterialSource | null = null;
|
|
299
302
|
private sceneRef: Scene | undefined;
|
|
300
303
|
private qualityFactor = 1;
|
|
301
304
|
private destroyed = false;
|
|
@@ -395,11 +398,11 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
395
398
|
* is not drawn with; particles always use quarks' own shaders. Null leaves the settings as they
|
|
396
399
|
* are.
|
|
397
400
|
*/
|
|
398
|
-
get material():
|
|
401
|
+
get material(): ParticleMaterialSource | null {
|
|
399
402
|
return this.materialRef;
|
|
400
403
|
}
|
|
401
404
|
|
|
402
|
-
set material(material:
|
|
405
|
+
set material(material: ParticleMaterialSource | null) {
|
|
403
406
|
this.materialRef = material;
|
|
404
407
|
this.applyMaterialSettings(material);
|
|
405
408
|
}
|
|
@@ -473,6 +476,21 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
473
476
|
this.neededToUpdateRender = true;
|
|
474
477
|
}
|
|
475
478
|
|
|
479
|
+
/**
|
|
480
|
+
* Which faces of a mesh particle are drawn (Unity's Render Face): `'both'` by default,
|
|
481
|
+
* `'front'` to cull the back faces of a closed mesh, `'back'` to see only its inside. Applies
|
|
482
|
+
* to the Mesh render mode; the other modes draw both faces. See {@link RenderFace}. Saved in
|
|
483
|
+
* the effect JSON only when not `'both'`.
|
|
484
|
+
*/
|
|
485
|
+
get renderFace(): RenderFace {
|
|
486
|
+
return this.rendererSettings.renderFace ?? 'both';
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
set renderFace(face: RenderFace) {
|
|
490
|
+
this.rendererSettings.renderFace = validRenderFace(face);
|
|
491
|
+
this.neededToUpdateRender = true;
|
|
492
|
+
}
|
|
493
|
+
|
|
476
494
|
/**
|
|
477
495
|
* Smallest size a billboard or stretched particle is drawn at, as a
|
|
478
496
|
* fraction of the viewport height (Unity's Min Particle Size); 0 by
|
|
@@ -946,6 +964,24 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
946
964
|
|
|
947
965
|
private playbackSpeedValue = 1;
|
|
948
966
|
|
|
967
|
+
/**
|
|
968
|
+
* A runtime multiplier on {@link playbackSpeed} that is not part of the effect: what a
|
|
969
|
+
* timeline, a slow-motion moment or a game's pause menu sets on a system it plays, like
|
|
970
|
+
* Unreal's Custom Time Dilation or Unity's `VisualEffect.playRate`. The system runs at
|
|
971
|
+
* `playbackSpeed * timeDilation`. 1 by default; never saved, cloned or loaded. A negative,
|
|
972
|
+
* NaN or infinite value is read as 0. A `QuarksPrefab` sets it to its `timeScale` on the systems of
|
|
973
|
+
* its tracks.
|
|
974
|
+
*/
|
|
975
|
+
get timeDilation(): number {
|
|
976
|
+
return this.timeDilationValue;
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
set timeDilation(dilation: number) {
|
|
980
|
+
this.timeDilationValue = dilation > 0 && dilation < Infinity ? dilation : 0;
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
private timeDilationValue = 1;
|
|
984
|
+
|
|
949
985
|
/**
|
|
950
986
|
* Runtime multiplier on how many particles the system emits: bursts,
|
|
951
987
|
* emission over time and over distance alike. Unlike
|
|
@@ -1125,6 +1161,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1125
1161
|
layerMask: parameters.layerMask ?? 0x0fffffff,
|
|
1126
1162
|
pivot: nonZeroPivot(parameters.pivot),
|
|
1127
1163
|
sortMode: validSortMode(parameters.sortMode),
|
|
1164
|
+
renderFace: validRenderFace(parameters.renderFace),
|
|
1128
1165
|
minParticleSize: Math.max(0, parameters.minParticleSize ?? 0),
|
|
1129
1166
|
maxParticleSize: parameters.maxParticleSize ?? Infinity,
|
|
1130
1167
|
renderingGroupId: parameters.renderingGroupId ?? 0,
|
|
@@ -1184,7 +1221,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1184
1221
|
* ran) while the system still uses it, drops it and builds a new one if the cube is still alive,
|
|
1185
1222
|
* so the batch never samples a disposed texture.
|
|
1186
1223
|
*/
|
|
1187
|
-
private watchEnvAtlas(atlas: BaseTexture | null, material:
|
|
1224
|
+
private watchEnvAtlas(atlas: BaseTexture | null, material: ParticleMaterialSource | null): void {
|
|
1188
1225
|
atlas?.onDisposeObservable.addOnce(() => {
|
|
1189
1226
|
if (this.destroyed || this.rendererSettings.reflectionAtlas !== atlas) {
|
|
1190
1227
|
return;
|
|
@@ -1211,7 +1248,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1211
1248
|
});
|
|
1212
1249
|
}
|
|
1213
1250
|
|
|
1214
|
-
private applyMaterialSettings(material:
|
|
1251
|
+
private applyMaterialSettings(material: ParticleMaterialSource | null, overrides: MaterialOverrides = {}) {
|
|
1215
1252
|
/* istanbul ignore if -- rendererSettings is always assigned in the constructor before any code path can call this private method */
|
|
1216
1253
|
if (!this.rendererSettings) {
|
|
1217
1254
|
return;
|
|
@@ -1261,10 +1298,30 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1261
1298
|
}
|
|
1262
1299
|
|
|
1263
1300
|
/**
|
|
1264
|
-
*
|
|
1265
|
-
*
|
|
1301
|
+
* Plays the system, like Unity's `Play`: a paused or stopped system resumes from where it is,
|
|
1302
|
+
* and one that has finished ({@link isFinished}) starts over from the beginning, as
|
|
1303
|
+
* {@link restart} does. A playing system that has not finished is left as it is. To undo a
|
|
1304
|
+
* {@link pause} without replaying a system that had finished, use {@link resume}.
|
|
1266
1305
|
*/
|
|
1267
1306
|
play(): void {
|
|
1307
|
+
if (this.destroyed) {
|
|
1308
|
+
return;
|
|
1309
|
+
}
|
|
1310
|
+
if (this.isFinished()) {
|
|
1311
|
+
this.restart();
|
|
1312
|
+
}
|
|
1313
|
+
this.resume();
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1316
|
+
/**
|
|
1317
|
+
* Undoes {@link pause}: the system goes on from where it is, and one that has finished stays
|
|
1318
|
+
* finished. For a pause button over a whole scene, where {@link play} would replay the
|
|
1319
|
+
* effects that had already ended.
|
|
1320
|
+
*/
|
|
1321
|
+
resume(): void {
|
|
1322
|
+
if (this.destroyed) {
|
|
1323
|
+
return;
|
|
1324
|
+
}
|
|
1268
1325
|
this.paused = false;
|
|
1269
1326
|
// A move made while paused is a placement, not motion to spread the next frame over.
|
|
1270
1327
|
this.previousEmitterMatrix = undefined;
|
|
@@ -1406,12 +1463,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1406
1463
|
// Bursts edited in place since the last play are put back in time order and in range.
|
|
1407
1464
|
this.emissionBursts.forEach((burst) => Object.assign(burst, normalizeBurst(burst)));
|
|
1408
1465
|
this.emissionBursts.sort((a, b) => a.time - b.time);
|
|
1409
|
-
this.behaviors.forEach((behavior) =>
|
|
1410
|
-
behavior.reset();
|
|
1411
|
-
if (behavior instanceof EmitSubParticleSystem) {
|
|
1412
|
-
behavior.clearEmissions();
|
|
1413
|
-
}
|
|
1414
|
-
});
|
|
1466
|
+
this.behaviors.forEach((behavior) => behavior.reset());
|
|
1415
1467
|
// A sweeping shape (Loop, PingPong) starts its arc over, so the replay is the same.
|
|
1416
1468
|
this.emitterShape.reset?.();
|
|
1417
1469
|
this.emitEnded = false;
|
|
@@ -1439,7 +1491,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1439
1491
|
*/
|
|
1440
1492
|
update(delta: number, stepDeadline = Infinity, catchUpDeadline?: number) {
|
|
1441
1493
|
if (this.paused || this.destroyed) return;
|
|
1442
|
-
const speed = this.playbackSpeed;
|
|
1494
|
+
const speed = this.playbackSpeed * this.timeDilationValue;
|
|
1443
1495
|
delta = SimulationClock.frameSeconds(delta, speed);
|
|
1444
1496
|
|
|
1445
1497
|
if (this.culled) {
|
|
@@ -1647,9 +1699,9 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1647
1699
|
* per-instance data, Unreal's user parameters). With {@link erosion},
|
|
1648
1700
|
* alpha is the erosion level, so this wipes the shape in and out. The
|
|
1649
1701
|
* particles' own colours are untouched, so clearing it restores them, and
|
|
1650
|
-
* systems with different tints still batch together.
|
|
1651
|
-
*
|
|
1652
|
-
*
|
|
1702
|
+
* systems with different tints still batch together. Its alpha fades the
|
|
1703
|
+
* particles in every blend mode that blends by coverage (see
|
|
1704
|
+
* {@link blendCategory}). Unset (the default): none.
|
|
1653
1705
|
* Not saved in the effect JSON; copied by {@link clone}.
|
|
1654
1706
|
*/
|
|
1655
1707
|
tint?: Vector4;
|
|
@@ -1799,9 +1851,15 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1799
1851
|
/** Re-points every particle at the store, for {@link snapshotPreviousState}. */
|
|
1800
1852
|
private readonly rebindParticles = (): void => this.spawner.rebind();
|
|
1801
1853
|
|
|
1854
|
+
/** The one `particleDied` event, filled in for each death so a death allocates nothing. */
|
|
1855
|
+
private readonly particleDiedEvent: ParticleSystemEvent = {type: 'particleDied', particleSystem: this};
|
|
1856
|
+
|
|
1802
1857
|
/** Fires `particleDied` for a particle the step retired. */
|
|
1803
1858
|
private readonly fireParticleDied = (particle: Particle): void => {
|
|
1804
|
-
|
|
1859
|
+
const event = this.particleDiedEvent;
|
|
1860
|
+
event.particle = particle;
|
|
1861
|
+
this.events.fire(event);
|
|
1862
|
+
event.particle = undefined;
|
|
1805
1863
|
};
|
|
1806
1864
|
|
|
1807
1865
|
private notifyFinished(): void {
|
|
@@ -1850,10 +1908,11 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1850
1908
|
): ParticleSystem {
|
|
1851
1909
|
const ps = new ParticleSystem(particleSystemParametersFromJSON(json, meta, scene));
|
|
1852
1910
|
ps.behaviors = (json.behaviors ?? [])
|
|
1853
|
-
.map((behaviorJson) => {
|
|
1854
|
-
const behavior = BehaviorFromJSON(behaviorJson, ps);
|
|
1855
|
-
|
|
1856
|
-
|
|
1911
|
+
.map((behaviorJson, index) => {
|
|
1912
|
+
const behavior = readFormatPart(`behaviors[${index}]`, () => BehaviorFromJSON(behaviorJson, ps));
|
|
1913
|
+
const target = (behaviorJson as {subParticleSystem?: unknown}).subParticleSystem;
|
|
1914
|
+
if (behavior && behavior.type === 'EmitSubParticleSystem' && typeof target === 'string') {
|
|
1915
|
+
dependencies[target] = behavior;
|
|
1857
1916
|
}
|
|
1858
1917
|
return behavior;
|
|
1859
1918
|
})
|
|
@@ -1880,13 +1939,21 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1880
1939
|
* Calls `callback` on each `event`: `'emitEnd'` when emission ends, `'finished'` when the last
|
|
1881
1940
|
* particle of an ended system dies, `'particleDied'` per particle (with `event.particle`), and
|
|
1882
1941
|
* `'destroy'` on dispose.
|
|
1942
|
+
*
|
|
1943
|
+
* A `'particleDied'` event and the particle on it are reused: read what you need inside the
|
|
1944
|
+
* callback and keep neither, since the particle's slot goes to the next one spawned. In trail
|
|
1945
|
+
* mode a particle dies when its trail is gone, so with a trail lifetime the event comes after
|
|
1946
|
+
* the head stopped, once the last sample has faded.
|
|
1947
|
+
*
|
|
1948
|
+
* A callback added or removed while an event is firing takes effect from the next event; the
|
|
1949
|
+
* callbacks of the one firing are all called.
|
|
1883
1950
|
*/
|
|
1884
1951
|
addEventListener(event: ParticleSystemEventType, callback: (event: ParticleSystemEvent) => void): void {
|
|
1885
1952
|
this.events.add(event, callback);
|
|
1886
1953
|
}
|
|
1887
1954
|
|
|
1888
|
-
/** Removes every callback registered for `event
|
|
1889
|
-
removeAllEventListeners(event
|
|
1955
|
+
/** Removes every callback registered for `event`, or every callback of every event when it is left out. */
|
|
1956
|
+
removeAllEventListeners(event?: ParticleSystemEventType): void {
|
|
1890
1957
|
this.events.removeAll(event);
|
|
1891
1958
|
}
|
|
1892
1959
|
|