@codexo/exojs-particles 0.12.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/LICENSE +21 -0
- package/README.md +129 -0
- package/dist/esm/ParticleSystem.d.ts +212 -0
- package/dist/esm/ParticleSystem.js +570 -0
- package/dist/esm/ParticleSystem.js.map +1 -0
- package/dist/esm/distributions/BoxArea.d.ts +17 -0
- package/dist/esm/distributions/BoxArea.js +48 -0
- package/dist/esm/distributions/BoxArea.js.map +1 -0
- package/dist/esm/distributions/CircleArea.d.ts +19 -0
- package/dist/esm/distributions/CircleArea.js +33 -0
- package/dist/esm/distributions/CircleArea.js.map +1 -0
- package/dist/esm/distributions/ColorGradient.d.ts +40 -0
- package/dist/esm/distributions/ColorGradient.js +72 -0
- package/dist/esm/distributions/ColorGradient.js.map +1 -0
- package/dist/esm/distributions/ConeDirection.d.ts +28 -0
- package/dist/esm/distributions/ConeDirection.js +44 -0
- package/dist/esm/distributions/ConeDirection.js.map +1 -0
- package/dist/esm/distributions/Constant.d.ts +17 -0
- package/dist/esm/distributions/Constant.js +35 -0
- package/dist/esm/distributions/Constant.js.map +1 -0
- package/dist/esm/distributions/Curve.d.ts +30 -0
- package/dist/esm/distributions/Curve.js +53 -0
- package/dist/esm/distributions/Curve.js.map +1 -0
- package/dist/esm/distributions/Distribution.d.ts +45 -0
- package/dist/esm/distributions/LineSegment.d.ts +15 -0
- package/dist/esm/distributions/LineSegment.js +27 -0
- package/dist/esm/distributions/LineSegment.js.map +1 -0
- package/dist/esm/distributions/Range.d.ts +12 -0
- package/dist/esm/distributions/Range.js +19 -0
- package/dist/esm/distributions/Range.js.map +1 -0
- package/dist/esm/distributions/VectorRange.d.ts +20 -0
- package/dist/esm/distributions/VectorRange.js +31 -0
- package/dist/esm/distributions/VectorRange.js.map +1 -0
- package/dist/esm/distributions/index.d.ts +12 -0
- package/dist/esm/gpu/ParticleGpuState.d.ts +57 -0
- package/dist/esm/gpu/ParticleGpuState.js +535 -0
- package/dist/esm/gpu/ParticleGpuState.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +32 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +24 -0
- package/dist/esm/modules/AlphaFadeOverLifetime.js +64 -0
- package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -0
- package/dist/esm/modules/ApplyForce.d.ts +20 -0
- package/dist/esm/modules/ApplyForce.js +48 -0
- package/dist/esm/modules/ApplyForce.js.map +1 -0
- package/dist/esm/modules/AttractToPoint.d.ts +27 -0
- package/dist/esm/modules/AttractToPoint.js +73 -0
- package/dist/esm/modules/AttractToPoint.js.map +1 -0
- package/dist/esm/modules/BurstSpawn.d.ts +53 -0
- package/dist/esm/modules/BurstSpawn.js +93 -0
- package/dist/esm/modules/BurstSpawn.js.map +1 -0
- package/dist/esm/modules/ColorOverLifetime.d.ts +22 -0
- package/dist/esm/modules/ColorOverLifetime.js +65 -0
- package/dist/esm/modules/ColorOverLifetime.js.map +1 -0
- package/dist/esm/modules/ColorOverSpeed.d.ts +27 -0
- package/dist/esm/modules/ColorOverSpeed.js +86 -0
- package/dist/esm/modules/ColorOverSpeed.js.map +1 -0
- package/dist/esm/modules/DeathModule.d.ts +24 -0
- package/dist/esm/modules/DeathModule.js +25 -0
- package/dist/esm/modules/DeathModule.js.map +1 -0
- package/dist/esm/modules/Drag.d.ts +20 -0
- package/dist/esm/modules/Drag.js +43 -0
- package/dist/esm/modules/Drag.js.map +1 -0
- package/dist/esm/modules/OrbitalForce.d.ts +28 -0
- package/dist/esm/modules/OrbitalForce.js +65 -0
- package/dist/esm/modules/OrbitalForce.js.map +1 -0
- package/dist/esm/modules/RateSpawn.d.ts +41 -0
- package/dist/esm/modules/RateSpawn.js +75 -0
- package/dist/esm/modules/RateSpawn.js.map +1 -0
- package/dist/esm/modules/RepelFromPoint.d.ts +24 -0
- package/dist/esm/modules/RepelFromPoint.js +76 -0
- package/dist/esm/modules/RepelFromPoint.js.map +1 -0
- package/dist/esm/modules/RotateOverLifetime.d.ts +20 -0
- package/dist/esm/modules/RotateOverLifetime.js +41 -0
- package/dist/esm/modules/RotateOverLifetime.js.map +1 -0
- package/dist/esm/modules/ScaleOverLifetime.d.ts +26 -0
- package/dist/esm/modules/ScaleOverLifetime.js +59 -0
- package/dist/esm/modules/ScaleOverLifetime.js.map +1 -0
- package/dist/esm/modules/SpawnModule.d.ts +30 -0
- package/dist/esm/modules/SpawnModule.js +31 -0
- package/dist/esm/modules/SpawnModule.js.map +1 -0
- package/dist/esm/modules/SpawnOnDeath.d.ts +24 -0
- package/dist/esm/modules/SpawnOnDeath.js +47 -0
- package/dist/esm/modules/SpawnOnDeath.js.map +1 -0
- package/dist/esm/modules/Turbulence.d.ts +30 -0
- package/dist/esm/modules/Turbulence.js +122 -0
- package/dist/esm/modules/Turbulence.js.map +1 -0
- package/dist/esm/modules/UpdateModule.d.ts +95 -0
- package/dist/esm/modules/UpdateModule.js +66 -0
- package/dist/esm/modules/UpdateModule.js.map +1 -0
- package/dist/esm/modules/VelocityOverLifetime.d.ts +30 -0
- package/dist/esm/modules/VelocityOverLifetime.js +84 -0
- package/dist/esm/modules/VelocityOverLifetime.js.map +1 -0
- package/dist/esm/modules/WgslContribution.d.ts +81 -0
- package/dist/esm/modules/WgslContribution.js +34 -0
- package/dist/esm/modules/WgslContribution.js.map +1 -0
- package/dist/esm/modules/index.d.ts +22 -0
- package/dist/esm/particlesBuildInfo.d.ts +15 -0
- package/dist/esm/particlesBuildInfo.js +8 -0
- package/dist/esm/particlesBuildInfo.js.map +1 -0
- package/dist/esm/particlesExtension.d.ts +24 -0
- package/dist/esm/particlesExtension.js +49 -0
- package/dist/esm/particlesExtension.js.map +1 -0
- package/dist/esm/public.d.ts +9 -0
- package/dist/esm/register.d.ts +1 -0
- package/dist/esm/register.js +43 -0
- package/dist/esm/register.js.map +1 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +45 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.js +325 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -0
- package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +48 -0
- package/dist/esm/renderers/WebGpuParticleRenderer.js +553 -0
- package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -0
- package/dist/esm/renderers/glsl/particle.frag.js +4 -0
- package/dist/esm/renderers/glsl/particle.frag.js.map +1 -0
- package/dist/esm/renderers/glsl/particle.vert.js +4 -0
- package/dist/esm/renderers/glsl/particle.vert.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,570 @@
|
|
|
1
|
+
import { Drawable, Rectangle, Texture, Spritesheet } from '@codexo/exojs';
|
|
2
|
+
import { ParticleGpuState } from './gpu/ParticleGpuState.js';
|
|
3
|
+
|
|
4
|
+
/// <reference types="@webgpu/types" />
|
|
5
|
+
const defaultCapacity = 4096;
|
|
6
|
+
/**
|
|
7
|
+
* Lazily-initialised 1×1 opaque-white texture used as the default sprite
|
|
8
|
+
* when a {@link ParticleSystem} is constructed without one. Particles
|
|
9
|
+
* render as solid color quads (the per-particle `color` channel times
|
|
10
|
+
* white-with-alpha-1). Shared across systems to avoid wasted texture
|
|
11
|
+
* allocations.
|
|
12
|
+
*/
|
|
13
|
+
let defaultWhiteTexture = null;
|
|
14
|
+
const getDefaultWhiteTexture = () => {
|
|
15
|
+
if (defaultWhiteTexture === null) {
|
|
16
|
+
const canvas = document.createElement('canvas');
|
|
17
|
+
canvas.width = 1;
|
|
18
|
+
canvas.height = 1;
|
|
19
|
+
const ctx = canvas.getContext('2d');
|
|
20
|
+
if (ctx !== null) {
|
|
21
|
+
ctx.fillStyle = '#ffffff';
|
|
22
|
+
ctx.fillRect(0, 0, 1, 1);
|
|
23
|
+
}
|
|
24
|
+
defaultWhiteTexture = new Texture(canvas);
|
|
25
|
+
}
|
|
26
|
+
return defaultWhiteTexture;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The central coordinator of the particle pipeline. `ParticleSystem` is a
|
|
30
|
+
* {@link Drawable} that owns:
|
|
31
|
+
*
|
|
32
|
+
* - **SoA particle storage** — one typed array per attribute (position,
|
|
33
|
+
* velocity, scale, rotation, color, lifetime, ...), sized to a fixed
|
|
34
|
+
* capacity at construction. User code reads/writes via
|
|
35
|
+
* `system.posX[slot]`, `system.velX[slot]`, etc.
|
|
36
|
+
* - **Spawn modules** — write new particles into freshly allocated slots.
|
|
37
|
+
* - **Update modules** — mutate the live range each frame (forces, color
|
|
38
|
+
* blends, scale curves, drag, ...). Built-in modules ship both CPU and
|
|
39
|
+
* WGSL implementations; custom modules can opt into GPU acceleration by
|
|
40
|
+
* implementing `wgsl()`.
|
|
41
|
+
* - **Death modules** — fire once per dying particle, before its slot is
|
|
42
|
+
* recycled (sub-emitters, event hooks).
|
|
43
|
+
*
|
|
44
|
+
* **Auto-routing CPU vs GPU:** at first {@link update}, the system checks:
|
|
45
|
+
* if a `WebGpuBackend` was supplied AND every registered update module has
|
|
46
|
+
* `wgsl()`, the GPU path engages — a composite compute pipeline runs
|
|
47
|
+
* integration plus all module bodies in one dispatch and writes directly
|
|
48
|
+
* into the renderer's instance buffer (no CPU readback). Otherwise the CPU
|
|
49
|
+
* path runs the existing per-module `apply()` loops.
|
|
50
|
+
*
|
|
51
|
+
* **Per-frame order in {@link update} (CPU mode):**
|
|
52
|
+
* 1. Run every spawn module.
|
|
53
|
+
* 2. Integrate position from velocity, rotation from rotationSpeed, advance `elapsed`.
|
|
54
|
+
* 3. Run every update module on the live range.
|
|
55
|
+
* 4. Compact: scan `[0, liveCount)` forward, fire death modules on expired
|
|
56
|
+
* slots, copy survivors down. `liveCount` shrinks to the survivor count.
|
|
57
|
+
*
|
|
58
|
+
* **Per-frame order in {@link update} (GPU mode):**
|
|
59
|
+
* 1. Run every spawn module (CPU writes initial values into the spawn slot).
|
|
60
|
+
* 2. Detect expiries on CPU (via `elapsed >= lifetime`); fire death modules;
|
|
61
|
+
* set `lifetime[slot] = -1` sentinel + clear `alive[slot]` so the GPU
|
|
62
|
+
* shader skips them. **No compaction** — slots are recycled on next spawn.
|
|
63
|
+
* 3. Dispatch the composite compute pipeline. Integration + update modules
|
|
64
|
+
* + pack-instances run in one pass; the instance buffer is written
|
|
65
|
+
* directly. CPU SoA stays as-is for spawn writes.
|
|
66
|
+
*
|
|
67
|
+
* **Coordinate space:** particle positions are LOCAL to the system. The
|
|
68
|
+
* system's `getGlobalTransform()` is applied on top during rendering — both
|
|
69
|
+
* the WebGL2 and WebGPU shaders multiply `projection * translation * rotated`.
|
|
70
|
+
* Setting world-space positions on individual particles double-translates.
|
|
71
|
+
* Position the system itself via `system.setPosition(...)` and emit relative
|
|
72
|
+
* to `(0, 0)`.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* // Backend-agnostic — runs CPU on WebGL2, GPU on WebGPU automatically.
|
|
76
|
+
* const system = new ParticleSystem(loader.get(Texture, 'spark'), {
|
|
77
|
+
* capacity: 8192,
|
|
78
|
+
* backend: app.backend,
|
|
79
|
+
* });
|
|
80
|
+
*
|
|
81
|
+
* system.addSpawnModule(new RateSpawn({ rate: new Constant(60), ... }));
|
|
82
|
+
* system.addUpdateModule(new ApplyForce(0, 980)); // gravity, GPU-eligible
|
|
83
|
+
* system.addUpdateModule(new ColorOverLifetime(fireGradient));
|
|
84
|
+
* scene.addChild(system);
|
|
85
|
+
*/
|
|
86
|
+
class ParticleSystem extends Drawable {
|
|
87
|
+
/** Maximum particle count this system will store. Fixed at construction. */
|
|
88
|
+
capacity;
|
|
89
|
+
posX;
|
|
90
|
+
posY;
|
|
91
|
+
velX;
|
|
92
|
+
velY;
|
|
93
|
+
scaleX;
|
|
94
|
+
scaleY;
|
|
95
|
+
rotations;
|
|
96
|
+
rotationSpeeds;
|
|
97
|
+
color; // packed 0xAABBGGRR
|
|
98
|
+
elapsed; // seconds since spawn
|
|
99
|
+
lifetime; // total seconds before expiry; -1 sentinel for dead in GPU mode
|
|
100
|
+
textureIndex;
|
|
101
|
+
/**
|
|
102
|
+
* Number of currently live particles. In CPU mode this is exact: slots
|
|
103
|
+
* `[0, liveCount)` are all alive after each `update()`. In GPU mode
|
|
104
|
+
* this is a high-water mark — slots `[0, liveCount)` may contain dead
|
|
105
|
+
* holes (filled in by future spawns); use {@link aliveCount} for the
|
|
106
|
+
* actual alive count.
|
|
107
|
+
*/
|
|
108
|
+
liveCount = 0;
|
|
109
|
+
/**
|
|
110
|
+
* Per-slot alive flag (1 = alive, 0 = dead). Maintained in both CPU
|
|
111
|
+
* and GPU mode. Custom modules iterating the live range should check
|
|
112
|
+
* this to skip dead slots in GPU mode.
|
|
113
|
+
*/
|
|
114
|
+
alive;
|
|
115
|
+
_spawnModules = [];
|
|
116
|
+
_updateModules = [];
|
|
117
|
+
_deathModules = [];
|
|
118
|
+
_backend = null;
|
|
119
|
+
_device = null;
|
|
120
|
+
_gpuState = null;
|
|
121
|
+
_gpuMode = false;
|
|
122
|
+
_compiled = false;
|
|
123
|
+
_spawnHint = 0; // round-robin pointer for first-dead lookup in GPU mode
|
|
124
|
+
/**
|
|
125
|
+
* In GPU mode, slots whose CPU SoA values need re-uploading to the GPU
|
|
126
|
+
* (newly spawned, or just-expired with lifetime sentinel). Cleared
|
|
127
|
+
* after each compute dispatch. CPU never overwrites integrated GPU
|
|
128
|
+
* state — only dirty slots flow CPU → GPU.
|
|
129
|
+
*/
|
|
130
|
+
_gpuDirtySlots = new Set();
|
|
131
|
+
_texture;
|
|
132
|
+
_frames = [];
|
|
133
|
+
_textureFrame = new Rectangle();
|
|
134
|
+
_vertices = new Float32Array(4);
|
|
135
|
+
_texCoords = new Uint32Array(4);
|
|
136
|
+
_updateTexCoords = true;
|
|
137
|
+
_updateVertices = true;
|
|
138
|
+
constructor(sourceOrOptions, framesOrOptions, finalOptions) {
|
|
139
|
+
super();
|
|
140
|
+
// Disambiguate the four valid call shapes via instanceof checks.
|
|
141
|
+
// The TS overloads above already prevent illegal combinations like
|
|
142
|
+
// `(texture, sheet)` or `(sheet, frames)` at compile time; this
|
|
143
|
+
// narrowing only sorts out the legal ones.
|
|
144
|
+
let texture = null;
|
|
145
|
+
let frames = null;
|
|
146
|
+
let options;
|
|
147
|
+
if (sourceOrOptions instanceof Texture) {
|
|
148
|
+
texture = sourceOrOptions;
|
|
149
|
+
if (Array.isArray(framesOrOptions)) {
|
|
150
|
+
frames = framesOrOptions;
|
|
151
|
+
options = finalOptions ?? {};
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
options = framesOrOptions ?? {};
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
else if (sourceOrOptions instanceof Spritesheet) {
|
|
158
|
+
texture = sourceOrOptions.texture;
|
|
159
|
+
frames = [...sourceOrOptions.frames.values()];
|
|
160
|
+
options = framesOrOptions ?? {};
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
options = sourceOrOptions ?? {};
|
|
164
|
+
}
|
|
165
|
+
const capacity = options.capacity ?? defaultCapacity;
|
|
166
|
+
if (capacity <= 0 || !Number.isInteger(capacity)) {
|
|
167
|
+
throw new Error(`ParticleSystem capacity must be a positive integer (got ${capacity}).`);
|
|
168
|
+
}
|
|
169
|
+
this.capacity = capacity;
|
|
170
|
+
this.posX = new Float32Array(capacity);
|
|
171
|
+
this.posY = new Float32Array(capacity);
|
|
172
|
+
this.velX = new Float32Array(capacity);
|
|
173
|
+
this.velY = new Float32Array(capacity);
|
|
174
|
+
this.scaleX = new Float32Array(capacity);
|
|
175
|
+
this.scaleY = new Float32Array(capacity);
|
|
176
|
+
this.rotations = new Float32Array(capacity);
|
|
177
|
+
this.rotationSpeeds = new Float32Array(capacity);
|
|
178
|
+
this.color = new Uint32Array(capacity);
|
|
179
|
+
this.elapsed = new Float32Array(capacity);
|
|
180
|
+
this.lifetime = new Float32Array(capacity);
|
|
181
|
+
this.textureIndex = new Uint16Array(capacity);
|
|
182
|
+
this.alive = new Uint8Array(capacity);
|
|
183
|
+
this._device = options.device ?? null;
|
|
184
|
+
this._texture = texture ?? getDefaultWhiteTexture();
|
|
185
|
+
if (frames !== null) {
|
|
186
|
+
for (const frame of frames) {
|
|
187
|
+
this._frames.push(frame.clone());
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
this.resetTextureFrame();
|
|
191
|
+
}
|
|
192
|
+
get texture() {
|
|
193
|
+
return this._texture;
|
|
194
|
+
}
|
|
195
|
+
set texture(texture) {
|
|
196
|
+
this.setTexture(texture);
|
|
197
|
+
}
|
|
198
|
+
get textureFrame() {
|
|
199
|
+
return this._textureFrame;
|
|
200
|
+
}
|
|
201
|
+
set textureFrame(frame) {
|
|
202
|
+
this.setTextureFrame(frame);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Atlas frames declared on this system, or empty when the texture is
|
|
206
|
+
* used as a single frame. Each particle's `textureIndex[i]` selects
|
|
207
|
+
* an entry from this list; out-of-range indices are clamped to 0.
|
|
208
|
+
*/
|
|
209
|
+
get frames() {
|
|
210
|
+
return this._frames;
|
|
211
|
+
}
|
|
212
|
+
/** `true` when the system declares more than one atlas frame. */
|
|
213
|
+
get hasAtlas() {
|
|
214
|
+
return this._frames.length > 1;
|
|
215
|
+
}
|
|
216
|
+
get vertices() {
|
|
217
|
+
if (this._updateVertices) {
|
|
218
|
+
const { x, y, width, height } = this._textureFrame;
|
|
219
|
+
const offsetX = width / 2;
|
|
220
|
+
const offsetY = height / 2;
|
|
221
|
+
this._vertices[0] = x - offsetX;
|
|
222
|
+
this._vertices[1] = y - offsetY;
|
|
223
|
+
this._vertices[2] = width - offsetX;
|
|
224
|
+
this._vertices[3] = height - offsetY;
|
|
225
|
+
this._updateVertices = false;
|
|
226
|
+
}
|
|
227
|
+
return this._vertices;
|
|
228
|
+
}
|
|
229
|
+
get texCoords() {
|
|
230
|
+
if (this._updateTexCoords) {
|
|
231
|
+
const { width, height } = this._texture;
|
|
232
|
+
const { left, top, right, bottom } = this._textureFrame;
|
|
233
|
+
const minX = ((left / width) * 65535) & 65535;
|
|
234
|
+
const minY = (((top / height) * 65535) & 65535) << 16;
|
|
235
|
+
const maxX = ((right / width) * 65535) & 65535;
|
|
236
|
+
const maxY = (((bottom / height) * 65535) & 65535) << 16;
|
|
237
|
+
if (this._texture.flipY) {
|
|
238
|
+
this._texCoords[0] = maxY | minX;
|
|
239
|
+
this._texCoords[1] = maxY | maxX;
|
|
240
|
+
this._texCoords[2] = minY | maxX;
|
|
241
|
+
this._texCoords[3] = minY | minX;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
this._texCoords[0] = minY | minX;
|
|
245
|
+
this._texCoords[1] = minY | maxX;
|
|
246
|
+
this._texCoords[2] = maxY | maxX;
|
|
247
|
+
this._texCoords[3] = maxY | minX;
|
|
248
|
+
}
|
|
249
|
+
this._updateTexCoords = false;
|
|
250
|
+
}
|
|
251
|
+
return this._texCoords;
|
|
252
|
+
}
|
|
253
|
+
/** `true` when the system is running on the GPU compute pipeline. */
|
|
254
|
+
get gpuMode() {
|
|
255
|
+
return this._gpuMode;
|
|
256
|
+
}
|
|
257
|
+
/** GPU-side state, or `null` in CPU mode. */
|
|
258
|
+
get gpuState() {
|
|
259
|
+
return this._gpuState;
|
|
260
|
+
}
|
|
261
|
+
/** Actual count of live particles (slots with `alive[i] === 1`). May differ from `liveCount` in GPU mode. */
|
|
262
|
+
get aliveCount() {
|
|
263
|
+
let count = 0;
|
|
264
|
+
for (let i = 0; i < this.liveCount; i++) {
|
|
265
|
+
if (this.alive[i])
|
|
266
|
+
count++;
|
|
267
|
+
}
|
|
268
|
+
return count;
|
|
269
|
+
}
|
|
270
|
+
get spawnModules() {
|
|
271
|
+
return this._spawnModules;
|
|
272
|
+
}
|
|
273
|
+
get updateModules() {
|
|
274
|
+
return this._updateModules;
|
|
275
|
+
}
|
|
276
|
+
get deathModules() {
|
|
277
|
+
return this._deathModules;
|
|
278
|
+
}
|
|
279
|
+
setTexture(texture) {
|
|
280
|
+
if (this._texture !== texture) {
|
|
281
|
+
this._texture = texture;
|
|
282
|
+
this.resetTextureFrame();
|
|
283
|
+
}
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
setTextureFrame(frame) {
|
|
287
|
+
this._textureFrame.copy(frame);
|
|
288
|
+
this._updateTexCoords = true;
|
|
289
|
+
this._updateVertices = true;
|
|
290
|
+
this.getLocalBounds().set(0, 0, frame.width, frame.height);
|
|
291
|
+
this._invalidateBoundsCascade();
|
|
292
|
+
return this;
|
|
293
|
+
}
|
|
294
|
+
resetTextureFrame() {
|
|
295
|
+
return this.setTextureFrame(Rectangle.temp.set(0, 0, this._texture.width, this._texture.height));
|
|
296
|
+
}
|
|
297
|
+
addSpawnModule(mod) {
|
|
298
|
+
this._spawnModules.push(mod);
|
|
299
|
+
return this;
|
|
300
|
+
}
|
|
301
|
+
addUpdateModule(mod) {
|
|
302
|
+
if (this._compiled) {
|
|
303
|
+
throw new Error('Cannot add update modules after the system has been compiled (first update). Register all modules before the first update().');
|
|
304
|
+
}
|
|
305
|
+
this._updateModules.push(mod);
|
|
306
|
+
return this;
|
|
307
|
+
}
|
|
308
|
+
addDeathModule(mod) {
|
|
309
|
+
this._deathModules.push(mod);
|
|
310
|
+
return this;
|
|
311
|
+
}
|
|
312
|
+
clearSpawnModules() {
|
|
313
|
+
for (const mod of this._spawnModules)
|
|
314
|
+
mod.destroy();
|
|
315
|
+
this._spawnModules.length = 0;
|
|
316
|
+
return this;
|
|
317
|
+
}
|
|
318
|
+
clearUpdateModules() {
|
|
319
|
+
for (const mod of this._updateModules)
|
|
320
|
+
mod.destroy();
|
|
321
|
+
this._updateModules.length = 0;
|
|
322
|
+
return this;
|
|
323
|
+
}
|
|
324
|
+
clearDeathModules() {
|
|
325
|
+
for (const mod of this._deathModules)
|
|
326
|
+
mod.destroy();
|
|
327
|
+
this._deathModules.length = 0;
|
|
328
|
+
return this;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Allocates a particle slot and returns its index. Returns `-1` when
|
|
332
|
+
* the system is at {@link capacity}.
|
|
333
|
+
*
|
|
334
|
+
* **CPU mode:** slots are dense in `[0, liveCount)`. `spawn()` returns
|
|
335
|
+
* the next sequential slot; `liveCount++`.
|
|
336
|
+
*
|
|
337
|
+
* **GPU mode:** slots may have dead holes. `spawn()` finds the first
|
|
338
|
+
* `alive[i] === 0` slot via a round-robin hint pointer (amortised O(1),
|
|
339
|
+
* worst case O(capacity) on full systems).
|
|
340
|
+
*/
|
|
341
|
+
spawn() {
|
|
342
|
+
if (this._gpuMode) {
|
|
343
|
+
return this._spawnGpu();
|
|
344
|
+
}
|
|
345
|
+
return this._spawnCpu();
|
|
346
|
+
}
|
|
347
|
+
/** Resets the system to zero live particles without destroying it. */
|
|
348
|
+
clearParticles() {
|
|
349
|
+
this.liveCount = 0;
|
|
350
|
+
this._spawnHint = 0;
|
|
351
|
+
this.alive.fill(0);
|
|
352
|
+
this.lifetime.fill(0);
|
|
353
|
+
this.elapsed.fill(0);
|
|
354
|
+
return this;
|
|
355
|
+
}
|
|
356
|
+
/**
|
|
357
|
+
* @internal
|
|
358
|
+
*
|
|
359
|
+
* Collect-hook: captures the active backend before this node is emitted so
|
|
360
|
+
* the next `update()` can compile a GPU pipeline if the backend turned out
|
|
361
|
+
* to be `WebGpuBackend`. Re-captures and rebuilds when the backend
|
|
362
|
+
* reference changes (e.g. after device-loss recovery).
|
|
363
|
+
*/
|
|
364
|
+
/** @internal */
|
|
365
|
+
_collect(builder, seq) {
|
|
366
|
+
const backend = builder.backend;
|
|
367
|
+
if (this._backend !== backend) {
|
|
368
|
+
this._backend = backend;
|
|
369
|
+
if (this._gpuState !== null) {
|
|
370
|
+
this._gpuState.destroy();
|
|
371
|
+
this._gpuState = null;
|
|
372
|
+
}
|
|
373
|
+
this._gpuMode = false;
|
|
374
|
+
this._compiled = false;
|
|
375
|
+
}
|
|
376
|
+
super._collect(builder, seq);
|
|
377
|
+
}
|
|
378
|
+
/** Per-frame entry point. Routes to CPU or GPU pipeline based on auto-detection at first call. */
|
|
379
|
+
update(delta) {
|
|
380
|
+
if (!this._compiled) {
|
|
381
|
+
this._compile();
|
|
382
|
+
}
|
|
383
|
+
const dt = delta.seconds;
|
|
384
|
+
// 1. Spawn (CPU writes SoA in both modes).
|
|
385
|
+
for (let i = 0; i < this._spawnModules.length; i++) {
|
|
386
|
+
this._spawnModules[i].apply(this, dt);
|
|
387
|
+
}
|
|
388
|
+
if (this._gpuMode) {
|
|
389
|
+
this._updateGpu(dt);
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
this._updateCpu(dt);
|
|
393
|
+
}
|
|
394
|
+
return this;
|
|
395
|
+
}
|
|
396
|
+
destroy() {
|
|
397
|
+
super.destroy();
|
|
398
|
+
this.clearSpawnModules();
|
|
399
|
+
this.clearUpdateModules();
|
|
400
|
+
this.clearDeathModules();
|
|
401
|
+
if (this._gpuState !== null) {
|
|
402
|
+
this._gpuState.destroy();
|
|
403
|
+
this._gpuState = null;
|
|
404
|
+
}
|
|
405
|
+
for (const frame of this._frames) {
|
|
406
|
+
frame.destroy();
|
|
407
|
+
}
|
|
408
|
+
this._frames.length = 0;
|
|
409
|
+
this._gpuMode = false;
|
|
410
|
+
this._compiled = false;
|
|
411
|
+
this.liveCount = 0;
|
|
412
|
+
this.alive.fill(0);
|
|
413
|
+
this._textureFrame.destroy();
|
|
414
|
+
}
|
|
415
|
+
_compile() {
|
|
416
|
+
this._compiled = true;
|
|
417
|
+
// Duck-typed `instanceof WebGpuBackend` — avoids importing the
|
|
418
|
+
// backend class (which registers a renderer for ParticleSystem
|
|
419
|
+
// and would create a circular dependency). WebGl2Backend has no
|
|
420
|
+
// `device` field, so this naturally falls back to CPU mode.
|
|
421
|
+
const backendDevice = this._backend?.device ?? null;
|
|
422
|
+
const device = this._device ?? backendDevice;
|
|
423
|
+
if (device === null) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
const allEligible = this._updateModules.every(m => typeof m.wgsl === 'function');
|
|
427
|
+
if (!allEligible) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
this._gpuState = new ParticleGpuState(device, this.capacity, this._updateModules, this._frames, this._texture);
|
|
431
|
+
this._gpuMode = true;
|
|
432
|
+
// Mark every currently-alive slot dirty so the initial upload
|
|
433
|
+
// matches CPU state; subsequent frames only push deltas.
|
|
434
|
+
for (let i = 0; i < this.liveCount; i++) {
|
|
435
|
+
if (this.alive[i])
|
|
436
|
+
this._gpuDirtySlots.add(i);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
_spawnCpu() {
|
|
440
|
+
if (this.liveCount >= this.capacity) {
|
|
441
|
+
return -1;
|
|
442
|
+
}
|
|
443
|
+
const slot = this.liveCount++;
|
|
444
|
+
this.alive[slot] = 1;
|
|
445
|
+
this.elapsed[slot] = 0;
|
|
446
|
+
return slot;
|
|
447
|
+
}
|
|
448
|
+
_spawnGpu() {
|
|
449
|
+
const capacity = this.capacity;
|
|
450
|
+
const alive = this.alive;
|
|
451
|
+
const start = this._spawnHint;
|
|
452
|
+
// Search forward from hint, then wrap.
|
|
453
|
+
for (let i = start; i < capacity; i++) {
|
|
454
|
+
if (alive[i] === 0) {
|
|
455
|
+
alive[i] = 1;
|
|
456
|
+
this.elapsed[i] = 0;
|
|
457
|
+
this._spawnHint = i + 1 === capacity ? 0 : i + 1;
|
|
458
|
+
if (i >= this.liveCount)
|
|
459
|
+
this.liveCount = i + 1;
|
|
460
|
+
this._gpuDirtySlots.add(i);
|
|
461
|
+
return i;
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
for (let i = 0; i < start; i++) {
|
|
465
|
+
if (alive[i] === 0) {
|
|
466
|
+
alive[i] = 1;
|
|
467
|
+
this.elapsed[i] = 0;
|
|
468
|
+
this._spawnHint = i + 1;
|
|
469
|
+
if (i >= this.liveCount)
|
|
470
|
+
this.liveCount = i + 1;
|
|
471
|
+
this._gpuDirtySlots.add(i);
|
|
472
|
+
return i;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
return -1;
|
|
476
|
+
}
|
|
477
|
+
_updateCpu(dt) {
|
|
478
|
+
const { posX, posY, velX, velY, rotations, rotationSpeeds, elapsed } = this;
|
|
479
|
+
const liveCount = this.liveCount;
|
|
480
|
+
for (let i = 0; i < liveCount; i++) {
|
|
481
|
+
posX[i] += velX[i] * dt;
|
|
482
|
+
posY[i] += velY[i] * dt;
|
|
483
|
+
rotations[i] += rotationSpeeds[i] * dt;
|
|
484
|
+
elapsed[i] += dt;
|
|
485
|
+
}
|
|
486
|
+
for (let i = 0; i < this._updateModules.length; i++) {
|
|
487
|
+
this._updateModules[i].apply(this, dt);
|
|
488
|
+
}
|
|
489
|
+
// Compact: forward pass, fire death modules on expired, copy survivors down.
|
|
490
|
+
const lifetime = this.lifetime;
|
|
491
|
+
const alive = this.alive;
|
|
492
|
+
const deathModules = this._deathModules;
|
|
493
|
+
let writeIndex = 0;
|
|
494
|
+
for (let readIndex = 0; readIndex < this.liveCount; readIndex++) {
|
|
495
|
+
if (elapsed[readIndex] >= lifetime[readIndex]) {
|
|
496
|
+
for (let m = 0; m < deathModules.length; m++) {
|
|
497
|
+
deathModules[m].onDeath(this, readIndex);
|
|
498
|
+
}
|
|
499
|
+
alive[readIndex] = 0;
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
if (writeIndex !== readIndex) {
|
|
503
|
+
this._copySlot(readIndex, writeIndex);
|
|
504
|
+
alive[writeIndex] = 1;
|
|
505
|
+
}
|
|
506
|
+
writeIndex++;
|
|
507
|
+
}
|
|
508
|
+
for (let i = writeIndex; i < this.liveCount; i++) {
|
|
509
|
+
alive[i] = 0;
|
|
510
|
+
}
|
|
511
|
+
this.liveCount = writeIndex;
|
|
512
|
+
}
|
|
513
|
+
_updateGpu(dt) {
|
|
514
|
+
// CPU advances its own copy of `elapsed` for expire detection only.
|
|
515
|
+
// GPU's `timing[idx].x` is advanced independently inside the compute
|
|
516
|
+
// shader; the two are never synced after spawn. They tick at the
|
|
517
|
+
// same rate (both add `dt` per frame) so they stay equivalent in
|
|
518
|
+
// practice (modulo numerical drift).
|
|
519
|
+
const elapsed = this.elapsed;
|
|
520
|
+
const lifetime = this.lifetime;
|
|
521
|
+
const alive = this.alive;
|
|
522
|
+
const deathModules = this._deathModules;
|
|
523
|
+
const liveCount = this.liveCount;
|
|
524
|
+
for (let i = 0; i < liveCount; i++) {
|
|
525
|
+
if (alive[i] === 0)
|
|
526
|
+
continue;
|
|
527
|
+
elapsed[i] += dt;
|
|
528
|
+
if (elapsed[i] >= lifetime[i]) {
|
|
529
|
+
for (let m = 0; m < deathModules.length; m++) {
|
|
530
|
+
deathModules[m].onDeath(this, i);
|
|
531
|
+
}
|
|
532
|
+
alive[i] = 0;
|
|
533
|
+
lifetime[i] = -1; // sentinel — GPU shader skips
|
|
534
|
+
this._gpuDirtySlots.add(i); // upload the sentinel so GPU sees the death
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
// Trim trailing dead slots.
|
|
538
|
+
let newLiveCount = this.liveCount;
|
|
539
|
+
while (newLiveCount > 0 && alive[newLiveCount - 1] === 0) {
|
|
540
|
+
newLiveCount--;
|
|
541
|
+
}
|
|
542
|
+
this.liveCount = newLiveCount;
|
|
543
|
+
// Push dirty slots (new spawns + just-expired) to GPU. CPU is NOT
|
|
544
|
+
// the source of truth for integrated position/velocity/etc. after
|
|
545
|
+
// spawn — uploading the full live range every frame would wipe
|
|
546
|
+
// out GPU's integrated state.
|
|
547
|
+
if (this._gpuDirtySlots.size > 0) {
|
|
548
|
+
this._gpuState.uploadDirty(this, this._gpuDirtySlots);
|
|
549
|
+
this._gpuDirtySlots.clear();
|
|
550
|
+
}
|
|
551
|
+
this._gpuState.dispatch(this, dt);
|
|
552
|
+
}
|
|
553
|
+
_copySlot(from, to) {
|
|
554
|
+
this.posX[to] = this.posX[from];
|
|
555
|
+
this.posY[to] = this.posY[from];
|
|
556
|
+
this.velX[to] = this.velX[from];
|
|
557
|
+
this.velY[to] = this.velY[from];
|
|
558
|
+
this.scaleX[to] = this.scaleX[from];
|
|
559
|
+
this.scaleY[to] = this.scaleY[from];
|
|
560
|
+
this.rotations[to] = this.rotations[from];
|
|
561
|
+
this.rotationSpeeds[to] = this.rotationSpeeds[from];
|
|
562
|
+
this.color[to] = this.color[from];
|
|
563
|
+
this.elapsed[to] = this.elapsed[from];
|
|
564
|
+
this.lifetime[to] = this.lifetime[from];
|
|
565
|
+
this.textureIndex[to] = this.textureIndex[from];
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
export { ParticleSystem };
|
|
570
|
+
//# sourceMappingURL=ParticleSystem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticleSystem.js","sources":["../../../src/ParticleSystem.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Vector } from '@codexo/exojs';
|
|
2
|
+
import type { Distribution } from './Distribution';
|
|
3
|
+
/**
|
|
4
|
+
* Random point inside an axis-aligned box. With `mode: 'edge'` the result
|
|
5
|
+
* lies on the perimeter (uniform along all four edges combined); with
|
|
6
|
+
* `mode: 'volume'` (default) it's uniformly distributed across the area.
|
|
7
|
+
*/
|
|
8
|
+
export declare class BoxArea implements Distribution<Vector> {
|
|
9
|
+
minX: number;
|
|
10
|
+
maxX: number;
|
|
11
|
+
minY: number;
|
|
12
|
+
maxY: number;
|
|
13
|
+
mode: 'volume' | 'edge';
|
|
14
|
+
private readonly _scratch;
|
|
15
|
+
constructor(minX: number, maxX: number, minY: number, maxY: number, mode?: 'volume' | 'edge');
|
|
16
|
+
sample(out?: Vector): Vector;
|
|
17
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Vector } from '@codexo/exojs';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Random point inside an axis-aligned box. With `mode: 'edge'` the result
|
|
5
|
+
* lies on the perimeter (uniform along all four edges combined); with
|
|
6
|
+
* `mode: 'volume'` (default) it's uniformly distributed across the area.
|
|
7
|
+
*/
|
|
8
|
+
class BoxArea {
|
|
9
|
+
minX;
|
|
10
|
+
maxX;
|
|
11
|
+
minY;
|
|
12
|
+
maxY;
|
|
13
|
+
mode;
|
|
14
|
+
_scratch = new Vector();
|
|
15
|
+
constructor(minX, maxX, minY, maxY, mode = 'volume') {
|
|
16
|
+
this.minX = minX;
|
|
17
|
+
this.maxX = maxX;
|
|
18
|
+
this.minY = minY;
|
|
19
|
+
this.maxY = maxY;
|
|
20
|
+
this.mode = mode;
|
|
21
|
+
}
|
|
22
|
+
sample(out = this._scratch) {
|
|
23
|
+
if (this.mode === 'volume') {
|
|
24
|
+
out.set(this.minX + Math.random() * (this.maxX - this.minX), this.minY + Math.random() * (this.maxY - this.minY));
|
|
25
|
+
return out;
|
|
26
|
+
}
|
|
27
|
+
const w = this.maxX - this.minX;
|
|
28
|
+
const h = this.maxY - this.minY;
|
|
29
|
+
const perimeter = (w + h) * 2;
|
|
30
|
+
const t = Math.random() * perimeter;
|
|
31
|
+
if (t < w) {
|
|
32
|
+
out.set(this.minX + t, this.minY);
|
|
33
|
+
}
|
|
34
|
+
else if (t < w + h) {
|
|
35
|
+
out.set(this.maxX, this.minY + (t - w));
|
|
36
|
+
}
|
|
37
|
+
else if (t < w * 2 + h) {
|
|
38
|
+
out.set(this.maxX - (t - w - h), this.maxY);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
out.set(this.minX, this.maxY - (t - w * 2 - h));
|
|
42
|
+
}
|
|
43
|
+
return out;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export { BoxArea };
|
|
48
|
+
//# sourceMappingURL=BoxArea.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BoxArea.js","sources":["../../../../src/distributions/BoxArea.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAIA;;;;AAIG;MACU,OAAO,CAAA;AAIT,IAAA,IAAA;AACA,IAAA,IAAA;AACA,IAAA,IAAA;AACA,IAAA,IAAA;AACA,IAAA,IAAA;AAPQ,IAAA,QAAQ,GAAG,IAAI,MAAM,EAAE;IAExC,WAAA,CACS,IAAY,EACZ,IAAY,EACZ,IAAY,EACZ,IAAY,EACZ,IAAA,GAA0B,QAAQ,EAAA;QAJlC,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,IAAI,GAAJ,IAAI;QACJ,IAAA,CAAA,IAAI,GAAJ,IAAI;IACV;AAEI,IAAA,MAAM,CAAC,GAAA,GAAc,IAAI,CAAC,QAAQ,EAAA;AACvC,QAAA,IAAI,IAAI,CAAC,IAAI,KAAK,QAAQ,EAAE;AAC1B,YAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;AAEjH,YAAA,OAAO,GAAG;QACZ;QAEA,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;QAC/B,MAAM,CAAC,GAAG,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI;QAC/B,MAAM,SAAS,GAAG,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC;QAC7B,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,SAAS;AAEnC,QAAA,IAAI,CAAC,GAAG,CAAC,EAAE;AACT,YAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;QACnC;AAAO,aAAA,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACpB,YAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC;aAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE;AACxB,YAAA,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC;QAC7C;aAAO;YACL,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACjD;AAEA,QAAA,OAAO,GAAG;IACZ;AACD;;;;"}
|