sparcoon 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/emitter/FXEmitter.Internal.d.ts +0 -7
- package/dist/emitter/FXEmitter.Internal.js +1 -2
- package/dist/emitter/FXEmitter.d.ts +5 -3
- package/dist/emitter/FXEmitter.js +20 -13
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.d.ts +0 -2
- package/dist/emitter/behavior/FXBehaviorDirectionalForce.js +0 -2
- package/dist/emitter/behavior/FXBehaviorPointForce.d.ts +0 -4
- package/dist/emitter/behavior/FXBehaviorPointForce.js +0 -4
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.d.ts +4 -3
- package/dist/emitter/behavior/FXBehaviorScaleOverLife.js +9 -3
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.d.ts +0 -1
- package/dist/emitter/behavior/FXBehaviorTorqueDamping.js +0 -1
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.d.ts +0 -2
- package/dist/emitter/behavior/FXBehaviorTorqueNoise.js +0 -2
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.d.ts +4 -2
- package/dist/emitter/behavior/FXBehaviorTorqueOverLife.js +9 -2
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.d.ts +0 -1
- package/dist/emitter/behavior/FXBehaviorVelocityDamping.js +0 -1
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.d.ts +0 -2
- package/dist/emitter/behavior/FXBehaviorVelocityNoise.js +0 -2
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.d.ts +4 -2
- package/dist/emitter/behavior/FXBehaviorVelocityOverLife.js +9 -2
- package/dist/emitter/spawn/FXSpawnBox.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnBox.js +0 -1
- package/dist/emitter/spawn/FXSpawnLifetime.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnLifetime.js +0 -1
- package/dist/emitter/spawn/FXSpawnOffset.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnOffset.js +0 -1
- package/dist/emitter/spawn/FXSpawnPoint.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnPoint.js +0 -1
- package/dist/emitter/spawn/FXSpawnRotation.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnRotation.js +0 -1
- package/dist/emitter/spawn/FXSpawnScale.d.ts +0 -2
- package/dist/emitter/spawn/FXSpawnScale.js +0 -2
- package/dist/emitter/spawn/FXSpawnSphere.d.ts +3 -10
- package/dist/emitter/spawn/FXSpawnSphere.js +8 -15
- package/dist/emitter/spawn/FXSpawnTorque.d.ts +0 -1
- package/dist/emitter/spawn/FXSpawnTorque.js +0 -1
- package/dist/emitter/spawn/FXSpawnVelocity.d.ts +3 -6
- package/dist/emitter/spawn/FXSpawnVelocity.js +6 -9
- package/dist/index.d.ts +3 -1
- package/dist/index.js +3 -1
- package/dist/instancedParticle/FXInstancedParticle.d.ts +1 -0
- package/dist/instancedParticle/FXInstancedParticle.js +4 -1
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +4 -3
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +42 -56
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +1 -1
- package/dist/materials/FXMaterial/FXBlending.Internal.d.ts +24 -0
- package/dist/materials/FXMaterial/FXBlending.Internal.js +64 -0
- package/dist/materials/FXMaterial/FXDepthMaterial.Internal.js +8 -3
- package/dist/materials/FXMaterial/FXDistanceMaterial.Internal.js +7 -3
- package/dist/materials/FXMaterial/FXMaterial.d.ts +18 -15
- package/dist/materials/FXMaterial/FXMaterial.js +22 -7
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +3 -2
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.js +16 -14
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +1 -1
- package/dist/nodes/FXNode.d.ts +5 -0
- package/dist/nodes/FXNode.js +10 -0
- package/dist/nodes/blending/FXNodeBlending.d.ts +6 -0
- package/dist/nodes/blending/FXNodeBlending.js +9 -0
- package/dist/nodes/blending/FXNodeLightnessBlendingMask.d.ts +16 -0
- package/dist/nodes/blending/FXNodeLightnessBlendingMask.js +49 -0
- package/dist/nodes/color/FXNodeColor.d.ts +2 -3
- package/dist/nodes/color/FXNodeColor.js +3 -5
- package/dist/nodes/color/FXNodeColorOverLife.d.ts +0 -5
- package/dist/nodes/color/FXNodeColorOverLife.js +6 -8
- package/dist/nodes/color/FXNodeSphericalClip.js +6 -3
- package/dist/nodes/normal/FXNodeFlatNormal.d.ts +0 -1
- package/dist/nodes/normal/FXNodeFlatNormal.js +5 -5
- package/dist/nodes/normal/FXNodeNormal.d.ts +2 -3
- package/dist/nodes/normal/FXNodeNormal.js +3 -1
- package/dist/nodes/normal/FXNodeSphericalNormal.d.ts +0 -1
- package/dist/nodes/normal/FXNodeSphericalNormal.js +11 -9
- package/dist/nodes/texture/FXNodeAnimatedTexture.d.ts +3 -3
- package/dist/nodes/texture/FXNodeAnimatedTexture.js +19 -17
- package/dist/nodes/texture/FXNodeStaticTexture.d.ts +3 -3
- package/dist/nodes/texture/FXNodeStaticTexture.js +21 -18
- package/dist/nodes/texture/FXNodeTexture.d.ts +2 -3
- package/dist/nodes/texture/FXNodeTexture.js +3 -5
- package/package.json +1 -1
|
@@ -28,6 +28,10 @@ class FXSpawnVelocity extends FXSpawn {
|
|
|
28
28
|
assertValidNumber(this._private_magnitudeInternal.min, "FXSpawnVelocity.constructor.magnitude.min");
|
|
29
29
|
assertValidNumber(this._private_magnitudeInternal.max, "FXSpawnVelocity.constructor.magnitude.max");
|
|
30
30
|
}
|
|
31
|
+
/** Cone axis (always normalized) */
|
|
32
|
+
get direction() {
|
|
33
|
+
return this._private_directionInternal;
|
|
34
|
+
}
|
|
31
35
|
/** Cone half-angle range in radians */
|
|
32
36
|
get angle() {
|
|
33
37
|
return this._private_angleInternal;
|
|
@@ -36,26 +40,19 @@ class FXSpawnVelocity extends FXSpawn {
|
|
|
36
40
|
get magnitude() {
|
|
37
41
|
return this._private_magnitudeInternal;
|
|
38
42
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
return this._private_directionInternal;
|
|
43
|
+
set direction(value) {
|
|
44
|
+
this._private_directionInternal.copy(value).normalize();
|
|
42
45
|
}
|
|
43
|
-
/** Cone half-angle range in radians */
|
|
44
46
|
set angle(value) {
|
|
45
47
|
this._private_angleInternal = resolveFXRangeConfig(value);
|
|
46
48
|
assertValidNumber(this._private_angleInternal.min, "FXSpawnVelocity.angle.min");
|
|
47
49
|
assertValidNumber(this._private_angleInternal.max, "FXSpawnVelocity.angle.max");
|
|
48
50
|
}
|
|
49
|
-
/** Speed range in units/second */
|
|
50
51
|
set magnitude(value) {
|
|
51
52
|
this._private_magnitudeInternal = resolveFXRangeConfig(value);
|
|
52
53
|
assertValidNumber(this._private_magnitudeInternal.min, "FXSpawnVelocity.magnitude.min");
|
|
53
54
|
assertValidNumber(this._private_magnitudeInternal.max, "FXSpawnVelocity.magnitude.max");
|
|
54
55
|
}
|
|
55
|
-
/** Cone axis - normalized on assignment */
|
|
56
|
-
set direction(value) {
|
|
57
|
-
this._private_directionInternal.copy(value).normalize();
|
|
58
|
-
}
|
|
59
56
|
/** @internal */
|
|
60
57
|
spawn(properties, instanceBegin, instanceEnd) {
|
|
61
58
|
const { builtin } = properties;
|
package/dist/index.d.ts
CHANGED
|
@@ -18,13 +18,15 @@ export { FXSpawnSphere } from "./emitter/spawn/FXSpawnSphere";
|
|
|
18
18
|
export { FXSpawnTorque } from "./emitter/spawn/FXSpawnTorque";
|
|
19
19
|
export { FXSpawnVelocity } from "./emitter/spawn/FXSpawnVelocity";
|
|
20
20
|
export { FXDiffuseMaterial } from "./materials/FXDiffuseMaterial/FXDiffuseMaterial";
|
|
21
|
-
export { FXMaterial } from "./materials/FXMaterial/FXMaterial";
|
|
21
|
+
export { FXBlending, FXMaterial } from "./materials/FXMaterial/FXMaterial";
|
|
22
22
|
export { FXUnlitMaterial } from "./materials/FXUnlitMaterial/FXUnlitMaterial";
|
|
23
23
|
export { FXColor } from "./miscellaneous/color/FXColor";
|
|
24
24
|
export { FXCurve1D } from "./miscellaneous/curve/FXCurve1D";
|
|
25
25
|
export type { FXCurve1DAnchor, FXCurve1DConfig, FXCurve1DPoint, } from "./miscellaneous/curve/FXCurve1D";
|
|
26
26
|
export { FXTextureView } from "./miscellaneous/texture/FXTextureView";
|
|
27
27
|
export type { FXTextureAtlasConfig, FXTextureConfig, FXTextureRect, FXTextureSize, FXTextureTrim, } from "./miscellaneous/texture/FXTextureView.Internal";
|
|
28
|
+
export { FXNodeBlending } from "./nodes/blending/FXNodeBlending";
|
|
29
|
+
export { FXNodeLightnessBlendingMask } from "./nodes/blending/FXNodeLightnessBlendingMask";
|
|
28
30
|
export { FXNodeColor } from "./nodes/color/FXNodeColor";
|
|
29
31
|
export { FXNodeColorOverLife } from "./nodes/color/FXNodeColorOverLife";
|
|
30
32
|
export { FXNodeSphericalClip } from "./nodes/color/FXNodeSphericalClip";
|
package/dist/index.js
CHANGED
|
@@ -18,11 +18,13 @@ export { FXSpawnSphere } from './emitter/spawn/FXSpawnSphere.js';
|
|
|
18
18
|
export { FXSpawnTorque } from './emitter/spawn/FXSpawnTorque.js';
|
|
19
19
|
export { FXSpawnVelocity } from './emitter/spawn/FXSpawnVelocity.js';
|
|
20
20
|
export { FXDiffuseMaterial } from './materials/FXDiffuseMaterial/FXDiffuseMaterial.js';
|
|
21
|
-
export { FXMaterial } from './materials/FXMaterial/FXMaterial.js';
|
|
21
|
+
export { FXBlending, FXMaterial } from './materials/FXMaterial/FXMaterial.js';
|
|
22
22
|
export { FXUnlitMaterial } from './materials/FXUnlitMaterial/FXUnlitMaterial.js';
|
|
23
23
|
export { FXColor } from './miscellaneous/color/FXColor.js';
|
|
24
24
|
export { FXCurve1D } from './miscellaneous/curve/FXCurve1D.js';
|
|
25
25
|
export { FXTextureView } from './miscellaneous/texture/FXTextureView.js';
|
|
26
|
+
export { FXNodeBlending } from './nodes/blending/FXNodeBlending.js';
|
|
27
|
+
export { FXNodeLightnessBlendingMask } from './nodes/blending/FXNodeLightnessBlendingMask.js';
|
|
26
28
|
export { FXNodeColor } from './nodes/color/FXNodeColor.js';
|
|
27
29
|
export { FXNodeColorOverLife } from './nodes/color/FXNodeColorOverLife.js';
|
|
28
30
|
export { FXNodeSphericalClip } from './nodes/color/FXNodeSphericalClip.js';
|
|
@@ -12,6 +12,7 @@ export declare class FXInstancedParticle extends Mesh {
|
|
|
12
12
|
private sortingTemporaryBuffer;
|
|
13
13
|
constructor(varyings: Record<string, GLTypeInfo>, expectedCapacity: number, capacityStep: number, material: Material);
|
|
14
14
|
get instanceCount(): number;
|
|
15
|
+
get instanceCapacity(): number;
|
|
15
16
|
createInstances(count: number): void;
|
|
16
17
|
removeDeadParticles(): void;
|
|
17
18
|
sortByDistance(cameraWorldPosition: Vector3): void;
|
|
@@ -27,6 +27,9 @@ class FXInstancedParticle extends Mesh {
|
|
|
27
27
|
get instanceCount() {
|
|
28
28
|
return this._private_instancedGeometry.instanceCount;
|
|
29
29
|
}
|
|
30
|
+
get instanceCapacity() {
|
|
31
|
+
return this._private_capacity;
|
|
32
|
+
}
|
|
30
33
|
createInstances(count) {
|
|
31
34
|
const currentInstanceCount = this._private_instancedGeometry.instanceCount;
|
|
32
35
|
this._private_ensureCapacity(currentInstanceCount + count);
|
|
@@ -151,7 +154,7 @@ class FXInstancedParticle extends Mesh {
|
|
|
151
154
|
//
|
|
152
155
|
// _maxInstanceCount is an internal renderer cache field set by
|
|
153
156
|
// WebGLBindingStates.js. It was never promoted to a public API in the
|
|
154
|
-
// r157
|
|
157
|
+
// r157-r180 range. Deleting it is the standard workaround used across
|
|
155
158
|
// the Three.js ecosystem (see #19706, #26363, #27205).
|
|
156
159
|
//
|
|
157
160
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type { Blending } from "three";
|
|
2
1
|
import { Color, MeshLambertMaterial } from "three";
|
|
3
2
|
import type { GLTypeInfo } from "../../instancedParticle/shared";
|
|
4
|
-
import type {
|
|
3
|
+
import type { FXNodeBlending } from "../../nodes/blending/FXNodeBlending";
|
|
4
|
+
import { FXNodeColor } from "../../nodes/color/FXNodeColor";
|
|
5
5
|
import type { FXNodeNormal } from "../../nodes/normal/FXNodeNormal";
|
|
6
6
|
import { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
|
|
7
|
+
import { FXBlending } from "../FXMaterial/FXMaterial";
|
|
7
8
|
export interface FXScatterUniforms {
|
|
8
9
|
u_ScatterTint: {
|
|
9
10
|
value: Color;
|
|
@@ -18,6 +19,6 @@ export interface FXScatterUniforms {
|
|
|
18
19
|
value: number;
|
|
19
20
|
};
|
|
20
21
|
}
|
|
21
|
-
export declare function buildFXDiffuseMaterial(varyings: Record<string, GLTypeInfo>, blending:
|
|
22
|
+
export declare function buildFXDiffuseMaterial(varyings: Record<string, GLTypeInfo>, blending: FXBlending, useAlphaHashing: boolean, alphaTest: number, albedoNodes: readonly (FXNodeColor | FXNodeTexture | FXNodeBlending)[], normalNodes: readonly (FXNodeTexture | FXNodeNormal)[], emissionNodes: readonly (FXNodeColor | FXNodeTexture)[], scatterUniforms?: FXScatterUniforms, shadowSensitivity?: {
|
|
22
23
|
value: number;
|
|
23
24
|
}): MeshLambertMaterial;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { MeshLambertMaterial, Color, DoubleSide } from 'three';
|
|
1
|
+
import { MeshLambertMaterial, Color, DoubleSide, NormalBlending } from 'three';
|
|
2
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';
|
|
3
5
|
import { FXNodeTexture } from '../../nodes/texture/FXNodeTexture.js';
|
|
6
|
+
import { buildAlbedoAndBlendingCode } from '../FXMaterial/FXBlending.Internal.js';
|
|
7
|
+
import { FXBlending } from '../FXMaterial/FXMaterial.js';
|
|
4
8
|
|
|
5
9
|
/**
|
|
6
10
|
* Wraps every `getShadow(...)` call in the shader source with
|
|
@@ -37,7 +41,7 @@ function wrapShadowCalls(source, uniformName) {
|
|
|
37
41
|
}
|
|
38
42
|
return result;
|
|
39
43
|
}
|
|
40
|
-
function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
44
|
+
function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest, albedoNodes, normalNodes, emissionNodes, scatterUniforms, shadowSensitivity) {
|
|
41
45
|
const useScatter = scatterUniforms !== undefined;
|
|
42
46
|
const useShadowSensitivity = shadowSensitivity !== undefined;
|
|
43
47
|
const useNormals = normalNodes.length > 0;
|
|
@@ -56,12 +60,12 @@ function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
|
56
60
|
depthWrite: useAlphaHashing,
|
|
57
61
|
depthTest: true,
|
|
58
62
|
alphaHash: useAlphaHashing,
|
|
59
|
-
blending,
|
|
63
|
+
blending: NormalBlending,
|
|
60
64
|
side: DoubleSide,
|
|
61
65
|
forceSinglePass: true,
|
|
62
66
|
alphaTest,
|
|
63
67
|
emissive: emissionNodes.length > 0 ? new Color(1, 1, 1) : new Color(0, 0, 0),
|
|
64
|
-
premultipliedAlpha,
|
|
68
|
+
premultipliedAlpha: true,
|
|
65
69
|
});
|
|
66
70
|
material.onBeforeCompile = (shader) => {
|
|
67
71
|
const vertexPreamble = [
|
|
@@ -117,25 +121,16 @@ function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
|
117
121
|
);
|
|
118
122
|
#endif
|
|
119
123
|
`);
|
|
120
|
-
const blendReorientedNormalsHelper = normalNodes.length > 1
|
|
121
|
-
? `
|
|
122
|
-
vec3 blendReorientedNormals(vec3 base, vec3 detail) {
|
|
123
|
-
base += vec3(0.0, 0.0, 1.0);
|
|
124
|
-
detail *= vec3(-1.0, -1.0, 1.0);
|
|
125
|
-
return normalize(base * dot(base, detail) / base.z - detail);
|
|
126
|
-
}
|
|
127
|
-
`
|
|
128
|
-
: "";
|
|
129
124
|
const seenCacheKeys = new Set();
|
|
130
125
|
const uniqueHelpers = (nodes) => nodes
|
|
131
|
-
.filter((
|
|
132
|
-
if (seenCacheKeys.has(
|
|
126
|
+
.filter((node) => {
|
|
127
|
+
if (seenCacheKeys.has(node.cacheKey)) {
|
|
133
128
|
return false;
|
|
134
129
|
}
|
|
135
|
-
seenCacheKeys.add(
|
|
130
|
+
seenCacheKeys.add(node.cacheKey);
|
|
136
131
|
return true;
|
|
137
132
|
})
|
|
138
|
-
.map((
|
|
133
|
+
.map((node) => node.helperFunctions)
|
|
139
134
|
.join("\n");
|
|
140
135
|
const scatterDeclarations = useScatter
|
|
141
136
|
? [
|
|
@@ -160,12 +155,11 @@ function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
|
160
155
|
"varying vec2 p_uv;",
|
|
161
156
|
useNormals ? "varying vec2 p_billboardSinCos;" : "",
|
|
162
157
|
varyingDeclarations.join("\n"),
|
|
163
|
-
albedoNodes.flatMap((
|
|
158
|
+
albedoNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
|
|
164
159
|
uniqueHelpers(albedoNodes),
|
|
165
|
-
normalNodes.flatMap((
|
|
160
|
+
normalNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
|
|
166
161
|
uniqueHelpers(normalNodes),
|
|
167
|
-
|
|
168
|
-
emissionNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
162
|
+
emissionNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
|
|
169
163
|
uniqueHelpers(emissionNodes),
|
|
170
164
|
...scatterDeclarations,
|
|
171
165
|
...shadowSensitivityDeclarations,
|
|
@@ -190,42 +184,32 @@ function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
|
190
184
|
}
|
|
191
185
|
let fragmentShader = shader.fragmentShader;
|
|
192
186
|
if (useNormals) {
|
|
193
|
-
const
|
|
194
|
-
if (node instanceof FXNodeTexture) {
|
|
195
|
-
return `normalize(${node.colorExpression}.rgb * 2.0 - 1.0)`;
|
|
196
|
-
}
|
|
197
|
-
return node.normalExpression;
|
|
198
|
-
});
|
|
199
|
-
const blendLines = normalExpressions
|
|
200
|
-
.slice(1)
|
|
201
|
-
.map((expression) => `normal = blendReorientedNormals(normal, ${expression});`);
|
|
202
|
-
const normalCode = [
|
|
187
|
+
const normalLines = [
|
|
203
188
|
"float faceDirection = gl_FrontFacing ? 1.0 : -1.0;",
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
"",
|
|
217
|
-
"normal *= faceDirection;",
|
|
218
|
-
].join("\n");
|
|
219
|
-
fragmentShader = fragmentShader.replace("#include <normal_fragment_begin>", normalCode);
|
|
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"));
|
|
220
201
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
fragmentShader = fragmentShader.replace("#include <map_fragment>", `diffuseColor = ${combinedExpression}; ${discardLine}`);
|
|
202
|
+
const albedoCode = buildAlbedoAndBlendingCode(albedoNodes, blending, useAlphaHashing);
|
|
203
|
+
if (albedoCode.mapFragment !== null) {
|
|
204
|
+
fragmentShader = fragmentShader.replace("#include <map_fragment>", albedoCode.mapFragment);
|
|
225
205
|
}
|
|
226
206
|
if (emissionNodes.length > 0) {
|
|
227
|
-
const
|
|
228
|
-
|
|
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"));
|
|
229
213
|
}
|
|
230
214
|
if (useScatter) {
|
|
231
215
|
fragmentShader = fragmentShader.replace("#include <opaque_fragment>", `
|
|
@@ -271,15 +255,17 @@ function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
|
271
255
|
if (useShadowSensitivity) {
|
|
272
256
|
fragmentShader = wrapShadowCalls(fragmentShader, "u_shadowSensitivity");
|
|
273
257
|
}
|
|
258
|
+
fragmentShader = fragmentShader.replace("#include <premultiplied_alpha_fragment>", albedoCode.premultChunk);
|
|
274
259
|
shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
|
|
275
260
|
};
|
|
276
261
|
const scatterKey = useScatter ? `scatter(${scatterUniforms.u_ScatterPower.value})` : "no-scatter";
|
|
277
262
|
const shadowKey = useShadowSensitivity ? "shadow-sens" : "no-shadow-sens";
|
|
278
263
|
material.customProgramCacheKey = () => [
|
|
279
264
|
"fx",
|
|
280
|
-
albedoNodes.map((
|
|
281
|
-
normalNodes.map((
|
|
282
|
-
emissionNodes.map((
|
|
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],
|
|
283
269
|
scatterKey,
|
|
284
270
|
shadowKey,
|
|
285
271
|
].join("_");
|
|
@@ -41,7 +41,7 @@ class FXDiffuseMaterial extends FXMaterial {
|
|
|
41
41
|
}
|
|
42
42
|
/** @internal */
|
|
43
43
|
buildThreeMaterial(varyings) {
|
|
44
|
-
return buildFXDiffuseMaterial(varyings, this.blending, this.useAlphaHashing, this.alphaTest, this.
|
|
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
45
|
}
|
|
46
46
|
}
|
|
47
47
|
|
|
@@ -0,0 +1,24 @@
|
|
|
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;
|
|
@@ -0,0 +1,64 @@
|
|
|
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,5 +1,6 @@
|
|
|
1
1
|
import { MeshDepthMaterial, DoubleSide, RGBADepthPacking } from 'three';
|
|
2
2
|
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER } from '../../nodes/FXNode.js';
|
|
3
4
|
|
|
4
5
|
const DEPTH_VERTEX_SHADER = `
|
|
5
6
|
#define PARTICLE_POSITION_X a_builtin[0][0]
|
|
@@ -116,9 +117,13 @@ function buildDepthMaterial(albedoNodes, depthAlphaTest, useDepthAlphaHash, useS
|
|
|
116
117
|
}
|
|
117
118
|
let fragmentShader = shader.fragmentShader;
|
|
118
119
|
if (depthNodes.length > 0) {
|
|
119
|
-
const
|
|
120
|
-
|
|
121
|
-
|
|
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"));
|
|
122
127
|
}
|
|
123
128
|
if (useSphericalDepth) {
|
|
124
129
|
if (!FRAG_COORD_Z_PATTERN.test(fragmentShader)) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { MeshDistanceMaterial, DoubleSide } from 'three';
|
|
2
2
|
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { CURRENT_EXPRESSION_VALUE_PLACEHOLDER } from '../../nodes/FXNode.js';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Custom vertex shader for point/spot light shadow maps.
|
|
@@ -134,9 +135,12 @@ function buildDistanceMaterial(albedoNodes, distanceAlphaTest, useDistanceAlphaH
|
|
|
134
135
|
// <map_fragment> depending on the Three.js version. If the chunk is
|
|
135
136
|
// absent, insert the alpha discard before the distance calculation.
|
|
136
137
|
if (distanceNodes.length > 0) {
|
|
137
|
-
const
|
|
138
|
-
const
|
|
139
|
-
`
|
|
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;`;
|
|
140
144
|
if (fragmentShader.includes("#include <map_fragment>")) {
|
|
141
145
|
fragmentShader = fragmentShader.replace("#include <map_fragment>", alphaDiscardCode);
|
|
142
146
|
}
|
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
|
|
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
|
+
}
|
|
4
18
|
/**
|
|
5
19
|
* Configuration options shared by all {@link FXMaterial} subclasses
|
|
6
20
|
*/
|
|
@@ -11,12 +25,7 @@ export interface FXMaterialOptions {
|
|
|
11
25
|
* @defaultValue `[]`
|
|
12
26
|
*/
|
|
13
27
|
albedoNodes: (FXNodeColor | FXNodeTexture)[];
|
|
14
|
-
|
|
15
|
-
* Three.js blending mode
|
|
16
|
-
*
|
|
17
|
-
* @defaultValue `NormalBlending`
|
|
18
|
-
*/
|
|
19
|
-
blending: Blending;
|
|
28
|
+
blending: FXBlending;
|
|
20
29
|
/**
|
|
21
30
|
* Use alpha hashing instead of standard transparency
|
|
22
31
|
*
|
|
@@ -47,12 +56,6 @@ export interface FXMaterialOptions {
|
|
|
47
56
|
* @defaultValue `true`
|
|
48
57
|
*/
|
|
49
58
|
useSphericalDepth: boolean;
|
|
50
|
-
/**
|
|
51
|
-
* Use premultiplied alpha blending
|
|
52
|
-
*
|
|
53
|
-
* @defaultValue `true`
|
|
54
|
-
*/
|
|
55
|
-
premultipliedAlpha: boolean;
|
|
56
59
|
}
|
|
57
60
|
/**
|
|
58
61
|
* Abstract base class for particle materials
|
|
@@ -1,18 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FXNodeColor } from '../../nodes/color/FXNodeColor.js';
|
|
2
|
+
import { FXNodeTexture } from '../../nodes/texture/FXNodeTexture.js';
|
|
2
3
|
import { buildDepthMaterial } from './FXDepthMaterial.Internal.js';
|
|
3
4
|
import { buildDistanceMaterial } from './FXDistanceMaterial.Internal.js';
|
|
4
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 = {}));
|
|
5
22
|
/**
|
|
6
23
|
* Abstract base class for particle materials
|
|
7
24
|
*/
|
|
8
25
|
class FXMaterial {
|
|
9
26
|
constructor(options = {}) {
|
|
10
27
|
this.albedoNodes = options.albedoNodes ?? [];
|
|
11
|
-
this.blending = options.blending ??
|
|
28
|
+
this.blending = options.blending ?? FXBlending.MASKED_ADDITIVE;
|
|
12
29
|
this.useAlphaHashing = options.useAlphaHashing ?? false;
|
|
13
30
|
this.alphaTest = options.alphaTest ?? 0.0075;
|
|
14
|
-
this.premultipliedAlpha =
|
|
15
|
-
(options.useAlphaHashing === undefined && options.premultipliedAlpha) ?? true;
|
|
16
31
|
this._private_depthAlphaTest = options.depthAlphaTest ?? 0.5;
|
|
17
32
|
this._private_useDepthAlphaHash = options.useDepthAlphaHash ?? false;
|
|
18
33
|
this._private_useSphericalDepth = options.useSphericalDepth ?? true;
|
|
@@ -25,12 +40,12 @@ class FXMaterial {
|
|
|
25
40
|
}
|
|
26
41
|
/** @internal */
|
|
27
42
|
buildDepthMaterial() {
|
|
28
|
-
return buildDepthMaterial(this.albedoNodes, this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
|
|
43
|
+
return buildDepthMaterial(this.albedoNodes.filter((node) => node instanceof FXNodeColor || node instanceof FXNodeTexture), this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
|
|
29
44
|
}
|
|
30
45
|
/** @internal */
|
|
31
46
|
buildDistanceMaterial() {
|
|
32
|
-
return buildDistanceMaterial(this.albedoNodes, this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
|
|
47
|
+
return buildDistanceMaterial(this.albedoNodes.filter((node) => node instanceof FXNodeColor || node instanceof FXNodeTexture), this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
|
|
33
48
|
}
|
|
34
49
|
}
|
|
35
50
|
|
|
36
|
-
export { FXMaterial };
|
|
51
|
+
export { FXBlending, FXMaterial };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { Blending } from "three";
|
|
2
1
|
import { MeshBasicMaterial } from "three";
|
|
3
2
|
import type { GLTypeInfo } from "../../instancedParticle/shared";
|
|
3
|
+
import type { FXNodeBlending } from "../../nodes/blending/FXNodeBlending";
|
|
4
4
|
import type { FXNodeColor } from "../../nodes/color/FXNodeColor";
|
|
5
5
|
import type { FXNodeTexture } from "../../nodes/texture/FXNodeTexture";
|
|
6
|
-
|
|
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;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { MeshBasicMaterial, DoubleSide } from 'three';
|
|
1
|
+
import { MeshBasicMaterial, DoubleSide, NormalBlending } from 'three';
|
|
2
2
|
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { buildAlbedoAndBlendingCode } from '../FXMaterial/FXBlending.Internal.js';
|
|
4
|
+
import { FXBlending } from '../FXMaterial/FXMaterial.js';
|
|
3
5
|
|
|
4
|
-
function buildFXUnlitMaterial(varyings, blending, useAlphaHashing, alphaTest,
|
|
6
|
+
function buildFXUnlitMaterial(varyings, blending, useAlphaHashing, alphaTest, albedoNodes) {
|
|
5
7
|
const attributeDeclarations = [];
|
|
6
8
|
const varyingDeclarations = [];
|
|
7
9
|
const vertexAssignments = [];
|
|
@@ -17,11 +19,11 @@ function buildFXUnlitMaterial(varyings, blending, useAlphaHashing, alphaTest, pr
|
|
|
17
19
|
depthWrite: useAlphaHashing,
|
|
18
20
|
depthTest: true,
|
|
19
21
|
alphaHash: useAlphaHashing,
|
|
20
|
-
blending,
|
|
22
|
+
blending: NormalBlending,
|
|
21
23
|
side: DoubleSide,
|
|
22
24
|
forceSinglePass: true,
|
|
23
25
|
alphaTest,
|
|
24
|
-
premultipliedAlpha,
|
|
26
|
+
premultipliedAlpha: true,
|
|
25
27
|
});
|
|
26
28
|
material.onBeforeCompile = (shader) => {
|
|
27
29
|
const vertexPreamble = [
|
|
@@ -63,34 +65,34 @@ function buildFXUnlitMaterial(varyings, blending, useAlphaHashing, alphaTest, pr
|
|
|
63
65
|
`);
|
|
64
66
|
const seenCacheKeys = new Set();
|
|
65
67
|
const uniqueHelpers = (nodes) => nodes
|
|
66
|
-
.filter((
|
|
67
|
-
if (seenCacheKeys.has(
|
|
68
|
+
.filter((node) => {
|
|
69
|
+
if (seenCacheKeys.has(node.cacheKey)) {
|
|
68
70
|
return false;
|
|
69
71
|
}
|
|
70
|
-
seenCacheKeys.add(
|
|
72
|
+
seenCacheKeys.add(node.cacheKey);
|
|
71
73
|
return true;
|
|
72
74
|
})
|
|
73
|
-
.map((
|
|
75
|
+
.map((node) => node.helperFunctions)
|
|
74
76
|
.join("\n");
|
|
75
77
|
const fragmentPreamble = [
|
|
76
78
|
PARTICLE_DEFINES,
|
|
77
79
|
"varying vec2 p_uv;",
|
|
78
80
|
varyingDeclarations.join("\n"),
|
|
79
|
-
albedoNodes.flatMap((
|
|
81
|
+
albedoNodes.flatMap((node) => node.uniformDeclarations).join("\n"),
|
|
80
82
|
uniqueHelpers(albedoNodes),
|
|
81
83
|
].join("\n");
|
|
82
84
|
for (const node of albedoNodes) {
|
|
83
85
|
Object.assign(shader.uniforms, node.uniforms);
|
|
84
86
|
}
|
|
87
|
+
const albedoCode = buildAlbedoAndBlendingCode(albedoNodes, blending, useAlphaHashing);
|
|
85
88
|
let fragmentShader = shader.fragmentShader;
|
|
86
|
-
if (
|
|
87
|
-
|
|
88
|
-
const discardLine = useAlphaHashing ? "" : "if (diffuseColor.a < 0.0035) discard;";
|
|
89
|
-
fragmentShader = fragmentShader.replace("#include <map_fragment>", `diffuseColor = ${combinedExpression}; ${discardLine}`);
|
|
89
|
+
if (albedoCode.mapFragment !== null) {
|
|
90
|
+
fragmentShader = fragmentShader.replace("#include <map_fragment>", albedoCode.mapFragment);
|
|
90
91
|
}
|
|
92
|
+
fragmentShader = fragmentShader.replace("#include <premultiplied_alpha_fragment>", albedoCode.premultChunk);
|
|
91
93
|
shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
|
|
92
94
|
};
|
|
93
|
-
material.customProgramCacheKey = () =>
|
|
95
|
+
material.customProgramCacheKey = () => ["fx-unlit", albedoNodes.map((node) => node.cacheKey).join("-") || "none", FXBlending[blending]].join("_");
|
|
94
96
|
return material;
|
|
95
97
|
}
|
|
96
98
|
|