sparcoon 0.7.3 → 0.9.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 +124 -76
- package/dist/artifact/FXArtifact.d.ts +120 -0
- package/dist/artifact/FXArtifact.js +1 -0
- package/dist/coreLayout.d.ts +22 -0
- package/dist/coreLayout.js +32 -0
- package/dist/effect/FXEffect.d.ts +77 -0
- package/dist/effect/FXEffect.js +454 -0
- package/dist/effect/FXEffectSampling.Internal.d.ts +24 -0
- package/dist/effect/FXEffectSampling.Internal.js +165 -0
- package/dist/effect/FXEffectSpec.d.ts +115 -0
- package/dist/effect/FXEffectSpec.js +1 -0
- package/dist/emitter/FXEmitter.Internal.d.ts +23 -58
- package/dist/emitter/FXEmitter.Internal.js +13 -29
- package/dist/emitter/FXEmitter.d.ts +46 -48
- package/dist/emitter/FXEmitter.js +355 -186
- package/dist/emitter/FXFromArtifactsOptions.d.ts +13 -0
- package/dist/emitter/FXFromArtifactsOptions.js +1 -0
- package/dist/emitter/FXObjectMotion.Internal.d.ts +33 -0
- package/dist/emitter/FXObjectMotion.Internal.js +87 -0
- package/dist/emitter/FXSimulationHolder.d.ts +1 -0
- package/dist/emitter/FXSimulationHolder.js +43 -0
- package/dist/index.d.ts +10 -39
- package/dist/index.js +13 -36
- package/dist/instancedParticle/FXInstancedParticle.d.ts +1 -22
- package/dist/instancedParticle/FXInstancedParticle.js +145 -85
- package/dist/instancedParticle/glTypeInfo.d.ts +5 -0
- package/dist/instancedParticle/glTypeInfo.js +3 -0
- package/dist/instancedParticle/primitiveGeometry.d.ts +1 -0
- package/dist/instancedParticle/primitiveGeometry.js +30 -0
- package/dist/miscellaneous/asserts.d.ts +1 -18
- package/dist/miscellaneous/asserts.js +21 -28
- package/dist/miscellaneous/fxMath.d.ts +26 -0
- package/dist/miscellaneous/fxMath.js +261 -0
- package/dist/miscellaneous/math.d.ts +2 -7
- package/dist/miscellaneous/math.js +8 -9
- package/dist/miscellaneous/texture/fxDataTexture.d.ts +26 -0
- package/dist/miscellaneous/texture/fxDataTexture.js +20 -0
- package/dist/render/FXArtifactMaterial.Internal.d.ts +16 -0
- package/dist/render/FXArtifactMaterial.Internal.js +944 -0
- package/dist/render/FXArtifactMaterial.d.ts +1 -0
- package/dist/render/FXArtifactMaterial.js +79 -0
- package/dist/render/FXMaterialOptions.d.ts +27 -0
- package/dist/render/FXMaterialOptions.js +5 -0
- package/dist/render/FXMeshMaterial.d.ts +40 -0
- package/dist/render/FXMeshMaterial.js +77 -0
- package/dist/world/FXWorld.d.ts +42 -0
- package/dist/world/FXWorld.js +103 -0
- package/package.json +45 -29
- package/dist/_virtual/2d.js +0 -3
- package/dist/_virtual/3d.js +0 -3
- package/dist/_virtual/4d.js +0 -3
- package/dist/_virtual/mod.js +0 -5
- package/dist/_virtual/mod2.js +0 -3
- package/dist/emitter/behavior/FXBehavior.d.ts +0 -24
- package/dist/emitter/behavior/FXBehavior.js +0 -10
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.d.ts +0 -23
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.js +0 -72
- package/dist/emitter/behavior/FXBehaviorPointForce.d.ts +0 -36
- package/dist/emitter/behavior/FXBehaviorPointForce.js +0 -94
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.d.ts +0 -24
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.js +0 -55
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.d.ts +0 -20
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.js +0 -48
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.d.ts +0 -25
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.js +0 -67
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.d.ts +0 -19
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.js +0 -41
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.d.ts +0 -20
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.js +0 -50
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.d.ts +0 -25
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.js +0 -76
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.d.ts +0 -22
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.js +0 -55
- package/dist/emitter/spawn/FXSpawn.d.ts +0 -11
- package/dist/emitter/spawn/FXSpawn.js +0 -10
- package/dist/emitter/spawn/FXSpawnBox.d.ts +0 -18
- package/dist/emitter/spawn/FXSpawnBox.js +0 -50
- package/dist/emitter/spawn/FXSpawnLifetime.d.ts +0 -17
- package/dist/emitter/spawn/FXSpawnLifetime.js +0 -44
- package/dist/emitter/spawn/FXSpawnOffset.d.ts +0 -21
- package/dist/emitter/spawn/FXSpawnOffset.js +0 -49
- package/dist/emitter/spawn/FXSpawnPoint.d.ts +0 -18
- package/dist/emitter/spawn/FXSpawnPoint.js +0 -46
- package/dist/emitter/spawn/FXSpawnRotation.d.ts +0 -17
- package/dist/emitter/spawn/FXSpawnRotation.js +0 -42
- package/dist/emitter/spawn/FXSpawnScale.d.ts +0 -22
- package/dist/emitter/spawn/FXSpawnScale.js +0 -56
- package/dist/emitter/spawn/FXSpawnSphere.d.ts +0 -36
- package/dist/emitter/spawn/FXSpawnSphere.js +0 -87
- package/dist/emitter/spawn/FXSpawnTorque.d.ts +0 -17
- package/dist/emitter/spawn/FXSpawnTorque.js +0 -42
- package/dist/emitter/spawn/FXSpawnVelocity.d.ts +0 -29
- package/dist/emitter/spawn/FXSpawnVelocity.js +0 -91
- package/dist/instancedParticle/FXInstancedParticle.Internal.d.ts +0 -2
- package/dist/instancedParticle/shared.d.ts +0 -19
- package/dist/instancedParticle/shared.js +0 -116
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +0 -24
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +0 -275
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.d.ts +0 -75
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +0 -63
- package/dist/materials/FXMaterial/FXBlending.Internal.d.ts +0 -24
- package/dist/materials/FXMaterial/FXBlending.Internal.js +0 -64
- package/dist/materials/FXMaterial/FXDepthMaterial.Internal.d.ts +0 -4
- package/dist/materials/FXMaterial/FXDepthMaterial.Internal.js +0 -144
- package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.d.ts +0 -4
- package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.js +0 -175
- package/dist/materials/FXMaterial/FXMaterial.d.ts +0 -70
- package/dist/materials/FXMaterial/FXMaterial.js +0 -51
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +0 -7
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.js +0 -103
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.d.ts +0 -7
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +0 -21
- package/dist/miscellaneous/color/FXColor.Internal.d.ts +0 -7
- package/dist/miscellaneous/color/FXColor.Internal.js +0 -42
- package/dist/miscellaneous/color/FXColor.d.ts +0 -234
- package/dist/miscellaneous/color/FXColor.js +0 -636
- package/dist/miscellaneous/curve/FXCurve1D.Internal.d.ts +0 -20
- package/dist/miscellaneous/curve/FXCurve1D.d.ts +0 -17
- package/dist/miscellaneous/curve/FXCurve1D.js +0 -94
- package/dist/miscellaneous/miscellaneous.d.ts +0 -34
- package/dist/miscellaneous/miscellaneous.js +0 -85
- package/dist/miscellaneous/texture/FXTextureView.Internal.d.ts +0 -49
- package/dist/miscellaneous/texture/FXTextureView.Internal.js +0 -6
- package/dist/miscellaneous/texture/FXTextureView.d.ts +0 -72
- package/dist/miscellaneous/texture/FXTextureView.js +0 -234
- package/dist/miscellaneous/webglCapabilities.d.ts +0 -1
- package/dist/miscellaneous/webglCapabilities.js +0 -45
- package/dist/node_modules/fast-simplex-noise/lib/2d.js +0 -93
- package/dist/node_modules/fast-simplex-noise/lib/3d.js +0 -142
- package/dist/node_modules/fast-simplex-noise/lib/4d.js +0 -196
- package/dist/node_modules/fast-simplex-noise/lib/mod.js +0 -24
- package/dist/nodes/FXNode.d.ts +0 -5
- package/dist/nodes/FXNode.js +0 -10
- package/dist/nodes/blending/FXNodeBlending.d.ts +0 -6
- package/dist/nodes/blending/FXNodeBlending.js +0 -9
- package/dist/nodes/blending/FXNodeLightnessBlendingMask.d.ts +0 -19
- package/dist/nodes/blending/FXNodeLightnessBlendingMask.js +0 -65
- package/dist/nodes/color/FXNodeColor.d.ts +0 -6
- package/dist/nodes/color/FXNodeColor.js +0 -9
- package/dist/nodes/color/FXNodeColorOverLife.d.ts +0 -22
- package/dist/nodes/color/FXNodeColorOverLife.js +0 -64
- package/dist/nodes/color/FXNodeSphericalClip.d.ts +0 -21
- package/dist/nodes/color/FXNodeSphericalClip.js +0 -54
- package/dist/nodes/normal/FXNodeFlatNormal.d.ts +0 -6
- package/dist/nodes/normal/FXNodeFlatNormal.js +0 -16
- package/dist/nodes/normal/FXNodeNormal.d.ts +0 -6
- package/dist/nodes/normal/FXNodeNormal.js +0 -9
- package/dist/nodes/normal/FXNodeSphericalNormal.d.ts +0 -6
- package/dist/nodes/normal/FXNodeSphericalNormal.js +0 -23
- package/dist/nodes/texture/FXNodeAnimatedTexture.d.ts +0 -52
- package/dist/nodes/texture/FXNodeAnimatedTexture.js +0 -151
- package/dist/nodes/texture/FXNodeStaticTexture.d.ts +0 -20
- package/dist/nodes/texture/FXNodeStaticTexture.js +0 -67
- package/dist/nodes/texture/FXNodeTexture.d.ts +0 -6
- package/dist/nodes/texture/FXNodeTexture.js +0 -9
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { buildArtifactDepthMaterial, buildArtifactMaterial } from "./FXArtifactMaterial.Internal.js";
|
|
2
|
+
/**
|
|
3
|
+
* @internal Runtime material driver: wraps a precompiled {@link FXRenderArtifact} and builds the
|
|
4
|
+
* concrete self-contained `ShaderMaterial` via {@link buildArtifactMaterial}, which picks the flat or
|
|
5
|
+
* lit variant from the artifact's lighting capability ({@link FXRenderArtifact.lightingIntrinsics}).
|
|
6
|
+
* The emitter owns one, calls {@link buildThreeMaterial} once, and pushes the shared clock each frame.
|
|
7
|
+
*/
|
|
8
|
+
export class FXArtifactMaterial {
|
|
9
|
+
/**
|
|
10
|
+
* @param textures - App-supplied external textures by slot name. A missing one fails fast here
|
|
11
|
+
* rather than surfacing as a blank sampler at draw time.
|
|
12
|
+
*/
|
|
13
|
+
constructor(artifact, textures = {}) {
|
|
14
|
+
this.artifact = artifact;
|
|
15
|
+
this.elapsedTime = { value: 0 };
|
|
16
|
+
this.deltaTime = { value: 0 };
|
|
17
|
+
this.velocity = { value: [0, 0, 0] };
|
|
18
|
+
this.angularVelocity = { value: [0, 0, 0] };
|
|
19
|
+
// Durable per-emitter slots for `external` texture uniforms. Unlike non-external uniforms (which
|
|
20
|
+
// bind the shared artifact slot directly), an external uniform resolves per emitter, so it gets
|
|
21
|
+
// its own slot here - and that slot is what gets bound, keeping it live-scrubbable.
|
|
22
|
+
this.externalSlots = {};
|
|
23
|
+
for (const name in artifact.uniforms) {
|
|
24
|
+
const uniform = artifact.uniforms[name];
|
|
25
|
+
if ("external" in uniform) {
|
|
26
|
+
if (!(uniform.external in textures)) {
|
|
27
|
+
throw new Error(`FXArtifactMaterial: no texture supplied for external slot "${uniform.external}" ` +
|
|
28
|
+
`(uniform "${name}"); pass it via FXEmitter.fromArtifacts({ textures })`);
|
|
29
|
+
}
|
|
30
|
+
this.externalSlots[name] = { value: textures[uniform.external] };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
get attributeReads() {
|
|
35
|
+
return this.artifact.attributeReads;
|
|
36
|
+
}
|
|
37
|
+
buildThreeMaterial(varyings) {
|
|
38
|
+
return buildArtifactMaterial(this.artifact, varyings, this.elapsedTime, this.deltaTime, this.velocity, this.angularVelocity, this.externalSlots);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* The `customDepthMaterial` for casting shadows: the same artifact IR spliced into a depth-packing
|
|
42
|
+
* material, so the shadow silhouette honors the authored shape/alpha-cutout (see
|
|
43
|
+
* {@link buildArtifactDepthMaterial}). Built only when the emitter opts into `castShadow`.
|
|
44
|
+
*/
|
|
45
|
+
buildThreeDepthMaterial(varyings) {
|
|
46
|
+
return buildArtifactDepthMaterial(this.artifact, varyings, this.elapsedTime, this.deltaTime, this.velocity, this.angularVelocity, this.externalSlots);
|
|
47
|
+
}
|
|
48
|
+
setElapsedTime(seconds) {
|
|
49
|
+
this.elapsedTime.value = seconds;
|
|
50
|
+
}
|
|
51
|
+
setDeltaTime(seconds) {
|
|
52
|
+
this.deltaTime.value = seconds;
|
|
53
|
+
}
|
|
54
|
+
setObjectVelocity(velocity) {
|
|
55
|
+
this.velocity.value = velocity;
|
|
56
|
+
}
|
|
57
|
+
setObjectAngularVelocity(angularVelocity) {
|
|
58
|
+
this.angularVelocity.value = angularVelocity;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Render half of {@link FXEmitter.applyValues}. A non-external uniform mutates its shared artifact
|
|
62
|
+
* slot (module-scoped); an external uniform mutates this emitter's own slot. Unknown name = no-op.
|
|
63
|
+
*/
|
|
64
|
+
applyUniformValues(values) {
|
|
65
|
+
for (const name in values) {
|
|
66
|
+
const value = values[name];
|
|
67
|
+
if (name in this.externalSlots) {
|
|
68
|
+
this.externalSlots[name].value = value;
|
|
69
|
+
}
|
|
70
|
+
else if (name in this.artifact.uniforms) {
|
|
71
|
+
this.artifact.uniforms[name].value =
|
|
72
|
+
value;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// No-op: the artifact owns no per-emitter resources. Generated textures are module-scoped and
|
|
77
|
+
// shared (see the `fromArtifacts` texture-ownership contract); external textures are app-owned.
|
|
78
|
+
destroy() { }
|
|
79
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render options carried in `FXRenderArtifact.options`. Runtime-owned so the artifact contract has no
|
|
3
|
+
* editor dependency; the editor's graph material adapters import the same types.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* How a particle fragment's alpha is composited into the framebuffer:
|
|
7
|
+
* - `blending` - alpha blending (transparent, no depth write). The only mode that honors the
|
|
8
|
+
* `additivity` output slot: 0 = normal "over", 1 = additive, blended continuously via the
|
|
9
|
+
* premultiplied-alpha channel (a single `NormalBlending`, no separate additive blend mode).
|
|
10
|
+
* - `alphaHash` - stochastic alpha hashing (opaque, depth-writing, dithered discard).
|
|
11
|
+
* - `alphaTest` - hard alpha cutout (opaque, depth-writing).
|
|
12
|
+
* - `opaque` - fully opaque (alpha forced to 1).
|
|
13
|
+
*
|
|
14
|
+
* The `alphaThreshold` output slot (hard discard cutoff, default 0.0075) applies in every mode except
|
|
15
|
+
* `opaque`; `additivity` applies only in `blending`.
|
|
16
|
+
*/
|
|
17
|
+
export type FXRenderMode = "blending" | "alphaHash" | "alphaTest" | "opaque";
|
|
18
|
+
/** Unlit (flat albedo `ShaderMaterial`) particle material options. */
|
|
19
|
+
export interface FXGraphUnlitMaterialOptions {
|
|
20
|
+
/** @defaultValue `"blending"` */
|
|
21
|
+
renderMode?: FXRenderMode;
|
|
22
|
+
}
|
|
23
|
+
/** Lambert (lit `ShaderMaterial`, `lights`/`fog` from Three) particle material options. */
|
|
24
|
+
export interface FXGraphLambertMaterialOptions {
|
|
25
|
+
/** @defaultValue `"blending"` */
|
|
26
|
+
renderMode?: FXRenderMode;
|
|
27
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { Material, Texture } from "three";
|
|
2
|
+
import type { FXRenderArtifact } from "../artifact/FXArtifact.js";
|
|
3
|
+
/**
|
|
4
|
+
* Runtime material driver for a **VFX mesh**: the mesh twin of {@link FXArtifactMaterial}. Wraps a
|
|
5
|
+
* precompiled {@link FXRenderArtifact} (compiled against the mesh render target) and builds a
|
|
6
|
+
* self-contained `ShaderMaterial` for a single, non-instanced `THREE.Mesh`. Unlike the particle
|
|
7
|
+
* driver it takes no per-particle `varyings` - a VFX mesh has no simulation and no attributes. The
|
|
8
|
+
* host builds the material once and pushes the shared clock each frame.
|
|
9
|
+
*/
|
|
10
|
+
export declare class FXMeshMaterial {
|
|
11
|
+
private readonly artifact;
|
|
12
|
+
private readonly elapsedTime;
|
|
13
|
+
private readonly deltaTime;
|
|
14
|
+
private readonly velocity;
|
|
15
|
+
private readonly angularVelocity;
|
|
16
|
+
private readonly externalSlots;
|
|
17
|
+
/**
|
|
18
|
+
* @param textures - App-supplied external textures by slot name. A missing one fails fast here
|
|
19
|
+
* rather than surfacing as a blank sampler at draw time.
|
|
20
|
+
*/
|
|
21
|
+
constructor(artifact: FXRenderArtifact, textures?: Readonly<Record<string, Texture>>);
|
|
22
|
+
buildThreeMaterial(): Material;
|
|
23
|
+
/**
|
|
24
|
+
* The `customDepthMaterial` for casting shadows: the mesh twin of
|
|
25
|
+
* {@link FXArtifactMaterial.buildThreeDepthMaterial}. The mesh host attaches it on the `Mesh` only
|
|
26
|
+
* when the mesh opts into `castShadow`.
|
|
27
|
+
*/
|
|
28
|
+
buildThreeDepthMaterial(): Material;
|
|
29
|
+
setElapsedTime(seconds: number): void;
|
|
30
|
+
setDeltaTime(seconds: number): void;
|
|
31
|
+
setObjectVelocity(velocity: readonly [number, number, number]): void;
|
|
32
|
+
setObjectAngularVelocity(angularVelocity: readonly [number, number, number]): void;
|
|
33
|
+
/**
|
|
34
|
+
* Applies a value edit (a rebind, no rebuild). A non-external uniform mutates its shared artifact
|
|
35
|
+
* slot; an external uniform mutates this mesh's own slot. Unknown name = no-op (an editor<->runtime
|
|
36
|
+
* name drift never corrupts values).
|
|
37
|
+
*/
|
|
38
|
+
applyUniformValues(values: Readonly<Record<string, number | readonly number[] | Texture>>): void;
|
|
39
|
+
destroy(): void;
|
|
40
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { buildMeshArtifactDepthMaterial, buildMeshArtifactMaterial, } from "./FXArtifactMaterial.Internal.js";
|
|
2
|
+
/**
|
|
3
|
+
* Runtime material driver for a **VFX mesh**: the mesh twin of {@link FXArtifactMaterial}. Wraps a
|
|
4
|
+
* precompiled {@link FXRenderArtifact} (compiled against the mesh render target) and builds a
|
|
5
|
+
* self-contained `ShaderMaterial` for a single, non-instanced `THREE.Mesh`. Unlike the particle
|
|
6
|
+
* driver it takes no per-particle `varyings` - a VFX mesh has no simulation and no attributes. The
|
|
7
|
+
* host builds the material once and pushes the shared clock each frame.
|
|
8
|
+
*/
|
|
9
|
+
export class FXMeshMaterial {
|
|
10
|
+
/**
|
|
11
|
+
* @param textures - App-supplied external textures by slot name. A missing one fails fast here
|
|
12
|
+
* rather than surfacing as a blank sampler at draw time.
|
|
13
|
+
*/
|
|
14
|
+
constructor(artifact, textures = {}) {
|
|
15
|
+
this.artifact = artifact;
|
|
16
|
+
this.elapsedTime = { value: 0 };
|
|
17
|
+
this.deltaTime = { value: 0 };
|
|
18
|
+
this.velocity = { value: [0, 0, 0] };
|
|
19
|
+
this.angularVelocity = { value: [0, 0, 0] };
|
|
20
|
+
// Durable per-mesh slots for `external` texture uniforms: an external uniform resolves per mesh, so
|
|
21
|
+
// it binds its own live-scrubbable slot (non-external uniforms bind the shared artifact slot).
|
|
22
|
+
this.externalSlots = {};
|
|
23
|
+
for (const name in artifact.uniforms) {
|
|
24
|
+
const uniform = artifact.uniforms[name];
|
|
25
|
+
if ("external" in uniform) {
|
|
26
|
+
if (!(uniform.external in textures)) {
|
|
27
|
+
throw new Error(`FXMeshMaterial: no texture supplied for external slot "${uniform.external}" ` +
|
|
28
|
+
`(uniform "${name}"); pass it via the mesh host's textures`);
|
|
29
|
+
}
|
|
30
|
+
this.externalSlots[name] = { value: textures[uniform.external] };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
buildThreeMaterial() {
|
|
35
|
+
return buildMeshArtifactMaterial(this.artifact, this.elapsedTime, this.deltaTime, this.velocity, this.angularVelocity, this.externalSlots);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The `customDepthMaterial` for casting shadows: the mesh twin of
|
|
39
|
+
* {@link FXArtifactMaterial.buildThreeDepthMaterial}. The mesh host attaches it on the `Mesh` only
|
|
40
|
+
* when the mesh opts into `castShadow`.
|
|
41
|
+
*/
|
|
42
|
+
buildThreeDepthMaterial() {
|
|
43
|
+
return buildMeshArtifactDepthMaterial(this.artifact, this.elapsedTime, this.deltaTime, this.velocity, this.angularVelocity, this.externalSlots);
|
|
44
|
+
}
|
|
45
|
+
setElapsedTime(seconds) {
|
|
46
|
+
this.elapsedTime.value = seconds;
|
|
47
|
+
}
|
|
48
|
+
setDeltaTime(seconds) {
|
|
49
|
+
this.deltaTime.value = seconds;
|
|
50
|
+
}
|
|
51
|
+
setObjectVelocity(velocity) {
|
|
52
|
+
this.velocity.value = velocity;
|
|
53
|
+
}
|
|
54
|
+
setObjectAngularVelocity(angularVelocity) {
|
|
55
|
+
this.angularVelocity.value = angularVelocity;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Applies a value edit (a rebind, no rebuild). A non-external uniform mutates its shared artifact
|
|
59
|
+
* slot; an external uniform mutates this mesh's own slot. Unknown name = no-op (an editor<->runtime
|
|
60
|
+
* name drift never corrupts values).
|
|
61
|
+
*/
|
|
62
|
+
applyUniformValues(values) {
|
|
63
|
+
for (const name in values) {
|
|
64
|
+
const value = values[name];
|
|
65
|
+
if (name in this.externalSlots) {
|
|
66
|
+
this.externalSlots[name].value = value;
|
|
67
|
+
}
|
|
68
|
+
else if (name in this.artifact.uniforms) {
|
|
69
|
+
this.artifact.uniforms[name].value =
|
|
70
|
+
value;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// No-op: the artifact owns no per-mesh resources (generated textures are module-scoped; external
|
|
75
|
+
// textures are app-owned).
|
|
76
|
+
destroy() { }
|
|
77
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tick domain that drives effects. A world owns a pool of emitters, a registry of effects, and
|
|
3
|
+
* a single monotonic clock; one {@link FXWorld.update} call per frame advances every playing
|
|
4
|
+
* effect's timeline and then ticks the shared particle pool exactly once.
|
|
5
|
+
*
|
|
6
|
+
* An {@link FXEffect} subscribes to a world on construction and unsubscribes on
|
|
7
|
+
* {@link FXEffect.dispose} - the host never wires that up by hand. Pass a world through
|
|
8
|
+
* `FXEffectOptions.world` to place an effect in a specific world; omit it and the effect joins the
|
|
9
|
+
* lazily created default world, which the static {@link FXWorld.update} ticks.
|
|
10
|
+
*
|
|
11
|
+
* A world is only a tick/clock domain, NOT a scene graph: an effect is still a `THREE.Object3D` you
|
|
12
|
+
* add to your own scene. Two worlds give two independent clocks (for an independent time scale or an
|
|
13
|
+
* isolated group); within one world every effect shares the clock and one delta per frame, so
|
|
14
|
+
* {@link FXEffect.stop} clears rather than freezes - freeze one effect independently by giving it
|
|
15
|
+
* its own world.
|
|
16
|
+
*/
|
|
17
|
+
export declare class FXWorld {
|
|
18
|
+
private static defaultInstance;
|
|
19
|
+
private readonly emitters;
|
|
20
|
+
private readonly effects;
|
|
21
|
+
private elapsedTime;
|
|
22
|
+
private disposed;
|
|
23
|
+
/**
|
|
24
|
+
* The lazily created default world - the one an effect joins when its options carry no `world`.
|
|
25
|
+
* Nothing is allocated until the first effect or the first {@link FXWorld.update}.
|
|
26
|
+
*/
|
|
27
|
+
static getDefault(): FXWorld;
|
|
28
|
+
/** Advances the default world by `deltaTime`; call once per frame. See {@link update}. */
|
|
29
|
+
static update(deltaTime: number): void;
|
|
30
|
+
/**
|
|
31
|
+
* Advances this world by `deltaTime` seconds: every playing effect drives its own timeline and
|
|
32
|
+
* meshes, then the shared particle pool ticks once. Call exactly once per frame, before rendering.
|
|
33
|
+
* A disposed world is a no-op.
|
|
34
|
+
*/
|
|
35
|
+
update(deltaTime: number): void;
|
|
36
|
+
/**
|
|
37
|
+
* Disposes every effect registered in this world (each frees its emitters and mesh resources),
|
|
38
|
+
* then drops any remaining emitters. Idempotent. After this the world's {@link update} is a
|
|
39
|
+
* no-op; disposing the default world simply makes the next {@link getDefault} create a fresh one.
|
|
40
|
+
*/
|
|
41
|
+
dispose(): void;
|
|
42
|
+
}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { EMITTER_TIME_WRAP_PERIOD } from "../emitter/FXEmitter.Internal.js";
|
|
2
|
+
/**
|
|
3
|
+
* The tick domain that drives effects. A world owns a pool of emitters, a registry of effects, and
|
|
4
|
+
* a single monotonic clock; one {@link FXWorld.update} call per frame advances every playing
|
|
5
|
+
* effect's timeline and then ticks the shared particle pool exactly once.
|
|
6
|
+
*
|
|
7
|
+
* An {@link FXEffect} subscribes to a world on construction and unsubscribes on
|
|
8
|
+
* {@link FXEffect.dispose} - the host never wires that up by hand. Pass a world through
|
|
9
|
+
* `FXEffectOptions.world` to place an effect in a specific world; omit it and the effect joins the
|
|
10
|
+
* lazily created default world, which the static {@link FXWorld.update} ticks.
|
|
11
|
+
*
|
|
12
|
+
* A world is only a tick/clock domain, NOT a scene graph: an effect is still a `THREE.Object3D` you
|
|
13
|
+
* add to your own scene. Two worlds give two independent clocks (for an independent time scale or an
|
|
14
|
+
* isolated group); within one world every effect shares the clock and one delta per frame, so
|
|
15
|
+
* {@link FXEffect.stop} clears rather than freezes - freeze one effect independently by giving it
|
|
16
|
+
* its own world.
|
|
17
|
+
*/
|
|
18
|
+
export class FXWorld {
|
|
19
|
+
constructor() {
|
|
20
|
+
this.emitters = [];
|
|
21
|
+
this.effects = [];
|
|
22
|
+
this.elapsedTime = 0;
|
|
23
|
+
this.disposed = false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The lazily created default world - the one an effect joins when its options carry no `world`.
|
|
27
|
+
* Nothing is allocated until the first effect or the first {@link FXWorld.update}.
|
|
28
|
+
*/
|
|
29
|
+
static getDefault() {
|
|
30
|
+
if (FXWorld.defaultInstance === undefined || FXWorld.defaultInstance.disposed) {
|
|
31
|
+
FXWorld.defaultInstance = new FXWorld();
|
|
32
|
+
}
|
|
33
|
+
return FXWorld.defaultInstance;
|
|
34
|
+
}
|
|
35
|
+
/** Advances the default world by `deltaTime`; call once per frame. See {@link update}. */
|
|
36
|
+
static update(deltaTime) {
|
|
37
|
+
FXWorld.getDefault().update(deltaTime);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Advances this world by `deltaTime` seconds: every playing effect drives its own timeline and
|
|
41
|
+
* meshes, then the shared particle pool ticks once. Call exactly once per frame, before rendering.
|
|
42
|
+
* A disposed world is a no-op.
|
|
43
|
+
*/
|
|
44
|
+
update(deltaTime) {
|
|
45
|
+
if (this.disposed) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const effect of this.effects) {
|
|
49
|
+
effect.advanceFrame(deltaTime);
|
|
50
|
+
}
|
|
51
|
+
// Wrap the clock so a float32 `u_time` keeps sub-millisecond resolution in long sessions, then
|
|
52
|
+
// tick the whole pool once with the shared clock.
|
|
53
|
+
this.elapsedTime = (this.elapsedTime + deltaTime) % EMITTER_TIME_WRAP_PERIOD;
|
|
54
|
+
for (const emitter of this.emitters) {
|
|
55
|
+
emitter.onWorldTick(deltaTime, this.elapsedTime);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Disposes every effect registered in this world (each frees its emitters and mesh resources),
|
|
60
|
+
* then drops any remaining emitters. Idempotent. After this the world's {@link update} is a
|
|
61
|
+
* no-op; disposing the default world simply makes the next {@link getDefault} create a fresh one.
|
|
62
|
+
*/
|
|
63
|
+
dispose() {
|
|
64
|
+
if (this.disposed) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
this.disposed = true;
|
|
68
|
+
// dispose() unregisters each effect from this world, so iterate a snapshot.
|
|
69
|
+
for (const effect of [...this.effects]) {
|
|
70
|
+
effect.dispose();
|
|
71
|
+
}
|
|
72
|
+
for (const emitter of [...this.emitters]) {
|
|
73
|
+
emitter.destroy();
|
|
74
|
+
}
|
|
75
|
+
this.effects.length = 0;
|
|
76
|
+
this.emitters.length = 0;
|
|
77
|
+
if (FXWorld.defaultInstance === this) {
|
|
78
|
+
FXWorld.defaultInstance = undefined;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** @internal Called by {@link FXEffect}'s constructor. */
|
|
82
|
+
registerEffect(effect) {
|
|
83
|
+
this.effects.push(effect);
|
|
84
|
+
}
|
|
85
|
+
/** @internal Called by {@link FXEffect.dispose}. */
|
|
86
|
+
unregisterEffect(effect) {
|
|
87
|
+
const index = this.effects.indexOf(effect);
|
|
88
|
+
if (index !== -1) {
|
|
89
|
+
this.effects.splice(index, 1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/** @internal Called by {@link FXEmitter}'s constructor. */
|
|
93
|
+
registerEmitter(emitter) {
|
|
94
|
+
this.emitters.push(emitter);
|
|
95
|
+
}
|
|
96
|
+
/** @internal Called by {@link FXEmitter.destroy}. */
|
|
97
|
+
unregisterEmitter(emitter) {
|
|
98
|
+
const index = this.emitters.indexOf(emitter);
|
|
99
|
+
if (index !== -1) {
|
|
100
|
+
this.emitters.splice(index, 1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/package.json
CHANGED
|
@@ -1,60 +1,76 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sparcoon",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Runtime for VFX artifacts authored in the Sparcoon Editor.",
|
|
5
5
|
"keywords": [
|
|
6
|
+
"three",
|
|
7
|
+
"threejs",
|
|
8
|
+
"three.js",
|
|
6
9
|
"particles",
|
|
7
10
|
"particle-system",
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
+
"vfx",
|
|
12
|
+
"visual-effects",
|
|
13
|
+
"fx",
|
|
11
14
|
"instanced",
|
|
15
|
+
"instancing",
|
|
12
16
|
"billboard",
|
|
13
|
-
"
|
|
17
|
+
"webgl",
|
|
18
|
+
"3d",
|
|
19
|
+
"animation",
|
|
20
|
+
"typescript",
|
|
21
|
+
"sparcoon"
|
|
14
22
|
],
|
|
15
|
-
"
|
|
16
|
-
"
|
|
23
|
+
"homepage": "https://github.com/jango-git/sparcoon#readme",
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/jango-git/sparcoon/issues"
|
|
26
|
+
},
|
|
17
27
|
"repository": {
|
|
18
28
|
"type": "git",
|
|
19
29
|
"url": "git+https://github.com/jango-git/sparcoon.git"
|
|
20
30
|
},
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"author": "jango",
|
|
21
33
|
"type": "module",
|
|
22
|
-
"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
34
|
+
"exports": {
|
|
35
|
+
".": {
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js"
|
|
38
|
+
},
|
|
39
|
+
"./package.json": "./package.json"
|
|
27
40
|
},
|
|
28
41
|
"main": "./dist/index.js",
|
|
29
|
-
"
|
|
42
|
+
"types": "./dist/index.d.ts",
|
|
30
43
|
"files": [
|
|
31
44
|
"dist",
|
|
32
45
|
"README.md",
|
|
33
46
|
"LICENSE"
|
|
34
47
|
],
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
48
|
+
"sideEffects": false,
|
|
49
|
+
"engines": {
|
|
50
|
+
"node": ">=18.18.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"clean": "rm -rf dist",
|
|
54
|
+
"build": "npm run clean && tsc -p tsconfig.json",
|
|
55
|
+
"lint": "eslint src",
|
|
56
|
+
"test": "vitest run",
|
|
57
|
+
"test:watch": "vitest",
|
|
58
|
+
"test:coverage": "vitest run --coverage",
|
|
59
|
+
"format": "prettier --write . && eslint src --fix",
|
|
60
|
+
"prepublishOnly": "npm run build"
|
|
40
61
|
},
|
|
41
62
|
"peerDependencies": {
|
|
42
|
-
"fast-simplex-noise": "^4.0.0",
|
|
43
|
-
"ferrsign": "^0.0.4",
|
|
44
63
|
"three": ">=0.157.0 <0.180.0"
|
|
45
64
|
},
|
|
46
65
|
"devDependencies": {
|
|
47
|
-
"@
|
|
48
|
-
"@rollup/plugin-commonjs": "^29.0.2",
|
|
49
|
-
"@types/node": "^25.0.2",
|
|
66
|
+
"@types/node": "^20.0.0",
|
|
50
67
|
"@types/three": "^0.157.0",
|
|
51
68
|
"@typescript-eslint/eslint-plugin": "^8.32.0",
|
|
52
69
|
"@typescript-eslint/parser": "^8.32.0",
|
|
70
|
+
"@vitest/coverage-v8": "^4.1.9",
|
|
53
71
|
"eslint": "^9.26.0",
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"ts-transformer-minify-privates": "^1.0.0",
|
|
58
|
-
"typescript": "^5.8.3"
|
|
72
|
+
"prettier": "^3.9.5",
|
|
73
|
+
"typescript": "^5.8.3",
|
|
74
|
+
"vitest": "^4.1.9"
|
|
59
75
|
}
|
|
60
76
|
}
|
package/dist/_virtual/2d.js
DELETED
package/dist/_virtual/3d.js
DELETED
package/dist/_virtual/4d.js
DELETED
package/dist/_virtual/mod.js
DELETED
package/dist/_virtual/mod2.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { InstancedBufferAttribute } from "three";
|
|
2
|
-
import type { FXPropertyName } from "../../instancedParticle/shared";
|
|
3
|
-
/**
|
|
4
|
-
* Abstract base for particle behavior modules that mutate per-particle properties each frame
|
|
5
|
-
*
|
|
6
|
-
* @typeParam T - Map of local property keys to {@link FXPropertyName} identifiers,
|
|
7
|
-
* declaring which particle attributes this behavior reads or writes
|
|
8
|
-
*/
|
|
9
|
-
export declare abstract class FXBehavior<T extends Record<string, FXPropertyName> = Record<string, FXPropertyName>> {
|
|
10
|
-
/** Particle attributes required by this behavior */
|
|
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
|
-
*/
|
|
19
|
-
abstract update(properties: {
|
|
20
|
-
[K in keyof T]: InstancedBufferAttribute;
|
|
21
|
-
}, instanceCount: number, deltaTime: number): void;
|
|
22
|
-
/** Releases any resources held by this behavior */
|
|
23
|
-
destroy?(): void;
|
|
24
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abstract base for particle behavior modules that mutate per-particle properties each frame
|
|
3
|
-
*
|
|
4
|
-
* @typeParam T - Map of local property keys to {@link FXPropertyName} identifiers,
|
|
5
|
-
* declaring which particle attributes this behavior reads or writes
|
|
6
|
-
*/
|
|
7
|
-
class FXBehavior {
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export { FXBehavior };
|
|
@@ -1,23 +0,0 @@
|
|
|
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
|
-
set direction(value: FXVector3Config);
|
|
22
|
-
set magnitude(value: number);
|
|
23
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
set direction(value) {
|
|
40
|
-
const resolved = resolveFXVector3Config(value);
|
|
41
|
-
assertValidNumber(resolved.x, "FXBehaviorDirectionalForce.direction.x");
|
|
42
|
-
assertValidNumber(resolved.y, "FXBehaviorDirectionalForce.direction.y");
|
|
43
|
-
assertValidNumber(resolved.z, "FXBehaviorDirectionalForce.direction.z");
|
|
44
|
-
this._private_directionInternal.set(resolved.x, resolved.y, resolved.z);
|
|
45
|
-
if (this._private_directionInternal.lengthSq() < EPSILON) {
|
|
46
|
-
throw new Error("FXBehaviorDirectionalForce.direction: vector cannot be zero");
|
|
47
|
-
}
|
|
48
|
-
this._private_directionInternal.normalize();
|
|
49
|
-
}
|
|
50
|
-
set magnitude(value) {
|
|
51
|
-
assertValidNumber(value, "FXBehaviorDirectionalForce.magnitude");
|
|
52
|
-
this._private_magnitudeInternal = value;
|
|
53
|
-
}
|
|
54
|
-
/** @internal */
|
|
55
|
-
update(properties, instanceCount, deltaTime) {
|
|
56
|
-
const { builtin } = properties;
|
|
57
|
-
const { array, itemSize } = builtin;
|
|
58
|
-
const scale = this._private_magnitudeInternal * deltaTime;
|
|
59
|
-
const offsetX = this._private_directionInternal.x * scale;
|
|
60
|
-
const offsetY = this._private_directionInternal.y * scale;
|
|
61
|
-
const offsetZ = this._private_directionInternal.z * scale;
|
|
62
|
-
for (let i = 0; i < instanceCount; i++) {
|
|
63
|
-
const itemOffset = i * itemSize;
|
|
64
|
-
array[itemOffset + BUILTIN_OFFSET_VELOCITY_X] += offsetX;
|
|
65
|
-
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Y] += offsetY;
|
|
66
|
-
array[itemOffset + BUILTIN_OFFSET_VELOCITY_Z] += offsetZ;
|
|
67
|
-
}
|
|
68
|
-
builtin.needsUpdate = true;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export { FXBehaviorDirectionalForce };
|