sparcoon 0.7.2 → 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.
Files changed (155) 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/effect/FXEffect.d.ts +77 -0
  7. package/dist/effect/FXEffect.js +454 -0
  8. package/dist/effect/FXEffectSampling.Internal.d.ts +24 -0
  9. package/dist/effect/FXEffectSampling.Internal.js +165 -0
  10. package/dist/effect/FXEffectSpec.d.ts +115 -0
  11. package/dist/effect/FXEffectSpec.js +1 -0
  12. package/dist/emitter/FXEmitter.Internal.d.ts +23 -58
  13. package/dist/emitter/FXEmitter.Internal.js +13 -29
  14. package/dist/emitter/FXEmitter.d.ts +46 -48
  15. package/dist/emitter/FXEmitter.js +355 -186
  16. package/dist/emitter/FXFromArtifactsOptions.d.ts +13 -0
  17. package/dist/emitter/FXFromArtifactsOptions.js +1 -0
  18. package/dist/emitter/FXObjectMotion.Internal.d.ts +33 -0
  19. package/dist/emitter/FXObjectMotion.Internal.js +87 -0
  20. package/dist/emitter/FXSimulationHolder.d.ts +1 -0
  21. package/dist/emitter/FXSimulationHolder.js +43 -0
  22. package/dist/index.d.ts +10 -39
  23. package/dist/index.js +13 -36
  24. package/dist/instancedParticle/FXInstancedParticle.d.ts +1 -22
  25. package/dist/instancedParticle/FXInstancedParticle.js +145 -85
  26. package/dist/instancedParticle/glTypeInfo.d.ts +5 -0
  27. package/dist/instancedParticle/glTypeInfo.js +3 -0
  28. package/dist/instancedParticle/primitiveGeometry.d.ts +1 -0
  29. package/dist/instancedParticle/primitiveGeometry.js +30 -0
  30. package/dist/miscellaneous/asserts.d.ts +1 -18
  31. package/dist/miscellaneous/asserts.js +21 -28
  32. package/dist/miscellaneous/fxMath.d.ts +26 -0
  33. package/dist/miscellaneous/fxMath.js +261 -0
  34. package/dist/miscellaneous/math.d.ts +2 -7
  35. package/dist/miscellaneous/math.js +8 -9
  36. package/dist/miscellaneous/texture/fxDataTexture.d.ts +26 -0
  37. package/dist/miscellaneous/texture/fxDataTexture.js +20 -0
  38. package/dist/render/FXArtifactMaterial.Internal.d.ts +16 -0
  39. package/dist/render/FXArtifactMaterial.Internal.js +944 -0
  40. package/dist/render/FXArtifactMaterial.d.ts +1 -0
  41. package/dist/render/FXArtifactMaterial.js +79 -0
  42. package/dist/render/FXMaterialOptions.d.ts +27 -0
  43. package/dist/render/FXMaterialOptions.js +5 -0
  44. package/dist/render/FXMeshMaterial.d.ts +40 -0
  45. package/dist/render/FXMeshMaterial.js +77 -0
  46. package/dist/world/FXWorld.d.ts +42 -0
  47. package/dist/world/FXWorld.js +103 -0
  48. package/package.json +45 -29
  49. package/dist/_virtual/2d.js +0 -3
  50. package/dist/_virtual/3d.js +0 -3
  51. package/dist/_virtual/4d.js +0 -3
  52. package/dist/_virtual/mod.js +0 -5
  53. package/dist/_virtual/mod2.js +0 -3
  54. package/dist/emitter/behavior/FXBehavior.d.ts +0 -24
  55. package/dist/emitter/behavior/FXBehavior.js +0 -10
  56. package/dist/emitter/behavior/FXBehaviorDirectionalForce.d.ts +0 -23
  57. package/dist/emitter/behavior/FXBehaviorDirectionalForce.js +0 -72
  58. package/dist/emitter/behavior/FXBehaviorPointForce.d.ts +0 -36
  59. package/dist/emitter/behavior/FXBehaviorPointForce.js +0 -94
  60. package/dist/emitter/behavior/FXBehaviorScaleOverLife.d.ts +0 -24
  61. package/dist/emitter/behavior/FXBehaviorScaleOverLife.js +0 -55
  62. package/dist/emitter/behavior/FXBehaviorTorqueDamping.d.ts +0 -20
  63. package/dist/emitter/behavior/FXBehaviorTorqueDamping.js +0 -48
  64. package/dist/emitter/behavior/FXBehaviorTorqueNoise.d.ts +0 -25
  65. package/dist/emitter/behavior/FXBehaviorTorqueNoise.js +0 -67
  66. package/dist/emitter/behavior/FXBehaviorTorqueOverLife.d.ts +0 -19
  67. package/dist/emitter/behavior/FXBehaviorTorqueOverLife.js +0 -41
  68. package/dist/emitter/behavior/FXBehaviorVelocityDamping.d.ts +0 -20
  69. package/dist/emitter/behavior/FXBehaviorVelocityDamping.js +0 -50
  70. package/dist/emitter/behavior/FXBehaviorVelocityNoise.d.ts +0 -25
  71. package/dist/emitter/behavior/FXBehaviorVelocityNoise.js +0 -76
  72. package/dist/emitter/behavior/FXBehaviorVelocityOverLife.d.ts +0 -22
  73. package/dist/emitter/behavior/FXBehaviorVelocityOverLife.js +0 -55
  74. package/dist/emitter/spawn/FXSpawn.d.ts +0 -11
  75. package/dist/emitter/spawn/FXSpawn.js +0 -10
  76. package/dist/emitter/spawn/FXSpawnBox.d.ts +0 -18
  77. package/dist/emitter/spawn/FXSpawnBox.js +0 -50
  78. package/dist/emitter/spawn/FXSpawnLifetime.d.ts +0 -17
  79. package/dist/emitter/spawn/FXSpawnLifetime.js +0 -44
  80. package/dist/emitter/spawn/FXSpawnOffset.d.ts +0 -21
  81. package/dist/emitter/spawn/FXSpawnOffset.js +0 -49
  82. package/dist/emitter/spawn/FXSpawnPoint.d.ts +0 -18
  83. package/dist/emitter/spawn/FXSpawnPoint.js +0 -46
  84. package/dist/emitter/spawn/FXSpawnRotation.d.ts +0 -17
  85. package/dist/emitter/spawn/FXSpawnRotation.js +0 -42
  86. package/dist/emitter/spawn/FXSpawnScale.d.ts +0 -22
  87. package/dist/emitter/spawn/FXSpawnScale.js +0 -56
  88. package/dist/emitter/spawn/FXSpawnSphere.d.ts +0 -36
  89. package/dist/emitter/spawn/FXSpawnSphere.js +0 -87
  90. package/dist/emitter/spawn/FXSpawnTorque.d.ts +0 -17
  91. package/dist/emitter/spawn/FXSpawnTorque.js +0 -42
  92. package/dist/emitter/spawn/FXSpawnVelocity.d.ts +0 -29
  93. package/dist/emitter/spawn/FXSpawnVelocity.js +0 -91
  94. package/dist/instancedParticle/FXInstancedParticle.Internal.d.ts +0 -2
  95. package/dist/instancedParticle/shared.d.ts +0 -19
  96. package/dist/instancedParticle/shared.js +0 -116
  97. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +0 -24
  98. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +0 -275
  99. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.d.ts +0 -75
  100. package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +0 -63
  101. package/dist/materials/FXMaterial/FXBlending.Internal.d.ts +0 -24
  102. package/dist/materials/FXMaterial/FXBlending.Internal.js +0 -64
  103. package/dist/materials/FXMaterial/FXDepthMaterial.Internal.d.ts +0 -4
  104. package/dist/materials/FXMaterial/FXDepthMaterial.Internal.js +0 -144
  105. package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.d.ts +0 -4
  106. package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.js +0 -175
  107. package/dist/materials/FXMaterial/FXMaterial.d.ts +0 -70
  108. package/dist/materials/FXMaterial/FXMaterial.js +0 -51
  109. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +0 -7
  110. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.js +0 -103
  111. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.d.ts +0 -7
  112. package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +0 -21
  113. package/dist/miscellaneous/color/FXColor.Internal.d.ts +0 -7
  114. package/dist/miscellaneous/color/FXColor.Internal.js +0 -42
  115. package/dist/miscellaneous/color/FXColor.d.ts +0 -234
  116. package/dist/miscellaneous/color/FXColor.js +0 -636
  117. package/dist/miscellaneous/curve/FXCurve1D.Internal.d.ts +0 -20
  118. package/dist/miscellaneous/curve/FXCurve1D.d.ts +0 -17
  119. package/dist/miscellaneous/curve/FXCurve1D.js +0 -94
  120. package/dist/miscellaneous/miscellaneous.d.ts +0 -34
  121. package/dist/miscellaneous/miscellaneous.js +0 -85
  122. package/dist/miscellaneous/texture/FXTextureView.Internal.d.ts +0 -49
  123. package/dist/miscellaneous/texture/FXTextureView.Internal.js +0 -6
  124. package/dist/miscellaneous/texture/FXTextureView.d.ts +0 -72
  125. package/dist/miscellaneous/texture/FXTextureView.js +0 -234
  126. package/dist/miscellaneous/webglCapabilities.d.ts +0 -1
  127. package/dist/miscellaneous/webglCapabilities.js +0 -45
  128. package/dist/node_modules/fast-simplex-noise/lib/2d.js +0 -93
  129. package/dist/node_modules/fast-simplex-noise/lib/3d.js +0 -142
  130. package/dist/node_modules/fast-simplex-noise/lib/4d.js +0 -196
  131. package/dist/node_modules/fast-simplex-noise/lib/mod.js +0 -24
  132. package/dist/nodes/FXNode.d.ts +0 -5
  133. package/dist/nodes/FXNode.js +0 -10
  134. package/dist/nodes/blending/FXNodeBlending.d.ts +0 -6
  135. package/dist/nodes/blending/FXNodeBlending.js +0 -9
  136. package/dist/nodes/blending/FXNodeLightnessBlendingMask.d.ts +0 -19
  137. package/dist/nodes/blending/FXNodeLightnessBlendingMask.js +0 -65
  138. package/dist/nodes/color/FXNodeColor.d.ts +0 -6
  139. package/dist/nodes/color/FXNodeColor.js +0 -9
  140. package/dist/nodes/color/FXNodeColorOverLife.d.ts +0 -22
  141. package/dist/nodes/color/FXNodeColorOverLife.js +0 -64
  142. package/dist/nodes/color/FXNodeSphericalClip.d.ts +0 -21
  143. package/dist/nodes/color/FXNodeSphericalClip.js +0 -54
  144. package/dist/nodes/normal/FXNodeFlatNormal.d.ts +0 -6
  145. package/dist/nodes/normal/FXNodeFlatNormal.js +0 -17
  146. package/dist/nodes/normal/FXNodeNormal.d.ts +0 -6
  147. package/dist/nodes/normal/FXNodeNormal.js +0 -9
  148. package/dist/nodes/normal/FXNodeSphericalNormal.d.ts +0 -6
  149. package/dist/nodes/normal/FXNodeSphericalNormal.js +0 -24
  150. package/dist/nodes/texture/FXNodeAnimatedTexture.d.ts +0 -52
  151. package/dist/nodes/texture/FXNodeAnimatedTexture.js +0 -151
  152. package/dist/nodes/texture/FXNodeStaticTexture.d.ts +0 -20
  153. package/dist/nodes/texture/FXNodeStaticTexture.js +0 -67
  154. package/dist/nodes/texture/FXNodeTexture.d.ts +0 -6
  155. package/dist/nodes/texture/FXNodeTexture.js +0 -9
