mirror of
https://github.com/galacean/engine.git
synced 2026-06-22 02:42:48 +08:00
Particle start size support curve mode (#2568)
* feat: particle start size support curve mode
This commit is contained in:
@@ -237,7 +237,7 @@ export class ParticleGenerator {
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_emit(time: number, count: number): void {
|
||||
_emit(playTime: number, count: number): void {
|
||||
if (this.emission.enabled) {
|
||||
// Wait the existing particles to be retired
|
||||
const notRetireParticleCount = this._getNotRetiredParticleCount();
|
||||
@@ -250,7 +250,7 @@ export class ParticleGenerator {
|
||||
const shape = this.emission.shape;
|
||||
for (let i = 0; i < count; i++) {
|
||||
if (shape?.enabled) {
|
||||
shape._generatePositionAndDirection(this.emission._shapeRand, time, position, direction);
|
||||
shape._generatePositionAndDirection(this.emission._shapeRand, playTime, position, direction);
|
||||
const positionScale = this.main._getPositionScale();
|
||||
position.multiply(positionScale);
|
||||
direction.normalize().multiply(positionScale);
|
||||
@@ -258,7 +258,7 @@ export class ParticleGenerator {
|
||||
position.set(0, 0, 0);
|
||||
direction.set(0, 0, -1);
|
||||
}
|
||||
this._addNewParticle(position, direction, transform, time);
|
||||
this._addNewParticle(position, direction, transform, playTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -669,7 +669,7 @@ export class ParticleGenerator {
|
||||
}
|
||||
}
|
||||
|
||||
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, time: number): void {
|
||||
private _addNewParticle(position: Vector3, direction: Vector3, transform: Transform, playTime: number): void {
|
||||
const firstFreeElement = this._firstFreeElement;
|
||||
let nextFreeElement = firstFreeElement + 1;
|
||||
if (nextFreeElement >= this._currentParticleCount) {
|
||||
@@ -711,9 +711,7 @@ export class ParticleGenerator {
|
||||
const offset = firstFreeElement * ParticleBufferUtils.instanceVertexFloatStride;
|
||||
|
||||
// Position
|
||||
instanceVertices[offset] = position.x;
|
||||
instanceVertices[offset + 1] = position.y;
|
||||
instanceVertices[offset + 2] = position.z;
|
||||
position.copyToArray(instanceVertices, offset);
|
||||
|
||||
// Start life time
|
||||
instanceVertices[offset + ParticleBufferUtils.startLifeTimeOffset] = main.startLifetime.evaluate(
|
||||
@@ -722,12 +720,10 @@ export class ParticleGenerator {
|
||||
);
|
||||
|
||||
// Direction
|
||||
instanceVertices[offset + 4] = direction.x;
|
||||
instanceVertices[offset + 5] = direction.y;
|
||||
instanceVertices[offset + 6] = direction.z;
|
||||
direction.copyToArray(instanceVertices, offset + 4);
|
||||
|
||||
// Time
|
||||
instanceVertices[offset + ParticleBufferUtils.timeOffset] = time;
|
||||
instanceVertices[offset + ParticleBufferUtils.timeOffset] = playTime;
|
||||
|
||||
// Color
|
||||
const startColor = ParticleGenerator._tempColor0;
|
||||
@@ -736,19 +732,19 @@ export class ParticleGenerator {
|
||||
startColor.toLinear(startColor);
|
||||
}
|
||||
|
||||
instanceVertices[offset + 8] = startColor.r;
|
||||
instanceVertices[offset + 9] = startColor.g;
|
||||
instanceVertices[offset + 10] = startColor.b;
|
||||
instanceVertices[offset + 11] = startColor.a;
|
||||
startColor.copyToArray(instanceVertices, offset + 8);
|
||||
|
||||
const duration = this.main.duration;
|
||||
const normalizedEmitAge = (playTime % duration) / duration;
|
||||
|
||||
// Start size
|
||||
const startSizeRand = main._startSizeRand;
|
||||
if (main.startSize3D) {
|
||||
instanceVertices[offset + 12] = main.startSizeX.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 13] = main.startSizeY.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 14] = main.startSizeZ.evaluate(undefined, startSizeRand.random());
|
||||
instanceVertices[offset + 12] = main.startSizeX.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 13] = main.startSizeY.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 14] = main.startSizeZ.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
} else {
|
||||
const size = main.startSize.evaluate(undefined, startSizeRand.random());
|
||||
const size = main.startSize.evaluate(normalizedEmitAge, startSizeRand.random());
|
||||
instanceVertices[offset + 12] = size;
|
||||
instanceVertices[offset + 13] = size;
|
||||
instanceVertices[offset + 14] = size;
|
||||
@@ -815,15 +811,10 @@ export class ParticleGenerator {
|
||||
|
||||
if (this.main.simulationSpace === ParticleSimulationSpace.World) {
|
||||
// Simulation world position
|
||||
instanceVertices[offset + 27] = pos.x;
|
||||
instanceVertices[offset + 28] = pos.y;
|
||||
instanceVertices[offset + 29] = pos.z;
|
||||
pos.copyToArray(instanceVertices, offset + 27);
|
||||
|
||||
// Simulation world position
|
||||
instanceVertices[offset + 30] = rot.x;
|
||||
instanceVertices[offset + 31] = rot.y;
|
||||
instanceVertices[offset + 32] = rot.z;
|
||||
instanceVertices[offset + 33] = rot.w;
|
||||
rot.copyToArray(instanceVertices, offset + 30);
|
||||
}
|
||||
|
||||
// Simulation UV
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Vector2 } from "@galacean/engine-math";
|
||||
import { deepClone, ignoreClone } from "../../clone/CloneManager";
|
||||
import { UpdateFlagManager } from "../../UpdateFlagManager";
|
||||
import { ParticleCurveMode } from "../enums/ParticleCurveMode";
|
||||
import { CurveKey, ParticleCurve } from "./ParticleCurve";
|
||||
import { UpdateFlagManager } from "../../UpdateFlagManager";
|
||||
|
||||
/**
|
||||
* Particle composite curve.
|
||||
@@ -175,6 +175,11 @@ export class ParticleCompositeCurve {
|
||||
return this.constant;
|
||||
case ParticleCurveMode.TwoConstants:
|
||||
return this.constantMin + (this.constantMax - this.constantMin) * lerpFactor;
|
||||
case ParticleCurveMode.Curve:
|
||||
return this.curve?._evaluate(time);
|
||||
case ParticleCurveMode.TwoCurves:
|
||||
const min = this.curveMin?._evaluate(time);
|
||||
return min + (this.curveMax?._evaluate(time) - min) * lerpFactor;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -67,9 +67,10 @@ export class ParticleCurve {
|
||||
* @param index - The remove key index
|
||||
*/
|
||||
removeKey(index: number): void {
|
||||
this._keys.splice(index, 1);
|
||||
const keys = this._keys;
|
||||
const removeKey = keys[index];
|
||||
keys.splice(index, 1);
|
||||
this._typeArrayDirty = true;
|
||||
const removeKey = this._keys[index];
|
||||
removeKey._unRegisterOnValueChanged(this._updateDispatch);
|
||||
this._updateDispatch();
|
||||
}
|
||||
@@ -86,6 +87,32 @@ export class ParticleCurve {
|
||||
this._typeArrayDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
_evaluate(normalizedAge: number): number {
|
||||
const { keys } = this;
|
||||
const { length } = keys;
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = keys[i];
|
||||
const { time } = key;
|
||||
if (normalizedAge <= time) {
|
||||
if (i === 0) {
|
||||
// Small than first key
|
||||
return key.value;
|
||||
} else {
|
||||
// Between two keys
|
||||
const { time: lastTime, value: lastValue } = keys[i - 1];
|
||||
const age = (normalizedAge - lastTime) / (time - lastTime);
|
||||
return lastValue + (key.value - lastValue) * age;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Large than last key
|
||||
return keys[length - 1].value;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
|
||||
74
tests/src/core/particle/ParticleCurve.test.ts
Normal file
74
tests/src/core/particle/ParticleCurve.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { CurveKey, ParticleCompositeCurve, ParticleCurve, ParticleCurveMode } from "@galacean/engine-core";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
describe("ParticleCurve tests", () => {
|
||||
it("Constructor with const params", () => {
|
||||
const gradient = new ParticleCompositeCurve(0.5);
|
||||
expect(gradient.mode).to.equal(ParticleCurveMode.Constant);
|
||||
expect(gradient.evaluate(undefined, undefined)).to.equal(0.5);
|
||||
});
|
||||
|
||||
it("Constructor with two const params", () => {
|
||||
const gradient = new ParticleCompositeCurve(0.5, 0.2);
|
||||
expect(gradient.mode).to.equal(ParticleCurveMode.TwoConstants);
|
||||
expect(gradient.evaluate(undefined, 0.5)).to.equal(0.35);
|
||||
expect(gradient.evaluate(undefined, 0.0)).to.equal(0.5);
|
||||
expect(gradient.evaluate(undefined, 1.0)).to.equal(0.2);
|
||||
});
|
||||
|
||||
it("Constructor with curve params", () => {
|
||||
const gradient0 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.333)));
|
||||
expect(gradient0.mode).to.equal(ParticleCurveMode.Curve);
|
||||
expect(gradient0.evaluate(0.2, undefined)).to.equal(0.333);
|
||||
|
||||
const gradient1 = new ParticleCompositeCurve(new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7)));
|
||||
expect(gradient1.evaluate(0.0, undefined)).to.equal(0.3);
|
||||
expect(gradient1.evaluate(0.5, undefined)).to.equal(0.6333333333333333);
|
||||
expect(gradient1.evaluate(0.6, undefined)).to.equal(0.7);
|
||||
expect(gradient1.evaluate(0.9, undefined)).to.equal(0.7);
|
||||
expect(gradient1.evaluate(1.0, undefined)).to.equal(0.7);
|
||||
});
|
||||
|
||||
it("Constructor with two curve params", () => {
|
||||
const curveMin = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
|
||||
const curveMax = new ParticleCurve(new CurveKey(0.4, 0.5), new CurveKey(1.0, 0.8));
|
||||
|
||||
const compositeCurve = new ParticleCompositeCurve(curveMin, curveMax);
|
||||
|
||||
expect(compositeCurve.evaluate(0.0, 0.0)).to.equal(0.3);
|
||||
expect(compositeCurve.evaluate(0.5, 0.0)).to.equal(0.6333333333333333);
|
||||
expect(compositeCurve.evaluate(0.6, 0.0)).to.equal(0.7);
|
||||
expect(compositeCurve.evaluate(0.9, 0.0)).to.equal(0.7);
|
||||
expect(compositeCurve.evaluate(1.0, 0.0)).to.equal(0.7);
|
||||
|
||||
expect(compositeCurve.evaluate(0.0, 1.0)).to.equal(0.5);
|
||||
expect(compositeCurve.evaluate(0.5, 1.0)).to.equal(0.55);
|
||||
expect(compositeCurve.evaluate(0.6, 1.0)).to.equal(0.6);
|
||||
expect(compositeCurve.evaluate(0.9, 1.0)).to.equal(0.75);
|
||||
expect(compositeCurve.evaluate(1.0, 1.0)).to.equal(0.8);
|
||||
|
||||
expect(compositeCurve.evaluate(0.6, 0.5)).to.equal(0.6499999999999999);
|
||||
});
|
||||
|
||||
it("Add and remove", () => {
|
||||
const curve = new ParticleCurve(new CurveKey(0, 0.3), new CurveKey(0.6, 0.7));
|
||||
+ expect(curve.keys.length).to.equal(2);
|
||||
+
|
||||
|
||||
curve.addKey(new CurveKey(0, 0.4));
|
||||
+ expect(curve.keys.length).to.equal(3);
|
||||
+ expect(curve.keys[0].value).to.equal(0.3);
|
||||
+
|
||||
curve.removeKey(2);
|
||||
+ expect(curve.keys.length).to.equal(2);
|
||||
+
|
||||
curve.removeKey(0);
|
||||
|
||||
+ expect(curve.keys.length).to.equal(1);
|
||||
+ expect(curve.keys[0].time).to.equal(0.0);
|
||||
+ expect(curve.keys[0].value).to.equal(0.4);
|
||||
+
|
||||
curve.removeKey(0);
|
||||
+ expect(curve.keys.length).to.equal(0);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user