sparcoon 0.7.3 → 0.9.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.
Files changed (159) hide show
  1. package/README.md +124 -76
  2. package/dist/artifact/FXArtifact.d.ts +120 -0
  3. package/dist/artifact/FXArtifact.js +1 -0
  4. package/dist/coreLayout.d.ts +22 -0
  5. package/dist/coreLayout.js +32 -0
  6. package/dist/editor/index.d.ts +8 -0
  7. package/dist/editor/index.js +17 -0
  8. package/dist/effect/FXEffect.d.ts +77 -0
  9. package/dist/effect/FXEffect.js +454 -0
  10. package/dist/effect/FXEffectSampling.Internal.d.ts +24 -0
  11. package/dist/effect/FXEffectSampling.Internal.js +165 -0
  12. package/dist/effect/FXEffectSpec.d.ts +115 -0
  13. package/dist/effect/FXEffectSpec.js +1 -0
  14. package/dist/emitter/FXEmitter.Internal.d.ts +23 -58
  15. package/dist/emitter/FXEmitter.Internal.js +13 -29
  16. package/dist/emitter/FXEmitter.d.ts +54 -46
  17. package/dist/emitter/FXEmitter.js +357 -186
  18. package/dist/emitter/FXFromArtifactsOptions.d.ts +13 -0
  19. package/dist/emitter/FXFromArtifactsOptions.js +1 -0
  20. package/dist/emitter/FXObjectMotion.Internal.d.ts +33 -0
  21. package/dist/emitter/FXObjectMotion.Internal.js +87 -0
  22. package/dist/emitter/FXSimulationHolder.d.ts +1 -0
  23. package/dist/emitter/FXSimulationHolder.js +43 -0
  24. package/dist/index.d.ts +11 -39
  25. package/dist/index.js +13 -36
  26. package/dist/instancedParticle/FXInstancedParticle.d.ts +1 -22
  27. package/dist/instancedParticle/FXInstancedParticle.js +145 -85
  28. package/dist/instancedParticle/glTypeInfo.d.ts +5 -0
  29. package/dist/instancedParticle/glTypeInfo.js +3 -0
  30. package/dist/instancedParticle/primitiveGeometry.d.ts +10 -0
  31. package/dist/instancedParticle/primitiveGeometry.js +30 -0
  32. package/dist/miscellaneous/asserts.d.ts +1 -18
  33. package/dist/miscellaneous/asserts.js +21 -28
  34. package/dist/miscellaneous/fxMath.d.ts +26 -0
  35. package/dist/miscellaneous/fxMath.js +261 -0
  36. package/dist/miscellaneous/math.d.ts +2 -7
  37. package/dist/miscellaneous/math.js +8 -9
  38. package/dist/miscellaneous/texture/fxDataTexture.d.ts +26 -0
  39. package/dist/miscellaneous/texture/fxDataTexture.js +20 -0
  40. package/dist/render/FXArtifactMaterial.Internal.d.ts +16 -0
  41. package/dist/render/FXArtifactMaterial.Internal.js +944 -0
  42. package/dist/render/FXArtifactMaterial.d.ts +1 -0
  43. package/dist/render/FXArtifactMaterial.js +79 -0
  44. package/dist/render/FXMaterialOptions.d.ts +27 -0
  45. package/dist/render/FXMaterialOptions.js +5 -0
  46. package/dist/render/FXMesh.d.ts +47 -0
  47. package/dist/render/FXMesh.js +99 -0
  48. package/dist/render/FXMeshMaterial.d.ts +40 -0
  49. package/dist/render/FXMeshMaterial.js +77 -0
  50. package/dist/world/FXWorld.d.ts +52 -0
  51. package/dist/world/FXWorld.js +104 -0
  52. package/package.json +49 -29
  53. package/dist/_virtual/2d.js +0 -3
  54. package/dist/_virtual/3d.js +0 -3
  55. package/dist/_virtual/4d.js +0 -3
  56. package/dist/_virtual/mod.js +0 -5
  57. package/dist/_virtual/mod2.js +0 -3
  58. package/dist/emitter/behavior/FXBehavior.d.ts +0 -24
  59. package/dist/emitter/behavior/FXBehavior.js +0 -10
  60. package/dist/emitter/behavior/FXBehaviorDirectionalForce.d.ts +0 -23
  61. package/dist/emitter/behavior/FXBehaviorDirectionalForce.js +0 -72
  62. package/dist/emitter/behavior/FXBehaviorPointForce.d.ts +0 -36
  63. package/dist/emitter/behavior/FXBehaviorPointForce.js +0 -94
  64. package/dist/emitter/behavior/FXBehaviorScaleOverLife.d.ts +0 -24
  65. package/dist/emitter/behavior/FXBehaviorScaleOverLife.js +0 -55
  66. package/dist/emitter/behavior/FXBehaviorTorqueDamping.d.ts +0 -20
  67. package/dist/emitter/behavior/FXBehaviorTorqueDamping.js +0 -48
  68. package/dist/emitter/behavior/FXBehaviorTorqueNoise.d.ts +0 -25
  69. package/dist/emitter/behavior/FXBehaviorTorqueNoise.js +0 -67
  70. package/dist/emitter/behavior/FXBehaviorTorqueOverLife.d.ts +0 -19
  71. package/dist/emitter/behavior/FXBehaviorTorqueOverLife.js +0 -41
  72. package/dist/emitter/behavior/FXBehaviorVelocityDamping.d.ts +0 -20
  73. package/dist/emitter/behavior/FXBehaviorVelocityDamping.js +0 -50
  74. package/dist/emitter/behavior/FXBehaviorVelocityNoise.d.ts +0 -25
  75. package/dist/emitter/behavior/FXBehaviorVelocityNoise.js +0 -76
  76. package/dist/emitter/behavior/FXBehaviorVelocityOverLife.d.ts +0 -22
  77. package/dist/emitter/behavior/FXBehaviorVelocityOverLife.js +0 -55
  78. package/dist/emitter/spawn/FXSpawn.d.ts +0 -11
  79. package/dist/emitter/spawn/FXSpawn.js +0 -10
  80. package/dist/emitter/spawn/FXSpawnBox.d.ts +0 -18
  81. package/dist/emitter/spawn/FXSpawnBox.js +0 -50
  82. package/dist/emitter/spawn/FXSpawnLifetime.d.ts +0 -17
  83. package/dist/emitter/spawn/FXSpawnLifetime.js +0 -44
  84. package/dist/emitter/spawn/FXSpawnOffset.d.ts +0 -21
  85. package/dist/emitter/spawn/FXSpawnOffset.js +0 -49
  86. package/dist/emitter/spawn/FXSpawnPoint.d.ts +0 -18
  87. package/dist/emitter/spawn/FXSpawnPoint.js +0 -46
  88. package/dist/emitter/spawn/FXSpawnRotation.d.ts +0 -17
  89. package/dist/emitter/spawn/FXSpawnRotation.js +0 -42
  90. package/dist/emitter/spawn/FXSpawnScale.d.ts +0 -22
  91. package/dist/emitter/spawn/FXSpawnScale.js +0 -56
  92. package/dist/emitter/spawn/FXSpawnSphere.d.ts +0 -36
  93. package/dist/emitter/spawn/FXSpawnSphere.js +0 -87
  94. package/dist/emitter/spawn/FXSpawnTorque.d.ts +0 -17
  95. package/dist/emitter/spawn/FXSpawnTorque.js +0 -42
  96. package/dist/emitter/spawn/FXSpawnVelocity.d.ts +0 -29
  97. package/dist/emitter/spawn/FXSpawnVelocity.js +0 -91
  98. package/dist/instancedParticle/FXInstancedParticle.Internal.d.ts +0 -2
  99. package/dist/instancedParticle/shared.d.ts +0 -19
  100. package/dist/instancedParticle/shared.js +0 -116
  101. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +0 -24
  102. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +0 -275
  103. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.d.ts +0 -75
  104. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +0 -63
  105. package/dist/materials/FXMaterial/FXBlending.Internal.d.ts +0 -24
  106. package/dist/materials/FXMaterial/FXBlending.Internal.js +0 -64
  107. package/dist/materials/FXMaterial/FXDepthMaterial.Internal.d.ts +0 -4
  108. package/dist/materials/FXMaterial/FXDepthMaterial.Internal.js +0 -144
  109. package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.d.ts +0 -4
  110. package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.js +0 -175
  111. package/dist/materials/FXMaterial/FXMaterial.d.ts +0 -70
  112. package/dist/materials/FXMaterial/FXMaterial.js +0 -51
  113. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +0 -7
  114. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.js +0 -103
  115. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.d.ts +0 -7
  116. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +0 -21
  117. package/dist/miscellaneous/color/FXColor.Internal.d.ts +0 -7
  118. package/dist/miscellaneous/color/FXColor.Internal.js +0 -42
  119. package/dist/miscellaneous/color/FXColor.d.ts +0 -234
  120. package/dist/miscellaneous/color/FXColor.js +0 -636
  121. package/dist/miscellaneous/curve/FXCurve1D.Internal.d.ts +0 -20
  122. package/dist/miscellaneous/curve/FXCurve1D.d.ts +0 -17
  123. package/dist/miscellaneous/curve/FXCurve1D.js +0 -94
  124. package/dist/miscellaneous/miscellaneous.d.ts +0 -34
  125. package/dist/miscellaneous/miscellaneous.js +0 -85
  126. package/dist/miscellaneous/texture/FXTextureView.Internal.d.ts +0 -49
  127. package/dist/miscellaneous/texture/FXTextureView.Internal.js +0 -6
  128. package/dist/miscellaneous/texture/FXTextureView.d.ts +0 -72
  129. package/dist/miscellaneous/texture/FXTextureView.js +0 -234
  130. package/dist/miscellaneous/webglCapabilities.d.ts +0 -1
  131. package/dist/miscellaneous/webglCapabilities.js +0 -45
  132. package/dist/node_modules/fast-simplex-noise/lib/2d.js +0 -93
  133. package/dist/node_modules/fast-simplex-noise/lib/3d.js +0 -142
  134. package/dist/node_modules/fast-simplex-noise/lib/4d.js +0 -196
  135. package/dist/node_modules/fast-simplex-noise/lib/mod.js +0 -24
  136. package/dist/nodes/FXNode.d.ts +0 -5
  137. package/dist/nodes/FXNode.js +0 -10
  138. package/dist/nodes/blending/FXNodeBlending.d.ts +0 -6
  139. package/dist/nodes/blending/FXNodeBlending.js +0 -9
  140. package/dist/nodes/blending/FXNodeLightnessBlendingMask.d.ts +0 -19
  141. package/dist/nodes/blending/FXNodeLightnessBlendingMask.js +0 -65
  142. package/dist/nodes/color/FXNodeColor.d.ts +0 -6
  143. package/dist/nodes/color/FXNodeColor.js +0 -9
  144. package/dist/nodes/color/FXNodeColorOverLife.d.ts +0 -22
  145. package/dist/nodes/color/FXNodeColorOverLife.js +0 -64
  146. package/dist/nodes/color/FXNodeSphericalClip.d.ts +0 -21
  147. package/dist/nodes/color/FXNodeSphericalClip.js +0 -54
  148. package/dist/nodes/normal/FXNodeFlatNormal.d.ts +0 -6
  149. package/dist/nodes/normal/FXNodeFlatNormal.js +0 -16
  150. package/dist/nodes/normal/FXNodeNormal.d.ts +0 -6
  151. package/dist/nodes/normal/FXNodeNormal.js +0 -9
  152. package/dist/nodes/normal/FXNodeSphericalNormal.d.ts +0 -6
  153. package/dist/nodes/normal/FXNodeSphericalNormal.js +0 -23
  154. package/dist/nodes/texture/FXNodeAnimatedTexture.d.ts +0 -52
  155. package/dist/nodes/texture/FXNodeAnimatedTexture.js +0 -151
  156. package/dist/nodes/texture/FXNodeStaticTexture.d.ts +0 -20
  157. package/dist/nodes/texture/FXNodeStaticTexture.js +0 -67
  158. package/dist/nodes/texture/FXNodeTexture.d.ts +0 -6
  159. package/dist/nodes/texture/FXNodeTexture.js +0 -9
