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.
- package/CHANGELOG.md +1194 -8
- package/README.md +13 -9
- 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 +10108 -0
- package/dist/types/index.d.ts +5794 -998
- package/package.json +18 -7
- package/src/BatchedRenderer.ts +274 -156
- package/src/EffectPool.ts +94 -12
- package/src/MeshSurfaceEmitter.ts +50 -14
- package/src/ParticleEmitter.ts +75 -17
- package/src/ParticleSystem.ts +981 -2066
- package/src/QuarksLoader.ts +666 -194
- package/src/QuarksPrefab.ts +367 -73
- package/src/QuarksSerializer.ts +512 -0
- package/src/QuarksUtil.ts +77 -7
- package/src/SpriteBatch.ts +127 -252
- package/src/TrailBatch.ts +545 -348
- package/src/VFXBatch.ts +238 -596
- package/src/batchKey.ts +239 -0
- package/src/batchSettings.ts +546 -0
- package/src/effectDocumentJSON.ts +261 -0
- package/src/effectResources.ts +76 -0
- package/src/envAtlas.ts +126 -18
- package/src/geometryUtil.ts +1 -1
- package/src/index.ts +69 -35
- package/src/nodeId.ts +36 -0
- package/src/shaderProgram.ts +419 -0
- package/src/shaderVariants.ts +245 -0
- package/src/shaders/chunks/blend.ts +31 -0
- package/src/shaders/chunks/fog.ts +12 -5
- package/src/shaders/chunks/sheet.ts +111 -0
- package/src/shaders/chunks/soft.ts +85 -0
- package/src/shaders/chunks/surface.ts +12 -2
- package/src/shaders/local_particle_physics_vert.glsl.ts +14 -28
- package/src/shaders/local_particle_physics_vert.wgsl.ts +16 -29
- package/src/shaders/particle_frag.glsl.ts +5 -22
- package/src/shaders/particle_frag.wgsl.ts +5 -23
- package/src/shaders/particle_physics_frag.glsl.ts +16 -26
- package/src/shaders/particle_physics_frag.wgsl.ts +14 -26
- package/src/shaders/particle_vert.glsl.ts +5 -27
- package/src/shaders/particle_vert.wgsl.ts +5 -26
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +7 -33
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +7 -33
- package/src/shaders/trail_frag.glsl.ts +13 -0
- package/src/shaders/trail_frag.wgsl.ts +13 -0
- package/src/shaders/trail_vert.glsl.ts +45 -36
- package/src/shaders/trail_vert.wgsl.ts +46 -40
- package/src/softDepth.ts +136 -0
- package/src/system/BehaviorPlan.ts +85 -0
- package/src/system/BoundsTracker.ts +181 -0
- package/src/system/EmissionController.ts +358 -0
- package/src/system/EventDispatcher.ts +60 -0
- package/src/system/ParticleSpawner.ts +274 -0
- package/src/system/SimulationClock.ts +130 -0
- package/src/system/geometry.ts +104 -0
- package/src/system/integrate.ts +428 -0
- package/src/system/materialResolve.ts +165 -0
- package/src/system/normalize.ts +100 -0
- package/src/system/serialization.ts +422 -0
- package/src/system/settingsSchema.ts +339 -0
- package/src/system/types.ts +449 -0
- package/src/textureSource.ts +30 -0
- package/src/trailSettings.ts +54 -0
- package/src/BatchedParticleRenderer.ts +0 -3
- package/src/materials/ParticleMaterials.ts +0 -23
- package/src/materials/index.ts +0 -1
- package/src/shaders/chunks/index.ts +0 -1
- package/src/shaders/chunks/register-shader-chunks.ts +0 -32
- package/src/shaders/chunks/soft_fragment.glsl.ts +0 -13
- package/src/shaders/chunks/soft_pars_fragment.glsl.ts +0 -20
- package/src/shaders/chunks/soft_pars_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/soft_vertex.glsl.ts +0 -6
- package/src/shaders/chunks/tile_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_fragment.glsl.ts +0 -9
- package/src/shaders/chunks/tile_pars_vertex.glsl.ts +0 -28
- package/src/shaders/chunks/tile_vertex.glsl.ts +0 -18
- package/src/shaders/index.ts +0 -9
- package/src/shaders/local_particle_vert.glsl.ts +0 -50
package/src/EffectPool.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import {Vector3} from '@babylonjs/core/Maths/math.vector';
|
|
1
|
+
import {Quaternion, Vector3} from '@babylonjs/core/Maths/math.vector';
|
|
2
2
|
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
3
3
|
import {Node} from '@babylonjs/core/node';
|
|
4
4
|
import {BatchedRenderer} from './BatchedRenderer';
|
|
5
|
+
import {disposeEffect} from './effectResources';
|
|
5
6
|
import {ParticleSystem} from './ParticleSystem';
|
|
6
7
|
import {QuarksUtil} from './QuarksUtil';
|
|
7
8
|
|
|
9
|
+
/** Options for a new {@link EffectPool}. */
|
|
8
10
|
export interface EffectPoolOptions {
|
|
9
11
|
/** The renderer every copy the pool makes is added to. */
|
|
10
12
|
renderer: BatchedRenderer;
|
|
@@ -14,6 +16,31 @@ export interface EffectPoolOptions {
|
|
|
14
16
|
maxSize?: number;
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
/**
|
|
20
|
+
* Where {@link EffectPool.acquire} puts a copy. Each part left out is the template's own, as the
|
|
21
|
+
* copy was made, so nothing carries over from the copy's previous use.
|
|
22
|
+
*/
|
|
23
|
+
export interface EffectPlacement {
|
|
24
|
+
/** Local position under {@link parent}. */
|
|
25
|
+
position?: Vector3;
|
|
26
|
+
/** Local rotation as Euler angles in radians; ignored when {@link rotationQuaternion} is set. */
|
|
27
|
+
rotation?: Vector3;
|
|
28
|
+
/** Local rotation as a quaternion, for a direction: a hit normal, a muzzle's facing. */
|
|
29
|
+
rotationQuaternion?: Quaternion;
|
|
30
|
+
/** Local scale. */
|
|
31
|
+
scaling?: Vector3;
|
|
32
|
+
/** The node the copy is parented to; none (world space) by default. */
|
|
33
|
+
parent?: Node | null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The local transform a copy is put back to on every acquire. */
|
|
37
|
+
interface RestTransform {
|
|
38
|
+
position: Vector3;
|
|
39
|
+
rotation: Vector3;
|
|
40
|
+
rotationQuaternion: Quaternion | null;
|
|
41
|
+
scaling: Vector3;
|
|
42
|
+
}
|
|
43
|
+
|
|
17
44
|
/**
|
|
18
45
|
* A pool of copies of one effect, for effects played many times over: hits,
|
|
19
46
|
* footsteps, muzzle flashes, pickups.
|
|
@@ -27,6 +54,10 @@ export interface EffectPoolOptions {
|
|
|
27
54
|
* Idle copies are parked disabled, which costs the renderer nothing, and keep
|
|
28
55
|
* their place in its batches, so acquiring one allocates nothing.
|
|
29
56
|
*
|
|
57
|
+
* A copy disposed from outside (by its own `dispose()`, or with a parent it
|
|
58
|
+
* was acquired under) leaves the pool on its own, and the pool makes a new
|
|
59
|
+
* one when it needs it.
|
|
60
|
+
*
|
|
30
61
|
* The template itself is never played by the pool; it is only cloned. Disable
|
|
31
62
|
* it, or keep it out of the renderer, if it is not meant to be seen.
|
|
32
63
|
*/
|
|
@@ -34,6 +65,7 @@ export class EffectPool {
|
|
|
34
65
|
private readonly idle: TransformNode[] = [];
|
|
35
66
|
private readonly playing = new Set<TransformNode>();
|
|
36
67
|
private readonly systemsOf = new Map<TransformNode, ParticleSystem[]>();
|
|
68
|
+
private readonly restOf = new Map<TransformNode, RestTransform>();
|
|
37
69
|
private disposed = false;
|
|
38
70
|
|
|
39
71
|
constructor(
|
|
@@ -61,10 +93,15 @@ export class EffectPool {
|
|
|
61
93
|
|
|
62
94
|
/**
|
|
63
95
|
* Takes an idle copy (making one when none is idle and the pool is under
|
|
64
|
-
* its size),
|
|
65
|
-
*
|
|
96
|
+
* its size), places it and starts it from the beginning. The copy's
|
|
97
|
+
* transform is put back to the template's first, so a rotation or scale
|
|
98
|
+
* from its last use does not stay. Null when the pool is full or disposed.
|
|
99
|
+
*
|
|
100
|
+
* @param placement - Where to put the copy, or just its position.
|
|
101
|
+
* @param parent - The node to parent it to, when `placement` is a position;
|
|
102
|
+
* ignored otherwise (use {@link EffectPlacement.parent}).
|
|
66
103
|
*/
|
|
67
|
-
acquire(
|
|
104
|
+
acquire(placement?: Vector3 | EffectPlacement, parent: Node | null = null): TransformNode | null {
|
|
68
105
|
if (this.disposed) {
|
|
69
106
|
return null;
|
|
70
107
|
}
|
|
@@ -72,10 +109,12 @@ export class EffectPool {
|
|
|
72
109
|
if (copy === null) {
|
|
73
110
|
return null;
|
|
74
111
|
}
|
|
75
|
-
copy
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
112
|
+
// Told apart by shape, not class: a vector from another copy of Babylon is still a position.
|
|
113
|
+
const place: EffectPlacement =
|
|
114
|
+
placement === undefined || typeof (placement as Vector3).x === 'number'
|
|
115
|
+
? {position: placement as Vector3 | undefined, parent}
|
|
116
|
+
: (placement as EffectPlacement);
|
|
117
|
+
this.placeCopy(copy, place);
|
|
79
118
|
copy.setEnabled(true);
|
|
80
119
|
this.playing.add(copy);
|
|
81
120
|
QuarksUtil.restart(copy);
|
|
@@ -98,6 +137,10 @@ export class EffectPool {
|
|
|
98
137
|
if (!this.playing.delete(copy)) {
|
|
99
138
|
return;
|
|
100
139
|
}
|
|
140
|
+
if (copy.isDisposed()) {
|
|
141
|
+
this.forget(copy);
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
101
144
|
QuarksUtil.stop(copy);
|
|
102
145
|
copy.setEnabled(false);
|
|
103
146
|
copy.parent = null;
|
|
@@ -132,21 +175,60 @@ export class EffectPool {
|
|
|
132
175
|
systems.push(system);
|
|
133
176
|
});
|
|
134
177
|
this.systemsOf.set(copy, systems);
|
|
178
|
+
this.restOf.set(copy, {
|
|
179
|
+
position: copy.position.clone(),
|
|
180
|
+
rotation: copy.rotation.clone(),
|
|
181
|
+
rotationQuaternion: copy.rotationQuaternion?.clone() ?? null,
|
|
182
|
+
scaling: copy.scaling.clone(),
|
|
183
|
+
});
|
|
135
184
|
QuarksUtil.addToBatchRenderer(copy, this.options.renderer);
|
|
136
185
|
copy.setEnabled(false);
|
|
137
186
|
copy.parent = null;
|
|
187
|
+
// A copy disposed from outside, or with a parent it was placed under, is let go of.
|
|
188
|
+
copy.onDisposeObservable.addOnce(() => this.forget(copy));
|
|
138
189
|
return copy;
|
|
139
190
|
}
|
|
140
191
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
192
|
+
/** Puts the copy back to the template's transform, then applies `place`. */
|
|
193
|
+
private placeCopy(copy: TransformNode, place: EffectPlacement): void {
|
|
194
|
+
const rest = this.restOf.get(copy)!;
|
|
195
|
+
copy.parent = place.parent ?? null;
|
|
196
|
+
// By components: Babylon's copyFrom reads private fields, which a vector from elsewhere lacks.
|
|
197
|
+
const position = place.position ?? rest.position;
|
|
198
|
+
const scaling = place.scaling ?? rest.scaling;
|
|
199
|
+
copy.position.copyFromFloats(position.x, position.y, position.z);
|
|
200
|
+
copy.scaling.copyFromFloats(scaling.x, scaling.y, scaling.z);
|
|
201
|
+
if (place.rotationQuaternion !== undefined) {
|
|
202
|
+
copy.rotationQuaternion = place.rotationQuaternion.clone();
|
|
203
|
+
} else if (place.rotation !== undefined) {
|
|
204
|
+
copy.rotationQuaternion = null;
|
|
205
|
+
copy.rotation.copyFromFloats(place.rotation.x, place.rotation.y, place.rotation.z);
|
|
206
|
+
} else {
|
|
207
|
+
copy.rotationQuaternion = rest.rotationQuaternion?.clone() ?? null;
|
|
208
|
+
copy.rotation.copyFrom(rest.rotation);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Drops every trace of a copy, which was or is being disposed. */
|
|
213
|
+
private forget(copy: TransformNode): void {
|
|
214
|
+
for (const system of this.systemsOf.get(copy) ?? []) {
|
|
144
215
|
system.removeEventListener('finished', this.onSystemFinished);
|
|
145
216
|
this.options.renderer.deleteSystem(system);
|
|
146
217
|
system.dispose();
|
|
147
218
|
}
|
|
148
219
|
this.systemsOf.delete(copy);
|
|
149
|
-
|
|
220
|
+
this.restOf.delete(copy);
|
|
221
|
+
this.playing.delete(copy);
|
|
222
|
+
const index = this.idle.indexOf(copy);
|
|
223
|
+
if (index !== -1) {
|
|
224
|
+
this.idle.splice(index, 1);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
private disposeCopy(copy: TransformNode): void {
|
|
229
|
+
this.forget(copy);
|
|
230
|
+
// Lets go of the textures and materials the copy shares with the template.
|
|
231
|
+
disposeEffect(copy);
|
|
150
232
|
}
|
|
151
233
|
|
|
152
234
|
/** The copy a system belongs to is done when all of its systems are. */
|
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import {VertexBuffer} from '@babylonjs/core/Buffers/buffer';
|
|
2
2
|
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
3
3
|
import {EmitterShape, IParticleSystem, Particle, Plugin, random, ShapeJSON, ShapeTransform, Vector3} from 'quarks.core';
|
|
4
|
-
|
|
4
|
+
import {ensureTriangleIndices} from './geometryUtil';
|
|
5
|
+
import {quarksNodeId} from './nodeId';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Emits from the surface of a Babylon mesh (Unity's Shape: Mesh, Unreal's Skeletal/Static Mesh
|
|
9
|
+
* Location): a point picked at random over the triangles, weighted by their area, moving along the
|
|
10
|
+
* triangle's normal at the particle's start speed. The mesh's vertices are read once, when it is
|
|
11
|
+
* set, in the mesh's own space, and laid in the emitter's space as they are, moved by
|
|
12
|
+
* {@link transform}; moving the mesh node afterwards does not move the shape. A mesh without an
|
|
13
|
+
* index buffer is read as a plain triangle list. A shape without a mesh emits from the origin
|
|
14
|
+
* along +Z.
|
|
15
|
+
*/
|
|
5
16
|
export class MeshSurfaceEmitter implements EmitterShape {
|
|
17
|
+
/** The shape's type in effect files. */
|
|
6
18
|
type = 'mesh_surface';
|
|
7
19
|
|
|
8
20
|
private _triangleIndexToArea: number[] = [];
|
|
@@ -13,29 +25,43 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
13
25
|
/** Offset, rotation and scale of the mesh within the particle system. */
|
|
14
26
|
transform?: ShapeTransform;
|
|
15
27
|
|
|
16
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The mesh emitted from. Setting it reads its positions and indices; a mesh without indices is
|
|
30
|
+
* read as a triangle list (vertices 0-1-2, 3-4-5, ...), and a mesh without positions, or with
|
|
31
|
+
* no whole triangle, emits from the origin.
|
|
32
|
+
*/
|
|
33
|
+
get mesh(): Mesh | undefined {
|
|
17
34
|
return this._mesh;
|
|
18
35
|
}
|
|
19
36
|
|
|
20
37
|
set mesh(mesh: Mesh | undefined) {
|
|
21
38
|
this._mesh = mesh;
|
|
39
|
+
this._positions = undefined;
|
|
40
|
+
this._indices = undefined;
|
|
41
|
+
this._triangleIndexToArea.length = 0;
|
|
22
42
|
if (!mesh) return;
|
|
23
43
|
|
|
24
44
|
const positions = mesh.getVerticesData(VertexBuffer.PositionKind);
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
if (!positions || positions.length < 9) return;
|
|
46
|
+
const vertexPositions = new Float32Array(positions);
|
|
47
|
+
const read = mesh.getIndices();
|
|
48
|
+
const indices = ensureTriangleIndices(
|
|
49
|
+
vertexPositions,
|
|
50
|
+
read && read.length > 0 ? Uint32Array.from(read) : undefined
|
|
51
|
+
);
|
|
52
|
+
const triCount = Math.floor(indices.length / 3);
|
|
53
|
+
if (triCount === 0) return;
|
|
54
|
+
|
|
55
|
+
this._positions = vertexPositions;
|
|
56
|
+
this._indices = new Int32Array(indices.subarray(0, triCount * 3));
|
|
27
57
|
|
|
28
|
-
this._positions = new Float32Array(positions);
|
|
29
|
-
this._indices = new Int32Array(indices);
|
|
30
|
-
|
|
31
|
-
this._triangleIndexToArea.length = 0;
|
|
32
58
|
let area = 0;
|
|
33
|
-
const triCount = indices.length / 3;
|
|
34
59
|
this._triangleIndexToArea.push(0);
|
|
35
60
|
|
|
36
61
|
const a = new Vector3();
|
|
37
62
|
const b = new Vector3();
|
|
38
63
|
const c = new Vector3();
|
|
64
|
+
const cross = new Vector3();
|
|
39
65
|
|
|
40
66
|
for (let i = 0; i < triCount; i++) {
|
|
41
67
|
const i0 = indices[i * 3];
|
|
@@ -48,20 +74,25 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
48
74
|
|
|
49
75
|
b.sub(a);
|
|
50
76
|
c.sub(a);
|
|
51
|
-
const cross = new Vector3();
|
|
52
77
|
cross.crossVectors(b, c);
|
|
53
78
|
area += cross.length() * 0.5;
|
|
54
79
|
this._triangleIndexToArea.push(area);
|
|
55
80
|
}
|
|
56
81
|
}
|
|
57
82
|
|
|
83
|
+
/**
|
|
84
|
+
* @param mesh The mesh to emit from.
|
|
85
|
+
* @param meshReferenceId The id of a mesh node in the effect file, kept until the loader links it.
|
|
86
|
+
* @param transform Offset, rotation and scale of the mesh within the system.
|
|
87
|
+
*/
|
|
58
88
|
constructor(mesh?: Mesh, meshReferenceId?: string, transform?: ShapeTransform) {
|
|
59
89
|
this._meshReferenceId = meshReferenceId;
|
|
60
90
|
this.transform = transform;
|
|
61
91
|
if (mesh) this.mesh = mesh;
|
|
62
92
|
}
|
|
63
93
|
|
|
64
|
-
|
|
94
|
+
/** Places a new particle on the surface and sets its velocity along the triangle's normal. */
|
|
95
|
+
initialize(p: Particle): void {
|
|
65
96
|
if (!this._positions || !this._indices) {
|
|
66
97
|
p.position.set(0, 0, 0);
|
|
67
98
|
p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);
|
|
@@ -114,25 +145,30 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
114
145
|
this.transform?.apply(p);
|
|
115
146
|
}
|
|
116
147
|
|
|
148
|
+
/** The shape in effect-file form; the mesh is written as its node id. */
|
|
117
149
|
toJSON(): ShapeJSON {
|
|
118
150
|
return {
|
|
119
151
|
type: 'mesh_surface',
|
|
120
|
-
mesh: this._mesh ? (this._mesh
|
|
152
|
+
mesh: this._mesh ? quarksNodeId(this._mesh) : (this._meshReferenceId ?? ''),
|
|
121
153
|
...ShapeTransform.toShapeJSON(this.transform),
|
|
122
154
|
};
|
|
123
155
|
}
|
|
124
156
|
|
|
125
|
-
|
|
157
|
+
/** Reads {@link toJSON} output; the mesh stays an id until `QuarksLoader` links it. */
|
|
158
|
+
static fromJSON(json: ShapeJSON | undefined): MeshSurfaceEmitter {
|
|
126
159
|
return new MeshSurfaceEmitter(undefined, json?.mesh, ShapeTransform.fromJSON(json?.transform));
|
|
127
160
|
}
|
|
128
161
|
|
|
162
|
+
/** A copy emitting from the same mesh. */
|
|
129
163
|
clone(): EmitterShape {
|
|
130
164
|
return new MeshSurfaceEmitter(this._mesh, this._meshReferenceId, this.transform?.clone());
|
|
131
165
|
}
|
|
132
166
|
|
|
167
|
+
/** Does nothing: the shape keeps no state between steps. */
|
|
133
168
|
update(_system: IParticleSystem, _delta: number): void {}
|
|
134
169
|
}
|
|
135
170
|
|
|
171
|
+
/** Registers {@link MeshSurfaceEmitter} with quarks.core's shape table; loaded when the package is imported. */
|
|
136
172
|
export const MeshSurfaceEmitterPlugin: Plugin = {
|
|
137
173
|
id: 'babylon.quarks',
|
|
138
174
|
initialize: () => {},
|
|
@@ -141,7 +177,7 @@ export const MeshSurfaceEmitterPlugin: Plugin = {
|
|
|
141
177
|
type: 'mesh_surface',
|
|
142
178
|
params: [['mesh', ['mesh']]],
|
|
143
179
|
constructor: MeshSurfaceEmitter,
|
|
144
|
-
loadJSON: (json:
|
|
180
|
+
loadJSON: (json: ShapeJSON) => MeshSurfaceEmitter.fromJSON(json),
|
|
145
181
|
},
|
|
146
182
|
],
|
|
147
183
|
behaviors: [],
|
package/src/ParticleEmitter.ts
CHANGED
|
@@ -1,8 +1,56 @@
|
|
|
1
1
|
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
|
+
import {quarksNodeId} from './nodeId';
|
|
5
|
+
import type {BabylonMetaData} from './system/types';
|
|
4
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
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Above 0 while a particle system disposes its own emitter (`ParticleSystem.dispose()`, which
|
|
31
|
+
* `autoDestroy` calls when the system finishes). Babylon then disposes the child nodes too, and
|
|
32
|
+
* child emitters keep their systems playing, so an effect's longer-lived children are not cut off
|
|
33
|
+
* when a short parent finishes. Disposing a node directly (`root.dispose()`) disposes its systems.
|
|
34
|
+
*/
|
|
35
|
+
let systemDisposeDepth = 0;
|
|
36
|
+
|
|
37
|
+
/** Disposes `emitter` for its own system without disposing the systems of child emitters. */
|
|
38
|
+
export function disposeEmitterForSystem(emitter: TransformNode): void {
|
|
39
|
+
systemDisposeDepth++;
|
|
40
|
+
try {
|
|
41
|
+
emitter.dispose();
|
|
42
|
+
} finally {
|
|
43
|
+
systemDisposeDepth--;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The Babylon node a particle system lives at: its position, rotation and scale place the system,
|
|
49
|
+
* parenting it moves the effect with another node, and disabling it freezes and hides the system.
|
|
50
|
+
* Every {@link ParticleSystem} makes its own, as `system.emitter`.
|
|
51
|
+
*/
|
|
5
52
|
export class ParticleEmitter extends TransformNode {
|
|
53
|
+
/** The particle system this node carries. */
|
|
6
54
|
system: IParticleSystem;
|
|
7
55
|
private _matrixWorld: Matrix4 = new Matrix4();
|
|
8
56
|
private _matrixWorldFlag = -1;
|
|
@@ -40,6 +88,7 @@ export class ParticleEmitter extends TransformNode {
|
|
|
40
88
|
return this._matrixWorld;
|
|
41
89
|
}
|
|
42
90
|
|
|
91
|
+
/** Whether the node and its parents are enabled; setting it is `setEnabled`. */
|
|
43
92
|
get visible(): boolean {
|
|
44
93
|
return this.isEnabled();
|
|
45
94
|
}
|
|
@@ -48,8 +97,9 @@ export class ParticleEmitter extends TransformNode {
|
|
|
48
97
|
this.setEnabled(value);
|
|
49
98
|
}
|
|
50
99
|
|
|
100
|
+
/** The id effect files know this emitter by; see {@link quarksNodeId}. */
|
|
51
101
|
get uuid(): string {
|
|
52
|
-
return this
|
|
102
|
+
return quarksNodeId(this);
|
|
53
103
|
}
|
|
54
104
|
|
|
55
105
|
constructor(system: IParticleSystem, scene?: Scene) {
|
|
@@ -57,20 +107,22 @@ export class ParticleEmitter extends TransformNode {
|
|
|
57
107
|
this.system = system;
|
|
58
108
|
}
|
|
59
109
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
delete data.metadata;
|
|
70
|
-
}
|
|
71
|
-
values.push(data);
|
|
110
|
+
/**
|
|
111
|
+
* Disposes the node and the particle system it carries, so `root.dispose()` on an effect tree
|
|
112
|
+
* also removes its systems from their renderer. Safe to call more than once. When a system
|
|
113
|
+
* disposes its own emitter (`ParticleSystem.dispose()`, `autoDestroy`), the systems of child
|
|
114
|
+
* emitters keep playing until they finish or are disposed themselves.
|
|
115
|
+
*/
|
|
116
|
+
dispose(doNotRecurse?: boolean, disposeMaterialAndTextures = false): void {
|
|
117
|
+
if (this.isDisposed()) {
|
|
118
|
+
return;
|
|
72
119
|
}
|
|
73
|
-
|
|
120
|
+
super.dispose(doNotRecurse, disposeMaterialAndTextures);
|
|
121
|
+
if (systemDisposeDepth > 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
// IParticleSystem (quarks.core) has no dispose(); ParticleSystem does.
|
|
125
|
+
(this.system as IParticleSystem & {dispose?: () => void}).dispose?.();
|
|
74
126
|
}
|
|
75
127
|
|
|
76
128
|
clone(name?: string): ParticleEmitter {
|
|
@@ -88,9 +140,15 @@ export class ParticleEmitter extends TransformNode {
|
|
|
88
140
|
return clonedEmitter;
|
|
89
141
|
}
|
|
90
142
|
|
|
91
|
-
|
|
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 {
|
|
92
150
|
return {
|
|
93
|
-
uuid: (this
|
|
151
|
+
uuid: quarksNodeId(this),
|
|
94
152
|
type: 'ParticleEmitter',
|
|
95
153
|
name: this.name,
|
|
96
154
|
position: [this.position.x, this.position.y, this.position.z],
|
|
@@ -104,7 +162,7 @@ export class ParticleEmitter extends TransformNode {
|
|
|
104
162
|
]
|
|
105
163
|
: undefined,
|
|
106
164
|
scale: [this.scaling.x, this.scaling.y, this.scaling.z],
|
|
107
|
-
visible: this.isEnabled(),
|
|
165
|
+
visible: this.isEnabled(false),
|
|
108
166
|
ps: this.system.toJSON(meta, options),
|
|
109
167
|
};
|
|
110
168
|
}
|