sparcoon 0.5.0 → 0.6.1
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 +50 -2
- package/dist/emitter/FXEmitter.Internal.js +1 -2
- package/dist/emitter/FXEmitter.d.ts +48 -1
- package/dist/emitter/FXEmitter.js +74 -11
- 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,26 +1,74 @@
|
|
|
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;
|
|
7
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Enables shadow casting
|
|
22
|
+
*
|
|
23
|
+
* @defaultValue `false`
|
|
24
|
+
*/
|
|
8
25
|
castShadow: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Enables shadow receiving
|
|
28
|
+
*
|
|
29
|
+
* @defaultValue `false`
|
|
30
|
+
*/
|
|
9
31
|
receiveShadow: boolean;
|
|
32
|
+
/** Camera used for back-to-front depth sorting; omit to disable sorting */
|
|
10
33
|
sortCamera: Camera;
|
|
34
|
+
/**
|
|
35
|
+
* Fraction of frames on which sorting runs (`1` = every frame, `0.1` = ~every 10th frame)
|
|
36
|
+
*
|
|
37
|
+
* @defaultValue `0.1`
|
|
38
|
+
*/
|
|
11
39
|
sortFraction: number;
|
|
12
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Options for {@link FXEmitter.burst}
|
|
43
|
+
*/
|
|
13
44
|
export interface FXEmitterBurstOptions {
|
|
45
|
+
/**
|
|
46
|
+
* Delay in seconds before particles are spawned
|
|
47
|
+
*
|
|
48
|
+
* @defaultValue `0`
|
|
49
|
+
*/
|
|
14
50
|
delay: number;
|
|
15
51
|
}
|
|
52
|
+
/**
|
|
53
|
+
* Options for {@link FXEmitter.play}
|
|
54
|
+
*/
|
|
16
55
|
export interface FXEmitterPlayOptions {
|
|
56
|
+
/**
|
|
57
|
+
* Delay in seconds before emission starts
|
|
58
|
+
*
|
|
59
|
+
* @defaultValue `0`
|
|
60
|
+
*/
|
|
17
61
|
delay: number;
|
|
62
|
+
/**
|
|
63
|
+
* Total emission duration in seconds
|
|
64
|
+
*
|
|
65
|
+
* @defaultValue `Infinity`
|
|
66
|
+
*/
|
|
18
67
|
duration: number;
|
|
19
68
|
}
|
|
20
69
|
export declare const EMITTERS: FXEmitter[];
|
|
21
70
|
export declare const EMITTER_DEFAULT_EXPECTED_CAPACITY = 32;
|
|
22
71
|
export declare const EMITTER_DEFAULT_CAPACITY_STEP = 32;
|
|
23
|
-
export declare const EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES = true;
|
|
24
72
|
export declare const EMITTER_DEFAULT_CAST_SHADOW = false;
|
|
25
73
|
export declare const EMITTER_DEFAULT_RECEIVE_SHADOW = false;
|
|
26
74
|
export declare const EMITTER_DEFAULT_SORT_FRACTION: number;
|
|
@@ -3,7 +3,6 @@ import { resolveGLSLTypeInfo } from '../instancedParticle/shared.js';
|
|
|
3
3
|
const EMITTERS = new Array();
|
|
4
4
|
const EMITTER_DEFAULT_EXPECTED_CAPACITY = 32;
|
|
5
5
|
const EMITTER_DEFAULT_CAPACITY_STEP = 32;
|
|
6
|
-
const EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES = true;
|
|
7
6
|
const EMITTER_DEFAULT_CAST_SHADOW = false;
|
|
8
7
|
const EMITTER_DEFAULT_RECEIVE_SHADOW = false;
|
|
9
8
|
const EMITTER_DEFAULT_SORT_FRACTION = 1 / 10;
|
|
@@ -27,4 +26,4 @@ function collectProperties(keeper, modules, debugContext) {
|
|
|
27
26
|
}
|
|
28
27
|
}
|
|
29
28
|
|
|
30
|
-
export { EMITTERS,
|
|
29
|
+
export { EMITTERS, EMITTER_DEFAULT_CAPACITY_STEP, EMITTER_DEFAULT_CAST_SHADOW, EMITTER_DEFAULT_EXPECTED_CAPACITY, EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT, EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION, EMITTER_DEFAULT_RECEIVE_SHADOW, EMITTER_DEFAULT_SORT_FRACTION, collectProperties };
|
|
@@ -4,11 +4,19 @@ 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;
|
|
10
|
-
|
|
17
|
+
/** Camera used for back-to-front depth sorting; `undefined` disables sorting */
|
|
11
18
|
sortCamera?: Camera;
|
|
19
|
+
/** Fraction of frames on which sorting runs (`1` = every frame, `0.1` = ~every 10th frame); defaults to `0.1` */
|
|
12
20
|
sortFraction: number;
|
|
13
21
|
private readonly mesh;
|
|
14
22
|
private readonly material;
|
|
@@ -17,13 +25,52 @@ export declare class FXEmitter extends Object3D {
|
|
|
17
25
|
private readonly activePlays;
|
|
18
26
|
private sortingAccumulator;
|
|
19
27
|
private readonly sortingCameraWorldPosition;
|
|
28
|
+
/**
|
|
29
|
+
* @param spawnSequence - Modules executed in order when particles are born
|
|
30
|
+
* @param behaviorSequence - Modules executed in order every frame for all live particles
|
|
31
|
+
* @param material - Particle material
|
|
32
|
+
* @param options - Emitter configuration
|
|
33
|
+
*/
|
|
20
34
|
constructor(spawnSequence: FXSpawn[], behaviorSequence: readonly FXBehavior[], material: FXMaterial, options?: Partial<FXEmitterOptions>);
|
|
35
|
+
/**
|
|
36
|
+
* Ticks all active emitters; call once per frame before rendering
|
|
37
|
+
*
|
|
38
|
+
* @param deltaTime - Elapsed time in seconds since the last frame
|
|
39
|
+
*/
|
|
21
40
|
static onWillRender: (deltaTime: number) => void;
|
|
41
|
+
/** Unregisters the emitter, removes it from the scene, and disposes modules and material */
|
|
22
42
|
destroy(): void;
|
|
43
|
+
/**
|
|
44
|
+
* Spawns `count` particles immediately, or after a delay when `options.delay` is set
|
|
45
|
+
*
|
|
46
|
+
* @param count - Number of particles to spawn
|
|
47
|
+
* @param options - Burst options
|
|
48
|
+
* @returns Handler that can be passed to {@link stop} to cancel a pending delayed burst
|
|
49
|
+
*/
|
|
23
50
|
burst(count: number, options?: Partial<FXEmitterBurstOptions>): number;
|
|
51
|
+
/**
|
|
52
|
+
* Starts continuous emission at the given rate
|
|
53
|
+
*
|
|
54
|
+
* @param rate - Particles per second
|
|
55
|
+
* @param options - Play options
|
|
56
|
+
* @returns Handler that can be passed to {@link stop} to cancel
|
|
57
|
+
*/
|
|
24
58
|
play(rate: number, options?: Partial<FXEmitterPlayOptions>): number;
|
|
59
|
+
/**
|
|
60
|
+
* Stops an active play or pending burst by handler; stops all when called with no arguments
|
|
61
|
+
*
|
|
62
|
+
* @param handler - Handler returned by {@link play} or {@link burst}; omit to stop everything
|
|
63
|
+
* @returns `true` if anything was stopped
|
|
64
|
+
*/
|
|
25
65
|
stop(handler?: number): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Simulates the emitter forward in time to avoid a cold-start on frame 0
|
|
68
|
+
*
|
|
69
|
+
* @param duration - Total time to simulate in seconds
|
|
70
|
+
* @param stepDuration - Maximum simulation step size in seconds. Defaults to `1/60`
|
|
71
|
+
*/
|
|
26
72
|
prewarm(duration: number, stepDuration?: number): void;
|
|
73
|
+
/** Kills all live particles and cancels all active plays and pending bursts */
|
|
27
74
|
reset(): void;
|
|
28
75
|
private tick;
|
|
29
76
|
private readonly onRendering;
|
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
import { Object3D, Vector3 } from 'three';
|
|
2
2
|
import { FXInstancedParticle } from '../instancedParticle/FXInstancedParticle.js';
|
|
3
3
|
import { resolveGLSLTypeInfo } from '../instancedParticle/shared.js';
|
|
4
|
+
import { assertValidPositiveNumber, assertValidNonNegativeNumber } from '../miscellaneous/asserts.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
|
-
import { EMITTERS, collectProperties, EMITTER_DEFAULT_EXPECTED_CAPACITY, EMITTER_DEFAULT_CAPACITY_STEP, EMITTER_DEFAULT_CAST_SHADOW, EMITTER_DEFAULT_RECEIVE_SHADOW,
|
|
6
|
+
import { EMITTERS, collectProperties, EMITTER_DEFAULT_EXPECTED_CAPACITY, EMITTER_DEFAULT_CAPACITY_STEP, EMITTER_DEFAULT_CAST_SHADOW, EMITTER_DEFAULT_RECEIVE_SHADOW, 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
|
};
|
|
@@ -40,33 +63,42 @@ class FXEmitter extends Object3D {
|
|
|
40
63
|
if (options.receiveShadow ?? EMITTER_DEFAULT_RECEIVE_SHADOW) {
|
|
41
64
|
this._private_mesh.receiveShadow = true;
|
|
42
65
|
}
|
|
43
|
-
this.automaticallyDestroyModules =
|
|
44
|
-
options.automaticallyDestroyModules ?? EMITTER_DEFAULT_AUTOMATICALLY_DESTROY_MODULES;
|
|
45
66
|
this.sortCamera = options.sortCamera;
|
|
46
67
|
this.sortFraction = options.sortFraction ?? EMITTER_DEFAULT_SORT_FRACTION;
|
|
47
68
|
this._private_material = material;
|
|
48
69
|
this.add(this._private_mesh);
|
|
49
70
|
EMITTERS.push(this);
|
|
50
71
|
}
|
|
72
|
+
/** Unregisters the emitter, removes it from the scene, and disposes modules and material */
|
|
51
73
|
destroy() {
|
|
52
74
|
const index = EMITTERS.indexOf(this);
|
|
53
75
|
if (index !== -1) {
|
|
54
76
|
EMITTERS.splice(index, 1);
|
|
55
77
|
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
module.destroy?.();
|
|
59
|
-
}
|
|
60
|
-
for (const module of this._private_behaviorSequence) {
|
|
61
|
-
module.destroy?.();
|
|
62
|
-
}
|
|
63
|
-
this._private_material.destroy();
|
|
78
|
+
for (const module of this._private_spawnSequence) {
|
|
79
|
+
module.destroy?.();
|
|
64
80
|
}
|
|
81
|
+
for (const module of this._private_behaviorSequence) {
|
|
82
|
+
module.destroy?.();
|
|
83
|
+
}
|
|
84
|
+
this._private_material.destroy();
|
|
85
|
+
this._private_mesh.material?.dispose();
|
|
86
|
+
this._private_mesh.customDepthMaterial?.dispose();
|
|
87
|
+
this._private_mesh.customDistanceMaterial?.dispose();
|
|
65
88
|
this.removeFromParent();
|
|
66
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Spawns `count` particles immediately, or after a delay when `options.delay` is set
|
|
92
|
+
*
|
|
93
|
+
* @param count - Number of particles to spawn
|
|
94
|
+
* @param options - Burst options
|
|
95
|
+
* @returns Handler that can be passed to {@link stop} to cancel a pending delayed burst
|
|
96
|
+
*/
|
|
67
97
|
burst(count, options = {}) {
|
|
98
|
+
assertValidPositiveNumber(count, "FXEmitter.burst.count");
|
|
68
99
|
const handler = this._private_nextHandler++;
|
|
69
100
|
const delay = options.delay ?? 0;
|
|
101
|
+
assertValidNonNegativeNumber(delay, "FXEmitter.burst.options.delay");
|
|
70
102
|
if (delay <= 0) {
|
|
71
103
|
this._private_spawnBurst(count);
|
|
72
104
|
}
|
|
@@ -75,7 +107,18 @@ class FXEmitter extends Object3D {
|
|
|
75
107
|
}
|
|
76
108
|
return handler;
|
|
77
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Starts continuous emission at the given rate
|
|
112
|
+
*
|
|
113
|
+
* @param rate - Particles per second
|
|
114
|
+
* @param options - Play options
|
|
115
|
+
* @returns Handler that can be passed to {@link stop} to cancel
|
|
116
|
+
*/
|
|
78
117
|
play(rate, options = {}) {
|
|
118
|
+
assertValidPositiveNumber(rate, "FXEmitter.play.rate");
|
|
119
|
+
if (options.delay !== undefined) {
|
|
120
|
+
assertValidNonNegativeNumber(options.delay, "FXEmitter.play.options.delay");
|
|
121
|
+
}
|
|
79
122
|
const handler = this._private_nextHandler++;
|
|
80
123
|
this._private_activePlays.push({
|
|
81
124
|
handler,
|
|
@@ -87,6 +130,12 @@ class FXEmitter extends Object3D {
|
|
|
87
130
|
});
|
|
88
131
|
return handler;
|
|
89
132
|
}
|
|
133
|
+
/**
|
|
134
|
+
* Stops an active play or pending burst by handler; stops all when called with no arguments
|
|
135
|
+
*
|
|
136
|
+
* @param handler - Handler returned by {@link play} or {@link burst}; omit to stop everything
|
|
137
|
+
* @returns `true` if anything was stopped
|
|
138
|
+
*/
|
|
90
139
|
stop(handler) {
|
|
91
140
|
if (handler === undefined) {
|
|
92
141
|
const hadAnything = this._private_pendingBursts.length > 0 || this._private_activePlays.length > 0;
|
|
@@ -106,7 +155,15 @@ class FXEmitter extends Object3D {
|
|
|
106
155
|
}
|
|
107
156
|
return false;
|
|
108
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Simulates the emitter forward in time to avoid a cold-start on frame 0
|
|
160
|
+
*
|
|
161
|
+
* @param duration - Total time to simulate in seconds
|
|
162
|
+
* @param stepDuration - Maximum simulation step size in seconds. Defaults to `1/60`
|
|
163
|
+
*/
|
|
109
164
|
prewarm(duration, stepDuration = EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION) {
|
|
165
|
+
assertValidPositiveNumber(duration, "FXEmitter.prewarm.duration");
|
|
166
|
+
assertValidPositiveNumber(stepDuration, "FXEmitter.prewarm.stepDuration");
|
|
110
167
|
const stepCount = Math.min(duration / Math.max(stepDuration, EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION), EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT);
|
|
111
168
|
const step = duration / stepCount;
|
|
112
169
|
let remaining = duration;
|
|
@@ -116,6 +173,7 @@ class FXEmitter extends Object3D {
|
|
|
116
173
|
remaining -= deltaTime;
|
|
117
174
|
}
|
|
118
175
|
}
|
|
176
|
+
/** Kills all live particles and cancels all active plays and pending bursts */
|
|
119
177
|
reset() {
|
|
120
178
|
this._private_pendingBursts.length = 0;
|
|
121
179
|
this._private_activePlays.length = 0;
|
|
@@ -213,6 +271,11 @@ class FXEmitter extends Object3D {
|
|
|
213
271
|
}
|
|
214
272
|
}
|
|
215
273
|
}
|
|
274
|
+
/**
|
|
275
|
+
* Ticks all active emitters; call once per frame before rendering
|
|
276
|
+
*
|
|
277
|
+
* @param deltaTime - Elapsed time in seconds since the last frame
|
|
278
|
+
*/
|
|
216
279
|
FXEmitter.onWillRender = (deltaTime) => {
|
|
217
280
|
for (const instance of EMITTERS) {
|
|
218
281
|
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 };
|