@@ -0,0 +1,115 @@
1
+ import type { Camera } from "three";
2
+ import type { FXBehaviorArtifact, FXGeometrySource, FXRenderArtifact } from "../artifact/FXArtifact.js";
3
+ import type { FXWorld } from "../world/FXWorld.js";
4
+ /**
5
+ * Data shapes for {@link FXEffect}: a whole exported project (every emitter + VFX mesh, its
6
+ * timeline and transforms) as plain, JSON-serializable data. The editor emits an `FXEffectSpec`
7
+ * literal into every project module; this file plus {@link FXEffect} is the runtime that plays it
8
+ * back, so the editor no longer needs to emit this scaffolding itself.
9
+ *
10
+ * Everything here is `readonly` so it structurally matches the editor's own `Transform` /
11
+ * `TransformTrack` / `AnimationTrack` model types - the editor's richer `Keyframe` (which also
12
+ * carries a UI-only `id`) is a valid `FXKeyframe` too, so the sampling functions in
13
+ * {@link FXEffectSampling.Internal.ts} serve both.
14
+ */
15
+ export type FXVec3 = readonly [number, number, number];
16
+ export type FXQuat = readonly [number, number, number, number];
17
+ export interface FXTransform {
18
+ readonly position: FXVec3;
19
+ readonly rotation: FXQuat;
20
+ readonly scale: FXVec3;
21
+ }
22
+ export interface FXKeyframe {
23
+ readonly time: number;
24
+ readonly value: number | readonly number[];
25
+ }
26
+ /** A named value track (drives a render uniform / behavior binding by parameter name). */
27
+ export interface FXTrack {
28
+ readonly name: string;
29
+ readonly keys: readonly FXKeyframe[];
30
+ }
31
+ export type FXTransformChannel = "position" | "rotation" | "scale";
32
+ export interface FXTransformTrack {
33
+ readonly channel: FXTransformChannel;
34
+ readonly keys: readonly FXKeyframe[];
35
+ }
36
+ /** A timeline event: an instantaneous burst or a sustained emission rate over a window. */
37
+ export type FXEffectEvent = {
38
+ readonly kind: "burst";
39
+ readonly time: number;
40
+ readonly count: number;
41
+ } | {
42
+ readonly kind: "play";
43
+ readonly time: number;
44
+ readonly rate: number;
45
+ readonly duration: number;
46
+ };
47
+ /** One emitter's precompiled artifacts, transform, and timeline. */
48
+ export interface FXEffectEmitterSpec {
49
+ /** The editor-authored name, addressing this emitter through {@link FXEffect.getEmitter} etc. */
50
+ readonly name: string;
51
+ readonly render: FXRenderArtifact;
52
+ readonly behavior: FXBehaviorArtifact;
53
+ readonly expectedCapacity: number;
54
+ readonly sortInterval: number;
55
+ /** Casts a shape-aware shadow; omitted (treated as false) by pre-shadow exports. */
56
+ readonly castShadow?: boolean;
57
+ /** Receives shadows; omitted (treated as false) by pre-shadow exports. */
58
+ readonly receiveShadow?: boolean;
59
+ readonly externalSlots: readonly string[];
60
+ readonly transform: FXTransform;
61
+ readonly transformTracks: readonly FXTransformTrack[];
62
+ readonly tracks: readonly FXTrack[];
63
+ readonly events: readonly FXEffectEvent[];
64
+ /** Transform channels the editor marked "fake": never sampled from `transformTracks`, left for
65
+ * {@link FXEffect.getEmitter} to drive directly. */
66
+ readonly liveChannels: readonly FXTransformChannel[];
67
+ /** Timeline Value names the editor marked "fake": excluded from `tracks`, driven only through
68
+ * {@link FXEffect.setEmitterParam}. */
69
+ readonly liveParams: readonly string[];
70
+ }
71
+ /** One VFX mesh's precompiled render artifact, transform, and value tracks (no behavior/events). */
72
+ export interface FXEffectMeshSpec {
73
+ /** The editor-authored name, addressing this mesh through {@link FXEffect.getMesh} etc. */
74
+ readonly name: string;
75
+ readonly render: FXRenderArtifact;
76
+ readonly geometry: FXGeometrySource;
77
+ /** See {@link FXEffectEmitterSpec.castShadow}. */
78
+ readonly castShadow?: boolean;
79
+ /** See {@link FXEffectEmitterSpec.receiveShadow}. */
80
+ readonly receiveShadow?: boolean;
81
+ readonly externalSlots: readonly string[];
82
+ readonly transform: FXTransform;
83
+ readonly transformTracks: readonly FXTransformTrack[];
84
+ readonly tracks: readonly FXTrack[];
85
+ /** See {@link FXEffectEmitterSpec.liveChannels}. */
86
+ readonly liveChannels: readonly FXTransformChannel[];
87
+ /** See {@link FXEffectEmitterSpec.liveParams}. */
88
+ readonly liveParams: readonly string[];
89
+ }
90
+ /** A whole exported project: duration/fps, root transform, and every emitter + mesh. */
91
+ export interface FXEffectSpec {
92
+ readonly duration: number;
93
+ readonly fps: number;
94
+ readonly transform: FXTransform;
95
+ /**
96
+ * Always empty: the editor forces every root transform channel "fake", so the root pose is
97
+ * never sampled after construction (see {@link FXEffect}) - the consumer poses the effect
98
+ * instance itself through the inherited `Object3D` position/quaternion/scale, like any other
99
+ * `Group` added to a scene.
100
+ */
101
+ readonly transformTracks: readonly FXTransformTrack[];
102
+ readonly emitters: readonly FXEffectEmitterSpec[];
103
+ readonly meshes: readonly FXEffectMeshSpec[];
104
+ }
105
+ /** Options shared by every {@link FXEffect}. */
106
+ export interface FXEffectOptions {
107
+ /** Camera enabling per-emitter depth sorting (only emitters authored with a sort interval sort). */
108
+ readonly camera?: Camera;
109
+ /**
110
+ * The tick domain this effect joins. Omit to join the lazily created default world, which the
111
+ * static {@link FXWorld.update} advances. Pass an {@link FXWorld} to isolate the effect in its
112
+ * own clock and particle pool (an independent time scale, or a group you tick separately).
113
+ */
114
+ readonly world?: FXWorld;
115
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,79 +1,44 @@
1
- import type { Camera } from "three";
2
- import type { FXPropertyName, GLTypeInfo } from "../instancedParticle/shared";
3
- import type { FXEmitter } from "./FXEmitter";
1
+ import type { Camera, Texture } from "three";
4
2
  /**
5
- * Configuration options for {@link FXEmitter}
3
+ * Argument to {@link FXEmitter.applyValues}: values to scrub into uniform/binding slots by name.
4
+ * Both maps optional; an unknown name is a safe no-op. Only reaches parameters the compiled
5
+ * GLSL/kernel already declared - a structural edit is a fresh {@link FXEmitter.fromArtifacts}.
6
6
  */
7
+ export interface FXApplyValues {
8
+ /** Scalar/vector, or a `Texture` for a `sampler2D`. Mutates the shared artifact slot, or this emitter's own slot for an `external` texture. */
9
+ readonly uniforms?: Readonly<Record<string, number | readonly number[] | Texture>>;
10
+ /** A `number`, or a `Float32Array` curve LUT. */
11
+ readonly bindings?: Readonly<Record<string, number | Float32Array>>;
12
+ }
7
13
  export interface FXEmitterOptions {
8
- /**
9
- * Initial buffer size in particles; grows automatically when exceeded
10
- *
11
- * @defaultValue `32`
12
- */
14
+ /** Initial buffer size in particles; grows automatically. default 32 */
13
15
  expectedCapacity: number;
14
- /**
15
- * Growth increment added to the buffer when capacity is reached
16
- *
17
- * @defaultValue `32`
18
- */
16
+ /** Growth increment when capacity is reached. default 32 */
19
17
  capacityStep: number;
20
- /**
21
- * Enables shadow casting
22
- *
23
- * @defaultValue `false`
24
- */
25
- castShadow: boolean;
26
- /**
27
- * Enables shadow receiving
28
- *
29
- * @defaultValue `false`
30
- */
18
+ /** default false */
31
19
  receiveShadow: boolean;
32
- /** Camera used for back-to-front depth sorting; omit to disable sorting */
20
+ /** Casts a shape-aware shadow (a customDepthMaterial built from the render artifact). default false */
21
+ castShadow: boolean;
22
+ /** Camera for back-to-front depth sorting; omit to disable sorting. */
33
23
  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
- */
24
+ /** Fraction of frames sorting runs on (`1` = every frame, `0.1` = ~every 10th). default 0.1 */
39
25
  sortFraction: number;
40
26
  }
41
- /**
42
- * Options for {@link FXEmitter.burst}
43
- */
44
27
  export interface FXEmitterBurstOptions {
45
- /**
46
- * Delay in seconds before particles are spawned
47
- *
48
- * @defaultValue `0`
49
- */
28
+ /** Delay in seconds before spawning. default 0 */
50
29
  delay: number;
51
30
  }
52
- /**
53
- * Options for {@link FXEmitter.play}
54
- */
55
31
  export interface FXEmitterPlayOptions {
56
- /**
57
- * Delay in seconds before emission starts
58
- *
59
- * @defaultValue `0`
60
- */
32
+ /** Delay in seconds before emission starts. default 0 */
61
33
  delay: number;
62
- /**
63
- * Total emission duration in seconds
64
- *
65
- * @defaultValue `Infinity`
66
- */
34
+ /** Total emission duration in seconds. default Infinity */
67
35
  duration: number;
68
36
  }
69
- export declare const EMITTERS: FXEmitter[];
70
37
  export declare const EMITTER_DEFAULT_EXPECTED_CAPACITY = 32;
71
38
  export declare const EMITTER_DEFAULT_CAPACITY_STEP = 32;
72
- export declare const EMITTER_DEFAULT_CAST_SHADOW = false;
73
39
  export declare const EMITTER_DEFAULT_RECEIVE_SHADOW = false;
40
+ export declare const EMITTER_DEFAULT_CAST_SHADOW = false;
74
41
  export declare const EMITTER_DEFAULT_SORT_FRACTION: number;
75
42
  export declare const EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION: number;
76
- export declare const EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT = 10;
77
- export declare function collectProperties(keeper: Record<string, GLTypeInfo>, modules: readonly {
78
- requiredProperties?: Record<string, FXPropertyName>;
79
- }[], debugContext: string): void;
43
+ export declare const EMITTER_TIME_WRAP_PERIOD: number;
44
+ export declare const EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT = 1000;
@@ -1,29 +1,13 @@
1
- import { resolveGLSLTypeInfo } from '../instancedParticle/shared.js';
2
-
3
- const EMITTERS = new Array();
4
- const EMITTER_DEFAULT_EXPECTED_CAPACITY = 32;
5
- const EMITTER_DEFAULT_CAPACITY_STEP = 32;
6
- const EMITTER_DEFAULT_CAST_SHADOW = false;
7
- const EMITTER_DEFAULT_RECEIVE_SHADOW = false;
8
- const EMITTER_DEFAULT_SORT_FRACTION = 1 / 10;
9
- const EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION = 1 / 60;
10
- const EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT = 10;
11
- function collectProperties(keeper, modules, debugContext) {
12
- for (const module of modules) {
13
- if (module.requiredProperties === undefined) {
14
- continue;
15
- }
16
- for (const key in module.requiredProperties) {
17
- const existingTypeInfo = keeper[key];
18
- const newTypeInfo = resolveGLSLTypeInfo(module.requiredProperties[key]);
19
- if (existingTypeInfo === undefined) {
20
- keeper[key] = newTypeInfo;
21
- }
22
- else if (existingTypeInfo.glslTypeName !== newTypeInfo.glslTypeName) {
23
- throw new Error(`${debugContext}: property conflict for "${key}"`);
24
- }
25
- }
26
- }
27
- }
28
-
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 };
1
+ export const EMITTER_DEFAULT_EXPECTED_CAPACITY = 32;
2
+ export const EMITTER_DEFAULT_CAPACITY_STEP = 32;
3
+ export const EMITTER_DEFAULT_RECEIVE_SHADOW = false;
4
+ export const EMITTER_DEFAULT_CAST_SHADOW = false;
5
+ export const EMITTER_DEFAULT_SORT_FRACTION = 1 / 10;
6
+ export const EMITTER_DEFAULT_PREWARM_MIN_STEP_DURATION = 1 / 60;
7
+ // A float32 `u_time` accumulated forever quantizes to multi-millisecond steps after hours; wrapping
8
+ // keeps it small. A large multiple of 2pi (~1 hour) keeps unit-frequency sin/cos continuous across
9
+ // the wrap - other frequencies see at most one negligible seam per hour.
10
+ export const EMITTER_TIME_WRAP_PERIOD = 573 * 2 * Math.PI;
11
+ // Runaway guard, not a quality knob. High enough that a normal `prewarm(duration)` at the default
12
+ // `1/60` step honors its step size instead of being silently coarsened.
13
+ export const EMITTER_DEFAULT_PREWARM_MAX_STEP_COUNT = 1000;
@@ -1,81 +1,89 @@
1
1
  import type { Camera } from "three";
