@rzmps/rzmps 1.0.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 +1048 -0
- package/build/EmissionShape.d.ts +33 -0
- package/build/EmissionShape.js +129 -0
- package/build/Emitter.d.ts +58 -0
- package/build/Emitter.js +166 -0
- package/build/GpuModule.d.ts +28 -0
- package/build/GpuModule.js +28 -0
- package/build/Module.d.ts +19 -0
- package/build/Module.js +31 -0
- package/build/Particle.d.ts +60 -0
- package/build/Particle.js +52 -0
- package/build/ParticleForceField.d.ts +39 -0
- package/build/ParticleForceField.js +101 -0
- package/build/ParticleForceFieldHelper.d.ts +18 -0
- package/build/ParticleForceFieldHelper.js +103 -0
- package/build/ParticleSystem.d.ts +122 -0
- package/build/ParticleSystem.js +609 -0
- package/build/Renderer.d.ts +15 -0
- package/build/Renderer.js +11 -0
- package/build/assets/images/default.png +0 -0
- package/build/collision/ThreeCollisionBackend.d.ts +47 -0
- package/build/collision/ThreeCollisionBackend.js +340 -0
- package/build/enums/Axis.d.ts +6 -0
- package/build/enums/Axis.js +7 -0
- package/build/enums/DynamicValueType.d.ts +6 -0
- package/build/enums/DynamicValueType.js +7 -0
- package/build/enums/EmissionSource copy.d.ts +7 -0
- package/build/enums/EmissionSource copy.js +7 -0
- package/build/enums/EmissionSource.d.ts +7 -0
- package/build/enums/EmissionSource.js +7 -0
- package/build/enums/EndBehavior.d.ts +6 -0
- package/build/enums/EndBehavior.js +7 -0
- package/build/enums/SimulationSpace.d.ts +5 -0
- package/build/enums/SimulationSpace.js +6 -0
- package/build/enums/SpriteMaterialType.d.ts +5 -0
- package/build/enums/SpriteMaterialType.js +6 -0
- package/build/enums/TagSelectionMethod.d.ts +6 -0
- package/build/enums/TagSelectionMethod.js +7 -0
- package/build/enums/TrailMode copy.d.ts +5 -0
- package/build/enums/TrailMode copy.js +7 -0
- package/build/enums/TrailMode.d.ts +5 -0
- package/build/enums/TrailMode.js +7 -0
- package/build/enums/TrailTextureMode.d.ts +7 -0
- package/build/enums/TrailTextureMode.js +9 -0
- package/build/gpu/GpuDynamicValue.d.ts +9 -0
- package/build/gpu/GpuDynamicValue.js +113 -0
- package/build/gpu/GpuMath.d.ts +3 -0
- package/build/gpu/GpuMath.js +12 -0
- package/build/gpu/GpuParticleContext.d.ts +63 -0
- package/build/gpu/GpuParticleContext.js +291 -0
- package/build/helpers/acceptMultiple.d.ts +2 -0
- package/build/helpers/acceptMultiple.js +6 -0
- package/build/helpers/evaluateDynamic.d.ts +2 -0
- package/build/helpers/evaluateDynamic.js +18 -0
- package/build/helpers/evaluateDynamicColor.d.ts +3 -0
- package/build/helpers/evaluateDynamicColor.js +5 -0
- package/build/helpers/evaluateDynamicNumber.d.ts +2 -0
- package/build/helpers/evaluateDynamicNumber.js +4 -0
- package/build/helpers/evaluateDynamicVector3.d.ts +3 -0
- package/build/helpers/evaluateDynamicVector3.js +5 -0
- package/build/helpers/isPointInMesh.d.ts +2 -0
- package/build/helpers/isPointInMesh.js +8 -0
- package/build/helpers/particleRatio.d.ts +2 -0
- package/build/helpers/particleRatio.js +13 -0
- package/build/helpers/tagsIntersect.d.ts +2 -0
- package/build/helpers/tagsIntersect.js +4 -0
- package/build/index.d.ts +37 -0
- package/build/index.js +34 -0
- package/build/interfaces/ICollisionBackend.d.ts +21 -0
- package/build/interfaces/ICollisionBackend.js +1 -0
- package/build/interfaces/IParticleForceField.d.ts +5 -0
- package/build/interfaces/IParticleForceField.js +1 -0
- package/build/interfaces/InitialParticleValues.d.ts +18 -0
- package/build/interfaces/InitialParticleValues.js +1 -0
- package/build/materials/BasicSprite.d.ts +21 -0
- package/build/materials/BasicSprite.js +46 -0
- package/build/materials/UnlitSprite.d.ts +13 -0
- package/build/materials/UnlitSprite.js +31 -0
- package/build/materials/WebGPUBasicSprite.d.ts +8 -0
- package/build/materials/WebGPUBasicSprite.js +65 -0
- package/build/materials/WebGPUUnlitSprite.d.ts +8 -0
- package/build/materials/WebGPUUnlitSprite.js +49 -0
- package/build/modules/Audio.d.ts +60 -0
- package/build/modules/Audio.js +181 -0
- package/build/modules/Collision.d.ts +40 -0
- package/build/modules/Collision.js +162 -0
- package/build/modules/ColorBySpeed.d.ts +18 -0
- package/build/modules/ColorBySpeed.js +26 -0
- package/build/modules/ColorOverLifetime.d.ts +12 -0
- package/build/modules/ColorOverLifetime.js +17 -0
- package/build/modules/ExternalForces.d.ts +21 -0
- package/build/modules/ExternalForces.js +61 -0
- package/build/modules/ForceOverLifetime.d.ts +11 -0
- package/build/modules/ForceOverLifetime.js +12 -0
- package/build/modules/LimitVelocityOverLifetime.d.ts +16 -0
- package/build/modules/LimitVelocityOverLifetime.js +34 -0
- package/build/modules/MassOverLifetime.d.ts +11 -0
- package/build/modules/MassOverLifetime.js +15 -0
- package/build/modules/NoiseModule.d.ts +23 -0
- package/build/modules/NoiseModule.js +45 -0
- package/build/modules/RotationBySpeed.d.ts +14 -0
- package/build/modules/RotationBySpeed.js +19 -0
- package/build/modules/RotationOverLifetime.d.ts +11 -0
- package/build/modules/RotationOverLifetime.js +12 -0
- package/build/modules/ScaleBySpeed.d.ts +14 -0
- package/build/modules/ScaleBySpeed.js +19 -0
- package/build/modules/ScaleOverLifetime.d.ts +11 -0
- package/build/modules/ScaleOverLifetime.js +11 -0
- package/build/modules/TransformByNoise.d.ts +17 -0
- package/build/modules/TransformByNoise.js +32 -0
- package/build/modules/VelocityOverLifetime.d.ts +15 -0
- package/build/modules/VelocityOverLifetime.js +32 -0
- package/build/renderers/LightRenderer.d.ts +54 -0
- package/build/renderers/LightRenderer.js +141 -0
- package/build/renderers/MeshRenderer.d.ts +27 -0
- package/build/renderers/MeshRenderer.js +70 -0
- package/build/renderers/SpriteRenderer.d.ts +95 -0
- package/build/renderers/SpriteRenderer.js +456 -0
- package/build/renderers/TrailRenderer.d.ts +71 -0
- package/build/renderers/TrailRenderer.js +400 -0
- package/build/shaders/BasicSprite.frag.js +1 -0
- package/build/shaders/BasicSprite.vert.js +1 -0
- package/build/shaders/UnlitSprite.frag.js +1 -0
- package/build/shaders/UnlitSprite.vert.js +1 -0
- package/build/types/DynamicValue.d.ts +2 -0
- package/build/types/DynamicValue.js +1 -0
- package/build/types/EnumStringLiteral.d.ts +1 -0
- package/build/types/EnumStringLiteral.js +1 -0
- package/build/types/Multiple.d.ts +2 -0
- package/build/types/Multiple.js +1 -0
- package/build/types/Tag.d.ts +1 -0
- package/build/types/Tag.js +1 -0
- package/package.json +39 -0
package/README.md
ADDED
|
@@ -0,0 +1,1048 @@
|
|
|
1
|
+
# RZMPS
|
|
2
|
+
|
|
3
|
+
**🧅 Robert May Particle System** is a modular, extensible particle system for
|
|
4
|
+
[Three.js](https://threejs.org/).
|
|
5
|
+
|
|
6
|
+
[](https://www.npmjs.com/package/rzmps)
|
|
7
|
+
[](https://github.com/rzmay/rzmps)
|
|
8
|
+
|
|
9
|
+
| Resource | Link |
|
|
10
|
+
| ----------- | ------------------------------------------------------------ |
|
|
11
|
+
| npm package | [npmjs.com/package/rzmps](https://www.npmjs.com/package/rzmps) |
|
|
12
|
+
| GitHub repo | [github.com/rzmay/rzmps](https://github.com/rzmay/rzmps) |
|
|
13
|
+
| Live demo | [rzmps.rzmay.com](https://rzmps.rzmay.com) |
|
|
14
|
+
|
|
15
|
+
RZMPS is built from small composable pieces:
|
|
16
|
+
|
|
17
|
+
- **Emitters** create particles from shapes.
|
|
18
|
+
- **Modules** modify particles over time.
|
|
19
|
+
- **Renderers** decide how particles appear in a Three.js scene.
|
|
20
|
+
- **Subsystems** let particles emit other particle systems.
|
|
21
|
+
- **Physics backends** let particles collide with Three.js objects or external
|
|
22
|
+
physics engines.
|
|
23
|
+
|
|
24
|
+
## Contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick Start](#quick-start)
|
|
28
|
+
- [Core Types](#core-types)
|
|
29
|
+
- [Particle Systems](#particle-systems)
|
|
30
|
+
- [Emitters](#emitters)
|
|
31
|
+
- [Built-In Modules](#built-in-modules)
|
|
32
|
+
- [Built-In Renderers](#built-in-renderers)
|
|
33
|
+
- [Force Fields](#force-fields)
|
|
34
|
+
- [Collision And Physics](#collision-and-physics)
|
|
35
|
+
- [Custom Modules](#custom-modules)
|
|
36
|
+
- [Developer Guide](#developer-guide)
|
|
37
|
+
|
|
38
|
+
## Installation
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm install rzmps three
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
`three` is a peer dependency.
|
|
45
|
+
|
|
46
|
+
## Quick Start
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import * as THREE from "three";
|
|
50
|
+
import {
|
|
51
|
+
ColorOverLifetime,
|
|
52
|
+
EmissionShape,
|
|
53
|
+
Emitter,
|
|
54
|
+
ParticleSystem,
|
|
55
|
+
ScaleOverLifetime,
|
|
56
|
+
SpriteRenderer,
|
|
57
|
+
} from "rzmps";
|
|
58
|
+
|
|
59
|
+
const particles = new ParticleSystem({
|
|
60
|
+
emitters: new Emitter({
|
|
61
|
+
source: EmissionShape.Sphere(),
|
|
62
|
+
rate: 80,
|
|
63
|
+
radialSpeed: 2,
|
|
64
|
+
initialValues: {
|
|
65
|
+
lifetime: 1.5,
|
|
66
|
+
scale: new THREE.Vector3(0.25, 0.25, 0.25),
|
|
67
|
+
color: new THREE.Color("#ff9f43"),
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
modules: [
|
|
71
|
+
new ColorOverLifetime({
|
|
72
|
+
alpha: (t) => 1 - t,
|
|
73
|
+
}),
|
|
74
|
+
new ScaleOverLifetime({
|
|
75
|
+
scale: (t) => new THREE.Vector3(1 + t, 1 + t, 1 + t),
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
78
|
+
renderers: new SpriteRenderer(undefined, {
|
|
79
|
+
materialOptions: {
|
|
80
|
+
blending: THREE.AdditiveBlending,
|
|
81
|
+
depthWrite: false,
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
scene.add(particles);
|
|
87
|
+
|
|
88
|
+
function animate() {
|
|
89
|
+
requestAnimationFrame(animate);
|
|
90
|
+
particles.update();
|
|
91
|
+
renderer.render(scene, camera);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
animate();
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Core Types
|
|
98
|
+
|
|
99
|
+
### Dynamic Values
|
|
100
|
+
|
|
101
|
+
Many RZMPS options use `DynamicValue<T>`.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
type DynamicValue<T> =
|
|
105
|
+
| T
|
|
106
|
+
| ((t: number) => DynamicValue<T>)
|
|
107
|
+
| [DynamicValue<T>, DynamicValue<T>]
|
|
108
|
+
| Set<DynamicValue<T>>;
|
|
109
|
+
|
|
110
|
+
// Used for parameters that can take multiple elements or just one
|
|
111
|
+
type Multiple<T> = T | T[];
|
|
112
|
+
|
|
113
|
+
// Multiple with least one value
|
|
114
|
+
type StrictMultiple<T> = T | [T, ...[T]];
|
|
115
|
+
|
|
116
|
+
// In RZMPS, tags are natively strings
|
|
117
|
+
type Tag = string;
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
The `t` argument is normalized particle lifetime, from `0` to `1`.
|
|
121
|
+
|
|
122
|
+
| Form | Example | Behavior |
|
|
123
|
+
| -------------- | ----------------------------- | -------------------------------------- |
|
|
124
|
+
| Static value | `2` | Always returns the same value. |
|
|
125
|
+
| Function | `(t) => 1 - t` | Re-evaluates over normalized lifetime. |
|
|
126
|
+
| Two-item array | `[0.5, 2]` | Random value between min and max. |
|
|
127
|
+
| Set | `new Set(["spark", "smoke"])` | Random choice. |
|
|
128
|
+
|
|
129
|
+
Dynamic values are supported for numbers, vectors, and colors where the option
|
|
130
|
+
type uses `DynamicValue<number>`, `DynamicValue<THREE.Vector3>`, or
|
|
131
|
+
`DynamicValue<THREE.Color>`.
|
|
132
|
+
|
|
133
|
+
The [curves](https://www.npmjs.com/package/curves) package was designed and
|
|
134
|
+
built to streamline functional values by defining and evaluating eased curves,
|
|
135
|
+
similar to Unity's AnimationCurves with modifiers akin to Blender's FCurve
|
|
136
|
+
Modifiers. It has prebuilt keyframe types for numbers, colors, and vectors,
|
|
137
|
+
compatible with the `THREE.Vector3` and `THREE.Color` types.
|
|
138
|
+
|
|
139
|
+
### Tags
|
|
140
|
+
|
|
141
|
+
Emitters can assign tags to particles. Modules and renderers can filter by tags.
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
const emitter = new Emitter({
|
|
145
|
+
tags: ["spark", "hot"],
|
|
146
|
+
tagSelection: "all",
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const sparksOnly = new SpriteRenderer(undefined, { tags: "spark" });
|
|
150
|
+
const fadeHot = new ColorOverLifetime({ tags: "hot", alpha: (t) => 1 - t });
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`tagSelection` can follows the `TagSelectionMethod` enum, and can be:
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
enum TagSelectionMethod {
|
|
157
|
+
All = "all",
|
|
158
|
+
Random = "random",
|
|
159
|
+
Distribute = "distribute",
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
| Value | Behavior |
|
|
164
|
+
| ------------ | -------------------------------------------------------- |
|
|
165
|
+
| `All` | Every emitted particle receives every emitter tag.scene. |
|
|
166
|
+
| `Random` | Each particle receives one random tag. |
|
|
167
|
+
| `Distribute` | Tags are assigned round-robin. |
|
|
168
|
+
|
|
169
|
+
## Particle Systems
|
|
170
|
+
|
|
171
|
+
`ParticleSystem` extends `THREE.Object3D`, so it can be added, moved, rotated,
|
|
172
|
+
and scaled like any other Three.js object.
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
new ParticleSystem(options?: Partial<ParticleSystemOptions>)
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
interface ParticleSystemOptions {
|
|
180
|
+
emitters: Multiple<Emitter>;
|
|
181
|
+
renderers: Multiple<Renderer>;
|
|
182
|
+
modules: Multiple<Module>;
|
|
183
|
+
duration: number;
|
|
184
|
+
looping: boolean;
|
|
185
|
+
endBehavior: EndBehavior;
|
|
186
|
+
gravity: THREE.Vector3;
|
|
187
|
+
gravityModifier: DynamicValue<number>;
|
|
188
|
+
simulationSpace: SimulationSpace;
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`duration` and `looping` control the particle system's emission timeline.
|
|
193
|
+
Emitters use normalized system time for rate curves and bursts.
|
|
194
|
+
|
|
195
|
+
For non-looping systems, `endBehavior` controls what happens after the system
|
|
196
|
+
duration has elapsed.
|
|
197
|
+
|
|
198
|
+
```ts
|
|
199
|
+
enum EndBehavior {
|
|
200
|
+
None = "none",
|
|
201
|
+
Destroy = "destroy",
|
|
202
|
+
DestroyImmediate = "destroyImmediate",
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
| Value | Behavior |
|
|
207
|
+
| ------------------ | --------------------------------------------------------------------- |
|
|
208
|
+
| `None` | Stops emission and keeps the system in the scene. |
|
|
209
|
+
| `Destroy` | Stops emission, lets live particles finish, then destroys the system. |
|
|
210
|
+
| `DestroyImmediate` | Destroys the system as soon as its duration elapses. |
|
|
211
|
+
|
|
212
|
+
### Simulation Space
|
|
213
|
+
|
|
214
|
+
`simulationSpace` follows the `SimulationSpace` enum and controls whether
|
|
215
|
+
particle positions are stored relative to the particle system or in world space.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
enum EndBehavior {
|
|
219
|
+
Local = "local",
|
|
220
|
+
World = "world",
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
| Value | Behavior |
|
|
225
|
+
| ------- | ---------------------------------------------------------------------------------------------- |
|
|
226
|
+
| `Local` | Particles move with the particle system transform. |
|
|
227
|
+
| `World` | New particles spawn from the system transform, then remain in world space if the system moves. |
|
|
228
|
+
|
|
229
|
+
Subsystems inherit the parent system's simulation space.
|
|
230
|
+
|
|
231
|
+
### Control Methods
|
|
232
|
+
|
|
233
|
+
| Method | Description |
|
|
234
|
+
| ------------------------------- | ------------------------------------------------------------ |
|
|
235
|
+
| `update()` | Advances the particle system. Call once per animation frame. |
|
|
236
|
+
| `start()` | Starts or restarts emission. |
|
|
237
|
+
| `pause()` | Pauses emission and simulation. |
|
|
238
|
+
| `resume()` | Resumes from pause. |
|
|
239
|
+
| `stop(clearParticles)` | Stops emission; optionally clears existing particles. |
|
|
240
|
+
| `destroy()` | Stops, clears, destroys renderers, and removes the system. |
|
|
241
|
+
| `clearParticles()` | Removes all live particles. |
|
|
242
|
+
| `addEmitter(emitter)` | Adds and sets up an emitter. |
|
|
243
|
+
| `addModule(module)` | Adds a module. |
|
|
244
|
+
| `addRenderer(renderer)` | Adds and sets up a renderer. |
|
|
245
|
+
| `addSubSystem(system, options)` | Adds a child particle system emitted by particles. |
|
|
246
|
+
|
|
247
|
+
### Event Listeners
|
|
248
|
+
|
|
249
|
+
`ParticleSystem` exposes listener helpers for particle lifecycle events.
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
type ParticleListener = (particle: Particle) => void;
|
|
253
|
+
type CollisionListener = (particle: Particle, collision: CollisionHit) => void;
|
|
254
|
+
|
|
255
|
+
system.onSpawn((particle) => {
|
|
256
|
+
console.log("spawned", particle.id);
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
system.onDeath((particle) => {
|
|
260
|
+
console.log("died", particle.id);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
system.onCollision((particle, collision) => {
|
|
264
|
+
console.log("hit", particle.id, collision);
|
|
265
|
+
});
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
| Method | Description |
|
|
269
|
+
| ----------------------------------- | ----------------------------------------------------- |
|
|
270
|
+
| `onSpawn(listener)` | Runs when a particle is emitted. |
|
|
271
|
+
| `removeSpawnListener(listener)` | Removes a spawn listener. |
|
|
272
|
+
| `onDeath(listener)` | Runs when a particle reaches the end of its lifetime. |
|
|
273
|
+
| `removeDeathListener(listener)` | Removes a death listener. |
|
|
274
|
+
| `onCollision(listener)` | Runs when the `Collision` module reports a hit. |
|
|
275
|
+
| `removeCollisionListener(listener)` | Removes a collision listener. |
|
|
276
|
+
|
|
277
|
+
### Subsystems
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
interface SubSystemOptions {
|
|
281
|
+
shouldEmit: boolean | ((particle: Particle) => boolean);
|
|
282
|
+
ratio: number;
|
|
283
|
+
emitContinuous: boolean;
|
|
284
|
+
emitOnCollision: boolean;
|
|
285
|
+
emitOnSpawn: boolean;
|
|
286
|
+
emitOnDeath: boolean;
|
|
287
|
+
inheritScale: boolean;
|
|
288
|
+
inheritLifetime: boolean;
|
|
289
|
+
inheritColor: boolean;
|
|
290
|
+
inheritAlpha: boolean;
|
|
291
|
+
inheritMass: boolean;
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
rocketSystem.addSubSystem(sparkSystem, {
|
|
297
|
+
emitOnDeath: true,
|
|
298
|
+
emitContinuous: false,
|
|
299
|
+
inheritColor: true,
|
|
300
|
+
inheritAlpha: true,
|
|
301
|
+
inheritScale: true,
|
|
302
|
+
});
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
Subsystems are owned and updated by their parent. They can emit continuously
|
|
306
|
+
from parent particles, or trigger emission runs on spawn, collision, or death.
|
|
307
|
+
|
|
308
|
+
## Emitters
|
|
309
|
+
|
|
310
|
+
```ts
|
|
311
|
+
new Emitter(options?: Partial<EmitterOptions>)
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
```ts
|
|
315
|
+
type SpawnBurst = {
|
|
316
|
+
time: number;
|
|
317
|
+
count: DynamicValue<number>;
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
interface EmitterOptions {
|
|
321
|
+
initialValues: Partial<InitialParticleValues>;
|
|
322
|
+
source: EmissionShape;
|
|
323
|
+
bursts: SpawnBurst | SpawnBurst[];
|
|
324
|
+
rate: DynamicValue<number>;
|
|
325
|
+
radialSpeed: DynamicValue<number>;
|
|
326
|
+
alignment: DynamicValue<number>;
|
|
327
|
+
tags: StrictMultiple<Tag>;
|
|
328
|
+
tagSelection: TagSelectionMethod;
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
Emitter `rate` curves and burst `time` values are evaluated against the owning
|
|
333
|
+
particle system's normalized timeline.
|
|
334
|
+
|
|
335
|
+
### Initial Particle Values
|
|
336
|
+
|
|
337
|
+
Common `initialValues` fields include:
|
|
338
|
+
|
|
339
|
+
| Field | Type | Description |
|
|
340
|
+
| --------------------- | --------------- | ------------------------------------------------------------------ |
|
|
341
|
+
| `position` | `THREE.Vector3` | Initial particle position. Usually supplied by the emission shape. |
|
|
342
|
+
| `rotation` | `THREE.Vector3` | Initial Euler rotation. |
|
|
343
|
+
| `scale` | `THREE.Vector3` | Initial particle scale. |
|
|
344
|
+
| `velocity` | `THREE.Vector3` | Initial linear velocity. |
|
|
345
|
+
| `angularVelocity` | `THREE.Vector3` | Initial rotational velocity. |
|
|
346
|
+
| `scalarVelocity` | `THREE.Vector3` | Initial scale velocity. |
|
|
347
|
+
| `acceleration` | `THREE.Vector3` | Initial linear acceleration. |
|
|
348
|
+
| `angularAcceleration` | `THREE.Vector3` | Initial rotational acceleration. |
|
|
349
|
+
| `scalarAcceleration` | `THREE.Vector3` | Initial scale acceleration. |
|
|
350
|
+
| `lifetime` | `number` | Lifetime in seconds. |
|
|
351
|
+
| `speed` | `number` | Multiplier applied to particle simulation speed. |
|
|
352
|
+
| `color` | `THREE.Color` | Initial particle color. |
|
|
353
|
+
| `alpha` | `number` | Initial opacity. |
|
|
354
|
+
| `mass` | `number` | Particle mass for collision impulses. Defaults to `0`. |
|
|
355
|
+
|
|
356
|
+
Most initial values can be dynamic.
|
|
357
|
+
|
|
358
|
+
### Emission Shapes
|
|
359
|
+
|
|
360
|
+
```ts
|
|
361
|
+
new EmissionShape(options?: Partial<EmissionShapeOptions>)
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
interface EmissionShapeOptions {
|
|
366
|
+
geometry: THREE.BufferGeometry;
|
|
367
|
+
source: EmissionSource;
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
enum EmissionSource {
|
|
371
|
+
Volume = "volume",
|
|
372
|
+
Surface = "surface",
|
|
373
|
+
Vertices = "vertices",
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Helpers:
|
|
378
|
+
|
|
379
|
+
```ts
|
|
380
|
+
EmissionShape.Box(...boxGeometryArgs);
|
|
381
|
+
EmissionShape.Sphere(...sphereGeometryArgs);
|
|
382
|
+
EmissionShape.Cone(...coneGeometryArgs);
|
|
383
|
+
EmissionShape.Torus(...torusGeometryArgs);
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Built-In Modules
|
|
387
|
+
|
|
388
|
+
All built-in modules extend `Module`. Constructor option objects are partials,
|
|
389
|
+
so you only need to pass the fields you want to customize.
|
|
390
|
+
|
|
391
|
+
```ts
|
|
392
|
+
interface ModuleOptions {
|
|
393
|
+
priority: number;
|
|
394
|
+
tags: StrictMultiple<Tag>;
|
|
395
|
+
}
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
| Option | Description |
|
|
399
|
+
| ---------- | -------------------------------------------------------------------------------------------------------------------------------- |
|
|
400
|
+
| `priority` | Modules with priority below `0` run before movement. Modules with priority `0` or higher run after movement, sorted by priority. |
|
|
401
|
+
| `tags` | Restricts the module to particles with matching tags. |
|
|
402
|
+
|
|
403
|
+
### VelocityOverLifetime
|
|
404
|
+
|
|
405
|
+
```ts
|
|
406
|
+
new VelocityOverLifetime(options?: Partial<VelocityOverLifetimeOptions>)
|
|
407
|
+
|
|
408
|
+
interface VelocityOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
409
|
+
linear: DynamicValue<THREE.Vector3>;
|
|
410
|
+
orbital: DynamicValue<THREE.Vector3>;
|
|
411
|
+
orbitOffset: DynamicValue<THREE.Vector3>;
|
|
412
|
+
radial: DynamicValue<number>;
|
|
413
|
+
speedModifier: DynamicValue<number>;
|
|
414
|
+
}
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
Sets particle velocity from the start velocity plus optional linear, orbital,
|
|
418
|
+
and radial terms. `speedModifier` scales particle simulation speed.
|
|
419
|
+
|
|
420
|
+
### ForceOverLifetime
|
|
421
|
+
|
|
422
|
+
```ts
|
|
423
|
+
new ForceOverLifetime(options: ForceOverLifetimeOptions)
|
|
424
|
+
|
|
425
|
+
interface ForceOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
426
|
+
force: DynamicValue<THREE.Vector3>;
|
|
427
|
+
}
|
|
428
|
+
```
|
|
429
|
+
|
|
430
|
+
Adds a dynamic force to particle acceleration.
|
|
431
|
+
|
|
432
|
+
### LimitVelocityOverLifetime
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
new LimitVelocityOverLifetime(options: LimitVelocityOverLifetimeOptions)
|
|
436
|
+
|
|
437
|
+
interface LimitVelocityOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
438
|
+
limit: DynamicValue<THREE.Vector3>;
|
|
439
|
+
dampen?: number;
|
|
440
|
+
drag?: DynamicValue<number>;
|
|
441
|
+
multiplyDragBySize?: boolean;
|
|
442
|
+
multiplyDragByVelocity?: boolean;
|
|
443
|
+
}
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
Clamps velocity per axis and optionally applies drag.
|
|
447
|
+
|
|
448
|
+
### MassOverLifetime
|
|
449
|
+
|
|
450
|
+
```ts
|
|
451
|
+
new MassOverLifetime(options: MassOverLifetimeOptions)
|
|
452
|
+
|
|
453
|
+
interface MassOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
454
|
+
mass: DynamicValue<number>;
|
|
455
|
+
multiplyMassBySize: boolean;
|
|
456
|
+
}
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
Changes particle mass over lifetime. `multiplyMassBySize` defaults to `true`.
|
|
460
|
+
|
|
461
|
+
### ColorOverLifetime
|
|
462
|
+
|
|
463
|
+
```ts
|
|
464
|
+
new ColorOverLifetime(options?: ColorOverLifetimeOptions)
|
|
465
|
+
|
|
466
|
+
interface ColorOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
467
|
+
color?: DynamicValue<THREE.Color>;
|
|
468
|
+
alpha?: DynamicValue<number>;
|
|
469
|
+
}
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
Multiplies each particle's start color and alpha over lifetime.
|
|
473
|
+
|
|
474
|
+
### ColorBySpeed
|
|
475
|
+
|
|
476
|
+
```ts
|
|
477
|
+
new ColorBySpeed(options: ColorBySpeedOptions)
|
|
478
|
+
|
|
479
|
+
type SpeedRange = [number, number] | { min: number; max: number };
|
|
480
|
+
|
|
481
|
+
interface ColorBySpeedOptions extends Partial<ModuleOptions> {
|
|
482
|
+
color?: DynamicValue<THREE.Color>;
|
|
483
|
+
alpha?: DynamicValue<number>;
|
|
484
|
+
speedRange?: SpeedRange;
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
Multiplies start color and alpha based on normalized speed within `speedRange`.
|
|
489
|
+
|
|
490
|
+
### ScaleOverLifetime
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
new ScaleOverLifetime(options: ScaleOverLifetimeOptions)
|
|
494
|
+
|
|
495
|
+
interface ScaleOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
496
|
+
scale: DynamicValue<THREE.Vector3>;
|
|
497
|
+
}
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
Multiplies each particle's start scale over lifetime.
|
|
501
|
+
|
|
502
|
+
### ScaleBySpeed
|
|
503
|
+
|
|
504
|
+
```ts
|
|
505
|
+
new ScaleBySpeed(options: ScaleBySpeedOptions)
|
|
506
|
+
|
|
507
|
+
interface ScaleBySpeedOptions extends Partial<ModuleOptions> {
|
|
508
|
+
scale: DynamicValue<THREE.Vector3>;
|
|
509
|
+
speedRange?: SpeedRange;
|
|
510
|
+
}
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
Multiplies start scale based on normalized speed within `speedRange`.
|
|
514
|
+
|
|
515
|
+
### RotationOverLifetime
|
|
516
|
+
|
|
517
|
+
```ts
|
|
518
|
+
new RotationOverLifetime(options: RotationOverLifetimeOptions)
|
|
519
|
+
|
|
520
|
+
interface RotationOverLifetimeOptions extends Partial<ModuleOptions> {
|
|
521
|
+
angularVelocity: DynamicValue<THREE.Vector3>;
|
|
522
|
+
}
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
Adjusts angular velocity over lifetime.
|
|
526
|
+
|
|
527
|
+
### RotationBySpeed
|
|
528
|
+
|
|
529
|
+
```ts
|
|
530
|
+
new RotationBySpeed(options: RotationBySpeedOptions)
|
|
531
|
+
|
|
532
|
+
interface RotationBySpeedOptions extends Partial<ModuleOptions> {
|
|
533
|
+
angularVelocity: DynamicValue<THREE.Vector3>;
|
|
534
|
+
speedRange?: SpeedRange;
|
|
535
|
+
}
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
Adjusts angular velocity based on normalized speed within `speedRange`.
|
|
539
|
+
|
|
540
|
+
### NoiseModule
|
|
541
|
+
|
|
542
|
+
```ts
|
|
543
|
+
new NoiseModule(key: string, options?: Partial<NoiseOptions>)
|
|
544
|
+
|
|
545
|
+
interface NoiseOptions extends Partial<ModuleOptions> {
|
|
546
|
+
octaves: number;
|
|
547
|
+
frequency: number;
|
|
548
|
+
lacunarity: number;
|
|
549
|
+
persistence: number;
|
|
550
|
+
time: number;
|
|
551
|
+
offset: THREE.Vector3;
|
|
552
|
+
}
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Writes simplex noise into `particle.noise[key]` as `{ noise, noise4d }`.
|
|
556
|
+
|
|
557
|
+
### TransformByNoise
|
|
558
|
+
|
|
559
|
+
```ts
|
|
560
|
+
new TransformByNoise(options: Partial<TransformByNoiseOptions>)
|
|
561
|
+
|
|
562
|
+
interface TransformByNoiseOptions extends Partial<ModuleOptions>, NoiseOptions {
|
|
563
|
+
strength: DynamicValue<THREE.Vector3>;
|
|
564
|
+
scrollSpeed: DynamicValue<number>;
|
|
565
|
+
damping: boolean;
|
|
566
|
+
}
|
|
567
|
+
```
|
|
568
|
+
|
|
569
|
+
Pushes particles through animated 3D noise. Internally, it creates dependent
|
|
570
|
+
`NoiseModule` instances for the X, Y, and Z axes.
|
|
571
|
+
|
|
572
|
+
### ExternalForces
|
|
573
|
+
|
|
574
|
+
```ts
|
|
575
|
+
new ExternalForces(options: ExternalForcesOptions)
|
|
576
|
+
|
|
577
|
+
interface ExternalForcesOptions extends Partial<ModuleOptions> {
|
|
578
|
+
multiplier?: DynamicValue<number>;
|
|
579
|
+
forceFieldFilter?: (forceField: ParticleForceField) => boolean;
|
|
580
|
+
forceFields?: IParticleForceField[];
|
|
581
|
+
}
|
|
582
|
+
```
|
|
583
|
+
|
|
584
|
+
Samples `ParticleForceField` objects and applies their forces to particles. If
|
|
585
|
+
`forceFields` is omitted, fields are discovered from the particle system's
|
|
586
|
+
scene.
|
|
587
|
+
|
|
588
|
+
### Collision
|
|
589
|
+
|
|
590
|
+
```ts
|
|
591
|
+
new Collision(options?: Partial<CollisionOptions>)
|
|
592
|
+
|
|
593
|
+
interface CollisionOptions extends Partial<ModuleOptions> {
|
|
594
|
+
backend: ICollisionBackend;
|
|
595
|
+
dampen: DynamicValue<number>;
|
|
596
|
+
bounce: DynamicValue<number>;
|
|
597
|
+
lifetimeLoss: DynamicValue<number>;
|
|
598
|
+
applyImpulses: boolean;
|
|
599
|
+
radiusScale: number;
|
|
600
|
+
minKillSpeed: number;
|
|
601
|
+
maxKillSpeed: number;
|
|
602
|
+
onCollision?: CollisionListener;
|
|
603
|
+
}
|
|
604
|
+
```
|
|
605
|
+
|
|
606
|
+
Detects collisions using an `ICollisionBackend`, reflects velocity with bounce
|
|
607
|
+
and dampening, reduces lifetime on impact, and can apply impulses to dynamic
|
|
608
|
+
physics bodies when supported by the backend.
|
|
609
|
+
|
|
610
|
+
### Audio
|
|
611
|
+
|
|
612
|
+
```ts
|
|
613
|
+
new Audio(options?: Partial<AudioOptions>)
|
|
614
|
+
|
|
615
|
+
interface AudioOptions extends Partial<ModuleOptions> {
|
|
616
|
+
listener: THREE.AudioListener;
|
|
617
|
+
sound: AudioBuffer | AudioBuffer[];
|
|
618
|
+
onCollisionSound: AudioBuffer | AudioBuffer[];
|
|
619
|
+
onSpawnSound: AudioBuffer | AudioBuffer[];
|
|
620
|
+
onDeathSound: AudioBuffer | AudioBuffer[];
|
|
621
|
+
shouldPlay: (particle: Particle) => boolean;
|
|
622
|
+
loop: boolean;
|
|
623
|
+
ratio: number;
|
|
624
|
+
collisionRatio: number;
|
|
625
|
+
pitch: DynamicValue<number>;
|
|
626
|
+
volume: DynamicValue<number>;
|
|
627
|
+
sizeAffectsPitch: number;
|
|
628
|
+
sizeAffectsVolume: number;
|
|
629
|
+
alphaAffectsPitch: number;
|
|
630
|
+
alphaAffectsVolume: number;
|
|
631
|
+
speedAffectsPitch: number;
|
|
632
|
+
speedAffectsVolume: number;
|
|
633
|
+
}
|
|
634
|
+
```
|
|
635
|
+
|
|
636
|
+
Adds positional audio to particles and can play event sounds for spawn,
|
|
637
|
+
collision, and death.
|
|
638
|
+
|
|
639
|
+
## Built-In Renderers
|
|
640
|
+
|
|
641
|
+
All renderers accept shared renderer options:
|
|
642
|
+
|
|
643
|
+
```ts
|
|
644
|
+
interface RendererOptions {
|
|
645
|
+
tags: StrictMultiple<Tag>;
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
### SpriteRenderer
|
|
650
|
+
|
|
651
|
+
```ts
|
|
652
|
+
new SpriteRenderer(
|
|
653
|
+
texture?: string | THREE.Texture,
|
|
654
|
+
options?: Partial<SpriteRendererOptions>
|
|
655
|
+
)
|
|
656
|
+
|
|
657
|
+
interface SpriteRendererOptions extends RendererOptions {
|
|
658
|
+
fps: DynamicValue<number>;
|
|
659
|
+
tileSize: { x: number; y: number };
|
|
660
|
+
tileMargin: { x: number; y: number };
|
|
661
|
+
gridSize: { x: number; y: number };
|
|
662
|
+
frames: number;
|
|
663
|
+
randomStartFrame: boolean;
|
|
664
|
+
alphaMap: string | THREE.Texture;
|
|
665
|
+
material: SpriteMaterialType;
|
|
666
|
+
materialOptions: BasicSpriteOptions | UnlitSpriteOptions;
|
|
667
|
+
castShadow: boolean;
|
|
668
|
+
softParticleDistance: number;
|
|
669
|
+
}
|
|
670
|
+
```
|
|
671
|
+
|
|
672
|
+
`SpriteRenderer` renders particles as GPU points. It supports sprite sheets,
|
|
673
|
+
alpha maps, random start frames, shadows, and soft particles.
|
|
674
|
+
|
|
675
|
+
`SpriteMaterialType` is an enum consisting of two string values:
|
|
676
|
+
|
|
677
|
+
```ts
|
|
678
|
+
enum SpriteMaterialType {
|
|
679
|
+
Basic = "basic",
|
|
680
|
+
Unlit = "unlit",
|
|
681
|
+
}
|
|
682
|
+
```
|
|
683
|
+
|
|
684
|
+
`material: "unlit"` uses `UnlitSpriteOptions`:
|
|
685
|
+
|
|
686
|
+
```ts
|
|
687
|
+
interface UnlitSpriteOptions {
|
|
688
|
+
gridSize: { x: number; y: number };
|
|
689
|
+
frames: number;
|
|
690
|
+
alphaMap: THREE.Texture;
|
|
691
|
+
softParticles: boolean;
|
|
692
|
+
softParticleDistance: number;
|
|
693
|
+
}
|
|
694
|
+
```
|
|
695
|
+
|
|
696
|
+
`material: "basic"` uses `BasicSpriteOptions`, which extends
|
|
697
|
+
`THREE.ShaderMaterialParameters`:
|
|
698
|
+
|
|
699
|
+
```ts
|
|
700
|
+
interface BasicSpriteOptions extends THREE.ShaderMaterialParameters {
|
|
701
|
+
gridSize: { x: number; y: number };
|
|
702
|
+
frames: number;
|
|
703
|
+
alphaMap: THREE.Texture;
|
|
704
|
+
normalMap: THREE.Texture;
|
|
705
|
+
normalStrength: number;
|
|
706
|
+
normalLighting: number;
|
|
707
|
+
sphericalNormals: boolean;
|
|
708
|
+
roughness: number;
|
|
709
|
+
roughnessMap: THREE.Texture;
|
|
710
|
+
envMap: THREE.Texture;
|
|
711
|
+
envIntensity: number;
|
|
712
|
+
softParticles: boolean;
|
|
713
|
+
softParticleDistance: number;
|
|
714
|
+
}
|
|
715
|
+
```
|
|
716
|
+
|
|
717
|
+
Soft particles read scene depth from the active Three.js renderer and fade near
|
|
718
|
+
intersections. Set `softParticleDistance` above `0` to enable the effect.
|
|
719
|
+
|
|
720
|
+
### MeshRenderer
|
|
721
|
+
|
|
722
|
+
```ts
|
|
723
|
+
new MeshRenderer(options?: Partial<MeshRendererOptions>)
|
|
724
|
+
|
|
725
|
+
interface MeshRendererOptions extends RendererOptions {
|
|
726
|
+
mesh: THREE.Mesh;
|
|
727
|
+
maxParticles: number;
|
|
728
|
+
geometry: THREE.BufferGeometry;
|
|
729
|
+
material: THREE.MeshStandardMaterial;
|
|
730
|
+
materialOptions: THREE.MeshStandardMaterialParameters;
|
|
731
|
+
castShadow: boolean;
|
|
732
|
+
receiveShadow: boolean;
|
|
733
|
+
}
|
|
734
|
+
```
|
|
735
|
+
|
|
736
|
+
Renders particles as an `InstancedMesh`. Pass a complete `mesh`, or pass
|
|
737
|
+
`geometry`, `material`, or `materialOptions`.
|
|
738
|
+
|
|
739
|
+
### TrailRenderer
|
|
740
|
+
|
|
741
|
+
```ts
|
|
742
|
+
new TrailRenderer(options?: Partial<TrailRendererOptions>)
|
|
743
|
+
|
|
744
|
+
enum TrailMode {
|
|
745
|
+
Particle = "particle",
|
|
746
|
+
Ribbon = "ribbon",
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
enum TrailTextureMode {
|
|
750
|
+
Stretch = "stretch",
|
|
751
|
+
Tile = "tile",
|
|
752
|
+
RepeatPerSegment = "repeat",
|
|
753
|
+
DistributePerSegment = "distribute",
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
interface TrailRendererOptions extends RendererOptions {
|
|
757
|
+
mode: TrailMode;
|
|
758
|
+
textureMode: TrailTextureMode;
|
|
759
|
+
ratio: number;
|
|
760
|
+
lifetime: DynamicValue<number>;
|
|
761
|
+
minimumVertexDistance: number;
|
|
762
|
+
dieWithParticles: boolean;
|
|
763
|
+
ribbonCount: number;
|
|
764
|
+
width: DynamicValue<number>;
|
|
765
|
+
widthOverTrail: DynamicValue<number>;
|
|
766
|
+
sizeAffectsWidth: boolean;
|
|
767
|
+
sizeAffectsLifetime: boolean;
|
|
768
|
+
inheritParticleColor: boolean;
|
|
769
|
+
colorOverLifetime: DynamicValue<THREE.Color>;
|
|
770
|
+
colorOverTrail: DynamicValue<THREE.Color>;
|
|
771
|
+
material: THREE.Material;
|
|
772
|
+
materialOptions: THREE.MeshStandardMaterialParameters;
|
|
773
|
+
castShadow: boolean;
|
|
774
|
+
receiveShadow: boolean;
|
|
775
|
+
}
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
Builds per-particle trails or ribbon trails from particle movement.
|
|
779
|
+
|
|
780
|
+
### LightRenderer
|
|
781
|
+
|
|
782
|
+
```ts
|
|
783
|
+
new LightRenderer(options?: Partial<LightRendererOptions>)
|
|
784
|
+
|
|
785
|
+
interface PointLightOptions {
|
|
786
|
+
color?: THREE.Color | string | number;
|
|
787
|
+
intensity?: number;
|
|
788
|
+
distance?: number;
|
|
789
|
+
decay?: number;
|
|
790
|
+
power?: number;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
interface LightRendererOptions extends RendererOptions {
|
|
794
|
+
brightness: DynamicValue<number>;
|
|
795
|
+
rangeMultiplier: DynamicValue<number>;
|
|
796
|
+
groupingRadiusRatio: number;
|
|
797
|
+
decay: number;
|
|
798
|
+
count: number;
|
|
799
|
+
ratio: number;
|
|
800
|
+
randomDistribution: boolean;
|
|
801
|
+
inheritParticleColor: boolean;
|
|
802
|
+
sizeAffectsRange: boolean;
|
|
803
|
+
alphaAffectsIntensity: boolean;
|
|
804
|
+
lightOptions: PointLightOptions;
|
|
805
|
+
}
|
|
806
|
+
```
|
|
807
|
+
|
|
808
|
+
Maps particles or particle groups to point lights. Use `count` and `ratio` to
|
|
809
|
+
control how many particles can create lights.
|
|
810
|
+
|
|
811
|
+
## Force Fields
|
|
812
|
+
|
|
813
|
+
`ParticleForceField` is a `THREE.Object3D` that can be placed in the scene and
|
|
814
|
+
sampled by `ExternalForces`.
|
|
815
|
+
|
|
816
|
+
```ts
|
|
817
|
+
interface ForceFieldOptions {
|
|
818
|
+
position: THREE.Vector3;
|
|
819
|
+
direction: DynamicValue<THREE.Vector3>;
|
|
820
|
+
gravity: DynamicValue<number>;
|
|
821
|
+
rotationSpeed: DynamicValue<number>;
|
|
822
|
+
rotationAttraction: DynamicValue<number>;
|
|
823
|
+
drag: DynamicValue<number>;
|
|
824
|
+
scale: THREE.Vector3;
|
|
825
|
+
geometry: THREE.BufferGeometry;
|
|
826
|
+
tags: StrictMultiple<Tag>;
|
|
827
|
+
}
|
|
828
|
+
```
|
|
829
|
+
|
|
830
|
+
Helpers:
|
|
831
|
+
|
|
832
|
+
```ts
|
|
833
|
+
ParticleForceField.Box(options?, ...boxGeometryArgs)
|
|
834
|
+
ParticleForceField.Sphere(options?, ...sphereGeometryArgs)
|
|
835
|
+
ParticleForceField.Cone(options?, ...coneGeometryArgs)
|
|
836
|
+
ParticleForceField.Torus(options?, ...torusGeometryArgs)
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
Example:
|
|
840
|
+
|
|
841
|
+
```ts
|
|
842
|
+
const attractor = ParticleForceField.Sphere({
|
|
843
|
+
gravity: 10,
|
|
844
|
+
drag: 0.2,
|
|
845
|
+
scale: new THREE.Vector3(4, 4, 4),
|
|
846
|
+
tags: "attractor",
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
scene.add(attractor);
|
|
850
|
+
|
|
851
|
+
system.addModule(
|
|
852
|
+
new ExternalForces({
|
|
853
|
+
forceFieldFilter: (field) => field.tags?.includes("attractor") ?? false,
|
|
854
|
+
}),
|
|
855
|
+
);
|
|
856
|
+
```
|
|
857
|
+
|
|
858
|
+
Use `ParticleForceFieldHelper` to visualize a field while tuning.
|
|
859
|
+
|
|
860
|
+
## Collision And Physics
|
|
861
|
+
|
|
862
|
+
The base package includes `ThreeCollisionBackend`, which raycasts against a
|
|
863
|
+
Three.js scene.
|
|
864
|
+
|
|
865
|
+
```ts
|
|
866
|
+
interface ThreeCollisionBackendOptions {
|
|
867
|
+
world?: THREE.Object3D;
|
|
868
|
+
staticObjects?: THREE.Object3D[];
|
|
869
|
+
dynamicObjects?: THREE.Object3D[];
|
|
870
|
+
|
|
871
|
+
// How long must an object remain still to be considered static?
|
|
872
|
+
// Defaults to 2 seconds.
|
|
873
|
+
staticAfter?: number;
|
|
874
|
+
|
|
875
|
+
// How frequently is the dynamic octree rebuilt?
|
|
876
|
+
// Defaults to 1 => once per update.
|
|
877
|
+
timeQuality?: number;
|
|
878
|
+
|
|
879
|
+
// How frequently is the scene scanned for new objects?
|
|
880
|
+
// Defaults to 1 => once per update.
|
|
881
|
+
refreshQuality?: number;
|
|
882
|
+
|
|
883
|
+
objectFilter?: (object: THREE.Object3D) => boolean;
|
|
884
|
+
staticObjectFilter?: (object: THREE.Object3D) => boolean;
|
|
885
|
+
dynamicObjectFilter?: (object: THREE.Object3D) => boolean;
|
|
886
|
+
}
|
|
887
|
+
```
|
|
888
|
+
|
|
889
|
+
The `ThreeCollisionBackend` uses two octrees for static and dynamic collisions.
|
|
890
|
+
Sorting objects into static and dynamic can be done automatically, or with
|
|
891
|
+
supporting identifiers passed into the constructor options. Explicitly stated
|
|
892
|
+
`staticObjects` and `dynamicObjects` lists are treated as the source of truth if
|
|
893
|
+
present.
|
|
894
|
+
|
|
895
|
+
If explicit lists are not provided, the scene is traversed and the
|
|
896
|
+
`staticObjectFilter`, `dynamicObjectFilter`, and `objectFilter` predicates are
|
|
897
|
+
used to select objects. `objectFilter` selects whether objects are included at
|
|
898
|
+
all but does not decide what is considered static or dynamic.
|
|
899
|
+
|
|
900
|
+
If none of these predicates are provided, the octrees are automatically
|
|
901
|
+
constructed from the scene traversal, with distinctions between static and
|
|
902
|
+
dynamic objects being made based on movement in the scene.
|
|
903
|
+
|
|
904
|
+
For external physics engines, use the extension packages:
|
|
905
|
+
|
|
906
|
+
| Package | Engine | npm |
|
|
907
|
+
| -------------- | ------------ | ---------------------------------------------------------------------------- |
|
|
908
|
+
| `@rzmps/rapier` | Rapier | [npmjs.com/package/@rzmps/rapier](https://www.npmjs.com/package/@rzmps/rapier) |
|
|
909
|
+
| `@rzmps/jolt` | Jolt Physics | [npmjs.com/package/@rzmps/jolt](https://www.npmjs.com/package/@rzmps/jolt) |
|
|
910
|
+
| `@rzmps/ammo` | Ammo.js | [npmjs.com/package/@rzmps/ammo](https://www.npmjs.com/package/@rzmps/ammo) |
|
|
911
|
+
|
|
912
|
+
```ts
|
|
913
|
+
import { Collision } from "rzmps";
|
|
914
|
+
import { RapierCollisionBackend } from "@rzmps/rapier";
|
|
915
|
+
|
|
916
|
+
const collision = new Collision({
|
|
917
|
+
backend: new RapierCollisionBackend({ RAPIER, world }),
|
|
918
|
+
bounce: 0.6,
|
|
919
|
+
dampen: 0.1,
|
|
920
|
+
radiusScale: 1,
|
|
921
|
+
lifetimeLoss: 0.2,
|
|
922
|
+
applyImpulses: true,
|
|
923
|
+
});
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
Physics backends receive an existing physics world. They do not create, step, or
|
|
927
|
+
own the physics simulation.
|
|
928
|
+
|
|
929
|
+
## Custom Modules
|
|
930
|
+
|
|
931
|
+
The `Module` class is intentionally small. You can create one directly by
|
|
932
|
+
passing a custom particle modify function.
|
|
933
|
+
|
|
934
|
+
### Constructor Function
|
|
935
|
+
|
|
936
|
+
```ts
|
|
937
|
+
import { Module } from "rzmps";
|
|
938
|
+
|
|
939
|
+
const upwardDrift = new Module((particle, deltaTime) => {
|
|
940
|
+
particle.velocity.y += 0.5 * deltaTime;
|
|
941
|
+
}, {
|
|
942
|
+
priority: -1,
|
|
943
|
+
tags: "smoke",
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
system.addModule(upwardDrift);
|
|
947
|
+
```
|
|
948
|
+
|
|
949
|
+
### Class Extension
|
|
950
|
+
|
|
951
|
+
For more involved behavior, subclass `Module`. This is useful when a module
|
|
952
|
+
needs named options, cached state, dependent modules, setup work, or cleanup.
|
|
953
|
+
|
|
954
|
+
```ts
|
|
955
|
+
import * as THREE from "three";
|
|
956
|
+
import { Module, ModuleOptions, ParticleSystem } from "rzmps";
|
|
957
|
+
|
|
958
|
+
interface GustModuleOptions extends Partial<ModuleOptions> {
|
|
959
|
+
strength?: number;
|
|
960
|
+
direction?: THREE.Vector3;
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
class GustModule extends Module {
|
|
964
|
+
strength: number;
|
|
965
|
+
direction: THREE.Vector3;
|
|
966
|
+
|
|
967
|
+
private elapsed = 0;
|
|
968
|
+
|
|
969
|
+
constructor(options: GustModuleOptions = {}) {
|
|
970
|
+
super((particle, deltaTime) => {
|
|
971
|
+
const pulse = 0.5 + Math.sin(this.elapsed * 4) * 0.5;
|
|
972
|
+
|
|
973
|
+
particle.velocity.addScaledVector(
|
|
974
|
+
this.direction,
|
|
975
|
+
this.strength * pulse * deltaTime,
|
|
976
|
+
);
|
|
977
|
+
}, options);
|
|
978
|
+
|
|
979
|
+
this.strength = options.strength ?? 1;
|
|
980
|
+
this.direction = options.direction ?? new THREE.Vector3(1, 0, 0);
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
prepare(system: ParticleSystem, deltaTime: number) {
|
|
984
|
+
this.elapsed += deltaTime;
|
|
985
|
+
this.direction.normalize();
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
cleanup() {
|
|
989
|
+
// Release external resources here.
|
|
990
|
+
}
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
system.addModule(
|
|
994
|
+
new GustModule({
|
|
995
|
+
direction: new THREE.Vector3(1, 0.2, 0),
|
|
996
|
+
tags: "smoke",
|
|
997
|
+
}),
|
|
998
|
+
);
|
|
999
|
+
```
|
|
1000
|
+
|
|
1001
|
+
Use `prepare(system, deltaTime)` for once-per-frame setup, `dependents` for
|
|
1002
|
+
module chains that must run together, and `cleanup()` for external resources.
|
|
1003
|
+
|
|
1004
|
+
## Developer Guide
|
|
1005
|
+
|
|
1006
|
+
Clone the repository and install dependencies:
|
|
1007
|
+
|
|
1008
|
+
```bash
|
|
1009
|
+
git clone https://github.com/rzmay/rzmps.git
|
|
1010
|
+
cd rzmps
|
|
1011
|
+
npm install
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
Build the core package:
|
|
1015
|
+
|
|
1016
|
+
```bash
|
|
1017
|
+
npm run build --workspace packages/rzmps
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
Run the package in watch mode:
|
|
1021
|
+
|
|
1022
|
+
```bash
|
|
1023
|
+
npm run dev --workspace packages/rzmps
|
|
1024
|
+
```
|
|
1025
|
+
|
|
1026
|
+
Run the demo locally:
|
|
1027
|
+
|
|
1028
|
+
```bash
|
|
1029
|
+
npm start --workspace packages/demo
|
|
1030
|
+
```
|
|
1031
|
+
|
|
1032
|
+
Build the demo:
|
|
1033
|
+
|
|
1034
|
+
```bash
|
|
1035
|
+
npm run build --workspace packages/demo
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1038
|
+
Build the physics extensions:
|
|
1039
|
+
|
|
1040
|
+
```bash
|
|
1041
|
+
npm run build --workspace @rzmps/rapier
|
|
1042
|
+
npm run build --workspace @rzmps/jolt
|
|
1043
|
+
npm run build --workspace @rzmps/ammo
|
|
1044
|
+
```
|
|
1045
|
+
|
|
1046
|
+
## License
|
|
1047
|
+
|
|
1048
|
+
MIT
|