@codexo/exojs-particles 0.15.3 → 0.16.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 (211) hide show
  1. package/README.md +21 -43
  2. package/dist/esm/ParticleStorage.d.ts +188 -0
  3. package/dist/esm/ParticleStorage.d.ts.map +1 -0
  4. package/dist/esm/ParticleStorage.js +208 -0
  5. package/dist/esm/ParticleStorage.js.map +1 -0
  6. package/dist/esm/ParticleSystem.d.ts +189 -60
  7. package/dist/esm/ParticleSystem.d.ts.map +1 -0
  8. package/dist/esm/ParticleSystem.js +660 -560
  9. package/dist/esm/ParticleSystem.js.map +1 -1
  10. package/dist/esm/distributions/BoxArea.d.ts +1 -0
  11. package/dist/esm/distributions/BoxArea.d.ts.map +1 -0
  12. package/dist/esm/distributions/BoxArea.js +38 -44
  13. package/dist/esm/distributions/BoxArea.js.map +1 -1
  14. package/dist/esm/distributions/CircleArea.d.ts +1 -0
  15. package/dist/esm/distributions/CircleArea.d.ts.map +1 -0
  16. package/dist/esm/distributions/CircleArea.js +30 -28
  17. package/dist/esm/distributions/CircleArea.js.map +1 -1
  18. package/dist/esm/distributions/ColorGradient.d.ts +3 -2
  19. package/dist/esm/distributions/ColorGradient.d.ts.map +1 -0
  20. package/dist/esm/distributions/ColorGradient.js +70 -75
  21. package/dist/esm/distributions/ColorGradient.js.map +1 -1
  22. package/dist/esm/distributions/ConeDirection.d.ts +2 -1
  23. package/dist/esm/distributions/ConeDirection.d.ts.map +1 -0
  24. package/dist/esm/distributions/ConeDirection.js +41 -39
  25. package/dist/esm/distributions/ConeDirection.js.map +1 -1
  26. package/dist/esm/distributions/Constant.d.ts +1 -0
  27. package/dist/esm/distributions/Constant.d.ts.map +1 -0
  28. package/dist/esm/distributions/Constant.js +32 -32
  29. package/dist/esm/distributions/Constant.js.map +1 -1
  30. package/dist/esm/distributions/Curve.d.ts +2 -1
  31. package/dist/esm/distributions/Curve.d.ts.map +1 -0
  32. package/dist/esm/distributions/Curve.js +46 -55
  33. package/dist/esm/distributions/Curve.js.map +1 -1
  34. package/dist/esm/distributions/Distribution.d.ts +4 -3
  35. package/dist/esm/distributions/Distribution.d.ts.map +1 -0
  36. package/dist/esm/distributions/LineSegment.d.ts +1 -0
  37. package/dist/esm/distributions/LineSegment.d.ts.map +1 -0
  38. package/dist/esm/distributions/LineSegment.js +25 -23
  39. package/dist/esm/distributions/LineSegment.js.map +1 -1
  40. package/dist/esm/distributions/Range.d.ts +1 -0
  41. package/dist/esm/distributions/Range.d.ts.map +1 -0
  42. package/dist/esm/distributions/Range.js +18 -16
  43. package/dist/esm/distributions/Range.js.map +1 -1
  44. package/dist/esm/distributions/VectorRange.d.ts +1 -0
  45. package/dist/esm/distributions/VectorRange.d.ts.map +1 -0
  46. package/dist/esm/distributions/VectorRange.js +29 -27
  47. package/dist/esm/distributions/VectorRange.js.map +1 -1
  48. package/dist/esm/distributions/index.d.ts +1 -0
  49. package/dist/esm/distributions/index.d.ts.map +1 -0
  50. package/dist/esm/gpu/ParticleGpuState.d.ts +143 -18
  51. package/dist/esm/gpu/ParticleGpuState.d.ts.map +1 -0
  52. package/dist/esm/gpu/ParticleGpuState.js +655 -497
  53. package/dist/esm/gpu/ParticleGpuState.js.map +1 -1
  54. package/dist/esm/gpu/shaders/particle-simulate.wgsl.js +6 -0
  55. package/dist/esm/gpu/shaders/particle-simulate.wgsl.js.map +1 -0
  56. package/dist/esm/index.d.ts +1 -0
  57. package/dist/esm/index.d.ts.map +1 -0
  58. package/dist/esm/index.js +39 -32
  59. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +4 -3
  60. package/dist/esm/modules/AlphaFadeOverLifetime.d.ts.map +1 -0
  61. package/dist/esm/modules/AlphaFadeOverLifetime.js +68 -55
  62. package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -1
  63. package/dist/esm/modules/ApplyForce.d.ts +4 -3
  64. package/dist/esm/modules/ApplyForce.d.ts.map +1 -0
  65. package/dist/esm/modules/ApplyForce.js +49 -43
  66. package/dist/esm/modules/ApplyForce.js.map +1 -1
  67. package/dist/esm/modules/AttractToPoint.d.ts +4 -3
  68. package/dist/esm/modules/AttractToPoint.d.ts.map +1 -0
  69. package/dist/esm/modules/AttractToPoint.js +73 -62
  70. package/dist/esm/modules/AttractToPoint.js.map +1 -1
  71. package/dist/esm/modules/BurstSpawn.d.ts +6 -18
  72. package/dist/esm/modules/BurstSpawn.d.ts.map +1 -0
  73. package/dist/esm/modules/BurstSpawn.js +56 -91
  74. package/dist/esm/modules/BurstSpawn.js.map +1 -1
  75. package/dist/esm/modules/ColorOverLifetime.d.ts +4 -3
  76. package/dist/esm/modules/ColorOverLifetime.d.ts.map +1 -0
  77. package/dist/esm/modules/ColorOverLifetime.js +65 -53
  78. package/dist/esm/modules/ColorOverLifetime.js.map +1 -1
  79. package/dist/esm/modules/ColorOverSpeed.d.ts +5 -4
  80. package/dist/esm/modules/ColorOverSpeed.d.ts.map +1 -0
  81. package/dist/esm/modules/ColorOverSpeed.js +90 -75
  82. package/dist/esm/modules/ColorOverSpeed.js.map +1 -1
  83. package/dist/esm/modules/DeathModule.d.ts +26 -10
  84. package/dist/esm/modules/DeathModule.d.ts.map +1 -0
  85. package/dist/esm/modules/DeathModule.js +38 -22
  86. package/dist/esm/modules/DeathModule.js.map +1 -1
  87. package/dist/esm/modules/Drag.d.ts +3 -2
  88. package/dist/esm/modules/Drag.d.ts.map +1 -0
  89. package/dist/esm/modules/Drag.js +43 -37
  90. package/dist/esm/modules/Drag.js.map +1 -1
  91. package/dist/esm/modules/OrbitalForce.d.ts +4 -3
  92. package/dist/esm/modules/OrbitalForce.d.ts.map +1 -0
  93. package/dist/esm/modules/OrbitalForce.js +70 -58
  94. package/dist/esm/modules/OrbitalForce.js.map +1 -1
  95. package/dist/esm/modules/RateSpawn.d.ts +7 -22
  96. package/dist/esm/modules/RateSpawn.d.ts.map +1 -0
  97. package/dist/esm/modules/RateSpawn.js +39 -71
  98. package/dist/esm/modules/RateSpawn.js.map +1 -1
  99. package/dist/esm/modules/RepelFromPoint.d.ts +4 -3
  100. package/dist/esm/modules/RepelFromPoint.d.ts.map +1 -0
  101. package/dist/esm/modules/RepelFromPoint.js +72 -62
  102. package/dist/esm/modules/RepelFromPoint.js.map +1 -1
  103. package/dist/esm/modules/RotateOverLifetime.d.ts +3 -2
  104. package/dist/esm/modules/RotateOverLifetime.d.ts.map +1 -0
  105. package/dist/esm/modules/RotateOverLifetime.js +40 -36
  106. package/dist/esm/modules/RotateOverLifetime.js.map +1 -1
  107. package/dist/esm/modules/ScaleOverLifetime.d.ts +5 -4
  108. package/dist/esm/modules/ScaleOverLifetime.d.ts.map +1 -0
  109. package/dist/esm/modules/ScaleOverLifetime.js +60 -50
  110. package/dist/esm/modules/ScaleOverLifetime.js.map +1 -1
  111. package/dist/esm/modules/SpawnModule.d.ts +15 -12
  112. package/dist/esm/modules/SpawnModule.d.ts.map +1 -0
  113. package/dist/esm/modules/SpawnModule.js +32 -28
  114. package/dist/esm/modules/SpawnModule.js.map +1 -1
  115. package/dist/esm/modules/SpawnOnDeath.d.ts +13 -9
  116. package/dist/esm/modules/SpawnOnDeath.d.ts.map +1 -0
  117. package/dist/esm/modules/SpawnOnDeath.js +41 -43
  118. package/dist/esm/modules/SpawnOnDeath.js.map +1 -1
  119. package/dist/esm/modules/Turbulence.d.ts +4 -3
  120. package/dist/esm/modules/Turbulence.d.ts.map +1 -0
  121. package/dist/esm/modules/Turbulence.js +101 -89
  122. package/dist/esm/modules/Turbulence.js.map +1 -1
  123. package/dist/esm/modules/UpdateModule.d.ts +21 -13
  124. package/dist/esm/modules/UpdateModule.d.ts.map +1 -0
  125. package/dist/esm/modules/UpdateModule.js +65 -63
  126. package/dist/esm/modules/UpdateModule.js.map +1 -1
  127. package/dist/esm/modules/VelocityOverLifetime.d.ts +7 -6
  128. package/dist/esm/modules/VelocityOverLifetime.d.ts.map +1 -0
  129. package/dist/esm/modules/VelocityOverLifetime.js +75 -73
  130. package/dist/esm/modules/VelocityOverLifetime.js.map +1 -1
  131. package/dist/esm/modules/WgslContribution.d.ts +16 -15
  132. package/dist/esm/modules/WgslContribution.d.ts.map +1 -0
  133. package/dist/esm/modules/WgslContribution.js +37 -29
  134. package/dist/esm/modules/WgslContribution.js.map +1 -1
  135. package/dist/esm/modules/index.d.ts +3 -1
  136. package/dist/esm/modules/index.d.ts.map +1 -0
  137. package/dist/esm/modules/spawnFields.d.ts +32 -0
  138. package/dist/esm/modules/spawnFields.d.ts.map +1 -0
  139. package/dist/esm/modules/spawnFields.js +31 -0
  140. package/dist/esm/modules/spawnFields.js.map +1 -0
  141. package/dist/esm/particlesBuildInfo.d.ts +1 -0
  142. package/dist/esm/particlesBuildInfo.d.ts.map +1 -0
  143. package/dist/esm/particlesBuildInfo.js +6 -4
  144. package/dist/esm/particlesBuildInfo.js.map +1 -1
  145. package/dist/esm/particlesExtension.d.ts +13 -5
  146. package/dist/esm/particlesExtension.d.ts.map +1 -0
  147. package/dist/esm/particlesExtension.js +38 -42
  148. package/dist/esm/particlesExtension.js.map +1 -1
  149. package/dist/esm/public.d.ts +11 -0
  150. package/dist/esm/public.d.ts.map +1 -0
  151. package/dist/esm/renderModes/MeshParticles.d.ts +128 -0
  152. package/dist/esm/renderModes/MeshParticles.d.ts.map +1 -0
  153. package/dist/esm/renderModes/MeshParticles.js +263 -0
  154. package/dist/esm/renderModes/MeshParticles.js.map +1 -0
  155. package/dist/esm/renderModes/ParticleBufferLayout.d.ts +59 -0
  156. package/dist/esm/renderModes/ParticleBufferLayout.d.ts.map +1 -0
  157. package/dist/esm/renderModes/ParticleBufferLayout.js +99 -0
  158. package/dist/esm/renderModes/ParticleBufferLayout.js.map +1 -0
  159. package/dist/esm/renderModes/ParticleInstanceWriter.d.ts +58 -0
  160. package/dist/esm/renderModes/ParticleInstanceWriter.d.ts.map +1 -0
  161. package/dist/esm/renderModes/ParticleInstanceWriter.js +173 -0
  162. package/dist/esm/renderModes/ParticleInstanceWriter.js.map +1 -0
  163. package/dist/esm/renderModes/ParticleMaterial.d.ts +18 -0
  164. package/dist/esm/renderModes/ParticleMaterial.d.ts.map +1 -0
  165. package/dist/esm/renderModes/ParticleMaterial.js +24 -0
  166. package/dist/esm/renderModes/ParticleMaterial.js.map +1 -0
  167. package/dist/esm/renderModes/ParticleRenderMode.d.ts +114 -0
  168. package/dist/esm/renderModes/ParticleRenderMode.d.ts.map +1 -0
  169. package/dist/esm/renderModes/ParticleRenderMode.js +99 -0
  170. package/dist/esm/renderModes/ParticleRenderMode.js.map +1 -0
  171. package/dist/esm/renderModes/QuadParticles.d.ts +55 -0
  172. package/dist/esm/renderModes/QuadParticles.d.ts.map +1 -0
  173. package/dist/esm/renderModes/QuadParticles.js +95 -0
  174. package/dist/esm/renderModes/QuadParticles.js.map +1 -0
  175. package/dist/esm/renderModes/RibbonParticles.d.ts +96 -0
  176. package/dist/esm/renderModes/RibbonParticles.d.ts.map +1 -0
  177. package/dist/esm/renderModes/RibbonParticles.js +202 -0
  178. package/dist/esm/renderModes/RibbonParticles.js.map +1 -0
  179. package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js +6 -0
  180. package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js.map +1 -0
  181. package/dist/esm/renderModes/shaders/mesh.vert.js +6 -0
  182. package/dist/esm/renderModes/shaders/mesh.vert.js.map +1 -0
  183. package/dist/esm/renderModes/shaders/quad-particles.wgsl.js +6 -0
  184. package/dist/esm/renderModes/shaders/quad-particles.wgsl.js.map +1 -0
  185. package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js +6 -0
  186. package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js.map +1 -0
  187. package/dist/esm/renderModes/shaders/ribbon.frag.js +6 -0
  188. package/dist/esm/renderModes/shaders/ribbon.frag.js.map +1 -0
  189. package/dist/esm/renderModes/shaders/ribbon.vert.js +6 -0
  190. package/dist/esm/renderModes/shaders/ribbon.vert.js.map +1 -0
  191. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +48 -20
  192. package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts.map +1 -0
  193. package/dist/esm/renderers/WebGl2ParticleRenderer.js +317 -321
  194. package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -1
  195. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +99 -18
  196. package/dist/esm/renderers/WebGpuParticleRenderer.d.ts.map +1 -0
  197. package/dist/esm/renderers/WebGpuParticleRenderer.js +542 -551
  198. package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -1
  199. package/dist/esm/renderers/shaders/particle.frag.js +6 -0
  200. package/dist/esm/renderers/shaders/particle.frag.js.map +1 -0
  201. package/dist/esm/renderers/shaders/particle.vert.js +6 -0
  202. package/dist/esm/renderers/shaders/particle.vert.js.map +1 -0
  203. package/package.json +8 -15
  204. package/dist/esm/index.js.map +0 -1
  205. package/dist/esm/register.d.ts +0 -1
  206. package/dist/esm/register.js +0 -43
  207. package/dist/esm/register.js.map +0 -1
  208. package/dist/esm/renderers/glsl/particle.frag.js +0 -4
  209. package/dist/esm/renderers/glsl/particle.frag.js.map +0 -1
  210. package/dist/esm/renderers/glsl/particle.vert.js +0 -4
  211. package/dist/esm/renderers/glsl/particle.vert.js.map +0 -1