2
2
  import { Object3D } from "three";
3
- import type { FXMaterial } from "../materials/FXMaterial/FXMaterial";
4
- import type { FXBehavior } from "./behavior/FXBehavior";
5
- import type { FXEmitterBurstOptions, FXEmitterOptions, FXEmitterPlayOptions } from "./FXEmitter.Internal";
6
- import type { FXSpawn } from "./spawn/FXSpawn";
3
+ import type { FXBehaviorArtifact, FXRenderArtifact } from "../artifact/FXArtifact.js";
4
+ import type { FXFromArtifactsOptions } from "./FXFromArtifactsOptions.js";
5
+ import { FXWorld } from "../world/FXWorld.js";
6
+ import type { FXApplyValues, FXEmitterBurstOptions, FXEmitterPlayOptions } from "./FXEmitter.Internal.js";
7
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.
8
+ * Particle emitter - one instanced billboard/mesh draw call, per-particle state driven by a
9
+ * precompiled behavior artifact and appearance by a precompiled render artifact spliced into a
10
+ * Three material. Built and ticked by {@link FXEffect} through an {@link FXWorld}. Not exported from
11
+ * the package (the runtime path is running artifacts, not hand-building emitters); reached only as
12
+ * the live handle returned by {@link FXEffect.getEmitter}.
13
13
  */