@@ -1,63 +0,0 @@
1
- import { FXColor } from '../../miscellaneous/color/FXColor.js';
2
- import { FXNodeSphericalNormal } from '../../nodes/normal/FXNodeSphericalNormal.js';
3
- import { FXMaterial } from '../FXMaterial/FXMaterial.js';
4
- import { buildFXDiffuseMaterial } from './FXDiffuseMaterial.Internal.js';
5
-
6
- /**
7
- * Diffuse particle material with lighting, normals, emission, and optional subsurface scattering
8
- */
9
- class FXDiffuseMaterial extends FXMaterial {
10
- /**
11
- * @param options - Material configuration
12
- */
13
- constructor(options = {}) {
14
- super(options);
15
- this._private_normalNodes = options.normalNodes ?? [new FXNodeSphericalNormal()];
16
- this._private_emissionNodes = options.emissionNodes ?? [];
17
- this._private_scatterUniforms =
18
- options.enableScatter === true
19
- ? {
20
- // eslint-disable-next-line @typescript-eslint/naming-convention
21
- u_ScatterTint: { value: new FXColor(options.scatterTint ?? 0xffd999).toThreeColor() },
22
- // eslint-disable-next-line @typescript-eslint/naming-convention
23
- u_ScatterPower: { value: options.scatterPower ?? 10 },
24
- // eslint-disable-next-line @typescript-eslint/naming-convention
25
- u_ForwardScatterStrength: { value: options.forwardScatterStrength ?? 1 },
26
- // eslint-disable-next-line @typescript-eslint/naming-convention
27
- u_BackScatterStrength: { value: options.backScatterStrength ?? 1 },
28
- }
29
- : undefined;
30
- this._private_shadowSensitivity = { value: options.shadowSensitivity ?? 1 };
31
- }
32
- /** Releases resources held by all albedo, normal, and emission nodes */
33
- destroy() {
34
- super.destroy();
35
- for (const node of this._private_normalNodes) {
36
- node.destroy?.();
37
- }
38
- for (const node of this._private_emissionNodes) {
39
- node.destroy?.();
40
- }
41
- }
42
- /** @internal */
43
- buildThreeMaterial(varyings) {
44
- return buildFXDiffuseMaterial(varyings, this.blending, this.useAlphaHashing, this.alphaTest, this.albedoNodes, this._private_normalNodes, this._private_emissionNodes, this._private_scatterUniforms, this._private_shadowSensitivity);
45
- }
46
- prepare() {
47
- let i = 0;
48
- for (const node of this.albedoNodes) {
49
- node.prepare?.(i);
50
- i += 1;
51
- }
52
- for (const node of this._private_normalNodes) {
53
- node.prepare?.(i);
54
- i += 1;
55
- }
56
- for (const node of this._private_emissionNodes) {
57
- node.prepare?.(i);
58
- i += 1;
59
- }
60
- }
61
- }
62
-
63
- export { FXDiffuseMaterial };
@@ -1,24 +0,0 @@
1
- import { FXNodeBlending } from "../../nodes/blending/FXNodeBlending";
2
- import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
3
- import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
4
- import { FXBlending } from "./FXMaterial";
5
- export interface AlbedoBlendingCode {
6
- /** Replacement for #include <map_fragment>. null means: leave the include as-is */
7
- mapFragment: string | null;
8
- /** Replacement for #include <premultiplied_alpha_fragment> */
9
- premultChunk: string;
10
- }
11
- /**
12
- * Processes albedoNodes in order to produce shader code for both the color assignment
13
- * and the per-pixel blending mode.
14
- *
15
- * Nodes are composed sequentially: each node receives the current accumulated value via
16
- * EXPRESSION_CURRENT_VALUE_PLACEHOLDER and returns a new value. Nodes that do not use
17
- * the placeholder act as sources and replace the current value entirely.
18
- *
19
- * When a FXNodeBlending appears, it receives both the current mask (via
20
- * EXPRESSION_CURRENT_VALUE_PLACEHOLDER) and the current color (via
21
- * EXPRESSION_CURRENT_COLOR_PLACEHOLDER), and returns the new mask value. Color nodes
22
- * that follow a blending node do not affect the mask.
23
- */
24
- export declare function buildAlbedoAndBlendingCode(albedoNodes: readonly (FXNodeColor | FXNodeTexture | FXNodeBlending)[], blending: FXBlending, useAlphaHashing: boolean): AlbedoBlendingCode;
@@ -1,64 +0,0 @@
1
- import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER, BLENDING_EXPRESSION_CURRENT_COLOR_PLACEHOLDER } from '../../nodes/FXNode.js';
2
- import { FXNodeBlending } from '../../nodes/blending/FXNodeBlending.js';
3
- import { FXBlending } from './FXMaterial.js';
4
-
5
- /**
6
- * Processes albedoNodes in order to produce shader code for both the color assignment
7
- * and the per-pixel blending mode.
8
- *
9
- * Nodes are composed sequentially: each node receives the current accumulated value via
10
- * EXPRESSION_CURRENT_VALUE_PLACEHOLDER and returns a new value. Nodes that do not use
11
- * the placeholder act as sources and replace the current value entirely.
12
- *
13
- * When a FXNodeBlending appears, it receives both the current mask (via
14
- * EXPRESSION_CURRENT_VALUE_PLACEHOLDER) and the current color (via
15
- * EXPRESSION_CURRENT_COLOR_PLACEHOLDER), and returns the new mask value. Color nodes
16
- * that follow a blending node do not affect the mask.
17
- */
18
- function buildAlbedoAndBlendingCode(albedoNodes, blending, useAlphaHashing) {
19
- if (albedoNodes.length === 0) {
20
- return { mapFragment: null, premultChunk: buildPremultChunk(blending, false) };
21
- }
22
- const hasBlendingNodes = albedoNodes.some((node) => node instanceof FXNodeBlending);
23
- const lines = [];
24
- lines.push("vec4 fxAlbedo = vec4(1.0);");
25
- if (hasBlendingNodes) {
26
- lines.push("float fxBlendMask = 1.0;");
27
- }
28
- for (const node of albedoNodes) {
29
- if (node instanceof FXNodeBlending) {
30
- lines.push(`fxBlendMask = ${node.blendingExpression
31
- .replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxBlendMask")
32
- .replace(BLENDING_EXPRESSION_CURRENT_COLOR_PLACEHOLDER, "fxAlbedo")};`);
33
- }
34
- else {
35
- lines.push(`fxAlbedo = ${node.colorExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxAlbedo")};`);
36
- }
37
- }
38
- const discardLine = useAlphaHashing ? "" : "if (diffuseColor.a < 0.0035) discard;";
39
- lines.push(`diffuseColor = fxAlbedo; ${discardLine}`);
40
- return {
41
- mapFragment: lines.join("\n"),
42
- premultChunk: buildPremultChunk(blending, hasBlendingNodes),
43
- };
44
- }
45
- function buildPremultChunk(blending, hasBlendingNodes) {
46
- if (!hasBlendingNodes) {
47
- return `gl_FragColor.rgb *= gl_FragColor.a;`;
48
- }
49
- if (blending === FXBlending.MASKED_ADDITIVE) {
50
- return `
51
- gl_FragColor.rgb *= gl_FragColor.a;
52
- gl_FragColor.a *= (1.0 - clamp(fxBlendMask, 0.0, 1.0));
53
- `;
54
- }
55
- // MASKED_MULTIPLY: dark areas darken destination, bright areas normal-blend
56
- return `
57
- gl_FragColor.rgb *= gl_FragColor.a;
58
- float fxLumPremult = dot(gl_FragColor.rgb, vec3(0.2126, 0.7152, 0.0722));
59
- vec4 fxMultiply = vec4(0.0, 0.0, 0.0, gl_FragColor.a - fxLumPremult);
60
- gl_FragColor = mix(gl_FragColor, fxMultiply, clamp(fxBlendMask, 0.0, 1.0));
61
- `;
62
- }
63
-
64
- export { buildAlbedoAndBlendingCode };
@@ -1,4 +0,0 @@
1
- import { MeshDepthMaterial } from "three";
2
- import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
3
- import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
4
- export declare function buildDepthMaterial(albedoNodes: readonly (FXNodeColor | FXNodeTexture)[], depthAlphaTest: number, useDepthAlphaHash: boolean, useSphericalDepth: boolean): MeshDepthMaterial;
@@ -1,144 +0,0 @@
1
- import { MeshDepthMaterial, DoubleSide, RGBADepthPacking } from 'three';
2
- import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
3
- import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER } from '../../nodes/FXNode.js';
4
-
5
- const DEPTH_VERTEX_SHADER = `
6
- #define PARTICLE_POSITION_X a_builtin[0][0]
7
- #define PARTICLE_POSITION_Y a_builtin[0][1]
8
- #define PARTICLE_POSITION_Z a_builtin[0][2]
9
-
10
- #define PARTICLE_SCALE_X a_builtin[1][2]
11
- #define PARTICLE_SCALE_Y a_builtin[1][3]
12
-
13
- #define PARTICLE_ROTATION a_builtin[2][1]
14
-
15
- attribute mat4 a_builtin;
16
- varying mat4 p_builtin;
17
- varying vec2 p_uv;
18
- varying vec2 vHighPrecisionZW;
19
- varying float vCenterViewZ;
20
-
21
- #ifdef USE_ALPHAHASH
22
- varying vec3 vPosition;
23
- #endif
24
-
25
- void main() {
26
- p_builtin = a_builtin;
27
- p_uv = uv;
28
-
29
- #ifdef USE_ALPHAHASH
30
- vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);
31
- #endif
32
-
33
- vec2 billboardOffset = position.xy;
34
- billboardOffset.x *= PARTICLE_SCALE_X;
35
- billboardOffset.y *= PARTICLE_SCALE_Y;
36
-
37
- float cosRotation = cos(PARTICLE_ROTATION);
38
- float sinRotation = sin(PARTICLE_ROTATION);
39
- billboardOffset = vec2(
40
- billboardOffset.x * cosRotation - billboardOffset.y * sinRotation,
41
- billboardOffset.x * sinRotation + billboardOffset.y * cosRotation
42
- );
43
-
44
- vec3 particleCenter = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);
45
- vec4 modelViewPosition = modelViewMatrix * vec4(particleCenter, 1.0);
46
- vCenterViewZ = modelViewPosition.z;
47
- modelViewPosition.xy += billboardOffset;
48
- gl_Position = projectionMatrix * modelViewPosition;
49
- vHighPrecisionZW = gl_Position.zw;
50
- }
51
- `;
52
- /**
53
- * GLSL snippet that replaces the flat `fragCoordZ` with a spherical depth
54
- * approximation. Treats each billboard as the front hemisphere of a sphere
55
- * whose radius equals half the smaller particle scale dimension. Fragments
56
- * outside that radius are discarded, producing a circular silhouette.
57
- *
58
- * Expects `vCenterViewZ` (view-space Z of the particle center), `p_uv`,
59
- * `PARTICLE_SCALE_X / Y`, and `projectionMatrix` to be available.
60
- */
61
- const SPHERICAL_DEPTH_FRAG_COORD_Z = `
62
- float sphereOffsetX = (p_uv.x - 0.5) * PARTICLE_SCALE_X;
63
- float sphereOffsetY = (p_uv.y - 0.5) * PARTICLE_SCALE_Y;
64
- float sphereRadius = min(PARTICLE_SCALE_X, PARTICLE_SCALE_Y) * 0.5;
65
- float squaredDistanceFromCenter = sphereOffsetX * sphereOffsetX + sphereOffsetY * sphereOffsetY;
66
- if (squaredDistanceFromCenter > sphereRadius * sphereRadius) discard;
67
-
68
- float sphereViewZ = vCenterViewZ + sqrt(sphereRadius * sphereRadius - squaredDistanceFromCenter);
69
- float projectedZ = projectionMatrix[2][2] * sphereViewZ + projectionMatrix[3][2];
70
- float projectedW = projectionMatrix[2][3] * sphereViewZ + projectionMatrix[3][3];
71
- float normalizedDeviceZ = projectedZ / projectedW;
72
-
73
- #ifdef USE_REVERSEDEPTHBUF
74
- float fragCoordZ = normalizedDeviceZ;
75
- #else
76
- float fragCoordZ = 0.5 * normalizedDeviceZ + 0.5;
77
- #endif
78
- `;
79
- const FRAG_COORD_Z_PATTERN = /float fragCoordZ\b[^;]*;/;
80
- function buildDepthMaterial(albedoNodes, depthAlphaTest, useDepthAlphaHash, useSphericalDepth) {
81
- const depthNodes = albedoNodes.filter((node) => node.affectsDepth);
82
- const needsFragmentPatch = depthNodes.length > 0 || useSphericalDepth;
83
- const material = new MeshDepthMaterial({
84
- name: "FXDepthMaterial",
85
- depthPacking: RGBADepthPacking,
86
- side: DoubleSide,
87
- forceSinglePass: true,
88
- alphaHash: useDepthAlphaHash,
89
- });
90
- material.onBeforeCompile = (shader) => {
91
- shader.vertexShader = DEPTH_VERTEX_SHADER;
92
- if (!needsFragmentPatch) {
93
- return;
94
- }
95
- const seenCacheKeys = new Set();
96
- const uniqueHelpers = depthNodes
97
- .filter((node) => {
98
- if (seenCacheKeys.has(node.cacheKey)) {
99
- return false;
100
- }
101
- seenCacheKeys.add(node.cacheKey);
102
- return true;
103
- })
104
- .map((node) => node.helperFunctions)
105
- .join("\n");
106
- const fragmentPreamble = [
107
- PARTICLE_DEFINES,
108
- "varying mat4 p_builtin;",
109
- "varying vec2 p_uv;",
110
- "varying float vCenterViewZ;",
111
- useSphericalDepth ? "uniform mat4 projectionMatrix;" : "",
112
- depthNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
113
- uniqueHelpers,
114
- ].join("\n");
115
- for (const node of depthNodes) {
116
- Object.assign(shader.uniforms, node.uniforms);
117
- }
118
- let fragmentShader = shader.fragmentShader;
119
- if (depthNodes.length > 0) {
120
- const albedoLines = ["vec4 fxDepthAlbedo = vec4(1.0);"];
121
- for (const node of depthNodes) {
122
- albedoLines.push(`fxDepthAlbedo = ${node.colorExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxDepthAlbedo")};`);
123
- }
124
- albedoLines.push(`diffuseColor.a = fxDepthAlbedo.a;`);
125
- albedoLines.push(`if (diffuseColor.a < ${depthAlphaTest.toFixed(4)}) discard;`);
126
- fragmentShader = fragmentShader.replace("#include <map_fragment>", albedoLines.join("\n"));
127
- }
128
- if (useSphericalDepth) {
129
- if (!FRAG_COORD_Z_PATTERN.test(fragmentShader)) {
130
- console.warn("FXDepthMaterial: could not find 'float fragCoordZ' in the depth fragment shader. " +
131
- "Spherical depth will not be applied. " +
132
- "This may indicate a Three.js version mismatch.");
133
- }
134
- else {
135
- fragmentShader = fragmentShader.replace(FRAG_COORD_Z_PATTERN, SPHERICAL_DEPTH_FRAG_COORD_Z);
136
- }
137
- }
138
- shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
139
- };
140
- material.customProgramCacheKey = () => `fx-depth-${useDepthAlphaHash ? "alphaHash" : "alphaTest"}-${useSphericalDepth ? "spherical" : "flat"}-${depthNodes.map((node) => node.cacheKey).join("-") || "none"}`;
141
- return material;
142
- }
143
-
144
- export { buildDepthMaterial };
@@ -1,4 +0,0 @@
1
- import { MeshDistanceMaterial } from "three";
2
- import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
3
- import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
4
- export declare function buildDistanceMaterial(albedoNodes: readonly (FXNodeColor | FXNodeTexture)[], distanceAlphaTest: number, useDistanceAlphaHash: boolean, useSphericalDepth: boolean): MeshDistanceMaterial;
@@ -1,175 +0,0 @@
1
- import { MeshDistanceMaterial, DoubleSide } from 'three';
2
- import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
3
- import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER } from '../../nodes/FXNode.js';
4
-
5
- /**
6
- * Custom vertex shader for point/spot light shadow maps.
7
- *
8
- * Unlike the depth material (which writes clip-space Z for directional light
9
- * shadows), the distance material writes the world-space distance from each
10
- * fragment to the light position. This means we need to reconstruct world
11
- * position for each billboarded vertex.
12
- *
13
- * `vWorldPosition` is a Three.js convention consumed by the distance fragment
14
- * shader. `viewMatrix` here is the shadow camera's view matrix, so the
15
- * billboard faces the light - which is correct for shadow shape.
16
- */
17
- const DISTANCE_VERTEX_SHADER = `
18
- #define DISTANCE
19
-
20
- #define PARTICLE_POSITION_X a_builtin[0][0]
21
- #define PARTICLE_POSITION_Y a_builtin[0][1]
22
- #define PARTICLE_POSITION_Z a_builtin[0][2]
23
-
24
- #define PARTICLE_SCALE_X a_builtin[1][2]
25
- #define PARTICLE_SCALE_Y a_builtin[1][3]
26
-
27
- #define PARTICLE_ROTATION a_builtin[2][1]
28
-
29
- attribute mat4 a_builtin;
30
- varying mat4 p_builtin;
31
- varying vec2 p_uv;
32
- varying vec3 vWorldPosition;
33
-
34
- #ifdef USE_ALPHAHASH
35
- varying vec3 vPosition;
36
- #endif
37
-
38
- void main() {
39
- p_builtin = a_builtin;
40
- p_uv = uv;
41
-
42
- #ifdef USE_ALPHAHASH
43
- vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);
44
- #endif
45
-
46
- vec2 billboardOffset = position.xy;
47
- billboardOffset.x *= PARTICLE_SCALE_X;
48
- billboardOffset.y *= PARTICLE_SCALE_Y;
49
-
50
- float cosRotation = cos(PARTICLE_ROTATION);
51
- float sinRotation = sin(PARTICLE_ROTATION);
52
- billboardOffset = vec2(
53
- billboardOffset.x * cosRotation - billboardOffset.y * sinRotation,
54
- billboardOffset.x * sinRotation + billboardOffset.y * cosRotation
55
- );
56
-
57
- vec3 particleCenter = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);
58
-
59
- // Reconstruct world-space position for the billboarded vertex.
60
- // Camera axes are extracted from the shadow camera's view matrix (transposed
61
- // rotation part), then scaled by the billboard offset.
62
- vec3 cameraRight = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
63
- vec3 cameraUp = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
64
- vec3 worldCenter = (modelMatrix * vec4(particleCenter, 1.0)).xyz;
65
- vWorldPosition = worldCenter + cameraRight * billboardOffset.x + cameraUp * billboardOffset.y;
66
-
67
- // Standard clip-space projection via the shadow camera.
68
- vec4 modelViewPosition = modelViewMatrix * vec4(particleCenter, 1.0);
69
- modelViewPosition.xy += billboardOffset;
70
- gl_Position = projectionMatrix * modelViewPosition;
71
- }
72
- `;
73
- /**
74
- * GLSL snippet for spherical distance adjustment.
75
- *
76
- * Instead of measuring distance from the flat billboard surface, this
77
- * approximates the front hemisphere of a sphere. The sphere radius is half
78
- * the smaller scale dimension. Fragments outside the sphere are discarded.
79
- *
80
- * The depth offset is subtracted from the flat distance, bringing the
81
- * perceived surface closer to the light. This is an approximation - the
82
- * true offset direction depends on the light-to-fragment vector, not the
83
- * camera-to-fragment vector - but for smoke particles the error is
84
- * negligible.
85
- */
86
- const SPHERICAL_DISTANCE_SNIPPET = `
87
- {
88
- float sphereOffsetX = (p_uv.x - 0.5) * PARTICLE_SCALE_X;
89
- float sphereOffsetY = (p_uv.y - 0.5) * PARTICLE_SCALE_Y;
90
- float sphereRadius = min(PARTICLE_SCALE_X, PARTICLE_SCALE_Y) * 0.5;
91
- float squaredDistanceFromCenter = sphereOffsetX * sphereOffsetX + sphereOffsetY * sphereOffsetY;
92
- if (squaredDistanceFromCenter > sphereRadius * sphereRadius) discard;
93
- float sphereDepthOffset = sqrt(sphereRadius * sphereRadius - squaredDistanceFromCenter);
94
- dist = max(0.0, dist - sphereDepthOffset);
95
- }
96
- `;
97
- const DISTANCE_CALCULATION_PATTERN = /float\s+dist\s*=\s*length\s*\(\s*vWorldPosition\s*-\s*referencePosition\s*\)\s*;/;
98
- function buildDistanceMaterial(albedoNodes, distanceAlphaTest, useDistanceAlphaHash, useSphericalDepth) {
99
- const distanceNodes = albedoNodes.filter((node) => node.affectsDepth);
100
- const needsFragmentPatch = distanceNodes.length > 0 || useSphericalDepth;
101
- const material = new MeshDistanceMaterial({
102
- name: "FXDistanceMaterial",
103
- side: DoubleSide,
104
- forceSinglePass: true,
105
- alphaHash: useDistanceAlphaHash,
106
- });
107
- material.onBeforeCompile = (shader) => {
108
- shader.vertexShader = DISTANCE_VERTEX_SHADER;
109
- if (!needsFragmentPatch) {
110
- return;
111
- }
112
- const seenCacheKeys = new Set();
113
- const uniqueHelpers = distanceNodes
114
- .filter((node) => {
115
- if (seenCacheKeys.has(node.cacheKey)) {
116
- return false;
117
- }
118
- seenCacheKeys.add(node.cacheKey);
119
- return true;
120
- })
121
- .map((node) => node.helperFunctions)
122
- .join("\n");
123
- const fragmentPreamble = [
124
- PARTICLE_DEFINES,
125
- "varying mat4 p_builtin;",
126
- "varying vec2 p_uv;",
127
- distanceNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
128
- uniqueHelpers,
129
- ].join("\n");
130
- for (const node of distanceNodes) {
131
- Object.assign(shader.uniforms, node.uniforms);
132
- }
133
- let fragmentShader = shader.fragmentShader;
134
- // Alpha from nodes - MeshDistanceMaterial may or may not include
135
- // <map_fragment> depending on the Three.js version. If the chunk is
136
- // absent, insert the alpha discard before the distance calculation.
137
- if (distanceNodes.length > 0) {
138
- const albedoLines = ["vec4 fxDistAlbedo = vec4(1.0);"];
139
- for (const node of distanceNodes) {
140
- albedoLines.push(`fxDistAlbedo = ${node.colorExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxDistAlbedo")};`);
141
- }
142
- albedoLines.push(`float nodeAlpha = fxDistAlbedo.a;`);
143
- const alphaDiscardCode = albedoLines.join("\n") + `\nif (nodeAlpha < ${distanceAlphaTest.toFixed(4)}) discard;`;
144
- if (fragmentShader.includes("#include <map_fragment>")) {
145
- fragmentShader = fragmentShader.replace("#include <map_fragment>", alphaDiscardCode);
146
- }
147
- else if (DISTANCE_CALCULATION_PATTERN.test(fragmentShader)) {
148
- // Fallback: inject alpha discard just before the distance calculation.
149
- fragmentShader = fragmentShader.replace(DISTANCE_CALCULATION_PATTERN, (match) => alphaDiscardCode + "\n" + match);
150
- }
151
- else {
152
- console.warn("FXDistanceMaterial: could not find a suitable injection point for alpha discard. " +
153
- "This may indicate a Three.js version mismatch.");
154
- }
155
- }
156
- // Spherical distance adjustment - offset the flat-billboard distance by
157
- // the sphere's front surface depth.
158
- if (useSphericalDepth) {
159
- if (!DISTANCE_CALCULATION_PATTERN.test(fragmentShader)) {
160
- console.warn("FXDistanceMaterial: could not find the distance calculation line. " +
161
- "Spherical depth will not be applied. " +
162
- "This may indicate a Three.js version mismatch.");
163
- }
164
- else {
165
- // Inject the spherical adjustment right after the distance calculation.
166
- fragmentShader = fragmentShader.replace(DISTANCE_CALCULATION_PATTERN, (match) => match + "\n" + SPHERICAL_DISTANCE_SNIPPET);
167
- }
168
- }
169
- shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
170
- };
171
- material.customProgramCacheKey = () => `fx-distance-${useDistanceAlphaHash ? "alphaHash" : "alphaTest"}-${useSphericalDepth ? "spherical" : "flat"}-${distanceNodes.map((node) => node.cacheKey).join("-") || "none"}`;
172
- return material;
173
- }
174
-
175
- export { buildDistanceMaterial };
@@ -1,70 +0,0 @@
1
- import { FXNodeColor } from "../../nodes/color/FXNodeColor";
2
- import { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
3
- /**
4
- * Per-particle blend mode used when compositing particles into the scene
5
- */
6
- export declare enum FXBlending {
7
- /**
8
- * Premultiplied additive blend - particle color is pre-multiplied by alpha and added to the
9
- * destination with zero alpha contribution
10
- */
11
- MASKED_ADDITIVE = 0,
12
- /**
13
- * Luminance-aware multiply blend - dark areas darken the destination while bright areas
14
- * normal-blend through a mask
15
- */
16
- MASKED_MULTIPLY = 1
17
- }
18
- /**
19
- * Configuration options shared by all {@link FXMaterial} subclasses
20
- */
21
- export interface FXMaterialOptions {
22
- /**
23
- * Color and texture nodes composited for particle color
24
- *
25
- * @defaultValue `[]`
26
- */
27
- albedoNodes: (FXNodeColor | FXNodeTexture)[];
28
- blending: FXBlending;
29
- /**
30
- * Use alpha hashing instead of standard transparency
31
- *
32
- * @defaultValue `false`
33
- */
34
- useAlphaHashing: boolean;
35
- /**
36
- * Alpha cutoff threshold for color passes
37
- *
38
- * @defaultValue `0.0075`
39
- */
40
- alphaTest: number;
41
- /**
42
- * Alpha cutoff threshold for depth and shadow passes
43
- *
44
- * @defaultValue `0.5`
45
- */
46
- depthAlphaTest: number;
47
- /**
48
- * Use alpha hashing in depth and shadow passes
49
- *
50
- * @defaultValue `false`
51
- */
52
- useDepthAlphaHash: boolean;
53
- /**
54
- * Approximate spherical depth for shadow maps
55
- *
56
- * @defaultValue `true`
57
- */
58
- useSphericalDepth: boolean;
59
- }
60
- /**
61
- * Abstract base class for particle materials
62
- */
63
- export declare abstract class FXMaterial {
64
- private readonly depthAlphaTest;
65
- private readonly useDepthAlphaHash;
66
- private readonly useSphericalDepth;
67
- constructor(options?: Partial<FXMaterialOptions>);
68
- /** Releases resources held by all albedo nodes */
69
- destroy(): void;
70
- }
@@ -1,51 +0,0 @@
1
- import { FXNodeColor } from '../../nodes/color/FXNodeColor.js';
2
- import { FXNodeTexture } from '../../nodes/texture/FXNodeTexture.js';
3
- import { buildDepthMaterial } from './FXDepthMaterial.Internal.js';
4
- import { buildDistanceMaterial } from './FXDistanceMaterial.Internal.js';
5
-
6
- /**
7
- * Per-particle blend mode used when compositing particles into the scene
8
- */
9
- var FXBlending;
10
- (function (FXBlending) {
11
- /**
12
- * Premultiplied additive blend - particle color is pre-multiplied by alpha and added to the
13
- * destination with zero alpha contribution
14
- */
15
- FXBlending[FXBlending["MASKED_ADDITIVE"] = 0] = "MASKED_ADDITIVE";
16
- /**
17
- * Luminance-aware multiply blend - dark areas darken the destination while bright areas
18
- * normal-blend through a mask
19
- */
20
- FXBlending[FXBlending["MASKED_MULTIPLY"] = 1] = "MASKED_MULTIPLY";
21
- })(FXBlending || (FXBlending = {}));
22
- /**
23
- * Abstract base class for particle materials
24
- */
25
- class FXMaterial {
26
- constructor(options = {}) {
27
- this.albedoNodes = options.albedoNodes ?? [];
28
- this.blending = options.blending ?? FXBlending.MASKED_ADDITIVE;
29
- this.useAlphaHashing = options.useAlphaHashing ?? false;
30
- this.alphaTest = options.alphaTest ?? 0.0075;
31
- this._private_depthAlphaTest = options.depthAlphaTest ?? 0.5;
32
- this._private_useDepthAlphaHash = options.useDepthAlphaHash ?? false;
33
- this._private_useSphericalDepth = options.useSphericalDepth ?? true;
34
- }
35
- /** Releases resources held by all albedo nodes */
36
- destroy() {
37
- for (const node of this.albedoNodes) {
38
- node.destroy?.();
39
- }
40
- }
41
- /** @internal */
42
- buildDepthMaterial() {
43
- return buildDepthMaterial(this.albedoNodes.filter((node) => node instanceof FXNodeColor || node instanceof FXNodeTexture), this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
44
- }
45
- /** @internal */
46
- buildDistanceMaterial() {
47
- return buildDistanceMaterial(this.albedoNodes.filter((node) => node instanceof FXNodeColor || node instanceof FXNodeTexture), this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
48
- }
49
- }
50
-
51
- export { FXBlending, FXMaterial };
@@ -1,7 +0,0 @@
1
- import { MeshBasicMaterial } from "three";
2
- import type { GLTypeInfo } from "../../instancedParticle/shared";
3
- import type { FXNodeBlending } from "../../nodes/blending/FXNodeBlending";
4
- import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
5
- import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
6
- import { FXBlending } from "../FXMaterial/FXMaterial";
7
- export declare function buildFXUnlitMaterial(varyings: Record<string, GLTypeInfo>, blending: FXBlending, useAlphaHashing: boolean, alphaTest: number, albedoNodes: readonly (FXNodeColor | FXNodeTexture | FXNodeBlending)[]): MeshBasicMaterial;