@codexo/exojs-particles 0.15.2 → 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 +7 -14
- 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
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ This package requires `@codexo/exojs` as a peer dependency. Both must be the sam
|
|
|
13
13
|
## Core compatibility
|
|
14
14
|
|
|
15
15
|
| `@codexo/exojs-particles` | `@codexo/exojs` |
|
|
16
|
-
|
|
17
|
-
| 0.12.x
|
|
16
|
+
| ------------------------- | --------------- |
|
|
17
|
+
| 0.12.x | 0.12.x |
|
|
18
18
|
|
|
19
19
|
## Usage — side-effect-free root entry
|
|
20
20
|
|
|
@@ -25,7 +25,7 @@ import { Application } from '@codexo/exojs';
|
|
|
25
25
|
import { ParticleSystem, particlesExtension } from '@codexo/exojs-particles';
|
|
26
26
|
|
|
27
27
|
const app = new Application({
|
|
28
|
-
|
|
28
|
+
extensions: [particlesExtension],
|
|
29
29
|
});
|
|
30
30
|
```
|
|
31
31
|
|
|
@@ -47,7 +47,7 @@ const app = new Application({ extensions: [particlesExtension] });
|
|
|
47
47
|
import { createParticlesExtension } from '@codexo/exojs-particles';
|
|
48
48
|
|
|
49
49
|
const app = new Application({
|
|
50
|
-
|
|
50
|
+
extensions: [createParticlesExtension({ batchSize: 8192 })],
|
|
51
51
|
});
|
|
52
52
|
```
|
|
53
53
|
|
|
@@ -57,57 +57,35 @@ Pass any combination of descriptors:
|
|
|
57
57
|
|
|
58
58
|
```ts
|
|
59
59
|
const app = new Application({
|
|
60
|
-
|
|
60
|
+
extensions: [particlesExtension],
|
|
61
61
|
});
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
##
|
|
65
|
-
|
|
66
|
-
Importing `/register` registers the default `particlesExtension` descriptor in the global `ExtensionRegistry`. Subsequently created Applications that use global defaults will automatically receive the Particles extension. This is the only side-effectful entry in this package.
|
|
64
|
+
## Minimal working example
|
|
67
65
|
|
|
68
66
|
```ts
|
|
69
|
-
|
|
70
|
-
import '@codexo/exojs-particles
|
|
67
|
+
import { Application, type RenderingContext, Scene } from '@codexo/exojs';
|
|
68
|
+
import { Constant, ParticleSystem, particlesExtension, RateSpawn } from '@codexo/exojs-particles';
|
|
71
69
|
|
|
72
|
-
|
|
73
|
-
import { ParticleSystem, particlesExtension } from '@codexo/exojs-particles/register';
|
|
74
|
-
```
|
|
70
|
+
const app = new Application({ extensions: [particlesExtension], canvas: { mount: document.body } });
|
|
75
71
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
## Minimal working example
|
|
72
|
+
class DemoScene extends Scene {
|
|
73
|
+
private system!: ParticleSystem;
|
|
79
74
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
import {
|
|
83
|
-
ParticleSystem,
|
|
84
|
-
particlesExtension,
|
|
85
|
-
RateSpawn,
|
|
86
|
-
Constant,
|
|
87
|
-
} from '@codexo/exojs-particles';
|
|
75
|
+
override async load(): Promise<void> {
|
|
76
|
+
const texture = await this.loader.load('/particle.png');
|
|
88
77
|
|
|
89
|
-
|
|
90
|
-
|
|
78
|
+
this.system = new ParticleSystem(texture, { capacity: 1024 });
|
|
79
|
+
this.systems.add(this.system);
|
|
80
|
+
this.system.addSpawnModule(new RateSpawn({ rate: new Constant(120), lifetime: new Constant(2) }));
|
|
81
|
+
}
|
|
91
82
|
|
|
92
|
-
|
|
93
|
-
system
|
|
94
|
-
|
|
95
|
-
override async load(loader) {
|
|
96
|
-
await loader.load(Texture, { particle: '/particle.png' });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
override create(loader) {
|
|
100
|
-
this.system = new ParticleSystem(loader.get(Texture, 'particle'), {
|
|
101
|
-
capacity: 1024,
|
|
102
|
-
});
|
|
103
|
-
this.system.addSpawnModule(
|
|
104
|
-
new RateSpawn({ rate: new Constant(120), lifetime: new Constant(2) }),
|
|
105
|
-
);
|
|
106
|
-
this.addChild(this.system);
|
|
107
|
-
}
|
|
83
|
+
override draw(context: RenderingContext): void {
|
|
84
|
+
context.render(this.system);
|
|
85
|
+
}
|
|
108
86
|
}
|
|
109
87
|
|
|
110
|
-
app.
|
|
88
|
+
app.start(DemoScene);
|
|
111
89
|
```
|
|
112
90
|
|
|
113
91
|
## WebGL2 and WebGPU support
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A two-component particle channel, one array per component.
|
|
3
|
+
*
|
|
4
|
+
* The arrays are the simulation's own storage rather than a copy: writing
|
|
5
|
+
* `position.x[i]` moves particle `i`. Their length is the system's capacity, so
|
|
6
|
+
* only indices below {@link ParticleBatch.count} carry meaning.
|
|
7
|
+
*/
|
|
8
|
+
export interface ParticleVectorChannel {
|
|
9
|
+
readonly x: Float32Array;
|
|
10
|
+
readonly y: Float32Array;
|
|
11
|
+
}
|
|
12
|
+
/** The rotation channel: current angle and the speed integrated into it each frame. */
|
|
13
|
+
export interface ParticleRotationChannel {
|
|
14
|
+
readonly angle: Float32Array;
|
|
15
|
+
readonly speed: Float32Array;
|
|
16
|
+
}
|
|
17
|
+
/** The timing channel: seconds since spawn, and total seconds before expiry. */
|
|
18
|
+
export interface ParticleTimingChannel {
|
|
19
|
+
readonly elapsed: Float32Array;
|
|
20
|
+
readonly lifetime: Float32Array;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* The live particles of one system, addressed by semantic channel.
|
|
24
|
+
*
|
|
25
|
+
* This is the bulk surface an {@link UpdateModule} and a
|
|
26
|
+
* {@link ParticleRenderMode} operate on. Channels are named for what they mean;
|
|
27
|
+
* how they are packed - one array per component today, something else tomorrow -
|
|
28
|
+
* is the simulation's business and may change. A loop reads the channel it needs
|
|
29
|
+
* once and then indexes plain typed arrays, so naming costs nothing per particle.
|
|
30
|
+
*
|
|
31
|
+
* Indices `[0, count)` are the range worth visiting. A batch can contain dead
|
|
32
|
+
* slots, so a loop that must not touch them checks {@link isAlive}; a purely
|
|
33
|
+
* arithmetic pass over dead slots is harmless and can skip the check.
|
|
34
|
+
*/
|
|
35
|
+
export interface ParticleBatch {
|
|
36
|
+
/** Upper bound of the range that can hold live particles. */
|
|
37
|
+
readonly count: number;
|
|
38
|
+
/** Fixed number of slots this system can ever hold. */
|
|
39
|
+
readonly capacity: number;
|
|
40
|
+
readonly position: ParticleVectorChannel;
|
|
41
|
+
readonly velocity: ParticleVectorChannel;
|
|
42
|
+
readonly scale: ParticleVectorChannel;
|
|
43
|
+
readonly rotation: ParticleRotationChannel;
|
|
44
|
+
readonly timing: ParticleTimingChannel;
|
|
45
|
+
/** Packed `0xAABBGGRR` tint, one entry per slot. */
|
|
46
|
+
readonly color: Uint32Array;
|
|
47
|
+
/** Atlas frame selector, one entry per slot. Anything that is not a declared frame index shows frame 0. */
|
|
48
|
+
readonly frame: Uint16Array;
|
|
49
|
+
/** Whether the slot at `index` currently holds a live particle. */
|
|
50
|
+
isAlive(index: number): boolean;
|
|
51
|
+
}
|
|
52
|
+
/** Writable two-component channel of a single particle. */
|
|
53
|
+
export interface ParticleVectorWriter {
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
set(x: number, y: number): void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* A single freshly emitted particle, addressed by name.
|
|
60
|
+
*
|
|
61
|
+
* Every field starts at its default - zero position, velocity, rotation and
|
|
62
|
+
* rotation speed, unit scale, opaque white, frame 0, lifetime 1 - so a spawner
|
|
63
|
+
* writes only what it varies.
|
|
64
|
+
*
|
|
65
|
+
* The writer is a cursor onto the emitting system, not a particle object: it is
|
|
66
|
+
* reused by the next {@link ParticleEmitter.emit} call and must not be kept.
|
|
67
|
+
* Read what you need before emitting again.
|
|
68
|
+
*/
|
|
69
|
+
export interface ParticleWriter {
|
|
70
|
+
readonly position: ParticleVectorWriter;
|
|
71
|
+
readonly velocity: ParticleVectorWriter;
|
|
72
|
+
readonly scale: ParticleVectorWriter;
|
|
73
|
+
rotation: number;
|
|
74
|
+
rotationSpeed: number;
|
|
75
|
+
/** Packed `0xAABBGGRR` tint. */
|
|
76
|
+
color: number;
|
|
77
|
+
/** Total seconds before this particle expires. */
|
|
78
|
+
lifetime: number;
|
|
79
|
+
/** Atlas frame index. Ignored by a system that declares no frames. */
|
|
80
|
+
frame: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The emitting face of a particle system: the only supported way to bring
|
|
84
|
+
* particles into existence.
|
|
85
|
+
*
|
|
86
|
+
* Emission is the one per-particle write that is true on every backend, because
|
|
87
|
+
* spawn values always originate on the CPU and are uploaded from there.
|
|
88
|
+
*/
|
|
89
|
+
export interface ParticleEmitter {
|
|
90
|
+
/** Emits one particle at its default values, or returns `null` when the system is at capacity. */
|
|
91
|
+
emit(): ParticleWriter | null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The state of one particle at the moment it expired.
|
|
95
|
+
*
|
|
96
|
+
* A snapshot, not a view: it is copied out of the simulation when the particle
|
|
97
|
+
* dies and stays valid for the whole callback, whichever backend ran the
|
|
98
|
+
* simulation and whether or not the slot has already been reused. It carries no
|
|
99
|
+
* slot index for that reason - there is nothing to read back afterwards.
|
|
100
|
+
*/
|
|
101
|
+
export interface ParticleDeathContext {
|
|
102
|
+
readonly x: number;
|
|
103
|
+
readonly y: number;
|
|
104
|
+
readonly velocityX: number;
|
|
105
|
+
readonly velocityY: number;
|
|
106
|
+
readonly rotation: number;
|
|
107
|
+
readonly scaleX: number;
|
|
108
|
+
readonly scaleY: number;
|
|
109
|
+
/** Packed `0xAABBGGRR` tint at death. */
|
|
110
|
+
readonly color: number;
|
|
111
|
+
/** Seconds the particle lived. */
|
|
112
|
+
readonly elapsed: number;
|
|
113
|
+
/** Lifetime the particle was spawned with. */
|
|
114
|
+
readonly lifetime: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The simulation's own storage: one typed array per channel, sized to capacity
|
|
118
|
+
* once and never reallocated.
|
|
119
|
+
*
|
|
120
|
+
* Implements {@link ParticleBatch}, which is the face handed to modules and
|
|
121
|
+
* render modes. Nothing outside the simulation gets the object itself.
|
|
122
|
+
* @internal
|
|
123
|
+
*/
|
|
124
|
+
export declare class ParticleStorage implements ParticleBatch {
|
|
125
|
+
readonly capacity: number;
|
|
126
|
+
readonly posX: Float32Array;
|
|
127
|
+
readonly posY: Float32Array;
|
|
128
|
+
readonly velX: Float32Array;
|
|
129
|
+
readonly velY: Float32Array;
|
|
130
|
+
readonly scaleX: Float32Array;
|
|
131
|
+
readonly scaleY: Float32Array;
|
|
132
|
+
readonly rotations: Float32Array;
|
|
133
|
+
readonly rotationSpeeds: Float32Array;
|
|
134
|
+
readonly color: Uint32Array;
|
|
135
|
+
readonly elapsed: Float32Array;
|
|
136
|
+
readonly lifetime: Float32Array;
|
|
137
|
+
readonly frame: Uint16Array;
|
|
138
|
+
readonly alive: Uint8Array;
|
|
139
|
+
readonly position: ParticleVectorChannel;
|
|
140
|
+
readonly velocity: ParticleVectorChannel;
|
|
141
|
+
readonly scale: ParticleVectorChannel;
|
|
142
|
+
readonly rotation: ParticleRotationChannel;
|
|
143
|
+
readonly timing: ParticleTimingChannel;
|
|
144
|
+
/** Upper bound of the range that can hold live particles. Owned by the system. */
|
|
145
|
+
count: number;
|
|
146
|
+
constructor(capacity: number);
|
|
147
|
+
isAlive(index: number): boolean;
|
|
148
|
+
/** Resets one slot to the spawn defaults every writer starts from. */
|
|
149
|
+
reset(slot: number): void;
|
|
150
|
+
/** Copies every channel of `from` onto `to`, for the CPU compaction pass. */
|
|
151
|
+
copySlot(from: number, to: number): void;
|
|
152
|
+
/** Snapshots one slot for a death callback. */
|
|
153
|
+
snapshot(slot: number): ParticleDeathContext;
|
|
154
|
+
}
|
|
155
|
+
/** Cursor implementation of {@link ParticleWriter}, rebound to a slot by each emit. @internal */
|
|
156
|
+
export declare class ParticleSlotWriter implements ParticleWriter {
|
|
157
|
+
readonly position: ParticleVectorWriter;
|
|
158
|
+
readonly velocity: ParticleVectorWriter;
|
|
159
|
+
readonly scale: ParticleVectorWriter;
|
|
160
|
+
private readonly _storage;
|
|
161
|
+
private _slot;
|
|
162
|
+
constructor(storage: ParticleStorage);
|
|
163
|
+
/** The slot this cursor currently writes to. Used by the emitter to track what a spawner produced. @internal */
|
|
164
|
+
get slot(): number;
|
|
165
|
+
bind(slot: number): this;
|
|
166
|
+
/**
|
|
167
|
+
* Marks this writer as belonging to a finished emission. Development builds
|
|
168
|
+
* hand out one writer per emission and retire the previous one here, so
|
|
169
|
+
* writing through a stale writer fails loudly instead of silently filling
|
|
170
|
+
* whatever particle was emitted since.
|
|
171
|
+
* @internal
|
|
172
|
+
*/
|
|
173
|
+
retire(): void;
|
|
174
|
+
private _retired;
|
|
175
|
+
/** Resolves the bound slot, refusing a writer a later emission has taken over. */
|
|
176
|
+
private _currentSlot;
|
|
177
|
+
get rotation(): number;
|
|
178
|
+
set rotation(value: number);
|
|
179
|
+
get rotationSpeed(): number;
|
|
180
|
+
set rotationSpeed(value: number);
|
|
181
|
+
get color(): number;
|
|
182
|
+
set color(value: number);
|
|
183
|
+
get lifetime(): number;
|
|
184
|
+
set lifetime(value: number);
|
|
185
|
+
get frame(): number;
|
|
186
|
+
set frame(value: number);
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=ParticleStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticleStorage.d.ts","sourceRoot":"","sources":["../../src/ParticleStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
//#region src/ParticleStorage.ts
|
|
2
|
+
/**
|
|
3
|
+
* The simulation's own storage: one typed array per channel, sized to capacity
|
|
4
|
+
* once and never reallocated.
|
|
5
|
+
*
|
|
6
|
+
* Implements {@link ParticleBatch}, which is the face handed to modules and
|
|
7
|
+
* render modes. Nothing outside the simulation gets the object itself.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
var ParticleStorage = class {
|
|
11
|
+
capacity;
|
|
12
|
+
posX;
|
|
13
|
+
posY;
|
|
14
|
+
velX;
|
|
15
|
+
velY;
|
|
16
|
+
scaleX;
|
|
17
|
+
scaleY;
|
|
18
|
+
rotations;
|
|
19
|
+
rotationSpeeds;
|
|
20
|
+
color;
|
|
21
|
+
elapsed;
|
|
22
|
+
lifetime;
|
|
23
|
+
frame;
|
|
24
|
+
alive;
|
|
25
|
+
position;
|
|
26
|
+
velocity;
|
|
27
|
+
scale;
|
|
28
|
+
rotation;
|
|
29
|
+
timing;
|
|
30
|
+
/** Upper bound of the range that can hold live particles. Owned by the system. */
|
|
31
|
+
count = 0;
|
|
32
|
+
constructor(capacity) {
|
|
33
|
+
this.capacity = capacity;
|
|
34
|
+
this.posX = new Float32Array(capacity);
|
|
35
|
+
this.posY = new Float32Array(capacity);
|
|
36
|
+
this.velX = new Float32Array(capacity);
|
|
37
|
+
this.velY = new Float32Array(capacity);
|
|
38
|
+
this.scaleX = new Float32Array(capacity);
|
|
39
|
+
this.scaleY = new Float32Array(capacity);
|
|
40
|
+
this.rotations = new Float32Array(capacity);
|
|
41
|
+
this.rotationSpeeds = new Float32Array(capacity);
|
|
42
|
+
this.color = new Uint32Array(capacity);
|
|
43
|
+
this.elapsed = new Float32Array(capacity);
|
|
44
|
+
this.lifetime = new Float32Array(capacity);
|
|
45
|
+
this.frame = new Uint16Array(capacity);
|
|
46
|
+
this.alive = new Uint8Array(capacity);
|
|
47
|
+
this.position = {
|
|
48
|
+
x: this.posX,
|
|
49
|
+
y: this.posY
|
|
50
|
+
};
|
|
51
|
+
this.velocity = {
|
|
52
|
+
x: this.velX,
|
|
53
|
+
y: this.velY
|
|
54
|
+
};
|
|
55
|
+
this.scale = {
|
|
56
|
+
x: this.scaleX,
|
|
57
|
+
y: this.scaleY
|
|
58
|
+
};
|
|
59
|
+
this.rotation = {
|
|
60
|
+
angle: this.rotations,
|
|
61
|
+
speed: this.rotationSpeeds
|
|
62
|
+
};
|
|
63
|
+
this.timing = {
|
|
64
|
+
elapsed: this.elapsed,
|
|
65
|
+
lifetime: this.lifetime
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
isAlive(index) {
|
|
69
|
+
return this.alive[index] === 1;
|
|
70
|
+
}
|
|
71
|
+
/** Resets one slot to the spawn defaults every writer starts from. */
|
|
72
|
+
reset(slot) {
|
|
73
|
+
this.posX[slot] = 0;
|
|
74
|
+
this.posY[slot] = 0;
|
|
75
|
+
this.velX[slot] = 0;
|
|
76
|
+
this.velY[slot] = 0;
|
|
77
|
+
this.scaleX[slot] = 1;
|
|
78
|
+
this.scaleY[slot] = 1;
|
|
79
|
+
this.rotations[slot] = 0;
|
|
80
|
+
this.rotationSpeeds[slot] = 0;
|
|
81
|
+
this.color[slot] = 4294967295;
|
|
82
|
+
this.elapsed[slot] = 0;
|
|
83
|
+
this.lifetime[slot] = 1;
|
|
84
|
+
this.frame[slot] = 0;
|
|
85
|
+
}
|
|
86
|
+
/** Copies every channel of `from` onto `to`, for the CPU compaction pass. */
|
|
87
|
+
copySlot(from, to) {
|
|
88
|
+
this.posX[to] = this.posX[from];
|
|
89
|
+
this.posY[to] = this.posY[from];
|
|
90
|
+
this.velX[to] = this.velX[from];
|
|
91
|
+
this.velY[to] = this.velY[from];
|
|
92
|
+
this.scaleX[to] = this.scaleX[from];
|
|
93
|
+
this.scaleY[to] = this.scaleY[from];
|
|
94
|
+
this.rotations[to] = this.rotations[from];
|
|
95
|
+
this.rotationSpeeds[to] = this.rotationSpeeds[from];
|
|
96
|
+
this.color[to] = this.color[from];
|
|
97
|
+
this.elapsed[to] = this.elapsed[from];
|
|
98
|
+
this.lifetime[to] = this.lifetime[from];
|
|
99
|
+
this.frame[to] = this.frame[from];
|
|
100
|
+
}
|
|
101
|
+
/** Snapshots one slot for a death callback. */
|
|
102
|
+
snapshot(slot) {
|
|
103
|
+
return {
|
|
104
|
+
x: this.posX[slot],
|
|
105
|
+
y: this.posY[slot],
|
|
106
|
+
velocityX: this.velX[slot],
|
|
107
|
+
velocityY: this.velY[slot],
|
|
108
|
+
rotation: this.rotations[slot],
|
|
109
|
+
scaleX: this.scaleX[slot],
|
|
110
|
+
scaleY: this.scaleY[slot],
|
|
111
|
+
color: this.color[slot],
|
|
112
|
+
elapsed: this.elapsed[slot],
|
|
113
|
+
lifetime: this.lifetime[slot]
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
/** Cursor implementation of {@link ParticleWriter}, rebound to a slot by each emit. @internal */
|
|
118
|
+
var ParticleSlotWriter = class {
|
|
119
|
+
position;
|
|
120
|
+
velocity;
|
|
121
|
+
scale;
|
|
122
|
+
_storage;
|
|
123
|
+
_slot = 0;
|
|
124
|
+
constructor(storage) {
|
|
125
|
+
this._storage = storage;
|
|
126
|
+
const slotOf = this._currentSlot.bind(this);
|
|
127
|
+
this.position = createVectorWriter(storage.posX, storage.posY, slotOf);
|
|
128
|
+
this.velocity = createVectorWriter(storage.velX, storage.velY, slotOf);
|
|
129
|
+
this.scale = createVectorWriter(storage.scaleX, storage.scaleY, slotOf);
|
|
130
|
+
}
|
|
131
|
+
/** The slot this cursor currently writes to. Used by the emitter to track what a spawner produced. @internal */
|
|
132
|
+
get slot() {
|
|
133
|
+
return this._slot;
|
|
134
|
+
}
|
|
135
|
+
bind(slot) {
|
|
136
|
+
this._slot = slot;
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Marks this writer as belonging to a finished emission. Development builds
|
|
141
|
+
* hand out one writer per emission and retire the previous one here, so
|
|
142
|
+
* writing through a stale writer fails loudly instead of silently filling
|
|
143
|
+
* whatever particle was emitted since.
|
|
144
|
+
* @internal
|
|
145
|
+
*/
|
|
146
|
+
retire() {
|
|
147
|
+
this._retired = true;
|
|
148
|
+
}
|
|
149
|
+
_retired = false;
|
|
150
|
+
/** Resolves the bound slot, refusing a writer a later emission has taken over. */
|
|
151
|
+
_currentSlot() {
|
|
152
|
+
if (this._retired) throw new Error("ParticleWriter: this writer belongs to an earlier emit(). Fill each emitted particle before emitting the next one.");
|
|
153
|
+
return this._slot;
|
|
154
|
+
}
|
|
155
|
+
get rotation() {
|
|
156
|
+
return this._storage.rotations[this._currentSlot()];
|
|
157
|
+
}
|
|
158
|
+
set rotation(value) {
|
|
159
|
+
this._storage.rotations[this._currentSlot()] = value;
|
|
160
|
+
}
|
|
161
|
+
get rotationSpeed() {
|
|
162
|
+
return this._storage.rotationSpeeds[this._currentSlot()];
|
|
163
|
+
}
|
|
164
|
+
set rotationSpeed(value) {
|
|
165
|
+
this._storage.rotationSpeeds[this._currentSlot()] = value;
|
|
166
|
+
}
|
|
167
|
+
get color() {
|
|
168
|
+
return this._storage.color[this._currentSlot()];
|
|
169
|
+
}
|
|
170
|
+
set color(value) {
|
|
171
|
+
this._storage.color[this._currentSlot()] = value;
|
|
172
|
+
}
|
|
173
|
+
get lifetime() {
|
|
174
|
+
return this._storage.lifetime[this._currentSlot()];
|
|
175
|
+
}
|
|
176
|
+
set lifetime(value) {
|
|
177
|
+
this._storage.lifetime[this._currentSlot()] = value;
|
|
178
|
+
}
|
|
179
|
+
get frame() {
|
|
180
|
+
return this._storage.frame[this._currentSlot()];
|
|
181
|
+
}
|
|
182
|
+
set frame(value) {
|
|
183
|
+
this._storage.frame[this._currentSlot()] = value | 0;
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
const createVectorWriter = (xs, ys, slotOf) => ({
|
|
187
|
+
get x() {
|
|
188
|
+
return xs[slotOf()];
|
|
189
|
+
},
|
|
190
|
+
set x(value) {
|
|
191
|
+
xs[slotOf()] = value;
|
|
192
|
+
},
|
|
193
|
+
get y() {
|
|
194
|
+
return ys[slotOf()];
|
|
195
|
+
},
|
|
196
|
+
set y(value) {
|
|
197
|
+
ys[slotOf()] = value;
|
|
198
|
+
},
|
|
199
|
+
set(x, y) {
|
|
200
|
+
const slot = slotOf();
|
|
201
|
+
xs[slot] = x;
|
|
202
|
+
ys[slot] = y;
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
//#endregion
|
|
207
|
+
export { ParticleSlotWriter, ParticleStorage };
|
|
208
|
+
//# sourceMappingURL=ParticleStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ParticleStorage.js","names":[],"sources":["../../src/ParticleStorage.ts"],"sourcesContent":["/**\n * A two-component particle channel, one array per component.\n *\n * The arrays are the simulation's own storage rather than a copy: writing\n * `position.x[i]` moves particle `i`. Their length is the system's capacity, so\n * only indices below {@link ParticleBatch.count} carry meaning.\n */\nexport interface ParticleVectorChannel {\n readonly x: Float32Array;\n readonly y: Float32Array;\n}\n\n/** The rotation channel: current angle and the speed integrated into it each frame. */\nexport interface ParticleRotationChannel {\n readonly angle: Float32Array;\n readonly speed: Float32Array;\n}\n\n/** The timing channel: seconds since spawn, and total seconds before expiry. */\nexport interface ParticleTimingChannel {\n readonly elapsed: Float32Array;\n readonly lifetime: Float32Array;\n}\n\n/**\n * The live particles of one system, addressed by semantic channel.\n *\n * This is the bulk surface an {@link UpdateModule} and a\n * {@link ParticleRenderMode} operate on. Channels are named for what they mean;\n * how they are packed - one array per component today, something else tomorrow -\n * is the simulation's business and may change. A loop reads the channel it needs\n * once and then indexes plain typed arrays, so naming costs nothing per particle.\n *\n * Indices `[0, count)` are the range worth visiting. A batch can contain dead\n * slots, so a loop that must not touch them checks {@link isAlive}; a purely\n * arithmetic pass over dead slots is harmless and can skip the check.\n */\nexport interface ParticleBatch {\n /** Upper bound of the range that can hold live particles. */\n readonly count: number;\n /** Fixed number of slots this system can ever hold. */\n readonly capacity: number;\n readonly position: ParticleVectorChannel;\n readonly velocity: ParticleVectorChannel;\n readonly scale: ParticleVectorChannel;\n readonly rotation: ParticleRotationChannel;\n readonly timing: ParticleTimingChannel;\n /** Packed `0xAABBGGRR` tint, one entry per slot. */\n readonly color: Uint32Array;\n /** Atlas frame selector, one entry per slot. Anything that is not a declared frame index shows frame 0. */\n readonly frame: Uint16Array;\n /** Whether the slot at `index` currently holds a live particle. */\n isAlive(index: number): boolean;\n}\n\n/** Writable two-component channel of a single particle. */\nexport interface ParticleVectorWriter {\n x: number;\n y: number;\n set(x: number, y: number): void;\n}\n\n/**\n * A single freshly emitted particle, addressed by name.\n *\n * Every field starts at its default - zero position, velocity, rotation and\n * rotation speed, unit scale, opaque white, frame 0, lifetime 1 - so a spawner\n * writes only what it varies.\n *\n * The writer is a cursor onto the emitting system, not a particle object: it is\n * reused by the next {@link ParticleEmitter.emit} call and must not be kept.\n * Read what you need before emitting again.\n */\nexport interface ParticleWriter {\n readonly position: ParticleVectorWriter;\n readonly velocity: ParticleVectorWriter;\n readonly scale: ParticleVectorWriter;\n rotation: number;\n rotationSpeed: number;\n /** Packed `0xAABBGGRR` tint. */\n color: number;\n /** Total seconds before this particle expires. */\n lifetime: number;\n /** Atlas frame index. Ignored by a system that declares no frames. */\n frame: number;\n}\n\n/**\n * The emitting face of a particle system: the only supported way to bring\n * particles into existence.\n *\n * Emission is the one per-particle write that is true on every backend, because\n * spawn values always originate on the CPU and are uploaded from there.\n */\nexport interface ParticleEmitter {\n /** Emits one particle at its default values, or returns `null` when the system is at capacity. */\n emit(): ParticleWriter | null;\n}\n\n/**\n * The state of one particle at the moment it expired.\n *\n * A snapshot, not a view: it is copied out of the simulation when the particle\n * dies and stays valid for the whole callback, whichever backend ran the\n * simulation and whether or not the slot has already been reused. It carries no\n * slot index for that reason - there is nothing to read back afterwards.\n */\nexport interface ParticleDeathContext {\n readonly x: number;\n readonly y: number;\n readonly velocityX: number;\n readonly velocityY: number;\n readonly rotation: number;\n readonly scaleX: number;\n readonly scaleY: number;\n /** Packed `0xAABBGGRR` tint at death. */\n readonly color: number;\n /** Seconds the particle lived. */\n readonly elapsed: number;\n /** Lifetime the particle was spawned with. */\n readonly lifetime: number;\n}\n\n/**\n * The simulation's own storage: one typed array per channel, sized to capacity\n * once and never reallocated.\n *\n * Implements {@link ParticleBatch}, which is the face handed to modules and\n * render modes. Nothing outside the simulation gets the object itself.\n * @internal\n */\nexport class ParticleStorage implements ParticleBatch {\n public readonly capacity: number;\n\n public readonly posX: Float32Array;\n public readonly posY: Float32Array;\n public readonly velX: Float32Array;\n public readonly velY: Float32Array;\n public readonly scaleX: Float32Array;\n public readonly scaleY: Float32Array;\n public readonly rotations: Float32Array;\n public readonly rotationSpeeds: Float32Array;\n public readonly color: Uint32Array;\n public readonly elapsed: Float32Array;\n public readonly lifetime: Float32Array;\n public readonly frame: Uint16Array;\n public readonly alive: Uint8Array;\n\n public readonly position: ParticleVectorChannel;\n public readonly velocity: ParticleVectorChannel;\n public readonly scale: ParticleVectorChannel;\n public readonly rotation: ParticleRotationChannel;\n public readonly timing: ParticleTimingChannel;\n\n /** Upper bound of the range that can hold live particles. Owned by the system. */\n public count = 0;\n\n public constructor(capacity: number) {\n this.capacity = capacity;\n this.posX = new Float32Array(capacity);\n this.posY = new Float32Array(capacity);\n this.velX = new Float32Array(capacity);\n this.velY = new Float32Array(capacity);\n this.scaleX = new Float32Array(capacity);\n this.scaleY = new Float32Array(capacity);\n this.rotations = new Float32Array(capacity);\n this.rotationSpeeds = new Float32Array(capacity);\n this.color = new Uint32Array(capacity);\n this.elapsed = new Float32Array(capacity);\n this.lifetime = new Float32Array(capacity);\n this.frame = new Uint16Array(capacity);\n this.alive = new Uint8Array(capacity);\n\n this.position = { x: this.posX, y: this.posY };\n this.velocity = { x: this.velX, y: this.velY };\n this.scale = { x: this.scaleX, y: this.scaleY };\n this.rotation = { angle: this.rotations, speed: this.rotationSpeeds };\n this.timing = { elapsed: this.elapsed, lifetime: this.lifetime };\n }\n\n public isAlive(index: number): boolean {\n return this.alive[index] === 1;\n }\n\n /** Resets one slot to the spawn defaults every writer starts from. */\n public reset(slot: number): void {\n this.posX[slot] = 0;\n this.posY[slot] = 0;\n this.velX[slot] = 0;\n this.velY[slot] = 0;\n this.scaleX[slot] = 1;\n this.scaleY[slot] = 1;\n this.rotations[slot] = 0;\n this.rotationSpeeds[slot] = 0;\n this.color[slot] = 0xffffffff;\n this.elapsed[slot] = 0;\n this.lifetime[slot] = 1;\n this.frame[slot] = 0;\n }\n\n /** Copies every channel of `from` onto `to`, for the CPU compaction pass. */\n public copySlot(from: number, to: number): void {\n this.posX[to] = this.posX[from]!;\n this.posY[to] = this.posY[from]!;\n this.velX[to] = this.velX[from]!;\n this.velY[to] = this.velY[from]!;\n this.scaleX[to] = this.scaleX[from]!;\n this.scaleY[to] = this.scaleY[from]!;\n this.rotations[to] = this.rotations[from]!;\n this.rotationSpeeds[to] = this.rotationSpeeds[from]!;\n this.color[to] = this.color[from]!;\n this.elapsed[to] = this.elapsed[from]!;\n this.lifetime[to] = this.lifetime[from]!;\n this.frame[to] = this.frame[from]!;\n }\n\n /** Snapshots one slot for a death callback. */\n public snapshot(slot: number): ParticleDeathContext {\n return {\n x: this.posX[slot]!,\n y: this.posY[slot]!,\n velocityX: this.velX[slot]!,\n velocityY: this.velY[slot]!,\n rotation: this.rotations[slot]!,\n scaleX: this.scaleX[slot]!,\n scaleY: this.scaleY[slot]!,\n color: this.color[slot]!,\n elapsed: this.elapsed[slot]!,\n lifetime: this.lifetime[slot]!,\n };\n }\n}\n\n/** Cursor implementation of {@link ParticleWriter}, rebound to a slot by each emit. @internal */\nexport class ParticleSlotWriter implements ParticleWriter {\n public readonly position: ParticleVectorWriter;\n public readonly velocity: ParticleVectorWriter;\n public readonly scale: ParticleVectorWriter;\n\n private readonly _storage: ParticleStorage;\n private _slot = 0;\n\n public constructor(storage: ParticleStorage) {\n this._storage = storage;\n\n const slotOf = this._currentSlot.bind(this);\n\n this.position = createVectorWriter(storage.posX, storage.posY, slotOf);\n this.velocity = createVectorWriter(storage.velX, storage.velY, slotOf);\n this.scale = createVectorWriter(storage.scaleX, storage.scaleY, slotOf);\n }\n\n /** The slot this cursor currently writes to. Used by the emitter to track what a spawner produced. @internal */\n public get slot(): number {\n return this._slot;\n }\n\n public bind(slot: number): this {\n this._slot = slot;\n\n return this;\n }\n\n /**\n * Marks this writer as belonging to a finished emission. Development builds\n * hand out one writer per emission and retire the previous one here, so\n * writing through a stale writer fails loudly instead of silently filling\n * whatever particle was emitted since.\n * @internal\n */\n public retire(): void {\n this._retired = true;\n }\n\n private _retired = false;\n\n /** Resolves the bound slot, refusing a writer a later emission has taken over. */\n private _currentSlot(): number {\n if (this._retired) {\n throw new Error('ParticleWriter: this writer belongs to an earlier emit(). Fill each emitted particle before emitting the next one.');\n }\n\n return this._slot;\n }\n\n public get rotation(): number {\n return this._storage.rotations[this._currentSlot()]!;\n }\n\n public set rotation(value: number) {\n this._storage.rotations[this._currentSlot()] = value;\n }\n\n public get rotationSpeed(): number {\n return this._storage.rotationSpeeds[this._currentSlot()]!;\n }\n\n public set rotationSpeed(value: number) {\n this._storage.rotationSpeeds[this._currentSlot()] = value;\n }\n\n public get color(): number {\n return this._storage.color[this._currentSlot()]!;\n }\n\n public set color(value: number) {\n this._storage.color[this._currentSlot()] = value;\n }\n\n public get lifetime(): number {\n return this._storage.lifetime[this._currentSlot()]!;\n }\n\n public set lifetime(value: number) {\n this._storage.lifetime[this._currentSlot()] = value;\n }\n\n public get frame(): number {\n return this._storage.frame[this._currentSlot()]!;\n }\n\n public set frame(value: number) {\n this._storage.frame[this._currentSlot()] = value | 0;\n }\n}\n\nconst createVectorWriter = (xs: Float32Array, ys: Float32Array, slotOf: () => number): ParticleVectorWriter => ({\n get x(): number {\n return xs[slotOf()]!;\n },\n set x(value: number) {\n xs[slotOf()] = value;\n },\n get y(): number {\n return ys[slotOf()]!;\n },\n set y(value: number) {\n ys[slotOf()] = value;\n },\n set(x: number, y: number): void {\n const slot = slotOf();\n\n xs[slot] = x;\n ys[slot] = y;\n },\n});\n"],"mappings":"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"}
|