Skip to content

Commit 2cd5b85

Browse files
authored
Particle start size support curve mode (#2568)
* feat: particle start size support curve mode
1 parent 7bbdb1d commit 2cd5b85

File tree

4 files changed

+126
-29
lines changed

4 files changed

+126
-29
lines changed

packages/core/src/particle/ParticleGenerator.ts

+17-26
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ export class ParticleGenerator {
237237
/**
238238
* @internal
239239
*/
240-
_emit(time: number, count: number): void {
240+
_emit(playTime: number, count: number): void {
241241
if (this.emission.enabled) {
242242
// Wait the existing particles to be retired
243243
const notRetireParticleCount = this._getNotRetiredParticleCount();
@@ -250,15 +250,15 @@ export class ParticleGenerator {
250250
const shape = this.emission.shape;
251251
for (let i = 0; i < count; i++) {
252252
if (shape?.enabled) {
253-
shape._generatePositionAndDirection(this.emission._shapeRand, time, position, direction);
253+
shape._generatePositionAndDirection(this.emission._shapeRand, playTime, position, direction);
254254
const positionScale = this.main._getPositionScale();
255255
position.multiply(positionScale);
256256
direction.normalize().multiply(positionScale);
257257
} else {
258258
position.set(0, 0, 0);
259259
direction.set(0, 0, -1);
260260
}
261-
this._addNewParticle(position, direction, transform, time);
261+
this._addNewParticle(position, direction, transform, playTime);
262262
}
263263
}
264264
}
@@ -669,7 +669,7 @@ export class ParticleGenerator {
669669
}
670670
}
671671

672-
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, time: number): void {
672+
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, playTime: number): void {
673673
const firstFreeElement = this._firstFreeElement;
674674
let nextFreeElement = firstFreeElement + 1;
675675
if (nextFreeElement >= this._currentParticleCount) {
@@ -711,9 +711,7 @@ export class ParticleGenerator {
711711
const offset = firstFreeElement * ParticleBufferUtils.instanceVertexFloatStride;
712712

713713
// Position
714-
instanceVertices[offset] = position.x;
715-
instanceVertices[offset + 1] = position.y;
716-
instanceVertices[offset + 2] = position.z;
714+
position.copyToArray(instanceVertices, offset);
717715

718716
// Start life time
719717
instanceVertices[offset + ParticleBufferUtils.startLifeTimeOffset] = main.startLifetime.evaluate(
@@ -722,12 +720,10 @@ export class ParticleGenerator {
722720
);
723721

724722
// Direction
725-
instanceVertices[offset + 4] = direction.x;
726-
instanceVertices[offset + 5] = direction.y;
727-
instanceVertices[offset + 6] = direction.z;
723+
direction.copyToArray(instanceVertices, offset + 4);
728724

729725
// Time
730-
instanceVertices[offset + ParticleBufferUtils.timeOffset] = time;
726+
instanceVertices[offset + ParticleBufferUtils.timeOffset] = playTime;
731727

732728
// Color
733729
const startColor = ParticleGenerator._tempColor0;
@@ -736,19 +732,19 @@ export class ParticleGenerator {
736732
startColor.toLinear(startColor);
737733
}
738734

739-
instanceVertices[offset + 8] = startColor.r;
740-
instanceVertices[offset + 9] = startColor.g;
741-
instanceVertices[offset + 10] = startColor.b;
742-
instanceVertices[offset + 11] = startColor.a;
735+
startColor.copyToArray(instanceVertices, offset + 8);
736+
737+
const duration = this.main.duration;
738+
const normalizedEmitAge = (playTime % duration) / duration;
743739

744740
// Start size
745741
const startSizeRand = main._startSizeRand;
746742
if (main.startSize3D) {
747-
instanceVertices[offset + 12] = main.startSizeX.evaluate(undefined, startSizeRand.random());
748-
instanceVertices[offset + 13] = main.startSizeY.evaluate(undefined, startSizeRand.random());
749-
instanceVertices[offset + 14] = main.startSizeZ.evaluate(undefined, startSizeRand.random());
743+
instanceVertices[offset + 12] = main.startSizeX.evaluate(normalizedEmitAge, startSizeRand.random());
744+
instanceVertices[offset + 13] = main.startSizeY.evaluate(normalizedEmitAge, startSizeRand.random());
745+
instanceVertices[offset + 14] = main.startSizeZ.evaluate(normalizedEmitAge, startSizeRand.random());
750746
} else {
751-
const size = main.startSize.evaluate(undefined, startSizeRand.random());
747+
const size = main.startSize.evaluate(normalizedEmitAge, startSizeRand.random());
752748
instanceVertices[offset + 12] = size;
753749
instanceVertices[offset + 13] = size;
754750
instanceVertices[offset + 14] = size;
@@ -815,15 +811,10 @@ export class ParticleGenerator {
815811

816812
if (this.main.simulationSpace === ParticleSimulationSpace.World) {
817813
// Simulation world position
818-
instanceVertices[offset + 27] = pos.x;
819-
instanceVertices[offset + 28] = pos.y;
820-
instanceVertices[offset + 29] = pos.z;
814+
pos.copyToArray(instanceVertices, offset + 27);
821815

822816
// Simulation world position
823-
instanceVertices[offset + 30] = rot.x;
824-
instanceVertices[offset + 31] = rot.y;
825-
instanceVertices[offset + 32] = rot.z;
826-
instanceVertices[offset + 33] = rot.w;
817+
rot.copyToArray(instanceVertices, offset + 30);
827818
}
828819

829820
// Simulation UV

packages/core/src/particle/modules/ParticleCompositeCurve.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Vector2 } from "@galacean/engine-math";
22
import { deepClone, ignoreClone } from "../../clone/CloneManager";
3+
import { UpdateFlagManager } from "../../UpdateFlagManager";
34
import { ParticleCurveMode } from "../enums/ParticleCurveMode";
45
import { CurveKey, ParticleCurve } from "./ParticleCurve";
5-
import { UpdateFlagManager } from "../../UpdateFlagManager";
66

77
/**
88
* Particle composite curve.
@@ -175,6 +175,11 @@ export class ParticleCompositeCurve {
175175
return this.constant;
176176
case ParticleCurveMode.TwoConstants:
177177
return this.constantMin + (this.constantMax - this.constantMin) * lerpFactor;
178+
case ParticleCurveMode.Curve:
179+
return this.curve?._evaluate(time);
180+
case ParticleCurveMode.TwoCurves:
181+
const min = this.curveMin?._evaluate(time);
182+
return min + (this.curveMax?._evaluate(time) - min) * lerpFactor;
178183
default:
179184
break;
180185
}

packages/core/src/particle/modules/ParticleCurve.ts

+29-2
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,10 @@ export class ParticleCurve {
6767
* @param index - The remove key index
6868
*/
6969
removeKey(index: number): void {
70-
this._keys.splice(index, 1);
70+
const keys = this._keys;
71+
const removeKey = keys[index];
72+
keys.splice(index, 1);
7173
this._typeArrayDirty = true;
72-
const removeKey = this._keys[index];
7374
removeKey._unRegisterOnValueChanged(this._updateDispatch);
7475
this._updateDispatch();
7576
}
@@ -86,6 +87,32 @@ export class ParticleCurve {
8687
this._typeArrayDirty = true;
8788
}
8889

90+
/**
91+
* @internal
92+
*/
93+
_evaluate(normalizedAge: number): number {
94+
const { keys } = this;
95+
const { length } = keys;
96+
97+
for (let i = 0; i < length; i++) {
98+
const key = keys[i];
99+
const { time } = key;
100+
if (normalizedAge <= time) {
101+
if (i === 0) {
102+
// Small than first key
103+
return key.value;
104+
} else {
105+
// Between two keys
106+
const { time: lastTime, value: lastValue } = keys[i - 1];
107+
const age = (normalizedAge - lastTime) / (time - lastTime);
108+
return lastValue + (key.value - lastValue) * age;
109+
}
110+
}
111+
}
112+
// Large than last key
113+
return keys[length - 1].value;
114+
}
115+
89116
/**
90117
* @internal
91118
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { CurveKey, ParticleCompositeCurve, ParticleCurve, ParticleCurveMode } from "@galacean/engine-core";
2+
import { describe, expect, it } from "vitest";
3+
4+
describe("ParticleCurve tests", () => {
5+
it("Constructor with const params", () => {
6+
const gradient = new ParticleCompositeCurve(0.5);
7+
expect(gradient.mode).to.equal(ParticleCurveMode.Constant);
8+
expect(gradient.evaluate(undefined, undefined)).to.equal(0.5);
9+
});
10+
11+
it("Constructor with two const params", () => {
12+
const gradient = new ParticleCompositeCurve(0.5, 0.2);
13+
expect(gradient.mode).to.equal(ParticleCurveMode.TwoConstants);
14+
expect(gradient.evaluate(undefined, 0.5)).to.equal(0.35);
15+
expect(gradient.evaluate(undefined, 0.0)).to.equal(0.5);
16+
expect(gradient.evaluate(undefined, 1.0)).to.equal(0.2);
17+
});
18+
19+
it("Constructor with curve params", () => {
20+
const gradient0 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.333)));
21+
expect(gradient0.mode).to.equal(ParticleCurveMode.Curve);
22+
expect(gradient0.evaluate(0.2, undefined)).to.equal(0.333);
23+
24+
const gradient1 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7)));
25+
expect(gradient1.evaluate(0.0, undefined)).to.equal(0.3);
26+
expect(gradient1.evaluate(0.5, undefined)).to.equal(0.6333333333333333);
27+
expect(gradient1.evaluate(0.6, undefined)).to.equal(0.7);
28+
expect(gradient1.evaluate(0.9, undefined)).to.equal(0.7);
29+
expect(gradient1.evaluate(1.0, undefined)).to.equal(0.7);
30+
});
31+
32+
it("Constructor with two curve params", () => {
33+
const curveMin = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
34+
const curveMax = new ParticleCurve(new CurveKey(0.4, 0.5), new CurveKey(1.0, 0.8));
35+
36+
const compositeCurve = new ParticleCompositeCurve(curveMin, curveMax);
37+
38+
expect(compositeCurve.evaluate(0.0, 0.0)).to.equal(0.3);
39+
expect(compositeCurve.evaluate(0.5, 0.0)).to.equal(0.6333333333333333);
40+
expect(compositeCurve.evaluate(0.6, 0.0)).to.equal(0.7);
41+
expect(compositeCurve.evaluate(0.9, 0.0)).to.equal(0.7);
42+
expect(compositeCurve.evaluate(1.0, 0.0)).to.equal(0.7);
43+
44+
expect(compositeCurve.evaluate(0.0, 1.0)).to.equal(0.5);
45+
expect(compositeCurve.evaluate(0.5, 1.0)).to.equal(0.55);
46+
expect(compositeCurve.evaluate(0.6, 1.0)).to.equal(0.6);
47+
expect(compositeCurve.evaluate(0.9, 1.0)).to.equal(0.75);
48+
expect(compositeCurve.evaluate(1.0, 1.0)).to.equal(0.8);
49+
50+
expect(compositeCurve.evaluate(0.6, 0.5)).to.equal(0.6499999999999999);
51+
});
52+
53+
it("Add and remove", () => {
54+
const curve = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
55+
+ expect(curve.keys.length).to.equal(2);
56+
+
57+
58+
curve.addKey(new CurveKey(0, 0.4));
59+
+ expect(curve.keys.length).to.equal(3);
60+
+ expect(curve.keys[0].value).to.equal(0.3);
61+
+
62+
curve.removeKey(2);
63+
+ expect(curve.keys.length).to.equal(2);
64+
+
65+
curve.removeKey(0);
66+
67+
+ expect(curve.keys.length).to.equal(1);
68+
+ expect(curve.keys[0].time).to.equal(0.0);
69+
+ expect(curve.keys[0].value).to.equal(0.4);
70+
+
71+
curve.removeKey(0);
72+
+ expect(curve.keys.length).to.equal(0);
73+
});
74+
});

0 commit comments

Comments
 (0)