sparcoon 0.5.0 → 0.6.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 +6 -6
- package/dist/emitter/FXEmitter.Internal.d.ts +55 -0
- package/dist/emitter/FXEmitter.d.ts +49 -0
- package/dist/emitter/FXEmitter.js +64 -0
- package/dist/emitter/behavior/FXBehavior.d.ts +12 -2
- package/dist/emitter/behavior/FXBehavior.js +3 -2
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.d.ts +25 -0
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.js +74 -0
- package/dist/emitter/behavior/FXBehaviorPointForce.d.ts +40 -0
- package/dist/emitter/behavior/FXBehaviorPointForce.js +98 -0
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.d.ts +9 -8
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.js +12 -29
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.d.ts +4 -3
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.js +3 -2
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.d.ts +4 -3
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.js +3 -2
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.d.ts +17 -0
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.js +34 -0
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.d.ts +3 -2
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.js +3 -2
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.d.ts +6 -5
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.js +6 -5
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.d.ts +20 -0
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.js +48 -0
- package/dist/emitter/spawn/FXSpawn.d.ts +4 -2
- package/dist/emitter/spawn/FXSpawn.js +3 -2
- package/dist/emitter/spawn/FXSpawnBox.d.ts +8 -16
- package/dist/emitter/spawn/FXSpawnBox.js +23 -39
- package/dist/emitter/spawn/{FXSpawnRandomLifetime.d.ts → FXSpawnLifetime.d.ts} +3 -5
- package/dist/emitter/spawn/{FXSpawnRandomLifetime.js → FXSpawnLifetime.js} +8 -10
- package/dist/emitter/spawn/FXSpawnOffset.d.ts +9 -0
- package/dist/emitter/spawn/FXSpawnOffset.js +9 -0
- package/dist/emitter/spawn/FXSpawnPoint.d.ts +8 -0
- package/dist/emitter/spawn/FXSpawnPoint.js +8 -0
- package/dist/emitter/spawn/{FXSpawnRandomRotation.d.ts → FXSpawnRotation.d.ts} +3 -5
- package/dist/emitter/spawn/{FXSpawnRandomRotation.js → FXSpawnRotation.js} +8 -10
- package/dist/emitter/spawn/{FXSpawnRandomScale.d.ts → FXSpawnScale.d.ts} +4 -6
- package/dist/emitter/spawn/{FXSpawnRandomScale.js → FXSpawnScale.js} +12 -14
- package/dist/emitter/spawn/FXSpawnSphere.d.ts +11 -6
- package/dist/emitter/spawn/FXSpawnSphere.js +11 -6
- package/dist/emitter/spawn/{FXSpawnRandomTorque.d.ts → FXSpawnTorque.d.ts} +3 -5
- package/dist/emitter/spawn/{FXSpawnRandomTorque.js → FXSpawnTorque.js} +8 -10
- package/dist/emitter/spawn/FXSpawnVelocity.d.ts +32 -0
- package/dist/emitter/spawn/{FXSpawnRandomVelocity.js → FXSpawnVelocity.js} +26 -11
- package/dist/index.d.ts +23 -16
- package/dist/index.js +20 -16
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +4 -4
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +2 -2
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.d.ts +55 -5
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +9 -2
- package/dist/materials/FXMaterial/FXDepthMaterial.Internal.d.ts +3 -3
- package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.d.ts +3 -3
- package/dist/materials/FXMaterial/FXMaterial.d.ts +51 -5
- package/dist/materials/FXMaterial/FXMaterial.js +5 -0
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +3 -3
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.d.ts +3 -3
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +3 -3
- package/dist/miscellaneous/curve/FXCurve1D.Internal.d.ts +20 -0
- package/dist/miscellaneous/curve/FXCurve1D.d.ts +17 -0
- package/dist/miscellaneous/curve/FXCurve1D.js +94 -0
- package/dist/miscellaneous/miscellaneous.d.ts +3 -1
- package/dist/miscellaneous/miscellaneous.js +8 -4
- package/dist/miscellaneous/texture/FXTextureView.Internal.js +6 -0
- package/dist/miscellaneous/texture/FXTextureView.js +234 -0
- package/dist/nodes/color/FXNodeColor.d.ts +7 -0
- package/dist/nodes/color/FXNodeColor.js +11 -0
- package/dist/nodes/color/FXNodeColorOverLife.d.ts +25 -0
- package/dist/nodes/color/FXNodeColorOverLife.js +60 -0
- package/dist/nodes/color/FXNodeSphericalClip.d.ts +18 -0
- package/dist/nodes/color/FXNodeSphericalClip.js +45 -0
- package/dist/nodes/normal/FXNodeFlatNormal.d.ts +7 -0
- package/dist/nodes/normal/FXNodeFlatNormal.js +17 -0
- package/dist/nodes/normal/FXNodeNormal.d.ts +7 -0
- package/dist/nodes/normal/FXNodeNormal.js +7 -0
- package/dist/nodes/normal/FXNodeSphericalNormal.d.ts +7 -0
- package/dist/nodes/normal/{FXSphericalNormalNode.js → FXNodeSphericalNormal.js} +7 -4
- package/dist/nodes/texture/FXNodeAnimatedTexture.d.ts +47 -0
- package/dist/nodes/texture/FXNodeAnimatedTexture.js +137 -0
- package/dist/nodes/texture/FXNodeStaticTexture.d.ts +18 -0
- package/dist/nodes/texture/FXNodeStaticTexture.js +60 -0
- package/dist/nodes/texture/FXNodeTexture.d.ts +7 -0
- package/dist/nodes/texture/FXNodeTexture.js +11 -0
- package/package.json +2 -4
- package/dist/emitter/behavior/FXBehaviorDirectionalGravity.d.ts +0 -21
- package/dist/emitter/behavior/FXBehaviorDirectionalGravity.js +0 -51
- package/dist/emitter/behavior/FXBehaviorPointGravity.d.ts +0 -39
- package/dist/emitter/behavior/FXBehaviorPointGravity.js +0 -116
- package/dist/emitter/spawn/FXSpawnRandomVelocity.d.ts +0 -17
- package/dist/nodes/color/FXColorNode.d.ts +0 -3
- package/dist/nodes/color/FXColorNode.js +0 -8
- package/dist/nodes/color/FXColorOverLifeNode.d.ts +0 -6
- package/dist/nodes/color/FXColorOverLifeNode.js +0 -44
- package/dist/nodes/color/FXSphericalClipNode.d.ts +0 -5
- package/dist/nodes/color/FXSphericalClipNode.js +0 -30
- package/dist/nodes/normal/FXFlatNormalNode.d.ts +0 -4
- package/dist/nodes/normal/FXFlatNormalNode.js +0 -14
- package/dist/nodes/normal/FXNormalNode.d.ts +0 -3
- package/dist/nodes/normal/FXNormalNode.js +0 -4
- package/dist/nodes/normal/FXSphericalNormalNode.d.ts +0 -4
- package/dist/nodes/texture/FXAnimatedTextureNode.d.ts +0 -12
- package/dist/nodes/texture/FXAnimatedTextureNode.js +0 -88
- package/dist/nodes/texture/FXStaticTextureNode.d.ts +0 -6
- package/dist/nodes/texture/FXStaticTextureNode.js +0 -34
- package/dist/nodes/texture/FXTextureNode.d.ts +0 -3
- package/dist/nodes/texture/FXTextureNode.js +0 -8
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ Interactive visual **[EDITOR](https://jango-git.github.io/sparcoon/)**
|
|
|
15
15
|
|
|
16
16
|
- **FXEmitter** extends `Object3D`. Manages emission, lifetime, pooling, rendering. One draw call per emitter. Optional back-to-front sorting.
|
|
17
17
|
- **Spawn modules** set initial particle state: position (box/sphere/point), velocity (cone), rotation, scale, lifetime. Numeric params accept ranges.
|
|
18
|
-
- **Behavior modules** update particles per frame: directional/point
|
|
18
|
+
- **Behavior modules** update particles per frame: directional/point force, velocity/torque damping, simplex noise, scale-over-life, velocity/torque over lifetime.
|
|
19
19
|
- **Materials**: `FXUnlitMaterial` (MeshBasicMaterial), `FXDiffuseMaterial` (MeshLambertMaterial + optional subsurface scatter). Shadow support.
|
|
20
20
|
- **Nodes** are composable material inputs: color-over-life, static/animated textures, normals (flat/spherical/map).
|
|
21
21
|
|
|
@@ -30,9 +30,9 @@ const LIFETIME = 6;
|
|
|
30
30
|
const smoke = new FXEmitter(
|
|
31
31
|
[
|
|
32
32
|
new FXSpawnBox(areaMin, areaMax),
|
|
33
|
-
new
|
|
34
|
-
new
|
|
35
|
-
new
|
|
33
|
+
new FXSpawnLifetime({ min: LIFETIME * 0.5, max: LIFETIME }),
|
|
34
|
+
new FXSpawnScale({ min: 8, max: 16 }),
|
|
35
|
+
new FXSpawnRotation(),
|
|
36
36
|
],
|
|
37
37
|
[],
|
|
38
38
|
new FXDiffuseMaterial({
|
|
@@ -43,13 +43,13 @@ const smoke = new FXEmitter(
|
|
|
43
43
|
shadowSensitivity: 0.25,
|
|
44
44
|
premultipliedAlpha: true,
|
|
45
45
|
albedoNodes: [
|
|
46
|
-
new
|
|
46
|
+
new FXNodeAnimatedTexture({
|
|
47
47
|
texture: smokeAtlas,
|
|
48
48
|
rows: 8,
|
|
49
49
|
columns: 8,
|
|
50
50
|
interpolate: true,
|
|
51
51
|
}),
|
|
52
|
-
new
|
|
52
|
+
new FXNodeColorOverLife([
|
|
53
53
|
new FXColor(0xffffff, 0),
|
|
54
54
|
new FXColor(0xffffff, 0.4),
|
|
55
55
|
new FXColor(0xffffff, 0.4),
|
|
@@ -1,20 +1,75 @@
|
|
|
1
1
|
import type { Camera } from "three";
|
|
2
2
|
import type { FXPropertyName, GLTypeInfo } from "../instancedParticle/shared";
|
|
3
3
|
import type { FXEmitter } from "./FXEmitter";
|
|
4
|
+
/**
|
|
5
|
+
* Configuration options for {@link FXEmitter}
|
|
6
|
+
*/
|
|
4
7
|
export interface FXEmitterOptions {
|
|
8
|
+
/**
|
|
9
|
+
* Initial buffer size in particles; grows automatically when exceeded
|
|
10
|
+
*
|
|
11
|
+
* @defaultValue `32`
|
|
12
|
+
*/
|
|
5
13
|
expectedCapacity: number;
|
|
14
|
+
/**
|
|
15
|
+
* Growth increment added to the buffer when capacity is reached
|
|
16
|
+
*
|
|
17
|
+
* @defaultValue `32`
|
|
18
|
+
*/
|
|
6
19
|
capacityStep: number;
|
|
20
|
+
/**
|
|
21
|
+
* When `true`, {@link FXEmitter.destroy} also calls `destroy()` on all modules and the material
|
|
22
|
+
*
|
|
23
|
+
* @defaultValue `true`
|
|
24
|
+
*/
|
|
7
25
|
automaticallyDestroyModules: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Enables shadow casting
|
|
28
|
+
*
|
|
29
|
+
* @defaultValue `false`
|
|
30
|
+
*/
|
|
8
31
|
castShadow: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Enables shadow receiving
|
|
34
|
+
*
|
|
35
|
+
* @defaultValue `false`
|
|
36
|
+
*/
|
|
9
37
|
receiveShadow: boolean;
|
|
38
|
+
/** Camera used for back-to-front depth sorting; omit to disable sorting */
|
|
10
39
|
sortCamera: Camera;
|
|
40
|
+
/**
|
|
41
|
+
* Fraction of frames on which sorting runs (`1` = every frame, `0.1` = ~every 10th frame)
|
|
42
|
+
*
|
|
43
|
+
* @defaultValue `0.1`
|
|
44
|
+
*/
|
|
11
45
|
sortFraction: number;
|
|
12
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Options for {@link FXEmitter.burst}
|
|
49
|
+
*/
|
|
13
50
|
export interface FXEmitterBurstOptions {
|
|
51
|
+
/**
|
|
52
|
+
* Delay in seconds before particles are spawned
|
|
53
|
+
*
|
|
54
|
+
* @defaultValue `0`
|
|
55
|
+
*/
|
|
14
56
|
delay: number;
|
|
15
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Options for {@link FXEmitter.play}
|
|
60
|
+
*/
|
|
16
61
|
export interface FXEmitterPlayOptions {
|
|
62
|
+
/**
|
|
63
|
+
* Delay in seconds before emission starts
|
|
64
|
+
*
|
|
65
|
+
* @defaultValue `0`
|
|
66
|
+
*/
|
|
17
67
|
delay: number;
|
|
68
|
+
/**
|
|
69
|
+
* Total emission duration in seconds
|
|
70
|
+
*
|
|
71
|
+
* @defaultValue `Infinity`
|
|
72
|
+
*/
|
|
18
73
|
duration: number;
|
|
19
74
|
}
|
|
20
75
|
export declare const EMITTERS: FXEmitter[];
|
|
@@ -4,11 +4,21 @@ import type { FXMaterial } from "../materials/FXMaterial/FXMaterial";
|
|
|
4
4
|
import type { FXBehavior } from "./behavior/FXBehavior";
|
|
5
5
|
import type { FXEmitterBurstOptions, FXEmitterOptions, FXEmitterPlayOptions } from "./FXEmitter.Internal";
|
|
6
6
|
import type { FXSpawn } from "./spawn/FXSpawn";
|
|
7
|
+
/**
|
|
8
|
+
* Particle emitter - add to a scene to make particles visible
|
|
9
|
+
*
|
|
10
|
+
* @remarks
|
|
11
|
+
* Drives a pipeline of spawn modules (run once at particle birth) and behavior modules
|
|
12
|
+
* (run every frame). Call {@link FXEmitter.onWillRender} each frame to tick the system.
|
|
13
|
+
*/
|
|
7
14
|
export declare class FXEmitter extends Object3D {
|
|
8
15
|
private readonly spawnSequence;
|
|
9
16
|
private readonly behaviorSequence;
|
|
17
|
+
/** When `true`, {@link FXEmitter.destroy} also calls `destroy()` on all modules and the material; defaults to `true` */
|
|
10
18
|
automaticallyDestroyModules: boolean;
|
|
19
|
+
/** Camera used for back-to-front depth sorting; `undefined` disables sorting */
|
|
11
20
|
sortCamera?: Camera;
|
|
21
|
+
/** Fraction of frames on which sorting runs (`1` = every frame, `0.1` = ~every 10th frame); defaults to `0.1` */
|
|
12
22
|
sortFraction: number;
|
|
13
23
|
private readonly mesh;
|
|
14
24
|
private readonly material;
|
|
@@ -17,13 +27,52 @@ export declare class FXEmitter extends Object3D {
|
|
|
17
27
|
private readonly activePlays;
|
|
18
28
|
private sortingAccumulator;
|
|
19
29
|
private readonly sortingCameraWorldPosition;
|
|
30
|
+
/**
|
|
31
|
+
* @param spawnSequence - Modules executed in order when particles are born
|
|
32
|
+
* @param behaviorSequence - Modules executed in order every frame for all live particles
|
|
33
|
+
* @param material - Particle material
|
|
34
|
+
* @param options - Emitter configuration
|
|
35
|
+
*/
|
|
20
36
|
constructor(spawnSequence: FXSpawn[], behaviorSequence: readonly FXBehavior[], material: FXMaterial, options?: Partial<FXEmitterOptions>);
|
|
37
|
+
/**
|
|
38
|
+
* Ticks all active emitters; call once per frame before rendering
|
|
39
|
+
*
|
|
40
|
+
* @param deltaTime - Elapsed time in seconds since the last frame
|
|
41
|
+
*/
|
|
21
42
|
static onWillRender: (deltaTime: number) => void;
|
|
43
|
+
/** Unregisters the emitter, removes it from the scene, and disposes modules and material when {@link automaticallyDestroyModules} is `true` */
|
|
22
44
|
destroy(): void;
|
|
45
|
+
/**
|
|
46
|
+
* Spawns `count` particles immediately, or after a delay when `options.delay` is set
|
|
47
|
+
*
|
|
48
|
+
* @param count - Number of particles to spawn
|
|
49
|
+
* @param options - Burst options
|
|
50
|
+
* @returns Handler that can be passed to {@link stop} to cancel a pending delayed burst
|
|
51
|
+
*/
|
|
23
52
|
burst(count: number, options?: Partial<FXEmitterBurstOptions>): number;
|
|
53
|
+
/**
|
|
54
|
+
* Starts continuous emission at the given rate
|
|
55
|
+
*
|
|
56
|
+
* @param rate - Particles per second
|
|
57
|
+
* @param options - Play options
|
|
58
|
+
* @returns Handler that can be passed to {@link stop} to cancel
|
|
59
|
+
*/
|
|
24
60
|
play(rate: number, options?: Partial<FXEmitterPlayOptions>): number;
|
|
61
|
+
/**
|
|
62
|
+
* Stops an active play or pending burst by handler; stops all when called with no arguments
|
|
63
|
+
*
|
|
64
|
+
* @param handler - Handler returned by {@link play} or {@link burst}; omit to stop everything
|
|
65
|
+
* @returns `true` if anything was stopped
|
|
66
|
+
*/
|
|
25
67
|
stop(handler?: number): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Simulates the emitter forward in time to avoid a cold-start on frame 0
|
|
70
|
+
*
|
|
71
|
+
* @param duration - Total time to simulate in seconds
|
|
72
|
+
* @param stepDuration - Maximum simulation step size in seconds. Defaults to `1/60`
|
|
73
|
+
*/
|
|
26
74
|
prewarm(duration: number, stepDuration?: number): void;
|
|
75
|
+
/** Kills all live particles and cancels all active plays and pending bursts */
|
|
27
76
|
reset(): void;
|
|
28
77
|
private tick;
|
|
29
78
|
private readonly onRendering;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { Object3D, Vector3 } from 'three';
|
|
2
|
+
import { assertValidPositiveNumber, assertValidNonNegativeNumber } from '../miscellaneous/asserts.js';
|
|
2
3
|
import { FXInstancedParticle } from '../instancedParticle/FXInstancedParticle.js';
|
|
3
4
|
import { resolveGLSLTypeInfo } from '../instancedParticle/shared.js';
|
|
4
5
|
import { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_VELOCITY_Z, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C } from '../miscellaneous/miscellaneous.js';
|
|
5
6
|
import { EMITTERS, collectProperties, EMITTER_DEFAULT_EXPECTED_CAPACITY, EMITTER_DEFAULT_CAPACITY_STEP, EMITTER_DEFAULT_CAST_SHADOW, EMITTER_DEFAULT_RECEIVE_SHADOW, EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES, EMITTER_DEFAULT_SORT_FRACTION, EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION, EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT } from './FXEmitter.Internal.js';
|
|
6
7
|
|
|
8
|
+
/**
|
|
9
|
+
* Particle emitter - add to a scene to make particles visible
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Drives a pipeline of spawn modules (run once at particle birth) and behavior modules
|
|
13
|
+
* (run every frame). Call {@link FXEmitter.onWillRender} each frame to tick the system.
|
|
14
|
+
*/
|
|
7
15
|
class FXEmitter extends Object3D {
|
|
16
|
+
/**
|
|
17
|
+
* @param spawnSequence - Modules executed in order when particles are born
|
|
18
|
+
* @param behaviorSequence - Modules executed in order every frame for all live particles
|
|
19
|
+
* @param material - Particle material
|
|
20
|
+
* @param options - Emitter configuration
|
|
21
|
+
*/
|
|
8
22
|
constructor(_private_spawnSequence, _private_behaviorSequence, material, options = {}) {
|
|
9
23
|
super();
|
|
10
24
|
this._private_spawnSequence = _private_spawnSequence;
|
|
@@ -25,6 +39,15 @@ class FXEmitter extends Object3D {
|
|
|
25
39
|
}
|
|
26
40
|
}
|
|
27
41
|
};
|
|
42
|
+
if (options.expectedCapacity !== undefined) {
|
|
43
|
+
assertValidPositiveNumber(options.expectedCapacity, "FXEmitter.constructor.options.expectedCapacity");
|
|
44
|
+
}
|
|
45
|
+
if (options.capacityStep !== undefined) {
|
|
46
|
+
assertValidPositiveNumber(options.capacityStep, "FXEmitter.constructor.options.capacityStep");
|
|
47
|
+
}
|
|
48
|
+
if (options.sortFraction !== undefined) {
|
|
49
|
+
assertValidNonNegativeNumber(options.sortFraction, "FXEmitter.constructor.options.sortFraction");
|
|
50
|
+
}
|
|
28
51
|
const collectedProperties = {
|
|
29
52
|
builtin: resolveGLSLTypeInfo("Matrix4"),
|
|
30
53
|
};
|
|
@@ -48,6 +71,7 @@ class FXEmitter extends Object3D {
|
|
|
48
71
|
this.add(this._private_mesh);
|
|
49
72
|
EMITTERS.push(this);
|
|
50
73
|
}
|
|
74
|
+
/** Unregisters the emitter, removes it from the scene, and disposes modules and material when {@link automaticallyDestroyModules} is `true` */
|
|
51
75
|
destroy() {
|
|
52
76
|
const index = EMITTERS.indexOf(this);
|
|
53
77
|
if (index !== -1) {
|
|
@@ -64,9 +88,18 @@ class FXEmitter extends Object3D {
|
|
|
64
88
|
}
|
|
65
89
|
this.removeFromParent();
|
|
66
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* Spawns `count` particles immediately, or after a delay when `options.delay` is set
|
|
93
|
+
*
|
|
94
|
+
* @param count - Number of particles to spawn
|
|
95
|
+
* @param options - Burst options
|
|
96
|
+
* @returns Handler that can be passed to {@link stop} to cancel a pending delayed burst
|
|
97
|
+
*/
|
|
67
98
|
burst(count, options = {}) {
|
|
99
|
+
assertValidPositiveNumber(count, "FXEmitter.burst.count");
|
|
68
100
|
const handler = this._private_nextHandler++;
|
|
69
101
|
const delay = options.delay ?? 0;
|
|
102
|
+
assertValidNonNegativeNumber(delay, "FXEmitter.burst.options.delay");
|
|
70
103
|
if (delay <= 0) {
|
|
71
104
|
this._private_spawnBurst(count);
|
|
72
105
|
}
|
|
@@ -75,7 +108,18 @@ class FXEmitter extends Object3D {
|
|
|
75
108
|
}
|
|
76
109
|
return handler;
|
|
77
110
|
}
|
|
111
|
+
/**
|
|
112
|
+
* Starts continuous emission at the given rate
|
|
113
|
+
*
|
|
114
|
+
* @param rate - Particles per second
|
|
115
|
+
* @param options - Play options
|
|
116
|
+
* @returns Handler that can be passed to {@link stop} to cancel
|
|
117
|
+
*/
|
|
78
118
|
play(rate, options = {}) {
|
|
119
|
+
assertValidPositiveNumber(rate, "FXEmitter.play.rate");
|
|
120
|
+
if (options.delay !== undefined) {
|
|
121
|
+
assertValidNonNegativeNumber(options.delay, "FXEmitter.play.options.delay");
|
|
122
|
+
}
|
|
79
123
|
const handler = this._private_nextHandler++;
|
|
80
124
|
this._private_activePlays.push({
|
|
81
125
|
handler,
|
|
@@ -87,6 +131,12 @@ class FXEmitter extends Object3D {
|
|
|
87
131
|
});
|
|
88
132
|
return handler;
|
|
89
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Stops an active play or pending burst by handler; stops all when called with no arguments
|
|
136
|
+
*
|
|
137
|
+
* @param handler - Handler returned by {@link play} or {@link burst}; omit to stop everything
|
|
138
|
+
* @returns `true` if anything was stopped
|
|
139
|
+
*/
|
|
90
140
|
stop(handler) {
|
|
91
141
|
if (handler === undefined) {
|
|
92
142
|
const hadAnything = this._private_pendingBursts.length > 0 || this._private_activePlays.length > 0;
|
|
@@ -106,7 +156,15 @@ class FXEmitter extends Object3D {
|
|
|
106
156
|
}
|
|
107
157
|
return false;
|
|
108
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Simulates the emitter forward in time to avoid a cold-start on frame 0
|
|
161
|
+
*
|
|
162
|
+
* @param duration - Total time to simulate in seconds
|
|
163
|
+
* @param stepDuration - Maximum simulation step size in seconds. Defaults to `1/60`
|
|
164
|
+
*/
|
|
109
165
|
prewarm(duration, stepDuration = EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION) {
|
|
166
|
+
assertValidPositiveNumber(duration, "FXEmitter.prewarm.duration");
|
|
167
|
+
assertValidPositiveNumber(stepDuration, "FXEmitter.prewarm.stepDuration");
|
|
110
168
|
const stepCount = Math.min(duration / Math.max(stepDuration, EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION), EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT);
|
|
111
169
|
const step = duration / stepCount;
|
|
112
170
|
let remaining = duration;
|
|
@@ -116,6 +174,7 @@ class FXEmitter extends Object3D {
|
|
|
116
174
|
remaining -= deltaTime;
|
|
117
175
|
}
|
|
118
176
|
}
|
|
177
|
+
/** Kills all live particles and cancels all active plays and pending bursts */
|
|
119
178
|
reset() {
|
|
120
179
|
this._private_pendingBursts.length = 0;
|
|
121
180
|
this._private_activePlays.length = 0;
|
|
@@ -213,6 +272,11 @@ class FXEmitter extends Object3D {
|
|
|
213
272
|
}
|
|
214
273
|
}
|
|
215
274
|
}
|
|
275
|
+
/**
|
|
276
|
+
* Ticks all active emitters; call once per frame before rendering
|
|
277
|
+
*
|
|
278
|
+
* @param deltaTime - Elapsed time in seconds since the last frame
|
|
279
|
+
*/
|
|
216
280
|
FXEmitter.onWillRender = (deltaTime) => {
|
|
217
281
|
for (const instance of EMITTERS) {
|
|
218
282
|
instance._private_onRendering(deltaTime);
|
|
@@ -1,14 +1,24 @@
|
|
|
1
1
|
import type { InstancedBufferAttribute } from "three";
|
|
2
2
|
import type { FXPropertyName } from "../../instancedParticle/shared";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
4
|
+
* Abstract base for particle behavior modules that mutate per-particle properties each frame
|
|
5
5
|
*
|
|
6
|
-
*
|
|
6
|
+
* @typeParam T - Map of local property keys to {@link FXPropertyName} identifiers,
|
|
7
|
+
* declaring which particle attributes this behavior reads or writes
|
|
7
8
|
*/
|
|
8
9
|
export declare abstract class FXBehavior<T extends Record<string, FXPropertyName> = Record<string, FXPropertyName>> {
|
|
10
|
+
/** Particle attributes required by this behavior */
|
|
9
11
|
abstract readonly requiredProperties: T;
|
|
12
|
+
/**
|
|
13
|
+
* Called once per frame to update all live particles
|
|
14
|
+
*
|
|
15
|
+
* @param properties - Buffer attributes keyed by `requiredProperties`
|
|
16
|
+
* @param instanceCount - Number of currently live particles
|
|
17
|
+
* @param deltaTime - Elapsed time since the last frame, in seconds
|
|
18
|
+
*/
|
|
10
19
|
abstract update(properties: {
|
|
11
20
|
[K in keyof T]: InstancedBufferAttribute;
|
|
12
21
|
}, instanceCount: number, deltaTime: number): void;
|
|
22
|
+
/** Releases any resources held by this behavior */
|
|
13
23
|
destroy?(): void;
|
|
14
24
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Abstract base for particle behavior modules that mutate per-particle properties each frame
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* @typeParam T - Map of local property keys to {@link FXPropertyName} identifiers,
|
|
5
|
+
* declaring which particle attributes this behavior reads or writes
|
|
5
6
|
*/
|
|
6
7
|
class FXBehavior {
|
|
7
8
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { type Vector3Like } from "../../miscellaneous/math";
|
|
2
|
+
import type { FXVector3Config } from "../../miscellaneous/miscellaneous";
|
|
3
|
+
import { FXBehavior } from "./FXBehavior";
|
|
4
|
+
/**
|
|
5
|
+
* Applies constant acceleration to all particles
|
|
6
|
+
*/
|
|
7
|
+
export declare class FXBehaviorDirectionalForce extends FXBehavior<{
|
|
8
|
+
builtin: "Matrix4";
|
|
9
|
+
}> {
|
|
10
|
+
private readonly directionInternal;
|
|
11
|
+
private magnitudeInternal;
|
|
12
|
+
/**
|
|
13
|
+
* @param direction - Direction vector (will be normalized)
|
|
14
|
+
* @param magnitude - Acceleration magnitude in units/second²
|
|
15
|
+
*/
|
|
16
|
+
constructor(direction: Vector3Like, magnitude: number);
|
|
17
|
+
/** Direction vector (normalized) */
|
|
18
|
+
get direction(): Vector3Like;
|
|
19
|
+
/** Acceleration magnitude in units/second² */
|
|
20
|
+
get magnitude(): number;
|
|
21
|
+
/** Direction vector (normalized) */
|
|
22
|
+
set direction(value: FXVector3Config);
|
|
23
|
+
/** Acceleration magnitude in units/second² */
|
|
24
|
+
set magnitude(value: number);
|
|
25
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { Vector3 } from 'three';
|
|
2
|
+
import { assertValidNumber } from '../../miscellaneous/asserts.js';
|
|
3
|
+
import { EPSILON } from '../../miscellaneous/math.js';
|
|
4
|
+
import { resolveFXVector3Config, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z } from '../../miscellaneous/miscellaneous.js';
|
|
5
|
+
import { FXBehavior } from './FXBehavior.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Applies constant acceleration to all particles
|
|
9
|
+
*/
|
|
10
|
+
class FXBehaviorDirectionalForce extends FXBehavior {
|
|
11
|
+
/**
|
|
12
|
+
* @param direction - Direction vector (will be normalized)
|
|
13
|
+
* @param magnitude - Acceleration magnitude in units/second²
|
|
14
|
+
*/
|
|
15
|
+
constructor(direction, magnitude) {
|
|
16
|
+
super();
|
|
17
|
+
/** @internal */
|
|
18
|
+
this.requiredProperties = { builtin: "Matrix4" };
|
|
19
|
+
const resolvedDirection = resolveFXVector3Config(direction);
|
|
20
|
+
assertValidNumber(resolvedDirection.x, "FXBehaviorDirectionalForce.direction.x");
|
|
21
|
+
assertValidNumber(resolvedDirection.y, "FXBehaviorDirectionalForce.direction.y");
|
|
22
|
+
assertValidNumber(resolvedDirection.z, "FXBehaviorDirectionalForce.direction.z");
|
|
23
|
+
assertValidNumber(magnitude, "FXBehaviorDirectionalForce.constructor.magnitude");
|
|
24
|
+
this._private_directionInternal = new Vector3(resolvedDirection.x, resolvedDirection.y, resolvedDirection.z);
|
|
25
|
+
if (this._private_directionInternal.lengthSq() <= EPSILON) {
|
|
26
|
+
throw new Error("FXBehaviorDirectionalForce.direction: vector cannot be zero");
|
|
27
|
+
}
|
|
28
|
+
this._private_directionInternal.normalize();
|
|
29
|
+
this._private_magnitudeInternal = magnitude;
|
|
30
|
+
}
|
|
31
|
+
/** Direction vector (normalized) */
|
|
32
|
+
get direction() {
|
|
33
|
+
return this._private_directionInternal;
|
|
34
|
+
}
|
|
35
|
+
/** Acceleration magnitude in units/second² */
|
|
36
|
+
get magnitude() {
|
|
37
|
+
return this._private_magnitudeInternal;
|
|
38
|
+
}
|
|
39
|
+
/** Direction vector (normalized) */
|
|
40
|
+
set direction(value) {
|
|
41
|
+
const resolved = resolveFXVector3Config(value);
|
|
42
|
+
assertValidNumber(resolved.x, "FXBehaviorDirectionalForce.direction.x");
|
|
43
|
+
assertValidNumber(resolved.y, "FXBehaviorDirectionalForce.direction.y");
|
|
44
|
+
assertValidNumber(resolved.z, "FXBehaviorDirectionalForce.direction.z");
|
|
45
|
+
this._private_directionInternal.set(resolved.x, resolved.y, resolved.z);
|
|
46
|
+
if (this._private_directionInternal.lengthSq() < EPSILON) {
|
|
47
|
+
throw new Error("FXBehaviorDirectionalForce.direction: vector cannot be zero");
|
|
48
|
+
}
|
|
49
|
+
this._private_directionInternal.normalize();
|
|
50
|
+
}
|
|
51
|
+
/** Acceleration magnitude in units/second² */
|
|
52
|
+
set magnitude(value) {
|
|
53
|
+
assertValidNumber(value, "FXBehaviorDirectionalForce.magnitude");
|
|
54
|
+
this._private_magnitudeInternal = value;
|
|
55
|
+
}
|
|
56
|
+
/** @internal */
|
|
57
|
+
update(properties, instanceCount, deltaTime) {
|
|
58
|
+
const { builtin } = properties;
|
|
59
|
+
const { array, itemSize } = builtin;
|
|
60
|
+
const scale = this._private_magnitudeInternal * deltaTime;
|
|
61
|
+
const offsetX = this._private_directionInternal.x * scale;
|
|
62
|
+
const offsetY = this._private_directionInternal.y * scale;
|
|
63
|
+
const offsetZ = this._private_directionInternal.z * scale;
|
|
64
|
+
for (let i = 0; i < instanceCount; i++) {
|
|
65
|
+
const itemOffset = i * itemSize;
|
|
66
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] += offsetX;
|
|
67
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] += offsetY;
|
|
68
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] += offsetZ;
|
|
69
|
+
}
|
|
70
|
+
builtin.needsUpdate = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export { FXBehaviorDirectionalForce };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Vector3Like } from "../../miscellaneous/math";
|
|
2
|
+
import type { FXVector3Config } from "../../miscellaneous/miscellaneous";
|
|
3
|
+
import { FXBehavior } from "./FXBehavior";
|
|
4
|
+
/**
|
|
5
|
+
* Applies attraction or repulsion toward a point
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Particles closer than `threshold` are unaffected.
|
|
9
|
+
*/
|
|
10
|
+
export declare class FXBehaviorPointForce extends FXBehavior<{
|
|
11
|
+
builtin: "Matrix4";
|
|
12
|
+
}> {
|
|
13
|
+
private centerInternal;
|
|
14
|
+
private strengthInternal;
|
|
15
|
+
private exponentInternal;
|
|
16
|
+
private thresholdInternal;
|
|
17
|
+
/**
|
|
18
|
+
* @param center - Force center position
|
|
19
|
+
* @param strength - Force multiplier
|
|
20
|
+
* @param exponent - Distance power exponent
|
|
21
|
+
* @param threshold - Minimum distance
|
|
22
|
+
*/
|
|
23
|
+
constructor(center: Vector3Like, strength: number, exponent?: number, threshold?: number);
|
|
24
|
+
/** Force center position */
|
|
25
|
+
get center(): Vector3Like;
|
|
26
|
+
/** Force multiplier */
|
|
27
|
+
get strength(): number;
|
|
28
|
+
/** Distance power exponent */
|
|
29
|
+
get exponent(): number;
|
|
30
|
+
/** Minimum distance */
|
|
31
|
+
get threshold(): number;
|
|
32
|
+
/** Force center position */
|
|
33
|
+
set center(value: FXVector3Config);
|
|
34
|
+
/** Force multiplier */
|
|
35
|
+
set strength(value: number);
|
|
36
|
+
/** Distance power exponent */
|
|
37
|
+
set exponent(value: number);
|
|
38
|
+
/** Minimum distance */
|
|
39
|
+
set threshold(value: number);
|
|
40
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { assertValidNumber } from '../../miscellaneous/asserts.js';
|
|
2
|
+
import { resolveFXVector3Config, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { FXBehavior } from './FXBehavior.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Applies attraction or repulsion toward a point
|
|
7
|
+
*
|
|
8
|
+
* @remarks
|
|
9
|
+
* Particles closer than `threshold` are unaffected.
|
|
10
|
+
*/
|
|
11
|
+
class FXBehaviorPointForce extends FXBehavior {
|
|
12
|
+
/**
|
|
13
|
+
* @param center - Force center position
|
|
14
|
+
* @param strength - Force multiplier
|
|
15
|
+
* @param exponent - Distance power exponent
|
|
16
|
+
* @param threshold - Minimum distance
|
|
17
|
+
*/
|
|
18
|
+
constructor(center, strength, exponent = 2, threshold = 0.1) {
|
|
19
|
+
super();
|
|
20
|
+
/** @internal */
|
|
21
|
+
this.requiredProperties = { builtin: "Matrix4" };
|
|
22
|
+
this._private_centerInternal = resolveFXVector3Config(center);
|
|
23
|
+
assertValidNumber(this._private_centerInternal.x, "FXBehaviorPointForce.constructor.center.x");
|
|
24
|
+
assertValidNumber(this._private_centerInternal.y, "FXBehaviorPointForce.constructor.center.y");
|
|
25
|
+
assertValidNumber(this._private_centerInternal.z, "FXBehaviorPointForce.constructor.center.z");
|
|
26
|
+
assertValidNumber(strength, "FXBehaviorPointForce.constructor.strength");
|
|
27
|
+
this._private_strengthInternal = strength;
|
|
28
|
+
assertValidNumber(exponent, "FXBehaviorPointForce.constructor.exponent");
|
|
29
|
+
this._private_exponentInternal = exponent;
|
|
30
|
+
assertValidNumber(threshold, "FXBehaviorPointForce.constructor.threshold");
|
|
31
|
+
this._private_thresholdInternal = threshold;
|
|
32
|
+
}
|
|
33
|
+
/** Force center position */
|
|
34
|
+
get center() {
|
|
35
|
+
return this._private_centerInternal;
|
|
36
|
+
}
|
|
37
|
+
/** Force multiplier */
|
|
38
|
+
get strength() {
|
|
39
|
+
return this._private_strengthInternal;
|
|
40
|
+
}
|
|
41
|
+
/** Distance power exponent */
|
|
42
|
+
get exponent() {
|
|
43
|
+
return this._private_exponentInternal;
|
|
44
|
+
}
|
|
45
|
+
/** Minimum distance */
|
|
46
|
+
get threshold() {
|
|
47
|
+
return this._private_thresholdInternal;
|
|
48
|
+
}
|
|
49
|
+
/** Force center position */
|
|
50
|
+
set center(value) {
|
|
51
|
+
this._private_centerInternal = resolveFXVector3Config(value);
|
|
52
|
+
assertValidNumber(this._private_centerInternal.x, "FXBehaviorPointForce.center.x");
|
|
53
|
+
assertValidNumber(this._private_centerInternal.y, "FXBehaviorPointForce.center.y");
|
|
54
|
+
assertValidNumber(this._private_centerInternal.z, "FXBehaviorPointForce.center.z");
|
|
55
|
+
}
|
|
56
|
+
/** Force multiplier */
|
|
57
|
+
set strength(value) {
|
|
58
|
+
assertValidNumber(value, "FXBehaviorPointForce.strength");
|
|
59
|
+
this._private_strengthInternal = value;
|
|
60
|
+
}
|
|
61
|
+
/** Distance power exponent */
|
|
62
|
+
set exponent(value) {
|
|
63
|
+
assertValidNumber(value, "FXBehaviorPointForce.exponent");
|
|
64
|
+
this._private_exponentInternal = value;
|
|
65
|
+
}
|
|
66
|
+
/** Minimum distance */
|
|
67
|
+
set threshold(value) {
|
|
68
|
+
assertValidNumber(value, "FXBehaviorPointForce.threshold");
|
|
69
|
+
this._private_thresholdInternal = value;
|
|
70
|
+
}
|
|
71
|
+
/** @internal */
|
|
72
|
+
update(properties, instanceCount, deltaTime) {
|
|
73
|
+
const { builtin } = properties;
|
|
74
|
+
const { array, itemSize } = builtin;
|
|
75
|
+
const thresholdSquared = this._private_thresholdInternal * this._private_thresholdInternal;
|
|
76
|
+
for (let i = 0; i < instanceCount; i++) {
|
|
77
|
+
const itemOffset = i * itemSize;
|
|
78
|
+
const dx = this._private_centerInternal.x - array[itemOffset + BUILTIN_OFFSET_POSITION_X];
|
|
79
|
+
const dy = this._private_centerInternal.y - array[itemOffset + BUILTIN_OFFSET_POSITION_Y];
|
|
80
|
+
const dz = this._private_centerInternal.z - array[itemOffset + BUILTIN_OFFSET_POSITION_Z];
|
|
81
|
+
const distanceSquared = dx * dx + dy * dy + dz * dz;
|
|
82
|
+
if (distanceSquared < thresholdSquared) {
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
const distance = Math.sqrt(distanceSquared);
|
|
86
|
+
const forceMagnitude = this._private_strengthInternal / Math.pow(distance, this._private_exponentInternal);
|
|
87
|
+
const directionX = dx / distance;
|
|
88
|
+
const directionY = dy / distance;
|
|
89
|
+
const directionZ = dz / distance;
|
|
90
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] += directionX * forceMagnitude * deltaTime;
|
|
91
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] += directionY * forceMagnitude * deltaTime;
|
|
92
|
+
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] += directionZ * forceMagnitude * deltaTime;
|
|
93
|
+
}
|
|
94
|
+
builtin.needsUpdate = true;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export { FXBehaviorPointForce };
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FXCurve1DConfig } from "../../miscellaneous/curve/FXCurve1D";
|
|
2
|
+
import { FXCurve1D } from "../../miscellaneous/curve/FXCurve1D";
|
|
3
|
+
import type { FXAspectConfig, FXRange } from "../../miscellaneous/miscellaneous";
|
|
2
4
|
import { FXBehavior } from "./FXBehavior";
|
|
3
5
|
/**
|
|
4
|
-
* Animates particle scale over lifetime
|
|
5
|
-
*
|
|
6
|
-
* Interpolates between scale anchors based on particle age. Requires at least 2 anchors.
|
|
6
|
+
* Animates particle scale over lifetime using a {@link FXCurve1D}
|
|
7
7
|
*/
|
|
8
8
|
export declare class FXBehaviorScaleOverLife extends FXBehavior<{
|
|
9
9
|
builtin: "Matrix4";
|
|
10
10
|
}> {
|
|
11
|
-
|
|
11
|
+
/** Active scale curve */
|
|
12
|
+
curve: FXCurve1D<FXRange>;
|
|
12
13
|
private aspectInternal;
|
|
13
14
|
/**
|
|
14
|
-
* @param
|
|
15
|
-
* @param aspect - Width/height ratio.
|
|
15
|
+
* @param curve - Scale curve; accepts a {@link FXCurve1D} instance or a `FXRange[]` shorthand
|
|
16
|
+
* @param aspect - Width/height ratio; must be positive. Defaults to `1`
|
|
16
17
|
*/
|
|
17
|
-
constructor(
|
|
18
|
+
constructor(curve: FXCurve1DConfig<FXRange>, aspect?: FXAspectConfig);
|
|
18
19
|
/** Width/height aspect ratio */
|
|
19
20
|
get aspect(): number;
|
|
20
21
|
/** Width/height aspect ratio */
|