@@ -1,58 +1,73 @@
1
- import { UpdateModule } from './UpdateModule.js';
1
+ import { UpdateModule } from "./UpdateModule.js";
2
2
 
3
+ //#region src/modules/Turbulence.ts
3
4
  /**
4
- * Adds a smooth pseudo-random force field that animates over time.
5
- * Implemented as 2D value noise with cubic Hermite smoothing sampled
6
- * twice per particle (offset to decorrelate x and y components) and scaled
7
- * by `strength`. The field evolves at `timeScale` units per second; lower
8
- * values produce slow-moving currents, higher values produce buzzy chaos.
9
- *
10
- * `frequency` controls the spatial granularity: small values (≈ 0.005)
11
- * yield broad swirls across the playfield, large values (≈ 0.1) produce
12
- * tight per-particle jitter.
13
- *
14
- * Use cases: smoke turbulence, organic swirls, wind eddies, dust haze.
15
- * Pair with {@link Drag} to keep particle velocities bounded.
16
- *
17
- * GPU-eligible. The noise function is identical on CPU and GPU so visual
18
- * results match across backends (modulo float precision).
19
- */
20
- class Turbulence extends UpdateModule {
21
- strength;
22
- frequency;
23
- timeScale;
24
- _time = 0;
25
- constructor(strength, frequency = 0.01, timeScale = 1) {
26
- super();
27
- this.strength = strength;
28
- this.frequency = frequency;
29
- this.timeScale = timeScale;
30
- }
31
- apply(system, dt) {
32
- this._time += dt * this.timeScale;
33
- const t = this._time;
34
- const f = this.frequency;
35
- const s = this.strength * dt;
36
- const { posX, posY, velX, velY, liveCount } = system;
37
- for (let i = 0; i < liveCount; i++) {
38
- const x = (posX[i] ?? 0) * f;
39
- const y = (posY[i] ?? 0) * f;
40
- const nx = valueNoise2(x + t, y);
41
- const ny = valueNoise2(x, y + t + 17.31);
42
- velX[i] = (velX[i] ?? 0) + (nx * 2 - 1) * s;
43
- velY[i] = (velY[i] ?? 0) + (ny * 2 - 1) * s;
44
- }
45
- }
46
- wgsl() {
47
- return {
48
- key: 'Turbulence',
49
- uniforms: [
50
- { name: 'strength', type: 'f32' },
51
- { name: 'frequency', type: 'f32' },
52
- { name: 'time', type: 'f32' },
53
- { name: '_pad0', type: 'f32' },
54
- ],
55
- prelude: `
5
+ * Adds a smooth pseudo-random force field that animates over time.
6
+ * Implemented as 2D value noise with cubic Hermite smoothing - sampled
7
+ * twice per particle (offset to decorrelate x and y components) and scaled
8
+ * by `strength`. The field evolves at `timeScale` units per second; lower
9
+ * values produce slow-moving currents, higher values produce buzzy chaos.
10
+ *
11
+ * `frequency` controls the spatial granularity: small values (≈ 0.005)
12
+ * yield broad swirls across the playfield, large values (≈ 0.1) produce
13
+ * tight per-particle jitter.
14
+ *
15
+ * Use cases: smoke turbulence, organic swirls, wind eddies, dust haze.
16
+ * Pair with {@link Drag} to keep particle velocities bounded.
17
+ *
18
+ * GPU-eligible. The noise function is identical on CPU and GPU so visual
19
+ * results match across backends (modulo float precision).
20
+ */
21
+ var Turbulence = class extends UpdateModule {
22
+ strength;
23
+ frequency;
24
+ timeScale;
25
+ _time = 0;
26
+ constructor(strength, frequency = .01, timeScale = 1) {
27
+ super();
28
+ this.strength = strength;
29
+ this.frequency = frequency;
30
+ this.timeScale = timeScale;
31
+ }
32
+ apply(particles, dt) {
33
+ this._time += dt * this.timeScale;
34
+ const t = this._time;
35
+ const f = this.frequency;
36
+ const s = this.strength * dt;
37
+ const { x: posX, y: posY } = particles.position;
38
+ const { x: velX, y: velY } = particles.velocity;
39
+ const liveCount = particles.count;
40
+ for (let i = 0; i < liveCount; i++) {
41
+ const x = (posX[i] ?? 0) * f;
42
+ const y = (posY[i] ?? 0) * f;
43
+ const nx = valueNoise2(x + t, y);
44
+ const ny = valueNoise2(x, y + t + 17.31);
45
+ velX[i] = (velX[i] ?? 0) + (nx * 2 - 1) * s;
46
+ velY[i] = (velY[i] ?? 0) + (ny * 2 - 1) * s;
47
+ }
48
+ }
49
+ wgsl() {
50
+ return {
51
+ key: "Turbulence",
52
+ uniforms: [
53
+ {
54
+ name: "strength",
55
+ type: "f32"
56
+ },
57
+ {
58
+ name: "frequency",
59
+ type: "f32"
60
+ },
61
+ {
62
+ name: "time",
63
+ type: "f32"
64
+ },
65
+ {
66
+ name: "_pad0",
67
+ type: "f32"
68
+ }
69
+ ],
70
+ prelude: `
56
71
  fn exojs_turbulence_hash21(p: vec2<f32>) -> f32 {
57
72
  let n = sin(dot(p, vec2<f32>(127.1, 311.7))) * 43758.5453;
58
73
  return fract(n);
@@ -74,7 +89,7 @@ fn exojs_turbulence_valueNoise2(x: f32, y: f32) -> f32 {
74
89
  return ab + (cd - ab) * v;
75
90
  }
76
91
  `,
77
- body: `
92
+ body: `
78
93
  let turbF = modules.u_Turbulence.frequency;
79
94
  let turbT = modules.u_Turbulence.time;
80
95
  let turbS = modules.u_Turbulence.strength * dt;
@@ -83,40 +98,37 @@ fn exojs_turbulence_valueNoise2(x: f32, y: f32) -> f32 {
83
98
  let turbNx = exojs_turbulence_valueNoise2(turbX + turbT, turbY);
84
99
  let turbNy = exojs_turbulence_valueNoise2(turbX, turbY + turbT + 17.31);
85
100
  velocities[idx] = velocities[idx] + vec2<f32>(turbNx * 2.0 - 1.0, turbNy * 2.0 - 1.0) * turbS;
86
- `,
87
- };
88
- }
89
- writeUniforms(view, offset, dt) {
90
- // GPU mode: apply() never runs, so advance _time here once per frame.
91
- // CPU mode: apply() already advances _time before update finishes,
92
- // and writeUniforms is not called → no double-advance.
93
- this._time += dt * this.timeScale;
94
- view.setFloat32(offset + 0, this.strength, true);
95
- view.setFloat32(offset + 4, this.frequency, true);
96
- view.setFloat32(offset + 8, this._time, true);
97
- view.setFloat32(offset + 12, 0, true);
98
- }
99
- }
100
- function hash21(x, y) {
101
- let n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453;
102
- n = n - Math.floor(n);
103
- return n;
104
- }
105
- function valueNoise2(x, y) {
106
- const xi = Math.floor(x);
107
- const yi = Math.floor(y);
108
- const xf = x - xi;
109
- const yf = y - yi;
110
- const u = xf * xf * (3 - 2 * xf);
111
- const v = yf * yf * (3 - 2 * yf);
112
- const a = hash21(xi, yi);
113
- const b = hash21(xi + 1, yi);
114
- const c = hash21(xi, yi + 1);
115
- const d = hash21(xi + 1, yi + 1);
116
- const ab = a + (b - a) * u;
117
- const cd = c + (d - c) * u;
118
- return ab + (cd - ab) * v;
119
- }
101
+ `
102
+ };
103
+ }
104
+ writeUniforms(view, offset, dt) {
105
+ this._time += dt * this.timeScale;
106
+ view.setFloat32(offset + 0, this.strength, true);
107
+ view.setFloat32(offset + 4, this.frequency, true);
108
+ view.setFloat32(offset + 8, this._time, true);
109
+ view.setFloat32(offset + 12, 0, true);
110
+ }
111
+ };
112
+ const hash21 = (x, y) => {
113
+ let n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453;
114
+ n = n - Math.floor(n);
115
+ return n;
116
+ };
117
+ const valueNoise2 = (x, y) => {
118
+ const xi = Math.floor(x);
119
+ const yi = Math.floor(y);
120
+ const xf = x - xi;
121
+ const yf = y - yi;
122
+ const u = xf * xf * (3 - 2 * xf);
123
+ const v = yf * yf * (3 - 2 * yf);
124
+ const a = hash21(xi, yi);
125
+ const b = hash21(xi + 1, yi);
126
+ const c = hash21(xi, yi + 1);
127
+ const d = hash21(xi + 1, yi + 1);
128
+ const ab = a + (b - a) * u;
129
+ return ab + (c + (d - c) * u - ab) * v;
130
+ };
120
131
 
132
+ //#endregion
121
133
  export { Turbulence };
122
- //# sourceMappingURL=Turbulence.js.map
134
+ //# sourceMappingURL=Turbulence.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Turbulence.js","sources":["../../../../src/modules/Turbulence.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"Turbulence.js","names":[],"sources":["../../../src/modules/Turbulence.ts"],"sourcesContent":["import type { ParticleBatch } from '#ParticleStorage';\n\nimport { UpdateModule } from './UpdateModule';\nimport type { WgslContribution } from './WgslContribution';\n\n/**\n * Adds a smooth pseudo-random force field that animates over time.\n * Implemented as 2D value noise with cubic Hermite smoothing - sampled\n * twice per particle (offset to decorrelate x and y components) and scaled\n * by `strength`. The field evolves at `timeScale` units per second; lower\n * values produce slow-moving currents, higher values produce buzzy chaos.\n *\n * `frequency` controls the spatial granularity: small values (≈ 0.005)\n * yield broad swirls across the playfield, large values (≈ 0.1) produce\n * tight per-particle jitter.\n *\n * Use cases: smoke turbulence, organic swirls, wind eddies, dust haze.\n * Pair with {@link Drag} to keep particle velocities bounded.\n *\n * GPU-eligible. The noise function is identical on CPU and GPU so visual\n * results match across backends (modulo float precision).\n */\nexport class Turbulence extends UpdateModule {\n public strength: number;\n public frequency: number;\n public timeScale: number;\n private _time = 0;\n\n public constructor(strength: number, frequency = 0.01, timeScale = 1) {\n super();\n this.strength = strength;\n this.frequency = frequency;\n this.timeScale = timeScale;\n }\n\n public override apply(particles: ParticleBatch, dt: number): void {\n this._time += dt * this.timeScale;\n const t = this._time;\n const f = this.frequency;\n const s = this.strength * dt;\n\n const { x: posX, y: posY } = particles.position;\n const { x: velX, y: velY } = particles.velocity;\n const liveCount = particles.count;\n\n for (let i = 0; i < liveCount; i++) {\n const x = (posX[i] ?? 0) * f;\n const y = (posY[i] ?? 0) * f;\n const nx = valueNoise2(x + t, y);\n const ny = valueNoise2(x, y + t + 17.31);\n\n velX[i] = (velX[i] ?? 0) + (nx * 2 - 1) * s;\n velY[i] = (velY[i] ?? 0) + (ny * 2 - 1) * s;\n }\n }\n\n public override wgsl(): WgslContribution {\n return {\n key: 'Turbulence',\n uniforms: [\n { name: 'strength', type: 'f32' },\n { name: 'frequency', type: 'f32' },\n { name: 'time', type: 'f32' },\n { name: '_pad0', type: 'f32' },\n ],\n prelude: `\nfn exojs_turbulence_hash21(p: vec2<f32>) -> f32 {\n let n = sin(dot(p, vec2<f32>(127.1, 311.7))) * 43758.5453;\n return fract(n);\n}\n\nfn exojs_turbulence_valueNoise2(x: f32, y: f32) -> f32 {\n let xi = floor(x);\n let yi = floor(y);\n let xf = x - xi;\n let yf = y - yi;\n let u = xf * xf * (3.0 - 2.0 * xf);\n let v = yf * yf * (3.0 - 2.0 * yf);\n let a = exojs_turbulence_hash21(vec2<f32>(xi, yi));\n let b = exojs_turbulence_hash21(vec2<f32>(xi + 1.0, yi));\n let c = exojs_turbulence_hash21(vec2<f32>(xi, yi + 1.0));\n let d = exojs_turbulence_hash21(vec2<f32>(xi + 1.0, yi + 1.0));\n let ab = a + (b - a) * u;\n let cd = c + (d - c) * u;\n return ab + (cd - ab) * v;\n}\n `,\n body: `\n let turbF = modules.u_Turbulence.frequency;\n let turbT = modules.u_Turbulence.time;\n let turbS = modules.u_Turbulence.strength * dt;\n let turbX = positions[idx].x * turbF;\n let turbY = positions[idx].y * turbF;\n let turbNx = exojs_turbulence_valueNoise2(turbX + turbT, turbY);\n let turbNy = exojs_turbulence_valueNoise2(turbX, turbY + turbT + 17.31);\n velocities[idx] = velocities[idx] + vec2<f32>(turbNx * 2.0 - 1.0, turbNy * 2.0 - 1.0) * turbS;\n `,\n };\n }\n\n public override writeUniforms(view: DataView, offset: number, dt: number): void {\n // GPU mode: apply() never runs, so advance _time here once per frame.\n // CPU mode: apply() already advances _time before update finishes,\n // and writeUniforms is not called → no double-advance.\n this._time += dt * this.timeScale;\n\n view.setFloat32(offset + 0, this.strength, true);\n view.setFloat32(offset + 4, this.frequency, true);\n view.setFloat32(offset + 8, this._time, true);\n view.setFloat32(offset + 12, 0, true);\n }\n}\n\nconst hash21 = (x: number, y: number): number => {\n let n = Math.sin(x * 127.1 + y * 311.7) * 43758.5453;\n n = n - Math.floor(n);\n return n;\n};\n\nconst valueNoise2 = (x: number, y: number): number => {\n const xi = Math.floor(x);\n const yi = Math.floor(y);\n const xf = x - xi;\n const yf = y - yi;\n\n const u = xf * xf * (3 - 2 * xf);\n const v = yf * yf * (3 - 2 * yf);\n\n const a = hash21(xi, yi);\n const b = hash21(xi + 1, yi);\n const c = hash21(xi, yi + 1);\n const d = hash21(xi + 1, yi + 1);\n\n const ab = a + (b - a) * u;\n const cd = c + (d - c) * u;\n return ab + (cd - ab) * v;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAsBA,IAAa,aAAb,cAAgC,aAAa;CAC3C,AAAO;CACP,AAAO;CACP,AAAO;CACP,AAAQ,QAAQ;CAEhB,AAAO,YAAY,UAAkB,YAAY,KAAM,YAAY,GAAG;EACpE,MAAM;EACN,KAAK,WAAW;EAChB,KAAK,YAAY;EACjB,KAAK,YAAY;CACnB;CAEA,AAAgB,MAAM,WAA0B,IAAkB;EAChE,KAAK,SAAS,KAAK,KAAK;EACxB,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,KAAK;EACf,MAAM,IAAI,KAAK,WAAW;EAE1B,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,UAAU;EACvC,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,UAAU;EACvC,MAAM,YAAY,UAAU;EAE5B,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;GAClC,MAAM,KAAK,KAAK,MAAM,KAAK;GAC3B,MAAM,KAAK,KAAK,MAAM,KAAK;GAC3B,MAAM,KAAK,YAAY,IAAI,GAAG,CAAC;GAC/B,MAAM,KAAK,YAAY,GAAG,IAAI,IAAI,KAAK;GAEvC,KAAK,MAAM,KAAK,MAAM,MAAM,KAAK,IAAI,KAAK;GAC1C,KAAK,MAAM,KAAK,MAAM,MAAM,KAAK,IAAI,KAAK;EAC5C;CACF;CAEA,AAAgB,OAAyB;EACvC,OAAO;GACL,KAAK;GACL,UAAU;IACR;KAAE,MAAM;KAAY,MAAM;IAAM;IAChC;KAAE,MAAM;KAAa,MAAM;IAAM;IACjC;KAAE,MAAM;KAAQ,MAAM;IAAM;IAC5B;KAAE,MAAM;KAAS,MAAM;IAAM;GAC/B;GACA,SAAS;;;;;;;;;;;;;;;;;;;;;;GAsBT,MAAM;;;;;;;;;;EAUR;CACF;CAEA,AAAgB,cAAc,MAAgB,QAAgB,IAAkB;EAI9E,KAAK,SAAS,KAAK,KAAK;EAExB,KAAK,WAAW,SAAS,GAAG,KAAK,UAAU,IAAI;EAC/C,KAAK,WAAW,SAAS,GAAG,KAAK,WAAW,IAAI;EAChD,KAAK,WAAW,SAAS,GAAG,KAAK,OAAO,IAAI;EAC5C,KAAK,WAAW,SAAS,IAAI,GAAG,IAAI;CACtC;AACF;AAEA,MAAM,UAAU,GAAW,MAAsB;CAC/C,IAAI,IAAI,KAAK,IAAI,IAAI,QAAQ,IAAI,KAAK,IAAI;CAC1C,IAAI,IAAI,KAAK,MAAM,CAAC;CACpB,OAAO;AACT;AAEA,MAAM,eAAe,GAAW,MAAsB;CACpD,MAAM,KAAK,KAAK,MAAM,CAAC;CACvB,MAAM,KAAK,KAAK,MAAM,CAAC;CACvB,MAAM,KAAK,IAAI;CACf,MAAM,KAAK,IAAI;CAEf,MAAM,IAAI,KAAK,MAAM,IAAI,IAAI;CAC7B,MAAM,IAAI,KAAK,MAAM,IAAI,IAAI;CAE7B,MAAM,IAAI,OAAO,IAAI,EAAE;CACvB,MAAM,IAAI,OAAO,KAAK,GAAG,EAAE;CAC3B,MAAM,IAAI,OAAO,IAAI,KAAK,CAAC;CAC3B,MAAM,IAAI,OAAO,KAAK,GAAG,KAAK,CAAC;CAE/B,MAAM,KAAK,KAAK,IAAI,KAAK;CAEzB,OAAO,MADI,KAAK,IAAI,KAAK,IACP,MAAM;AAC1B"}
@@ -1,25 +1,25 @@
1
- import type { ParticleSystem } from "#ParticleSystem";
1
+ import type { ParticleBatch } from '#ParticleStorage';
2
2
  import type { WgslContribution } from './WgslContribution';
3
3
  /**
4
- * Per-frame, per-batch mutator. Operates on the system's SoA storage
5
- * directly typically a single tight loop over `[0, system.liveCount)`
6
- * that reads/writes the relevant `Float32Array`s.
4
+ * Per-frame, per-batch mutator. Operates on the live particles through their
5
+ * named channels - typically a single tight loop over `[0, particles.count)`
6
+ * that reads and writes the channel arrays it needs.
7
7
  *
8
8
  * Implementations must always provide a CPU `apply()`. To make a module
9
9
  * GPU-eligible (executed inside the system's composite compute shader on
10
10
  * WebGPU backends), additionally implement {@link wgsl} and
11
11
  * {@link writeUniforms}. Modules that declare a {@link WgslContribution}
12
12
  * may also opt to declare a 1D texture binding via the `textures` field
13
- * (used by `Curve` / `ColorGradient`-driven modules) in which case
13
+ * (used by `Curve` / `ColorGradient`-driven modules) - in which case
14
14
  * {@link uploadTextures} runs once at compile time to upload the data.
15
15
  *
16
16
  * Implementation pattern (CPU-only module):
17
17
  *
18
18
  * ```ts
19
19
  * class MyModule extends UpdateModule {
20
- * apply(system, dt) {
21
- * const { velX, velY, liveCount } = system;
22
- * for (let i = 0; i < liveCount; i++) { velX[i] *= 0.99; velY[i] *= 0.99; }
20
+ * apply(particles, dt) {
21
+ * const { x: velX, y: velY } = particles.velocity;
22
+ * for (let i = 0; i < particles.count; i++) { velX[i] *= 0.99; velY[i] *= 0.99; }
23
23
  * }
24
24
  * }
25
25
  * ```
@@ -30,9 +30,9 @@ import type { WgslContribution } from './WgslContribution';
30
30
  * class MyForce extends UpdateModule {
31
31
  * constructor(public ax: number, public ay: number) { super(); }
32
32
  *
33
- * apply(system, dt) {
34
- * const { velX, velY, liveCount } = system;
35
- * for (let i = 0; i < liveCount; i++) { velX[i] += this.ax * dt; velY[i] += this.ay * dt; }
33
+ * apply(particles, dt) {
34
+ * const { x: velX, y: velY } = particles.velocity;
35
+ * for (let i = 0; i < particles.count; i++) { velX[i] += this.ax * dt; velY[i] += this.ay * dt; }
36
36
  * }
37
37
  *
38
38
  * wgsl(): WgslContribution {
@@ -51,7 +51,7 @@ import type { WgslContribution } from './WgslContribution';
51
51
  * ```
52
52
  *
53
53
  * If *any* registered update module on a system lacks `wgsl()`, the system
54
- * forces CPU mode regardless of backend preserving the contract that
54
+ * forces CPU mode regardless of backend - preserving the contract that
55
55
  * `apply()` is always honoured. Built-in modules ship both
56
56
  * implementations; custom modules can opt into GPU acceleration at their
57
57
  * authors' discretion.
@@ -60,7 +60,14 @@ import type { WgslContribution } from './WgslContribution';
60
60
  * in registration order; later modules see the effects of earlier ones.
61
61
  */
62
62
  export declare abstract class UpdateModule {
63
- abstract apply(system: ParticleSystem, dt: number): void;
63
+ /**
64
+ * Mutates the live particles for one frame.
65
+ *
66
+ * Runs only on the CPU path: a system whose modules are all GPU-eligible
67
+ * executes {@link wgsl} bodies inside its compute shader instead, and never
68
+ * calls this.
69
+ */
70
+ abstract apply(particles: ParticleBatch, dt: number): void;
64
71
  /**
65
72
  * Override to declare a GPU contribution. Returning a {@link WgslContribution}
66
73
  * makes this module GPU-eligible; omitting (or returning undefined)
@@ -93,3 +100,4 @@ export declare abstract class UpdateModule {
93
100
  /** Optional cleanup hook called from `ParticleSystem.destroy`. */
94
101
  destroy(): void;
95
102
  }
103
+ //# sourceMappingURL=UpdateModule.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UpdateModule.d.ts","sourceRoot":"","sources":["../../../src/modules/UpdateModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0DG;AACH,8BAAsB,YAAY;IAChC;;;;;;OAMG;aACa,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAEjE;;;;OAIG;IACI,IAAI,CAAC,IAAI,gBAAgB;IAEhC;;;;;;;;;;;;OAYG;IACI,aAAa,CAAC,CAAC,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAE3E;;;;;;;OAOG;IACI,cAAc,CAAC,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;IAE1F,kEAAkE;IAC3D,OAAO,IAAI,IAAI;CACvB"}
@@ -1,66 +1,68 @@
1
+ //#region src/modules/UpdateModule.ts
1
2
  /**
2
- * Per-frame, per-batch mutator. Operates on the system's SoA storage
3
- * directly typically a single tight loop over `[0, system.liveCount)`
4
- * that reads/writes the relevant `Float32Array`s.
5
- *
6
- * Implementations must always provide a CPU `apply()`. To make a module
7
- * GPU-eligible (executed inside the system's composite compute shader on
8
- * WebGPU backends), additionally implement {@link wgsl} and
9
- * {@link writeUniforms}. Modules that declare a {@link WgslContribution}
10
- * may also opt to declare a 1D texture binding via the `textures` field
11
- * (used by `Curve` / `ColorGradient`-driven modules) in which case
12
- * {@link uploadTextures} runs once at compile time to upload the data.
13
- *
14
- * Implementation pattern (CPU-only module):
15
- *
16
- * ```ts
17
- * class MyModule extends UpdateModule {
18
- * apply(system, dt) {
19
- * const { velX, velY, liveCount } = system;
20
- * for (let i = 0; i < liveCount; i++) { velX[i] *= 0.99; velY[i] *= 0.99; }
21
- * }
22
- * }
23
- * ```
24
- *
25
- * Implementation pattern (GPU-eligible module):
26
- *
27
- * ```ts
28
- * class MyForce extends UpdateModule {
29
- * constructor(public ax: number, public ay: number) { super(); }
30
- *
31
- * apply(system, dt) {
32
- * const { velX, velY, liveCount } = system;
33
- * for (let i = 0; i < liveCount; i++) { velX[i] += this.ax * dt; velY[i] += this.ay * dt; }
34
- * }
35
- *
36
- * wgsl(): WgslContribution {
37
- * return {
38
- * key: 'MyForce',
39
- * uniforms: [{ name: 'ax', type: 'f32' }, { name: 'ay', type: 'f32' }],
40
- * body: `velX[idx] += u_MyForce.ax * dt; velY[idx] += u_MyForce.ay * dt;`,
41
- * };
42
- * }
43
- *
44
- * writeUniforms(view, offset) {
45
- * view.setFloat32(offset + 0, this.ax, true);
46
- * view.setFloat32(offset + 4, this.ay, true);
47
- * }
48
- * }
49
- * ```
50
- *
51
- * If *any* registered update module on a system lacks `wgsl()`, the system
52
- * forces CPU mode regardless of backend preserving the contract that
53
- * `apply()` is always honoured. Built-in modules ship both
54
- * implementations; custom modules can opt into GPU acceleration at their
55
- * authors' discretion.
56
- *
57
- * Update modules run after integration each frame. Multiple modules execute
58
- * in registration order; later modules see the effects of earlier ones.
59
- */
60
- class UpdateModule {
61
- /** Optional cleanup hook called from `ParticleSystem.destroy`. */
62
- destroy() { }
63
- }
3
+ * Per-frame, per-batch mutator. Operates on the live particles through their
4
+ * named channels - typically a single tight loop over `[0, particles.count)`
5
+ * that reads and writes the channel arrays it needs.
6
+ *
7
+ * Implementations must always provide a CPU `apply()`. To make a module
8
+ * GPU-eligible (executed inside the system's composite compute shader on
9
+ * WebGPU backends), additionally implement {@link wgsl} and
10
+ * {@link writeUniforms}. Modules that declare a {@link WgslContribution}
11
+ * may also opt to declare a 1D texture binding via the `textures` field
12
+ * (used by `Curve` / `ColorGradient`-driven modules) - in which case
13
+ * {@link uploadTextures} runs once at compile time to upload the data.
14
+ *
15
+ * Implementation pattern (CPU-only module):
16
+ *
17
+ * ```ts
18
+ * class MyModule extends UpdateModule {
19
+ * apply(particles, dt) {
20
+ * const { x: velX, y: velY } = particles.velocity;
21
+ * for (let i = 0; i < particles.count; i++) { velX[i] *= 0.99; velY[i] *= 0.99; }
22
+ * }
23
+ * }
24
+ * ```
25
+ *
26
+ * Implementation pattern (GPU-eligible module):
27
+ *
28
+ * ```ts
29
+ * class MyForce extends UpdateModule {
30
+ * constructor(public ax: number, public ay: number) { super(); }
31
+ *
32
+ * apply(particles, dt) {
33
+ * const { x: velX, y: velY } = particles.velocity;
34
+ * for (let i = 0; i < particles.count; i++) { velX[i] += this.ax * dt; velY[i] += this.ay * dt; }
35
+ * }
36
+ *
37
+ * wgsl(): WgslContribution {
38
+ * return {
39
+ * key: 'MyForce',
40
+ * uniforms: [{ name: 'ax', type: 'f32' }, { name: 'ay', type: 'f32' }],
41
+ * body: `velX[idx] += u_MyForce.ax * dt; velY[idx] += u_MyForce.ay * dt;`,
42
+ * };
43
+ * }
44
+ *
45
+ * writeUniforms(view, offset) {
46
+ * view.setFloat32(offset + 0, this.ax, true);
47
+ * view.setFloat32(offset + 4, this.ay, true);
48
+ * }
49
+ * }
50
+ * ```
51
+ *
52
+ * If *any* registered update module on a system lacks `wgsl()`, the system
53
+ * forces CPU mode regardless of backend - preserving the contract that
54
+ * `apply()` is always honoured. Built-in modules ship both
55
+ * implementations; custom modules can opt into GPU acceleration at their
56
+ * authors' discretion.
57
+ *
58
+ * Update modules run after integration each frame. Multiple modules execute
59
+ * in registration order; later modules see the effects of earlier ones.
60
+ */
61
+ var UpdateModule = class {
62
+ /** Optional cleanup hook called from `ParticleSystem.destroy`. */
63
+ destroy() {}
64
+ };
64
65
 
66
+ //#endregion
65
67
  export { UpdateModule };
66
- //# sourceMappingURL=UpdateModule.js.map
68
+ //# sourceMappingURL=UpdateModule.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"UpdateModule.js","sources":["../../../../src/modules/UpdateModule.ts"],"sourcesContent":[null],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0DG;MACmB,YAAY,CAAA;;AAoCzB,IAAA,OAAO,KAAU;AACzB;;;;"}
1
+ {"version":3,"file":"UpdateModule.js","names":[],"sources":["../../../src/modules/UpdateModule.ts"],"sourcesContent":["import type { ParticleBatch } from '#ParticleStorage';\n\nimport type { WgslContribution } from './WgslContribution';\n\n/**\n * Per-frame, per-batch mutator. Operates on the live particles through their\n * named channels - typically a single tight loop over `[0, particles.count)`\n * that reads and writes the channel arrays it needs.\n *\n * Implementations must always provide a CPU `apply()`. To make a module\n * GPU-eligible (executed inside the system's composite compute shader on\n * WebGPU backends), additionally implement {@link wgsl} and\n * {@link writeUniforms}. Modules that declare a {@link WgslContribution}\n * may also opt to declare a 1D texture binding via the `textures` field\n * (used by `Curve` / `ColorGradient`-driven modules) - in which case\n * {@link uploadTextures} runs once at compile time to upload the data.\n *\n * Implementation pattern (CPU-only module):\n *\n * ```ts\n * class MyModule extends UpdateModule {\n * apply(particles, dt) {\n * const { x: velX, y: velY } = particles.velocity;\n * for (let i = 0; i < particles.count; i++) { velX[i] *= 0.99; velY[i] *= 0.99; }\n * }\n * }\n * ```\n *\n * Implementation pattern (GPU-eligible module):\n *\n * ```ts\n * class MyForce extends UpdateModule {\n * constructor(public ax: number, public ay: number) { super(); }\n *\n * apply(particles, dt) {\n * const { x: velX, y: velY } = particles.velocity;\n * for (let i = 0; i < particles.count; i++) { velX[i] += this.ax * dt; velY[i] += this.ay * dt; }\n * }\n *\n * wgsl(): WgslContribution {\n * return {\n * key: 'MyForce',\n * uniforms: [{ name: 'ax', type: 'f32' }, { name: 'ay', type: 'f32' }],\n * body: `velX[idx] += u_MyForce.ax * dt; velY[idx] += u_MyForce.ay * dt;`,\n * };\n * }\n *\n * writeUniforms(view, offset) {\n * view.setFloat32(offset + 0, this.ax, true);\n * view.setFloat32(offset + 4, this.ay, true);\n * }\n * }\n * ```\n *\n * If *any* registered update module on a system lacks `wgsl()`, the system\n * forces CPU mode regardless of backend - preserving the contract that\n * `apply()` is always honoured. Built-in modules ship both\n * implementations; custom modules can opt into GPU acceleration at their\n * authors' discretion.\n *\n * Update modules run after integration each frame. Multiple modules execute\n * in registration order; later modules see the effects of earlier ones.\n */\nexport abstract class UpdateModule {\n /**\n * Mutates the live particles for one frame.\n *\n * Runs only on the CPU path: a system whose modules are all GPU-eligible\n * executes {@link wgsl} bodies inside its compute shader instead, and never\n * calls this.\n */\n public abstract apply(particles: ParticleBatch, dt: number): void;\n\n /**\n * Override to declare a GPU contribution. Returning a {@link WgslContribution}\n * makes this module GPU-eligible; omitting (or returning undefined)\n * forces CPU mode for any system that uses this module.\n */\n public wgsl?(): WgslContribution;\n\n /**\n * Write this module's current uniform values into the shared uniform\n * buffer at `byteOffset`. Layout must match the field declarations\n * returned by {@link wgsl} (in the same order).\n *\n * Receives the current frame `dt` (seconds). Modules tracking\n * accumulated time (e.g. noise/turbulence) should advance their\n * internal counter here to stay in sync when running in GPU mode\n * (where {@link apply} is not called).\n *\n * Required when {@link wgsl} declares uniforms. Called every frame by\n * the system before dispatching compute.\n */\n public writeUniforms?(view: DataView, byteOffset: number, dt: number): void;\n\n /**\n * Upload texture data (Curve/ColorGradient lookup tables) to the GPU at\n * compile time. Receives the GPUDevice and a map of texture bindings\n * keyed by the `name` in {@link WgslContribution.textures}. Called once\n * after pipeline creation.\n *\n * Required when {@link wgsl} declares textures.\n */\n public uploadTextures?(device: GPUDevice, textures: ReadonlyMap<string, GPUTexture>): void;\n\n /** Optional cleanup hook called from `ParticleSystem.destroy`. */\n public destroy(): void {}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA+DA,IAAsB,eAAtB,MAAmC;;CA2CjC,AAAO,UAAgB,CAAC;AAC1B"}
@@ -1,16 +1,16 @@
1
- import type { Curve } from "#distributions/Curve";
2
- import type { ParticleSystem } from "#ParticleSystem";
1
+ import type { Curve } from '#distributions/Curve';
2
+ import type { ParticleBatch } from '#ParticleStorage';
3
3
  import { UpdateModule } from './UpdateModule';
4
4
  import type { WgslContribution } from './WgslContribution';
5
5
  /**
6
6
  * Multiplies every live particle's velocity by a curve sampled at the
7
7
  * particle's current `elapsed / lifetime` ratio. The same scalar is applied
8
- * to both axes the direction is preserved, only the magnitude scales.
8
+ * to both axes - the direction is preserved, only the magnitude scales.
9
9
  *
10
10
  * Common patterns:
11
- * - **Snappy spawn, slow drift:** `[0,1]→[1,0.1]` fast initial motion
11
+ * - **Snappy spawn, slow drift:** `[0,1]→[1,0.1]` - fast initial motion
12
12
  * that decays over lifetime.
13
- * - **Late acceleration:** `[0,0.2]→[1,1]` particles ease in.
13
+ * - **Late acceleration:** `[0,0.2]→[1,1]` - particles ease in.
14
14
  * - **Pulse:** sine-shaped curve for breathing-style motion.
15
15
  *
16
16
  * Note: the curve **replaces** velocity each frame relative to the previous
@@ -24,7 +24,8 @@ export declare class VelocityOverLifetime extends UpdateModule {
24
24
  curve: Curve;
25
25
  private _prevSample;
26
26
  constructor(curve: Curve);
27
- apply(system: ParticleSystem, _dt: number): void;
27
+ apply(particles: ParticleBatch, _dt: number): void;
28
28
  wgsl(): WgslContribution;
29
29
  uploadTextures(device: GPUDevice, textures: ReadonlyMap<string, GPUTexture>): void;
30
30
  }
31
+ //# sourceMappingURL=VelocityOverLifetime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VelocityOverLifetime.d.ts","sourceRoot":"","sources":["../../../src/modules/VelocityOverLifetime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAI3D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,oBAAqB,SAAQ,YAAY;IAC7C,KAAK,EAAE,KAAK,CAAC;IAEpB,OAAO,CAAC,WAAW,CAAuB;gBAEvB,KAAK,EAAE,KAAK;IAKf,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IA2BlD,IAAI,IAAI,gBAAgB;IAsBxB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;CAoBnG"}