@flighthq/particleemitter 0.1.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/dist/emitParticleBurst.d.ts +15 -0
- package/dist/emitParticleBurst.d.ts.map +1 -0
- package/dist/emitParticleBurst.js +113 -0
- package/dist/emitParticleBurst.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/particleEmitter.d.ts +72 -0
- package/dist/particleEmitter.d.ts.map +1 -0
- package/dist/particleEmitter.js +328 -0
- package/dist/particleEmitter.js.map +1 -0
- package/dist/prewarmParticleEmitter.d.ts +5 -0
- package/dist/prewarmParticleEmitter.d.ts.map +1 -0
- package/dist/prewarmParticleEmitter.js +13 -0
- package/dist/prewarmParticleEmitter.js.map +1 -0
- package/dist/stepParticleEmitter.d.ts +21 -0
- package/dist/stepParticleEmitter.d.ts.map +1 -0
- package/dist/stepParticleEmitter.js +30 -0
- package/dist/stepParticleEmitter.js.map +1 -0
- package/dist/updateParticleEmitter.d.ts +8 -0
- package/dist/updateParticleEmitter.d.ts.map +1 -0
- package/dist/updateParticleEmitter.js +313 -0
- package/dist/updateParticleEmitter.js.map +1 -0
- package/package.json +42 -0
- package/src/deterministic.test.ts +53 -0
- package/src/emitParticleBurst.test.ts +102 -0
- package/src/particleEmitter.test.ts +516 -0
- package/src/prewarmParticleEmitter.test.ts +67 -0
- package/src/stepParticleEmitter.test.ts +86 -0
- package/src/updateParticleEmitter.test.ts +630 -0
- package/src/updateParticleEmitterIntegration.test.ts +270 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ParticleCollider, ParticleEmitter, ParticleEmitterCallbacks, ParticleEmitterConfig, ParticleEmitterState, ParticleForce, WorldTransform2D } from '@flighthq/types';
|
|
2
|
+
/**
|
|
3
|
+
* Convenience wrapper for the SoA typed-array emitter that folds the canonical three-step update
|
|
4
|
+
* sequence — `applyParticleForces` → `updateParticleEmitter` → `applyParticleCollisions` — into a
|
|
5
|
+
* single call.
|
|
6
|
+
*
|
|
7
|
+
* The three primitives remain exported for advanced use (custom interleaving, per-force-group
|
|
8
|
+
* timing, etc.). Use this function for the common case where all forces and colliders apply each
|
|
9
|
+
* frame in the documented order.
|
|
10
|
+
*
|
|
11
|
+
* @param emitter - The SoA particle emitter node.
|
|
12
|
+
* @param state - Mutable per-emitter simulation state.
|
|
13
|
+
* @param config - Immutable emitter configuration.
|
|
14
|
+
* @param deltaTime - Elapsed time in seconds since the last step.
|
|
15
|
+
* @param forces - Optional force fields to apply before integration (e.g. wind, attractor).
|
|
16
|
+
* @param colliders - Optional colliders to resolve after integration (e.g. plane, circle).
|
|
17
|
+
* @param callbacks - Optional spawn/death callbacks.
|
|
18
|
+
* @param worldTransform - World-space transform for world-space emitters.
|
|
19
|
+
*/
|
|
20
|
+
export declare function stepParticleEmitter(emitter: ParticleEmitter, state: ParticleEmitterState, config: Readonly<ParticleEmitterConfig>, deltaTime: number, forces?: ReadonlyArray<ParticleForce>, colliders?: ReadonlyArray<ParticleCollider>, callbacks?: ParticleEmitterCallbacks, worldTransform?: Readonly<WorldTransform2D>): void;
|
|
21
|
+
//# sourceMappingURL=stepParticleEmitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepParticleEmitter.d.ts","sourceRoot":"","sources":["../src/stepParticleEmitter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,aAAa,EACb,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAIzB;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EACvC,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,aAAa,CAAC,aAAa,CAAC,EACrC,SAAS,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,EAC3C,SAAS,CAAC,EAAE,wBAAwB,EACpC,cAAc,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAC1C,IAAI,CAQN"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { applyParticleCollisions, applyParticleForces } from '@flighthq/particles';
|
|
2
|
+
import { updateParticleEmitter } from './updateParticleEmitter';
|
|
3
|
+
/**
|
|
4
|
+
* Convenience wrapper for the SoA typed-array emitter that folds the canonical three-step update
|
|
5
|
+
* sequence — `applyParticleForces` → `updateParticleEmitter` → `applyParticleCollisions` — into a
|
|
6
|
+
* single call.
|
|
7
|
+
*
|
|
8
|
+
* The three primitives remain exported for advanced use (custom interleaving, per-force-group
|
|
9
|
+
* timing, etc.). Use this function for the common case where all forces and colliders apply each
|
|
10
|
+
* frame in the documented order.
|
|
11
|
+
*
|
|
12
|
+
* @param emitter - The SoA particle emitter node.
|
|
13
|
+
* @param state - Mutable per-emitter simulation state.
|
|
14
|
+
* @param config - Immutable emitter configuration.
|
|
15
|
+
* @param deltaTime - Elapsed time in seconds since the last step.
|
|
16
|
+
* @param forces - Optional force fields to apply before integration (e.g. wind, attractor).
|
|
17
|
+
* @param colliders - Optional colliders to resolve after integration (e.g. plane, circle).
|
|
18
|
+
* @param callbacks - Optional spawn/death callbacks.
|
|
19
|
+
* @param worldTransform - World-space transform for world-space emitters.
|
|
20
|
+
*/
|
|
21
|
+
export function stepParticleEmitter(emitter, state, config, deltaTime, forces, colliders, callbacks, worldTransform) {
|
|
22
|
+
if (forces != null && forces.length > 0) {
|
|
23
|
+
applyParticleForces(emitter, state, forces, deltaTime);
|
|
24
|
+
}
|
|
25
|
+
updateParticleEmitter(emitter, state, config, deltaTime, callbacks, worldTransform);
|
|
26
|
+
if (colliders != null && colliders.length > 0) {
|
|
27
|
+
applyParticleCollisions(emitter, state, colliders);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=stepParticleEmitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stepParticleEmitter.js","sourceRoot":"","sources":["../src/stepParticleEmitter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAWnF,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAEhE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAwB,EACxB,KAA2B,EAC3B,MAAuC,EACvC,SAAiB,EACjB,MAAqC,EACrC,SAA2C,EAC3C,SAAoC,EACpC,cAA2C;IAE3C,IAAI,MAAM,IAAI,IAAI,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxC,mBAAmB,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;IACzD,CAAC;IACD,qBAAqB,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IACpF,IAAI,SAAS,IAAI,IAAI,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9C,uBAAuB,CAAC,OAAO,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ParticleEmitter, ParticleEmitterCallbacks, ParticleEmitterConfig, ParticleEmitterState, WorldTransform2D } from '@flighthq/types';
|
|
2
|
+
export type { ParticleEmitterCallbacks, WorldTransform2D };
|
|
3
|
+
/** True once a finite, non-looping emitter has finished emitting AND all of its
|
|
4
|
+
* particles have died — i.e. a one-shot effect that is safe to recycle/remove.
|
|
5
|
+
* Always false for infinite or looping emitters (they never finish). */
|
|
6
|
+
export declare function isParticleEmitterComplete(emitter: ParticleEmitter, state: Readonly<ParticleEmitterState>, config: Readonly<ParticleEmitterConfig>): boolean;
|
|
7
|
+
export declare function updateParticleEmitter(emitter: ParticleEmitter, state: ParticleEmitterState, config: Readonly<ParticleEmitterConfig>, deltaTime: number, callbacks?: ParticleEmitterCallbacks, worldTransform?: Readonly<WorldTransform2D>): void;
|
|
8
|
+
//# sourceMappingURL=updateParticleEmitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateParticleEmitter.d.ts","sourceRoot":"","sources":["../src/updateParticleEmitter.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EACV,eAAe,EACf,wBAAwB,EACxB,qBAAqB,EACrB,oBAAoB,EACpB,gBAAgB,EACjB,MAAM,iBAAiB,CAAC;AAIzB,YAAY,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,CAAC;AAK3D;;yEAEyE;AACzE,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,QAAQ,CAAC,oBAAoB,CAAC,EACrC,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,GACtC,OAAO,CAGT;AAQD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,oBAAoB,EAC3B,MAAM,EAAE,QAAQ,CAAC,qBAAqB,CAAC,EACvC,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,wBAAwB,EACpC,cAAc,CAAC,EAAE,QAAQ,CAAC,gBAAgB,CAAC,GAC1C,IAAI,CAgUN"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import { invalidateNodeLocalBounds } from '@flighthq/node';
|
|
2
|
+
import { ensureParticleEmitterStateCapacity, getParticleEmitterSignals, sampleParticleColorCurve, sampleParticleCurve, } from '@flighthq/particles';
|
|
3
|
+
import { reserveParticleEmitter } from './particleEmitter';
|
|
4
|
+
const PARTICLE_TRANSFORM_STRIDE = 4; // must match ./particleEmitter
|
|
5
|
+
const TWO_PI = Math.PI * 2;
|
|
6
|
+
/** True once a finite, non-looping emitter has finished emitting AND all of its
|
|
7
|
+
* particles have died — i.e. a one-shot effect that is safe to recycle/remove.
|
|
8
|
+
* Always false for infinite or looping emitters (they never finish). */
|
|
9
|
+
export function isParticleEmitterComplete(emitter, state, config) {
|
|
10
|
+
if (config.duration <= 0 || config.loop)
|
|
11
|
+
return false;
|
|
12
|
+
return state.emitterAge >= config.duration && emitter.data.particleCount === 0;
|
|
13
|
+
}
|
|
14
|
+
/** Whether an emitter with the given config is still spawning, given how long it
|
|
15
|
+
* has been emitting. Infinite (duration <= 0) and looping emitters always emit. */
|
|
16
|
+
function isEmitting(config, emitterAge) {
|
|
17
|
+
return config.duration <= 0 || config.loop || emitterAge < config.duration;
|
|
18
|
+
}
|
|
19
|
+
export function updateParticleEmitter(emitter, state, config, deltaTime, callbacks, worldTransform) {
|
|
20
|
+
const data = emitter.data;
|
|
21
|
+
// Sync world-space flag to data so renderers can read it.
|
|
22
|
+
data.worldSpace = config.worldSpace;
|
|
23
|
+
// Guard against a zero or negative time step: no time has elapsed, so there is
|
|
24
|
+
// nothing to age, move, or spawn. Critically, this avoids dividing by deltaTime when
|
|
25
|
+
// computing emitter velocity below — a zero-deltaTime frame that still fires a burst
|
|
26
|
+
// would otherwise bake Infinity/NaN velocities into freshly spawned particles
|
|
27
|
+
// and corrupt them for the rest of their lifetime.
|
|
28
|
+
if (deltaTime <= 0)
|
|
29
|
+
return;
|
|
30
|
+
// ── Emitter velocity (for velocity inheritance and trail interpolation) ───────
|
|
31
|
+
// In world-space mode we track the world origin; in local-space mode we track
|
|
32
|
+
// the emitter node position in parent space.
|
|
33
|
+
const trackX = config.worldSpace && worldTransform != null ? worldTransform.tx : emitter.x;
|
|
34
|
+
const trackY = config.worldSpace && worldTransform != null ? worldTransform.ty : emitter.y;
|
|
35
|
+
const hasVelInherit = config.velocityInheritance !== 0;
|
|
36
|
+
let emitterVelX = 0;
|
|
37
|
+
let emitterVelY = 0;
|
|
38
|
+
if (!isNaN(state.prevX)) {
|
|
39
|
+
emitterVelX = (trackX - state.prevX) / deltaTime;
|
|
40
|
+
emitterVelY = (trackY - state.prevY) / deltaTime;
|
|
41
|
+
}
|
|
42
|
+
// ── Phase 1: age all live particles, compact dead ones ──────────────────────
|
|
43
|
+
const lifetimes = state.lifetimes;
|
|
44
|
+
const velocities = state.velocities;
|
|
45
|
+
const scales = state.scales;
|
|
46
|
+
const rotationSpeeds = state.rotationSpeeds;
|
|
47
|
+
const gx = config.gravityX * deltaTime;
|
|
48
|
+
const gy = config.gravityY * deltaTime;
|
|
49
|
+
const { colorStartR, colorStartG, colorStartB, colorEndR, colorEndG, colorEndB } = config;
|
|
50
|
+
const hasColorVariance = config.colorStartVarianceR !== 0 ||
|
|
51
|
+
config.colorStartVarianceG !== 0 ||
|
|
52
|
+
config.colorStartVarianceB !== 0 ||
|
|
53
|
+
config.colorEndVarianceR !== 0 ||
|
|
54
|
+
config.colorEndVarianceG !== 0 ||
|
|
55
|
+
config.colorEndVarianceB !== 0;
|
|
56
|
+
const hasColorGradient = hasColorVariance || colorStartR !== colorEndR || colorStartG !== colorEndG || colorStartB !== colorEndB;
|
|
57
|
+
// Opt-in lifetime curves override the linear paths below; an emitter without
|
|
58
|
+
// curves never touches these branches beyond a predicted-not-taken check.
|
|
59
|
+
const alphaCurve = config.alphaCurve;
|
|
60
|
+
const colorCurve = config.colorCurve;
|
|
61
|
+
const scaleCurve = config.scaleCurve;
|
|
62
|
+
const hasAlphaCurve = alphaCurve != null && alphaCurve.length > 0;
|
|
63
|
+
const hasColorCurve = colorCurve != null && colorCurve.length >= 3;
|
|
64
|
+
const hasScaleCurve = scaleCurve != null && scaleCurve.length > 0;
|
|
65
|
+
const hasScaleAnim = config.scaleEnd !== 1 || hasScaleCurve;
|
|
66
|
+
const hasColorWork = hasColorCurve || hasColorGradient;
|
|
67
|
+
const hasRotationSpeed = config.rotationSpeedMin !== 0 || config.rotationSpeedMax !== 0;
|
|
68
|
+
const hasFlipbook = config.frameCount > 1;
|
|
69
|
+
const onDeath = callbacks?.onDeath;
|
|
70
|
+
const onSpawn = callbacks?.onSpawn;
|
|
71
|
+
// Opt-in signals: null when enableParticleEmitterSignals has not been called on this state.
|
|
72
|
+
const signals = getParticleEmitterSignals(state);
|
|
73
|
+
let liveCount = data.particleCount;
|
|
74
|
+
let i = 0;
|
|
75
|
+
while (i < liveCount) {
|
|
76
|
+
const lt = i * 2;
|
|
77
|
+
lifetimes[lt] += deltaTime;
|
|
78
|
+
if (lifetimes[lt] >= lifetimes[lt + 1]) {
|
|
79
|
+
if (onDeath !== undefined || signals !== null) {
|
|
80
|
+
const tt = i * PARTICLE_TRANSFORM_STRIDE;
|
|
81
|
+
const dx = data.transforms[tt];
|
|
82
|
+
const dy = data.transforms[tt + 1];
|
|
83
|
+
onDeath?.(dx, dy);
|
|
84
|
+
signals?.onParticleDeath.emit(dx, dy);
|
|
85
|
+
}
|
|
86
|
+
liveCount--;
|
|
87
|
+
if (i < liveCount) {
|
|
88
|
+
const lt2 = liveCount * 2;
|
|
89
|
+
lifetimes[lt] = lifetimes[lt2];
|
|
90
|
+
lifetimes[lt + 1] = lifetimes[lt2 + 1];
|
|
91
|
+
const vt = i * 2;
|
|
92
|
+
const vt2 = liveCount * 2;
|
|
93
|
+
velocities[vt] = velocities[vt2];
|
|
94
|
+
velocities[vt + 1] = velocities[vt2 + 1];
|
|
95
|
+
const tt = i * PARTICLE_TRANSFORM_STRIDE;
|
|
96
|
+
const tt2 = liveCount * PARTICLE_TRANSFORM_STRIDE;
|
|
97
|
+
data.transforms[tt] = data.transforms[tt2];
|
|
98
|
+
data.transforms[tt + 1] = data.transforms[tt2 + 1];
|
|
99
|
+
data.transforms[tt + 2] = data.transforms[tt2 + 2];
|
|
100
|
+
data.transforms[tt + 3] = data.transforms[tt2 + 3];
|
|
101
|
+
data.alphas[i] = data.alphas[liveCount];
|
|
102
|
+
data.ids[i] = data.ids[liveCount];
|
|
103
|
+
const ct = i * 3;
|
|
104
|
+
const ct2 = liveCount * 3;
|
|
105
|
+
data.colors[ct] = data.colors[ct2];
|
|
106
|
+
data.colors[ct + 1] = data.colors[ct2 + 1];
|
|
107
|
+
data.colors[ct + 2] = data.colors[ct2 + 2];
|
|
108
|
+
scales[i] = scales[liveCount];
|
|
109
|
+
rotationSpeeds[i] = rotationSpeeds[liveCount];
|
|
110
|
+
if (hasColorVariance) {
|
|
111
|
+
state.colorBirth[ct] = state.colorBirth[ct2];
|
|
112
|
+
state.colorBirth[ct + 1] = state.colorBirth[ct2 + 1];
|
|
113
|
+
state.colorBirth[ct + 2] = state.colorBirth[ct2 + 2];
|
|
114
|
+
state.colorDeath[ct] = state.colorDeath[ct2];
|
|
115
|
+
state.colorDeath[ct + 1] = state.colorDeath[ct2 + 1];
|
|
116
|
+
state.colorDeath[ct + 2] = state.colorDeath[ct2 + 2];
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const vt = i * 2;
|
|
122
|
+
velocities[vt] += gx;
|
|
123
|
+
velocities[vt + 1] += gy;
|
|
124
|
+
const tt = i * PARTICLE_TRANSFORM_STRIDE;
|
|
125
|
+
data.transforms[tt] += velocities[vt] * deltaTime;
|
|
126
|
+
data.transforms[tt + 1] += velocities[vt + 1] * deltaTime;
|
|
127
|
+
const lifeFraction = lifetimes[lt] / lifetimes[lt + 1];
|
|
128
|
+
data.alphas[i] = hasAlphaCurve
|
|
129
|
+
? sampleParticleCurve(alphaCurve, lifeFraction)
|
|
130
|
+
: config.alphaStart + (config.alphaEnd - config.alphaStart) * lifeFraction;
|
|
131
|
+
if (hasColorWork) {
|
|
132
|
+
const ct = i * 3;
|
|
133
|
+
if (hasColorCurve) {
|
|
134
|
+
sampleParticleColorCurve(data.colors, ct, colorCurve, lifeFraction);
|
|
135
|
+
}
|
|
136
|
+
else if (hasColorVariance) {
|
|
137
|
+
data.colors[ct] = state.colorBirth[ct] + (state.colorDeath[ct] - state.colorBirth[ct]) * lifeFraction;
|
|
138
|
+
data.colors[ct + 1] =
|
|
139
|
+
state.colorBirth[ct + 1] + (state.colorDeath[ct + 1] - state.colorBirth[ct + 1]) * lifeFraction;
|
|
140
|
+
data.colors[ct + 2] =
|
|
141
|
+
state.colorBirth[ct + 2] + (state.colorDeath[ct + 2] - state.colorBirth[ct + 2]) * lifeFraction;
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
data.colors[ct] = colorStartR + (colorEndR - colorStartR) * lifeFraction;
|
|
145
|
+
data.colors[ct + 1] = colorStartG + (colorEndG - colorStartG) * lifeFraction;
|
|
146
|
+
data.colors[ct + 2] = colorStartB + (colorEndB - colorStartB) * lifeFraction;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (hasScaleAnim) {
|
|
150
|
+
const scaleFactor = hasScaleCurve
|
|
151
|
+
? sampleParticleCurve(scaleCurve, lifeFraction)
|
|
152
|
+
: 1 + (config.scaleEnd - 1) * lifeFraction;
|
|
153
|
+
data.transforms[tt + 3] = scales[i] * scaleFactor;
|
|
154
|
+
}
|
|
155
|
+
if (hasRotationSpeed) {
|
|
156
|
+
data.transforms[tt + 2] += rotationSpeeds[i] * deltaTime;
|
|
157
|
+
}
|
|
158
|
+
if (hasFlipbook) {
|
|
159
|
+
const frame = Math.floor(lifetimes[lt] * config.frameRate) % config.frameCount;
|
|
160
|
+
data.ids[i] = config.regionIdMin + frame;
|
|
161
|
+
}
|
|
162
|
+
i++;
|
|
163
|
+
}
|
|
164
|
+
data.particleCount = liveCount;
|
|
165
|
+
// ── Phase 2: spawn new particles ─────────────────────────────────────────────
|
|
166
|
+
// A finite, non-looping emitter stops spawning once its duration elapses;
|
|
167
|
+
// existing particles keep ageing out (use isParticleEmitterComplete to detect the end).
|
|
168
|
+
const emitting = isEmitting(config, state.emitterAge);
|
|
169
|
+
if (config.duration > 0 && !config.loop)
|
|
170
|
+
state.emitterAge += deltaTime;
|
|
171
|
+
state.spawnAccumulator += emitting ? config.spawnRate * deltaTime : 0;
|
|
172
|
+
let toSpawn = Math.floor(state.spawnAccumulator);
|
|
173
|
+
state.spawnAccumulator -= toSpawn;
|
|
174
|
+
if (emitting && config.burstCount > 0) {
|
|
175
|
+
state.burstTimer -= deltaTime;
|
|
176
|
+
if (state.burstTimer <= 0) {
|
|
177
|
+
toSpawn += config.burstCount;
|
|
178
|
+
state.burstTimer = config.burstInterval > 0 ? config.burstInterval : Infinity;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
const maxNew = config.maxParticles - liveCount;
|
|
182
|
+
if (toSpawn > maxNew)
|
|
183
|
+
toSpawn = maxNew;
|
|
184
|
+
if (toSpawn > 0) {
|
|
185
|
+
const newCount = liveCount + toSpawn;
|
|
186
|
+
reserveParticleEmitter(emitter, newCount);
|
|
187
|
+
ensureParticleEmitterStateCapacity(state, newCount, hasColorVariance);
|
|
188
|
+
const baseAngle = Math.atan2(config.directionY, config.directionX);
|
|
189
|
+
const regionRange = config.regionIdMax - config.regionIdMin;
|
|
190
|
+
const regionIdMin = config.regionIdMin;
|
|
191
|
+
const rotSpeedRange = config.rotationSpeedMax - config.rotationSpeedMin;
|
|
192
|
+
const hasRotSpeed = config.rotationSpeedMin !== 0 || config.rotationSpeedMax !== 0;
|
|
193
|
+
// World-space trail: distribute spawn origins along the path from prevPos → currentPos.
|
|
194
|
+
const doTrail = config.worldSpace && worldTransform != null && !isNaN(state.prevX);
|
|
195
|
+
const prevPathX = doTrail ? state.prevX : trackX;
|
|
196
|
+
const prevPathY = doTrail ? state.prevY : trackY;
|
|
197
|
+
for (let s = 0; s < toSpawn; s++) {
|
|
198
|
+
const idx = liveCount + s;
|
|
199
|
+
// Lifetime
|
|
200
|
+
const lifetime = config.lifetimeMin + state.random() * (config.lifetimeMax - config.lifetimeMin);
|
|
201
|
+
const lt = idx * 2;
|
|
202
|
+
state.lifetimes[lt] = 0;
|
|
203
|
+
state.lifetimes[lt + 1] = lifetime;
|
|
204
|
+
// Velocity direction in local/emitter space
|
|
205
|
+
const angle = baseAngle + (state.random() - 0.5) * 2 * config.spread;
|
|
206
|
+
const speed = config.speedMin + state.random() * (config.speedMax - config.speedMin);
|
|
207
|
+
let vx = Math.cos(angle) * speed;
|
|
208
|
+
let vy = Math.sin(angle) * speed;
|
|
209
|
+
// Spawn position (local to emitter, or shape offset)
|
|
210
|
+
let spawnX = 0;
|
|
211
|
+
let spawnY = 0;
|
|
212
|
+
if (config.emitterShape === 'circle' && config.emitterRadius > 0) {
|
|
213
|
+
const r = Math.sqrt(state.random()) * config.emitterRadius;
|
|
214
|
+
const a = state.random() * TWO_PI;
|
|
215
|
+
spawnX = Math.cos(a) * r;
|
|
216
|
+
spawnY = Math.sin(a) * r;
|
|
217
|
+
}
|
|
218
|
+
else if (config.emitterShape === 'rect' && (config.emitterWidth > 0 || config.emitterHeight > 0)) {
|
|
219
|
+
spawnX = (state.random() - 0.5) * config.emitterWidth;
|
|
220
|
+
spawnY = (state.random() - 0.5) * config.emitterHeight;
|
|
221
|
+
}
|
|
222
|
+
// World-space: transform spawn position and velocity into world space,
|
|
223
|
+
// and distribute origins along the emitter's movement path (trail interpolation).
|
|
224
|
+
if (config.worldSpace && worldTransform != null) {
|
|
225
|
+
const wt = worldTransform;
|
|
226
|
+
// Trail: interpolate origin between prev and current world position
|
|
227
|
+
const t = toSpawn > 1 ? s / (toSpawn - 1) : 1;
|
|
228
|
+
const originX = prevPathX + (trackX - prevPathX) * t;
|
|
229
|
+
const originY = prevPathY + (trackY - prevPathY) * t;
|
|
230
|
+
// Apply rotation+scale of world transform to shape offset, then add trail origin
|
|
231
|
+
const wx = wt.a * spawnX + wt.c * spawnY + originX;
|
|
232
|
+
const wy = wt.b * spawnX + wt.d * spawnY + originY;
|
|
233
|
+
spawnX = wx;
|
|
234
|
+
spawnY = wy;
|
|
235
|
+
// Rotate velocity by world transform (no translation for vectors)
|
|
236
|
+
const wvx = wt.a * vx + wt.c * vy;
|
|
237
|
+
const wvy = wt.b * vx + wt.d * vy;
|
|
238
|
+
vx = wvx;
|
|
239
|
+
vy = wvy;
|
|
240
|
+
}
|
|
241
|
+
// Velocity inheritance: blend emitter velocity into new particle velocity
|
|
242
|
+
if (hasVelInherit && !isNaN(state.prevX)) {
|
|
243
|
+
vx += emitterVelX * config.velocityInheritance;
|
|
244
|
+
vy += emitterVelY * config.velocityInheritance;
|
|
245
|
+
}
|
|
246
|
+
const vt = idx * 2;
|
|
247
|
+
state.velocities[vt] = vx;
|
|
248
|
+
state.velocities[vt + 1] = vy;
|
|
249
|
+
const spawnScale = config.scaleMin + state.random() * (config.scaleMax - config.scaleMin);
|
|
250
|
+
state.scales[idx] = spawnScale;
|
|
251
|
+
const tt = idx * PARTICLE_TRANSFORM_STRIDE;
|
|
252
|
+
data.transforms[tt] = spawnX;
|
|
253
|
+
data.transforms[tt + 1] = spawnY;
|
|
254
|
+
data.transforms[tt + 2] = angle;
|
|
255
|
+
data.transforms[tt + 3] = hasScaleCurve ? spawnScale * sampleParticleCurve(scaleCurve, 0) : spawnScale;
|
|
256
|
+
data.alphas[idx] = hasAlphaCurve ? sampleParticleCurve(alphaCurve, 0) : config.alphaStart;
|
|
257
|
+
// Color — curve takes precedence, then per-particle variance, then constants
|
|
258
|
+
const ct = idx * 3;
|
|
259
|
+
if (hasColorCurve) {
|
|
260
|
+
sampleParticleColorCurve(data.colors, ct, colorCurve, 0);
|
|
261
|
+
}
|
|
262
|
+
else if (hasColorVariance) {
|
|
263
|
+
const r0 = clamp01(colorStartR + (state.random() - 0.5) * 2 * config.colorStartVarianceR);
|
|
264
|
+
const g0 = clamp01(colorStartG + (state.random() - 0.5) * 2 * config.colorStartVarianceG);
|
|
265
|
+
const b0 = clamp01(colorStartB + (state.random() - 0.5) * 2 * config.colorStartVarianceB);
|
|
266
|
+
const r1 = clamp01(colorEndR + (state.random() - 0.5) * 2 * config.colorEndVarianceR);
|
|
267
|
+
const g1 = clamp01(colorEndG + (state.random() - 0.5) * 2 * config.colorEndVarianceG);
|
|
268
|
+
const b1 = clamp01(colorEndB + (state.random() - 0.5) * 2 * config.colorEndVarianceB);
|
|
269
|
+
state.colorBirth[ct] = r0;
|
|
270
|
+
state.colorBirth[ct + 1] = g0;
|
|
271
|
+
state.colorBirth[ct + 2] = b0;
|
|
272
|
+
state.colorDeath[ct] = r1;
|
|
273
|
+
state.colorDeath[ct + 1] = g1;
|
|
274
|
+
state.colorDeath[ct + 2] = b1;
|
|
275
|
+
data.colors[ct] = r0;
|
|
276
|
+
data.colors[ct + 1] = g0;
|
|
277
|
+
data.colors[ct + 2] = b0;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
data.colors[ct] = colorStartR;
|
|
281
|
+
data.colors[ct + 1] = colorStartG;
|
|
282
|
+
data.colors[ct + 2] = colorStartB;
|
|
283
|
+
}
|
|
284
|
+
data.ids[idx] =
|
|
285
|
+
regionIdMin + (config.frameCount > 1 ? 0 : regionRange > 0 ? (state.random() * regionRange) | 0 : 0);
|
|
286
|
+
state.rotationSpeeds[idx] = hasRotSpeed ? config.rotationSpeedMin + state.random() * rotSpeedRange : 0;
|
|
287
|
+
onSpawn?.(spawnX, spawnY);
|
|
288
|
+
if (signals !== null) {
|
|
289
|
+
signals.onParticleSpawn.emit(spawnX, spawnY, state.velocities[vt], state.velocities[vt + 1]);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
data.particleCount = newCount;
|
|
293
|
+
}
|
|
294
|
+
// Update prev-position tracking for next frame.
|
|
295
|
+
state.prevX = trackX;
|
|
296
|
+
state.prevY = trackY;
|
|
297
|
+
// Mirror the live per-particle velocities into the render data so the velocity G-buffer writer can
|
|
298
|
+
// smear each particle by its own vector. state.velocities is kept aligned with data.transforms by the
|
|
299
|
+
// compaction above, so the leading particleCount entries are the live particles in render order.
|
|
300
|
+
const liveVelocityCount = data.particleCount * 2;
|
|
301
|
+
if (data.velocities.length >= liveVelocityCount) {
|
|
302
|
+
data.velocities.set(state.velocities.subarray(0, liveVelocityCount));
|
|
303
|
+
}
|
|
304
|
+
// Fire onEmitterComplete when a finite emitter has just finished and all particles are gone.
|
|
305
|
+
if (signals !== null && isParticleEmitterComplete(emitter, state, config)) {
|
|
306
|
+
signals.onEmitterComplete.emit();
|
|
307
|
+
}
|
|
308
|
+
invalidateNodeLocalBounds(emitter);
|
|
309
|
+
}
|
|
310
|
+
function clamp01(v) {
|
|
311
|
+
return v < 0 ? 0 : v > 1 ? 1 : v;
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=updateParticleEmitter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updateParticleEmitter.js","sourceRoot":"","sources":["../src/updateParticleEmitter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@flighthq/particleemitter",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"default": "./dist/index.js"
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist",
|
|
15
|
+
"src/**/*.test.ts",
|
|
16
|
+
"!dist/**/*.test.js",
|
|
17
|
+
"!dist/**/*.test.d.ts",
|
|
18
|
+
"!dist/**/*.test.js.map",
|
|
19
|
+
"!dist/**/*.test.d.ts.map"
|
|
20
|
+
],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"build": "tsc -b",
|
|
23
|
+
"clean": "tsc -b --clean",
|
|
24
|
+
"test": "vitest run --config vitest.config.ts",
|
|
25
|
+
"test:watch": "vitest --watch --config vitest.config.ts",
|
|
26
|
+
"prepack": "npm run clean && npm run clean:dist && npm run build",
|
|
27
|
+
"clean:dist": "tsx ../../scripts/clean-package-dist.ts"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@flighthq/displayobject": "0.1.0",
|
|
31
|
+
"@flighthq/geometry": "0.1.0",
|
|
32
|
+
"@flighthq/node": "0.1.0",
|
|
33
|
+
"@flighthq/particles": "0.1.0",
|
|
34
|
+
"@flighthq/types": "0.1.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@flighthq/math": "*",
|
|
38
|
+
"typescript": "^5.3.0"
|
|
39
|
+
},
|
|
40
|
+
"description": "ParticleEmitter display-object composition layer over @flighthq/particles",
|
|
41
|
+
"sideEffects": false
|
|
42
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createRandomSource } from '@flighthq/math';
|
|
2
|
+
import { createParticleEmitterConfig, createParticleEmitterState } from '@flighthq/particles';
|
|
3
|
+
import type { TextureAtlas } from '@flighthq/types';
|
|
4
|
+
|
|
5
|
+
import { createParticleEmitter } from './particleEmitter';
|
|
6
|
+
import { stepParticleEmitter } from './stepParticleEmitter';
|
|
7
|
+
|
|
8
|
+
function makeAtlas(): TextureAtlas {
|
|
9
|
+
return {
|
|
10
|
+
image: null,
|
|
11
|
+
regions: [{ id: 0, x: 0, y: 0, width: 32, height: 32, pivotX: null, pivotY: null }],
|
|
12
|
+
} as unknown as TextureAtlas;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('deterministic replay', () => {
|
|
16
|
+
it('produces byte-identical SoA buffers from two identically-seeded emitters', () => {
|
|
17
|
+
const atlas = makeAtlas();
|
|
18
|
+
const config = createParticleEmitterConfig({
|
|
19
|
+
spawnRate: 20,
|
|
20
|
+
maxParticles: 100,
|
|
21
|
+
lifetimeMin: 0.5,
|
|
22
|
+
lifetimeMax: 2,
|
|
23
|
+
speedMin: 50,
|
|
24
|
+
speedMax: 150,
|
|
25
|
+
spread: Math.PI,
|
|
26
|
+
gravityY: 100,
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const emitterA = createParticleEmitter({ data: { atlas } });
|
|
30
|
+
const stateA = createParticleEmitterState(createRandomSource(42));
|
|
31
|
+
|
|
32
|
+
const emitterB = createParticleEmitter({ data: { atlas } });
|
|
33
|
+
const stateB = createParticleEmitterState(createRandomSource(42));
|
|
34
|
+
|
|
35
|
+
const steps = [0.016, 0.033, 0.016, 0.05, 0.016, 0.016, 0.033, 0.016];
|
|
36
|
+
for (const dt of steps) {
|
|
37
|
+
stepParticleEmitter(emitterA, stateA, config, dt);
|
|
38
|
+
stepParticleEmitter(emitterB, stateB, config, dt);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
expect(emitterA.data.particleCount).toBeGreaterThan(0);
|
|
42
|
+
expect(emitterA.data.particleCount).toBe(emitterB.data.particleCount);
|
|
43
|
+
|
|
44
|
+
const count = emitterA.data.particleCount;
|
|
45
|
+
expect(emitterA.data.transforms.subarray(0, count * 4)).toEqual(emitterB.data.transforms.subarray(0, count * 4));
|
|
46
|
+
expect(emitterA.data.alphas.subarray(0, count)).toEqual(emitterB.data.alphas.subarray(0, count));
|
|
47
|
+
expect(emitterA.data.colors.subarray(0, count * 3)).toEqual(emitterB.data.colors.subarray(0, count * 3));
|
|
48
|
+
expect(emitterA.data.velocities.subarray(0, count * 2)).toEqual(emitterB.data.velocities.subarray(0, count * 2));
|
|
49
|
+
expect(stateA.lifetimes.subarray(0, count * 2)).toEqual(stateB.lifetimes.subarray(0, count * 2));
|
|
50
|
+
expect(stateA.velocities.subarray(0, count * 2)).toEqual(stateB.velocities.subarray(0, count * 2));
|
|
51
|
+
expect(stateA.scales.subarray(0, count)).toEqual(stateB.scales.subarray(0, count));
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createRandomSource } from '@flighthq/math';
|
|
2
|
+
import { createParticleEmitterConfig, createParticleEmitterState } from '@flighthq/particles';
|
|
3
|
+
import type { TextureAtlas } from '@flighthq/types';
|
|
4
|
+
|
|
5
|
+
import { emitParticleBurst } from './emitParticleBurst';
|
|
6
|
+
import { createParticleEmitter } from './particleEmitter';
|
|
7
|
+
import { updateParticleEmitter } from './updateParticleEmitter';
|
|
8
|
+
|
|
9
|
+
function makeAtlas(): TextureAtlas {
|
|
10
|
+
return {
|
|
11
|
+
image: null,
|
|
12
|
+
regions: [{ id: 0, x: 0, y: 0, width: 32, height: 32, pivotX: null, pivotY: null }],
|
|
13
|
+
} as TextureAtlas;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
describe('emitParticleBurst', () => {
|
|
17
|
+
it('spawns the requested count at the given point', () => {
|
|
18
|
+
const emitter = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
19
|
+
const state = createParticleEmitterState();
|
|
20
|
+
const config = createParticleEmitterConfig({ speedMin: 0, speedMax: 0, lifetimeMin: 10, lifetimeMax: 10 });
|
|
21
|
+
const n = emitParticleBurst(emitter, state, config, 8, 200, 300);
|
|
22
|
+
expect(n).toBe(8);
|
|
23
|
+
expect(emitter.data.particleCount).toBe(8);
|
|
24
|
+
// With zero speed and a point emitter, every particle sits at the burst point.
|
|
25
|
+
for (let i = 0; i < 8; i++) {
|
|
26
|
+
expect(emitter.data.transforms[i * 4]).toBeCloseTo(200);
|
|
27
|
+
expect(emitter.data.transforms[i * 4 + 1]).toBeCloseTo(300);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('respects maxParticles and returns the actual spawn count', () => {
|
|
32
|
+
const emitter = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
33
|
+
const state = createParticleEmitterState();
|
|
34
|
+
const config = createParticleEmitterConfig({ maxParticles: 5, lifetimeMin: 10, lifetimeMax: 10 });
|
|
35
|
+
expect(emitParticleBurst(emitter, state, config, 100, 0, 0)).toBe(5);
|
|
36
|
+
expect(emitter.data.particleCount).toBe(5);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('adds to existing particles rather than replacing them', () => {
|
|
40
|
+
const emitter = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
41
|
+
const state = createParticleEmitterState();
|
|
42
|
+
const config = createParticleEmitterConfig({ maxParticles: 100, lifetimeMin: 10, lifetimeMax: 10 });
|
|
43
|
+
emitParticleBurst(emitter, state, config, 3, 0, 0);
|
|
44
|
+
emitParticleBurst(emitter, state, config, 4, 0, 0);
|
|
45
|
+
expect(emitter.data.particleCount).toBe(7);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("does not touch the emitter's continuous-emission bookkeeping", () => {
|
|
49
|
+
const emitter = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
50
|
+
const state = createParticleEmitterState();
|
|
51
|
+
const config = createParticleEmitterConfig({ lifetimeMin: 10, lifetimeMax: 10 });
|
|
52
|
+
emitParticleBurst(emitter, state, config, 3, 0, 0);
|
|
53
|
+
expect(state.spawnAccumulator).toBe(0);
|
|
54
|
+
expect(state.emitterAge).toBe(0);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('is deterministic with a seeded state', () => {
|
|
58
|
+
const run = (): number[] => {
|
|
59
|
+
const emitter = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
60
|
+
const state = createParticleEmitterState(createRandomSource(99));
|
|
61
|
+
const config = createParticleEmitterConfig({
|
|
62
|
+
speedMin: 10,
|
|
63
|
+
speedMax: 200,
|
|
64
|
+
spread: Math.PI,
|
|
65
|
+
lifetimeMin: 1,
|
|
66
|
+
lifetimeMax: 5,
|
|
67
|
+
});
|
|
68
|
+
emitParticleBurst(emitter, state, config, 10, 0, 0);
|
|
69
|
+
return Array.from(state.velocities.slice(0, 20));
|
|
70
|
+
};
|
|
71
|
+
expect(run()).toEqual(run());
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('composes into a death sub-emitter via onDeath (explosion on expiry)', () => {
|
|
75
|
+
// Parent: short-lived particles. Child: a burst spawned where each parent dies.
|
|
76
|
+
const parent = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
77
|
+
const parentState = createParticleEmitterState();
|
|
78
|
+
const parentConfig = createParticleEmitterConfig({
|
|
79
|
+
spawnRate: 5,
|
|
80
|
+
lifetimeMin: 0.5,
|
|
81
|
+
lifetimeMax: 0.5,
|
|
82
|
+
speedMin: 0,
|
|
83
|
+
speedMax: 0,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const child = createParticleEmitter({ data: { atlas: makeAtlas() } });
|
|
87
|
+
const childState = createParticleEmitterState();
|
|
88
|
+
const childConfig = createParticleEmitterConfig({ maxParticles: 1000, lifetimeMin: 10, lifetimeMax: 10 });
|
|
89
|
+
|
|
90
|
+
let deaths = 0;
|
|
91
|
+
const onDeath = (x: number, y: number): void => {
|
|
92
|
+
deaths++;
|
|
93
|
+
emitParticleBurst(child, childState, childConfig, 6, x, y);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Run long enough for the parent particles to spawn and then expire.
|
|
97
|
+
for (let i = 0; i < 30; i++) updateParticleEmitter(parent, parentState, parentConfig, 1 / 30, { onDeath });
|
|
98
|
+
|
|
99
|
+
expect(deaths).toBeGreaterThan(0);
|
|
100
|
+
expect(child.data.particleCount).toBe(deaths * 6); // 6 child particles per parent death
|
|
101
|
+
});
|
|
102
|
+
});
|