babylon.quarks 0.19.0 → 0.21.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 +79 -0
- package/README.md +24 -0
- 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.ts +428 -19
- package/package.json +1 -1
- package/src/BatchedRenderer.ts +16 -2
- package/src/MeshSurfaceEmitter.ts +10 -4
- package/src/ParticleSystem.ts +124 -1
- package/src/QuarksLoader.ts +470 -20
- package/src/SpriteBatch.ts +75 -4
- package/src/TrailBatch.ts +24 -13
- package/src/VFXBatch.ts +4 -1
- package/src/index.ts +7 -1
- package/src/shaders/particle_vert.glsl.ts +20 -0
- package/src/shaders/particle_vert.wgsl.ts +21 -1
- package/src/shaders/stretched_bb_particle_vert.glsl.ts +18 -5
- package/src/shaders/stretched_bb_particle_vert.wgsl.ts +19 -6
package/package.json
CHANGED
package/src/BatchedRenderer.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {BaseTexture} from '@babylonjs/core/Materials/Textures/baseTexture';
|
|
|
2
2
|
import {Texture} from '@babylonjs/core/Materials/Textures/texture';
|
|
3
3
|
import {TransformNode} from '@babylonjs/core/Meshes/transformNode';
|
|
4
4
|
import {Scene} from '@babylonjs/core/scene';
|
|
5
|
-
import {EmitSubParticleSystem, IParticleSystem} from 'quarks.core';
|
|
5
|
+
import {EmitSubParticleSystem, IParticleSystem, Vector3} from 'quarks.core';
|
|
6
6
|
import {ParticleSystem} from './ParticleSystem';
|
|
7
7
|
import {SpriteBatch} from './SpriteBatch';
|
|
8
8
|
import {TrailBatch} from './TrailBatch';
|
|
@@ -47,6 +47,14 @@ export interface VFXBatchSettings {
|
|
|
47
47
|
*/
|
|
48
48
|
reflectionAtlas: BaseTexture | null;
|
|
49
49
|
layerMask: number;
|
|
50
|
+
/**
|
|
51
|
+
* Offset of billboard quads from their particle, in particle-size units
|
|
52
|
+
* (Unity's renderer Pivot). On billboards x/y move the quad in its own plane
|
|
53
|
+
* before it is rotated and z moves it toward the camera (up for horizontal
|
|
54
|
+
* billboards). On stretched billboards x runs from the head to the tail and
|
|
55
|
+
* y across, and z is unused. Absent or zero: no offset. Meshes ignore it.
|
|
56
|
+
*/
|
|
57
|
+
pivot?: Vector3;
|
|
50
58
|
}
|
|
51
59
|
|
|
52
60
|
export interface AdaptivePerformanceOptions {
|
|
@@ -57,6 +65,11 @@ export interface AdaptivePerformanceOptions {
|
|
|
57
65
|
increaseStep: number;
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
/** True when both pivots put the quad in the same place; a missing pivot is zero. */
|
|
69
|
+
function pivotEqual(a: Vector3 | undefined, b: Vector3 | undefined): boolean {
|
|
70
|
+
return (a?.x ?? 0) === (b?.x ?? 0) && (a?.y ?? 0) === (b?.y ?? 0) && (a?.z ?? 0) === (b?.z ?? 0);
|
|
71
|
+
}
|
|
72
|
+
|
|
60
73
|
/** True when both face lists are the same six texture references (or both empty). */
|
|
61
74
|
function reflectionFacesEqual(a: BaseTexture[] | null | undefined, b: BaseTexture[] | null | undefined): boolean {
|
|
62
75
|
if (a === b) {
|
|
@@ -147,7 +160,8 @@ export class BatchedRenderer extends TransformNode {
|
|
|
147
160
|
a.vTileCount === b.vTileCount &&
|
|
148
161
|
a.instancingGeometry === b.instancingGeometry &&
|
|
149
162
|
a.renderOrder === b.renderOrder &&
|
|
150
|
-
a.layerMask === b.layerMask
|
|
163
|
+
a.layerMask === b.layerMask &&
|
|
164
|
+
pivotEqual(a.pivot, b.pivot)
|
|
151
165
|
);
|
|
152
166
|
}
|
|
153
167
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {VertexBuffer} from '@babylonjs/core/Buffers/buffer';
|
|
2
2
|
import {Mesh} from '@babylonjs/core/Meshes/mesh';
|
|
3
|
-
import {EmitterShape, IParticleSystem, Particle, Plugin, ShapeJSON, Vector3} from 'quarks.core';
|
|
3
|
+
import {EmitterShape, IParticleSystem, Particle, Plugin, ShapeJSON, ShapeTransform, Vector3} from 'quarks.core';
|
|
4
4
|
|
|
5
5
|
export class MeshSurfaceEmitter implements EmitterShape {
|
|
6
6
|
type = 'mesh_surface';
|
|
@@ -10,6 +10,8 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
10
10
|
private _positions?: Float32Array;
|
|
11
11
|
private _indices?: Uint32Array | Int32Array;
|
|
12
12
|
private _meshReferenceId?: string;
|
|
13
|
+
/** Offset, rotation and scale of the mesh within the particle system. */
|
|
14
|
+
transform?: ShapeTransform;
|
|
13
15
|
|
|
14
16
|
get mesh() {
|
|
15
17
|
return this._mesh;
|
|
@@ -53,8 +55,9 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
53
55
|
}
|
|
54
56
|
}
|
|
55
57
|
|
|
56
|
-
constructor(mesh?: Mesh, meshReferenceId?: string) {
|
|
58
|
+
constructor(mesh?: Mesh, meshReferenceId?: string, transform?: ShapeTransform) {
|
|
57
59
|
this._meshReferenceId = meshReferenceId;
|
|
60
|
+
this.transform = transform;
|
|
58
61
|
if (mesh) this.mesh = mesh;
|
|
59
62
|
}
|
|
60
63
|
|
|
@@ -62,6 +65,7 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
62
65
|
if (!this._positions || !this._indices) {
|
|
63
66
|
p.position.set(0, 0, 0);
|
|
64
67
|
p.velocity.set(0, 0, 1).multiplyScalar(p.startSpeed);
|
|
68
|
+
this.transform?.apply(p);
|
|
65
69
|
return;
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -107,21 +111,23 @@ export class MeshSurfaceEmitter implements EmitterShape {
|
|
|
107
111
|
const nz = bx * cy - by * cx;
|
|
108
112
|
const len = Math.sqrt(nx * nx + ny * ny + nz * nz) || 1;
|
|
109
113
|
p.velocity.set(nx / len, ny / len, nz / len).multiplyScalar(p.startSpeed);
|
|
114
|
+
this.transform?.apply(p);
|
|
110
115
|
}
|
|
111
116
|
|
|
112
117
|
toJSON(): ShapeJSON {
|
|
113
118
|
return {
|
|
114
119
|
type: 'mesh_surface',
|
|
115
120
|
mesh: this._mesh ? (this._mesh as any).uniqueId?.toString() : (this._meshReferenceId ?? ''),
|
|
121
|
+
...ShapeTransform.toShapeJSON(this.transform),
|
|
116
122
|
};
|
|
117
123
|
}
|
|
118
124
|
|
|
119
125
|
static fromJSON(json: any): MeshSurfaceEmitter {
|
|
120
|
-
return new MeshSurfaceEmitter(undefined, json?.mesh);
|
|
126
|
+
return new MeshSurfaceEmitter(undefined, json?.mesh, ShapeTransform.fromJSON(json?.transform));
|
|
121
127
|
}
|
|
122
128
|
|
|
123
129
|
clone(): EmitterShape {
|
|
124
|
-
return new MeshSurfaceEmitter(this._mesh, this._meshReferenceId);
|
|
130
|
+
return new MeshSurfaceEmitter(this._mesh, this._meshReferenceId, this.transform?.clone());
|
|
125
131
|
}
|
|
126
132
|
|
|
127
133
|
update(_system: IParticleSystem, _delta: number): void {}
|
package/src/ParticleSystem.ts
CHANGED
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
TrailSettings,
|
|
39
39
|
ValueGenerator,
|
|
40
40
|
ValueGeneratorFromJSON,
|
|
41
|
+
Vector2,
|
|
41
42
|
Vector3,
|
|
42
43
|
Vector3Generator,
|
|
43
44
|
Vector4,
|
|
@@ -83,6 +84,16 @@ function fallbackUVsForPositions(positions: Float32Array): Float32Array {
|
|
|
83
84
|
}
|
|
84
85
|
const DEFAULT_INDICES = new Uint32Array([0, 1, 2, 0, 2, 3]);
|
|
85
86
|
|
|
87
|
+
/** The optional trail flags that are switched on, so JSON and clones only carry the ones in use. */
|
|
88
|
+
function trailFlags(settings: TrailSettings | undefined): Partial<TrailSettings> {
|
|
89
|
+
const flags: Partial<TrailSettings> = {};
|
|
90
|
+
if (settings?.uvFromHead) flags.uvFromHead = true;
|
|
91
|
+
if (settings?.colorFromParticle) flags.colorFromParticle = true;
|
|
92
|
+
if (settings?.dieWithParticle) flags.dieWithParticle = true;
|
|
93
|
+
if (settings?.recordWorldSpace) flags.recordWorldSpace = true;
|
|
94
|
+
return flags;
|
|
95
|
+
}
|
|
96
|
+
|
|
86
97
|
export interface ParticleSystemParameters {
|
|
87
98
|
autoDestroy?: boolean;
|
|
88
99
|
looping?: boolean;
|
|
@@ -99,6 +110,8 @@ export interface ParticleSystemParameters {
|
|
|
99
110
|
emissionOverTime?: ValueGenerator | FunctionValueGenerator;
|
|
100
111
|
emissionOverDistance?: ValueGenerator | FunctionValueGenerator;
|
|
101
112
|
emissionBursts?: Array<BurstParameters>;
|
|
113
|
+
/** Most particles alive at once; emission past it is dropped. Leave unset for no limit. */
|
|
114
|
+
maxParticles?: number;
|
|
102
115
|
onlyUsedByOther?: boolean;
|
|
103
116
|
behaviors?: Array<Behavior>;
|
|
104
117
|
instancingGeometry?: Float32Array;
|
|
@@ -125,6 +138,10 @@ export interface ParticleSystemParameters {
|
|
|
125
138
|
depthWrite?: boolean;
|
|
126
139
|
alphaTest?: number;
|
|
127
140
|
layerMask?: number;
|
|
141
|
+
/** Billboard pivot in particle-size units, see {@link VFXBatchSettings.pivot}. */
|
|
142
|
+
pivot?: Vector3;
|
|
143
|
+
/** Fraction of billboard particles mirrored along U (x) and V (y), see {@link ParticleSystem.flip}. */
|
|
144
|
+
flip?: Vector2;
|
|
128
145
|
scene?: Scene;
|
|
129
146
|
}
|
|
130
147
|
|
|
@@ -152,6 +169,7 @@ export interface ParticleSystemJSONParameters {
|
|
|
152
169
|
emissionOverTime: FunctionJSON;
|
|
153
170
|
emissionOverDistance: FunctionJSON;
|
|
154
171
|
emissionBursts?: Array<BurstParametersJSON>;
|
|
172
|
+
maxParticles?: number;
|
|
155
173
|
onlyUsedByOther: boolean;
|
|
156
174
|
rendererEmitterSettings: RendererEmitterSettings;
|
|
157
175
|
instancingGeometry?: any;
|
|
@@ -175,6 +193,10 @@ export interface ParticleSystemJSONParameters {
|
|
|
175
193
|
alphaTest?: number;
|
|
176
194
|
behaviors: Array<any>;
|
|
177
195
|
worldSpace: boolean;
|
|
196
|
+
/** `[x, y, z]`; written only when non-zero. */
|
|
197
|
+
pivot?: number[];
|
|
198
|
+
/** `[u, v]`; written only when non-zero. */
|
|
199
|
+
flip?: number[];
|
|
178
200
|
}
|
|
179
201
|
|
|
180
202
|
export interface BabylonMetaData {
|
|
@@ -203,9 +225,17 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
203
225
|
startColor: ColorGenerator | FunctionColorGenerator;
|
|
204
226
|
startTileIndex: ValueGenerator;
|
|
205
227
|
rendererEmitterSettings: RendererEmitterSettings;
|
|
228
|
+
/**
|
|
229
|
+
* Fraction of billboard particles mirrored along U (x) and V (y), each
|
|
230
|
+
* picked at random when the particle is born — Unity's renderer Flip.
|
|
231
|
+
* Undefined: nothing is mirrored. Meshes ignore it.
|
|
232
|
+
*/
|
|
233
|
+
flip?: Vector2;
|
|
206
234
|
emissionOverTime: ValueGenerator | FunctionValueGenerator;
|
|
207
235
|
emissionOverDistance: ValueGenerator | FunctionValueGenerator;
|
|
208
236
|
emissionBursts: Array<BurstParameters>;
|
|
237
|
+
/** Most particles alive at once (Unity's Max Particles); emission past it is dropped. Undefined means no limit. */
|
|
238
|
+
maxParticles?: number;
|
|
209
239
|
onlyUsedByOther: boolean;
|
|
210
240
|
worldSpace: boolean;
|
|
211
241
|
particleNum: number;
|
|
@@ -356,6 +386,28 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
356
386
|
this.neededToUpdateRender = true;
|
|
357
387
|
}
|
|
358
388
|
|
|
389
|
+
/**
|
|
390
|
+
* Billboard pivot in particle-size units (Unity's renderer Pivot), see
|
|
391
|
+
* {@link VFXBatchSettings.pivot}. Assign a new vector to change it: editing
|
|
392
|
+
* the returned one in place does not reach the renderer.
|
|
393
|
+
*/
|
|
394
|
+
get pivot(): Vector3 | undefined {
|
|
395
|
+
return this.rendererSettings.pivot;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
set pivot(v: Vector3 | undefined) {
|
|
399
|
+
this.rendererSettings.pivot = ParticleSystem.nonZeroPivot(v);
|
|
400
|
+
this.neededToUpdateRender = true;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
private static nonZeroPivot(v: Vector3 | undefined): Vector3 | undefined {
|
|
404
|
+
return v && (v.x !== 0 || v.y !== 0 || v.z !== 0) ? v.clone() : undefined;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
private static nonZeroFlip(v: Vector2 | undefined): Vector2 | undefined {
|
|
408
|
+
return v && (v.x > 0 || v.y > 0) ? v.clone() : undefined;
|
|
409
|
+
}
|
|
410
|
+
|
|
359
411
|
get softNearFade() {
|
|
360
412
|
return this.rendererSettings.softNearFade;
|
|
361
413
|
}
|
|
@@ -468,6 +520,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
468
520
|
this.emissionOverTime = parameters.emissionOverTime ?? new ConstantValue(10);
|
|
469
521
|
this.emissionOverDistance = parameters.emissionOverDistance ?? new ConstantValue(0);
|
|
470
522
|
this.emissionBursts = parameters.emissionBursts ?? [];
|
|
523
|
+
this.maxParticles = parameters.maxParticles;
|
|
471
524
|
this.onlyUsedByOther = parameters.onlyUsedByOther ?? false;
|
|
472
525
|
this.emitterShape = parameters.shape ?? new SphereEmitter();
|
|
473
526
|
this.behaviors = parameters.behaviors ?? [];
|
|
@@ -509,7 +562,9 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
509
562
|
reflectionFaces: null,
|
|
510
563
|
reflectionAtlas: null,
|
|
511
564
|
layerMask: parameters.layerMask ?? 0x0fffffff,
|
|
565
|
+
pivot: ParticleSystem.nonZeroPivot(parameters.pivot),
|
|
512
566
|
};
|
|
567
|
+
this.flip = ParticleSystem.nonZeroFlip(parameters.flip);
|
|
513
568
|
if (this.rendererSettings.renderMode === RenderMode.Mesh && !this.rendererSettings.instancingNormals) {
|
|
514
569
|
this.rendererSettings.instancingNormals = ParticleSystem.createFallbackNormals(
|
|
515
570
|
this.rendererSettings.instancingGeometry
|
|
@@ -724,6 +779,10 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
724
779
|
const timeRatio = emissionState.time / this.duration;
|
|
725
780
|
const trailSettings = isTrailMode ? (this.rendererEmitterSettings as TrailSettings) : undefined;
|
|
726
781
|
const followLocalOrigin = Boolean(trailSettings?.followLocalOrigin);
|
|
782
|
+
if (this.maxParticles !== undefined) {
|
|
783
|
+
// Like Unity, a full system skips the particles it has no room for rather than queueing them.
|
|
784
|
+
count = Math.min(count, Math.max(0, this.maxParticles - this.particleNum));
|
|
785
|
+
}
|
|
727
786
|
|
|
728
787
|
for (let i = 0; i < count; i++) {
|
|
729
788
|
emissionState.burstParticleIndex = i;
|
|
@@ -795,6 +854,9 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
795
854
|
// Set until a turning behavior says otherwise, so the
|
|
796
855
|
// renderer never carries a recycled particle's old spin.
|
|
797
856
|
sprite.angularVelocity = 0;
|
|
857
|
+
const flip = this.flip;
|
|
858
|
+
sprite.flip =
|
|
859
|
+
flip === undefined ? 0 : (Math.random() < flip.x ? 1 : 0) | (Math.random() < flip.y ? 2 : 0);
|
|
798
860
|
}
|
|
799
861
|
} else if (trailSettings) {
|
|
800
862
|
const trail = particle as TrailParticle;
|
|
@@ -1084,6 +1146,33 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1084
1146
|
}
|
|
1085
1147
|
}
|
|
1086
1148
|
|
|
1149
|
+
// Newborns were snapshotted above with spawn size/color (startSize /
|
|
1150
|
+
// startColor) before SizeOverLife / ColorOverLife ran. That false jump
|
|
1151
|
+
// is then extrapolated by the renderer when simulationResidual is
|
|
1152
|
+
// negative (common with rounded fixed-step) and flashes the start
|
|
1153
|
+
// appearance at the emitter. Collapse it so birth matches t=0 on the
|
|
1154
|
+
// curves. Age is still 0 here — integration below ages them.
|
|
1155
|
+
if (particleCount > 0) {
|
|
1156
|
+
const prevSize = columns.previousSize;
|
|
1157
|
+
const curSize = columns.size;
|
|
1158
|
+
const prevColor = columns.previousColor;
|
|
1159
|
+
const curColor = columns.color;
|
|
1160
|
+
const scalars = columns.scalars;
|
|
1161
|
+
const stride = ParticleStore.SCALAR_STRIDE;
|
|
1162
|
+
for (let i = 0, o3 = 0, o4 = 0, s = 0; i < particleCount; i++, o3 += 3, o4 += 4, s += stride) {
|
|
1163
|
+
if (scalars[s + ParticleStore.AGE] !== 0) {
|
|
1164
|
+
continue;
|
|
1165
|
+
}
|
|
1166
|
+
prevSize[o3] = curSize[o3];
|
|
1167
|
+
prevSize[o3 + 1] = curSize[o3 + 1];
|
|
1168
|
+
prevSize[o3 + 2] = curSize[o3 + 2];
|
|
1169
|
+
prevColor[o4] = curColor[o4];
|
|
1170
|
+
prevColor[o4 + 1] = curColor[o4 + 1];
|
|
1171
|
+
prevColor[o4 + 2] = curColor[o4 + 2];
|
|
1172
|
+
prevColor[o4 + 3] = curColor[o4 + 3];
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
|
|
1087
1176
|
const followLocalOrigin = isTrailMode && (this.rendererEmitterSettings as TrailSettings).followLocalOrigin;
|
|
1088
1177
|
if (followLocalOrigin) {
|
|
1089
1178
|
const emitterMatrix = this.emitter.matrixWorld;
|
|
@@ -1114,8 +1203,30 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1114
1203
|
}
|
|
1115
1204
|
|
|
1116
1205
|
if (isTrailMode) {
|
|
1206
|
+
const trailSettings = this.rendererEmitterSettings as TrailSettings;
|
|
1207
|
+
const dieWithParticle = trailSettings.dieWithParticle;
|
|
1208
|
+
// A world-space system records world positions already.
|
|
1209
|
+
const recordWorldSpace = trailSettings.recordWorldSpace && !this.worldSpace;
|
|
1210
|
+
const emitterMatrix = this.emitter.matrixWorld;
|
|
1117
1211
|
for (let i = 0; i < particleCount; i++) {
|
|
1118
|
-
|
|
1212
|
+
const trail = particles[i] as TrailParticle;
|
|
1213
|
+
// Emptying the history lets the removal pass below take the
|
|
1214
|
+
// particle this same step.
|
|
1215
|
+
if (dieWithParticle && trail.age >= trail.life) {
|
|
1216
|
+
trail.resetHistory();
|
|
1217
|
+
continue;
|
|
1218
|
+
}
|
|
1219
|
+
trail.update();
|
|
1220
|
+
// update() wrote a sample only while the particle is alive.
|
|
1221
|
+
if (recordWorldSpace && trail.age <= trail.life) {
|
|
1222
|
+
const offset = ((trail.historyHead + trail.historyCapacity - 1) % trail.historyCapacity) * 3;
|
|
1223
|
+
const positions = trail.historyPositions;
|
|
1224
|
+
tempV.set(positions[offset], positions[offset + 1], positions[offset + 2]);
|
|
1225
|
+
tempV.applyMatrix4(trail.parentMatrix ?? emitterMatrix);
|
|
1226
|
+
positions[offset] = tempV.x;
|
|
1227
|
+
positions[offset + 1] = tempV.y;
|
|
1228
|
+
positions[offset + 2] = tempV.z;
|
|
1229
|
+
}
|
|
1119
1230
|
}
|
|
1120
1231
|
}
|
|
1121
1232
|
|
|
@@ -1281,6 +1392,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1281
1392
|
interval: burst.interval,
|
|
1282
1393
|
cycle: burst.cycle,
|
|
1283
1394
|
})),
|
|
1395
|
+
...(this.maxParticles !== undefined ? {maxParticles: this.maxParticles} : {}),
|
|
1284
1396
|
onlyUsedByOther: this.onlyUsedByOther,
|
|
1285
1397
|
instancingGeometry: geometryUUID,
|
|
1286
1398
|
renderMode: this.renderMode,
|
|
@@ -1302,6 +1414,8 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1302
1414
|
alphaTest: this.rendererSettings.materialAlphaTest,
|
|
1303
1415
|
behaviors: this.behaviors.map((b) => b.toJSON()),
|
|
1304
1416
|
worldSpace: this.worldSpace,
|
|
1417
|
+
...(this.pivot ? {pivot: [this.pivot.x, this.pivot.y, this.pivot.z]} : {}),
|
|
1418
|
+
...(this.flip ? {flip: [this.flip.x, this.flip.y]} : {}),
|
|
1305
1419
|
};
|
|
1306
1420
|
}
|
|
1307
1421
|
|
|
@@ -1321,6 +1435,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1321
1435
|
? ValueGeneratorFromJSON(trailSettings.startLength)
|
|
1322
1436
|
: new ConstantValue(30),
|
|
1323
1437
|
followLocalOrigin: trailSettings?.followLocalOrigin ?? false,
|
|
1438
|
+
...trailFlags(trailSettings),
|
|
1324
1439
|
};
|
|
1325
1440
|
} else if (json.renderMode === RenderMode.StretchedBillBoard) {
|
|
1326
1441
|
rendererEmitterSettings = {...(json.rendererEmitterSettings ?? {})};
|
|
@@ -1371,6 +1486,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1371
1486
|
interval: burst.interval ?? 0.1,
|
|
1372
1487
|
cycle: burst.cycle ?? burst.cycleCount ?? 1,
|
|
1373
1488
|
})),
|
|
1489
|
+
maxParticles: typeof json.maxParticles === 'number' ? json.maxParticles : undefined,
|
|
1374
1490
|
onlyUsedByOther: json.onlyUsedByOther,
|
|
1375
1491
|
instancingGeometry: resolvedGeometry.positions,
|
|
1376
1492
|
instancingIndices: resolvedGeometry.indices,
|
|
@@ -1399,6 +1515,8 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1399
1515
|
behaviors: [],
|
|
1400
1516
|
worldSpace: json.worldSpace,
|
|
1401
1517
|
layerMask: json.layers,
|
|
1518
|
+
pivot: json.pivot ? new Vector3(json.pivot[0] ?? 0, json.pivot[1] ?? 0, json.pivot[2] ?? 0) : undefined,
|
|
1519
|
+
flip: json.flip ? new Vector2(json.flip[0] ?? 0, json.flip[1] ?? 0) : undefined,
|
|
1402
1520
|
});
|
|
1403
1521
|
ps.behaviors = (json.behaviors ?? [])
|
|
1404
1522
|
.map((behaviorJson) => {
|
|
@@ -1432,6 +1550,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1432
1550
|
return {
|
|
1433
1551
|
startLength: (this.rendererEmitterSettings as TrailSettings).startLength.toJSON(),
|
|
1434
1552
|
followLocalOrigin: (this.rendererEmitterSettings as TrailSettings).followLocalOrigin,
|
|
1553
|
+
...trailFlags(this.rendererEmitterSettings as TrailSettings),
|
|
1435
1554
|
};
|
|
1436
1555
|
}
|
|
1437
1556
|
if (this.renderMode === RenderMode.StretchedBillBoard) {
|
|
@@ -1594,6 +1713,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1594
1713
|
rendererEmitterSettings = {
|
|
1595
1714
|
startLength: (this.rendererEmitterSettings as TrailSettings).startLength.clone(),
|
|
1596
1715
|
followLocalOrigin: (this.rendererEmitterSettings as TrailSettings).followLocalOrigin,
|
|
1716
|
+
...trailFlags(this.rendererEmitterSettings as TrailSettings),
|
|
1597
1717
|
};
|
|
1598
1718
|
} else if (this.renderMode === RenderMode.StretchedBillBoard) {
|
|
1599
1719
|
rendererEmitterSettings = {
|
|
@@ -1619,6 +1739,7 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1619
1739
|
emissionOverTime: this.emissionOverTime.clone(),
|
|
1620
1740
|
emissionOverDistance: this.emissionOverDistance.clone(),
|
|
1621
1741
|
emissionBursts: this.emissionBursts.map((b) => ({...b})),
|
|
1742
|
+
maxParticles: this.maxParticles,
|
|
1622
1743
|
onlyUsedByOther: this.onlyUsedByOther,
|
|
1623
1744
|
instancingGeometry: this.rendererSettings.instancingGeometry,
|
|
1624
1745
|
instancingIndices: this.rendererSettings.instancingIndices,
|
|
@@ -1644,6 +1765,8 @@ export class ParticleSystem implements IParticleSystem {
|
|
|
1644
1765
|
depthWrite: this.rendererSettings.materialDepthWrite,
|
|
1645
1766
|
alphaTest: this.rendererSettings.materialAlphaTest,
|
|
1646
1767
|
layerMask: this.rendererSettings.layerMask,
|
|
1768
|
+
pivot: this.pivot,
|
|
1769
|
+
flip: this.flip,
|
|
1647
1770
|
scene: this.emitter.getScene(),
|
|
1648
1771
|
});
|
|
1649
1772
|
}
|