@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.
Files changed (119) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +129 -0
  3. package/dist/esm/ParticleSystem.d.ts +212 -0
  4. package/dist/esm/ParticleSystem.js +570 -0
  5. package/dist/esm/ParticleSystem.js.map +1 -0
  6. package/dist/esm/distributions/BoxArea.d.ts +17 -0
  7. package/dist/esm/distributions/BoxArea.js +48 -0
  8. package/dist/esm/distributions/BoxArea.js.map +1 -0
  9. package/dist/esm/distributions/CircleArea.d.ts +19 -0
  10. package/dist/esm/distributions/CircleArea.js +33 -0
  11. package/dist/esm/distributions/CircleArea.js.map +1 -0
  12. package/dist/esm/distributions/ColorGradient.d.ts +40 -0
  13. package/dist/esm/distributions/ColorGradient.js +72 -0
  14. package/dist/esm/distributions/ColorGradient.js.map +1 -0
  15. package/dist/esm/distributions/ConeDirection.d.ts +28 -0
  16. package/dist/esm/distributions/ConeDirection.js +44 -0
  17. package/dist/esm/distributions/ConeDirection.js.map +1 -0
  18. package/dist/esm/distributions/Constant.d.ts +17 -0
  19. package/dist/esm/distributions/Constant.js +35 -0
  20. package/dist/esm/distributions/Constant.js.map +1 -0
  21. package/dist/esm/distributions/Curve.d.ts +30 -0
  22. package/dist/esm/distributions/Curve.js +53 -0
  23. package/dist/esm/distributions/Curve.js.map +1 -0
  24. package/dist/esm/distributions/Distribution.d.ts +45 -0
  25. package/dist/esm/distributions/LineSegment.d.ts +15 -0
  26. package/dist/esm/distributions/LineSegment.js +27 -0
  27. package/dist/esm/distributions/LineSegment.js.map +1 -0
  28. package/dist/esm/distributions/Range.d.ts +12 -0
  29. package/dist/esm/distributions/Range.js +19 -0
  30. package/dist/esm/distributions/Range.js.map +1 -0
  31. package/dist/esm/distributions/VectorRange.d.ts +20 -0
  32. package/dist/esm/distributions/VectorRange.js +31 -0
  33. package/dist/esm/distributions/VectorRange.js.map +1 -0
  34. package/dist/esm/distributions/index.d.ts +12 -0
  35. package/dist/esm/gpu/ParticleGpuState.d.ts +57 -0
  36. package/dist/esm/gpu/ParticleGpuState.js +535 -0
  37. package/dist/esm/gpu/ParticleGpuState.js.map +1 -0
  38. package/dist/esm/index.d.ts +1 -0
  39. package/dist/esm/index.js +32 -0
  40. package/dist/esm/index.js.map +1 -0
  41. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +24 -0
  42. package/dist/esm/modules/AlphaFadeOverLifetime.js +64 -0
  43. package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -0
  44. package/dist/esm/modules/ApplyForce.d.ts +20 -0
  45. package/dist/esm/modules/ApplyForce.js +48 -0
  46. package/dist/esm/modules/ApplyForce.js.map +1 -0
  47. package/dist/esm/modules/AttractToPoint.d.ts +27 -0
  48. package/dist/esm/modules/AttractToPoint.js +73 -0
  49. package/dist/esm/modules/AttractToPoint.js.map +1 -0
  50. package/dist/esm/modules/BurstSpawn.d.ts +53 -0
  51. package/dist/esm/modules/BurstSpawn.js +93 -0
  52. package/dist/esm/modules/BurstSpawn.js.map +1 -0
  53. package/dist/esm/modules/ColorOverLifetime.d.ts +22 -0
  54. package/dist/esm/modules/ColorOverLifetime.js +65 -0
  55. package/dist/esm/modules/ColorOverLifetime.js.map +1 -0
  56. package/dist/esm/modules/ColorOverSpeed.d.ts +27 -0
  57. package/dist/esm/modules/ColorOverSpeed.js +86 -0
  58. package/dist/esm/modules/ColorOverSpeed.js.map +1 -0
  59. package/dist/esm/modules/DeathModule.d.ts +24 -0
  60. package/dist/esm/modules/DeathModule.js +25 -0
  61. package/dist/esm/modules/DeathModule.js.map +1 -0
  62. package/dist/esm/modules/Drag.d.ts +20 -0
  63. package/dist/esm/modules/Drag.js +43 -0
  64. package/dist/esm/modules/Drag.js.map +1 -0
  65. package/dist/esm/modules/OrbitalForce.d.ts +28 -0
  66. package/dist/esm/modules/OrbitalForce.js +65 -0
  67. package/dist/esm/modules/OrbitalForce.js.map +1 -0
  68. package/dist/esm/modules/RateSpawn.d.ts +41 -0
  69. package/dist/esm/modules/RateSpawn.js +75 -0
  70. package/dist/esm/modules/RateSpawn.js.map +1 -0
  71. package/dist/esm/modules/RepelFromPoint.d.ts +24 -0
  72. package/dist/esm/modules/RepelFromPoint.js +76 -0
  73. package/dist/esm/modules/RepelFromPoint.js.map +1 -0
  74. package/dist/esm/modules/RotateOverLifetime.d.ts +20 -0
  75. package/dist/esm/modules/RotateOverLifetime.js +41 -0
  76. package/dist/esm/modules/RotateOverLifetime.js.map +1 -0
  77. package/dist/esm/modules/ScaleOverLifetime.d.ts +26 -0
  78. package/dist/esm/modules/ScaleOverLifetime.js +59 -0
  79. package/dist/esm/modules/ScaleOverLifetime.js.map +1 -0
  80. package/dist/esm/modules/SpawnModule.d.ts +30 -0
  81. package/dist/esm/modules/SpawnModule.js +31 -0
  82. package/dist/esm/modules/SpawnModule.js.map +1 -0
  83. package/dist/esm/modules/SpawnOnDeath.d.ts +24 -0
  84. package/dist/esm/modules/SpawnOnDeath.js +47 -0
  85. package/dist/esm/modules/SpawnOnDeath.js.map +1 -0
  86. package/dist/esm/modules/Turbulence.d.ts +30 -0
  87. package/dist/esm/modules/Turbulence.js +122 -0
  88. package/dist/esm/modules/Turbulence.js.map +1 -0
  89. package/dist/esm/modules/UpdateModule.d.ts +95 -0
  90. package/dist/esm/modules/UpdateModule.js +66 -0
  91. package/dist/esm/modules/UpdateModule.js.map +1 -0
  92. package/dist/esm/modules/VelocityOverLifetime.d.ts +30 -0
  93. package/dist/esm/modules/VelocityOverLifetime.js +84 -0
  94. package/dist/esm/modules/VelocityOverLifetime.js.map +1 -0
  95. package/dist/esm/modules/WgslContribution.d.ts +81 -0
  96. package/dist/esm/modules/WgslContribution.js +34 -0
  97. package/dist/esm/modules/WgslContribution.js.map +1 -0
  98. package/dist/esm/modules/index.d.ts +22 -0
  99. package/dist/esm/particlesBuildInfo.d.ts +15 -0
  100. package/dist/esm/particlesBuildInfo.js +8 -0
  101. package/dist/esm/particlesBuildInfo.js.map +1 -0
  102. package/dist/esm/particlesExtension.d.ts +24 -0
  103. package/dist/esm/particlesExtension.js +49 -0
  104. package/dist/esm/particlesExtension.js.map +1 -0
  105. package/dist/esm/public.d.ts +9 -0
  106. package/dist/esm/register.d.ts +1 -0
  107. package/dist/esm/register.js +43 -0
  108. package/dist/esm/register.js.map +1 -0
  109. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +45 -0
  110. package/dist/esm/renderers/WebGl2ParticleRenderer.js +325 -0
  111. package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -0
  112. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +48 -0
  113. package/dist/esm/renderers/WebGpuParticleRenderer.js +553 -0
  114. package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -0
  115. package/dist/esm/renderers/glsl/particle.frag.js +4 -0
  116. package/dist/esm/renderers/glsl/particle.frag.js.map +1 -0
  117. package/dist/esm/renderers/glsl/particle.vert.js +4 -0
  118. package/dist/esm/renderers/glsl/particle.vert.js.map +1 -0
  119. 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;;;;"}