@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.
- package/README.md +21 -43
- package/dist/esm/ParticleStorage.d.ts +188 -0
- package/dist/esm/ParticleStorage.d.ts.map +1 -0
- package/dist/esm/ParticleStorage.js +208 -0
- package/dist/esm/ParticleStorage.js.map +1 -0
- package/dist/esm/ParticleSystem.d.ts +189 -60
- package/dist/esm/ParticleSystem.d.ts.map +1 -0
- package/dist/esm/ParticleSystem.js +660 -560
- package/dist/esm/ParticleSystem.js.map +1 -1
- package/dist/esm/distributions/BoxArea.d.ts +1 -0
- package/dist/esm/distributions/BoxArea.d.ts.map +1 -0
- package/dist/esm/distributions/BoxArea.js +38 -44
- package/dist/esm/distributions/BoxArea.js.map +1 -1
- package/dist/esm/distributions/CircleArea.d.ts +1 -0
- package/dist/esm/distributions/CircleArea.d.ts.map +1 -0
- package/dist/esm/distributions/CircleArea.js +30 -28
- package/dist/esm/distributions/CircleArea.js.map +1 -1
- package/dist/esm/distributions/ColorGradient.d.ts +3 -2
- package/dist/esm/distributions/ColorGradient.d.ts.map +1 -0
- package/dist/esm/distributions/ColorGradient.js +70 -75
- package/dist/esm/distributions/ColorGradient.js.map +1 -1
- package/dist/esm/distributions/ConeDirection.d.ts +2 -1
- package/dist/esm/distributions/ConeDirection.d.ts.map +1 -0
- package/dist/esm/distributions/ConeDirection.js +41 -39
- package/dist/esm/distributions/ConeDirection.js.map +1 -1
- package/dist/esm/distributions/Constant.d.ts +1 -0
- package/dist/esm/distributions/Constant.d.ts.map +1 -0
- package/dist/esm/distributions/Constant.js +32 -32
- package/dist/esm/distributions/Constant.js.map +1 -1
- package/dist/esm/distributions/Curve.d.ts +2 -1
- package/dist/esm/distributions/Curve.d.ts.map +1 -0
- package/dist/esm/distributions/Curve.js +46 -55
- package/dist/esm/distributions/Curve.js.map +1 -1
- package/dist/esm/distributions/Distribution.d.ts +4 -3
- package/dist/esm/distributions/Distribution.d.ts.map +1 -0
- package/dist/esm/distributions/LineSegment.d.ts +1 -0
- package/dist/esm/distributions/LineSegment.d.ts.map +1 -0
- package/dist/esm/distributions/LineSegment.js +25 -23
- package/dist/esm/distributions/LineSegment.js.map +1 -1
- package/dist/esm/distributions/Range.d.ts +1 -0
- package/dist/esm/distributions/Range.d.ts.map +1 -0
- package/dist/esm/distributions/Range.js +18 -16
- package/dist/esm/distributions/Range.js.map +1 -1
- package/dist/esm/distributions/VectorRange.d.ts +1 -0
- package/dist/esm/distributions/VectorRange.d.ts.map +1 -0
- package/dist/esm/distributions/VectorRange.js +29 -27
- package/dist/esm/distributions/VectorRange.js.map +1 -1
- package/dist/esm/distributions/index.d.ts +1 -0
- package/dist/esm/distributions/index.d.ts.map +1 -0
- package/dist/esm/gpu/ParticleGpuState.d.ts +143 -18
- package/dist/esm/gpu/ParticleGpuState.d.ts.map +1 -0
- package/dist/esm/gpu/ParticleGpuState.js +655 -497
- package/dist/esm/gpu/ParticleGpuState.js.map +1 -1
- package/dist/esm/gpu/shaders/particle-simulate.wgsl.js +6 -0
- package/dist/esm/gpu/shaders/particle-simulate.wgsl.js.map +1 -0
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +39 -32
- package/dist/esm/modules/AlphaFadeOverLifetime.d.ts +4 -3
- package/dist/esm/modules/AlphaFadeOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/AlphaFadeOverLifetime.js +68 -55
- package/dist/esm/modules/AlphaFadeOverLifetime.js.map +1 -1
- package/dist/esm/modules/ApplyForce.d.ts +4 -3
- package/dist/esm/modules/ApplyForce.d.ts.map +1 -0
- package/dist/esm/modules/ApplyForce.js +49 -43
- package/dist/esm/modules/ApplyForce.js.map +1 -1
- package/dist/esm/modules/AttractToPoint.d.ts +4 -3
- package/dist/esm/modules/AttractToPoint.d.ts.map +1 -0
- package/dist/esm/modules/AttractToPoint.js +73 -62
- package/dist/esm/modules/AttractToPoint.js.map +1 -1
- package/dist/esm/modules/BurstSpawn.d.ts +6 -18
- package/dist/esm/modules/BurstSpawn.d.ts.map +1 -0
- package/dist/esm/modules/BurstSpawn.js +56 -91
- package/dist/esm/modules/BurstSpawn.js.map +1 -1
- package/dist/esm/modules/ColorOverLifetime.d.ts +4 -3
- package/dist/esm/modules/ColorOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/ColorOverLifetime.js +65 -53
- package/dist/esm/modules/ColorOverLifetime.js.map +1 -1
- package/dist/esm/modules/ColorOverSpeed.d.ts +5 -4
- package/dist/esm/modules/ColorOverSpeed.d.ts.map +1 -0
- package/dist/esm/modules/ColorOverSpeed.js +90 -75
- package/dist/esm/modules/ColorOverSpeed.js.map +1 -1
- package/dist/esm/modules/DeathModule.d.ts +26 -10
- package/dist/esm/modules/DeathModule.d.ts.map +1 -0
- package/dist/esm/modules/DeathModule.js +38 -22
- package/dist/esm/modules/DeathModule.js.map +1 -1
- package/dist/esm/modules/Drag.d.ts +3 -2
- package/dist/esm/modules/Drag.d.ts.map +1 -0
- package/dist/esm/modules/Drag.js +43 -37
- package/dist/esm/modules/Drag.js.map +1 -1
- package/dist/esm/modules/OrbitalForce.d.ts +4 -3
- package/dist/esm/modules/OrbitalForce.d.ts.map +1 -0
- package/dist/esm/modules/OrbitalForce.js +70 -58
- package/dist/esm/modules/OrbitalForce.js.map +1 -1
- package/dist/esm/modules/RateSpawn.d.ts +7 -22
- package/dist/esm/modules/RateSpawn.d.ts.map +1 -0
- package/dist/esm/modules/RateSpawn.js +39 -71
- package/dist/esm/modules/RateSpawn.js.map +1 -1
- package/dist/esm/modules/RepelFromPoint.d.ts +4 -3
- package/dist/esm/modules/RepelFromPoint.d.ts.map +1 -0
- package/dist/esm/modules/RepelFromPoint.js +72 -62
- package/dist/esm/modules/RepelFromPoint.js.map +1 -1
- package/dist/esm/modules/RotateOverLifetime.d.ts +3 -2
- package/dist/esm/modules/RotateOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/RotateOverLifetime.js +40 -36
- package/dist/esm/modules/RotateOverLifetime.js.map +1 -1
- package/dist/esm/modules/ScaleOverLifetime.d.ts +5 -4
- package/dist/esm/modules/ScaleOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/ScaleOverLifetime.js +60 -50
- package/dist/esm/modules/ScaleOverLifetime.js.map +1 -1
- package/dist/esm/modules/SpawnModule.d.ts +15 -12
- package/dist/esm/modules/SpawnModule.d.ts.map +1 -0
- package/dist/esm/modules/SpawnModule.js +32 -28
- package/dist/esm/modules/SpawnModule.js.map +1 -1
- package/dist/esm/modules/SpawnOnDeath.d.ts +13 -9
- package/dist/esm/modules/SpawnOnDeath.d.ts.map +1 -0
- package/dist/esm/modules/SpawnOnDeath.js +41 -43
- package/dist/esm/modules/SpawnOnDeath.js.map +1 -1
- package/dist/esm/modules/Turbulence.d.ts +4 -3
- package/dist/esm/modules/Turbulence.d.ts.map +1 -0
- package/dist/esm/modules/Turbulence.js +101 -89
- package/dist/esm/modules/Turbulence.js.map +1 -1
- package/dist/esm/modules/UpdateModule.d.ts +21 -13
- package/dist/esm/modules/UpdateModule.d.ts.map +1 -0
- package/dist/esm/modules/UpdateModule.js +65 -63
- package/dist/esm/modules/UpdateModule.js.map +1 -1
- package/dist/esm/modules/VelocityOverLifetime.d.ts +7 -6
- package/dist/esm/modules/VelocityOverLifetime.d.ts.map +1 -0
- package/dist/esm/modules/VelocityOverLifetime.js +75 -73
- package/dist/esm/modules/VelocityOverLifetime.js.map +1 -1
- package/dist/esm/modules/WgslContribution.d.ts +16 -15
- package/dist/esm/modules/WgslContribution.d.ts.map +1 -0
- package/dist/esm/modules/WgslContribution.js +37 -29
- package/dist/esm/modules/WgslContribution.js.map +1 -1
- package/dist/esm/modules/index.d.ts +3 -1
- package/dist/esm/modules/index.d.ts.map +1 -0
- package/dist/esm/modules/spawnFields.d.ts +32 -0
- package/dist/esm/modules/spawnFields.d.ts.map +1 -0
- package/dist/esm/modules/spawnFields.js +31 -0
- package/dist/esm/modules/spawnFields.js.map +1 -0
- package/dist/esm/particlesBuildInfo.d.ts +1 -0
- package/dist/esm/particlesBuildInfo.d.ts.map +1 -0
- package/dist/esm/particlesBuildInfo.js +6 -4
- package/dist/esm/particlesBuildInfo.js.map +1 -1
- package/dist/esm/particlesExtension.d.ts +13 -5
- package/dist/esm/particlesExtension.d.ts.map +1 -0
- package/dist/esm/particlesExtension.js +38 -42
- package/dist/esm/particlesExtension.js.map +1 -1
- package/dist/esm/public.d.ts +11 -0
- package/dist/esm/public.d.ts.map +1 -0
- package/dist/esm/renderModes/MeshParticles.d.ts +128 -0
- package/dist/esm/renderModes/MeshParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/MeshParticles.js +263 -0
- package/dist/esm/renderModes/MeshParticles.js.map +1 -0
- package/dist/esm/renderModes/ParticleBufferLayout.d.ts +59 -0
- package/dist/esm/renderModes/ParticleBufferLayout.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleBufferLayout.js +99 -0
- package/dist/esm/renderModes/ParticleBufferLayout.js.map +1 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.d.ts +58 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.js +173 -0
- package/dist/esm/renderModes/ParticleInstanceWriter.js.map +1 -0
- package/dist/esm/renderModes/ParticleMaterial.d.ts +18 -0
- package/dist/esm/renderModes/ParticleMaterial.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleMaterial.js +24 -0
- package/dist/esm/renderModes/ParticleMaterial.js.map +1 -0
- package/dist/esm/renderModes/ParticleRenderMode.d.ts +114 -0
- package/dist/esm/renderModes/ParticleRenderMode.d.ts.map +1 -0
- package/dist/esm/renderModes/ParticleRenderMode.js +99 -0
- package/dist/esm/renderModes/ParticleRenderMode.js.map +1 -0
- package/dist/esm/renderModes/QuadParticles.d.ts +55 -0
- package/dist/esm/renderModes/QuadParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/QuadParticles.js +95 -0
- package/dist/esm/renderModes/QuadParticles.js.map +1 -0
- package/dist/esm/renderModes/RibbonParticles.d.ts +96 -0
- package/dist/esm/renderModes/RibbonParticles.d.ts.map +1 -0
- package/dist/esm/renderModes/RibbonParticles.js +202 -0
- package/dist/esm/renderModes/RibbonParticles.js.map +1 -0
- package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/mesh-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/mesh.vert.js +6 -0
- package/dist/esm/renderModes/shaders/mesh.vert.js.map +1 -0
- package/dist/esm/renderModes/shaders/quad-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/quad-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon-particles.wgsl.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon.frag.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon.frag.js.map +1 -0
- package/dist/esm/renderModes/shaders/ribbon.vert.js +6 -0
- package/dist/esm/renderModes/shaders/ribbon.vert.js.map +1 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts +48 -20
- package/dist/esm/renderers/WebGl2ParticleRenderer.d.ts.map +1 -0
- package/dist/esm/renderers/WebGl2ParticleRenderer.js +317 -321
- package/dist/esm/renderers/WebGl2ParticleRenderer.js.map +1 -1
- package/dist/esm/renderers/WebGpuParticleRenderer.d.ts +99 -18
- package/dist/esm/renderers/WebGpuParticleRenderer.d.ts.map +1 -0
- package/dist/esm/renderers/WebGpuParticleRenderer.js +542 -551
- package/dist/esm/renderers/WebGpuParticleRenderer.js.map +1 -1
- package/dist/esm/renderers/shaders/particle.frag.js +6 -0
- package/dist/esm/renderers/shaders/particle.frag.js.map +1 -0
- package/dist/esm/renderers/shaders/particle.vert.js +6 -0
- package/dist/esm/renderers/shaders/particle.vert.js.map +1 -0
- package/package.json +8 -15
- package/dist/esm/index.js.map +0 -1
- package/dist/esm/register.d.ts +0 -1
- package/dist/esm/register.js +0 -43
- package/dist/esm/register.js.map +0 -1
- package/dist/esm/renderers/glsl/particle.frag.js +0 -4
- package/dist/esm/renderers/glsl/particle.frag.js.map +0 -1
- package/dist/esm/renderers/glsl/particle.vert.js +0 -4
- package/dist/esm/renderers/glsl/particle.vert.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { ColorGradient } from
|
|
2
|
-
import type {
|
|
1
|
+
import type { ColorGradient } from '#distributions/ColorGradient';
|
|
2
|
+
import type { ParticleBatch } from '#ParticleStorage';
|
|
3
3
|
import { UpdateModule } from './UpdateModule';
|
|
4
4
|
import type { WgslContribution } from './WgslContribution';
|
|
5
5
|
/**
|
|
@@ -11,7 +11,7 @@ import type { WgslContribution } from './WgslContribution';
|
|
|
11
11
|
* speed-gated highlights.
|
|
12
12
|
*
|
|
13
13
|
* GPU-eligible: gradient uploaded as a 256-tap 1D RGBA8 texture, sampled
|
|
14
|
-
* with linear filtering. Replaces the full color word
|
|
14
|
+
* with linear filtering. Replaces the full color word - pair with a
|
|
15
15
|
* separate {@link AlphaFadeOverLifetime} after this module if you want to
|
|
16
16
|
* keep alpha controlled by lifetime.
|
|
17
17
|
*/
|
|
@@ -20,8 +20,9 @@ export declare class ColorOverSpeed extends UpdateModule {
|
|
|
20
20
|
minSpeed: number;
|
|
21
21
|
maxSpeed: number;
|
|
22
22
|
constructor(gradient: ColorGradient, minSpeed: number, maxSpeed: number);
|
|
23
|
-
apply(
|
|
23
|
+
apply(particles: ParticleBatch, _dt: number): void;
|
|
24
24
|
wgsl(): WgslContribution;
|
|
25
25
|
writeUniforms(view: DataView, offset: number): void;
|
|
26
26
|
uploadTextures(device: GPUDevice, textures: ReadonlyMap<string, GPUTexture>): void;
|
|
27
27
|
}
|
|
28
|
+
//# sourceMappingURL=ColorOverSpeed.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ColorOverSpeed.d.ts","sourceRoot":"","sources":["../../../src/modules/ColorOverSpeed.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAC;AAClE,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;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAe,SAAQ,YAAY;IACvC,QAAQ,EAAE,aAAa,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;gBAEL,QAAQ,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM;IAO9D,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAkBlD,IAAI,IAAI,gBAAgB;IAqBxB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;IAOnD,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI;CA8BnG"}
|
|
@@ -1,53 +1,61 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { UpdateModule } from "./UpdateModule.js";
|
|
2
|
+
import { Color } from "@codexo/exojs";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
//#region src/modules/ColorOverSpeed.ts
|
|
5
5
|
const lookupSize = 256;
|
|
6
6
|
/**
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
7
|
+
* Per-frame, per-particle color sampler driven by velocity magnitude rather
|
|
8
|
+
* than lifetime ratio. Each live particle's tint is set to the gradient
|
|
9
|
+
* evaluated at `clamp((|velocity| - minSpeed) / (maxSpeed - minSpeed), 0, 1)`.
|
|
10
|
+
*
|
|
11
|
+
* Use cases: heat-mapping (slow=blue, fast=red), velocity-tinted trails,
|
|
12
|
+
* speed-gated highlights.
|
|
13
|
+
*
|
|
14
|
+
* GPU-eligible: gradient uploaded as a 256-tap 1D RGBA8 texture, sampled
|
|
15
|
+
* with linear filtering. Replaces the full color word - pair with a
|
|
16
|
+
* separate {@link AlphaFadeOverLifetime} after this module if you want to
|
|
17
|
+
* keep alpha controlled by lifetime.
|
|
18
|
+
*/
|
|
19
|
+
var ColorOverSpeed = class extends UpdateModule {
|
|
20
|
+
gradient;
|
|
21
|
+
minSpeed;
|
|
22
|
+
maxSpeed;
|
|
23
|
+
constructor(gradient, minSpeed, maxSpeed) {
|
|
24
|
+
super();
|
|
25
|
+
this.gradient = gradient;
|
|
26
|
+
this.minSpeed = minSpeed;
|
|
27
|
+
this.maxSpeed = maxSpeed;
|
|
28
|
+
}
|
|
29
|
+
apply(particles, _dt) {
|
|
30
|
+
const { x: velX, y: velY } = particles.velocity;
|
|
31
|
+
const color = particles.color;
|
|
32
|
+
const liveCount = particles.count;
|
|
33
|
+
const gradient = this.gradient;
|
|
34
|
+
const min = this.minSpeed;
|
|
35
|
+
const span = Math.max(1e-5, this.maxSpeed - this.minSpeed);
|
|
36
|
+
for (let i = 0; i < liveCount; i++) {
|
|
37
|
+
const vx = velX[i] ?? 0;
|
|
38
|
+
const vy = velY[i] ?? 0;
|
|
39
|
+
const speed = Math.sqrt(vx * vx + vy * vy);
|
|
40
|
+
const t = Math.max(0, Math.min(1, (speed - min) / span));
|
|
41
|
+
color[i] = gradient.evaluateRgba(t);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
wgsl() {
|
|
45
|
+
return {
|
|
46
|
+
key: "ColorOverSpeed",
|
|
47
|
+
uniforms: [{
|
|
48
|
+
name: "minSpeed",
|
|
49
|
+
type: "f32"
|
|
50
|
+
}, {
|
|
51
|
+
name: "invSpan",
|
|
52
|
+
type: "f32"
|
|
53
|
+
}],
|
|
54
|
+
textures: [{
|
|
55
|
+
name: "gradient",
|
|
56
|
+
format: "rgba8unorm"
|
|
57
|
+
}],
|
|
58
|
+
body: `
|
|
51
59
|
let speedMag = length(velocities[idx]);
|
|
52
60
|
let speedT = clamp((speedMag - modules.u_ColorOverSpeed.minSpeed) * modules.u_ColorOverSpeed.invSpan, 0.0, 1.0);
|
|
53
61
|
let speedSample = textureSampleLevel(u_ColorOverSpeed_gradient, u_ColorOverSpeed_gradient_sampler, speedT, 0.0);
|
|
@@ -56,33 +64,40 @@ class ColorOverSpeed extends UpdateModule {
|
|
|
56
64
|
let speedB = u32(speedSample.b * 255.0) & 255u;
|
|
57
65
|
let speedA = u32(speedSample.a * 255.0) & 255u;
|
|
58
66
|
color[idx] = (speedA << 24u) | (speedB << 16u) | (speedG << 8u) | speedR;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
}
|
|
67
|
+
`
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
writeUniforms(view, offset) {
|
|
71
|
+
const span = Math.max(1e-5, this.maxSpeed - this.minSpeed);
|
|
72
|
+
view.setFloat32(offset + 0, this.minSpeed, true);
|
|
73
|
+
view.setFloat32(offset + 4, 1 / span, true);
|
|
74
|
+
}
|
|
75
|
+
uploadTextures(device, textures) {
|
|
76
|
+
const texture = textures.get("gradient");
|
|
77
|
+
if (texture === void 0) return;
|
|
78
|
+
const data = new Uint8Array(lookupSize * 4);
|
|
79
|
+
const scratch = new Color();
|
|
80
|
+
for (let i = 0; i < lookupSize; i++) {
|
|
81
|
+
const t = i / 255;
|
|
82
|
+
this.gradient.evaluate(t, scratch);
|
|
83
|
+
const o = i * 4;
|
|
84
|
+
data[o + 0] = scratch.r & 255;
|
|
85
|
+
data[o + 1] = scratch.g & 255;
|
|
86
|
+
data[o + 2] = scratch.b & 255;
|
|
87
|
+
data[o + 3] = (scratch.a * 255 | 0) & 255;
|
|
88
|
+
}
|
|
89
|
+
device.queue.writeTexture({ texture }, data.buffer, {
|
|
90
|
+
offset: 0,
|
|
91
|
+
bytesPerRow: lookupSize * 4,
|
|
92
|
+
rowsPerImage: 1
|
|
93
|
+
}, {
|
|
94
|
+
width: lookupSize,
|
|
95
|
+
height: 1,
|
|
96
|
+
depthOrArrayLayers: 1
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
86
100
|
|
|
101
|
+
//#endregion
|
|
87
102
|
export { ColorOverSpeed };
|
|
88
|
-
//# sourceMappingURL=ColorOverSpeed.js.map
|
|
103
|
+
//# sourceMappingURL=ColorOverSpeed.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ColorOverSpeed.js","sources":["
|
|
1
|
+
{"version":3,"file":"ColorOverSpeed.js","names":[],"sources":["../../../src/modules/ColorOverSpeed.ts"],"sourcesContent":["/// <reference types=\"@webgpu/types\" />\n\nimport { Color } from '@codexo/exojs';\n\nimport type { ColorGradient } from '#distributions/ColorGradient';\nimport type { ParticleBatch } from '#ParticleStorage';\n\nimport { UpdateModule } from './UpdateModule';\nimport type { WgslContribution } from './WgslContribution';\n\nconst lookupSize = 256;\n\n/**\n * Per-frame, per-particle color sampler driven by velocity magnitude rather\n * than lifetime ratio. Each live particle's tint is set to the gradient\n * evaluated at `clamp((|velocity| - minSpeed) / (maxSpeed - minSpeed), 0, 1)`.\n *\n * Use cases: heat-mapping (slow=blue, fast=red), velocity-tinted trails,\n * speed-gated highlights.\n *\n * GPU-eligible: gradient uploaded as a 256-tap 1D RGBA8 texture, sampled\n * with linear filtering. Replaces the full color word - pair with a\n * separate {@link AlphaFadeOverLifetime} after this module if you want to\n * keep alpha controlled by lifetime.\n */\nexport class ColorOverSpeed extends UpdateModule {\n public gradient: ColorGradient;\n public minSpeed: number;\n public maxSpeed: number;\n\n public constructor(gradient: ColorGradient, minSpeed: number, maxSpeed: number) {\n super();\n this.gradient = gradient;\n this.minSpeed = minSpeed;\n this.maxSpeed = maxSpeed;\n }\n\n public override apply(particles: ParticleBatch, _dt: number): void {\n const { x: velX, y: velY } = particles.velocity;\n const color = particles.color;\n const liveCount = particles.count;\n const gradient = this.gradient;\n const min = this.minSpeed;\n const span = Math.max(1e-5, this.maxSpeed - this.minSpeed);\n\n for (let i = 0; i < liveCount; i++) {\n const vx = velX[i] ?? 0;\n const vy = velY[i] ?? 0;\n const speed = Math.sqrt(vx * vx + vy * vy);\n const t = Math.max(0, Math.min(1, (speed - min) / span));\n\n color[i] = gradient.evaluateRgba(t);\n }\n }\n\n public override wgsl(): WgslContribution {\n return {\n key: 'ColorOverSpeed',\n uniforms: [\n { name: 'minSpeed', type: 'f32' },\n { name: 'invSpan', type: 'f32' },\n ],\n textures: [{ name: 'gradient', format: 'rgba8unorm' }],\n body: `\n let speedMag = length(velocities[idx]);\n let speedT = clamp((speedMag - modules.u_ColorOverSpeed.minSpeed) * modules.u_ColorOverSpeed.invSpan, 0.0, 1.0);\n let speedSample = textureSampleLevel(u_ColorOverSpeed_gradient, u_ColorOverSpeed_gradient_sampler, speedT, 0.0);\n let speedR = u32(speedSample.r * 255.0) & 255u;\n let speedG = u32(speedSample.g * 255.0) & 255u;\n let speedB = u32(speedSample.b * 255.0) & 255u;\n let speedA = u32(speedSample.a * 255.0) & 255u;\n color[idx] = (speedA << 24u) | (speedB << 16u) | (speedG << 8u) | speedR;\n `,\n };\n }\n\n public override writeUniforms(view: DataView, offset: number): void {\n const span = Math.max(1e-5, this.maxSpeed - this.minSpeed);\n\n view.setFloat32(offset + 0, this.minSpeed, true);\n view.setFloat32(offset + 4, 1 / span, true);\n }\n\n public override uploadTextures(device: GPUDevice, textures: ReadonlyMap<string, GPUTexture>): void {\n const texture = textures.get('gradient');\n\n if (texture === undefined) {\n return;\n }\n\n const data = new Uint8Array(lookupSize * 4);\n const scratch = new Color();\n\n for (let i = 0; i < lookupSize; i++) {\n const t = i / (lookupSize - 1);\n\n this.gradient.evaluate(t, scratch);\n\n const o = i * 4;\n\n data[o + 0] = scratch.r & 255;\n data[o + 1] = scratch.g & 255;\n data[o + 2] = scratch.b & 255;\n data[o + 3] = ((scratch.a * 255) | 0) & 255;\n }\n\n device.queue.writeTexture(\n { texture },\n data.buffer,\n { offset: 0, bytesPerRow: lookupSize * 4, rowsPerImage: 1 },\n { width: lookupSize, height: 1, depthOrArrayLayers: 1 },\n );\n }\n}\n"],"mappings":";;;;AAUA,MAAM,aAAa;;;;;;;;;;;;;;AAenB,IAAa,iBAAb,cAAoC,aAAa;CAC/C,AAAO;CACP,AAAO;CACP,AAAO;CAEP,AAAO,YAAY,UAAyB,UAAkB,UAAkB;EAC9E,MAAM;EACN,KAAK,WAAW;EAChB,KAAK,WAAW;EAChB,KAAK,WAAW;CAClB;CAEA,AAAgB,MAAM,WAA0B,KAAmB;EACjE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,UAAU;EACvC,MAAM,QAAQ,UAAU;EACxB,MAAM,YAAY,UAAU;EAC5B,MAAM,WAAW,KAAK;EACtB,MAAM,MAAM,KAAK;EACjB,MAAM,OAAO,KAAK,IAAI,MAAM,KAAK,WAAW,KAAK,QAAQ;EAEzD,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;GAClC,MAAM,KAAK,KAAK,MAAM;GACtB,MAAM,KAAK,KAAK,MAAM;GACtB,MAAM,QAAQ,KAAK,KAAK,KAAK,KAAK,KAAK,EAAE;GACzC,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,IAAI,QAAQ,OAAO,IAAI,CAAC;GAEvD,MAAM,KAAK,SAAS,aAAa,CAAC;EACpC;CACF;CAEA,AAAgB,OAAyB;EACvC,OAAO;GACL,KAAK;GACL,UAAU,CACR;IAAE,MAAM;IAAY,MAAM;GAAM,GAChC;IAAE,MAAM;IAAW,MAAM;GAAM,CACjC;GACA,UAAU,CAAC;IAAE,MAAM;IAAY,QAAQ;GAAa,CAAC;GACrD,MAAM;;;;;;;;;;EAUR;CACF;CAEA,AAAgB,cAAc,MAAgB,QAAsB;EAClE,MAAM,OAAO,KAAK,IAAI,MAAM,KAAK,WAAW,KAAK,QAAQ;EAEzD,KAAK,WAAW,SAAS,GAAG,KAAK,UAAU,IAAI;EAC/C,KAAK,WAAW,SAAS,GAAG,IAAI,MAAM,IAAI;CAC5C;CAEA,AAAgB,eAAe,QAAmB,UAAiD;EACjG,MAAM,UAAU,SAAS,IAAI,UAAU;EAEvC,IAAI,YAAY,QACd;EAGF,MAAM,OAAO,IAAI,WAAW,aAAa,CAAC;EAC1C,MAAM,UAAU,IAAI,MAAM;EAE1B,KAAK,IAAI,IAAI,GAAG,IAAI,YAAY,KAAK;GACnC,MAAM,IAAI,IAAK;GAEf,KAAK,SAAS,SAAS,GAAG,OAAO;GAEjC,MAAM,IAAI,IAAI;GAEd,KAAK,IAAI,KAAK,QAAQ,IAAI;GAC1B,KAAK,IAAI,KAAK,QAAQ,IAAI;GAC1B,KAAK,IAAI,KAAK,QAAQ,IAAI;GAC1B,KAAK,IAAI,MAAO,QAAQ,IAAI,MAAO,KAAK;EAC1C;EAEA,OAAO,MAAM,aACX,EAAE,QAAQ,GACV,KAAK,QACL;GAAE,QAAQ;GAAG,aAAa,aAAa;GAAG,cAAc;EAAE,GAC1D;GAAE,OAAO;GAAY,QAAQ;GAAG,oBAAoB;EAAE,CACxD;CACF;AACF"}
|
|
@@ -1,24 +1,40 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParticleDeathContext } from '#ParticleStorage';
|
|
2
|
+
import type { ParticleSystem } from '#ParticleSystem';
|
|
2
3
|
/**
|
|
3
|
-
* Per-particle hook invoked exactly once
|
|
4
|
-
*
|
|
5
|
-
* is still readable at `system.posX[slot]` etc.
|
|
4
|
+
* Per-particle hook invoked exactly once after a particle expires, carrying its
|
|
5
|
+
* state at the moment it died.
|
|
6
6
|
*
|
|
7
7
|
* Use for sub-emitters (spawn child particles where this one died), event
|
|
8
|
-
* dispatch (trigger an audio cue, score event), or trail termination.
|
|
8
|
+
* dispatch (trigger an audio cue, a score event), or trail termination.
|
|
9
|
+
*
|
|
10
|
+
* The callback receives a snapshot rather than a slot, and for a reason: on the
|
|
11
|
+
* GPU path the simulation runs on the device and a death is reported once its
|
|
12
|
+
* state has been read back, by which time the slot may already hold a different
|
|
13
|
+
* particle. The snapshot is therefore the same on both backends, while a slot
|
|
14
|
+
* would not be.
|
|
15
|
+
*
|
|
16
|
+
* Delivery is exactly once per expired particle, but not necessarily in the
|
|
17
|
+
* frame the particle expired: a GPU-simulated death arrives as soon as its
|
|
18
|
+
* readback lands, typically the next frame. Deaths from one readback are
|
|
19
|
+
* delivered in slot order, and readbacks in the order they were submitted.
|
|
20
|
+
*
|
|
21
|
+
* Exactly-once holds while the deaths waiting on a readback fit the system's
|
|
22
|
+
* capacity. Beyond that the excess is dropped rather than stalling the frame
|
|
23
|
+
* loop, and a development build reports it once per system. Reaching that point
|
|
24
|
+
* takes a device that falls many frames behind while slots are recycled and die
|
|
25
|
+
* again, so a system sized for its emission rate does not see it.
|
|
9
26
|
*
|
|
10
27
|
* Implementation pattern:
|
|
11
28
|
*
|
|
12
29
|
* ```ts
|
|
13
|
-
* onDeath(system,
|
|
14
|
-
*
|
|
15
|
-
* const y = system.posY[slot];
|
|
16
|
-
* this._childSystem.spawnBurstAt(x, y, 8);
|
|
30
|
+
* onDeath(system, death) {
|
|
31
|
+
* this._childSystem.burstAt(death.x, death.y, 8);
|
|
17
32
|
* }
|
|
18
33
|
* ```
|
|
19
34
|
*/
|
|
20
35
|
export declare abstract class DeathModule {
|
|
21
|
-
abstract onDeath(system: ParticleSystem,
|
|
36
|
+
abstract onDeath(system: ParticleSystem, death: ParticleDeathContext): void;
|
|
22
37
|
/** Optional cleanup hook called from `ParticleSystem.destroy`. */
|
|
23
38
|
destroy(): void;
|
|
24
39
|
}
|
|
40
|
+
//# sourceMappingURL=DeathModule.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DeathModule.d.ts","sourceRoot":"","sources":["../../../src/modules/DeathModule.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAC7D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,8BAAsB,WAAW;aACf,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,KAAK,EAAE,oBAAoB,GAAG,IAAI;IAClF,kEAAkE;IAC3D,OAAO,IAAI,IAAI;CACvB"}
|
|
@@ -1,25 +1,41 @@
|
|
|
1
|
+
//#region src/modules/DeathModule.ts
|
|
1
2
|
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
3
|
+
* Per-particle hook invoked exactly once after a particle expires, carrying its
|
|
4
|
+
* state at the moment it died.
|
|
5
|
+
*
|
|
6
|
+
* Use for sub-emitters (spawn child particles where this one died), event
|
|
7
|
+
* dispatch (trigger an audio cue, a score event), or trail termination.
|
|
8
|
+
*
|
|
9
|
+
* The callback receives a snapshot rather than a slot, and for a reason: on the
|
|
10
|
+
* GPU path the simulation runs on the device and a death is reported once its
|
|
11
|
+
* state has been read back, by which time the slot may already hold a different
|
|
12
|
+
* particle. The snapshot is therefore the same on both backends, while a slot
|
|
13
|
+
* would not be.
|
|
14
|
+
*
|
|
15
|
+
* Delivery is exactly once per expired particle, but not necessarily in the
|
|
16
|
+
* frame the particle expired: a GPU-simulated death arrives as soon as its
|
|
17
|
+
* readback lands, typically the next frame. Deaths from one readback are
|
|
18
|
+
* delivered in slot order, and readbacks in the order they were submitted.
|
|
19
|
+
*
|
|
20
|
+
* Exactly-once holds while the deaths waiting on a readback fit the system's
|
|
21
|
+
* capacity. Beyond that the excess is dropped rather than stalling the frame
|
|
22
|
+
* loop, and a development build reports it once per system. Reaching that point
|
|
23
|
+
* takes a device that falls many frames behind while slots are recycled and die
|
|
24
|
+
* again, so a system sized for its emission rate does not see it.
|
|
25
|
+
*
|
|
26
|
+
* Implementation pattern:
|
|
27
|
+
*
|
|
28
|
+
* ```ts
|
|
29
|
+
* onDeath(system, death) {
|
|
30
|
+
* this._childSystem.burstAt(death.x, death.y, 8);
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
var DeathModule = class {
|
|
35
|
+
/** Optional cleanup hook called from `ParticleSystem.destroy`. */
|
|
36
|
+
destroy() {}
|
|
37
|
+
};
|
|
23
38
|
|
|
39
|
+
//#endregion
|
|
24
40
|
export { DeathModule };
|
|
25
|
-
//# sourceMappingURL=DeathModule.js.map
|
|
41
|
+
//# sourceMappingURL=DeathModule.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"DeathModule.js","sources":["
|
|
1
|
+
{"version":3,"file":"DeathModule.js","names":[],"sources":["../../../src/modules/DeathModule.ts"],"sourcesContent":["import type { ParticleDeathContext } from '#ParticleStorage';\nimport type { ParticleSystem } from '#ParticleSystem';\n\n/**\n * Per-particle hook invoked exactly once after a particle expires, carrying its\n * state at the moment it died.\n *\n * Use for sub-emitters (spawn child particles where this one died), event\n * dispatch (trigger an audio cue, a score event), or trail termination.\n *\n * The callback receives a snapshot rather than a slot, and for a reason: on the\n * GPU path the simulation runs on the device and a death is reported once its\n * state has been read back, by which time the slot may already hold a different\n * particle. The snapshot is therefore the same on both backends, while a slot\n * would not be.\n *\n * Delivery is exactly once per expired particle, but not necessarily in the\n * frame the particle expired: a GPU-simulated death arrives as soon as its\n * readback lands, typically the next frame. Deaths from one readback are\n * delivered in slot order, and readbacks in the order they were submitted.\n *\n * Exactly-once holds while the deaths waiting on a readback fit the system's\n * capacity. Beyond that the excess is dropped rather than stalling the frame\n * loop, and a development build reports it once per system. Reaching that point\n * takes a device that falls many frames behind while slots are recycled and die\n * again, so a system sized for its emission rate does not see it.\n *\n * Implementation pattern:\n *\n * ```ts\n * onDeath(system, death) {\n * this._childSystem.burstAt(death.x, death.y, 8);\n * }\n * ```\n */\nexport abstract class DeathModule {\n public abstract onDeath(system: ParticleSystem, death: ParticleDeathContext): void;\n /** Optional cleanup hook called from `ParticleSystem.destroy`. */\n public destroy(): void {}\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,IAAsB,cAAtB,MAAkC;;CAGhC,AAAO,UAAgB,CAAC;AAC1B"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParticleBatch } from '#ParticleStorage';
|
|
2
2
|
import { UpdateModule } from './UpdateModule';
|
|
3
3
|
import type { WgslContribution } from './WgslContribution';
|
|
4
4
|
/**
|
|
@@ -14,7 +14,8 @@ import type { WgslContribution } from './WgslContribution';
|
|
|
14
14
|
export declare class Drag extends UpdateModule {
|
|
15
15
|
drag: number;
|
|
16
16
|
constructor(drag: number);
|
|
17
|
-
apply(
|
|
17
|
+
apply(particles: ParticleBatch, dt: number): void;
|
|
18
18
|
wgsl(): WgslContribution;
|
|
19
19
|
writeUniforms(view: DataView, offset: number): void;
|
|
20
20
|
}
|
|
21
|
+
//# sourceMappingURL=Drag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Drag.d.ts","sourceRoot":"","sources":["../../../src/modules/Drag.ts"],"names":[],"mappings":"AAAA,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;AAE3D;;;;;;;;;GASG;AACH,qBAAa,IAAK,SAAQ,YAAY;IAC7B,IAAI,EAAE,MAAM,CAAC;gBAED,IAAI,EAAE,MAAM;IAKf,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAWjD,IAAI,IAAI,gBAAgB;IAWxB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAGpE"}
|
package/dist/esm/modules/Drag.js
CHANGED
|
@@ -1,43 +1,49 @@
|
|
|
1
|
-
import { UpdateModule } from
|
|
1
|
+
import { UpdateModule } from "./UpdateModule.js";
|
|
2
2
|
|
|
3
|
+
//#region src/modules/Drag.ts
|
|
3
4
|
/**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
5
|
+
* Exponential velocity damping. Each frame multiplies every live particle's
|
|
6
|
+
* velocity by `(1 - drag * dt)`, simulating linear air resistance.
|
|
7
|
+
*
|
|
8
|
+
* `drag = 0` is no damping; `drag = 1` halves velocity in ~1 second; higher
|
|
9
|
+
* values slow particles faster. Negative values accelerate (don't do that
|
|
10
|
+
* unless you mean it).
|
|
11
|
+
*
|
|
12
|
+
* GPU-eligible.
|
|
13
|
+
*/
|
|
14
|
+
var Drag = class extends UpdateModule {
|
|
15
|
+
drag;
|
|
16
|
+
constructor(drag) {
|
|
17
|
+
super();
|
|
18
|
+
this.drag = drag;
|
|
19
|
+
}
|
|
20
|
+
apply(particles, dt) {
|
|
21
|
+
const { x: velX, y: velY } = particles.velocity;
|
|
22
|
+
const liveCount = particles.count;
|
|
23
|
+
const factor = 1 - this.drag * dt;
|
|
24
|
+
for (let i = 0; i < liveCount; i++) {
|
|
25
|
+
velX[i] = (velX[i] ?? 0) * factor;
|
|
26
|
+
velY[i] = (velY[i] ?? 0) * factor;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
wgsl() {
|
|
30
|
+
return {
|
|
31
|
+
key: "Drag",
|
|
32
|
+
uniforms: [{
|
|
33
|
+
name: "drag",
|
|
34
|
+
type: "f32"
|
|
35
|
+
}],
|
|
36
|
+
body: `
|
|
32
37
|
let dragFactor = 1.0 - modules.u_Drag.drag * dt;
|
|
33
38
|
velocities[idx] = velocities[idx] * dragFactor;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
}
|
|
39
|
+
`
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
writeUniforms(view, offset) {
|
|
43
|
+
view.setFloat32(offset + 0, this.drag, true);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
41
46
|
|
|
47
|
+
//#endregion
|
|
42
48
|
export { Drag };
|
|
43
|
-
//# sourceMappingURL=Drag.js.map
|
|
49
|
+
//# sourceMappingURL=Drag.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Drag.js","sources":["
|
|
1
|
+
{"version":3,"file":"Drag.js","names":[],"sources":["../../../src/modules/Drag.ts"],"sourcesContent":["import type { ParticleBatch } from '#ParticleStorage';\n\nimport { UpdateModule } from './UpdateModule';\nimport type { WgslContribution } from './WgslContribution';\n\n/**\n * Exponential velocity damping. Each frame multiplies every live particle's\n * velocity by `(1 - drag * dt)`, simulating linear air resistance.\n *\n * `drag = 0` is no damping; `drag = 1` halves velocity in ~1 second; higher\n * values slow particles faster. Negative values accelerate (don't do that\n * unless you mean it).\n *\n * GPU-eligible.\n */\nexport class Drag extends UpdateModule {\n public drag: number;\n\n public constructor(drag: number) {\n super();\n this.drag = drag;\n }\n\n public override apply(particles: ParticleBatch, dt: number): void {\n const { x: velX, y: velY } = particles.velocity;\n const liveCount = particles.count;\n const factor = 1 - this.drag * dt;\n\n for (let i = 0; i < liveCount; i++) {\n velX[i] = (velX[i] ?? 0) * factor;\n velY[i] = (velY[i] ?? 0) * factor;\n }\n }\n\n public override wgsl(): WgslContribution {\n return {\n key: 'Drag',\n uniforms: [{ name: 'drag', type: 'f32' }],\n body: `\n let dragFactor = 1.0 - modules.u_Drag.drag * dt;\n velocities[idx] = velocities[idx] * dragFactor;\n `,\n };\n }\n\n public override writeUniforms(view: DataView, offset: number): void {\n view.setFloat32(offset + 0, this.drag, true);\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAeA,IAAa,OAAb,cAA0B,aAAa;CACrC,AAAO;CAEP,AAAO,YAAY,MAAc;EAC/B,MAAM;EACN,KAAK,OAAO;CACd;CAEA,AAAgB,MAAM,WAA0B,IAAkB;EAChE,MAAM,EAAE,GAAG,MAAM,GAAG,SAAS,UAAU;EACvC,MAAM,YAAY,UAAU;EAC5B,MAAM,SAAS,IAAI,KAAK,OAAO;EAE/B,KAAK,IAAI,IAAI,GAAG,IAAI,WAAW,KAAK;GAClC,KAAK,MAAM,KAAK,MAAM,KAAK;GAC3B,KAAK,MAAM,KAAK,MAAM,KAAK;EAC7B;CACF;CAEA,AAAgB,OAAyB;EACvC,OAAO;GACL,KAAK;GACL,UAAU,CAAC;IAAE,MAAM;IAAQ,MAAM;GAAM,CAAC;GACxC,MAAM;;;;EAIR;CACF;CAEA,AAAgB,cAAc,MAAgB,QAAsB;EAClE,KAAK,WAAW,SAAS,GAAG,KAAK,MAAM,IAAI;CAC7C;AACF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ParticleBatch } from '#ParticleStorage';
|
|
2
2
|
import { UpdateModule } from './UpdateModule';
|
|
3
3
|
import type { WgslContribution } from './WgslContribution';
|
|
4
4
|
/**
|
|
5
|
-
* Applies a tangential acceleration around a center point
|
|
5
|
+
* Applies a tangential acceleration around a center point - perpendicular
|
|
6
6
|
* to the radial vector `(particle − center)`. Combined with an attract /
|
|
7
7
|
* repel module that controls the radial distance, this produces orbital,
|
|
8
8
|
* spiral, or vortex motion.
|
|
@@ -22,7 +22,8 @@ export declare class OrbitalForce extends UpdateModule {
|
|
|
22
22
|
y: number;
|
|
23
23
|
angularSpeed: number;
|
|
24
24
|
constructor(x: number, y: number, angularSpeed: number);
|
|
25
|
-
apply(
|
|
25
|
+
apply(particles: ParticleBatch, dt: number): void;
|
|
26
26
|
wgsl(): WgslContribution;
|
|
27
27
|
writeUniforms(view: DataView, offset: number): void;
|
|
28
28
|
}
|
|
29
|
+
//# sourceMappingURL=OrbitalForce.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OrbitalForce.d.ts","sourceRoot":"","sources":["../../../src/modules/OrbitalForce.ts"],"names":[],"mappings":"AAAA,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;AAE3D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IACrC,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,YAAY,EAAE,MAAM,CAAC;gBAET,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;IAO7C,KAAK,CAAC,SAAS,EAAE,aAAa,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAgBjD,IAAI,IAAI,gBAAgB;IAgBxB,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI;CAMpE"}
|