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
@@ -1,275 +0,0 @@
1
- import { MeshLambertMaterial, Color, DoubleSide, NormalBlending } from 'three';
2
- import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
3
- import { FXNodeColor } from '../../nodes/color/FXNodeColor.js';
4
- import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER } from '../../nodes/FXNode.js';
5
- import { FXNodeTexture } from '../../nodes/texture/FXNodeTexture.js';
6
- import { buildAlbedoAndBlendingCode } from '../FXMaterial/FXBlending.Internal.js';
7
- import { FXBlending } from '../FXMaterial/FXMaterial.js';
8
-
9
- /**
10
- * Wraps every `getShadow(...)` call in the shader source with
11
- * `mix(1.0, getShadow(...), <uniformName>)` so that a uniform
12
- * can scale shadow influence from 0 (no shadows) to 1 (full shadows).
13
- */
14
- function wrapShadowCalls(source, uniformName) {
15
- const target = "getShadow(";
16
- let result = "";
17
- let position = 0;
18
- while (position < source.length) {
19
- const callStart = source.indexOf(target, position);
20
- if (callStart === -1) {
21
- result += source.slice(position);
22
- break;
23
- }
24
- result += source.slice(position, callStart);
25
- let depth = 0;
26
- let cursor = callStart + target.length - 1;
27
- for (; cursor < source.length; cursor++) {
28
- if (source[cursor] === "(") {
29
- depth++;
30
- }
31
- if (source[cursor] === ")") {
32
- depth--;
33
- }
34
- if (depth === 0) {
35
- break;
36
- }
37
- }
38
- const originalCall = source.slice(callStart, cursor + 1);
39
- result += `mix(1.0, ${originalCall}, ${uniformName})`;
40
- position = cursor + 1;
41
- }
42
- return result;
43
- }
44
- function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest, albedoNodes, normalNodes, emissionNodes, scatterUniforms, shadowSensitivity) {
45
- const useScatter = scatterUniforms !== undefined;
46
- const useShadowSensitivity = shadowSensitivity !== undefined;
47
- const useNormals = normalNodes.length > 0;
48
- const attributeDeclarations = [];
49
- const varyingDeclarations = [];
50
- const vertexAssignments = [];
51
- for (const name in varyings) {
52
- const { glslTypeName } = varyings[name];
53
- attributeDeclarations.push(`attribute ${glslTypeName} a_${name};`);
54
- varyingDeclarations.push(`varying ${glslTypeName} p_${name};`);
55
- vertexAssignments.push(`p_${name} = a_${name};`);
56
- }
57
- const material = new MeshLambertMaterial({
58
- name: "FXDiffuseMaterial",
59
- transparent: !useAlphaHashing,
60
- depthWrite: useAlphaHashing,
61
- depthTest: true,
62
- alphaHash: useAlphaHashing,
63
- blending: NormalBlending,
64
- side: DoubleSide,
65
- forceSinglePass: true,
66
- alphaTest,
67
- emissive: emissionNodes.length > 0 ? new Color(1, 1, 1) : new Color(0, 0, 0),
68
- premultipliedAlpha: true,
69
- });
70
- material.onBeforeCompile = (shader) => {
71
- const vertexPreamble = [
72
- PARTICLE_DEFINES,
73
- attributeDeclarations.join("\n"),
74
- "varying vec2 p_uv;",
75
- useNormals ? "varying vec2 p_billboardSinCos;" : "",
76
- useScatter ? "varying vec3 v_viewPosition;" : "",
77
- varyingDeclarations.join("\n"),
78
- ].join("\n");
79
- shader.vertexShader =
80
- vertexPreamble +
81
- "\n" +
82
- shader.vertexShader
83
- .replace("#include <beginnormal_vertex>", "vec3 objectNormal = vec3(0.0, 0.0, 1.0);")
84
- .replace("#include <defaultnormal_vertex>", "vec3 transformedNormal = vec3(0.0, 0.0, 1.0);")
85
- .replace("#include <begin_vertex>", [
86
- "p_uv = uv;",
87
- vertexAssignments.join("\n"),
88
- "vec3 transformed = position;",
89
- "#ifdef USE_ALPHAHASH",
90
- " vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);",
91
- "#endif",
92
- ].join("\n"))
93
- .replace("#include <project_vertex>", [
94
- "vec2 billboardOffset = position.xy;",
95
- "billboardOffset.x *= PARTICLE_SCALE_X;",
96
- "billboardOffset.y *= PARTICLE_SCALE_Y;",
97
- "",
98
- "float cosRotation = cos(PARTICLE_ROTATION);",
99
- "float sinRotation = sin(PARTICLE_ROTATION);",
100
- "billboardOffset = vec2(",
101
- " billboardOffset.x * cosRotation - billboardOffset.y * sinRotation,",
102
- " billboardOffset.x * sinRotation + billboardOffset.y * cosRotation",
103
- ");",
104
- "",
105
- // Pass the exact sin/cos that rotated the billboard to the fragment shader.
106
- useNormals ? "p_billboardSinCos = vec2(sinRotation, cosRotation);" : "",
107
- "",
108
- "vec3 particleCenter = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);",
109
- "vec4 mvPosition = modelViewMatrix * vec4(particleCenter, 1.0);",
110
- "mvPosition.xy += billboardOffset;",
111
- useScatter ? "v_viewPosition = mvPosition.xyz;" : "",
112
- "gl_Position = projectionMatrix * mvPosition;",
113
- ].join("\n"))
114
- .replace("#include <worldpos_vertex>", `
115
- #if defined(USE_SHADOWMAP) || defined(USE_ENVMAP) || defined(DISTANCE) || (NUM_SPOT_LIGHT_COORDS > 0)
116
- vec3 cameraRight = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
117
- vec3 cameraUp = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
118
- vec4 worldPosition = vec4(
119
- (modelMatrix * vec4(particleCenter, 1.0)).xyz + cameraRight * billboardOffset.x + cameraUp * billboardOffset.y,
120
- 1.0
121
- );
122
- #endif
123
- `);
124
- const seenCacheKeys = new Set();
125
- const uniqueHelpers = (nodes) => nodes
126
- .filter((node) => {
127
- if (seenCacheKeys.has(node.cacheKey)) {
128
- return false;
129
- }
130
- seenCacheKeys.add(node.cacheKey);
131
- return true;
132
- })
133
- .map((node) => node.helperFunctions)
134
- .join("\n");
135
- const scatterDeclarations = useScatter
136
- ? [
137
- "varying vec3 v_viewPosition;",
138
- "uniform vec3 u_ScatterTint;",
139
- "uniform float u_ScatterPower;",
140
- "uniform float u_ForwardScatterStrength;",
141
- "uniform float u_BackScatterStrength;",
142
- `vec3 calculateScatter(vec3 viewDirection, vec3 lightDirection, vec3 lightColor, float alpha) {
143
- float viewDotLight = dot(viewDirection, lightDirection);
144
- float scatterForward = pow(max(-viewDotLight, 0.0), u_ScatterPower) * u_ForwardScatterStrength;
145
- float scatterBackward = pow(max( viewDotLight, 0.0), 2.0) * u_BackScatterStrength;
146
- return lightColor * u_ScatterTint * (scatterForward + scatterBackward) * alpha;
147
- }`,
148
- ]
149
- : [];
150
- const shadowSensitivityDeclarations = useShadowSensitivity
151
- ? ["uniform float u_shadowSensitivity;"]
152
- : [];
153
- const fragmentPreamble = [
154
- PARTICLE_DEFINES,
155
- "varying vec2 p_uv;",
156
- useNormals ? "varying vec2 p_billboardSinCos;" : "",
157
- varyingDeclarations.join("\n"),
158
- albedoNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
159
- uniqueHelpers(albedoNodes),
160
- normalNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
161
- uniqueHelpers(normalNodes),
162
- emissionNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
163
- uniqueHelpers(emissionNodes),
164
- ...scatterDeclarations,
165
- ...shadowSensitivityDeclarations,
166
- ].join("\n");
167
- if (useScatter) {
168
- shader.uniforms["u_ScatterTint"] = scatterUniforms.u_ScatterTint;
169
- shader.uniforms["u_ScatterPower"] = scatterUniforms.u_ScatterPower;
170
- shader.uniforms["u_ForwardScatterStrength"] = scatterUniforms.u_ForwardScatterStrength;
171
- shader.uniforms["u_BackScatterStrength"] = scatterUniforms.u_BackScatterStrength;
172
- }
173
- if (useShadowSensitivity) {
174
- shader.uniforms["u_shadowSensitivity"] = shadowSensitivity;
175
- }
176
- for (const node of albedoNodes) {
177
- Object.assign(shader.uniforms, node.uniforms);
178
- }
179
- for (const node of normalNodes) {
180
- Object.assign(shader.uniforms, node.uniforms);
181
- }
182
- for (const node of emissionNodes) {
183
- Object.assign(shader.uniforms, node.uniforms);
184
- }
185
- let fragmentShader = shader.fragmentShader;
186
- if (useNormals) {
187
- const normalLines = [
188
- "float faceDirection = gl_FrontFacing ? 1.0 : -1.0;",
189
- "vec3 fxNormal = vec3(0.0, 0.0, 1.0);",
190
- ];
191
- for (const node of normalNodes) {
192
- if (node instanceof FXNodeTexture || node instanceof FXNodeColor) {
193
- normalLines.push(`fxNormal = normalize((${node.colorExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "vec4(fxNormal, 1.0)")}).rgb * 2.0 - 1.0);`);
194
- }
195
- else {
196
- normalLines.push(`fxNormal = ${node.normalExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxNormal")};`);
197
- }
198
- }
199
- normalLines.push("", "float sinR = p_billboardSinCos.x;", "float cosR = p_billboardSinCos.y;", "vec3 normal = vec3(", " fxNormal.x * cosR - fxNormal.y * sinR,", " fxNormal.x * sinR + fxNormal.y * cosR,", " fxNormal.z", ");", "", "normal *= faceDirection;");
200
- fragmentShader = fragmentShader.replace("#include <normal_fragment_begin>", normalLines.join("\n"));
201
- }
202
- const albedoCode = buildAlbedoAndBlendingCode(albedoNodes, blending, useAlphaHashing);
203
- if (albedoCode.mapFragment !== null) {
204
- fragmentShader = fragmentShader.replace("#include <map_fragment>", albedoCode.mapFragment);
205
- }
206
- if (emissionNodes.length > 0) {
207
- const emissiveLines = ["vec4 fxEmissive = vec4(1.0);"];
208
- for (const node of emissionNodes) {
209
- emissiveLines.push(`fxEmissive = ${node.colorExpression.replace(CURRENT_EXPRESSION_VALUE_PLACEHOLDER, "fxEmissive")};`);
210
- }
211
- emissiveLines.push("totalEmissiveRadiance = fxEmissive.rgb;");
212
- fragmentShader = fragmentShader.replace("#include <emissivemap_fragment>", emissiveLines.join("\n"));
213
- }
214
- if (useScatter) {
215
- fragmentShader = fragmentShader.replace("#include <opaque_fragment>", `
216
- #ifdef OPAQUE
217
- diffuseColor.a = 1.0;
218
- #endif
219
-
220
- vec3 viewDirection = normalize(-v_viewPosition);
221
- vec3 totalScatter = vec3(0.0);
222
-
223
- #if NUM_DIR_LIGHTS > 0
224
- for (int i = 0; i < NUM_DIR_LIGHTS; i++) {
225
- totalScatter += calculateScatter(viewDirection, directionalLights[i].direction, directionalLights[i].color, diffuseColor.a);
226
- }
227
- #endif
228
-
229
- #if NUM_POINT_LIGHTS > 0
230
- for (int i = 0; i < NUM_POINT_LIGHTS; i++) {
231
- vec3 toLightDirection = normalize(pointLights[i].position - v_viewPosition);
232
- float lightDistance = length(pointLights[i].position - v_viewPosition);
233
- float attenuation = 1.0 / (1.0 + 0.09 * lightDistance + 0.032 * lightDistance * lightDistance);
234
- totalScatter += calculateScatter(viewDirection, toLightDirection, pointLights[i].color, diffuseColor.a) * attenuation;
235
- }
236
- #endif
237
-
238
- #if NUM_SPOT_LIGHTS > 0
239
- for (int i = 0; i < NUM_SPOT_LIGHTS; i++) {
240
- vec3 toLightDirection = normalize(spotLights[i].position - v_viewPosition);
241
- float lightDistance = length(spotLights[i].position - v_viewPosition);
242
- float attenuation = 1.0 / (1.0 + 0.09 * lightDistance + 0.032 * lightDistance * lightDistance);
243
- float spotCosine = dot(normalize(spotLights[i].direction), -toLightDirection);
244
- float spotFalloff = smoothstep(spotLights[i].coneCos, spotLights[i].penumbraCos, spotCosine);
245
- totalScatter += calculateScatter(viewDirection, toLightDirection, spotLights[i].color, diffuseColor.a) * attenuation * spotFalloff;
246
- }
247
- #endif
248
-
249
- vec3 albedo = diffuseColor.a > 0.001
250
- ? diffuseColor.rgb / diffuseColor.a
251
- : vec3(0.0);
252
- gl_FragColor = vec4(outgoingLight + totalScatter * albedo, diffuseColor.a);
253
- `);
254
- }
255
- if (useShadowSensitivity) {
256
- fragmentShader = wrapShadowCalls(fragmentShader, "u_shadowSensitivity");
257
- }
258
- fragmentShader = fragmentShader.replace("#include <premultiplied_alpha_fragment>", albedoCode.premultChunk);
259
- shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
260
- };
261
- const scatterKey = useScatter ? `scatter(${scatterUniforms.u_ScatterPower.value})` : "no-scatter";
262
- const shadowKey = useShadowSensitivity ? "shadow-sens" : "no-shadow-sens";
263
- material.customProgramCacheKey = () => [
264
- "fx",
265
- albedoNodes.map((node) => node.cacheKey).join("-") || "none",
266
- normalNodes.map((node) => node.cacheKey).join("-") || "none",
267
- emissionNodes.map((node) => node.cacheKey).join("-") || "none",
268
- FXBlending[blending],
269
- scatterKey,
270
- shadowKey,
271
- ].join("_");
272
- return material;
273
- }
274
-
275
- export { buildFXDiffuseMaterial };
@@ -1,75 +0,0 @@
1
- import type { ColorRepresentation } from "three";
2
- import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
3
- import type { FXNodeNormal } from "../../nodes/normal/FXNodeNormal";
4
- import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
5
- import type { FXMaterialOptions } from "../FXMaterial/FXMaterial";
6
- import { FXMaterial } from "../FXMaterial/FXMaterial";
7
- /**
8
- * Configuration options for {@link FXDiffuseMaterial}
9
- */
10
- export interface FXDiffuseMaterialOptions extends FXMaterialOptions {
11
- /**
12
- * Normal nodes for lighting; multiple nodes are blended together
13
- *
14
- * @defaultValue `[new FXNodeSphericalNormal()]`
15
- */
16
- normalNodes: (FXNodeTexture | FXNodeNormal)[];
17
- /**
18
- * Emission color nodes composited for emissive output
19
- *
20
- * @defaultValue `[]`
21
- */
22
- emissionNodes: (FXNodeColor | FXNodeTexture)[];
23
- /**
24
- * Enable subsurface scattering approximation
25
- *
26
- * @defaultValue `false`
27
- */
28
- enableScatter: boolean;
29
- /**
30
- * Tint color applied to the scatter contribution
31
- *
32
- * @defaultValue `0xffd999`
33
- */
34
- scatterTint: ColorRepresentation;
35
- /**
36
- * Scatter lobe sharpness; higher values produce a tighter highlight
37
- *
38
- * @defaultValue `10`
39
- */
40
- scatterPower: number;
41
- /**
42
- * Forward scatter intensity (light-facing side)
43
- *
44
- * @defaultValue `1`
45
- */
46
- forwardScatterStrength: number;
47
- /**
48
- * Back scatter intensity (back-lit side)
49
- *
50
- * @defaultValue `1`
51
- */
52
- backScatterStrength: number;
53
- /**
54
- * Shadow influence (`0` = no shadows, `1` = full shadows)
55
- *
56
- * @defaultValue `1`
57
- */
58
- shadowSensitivity: number;
59
- }
60
- /**
61
- * Diffuse particle material with lighting, normals, emission, and optional subsurface scattering
62
- */
63
- export declare class FXDiffuseMaterial extends FXMaterial {
64
- private readonly normalNodes;
65
- private readonly emissionNodes;
66
- private readonly scatterUniforms;
67
- private readonly shadowSensitivity;
68
- /**
69
- * @param options - Material configuration
70
- */
71
- constructor(options?: Partial<FXDiffuseMaterialOptions>);
72
- /** Releases resources held by all albedo, normal, and emission nodes */
73
- destroy(): void;
74
- prepare(): void;
75
- }
@@ -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;