14
14
  export declare class FXEmitter extends Object3D {
15
- private readonly spawnSequence;
16
- private readonly behaviorSequence;
17
- /** Camera used for back-to-front depth sorting; `undefined` disables sorting */
15
+ /** Camera for back-to-front depth sorting; `undefined` disables it. Distances are world-space, so any emitter/ancestor transform is handled correctly. */
18
16
  sortCamera?: Camera;
19
- /** Fraction of frames on which sorting runs (`1` = every frame, `0.1` = ~every 10th frame); defaults to `0.1` */
17
+ /** Fraction of frames sorting runs on (`1` = every frame, `0.1` = ~every 10th); defaults to `0.1`. */
20
18
  sortFraction: number;
19
+ private readonly world;
21
20
  private readonly mesh;
22
21
  private readonly material;
22
+ private readonly simulation?;
23
+ private readonly collectedProperties;
23
24
  private nextHandler;
24
25
  private readonly pendingBursts;
25
26
  private readonly activePlays;
26
27
  private sortingAccumulator;
27
28
  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
- */
34
- constructor(spawnSequence: FXSpawn[], behaviorSequence: readonly FXBehavior[], material: FXMaterial, options?: Partial<FXEmitterOptions>);
35
- /** Number of currently alive particles */
29
+ private readonly emitterTransform;
30
+ private readonly motionPositionScratch;
31
+ private readonly motionQuaternionScratch;
32
+ private readonly motionScaleScratch;
33
+ private readonly motionTracker;
34
+ private destroyed;
35
+ private kernelBuffersCache?;
36
+ private kernelBuffersCacheVersion;
36
37
  get particleCount(): number;
37
- /** Maximum number of particles that fit in the currently allocated buffers */
38
38
  get particleCapacity(): number;
39
39
  /**
40
- * Ticks all active emitters; call once per frame before rendering
41
- *
42
- * @param deltaTime - Elapsed time in seconds since the last frame
40
+ * Builds an emitter into `world` from a precompiled effect - the low-level building block behind
41
+ * {@link FXEffect}. Exposed for editor tooling through the `sparcoon/editor` entry (live preview
42
+ * rebuilds a single emitter per graph edit); not part of the main package surface, where the
43
+ * runtime path is running whole projects through `FXEffect`.
43
44
  */
44
- static onWillRender: (deltaTime: number) => void;
45
- /** Unregisters the emitter, removes it from the scene, and disposes modules and material */
45
+ static fromArtifacts(render: FXRenderArtifact, behavior: FXBehaviorArtifact, options?: FXFromArtifactsOptions, world?: FXWorld): FXEmitter;
46
+ /** Unregisters, removes from the scene, disposes mesh and material. Idempotent. */
46
47
  destroy(): void;
47
48
  /**
48
- * Spawns `count` particles immediately, or after a delay when `options.delay` is set
49
- *
50
- * @param count - Number of particles to spawn
51
- * @param options - Burst options
52
- * @returns Handler that can be passed to {@link stop} to cancel a pending delayed burst
49
+ * Spawns `count` particles immediately, or after `options.delay`.
50
+ * @returns Handler for {@link stop} to cancel a pending delayed burst.
53
51
  */
54
52
  burst(count: number, options?: Partial<FXEmitterBurstOptions>): number;
55
53
  /**
56
- * Starts continuous emission at the given rate
57
- *
58
- * @param rate - Particles per second
59
- * @param options - Play options
60
- * @returns Handler that can be passed to {@link stop} to cancel
54
+ * Starts continuous emission at `rate` particles/second.
55
+ * @returns Handler for {@link stop} to cancel.
61
56
  */
62
57
  play(rate: number, options?: Partial<FXEmitterPlayOptions>): number;
63
58
  /**
64
- * Stops an active play or pending burst by handler; stops all when called with no arguments
65
- *
66
- * @param handler - Handler returned by {@link play} or {@link burst}; omit to stop everything
67
- * @returns `true` if anything was stopped
59
+ * Stops an active play or pending burst by handler; stops all when called with no arguments.
60
+ * @returns `true` if anything was stopped.
68
61
  */
69
62
  stop(handler?: number): boolean;
70
63
  /**
71
- * Simulates the emitter forward in time to avoid a cold-start on frame 0
72
- *
73
- * @param duration - Total time to simulate in seconds
74
- * @param stepDuration - Maximum simulation step size in seconds. Defaults to `1/60`
64
+ * Simulates forward in time to avoid a cold-start on frame 0.
65
+ * @param stepDuration - Maximum step size in seconds. Defaults to `1/60`.
75
66
  */
76
67
  prewarm(duration: number, stepDuration?: number): void;
77
- /** Kills all live particles and cancels all active plays and pending bursts */
68
+ /** Kills all live particles and cancels all active plays and pending bursts. */
78
69
  reset(): void;
70
+ /**
71
+ * Scrubs live parameter values into uniform/binding slots by name, picked up next frame - the
72
+ * runtime's value channel for a non-structural edit. Unknown name = safe no-op; never recompiles.
73
+ * A structural edit is instead a fresh {@link fromArtifacts} (the editor decides which).
74
+ */
75
+ applyValues(values: FXApplyValues): void;
76
+ /**
77
+ * Merged attribute set (name -> component count): union of the behavior's writes and the render's
78
+ * reads. Throws on a cross-artifact width disagreement rather than sizing a buffer wrong.
79
+ */
80
+ private attributeComponents;
81
+ private buildCollectedProperties;
82
+ private setSortFraction;
83
+ private assertCanSpawn;
84
+ private kernelBuffers;
85
+ private worldTransform;
86
+ private markBuffersNeedUpdate;
79
87
  private tick;
80
88
  private readonly onRendering;
81
89
  private spawnBurst;