sparcoon 0.1.0 → 0.3.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/{elements → emitter}/FXEmitter.Internal.d.ts +4 -4
- package/dist/emitter/FXEmitter.Internal.js +26 -0
- package/dist/emitter/FXEmitter.d.ts +31 -0
- package/dist/{elements → emitter}/FXEmitter.js +14 -22
- package/dist/{behaviorModules/FXBehaviorModule.d.ts → emitter/behavior/FXBehavior.d.ts} +2 -2
- package/dist/{behaviorModules/FXBehaviorModule.js → emitter/behavior/FXBehavior.js} +2 -2
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorDirectionalGravity.d.ts +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorDirectionalGravity.js +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorPointGravity.d.ts +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorPointGravity.js +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorScaleOverLife.d.ts +3 -3
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorScaleOverLife.js +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorTorqueDamping.d.ts +3 -3
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorTorqueDamping.js +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorTorqueNoise.d.ts +3 -3
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorTorqueNoise.js +5 -5
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorVelocityDamping.d.ts +3 -3
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorVelocityDamping.js +4 -4
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorVelocityNoise.d.ts +3 -3
- package/dist/{behaviorModules → emitter/behavior}/FXBehaviorVelocityNoise.js +5 -5
- package/dist/emitter/spawn/FXSpawn.d.ts +9 -0
- package/dist/{spawnModules/FXSpawnModule.js → emitter/spawn/FXSpawn.js} +2 -2
- package/dist/{spawnModules → emitter/spawn}/FXSpawnBox.d.ts +4 -4
- package/dist/{spawnModules → emitter/spawn}/FXSpawnBox.js +4 -4
- package/dist/{spawnModules/FXSpawnOffset.d.ts → emitter/spawn/FXSpawnPoint.d.ts} +4 -4
- package/dist/{spawnModules/FXSpawnOffset.js → emitter/spawn/FXSpawnPoint.js} +5 -5
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomLifetime.d.ts +3 -3
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomLifetime.js +4 -4
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomRotation.d.ts +3 -3
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomRotation.js +4 -4
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomScale.d.ts +3 -3
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomScale.js +4 -4
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomTorque.d.ts +3 -3
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomTorque.js +4 -4
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomVelocity.d.ts +3 -3
- package/dist/{spawnModules → emitter/spawn}/FXSpawnRandomVelocity.js +4 -4
- package/dist/emitter/spawn/FXSpawnSphere.d.ts +38 -0
- package/dist/emitter/spawn/FXSpawnSphere.js +89 -0
- package/dist/index.d.ts +28 -20
- package/dist/index.js +28 -22
- package/dist/instancedParticle/FXInstancedParticle.Internal.d.ts +1 -12
- package/dist/instancedParticle/FXInstancedParticle.Internal.js +2 -206
- package/dist/instancedParticle/FXInstancedParticle.d.ts +3 -5
- package/dist/instancedParticle/FXInstancedParticle.js +4 -10
- package/dist/instancedParticle/shared.d.ts +1 -1
- package/dist/instancedParticle/shared.js +1 -1
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.d.ts +7 -0
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.Internal.js +141 -0
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.d.ts +15 -0
- package/dist/materials/FXDiffuseMaterial/FXDiffuseMaterial.js +26 -0
- package/dist/materials/FXMaterial/FXMaterial.Internal.d.ts +5 -0
- package/dist/materials/FXMaterial/FXMaterial.Internal.js +106 -0
- package/dist/materials/FXMaterial/FXMaterial.d.ts +19 -0
- package/dist/materials/FXMaterial/FXMaterial.js +25 -0
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.d.ts +6 -0
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.Internal.js +84 -0
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.d.ts +6 -0
- package/dist/materials/FXUnlitMaterial/FXUnlitMaterial.js +14 -0
- package/dist/{color → miscellaneous/color}/FXColor.js +2 -2
- package/dist/miscellaneous/miscellaneous.d.ts +2 -1
- package/dist/miscellaneous/miscellaneous.js +24 -1
- package/dist/nodes/color/FXColorNode.d.ts +3 -0
- package/dist/nodes/color/FXColorNode.js +8 -0
- package/dist/nodes/color/FXColorOverLifeNode.d.ts +6 -0
- package/dist/nodes/color/FXColorOverLifeNode.js +38 -0
- package/dist/nodes/normal/FXFlatNormalNode.d.ts +4 -0
- package/dist/nodes/normal/FXFlatNormalNode.js +14 -0
- package/dist/nodes/normal/FXNormalNode.d.ts +3 -0
- package/dist/nodes/normal/FXNormalNode.js +4 -0
- package/dist/nodes/normal/FXSphericalNormalNode.d.ts +4 -0
- package/dist/nodes/normal/FXSphericalNormalNode.js +19 -0
- package/dist/nodes/texture/FXAnimatedTextureNode.d.ts +11 -0
- package/dist/nodes/texture/FXAnimatedTextureNode.js +80 -0
- package/dist/nodes/texture/FXStaticTextureNode.d.ts +5 -0
- package/dist/nodes/texture/FXStaticTextureNode.js +26 -0
- package/dist/nodes/texture/FXTextureNode.d.ts +3 -0
- package/dist/nodes/texture/FXTextureNode.js +8 -0
- package/package.json +1 -1
- package/dist/elements/FXEmitter.Internal.js +0 -12
- package/dist/elements/FXEmitter.d.ts +0 -38
- package/dist/instancedParticle/FXParticleDepthMaterial.d.ts +0 -2
- package/dist/instancedParticle/FXParticleDepthMaterial.js +0 -52
- package/dist/instancedParticle/FXParticleLambertMaterial.d.ts +0 -4
- package/dist/instancedParticle/FXParticleLambertMaterial.js +0 -152
- package/dist/instancedParticle/FXParticleNormalsMode.d.ts +0 -5
- package/dist/instancedParticle/FXParticleNormalsMode.js +0 -8
- package/dist/instancedParticle/FXParticleRenderingMode.d.ts +0 -4
- package/dist/instancedParticle/FXParticleRenderingMode.js +0 -7
- package/dist/miscellaneous/texture/FXTextureView.Internal.js +0 -6
- package/dist/miscellaneous/texture/FXTextureView.js +0 -234
- package/dist/renderingModules/FXRenderingAnimatedTexture.d.ts +0 -5
- package/dist/renderingModules/FXRenderingAnimatedTexture.js +0 -14
- package/dist/renderingModules/FXRenderingColorOverLife.d.ts +0 -14
- package/dist/renderingModules/FXRenderingColorOverLife.js +0 -32
- package/dist/renderingModules/FXRenderingColorOverVelocity.d.ts +0 -15
- package/dist/renderingModules/FXRenderingColorOverVelocity.js +0 -37
- package/dist/renderingModules/FXRenderingModule.d.ts +0 -8
- package/dist/renderingModules/FXRenderingModule.js +0 -9
- package/dist/renderingModules/FXRenderingShadow.d.ts +0 -12
- package/dist/renderingModules/FXRenderingTexture.d.ts +0 -14
- package/dist/renderingModules/FXRenderingTexture.js +0 -26
- package/dist/shaders/FXRenderingAnimatedTexture.glsl.js +0 -3
- package/dist/shaders/FXRenderingColorOverLife.glsl.js +0 -3
- package/dist/shaders/FXRenderingColorOverVelocity.glsl.js +0 -3
- package/dist/shaders/FXRenderingTexture.glsl.js +0 -3
- package/dist/spawnModules/FXSpawnModule.d.ts +0 -9
- /package/dist/{color → miscellaneous/color}/FXColor.Internal.d.ts +0 -0
- /package/dist/{color → miscellaneous/color}/FXColor.Internal.js +0 -0
- /package/dist/{color → miscellaneous/color}/FXColor.d.ts +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { MeshLambertMaterial, Color, DoubleSide } from 'three';
|
|
2
|
+
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
import { FXTextureNode } from '../../nodes/texture/FXTextureNode.js';
|
|
4
|
+
|
|
5
|
+
function buildFXDiffuseMaterial(varyings, blending, useAlphaHashing, alphaTest, albedoNodes, normalNodes, emissionNodes) {
|
|
6
|
+
const attributeDeclarations = [];
|
|
7
|
+
const varyingDeclarations = [];
|
|
8
|
+
const vertexAssignments = [];
|
|
9
|
+
for (const name in varyings) {
|
|
10
|
+
const { glslTypeName } = varyings[name];
|
|
11
|
+
attributeDeclarations.push(`attribute ${glslTypeName} a_${name};`);
|
|
12
|
+
varyingDeclarations.push(`varying ${glslTypeName} p_${name};`);
|
|
13
|
+
vertexAssignments.push(`p_${name} = a_${name};`);
|
|
14
|
+
}
|
|
15
|
+
const material = new MeshLambertMaterial({
|
|
16
|
+
transparent: !useAlphaHashing,
|
|
17
|
+
depthWrite: useAlphaHashing,
|
|
18
|
+
depthTest: true,
|
|
19
|
+
alphaHash: useAlphaHashing,
|
|
20
|
+
blending,
|
|
21
|
+
side: DoubleSide,
|
|
22
|
+
forceSinglePass: true,
|
|
23
|
+
alphaTest,
|
|
24
|
+
emissive: emissionNodes.length > 0 ? new Color(1, 1, 1) : new Color(0, 0, 0),
|
|
25
|
+
});
|
|
26
|
+
material.onBeforeCompile = (shader) => {
|
|
27
|
+
const vertexPreamble = [
|
|
28
|
+
PARTICLE_DEFINES,
|
|
29
|
+
attributeDeclarations.join("\n"),
|
|
30
|
+
"varying vec2 p_uv;",
|
|
31
|
+
varyingDeclarations.join("\n"),
|
|
32
|
+
].join("\n");
|
|
33
|
+
shader.vertexShader =
|
|
34
|
+
vertexPreamble +
|
|
35
|
+
"\n" +
|
|
36
|
+
shader.vertexShader
|
|
37
|
+
.replace("#include <beginnormal_vertex>", "vec3 objectNormal = vec3(0.0, 0.0, 1.0);")
|
|
38
|
+
.replace("#include <defaultnormal_vertex>", "vec3 transformedNormal = vec3(0.0, 0.0, 1.0);")
|
|
39
|
+
.replace("#include <begin_vertex>", [
|
|
40
|
+
"p_uv = uv;",
|
|
41
|
+
vertexAssignments.join("\n"),
|
|
42
|
+
"vec3 transformed = position;",
|
|
43
|
+
"#ifdef USE_ALPHAHASH",
|
|
44
|
+
" vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);",
|
|
45
|
+
"#endif",
|
|
46
|
+
].join("\n"))
|
|
47
|
+
.replace("#include <project_vertex>", `
|
|
48
|
+
// Scale and rotate the billboard offset in camera space
|
|
49
|
+
vec2 billboardOffset = position.xy;
|
|
50
|
+
billboardOffset.x *= PARTICLE_SCALE_X;
|
|
51
|
+
billboardOffset.y *= PARTICLE_SCALE_Y;
|
|
52
|
+
|
|
53
|
+
float cosRotation = cos(PARTICLE_ROTATION);
|
|
54
|
+
float sinRotation = sin(PARTICLE_ROTATION);
|
|
55
|
+
billboardOffset = vec2(
|
|
56
|
+
billboardOffset.x * cosRotation - billboardOffset.y * sinRotation,
|
|
57
|
+
billboardOffset.x * sinRotation + billboardOffset.y * cosRotation
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
// Move particle center to view space, then apply the billboard offset
|
|
61
|
+
vec3 particleCenter = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);
|
|
62
|
+
vec4 mvPosition = modelViewMatrix * vec4(particleCenter, 1.0);
|
|
63
|
+
mvPosition.xy += billboardOffset;
|
|
64
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
65
|
+
`)
|
|
66
|
+
.replace("#include <worldpos_vertex>", `
|
|
67
|
+
#if defined(USE_SHADOWMAP) || defined(USE_ENVMAP) || defined(DISTANCE) || (NUM_SPOT_LIGHT_COORDS > 0)
|
|
68
|
+
// Reconstruct world-space position for shadow/environment maps.
|
|
69
|
+
// Camera right and up are the first two rows of the view matrix rotation (transposed).
|
|
70
|
+
vec3 cameraRight = vec3(viewMatrix[0][0], viewMatrix[1][0], viewMatrix[2][0]);
|
|
71
|
+
vec3 cameraUp = vec3(viewMatrix[0][1], viewMatrix[1][1], viewMatrix[2][1]);
|
|
72
|
+
vec4 worldPosition = vec4(
|
|
73
|
+
(modelMatrix * vec4(particleCenter, 1.0)).xyz + cameraRight * billboardOffset.x + cameraUp * billboardOffset.y,
|
|
74
|
+
1.0
|
|
75
|
+
);
|
|
76
|
+
#endif
|
|
77
|
+
`);
|
|
78
|
+
const blendReorientedNormalsHelper = normalNodes.length > 1
|
|
79
|
+
? `
|
|
80
|
+
vec3 blendReorientedNormals(vec3 base, vec3 detail) {
|
|
81
|
+
base += vec3(0.0, 0.0, 1.0);
|
|
82
|
+
detail *= vec3(-1.0, -1.0, 1.0);
|
|
83
|
+
return normalize(base * dot(base, detail) / base.z - detail);
|
|
84
|
+
}
|
|
85
|
+
`
|
|
86
|
+
: "";
|
|
87
|
+
const fragmentPreamble = [
|
|
88
|
+
PARTICLE_DEFINES,
|
|
89
|
+
"varying vec2 p_uv;",
|
|
90
|
+
varyingDeclarations.join("\n"),
|
|
91
|
+
albedoNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
92
|
+
albedoNodes.map((n) => n.helperFunctions).join("\n"),
|
|
93
|
+
normalNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
94
|
+
normalNodes.map((n) => n.helperFunctions).join("\n"),
|
|
95
|
+
blendReorientedNormalsHelper,
|
|
96
|
+
emissionNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
97
|
+
emissionNodes.map((n) => n.helperFunctions).join("\n"),
|
|
98
|
+
].join("\n");
|
|
99
|
+
for (const node of albedoNodes) {
|
|
100
|
+
Object.assign(shader.uniforms, node.uniforms);
|
|
101
|
+
}
|
|
102
|
+
for (const node of normalNodes) {
|
|
103
|
+
Object.assign(shader.uniforms, node.uniforms);
|
|
104
|
+
}
|
|
105
|
+
for (const node of emissionNodes) {
|
|
106
|
+
Object.assign(shader.uniforms, node.uniforms);
|
|
107
|
+
}
|
|
108
|
+
let fragmentShader = shader.fragmentShader;
|
|
109
|
+
if (normalNodes.length > 0) {
|
|
110
|
+
const normalExpressions = normalNodes.map((node) => {
|
|
111
|
+
if (node instanceof FXTextureNode) {
|
|
112
|
+
return `normalize(${node.colorExpression}.rgb * 2.0 - 1.0)`;
|
|
113
|
+
}
|
|
114
|
+
return node.normalExpression;
|
|
115
|
+
});
|
|
116
|
+
const blendLines = normalExpressions
|
|
117
|
+
.slice(1)
|
|
118
|
+
.map((expression) => `normal = blendReorientedNormals(normal, ${expression});`);
|
|
119
|
+
const normalCode = [
|
|
120
|
+
"float faceDirection = gl_FrontFacing ? 1.0 : -1.0;",
|
|
121
|
+
`vec3 normal = ${normalExpressions[0]};`,
|
|
122
|
+
...blendLines,
|
|
123
|
+
].join("\n");
|
|
124
|
+
fragmentShader = fragmentShader.replace("#include <normal_fragment_begin>", normalCode);
|
|
125
|
+
}
|
|
126
|
+
if (albedoNodes.length > 0) {
|
|
127
|
+
const combinedExpression = albedoNodes.map((n) => n.colorExpression).join(" * ");
|
|
128
|
+
const discardLine = useAlphaHashing ? "" : "if (diffuseColor.a < 0.0035) discard;";
|
|
129
|
+
fragmentShader = fragmentShader.replace("#include <map_fragment>", `diffuseColor = ${combinedExpression}; ${discardLine}`);
|
|
130
|
+
}
|
|
131
|
+
if (emissionNodes.length > 0) {
|
|
132
|
+
const combinedExpression = emissionNodes.map((n) => n.colorExpression).join(" * ");
|
|
133
|
+
fragmentShader = fragmentShader.replace("#include <emissivemap_fragment>", `totalEmissiveRadiance = (${combinedExpression}).rgb;`);
|
|
134
|
+
}
|
|
135
|
+
shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
|
|
136
|
+
};
|
|
137
|
+
material.customProgramCacheKey = () => `fx-diffuse-${albedoNodes.map((n) => n.cacheKey).join("-") || "none"}-${normalNodes.map((n) => n.cacheKey).join("-") || "none"}-${emissionNodes.map((n) => n.cacheKey).join("-") || "none"}`;
|
|
138
|
+
return material;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
export { buildFXDiffuseMaterial };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { FXColorNode } from "../../nodes/color/FXColorNode";
|
|
2
|
+
import type { FXNormalNode } from "../../nodes/normal/FXNormalNode";
|
|
3
|
+
import type { FXTextureNode } from "../../nodes/texture/FXTextureNode";
|
|
4
|
+
import type { FXMaterialOptions } from "../FXMaterial/FXMaterial";
|
|
5
|
+
import { FXMaterial } from "../FXMaterial/FXMaterial";
|
|
6
|
+
export interface FXDiffuseMaterialOptions extends FXMaterialOptions {
|
|
7
|
+
normalNodes?: (FXTextureNode | FXNormalNode)[];
|
|
8
|
+
emissionNodes?: (FXColorNode | FXTextureNode)[];
|
|
9
|
+
}
|
|
10
|
+
export declare class FXDiffuseMaterial extends FXMaterial {
|
|
11
|
+
private readonly normalNodes;
|
|
12
|
+
private readonly emissionNodes;
|
|
13
|
+
constructor(options?: FXDiffuseMaterialOptions);
|
|
14
|
+
destroy(): void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { FXSphericalNormalNode } from '../../nodes/normal/FXSphericalNormalNode.js';
|
|
2
|
+
import { FXMaterial } from '../FXMaterial/FXMaterial.js';
|
|
3
|
+
import { buildFXDiffuseMaterial } from './FXDiffuseMaterial.Internal.js';
|
|
4
|
+
|
|
5
|
+
class FXDiffuseMaterial extends FXMaterial {
|
|
6
|
+
constructor(options = {}) {
|
|
7
|
+
super(options);
|
|
8
|
+
this._private_normalNodes = options.normalNodes ?? [new FXSphericalNormalNode()];
|
|
9
|
+
this._private_emissionNodes = options.emissionNodes ?? [];
|
|
10
|
+
}
|
|
11
|
+
destroy() {
|
|
12
|
+
super.destroy();
|
|
13
|
+
for (const node of this._private_normalNodes) {
|
|
14
|
+
node.destroy?.();
|
|
15
|
+
}
|
|
16
|
+
for (const node of this._private_emissionNodes) {
|
|
17
|
+
node.destroy?.();
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/** @internal */
|
|
21
|
+
buildThreeMaterial(varyings) {
|
|
22
|
+
return buildFXDiffuseMaterial(varyings, this.blending, this.useAlphaHashing, this.alphaTest, this.albedoNodes, this._private_normalNodes, this._private_emissionNodes);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export { FXDiffuseMaterial };
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { MeshDepthMaterial } from "three";
|
|
2
|
+
import type { FXColorNode } from "../../nodes/color/FXColorNode";
|
|
3
|
+
import type { FXTextureNode } from "../../nodes/texture/FXTextureNode";
|
|
4
|
+
export declare const DEPTH_VERTEX_SHADER = "\n #define PARTICLE_POSITION_X a_builtin[0][0]\n #define PARTICLE_POSITION_Y a_builtin[0][1]\n #define PARTICLE_POSITION_Z a_builtin[0][2]\n\n #define PARTICLE_SCALE_X a_builtin[1][2]\n #define PARTICLE_SCALE_Y a_builtin[1][3]\n\n #define PARTICLE_ROTATION a_builtin[2][1]\n\n attribute mat4 a_builtin;\n varying mat4 p_builtin;\n varying vec2 p_uv;\n varying vec2 vHighPrecisionZW;\n varying float v_viewZ;\n #ifdef USE_ALPHAHASH\n varying vec3 vPosition;\n #endif\n\n void main() {\n p_builtin = a_builtin;\n p_uv = uv;\n #ifdef USE_ALPHAHASH\n vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);\n #endif\n\n vec2 transformedPosition = position.xy;\n transformedPosition.x *= PARTICLE_SCALE_X;\n transformedPosition.y *= PARTICLE_SCALE_Y;\n\n float cosR = cos(PARTICLE_ROTATION);\n float sinR = sin(PARTICLE_ROTATION);\n transformedPosition = vec2(\n transformedPosition.x * cosR - transformedPosition.y * sinR,\n transformedPosition.x * sinR + transformedPosition.y * cosR\n );\n\n vec3 center = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);\n vec4 modelViewCenter = modelViewMatrix * vec4(center, 1.0);\n v_viewZ = modelViewCenter.z;\n modelViewCenter.xy += transformedPosition;\n gl_Position = projectionMatrix * modelViewCenter;\n vHighPrecisionZW = gl_Position.zw;\n }\n";
|
|
5
|
+
export declare function buildDepthMaterial(albedoNodes: readonly (FXColorNode | FXTextureNode)[], depthAlphaTest: number, useDepthAlphaHash: boolean, useSphericalDepth: boolean): MeshDepthMaterial;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { MeshDepthMaterial, DoubleSide, RGBADepthPacking } from 'three';
|
|
2
|
+
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
|
|
4
|
+
const DEPTH_VERTEX_SHADER = `
|
|
5
|
+
#define PARTICLE_POSITION_X a_builtin[0][0]
|
|
6
|
+
#define PARTICLE_POSITION_Y a_builtin[0][1]
|
|
7
|
+
#define PARTICLE_POSITION_Z a_builtin[0][2]
|
|
8
|
+
|
|
9
|
+
#define PARTICLE_SCALE_X a_builtin[1][2]
|
|
10
|
+
#define PARTICLE_SCALE_Y a_builtin[1][3]
|
|
11
|
+
|
|
12
|
+
#define PARTICLE_ROTATION a_builtin[2][1]
|
|
13
|
+
|
|
14
|
+
attribute mat4 a_builtin;
|
|
15
|
+
varying mat4 p_builtin;
|
|
16
|
+
varying vec2 p_uv;
|
|
17
|
+
varying vec2 vHighPrecisionZW;
|
|
18
|
+
varying float v_viewZ;
|
|
19
|
+
#ifdef USE_ALPHAHASH
|
|
20
|
+
varying vec3 vPosition;
|
|
21
|
+
#endif
|
|
22
|
+
|
|
23
|
+
void main() {
|
|
24
|
+
p_builtin = a_builtin;
|
|
25
|
+
p_uv = uv;
|
|
26
|
+
#ifdef USE_ALPHAHASH
|
|
27
|
+
vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);
|
|
28
|
+
#endif
|
|
29
|
+
|
|
30
|
+
vec2 transformedPosition = position.xy;
|
|
31
|
+
transformedPosition.x *= PARTICLE_SCALE_X;
|
|
32
|
+
transformedPosition.y *= PARTICLE_SCALE_Y;
|
|
33
|
+
|
|
34
|
+
float cosR = cos(PARTICLE_ROTATION);
|
|
35
|
+
float sinR = sin(PARTICLE_ROTATION);
|
|
36
|
+
transformedPosition = vec2(
|
|
37
|
+
transformedPosition.x * cosR - transformedPosition.y * sinR,
|
|
38
|
+
transformedPosition.x * sinR + transformedPosition.y * cosR
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
vec3 center = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);
|
|
42
|
+
vec4 modelViewCenter = modelViewMatrix * vec4(center, 1.0);
|
|
43
|
+
v_viewZ = modelViewCenter.z;
|
|
44
|
+
modelViewCenter.xy += transformedPosition;
|
|
45
|
+
gl_Position = projectionMatrix * modelViewCenter;
|
|
46
|
+
vHighPrecisionZW = gl_Position.zw;
|
|
47
|
+
}
|
|
48
|
+
`;
|
|
49
|
+
const DEPTH_SPHERICAL_FRAG_COORD_Z = `
|
|
50
|
+
float _dx = (p_uv.x - 0.5) * PARTICLE_SCALE_X;
|
|
51
|
+
float _dy = (p_uv.y - 0.5) * PARTICLE_SCALE_Y;
|
|
52
|
+
float _r = min(PARTICLE_SCALE_X, PARTICLE_SCALE_Y) * 0.5;
|
|
53
|
+
float _d2 = _dx * _dx + _dy * _dy;
|
|
54
|
+
if (_d2 > _r * _r) discard;
|
|
55
|
+
float _sphereZ = v_viewZ + sqrt(_r * _r - _d2);
|
|
56
|
+
float _pz = projectionMatrix[2][2] * _sphereZ + projectionMatrix[3][2];
|
|
57
|
+
float _pw = projectionMatrix[2][3] * _sphereZ + projectionMatrix[3][3];
|
|
58
|
+
float _ndc_z = _pz / _pw;
|
|
59
|
+
#ifdef USE_REVERSEDEPTHBUF
|
|
60
|
+
float fragCoordZ = _ndc_z;
|
|
61
|
+
#else
|
|
62
|
+
float fragCoordZ = 0.5 * _ndc_z + 0.5;
|
|
63
|
+
#endif
|
|
64
|
+
`;
|
|
65
|
+
function buildDepthMaterial(albedoNodes, depthAlphaTest, useDepthAlphaHash, useSphericalDepth) {
|
|
66
|
+
const depthNodes = albedoNodes.filter((n) => n.affectsDepth);
|
|
67
|
+
const needsFragmentMod = depthNodes.length > 0 || useSphericalDepth;
|
|
68
|
+
const material = new MeshDepthMaterial({
|
|
69
|
+
depthPacking: RGBADepthPacking,
|
|
70
|
+
side: DoubleSide,
|
|
71
|
+
forceSinglePass: true,
|
|
72
|
+
alphaHash: useDepthAlphaHash,
|
|
73
|
+
});
|
|
74
|
+
material.onBeforeCompile = (shader) => {
|
|
75
|
+
shader.vertexShader = DEPTH_VERTEX_SHADER;
|
|
76
|
+
if (!needsFragmentMod) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const fragmentPreamble = [
|
|
80
|
+
PARTICLE_DEFINES,
|
|
81
|
+
"varying mat4 p_builtin;",
|
|
82
|
+
"varying vec2 p_uv;",
|
|
83
|
+
"varying float v_viewZ;",
|
|
84
|
+
useSphericalDepth ? "uniform mat4 projectionMatrix;" : "",
|
|
85
|
+
depthNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
86
|
+
depthNodes.map((n) => n.helperFunctions).join("\n"),
|
|
87
|
+
].join("\n");
|
|
88
|
+
for (const node of depthNodes) {
|
|
89
|
+
Object.assign(shader.uniforms, node.uniforms);
|
|
90
|
+
}
|
|
91
|
+
let fs = shader.fragmentShader;
|
|
92
|
+
if (depthNodes.length > 0) {
|
|
93
|
+
const combinedAlpha = depthNodes.map((n) => n.colorExpression).join(" * ");
|
|
94
|
+
fs = fs.replace("#include <map_fragment>", `diffuseColor.a = (${combinedAlpha}).a;
|
|
95
|
+
if (diffuseColor.a < ${depthAlphaTest.toFixed(4)}) discard;`);
|
|
96
|
+
}
|
|
97
|
+
if (useSphericalDepth) {
|
|
98
|
+
fs = fs.replace(/float fragCoordZ\b[^;]*;/, DEPTH_SPHERICAL_FRAG_COORD_Z);
|
|
99
|
+
}
|
|
100
|
+
shader.fragmentShader = fragmentPreamble + "\n" + fs;
|
|
101
|
+
};
|
|
102
|
+
material.customProgramCacheKey = () => `fx-depth-${useDepthAlphaHash ? "ah" : "at"}-${useSphericalDepth ? "sd" : "flat"}-${depthNodes.map((n) => n.cacheKey).join("-") || "none"}`;
|
|
103
|
+
return material;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export { DEPTH_VERTEX_SHADER, buildDepthMaterial };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Blending } from "three";
|
|
2
|
+
import type { FXColorNode } from "../../nodes/color/FXColorNode";
|
|
3
|
+
import type { FXTextureNode } from "../../nodes/texture/FXTextureNode";
|
|
4
|
+
export interface FXMaterialOptions {
|
|
5
|
+
albedoNodes?: (FXColorNode | FXTextureNode)[];
|
|
6
|
+
blending?: Blending;
|
|
7
|
+
useAlphaHashing?: boolean;
|
|
8
|
+
alphaTest?: number;
|
|
9
|
+
depthAlphaTest?: number;
|
|
10
|
+
useDepthAlphaHash?: boolean;
|
|
11
|
+
useSphericalDepth?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare abstract class FXMaterial {
|
|
14
|
+
private readonly depthAlphaTest;
|
|
15
|
+
private readonly useDepthAlphaHash;
|
|
16
|
+
private readonly useSphericalDepth;
|
|
17
|
+
constructor(options?: FXMaterialOptions);
|
|
18
|
+
destroy(): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NormalBlending } from 'three';
|
|
2
|
+
import { buildDepthMaterial } from './FXMaterial.Internal.js';
|
|
3
|
+
|
|
4
|
+
class FXMaterial {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
this.albedoNodes = options.albedoNodes ?? [];
|
|
7
|
+
this.blending = options.blending ?? NormalBlending;
|
|
8
|
+
this.useAlphaHashing = options.useAlphaHashing ?? false;
|
|
9
|
+
this.alphaTest = options.alphaTest ?? 1 / 255;
|
|
10
|
+
this._private_depthAlphaTest = options.depthAlphaTest ?? 0.5;
|
|
11
|
+
this._private_useDepthAlphaHash = options.useDepthAlphaHash ?? false;
|
|
12
|
+
this._private_useSphericalDepth = options.useSphericalDepth ?? true;
|
|
13
|
+
}
|
|
14
|
+
destroy() {
|
|
15
|
+
for (const node of this.albedoNodes) {
|
|
16
|
+
node.destroy?.();
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/** @internal */
|
|
20
|
+
buildDepthMaterial() {
|
|
21
|
+
return buildDepthMaterial(this.albedoNodes, this._private_depthAlphaTest, this._private_useDepthAlphaHash, this._private_useSphericalDepth);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export { FXMaterial };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Blending } from "three";
|
|
2
|
+
import { MeshBasicMaterial } from "three";
|
|
3
|
+
import type { GLTypeInfo } from "../../instancedParticle/shared";
|
|
4
|
+
import type { FXColorNode } from "../../nodes/color/FXColorNode";
|
|
5
|
+
import type { FXTextureNode } from "../../nodes/texture/FXTextureNode";
|
|
6
|
+
export declare function buildFXUnlitMaterial(varyings: Record<string, GLTypeInfo>, blending: Blending, useAlphaHashing: boolean, alphaTest: number, albedoNodes: readonly (FXColorNode | FXTextureNode)[]): MeshBasicMaterial;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { MeshBasicMaterial, DoubleSide } from 'three';
|
|
2
|
+
import { PARTICLE_DEFINES } from '../../miscellaneous/miscellaneous.js';
|
|
3
|
+
|
|
4
|
+
function buildFXUnlitMaterial(varyings, blending, useAlphaHashing, alphaTest, albedoNodes) {
|
|
5
|
+
const attributeDeclarations = [];
|
|
6
|
+
const varyingDeclarations = [];
|
|
7
|
+
const vertexAssignments = [];
|
|
8
|
+
for (const name in varyings) {
|
|
9
|
+
const { glslTypeName } = varyings[name];
|
|
10
|
+
attributeDeclarations.push(`attribute ${glslTypeName} a_${name};`);
|
|
11
|
+
varyingDeclarations.push(`varying ${glslTypeName} p_${name};`);
|
|
12
|
+
vertexAssignments.push(`p_${name} = a_${name};`);
|
|
13
|
+
}
|
|
14
|
+
const material = new MeshBasicMaterial({
|
|
15
|
+
transparent: !useAlphaHashing,
|
|
16
|
+
depthWrite: useAlphaHashing,
|
|
17
|
+
depthTest: true,
|
|
18
|
+
alphaHash: useAlphaHashing,
|
|
19
|
+
blending,
|
|
20
|
+
side: DoubleSide,
|
|
21
|
+
forceSinglePass: true,
|
|
22
|
+
alphaTest,
|
|
23
|
+
});
|
|
24
|
+
material.onBeforeCompile = (shader) => {
|
|
25
|
+
const vertexPreamble = [
|
|
26
|
+
PARTICLE_DEFINES,
|
|
27
|
+
attributeDeclarations.join("\n"),
|
|
28
|
+
"varying vec2 p_uv;",
|
|
29
|
+
varyingDeclarations.join("\n"),
|
|
30
|
+
].join("\n");
|
|
31
|
+
shader.vertexShader =
|
|
32
|
+
vertexPreamble +
|
|
33
|
+
"\n" +
|
|
34
|
+
shader.vertexShader
|
|
35
|
+
.replace("#include <begin_vertex>", [
|
|
36
|
+
"p_uv = uv;",
|
|
37
|
+
vertexAssignments.join("\n"),
|
|
38
|
+
"vec3 transformed = position;",
|
|
39
|
+
"#ifdef USE_ALPHAHASH",
|
|
40
|
+
" vPosition = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z) + vec3(position.xy, 0.0);",
|
|
41
|
+
"#endif",
|
|
42
|
+
].join("\n"))
|
|
43
|
+
.replace("#include <project_vertex>", `
|
|
44
|
+
// Scale and rotate the billboard offset in camera space
|
|
45
|
+
vec2 billboardOffset = position.xy;
|
|
46
|
+
billboardOffset.x *= PARTICLE_SCALE_X;
|
|
47
|
+
billboardOffset.y *= PARTICLE_SCALE_Y;
|
|
48
|
+
|
|
49
|
+
float cosRotation = cos(PARTICLE_ROTATION);
|
|
50
|
+
float sinRotation = sin(PARTICLE_ROTATION);
|
|
51
|
+
billboardOffset = vec2(
|
|
52
|
+
billboardOffset.x * cosRotation - billboardOffset.y * sinRotation,
|
|
53
|
+
billboardOffset.x * sinRotation + billboardOffset.y * cosRotation
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
// Move particle center to view space, then apply the billboard offset
|
|
57
|
+
vec3 particleCenter = vec3(PARTICLE_POSITION_X, PARTICLE_POSITION_Y, PARTICLE_POSITION_Z);
|
|
58
|
+
vec4 mvPosition = modelViewMatrix * vec4(particleCenter, 1.0);
|
|
59
|
+
mvPosition.xy += billboardOffset;
|
|
60
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
61
|
+
`);
|
|
62
|
+
const fragmentPreamble = [
|
|
63
|
+
PARTICLE_DEFINES,
|
|
64
|
+
"varying vec2 p_uv;",
|
|
65
|
+
varyingDeclarations.join("\n"),
|
|
66
|
+
albedoNodes.flatMap((n) => n.uniformDeclarations).join("\n"),
|
|
67
|
+
albedoNodes.map((n) => n.helperFunctions).join("\n"),
|
|
68
|
+
].join("\n");
|
|
69
|
+
for (const node of albedoNodes) {
|
|
70
|
+
Object.assign(shader.uniforms, node.uniforms);
|
|
71
|
+
}
|
|
72
|
+
let fragmentShader = shader.fragmentShader;
|
|
73
|
+
if (albedoNodes.length > 0) {
|
|
74
|
+
const combinedExpression = albedoNodes.map((n) => n.colorExpression).join(" * ");
|
|
75
|
+
const discardLine = useAlphaHashing ? "" : "if (diffuseColor.a < 0.0035) discard;";
|
|
76
|
+
fragmentShader = fragmentShader.replace("#include <map_fragment>", `diffuseColor = ${combinedExpression}; ${discardLine}`);
|
|
77
|
+
}
|
|
78
|
+
shader.fragmentShader = fragmentPreamble + "\n" + fragmentShader;
|
|
79
|
+
};
|
|
80
|
+
material.customProgramCacheKey = () => `fx-unlit-${albedoNodes.map((n) => n.cacheKey).join("-") || "none"}`;
|
|
81
|
+
return material;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { buildFXUnlitMaterial };
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { FXMaterialOptions } from "../FXMaterial/FXMaterial";
|
|
2
|
+
import { FXMaterial } from "../FXMaterial/FXMaterial";
|
|
3
|
+
export type FXUnlitMaterialOptions = FXMaterialOptions;
|
|
4
|
+
export declare class FXUnlitMaterial extends FXMaterial {
|
|
5
|
+
constructor(options?: FXUnlitMaterialOptions);
|
|
6
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FXMaterial } from '../FXMaterial/FXMaterial.js';
|
|
2
|
+
import { buildFXUnlitMaterial } from './FXUnlitMaterial.Internal.js';
|
|
3
|
+
|
|
4
|
+
class FXUnlitMaterial extends FXMaterial {
|
|
5
|
+
constructor(options = {}) {
|
|
6
|
+
super(options);
|
|
7
|
+
}
|
|
8
|
+
/** @internal */
|
|
9
|
+
buildThreeMaterial(varyings) {
|
|
10
|
+
return buildFXUnlitMaterial(varyings, this.blending, this.useAlphaHashing, this.alphaTest, this.albedoNodes);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { FXUnlitMaterial };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MathUtils, Color, Vector4 } from 'three';
|
|
2
|
-
import { assertValidNumber } from '../
|
|
3
|
-
import { srgbToLinear, linearToSRGB } from '../
|
|
2
|
+
import { assertValidNumber } from '../asserts.js';
|
|
3
|
+
import { srgbToLinear, linearToSRGB } from '../math.js';
|
|
4
4
|
import { COLORS } from './FXColor.Internal.js';
|
|
5
5
|
|
|
6
6
|
/** RGBA color with RGB, HSL, hex, and named color support */
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Vector3Tuple } from "three";
|
|
2
2
|
import { DataTexture } from "three";
|
|
3
|
-
import type { FXColor } from "../color/FXColor";
|
|
4
3
|
import type { Vector3Like } from "../miscellaneous/math";
|
|
5
4
|
import type { FXTextureConfig } from "../miscellaneous/texture/FXTextureView.Internal";
|
|
5
|
+
import type { FXColor } from "./color/FXColor";
|
|
6
6
|
export interface FXRange {
|
|
7
7
|
min: number;
|
|
8
8
|
max: number;
|
|
@@ -30,3 +30,4 @@ export declare const BUILTIN_OFFSET_AGE = 12;
|
|
|
30
30
|
export declare const BUILTIN_OFFSET_RANDOM_A = 13;
|
|
31
31
|
export declare const BUILTIN_OFFSET_RANDOM_B = 14;
|
|
32
32
|
export declare const BUILTIN_OFFSET_RANDOM_C = 15;
|
|
33
|
+
export declare const PARTICLE_DEFINES = "\n #define PARTICLE_POSITION_X p_builtin[0][0]\n #define PARTICLE_POSITION_Y p_builtin[0][1]\n #define PARTICLE_POSITION_Z p_builtin[0][2]\n\n #define PARTICLE_VELOCITY_X p_builtin[0][3]\n #define PARTICLE_VELOCITY_Y p_builtin[1][0]\n #define PARTICLE_VELOCITY_Z p_builtin[1][1]\n\n #define PARTICLE_SCALE_X p_builtin[1][2]\n #define PARTICLE_SCALE_Y p_builtin[1][3]\n #define PARTICLE_SCALE_Z p_builtin[2][0]\n\n #define PARTICLE_ROTATION p_builtin[2][1]\n #define PARTICLE_TORQUE p_builtin[2][2]\n\n #define PARTICLE_LIFETIME p_builtin[2][3]\n #define PARTICLE_AGE p_builtin[3][0]\n\n #define PARTICLE_RANDOM_A p_builtin[3][1]\n #define PARTICLE_RANDOM_B p_builtin[3][2]\n #define PARTICLE_RANDOM_C p_builtin[3][3]\n";
|
|
@@ -58,5 +58,28 @@ const BUILTIN_OFFSET_AGE = 12;
|
|
|
58
58
|
const BUILTIN_OFFSET_RANDOM_A = 13;
|
|
59
59
|
const BUILTIN_OFFSET_RANDOM_B = 14;
|
|
60
60
|
const BUILTIN_OFFSET_RANDOM_C = 15;
|
|
61
|
+
const PARTICLE_DEFINES = `
|
|
62
|
+
#define PARTICLE_POSITION_X p_builtin[0][0]
|
|
63
|
+
#define PARTICLE_POSITION_Y p_builtin[0][1]
|
|
64
|
+
#define PARTICLE_POSITION_Z p_builtin[0][2]
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
#define PARTICLE_VELOCITY_X p_builtin[0][3]
|
|
67
|
+
#define PARTICLE_VELOCITY_Y p_builtin[1][0]
|
|
68
|
+
#define PARTICLE_VELOCITY_Z p_builtin[1][1]
|
|
69
|
+
|
|
70
|
+
#define PARTICLE_SCALE_X p_builtin[1][2]
|
|
71
|
+
#define PARTICLE_SCALE_Y p_builtin[1][3]
|
|
72
|
+
#define PARTICLE_SCALE_Z p_builtin[2][0]
|
|
73
|
+
|
|
74
|
+
#define PARTICLE_ROTATION p_builtin[2][1]
|
|
75
|
+
#define PARTICLE_TORQUE p_builtin[2][2]
|
|
76
|
+
|
|
77
|
+
#define PARTICLE_LIFETIME p_builtin[2][3]
|
|
78
|
+
#define PARTICLE_AGE p_builtin[3][0]
|
|
79
|
+
|
|
80
|
+
#define PARTICLE_RANDOM_A p_builtin[3][1]
|
|
81
|
+
#define PARTICLE_RANDOM_B p_builtin[3][2]
|
|
82
|
+
#define PARTICLE_RANDOM_C p_builtin[3][3]
|
|
83
|
+
`;
|
|
84
|
+
|
|
85
|
+
export { BUILTIN_OFFSET_AGE, BUILTIN_OFFSET_LIFETIME, BUILTIN_OFFSET_POSITION_X, BUILTIN_OFFSET_POSITION_Y, BUILTIN_OFFSET_POSITION_Z, BUILTIN_OFFSET_RANDOM_A, BUILTIN_OFFSET_RANDOM_B, BUILTIN_OFFSET_RANDOM_C, BUILTIN_OFFSET_ROTATION, BUILTIN_OFFSET_SCALE_X, BUILTIN_OFFSET_SCALE_Y, BUILTIN_OFFSET_TORQUE, BUILTIN_OFFSET_VELOCITY_X, BUILTIN_OFFSET_VELOCITY_Y, BUILTIN_OFFSET_VELOCITY_Z, PARTICLE_DEFINES, buildGradientTexture, resolveAspect, resolveFXRangeConfig, resolveFXVector3Config };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { buildGradientTexture } from '../../miscellaneous/miscellaneous.js';
|
|
2
|
+
import { checkSRGBSupport } from '../../miscellaneous/webglCapabilities.js';
|
|
3
|
+
import { FXColorNode } from './FXColorNode.js';
|
|
4
|
+
|
|
5
|
+
class FXColorOverLifeNode extends FXColorNode {
|
|
6
|
+
constructor(colors) {
|
|
7
|
+
if (colors.length === 0) {
|
|
8
|
+
throw new Error("FXColorOverLifeNode: colors array cannot be empty");
|
|
9
|
+
}
|
|
10
|
+
super();
|
|
11
|
+
this._private_gradientTexture = buildGradientTexture(colors);
|
|
12
|
+
this.affectsDepth = true;
|
|
13
|
+
this.cacheKey = "color-over-life";
|
|
14
|
+
this.uniformDeclarations = ["uniform sampler2D u_ColorOverLife;"];
|
|
15
|
+
this.uniforms = {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
17
|
+
u_ColorOverLife: { value: this._private_gradientTexture },
|
|
18
|
+
};
|
|
19
|
+
this.helperFunctions = `
|
|
20
|
+
vec4 sampleColorOverLife(sampler2D gradientTexture) {
|
|
21
|
+
// Normalized particle age [0, 1] maps to the gradient texture's x-axis
|
|
22
|
+
float lifetimeRatio = clamp(PARTICLE_AGE / PARTICLE_LIFETIME, 0.0, 1.0);
|
|
23
|
+
vec4 color = texture2D(gradientTexture, vec2(lifetimeRatio, 0.5));
|
|
24
|
+
|
|
25
|
+
// Apply gamma correction when sRGB textures are not natively supported
|
|
26
|
+
${checkSRGBSupport() ? "" : "color = vec4(pow(color.rgb, vec3(2.2)), color.a);"}
|
|
27
|
+
|
|
28
|
+
return color;
|
|
29
|
+
}
|
|
30
|
+
`;
|
|
31
|
+
this.colorExpression = "sampleColorOverLife(u_ColorOverLife)";
|
|
32
|
+
}
|
|
33
|
+
destroy() {
|
|
34
|
+
this._private_gradientTexture.dispose();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { FXColorOverLifeNode };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { FXNormalNode } from './FXNormalNode.js';
|
|
2
|
+
|
|
3
|
+
class FXFlatNormalNode extends FXNormalNode {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.cacheKey = "flat";
|
|
7
|
+
this.uniformDeclarations = [];
|
|
8
|
+
this.uniforms = {};
|
|
9
|
+
this.helperFunctions = "";
|
|
10
|
+
this.normalExpression = "vec3(0.0, 0.0, 1.0)";
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export { FXFlatNormalNode };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { FXNormalNode } from './FXNormalNode.js';
|
|
2
|
+
|
|
3
|
+
class FXSphericalNormalNode extends FXNormalNode {
|
|
4
|
+
constructor() {
|
|
5
|
+
super();
|
|
6
|
+
this.cacheKey = "spherical";
|
|
7
|
+
this.uniformDeclarations = [];
|
|
8
|
+
this.uniforms = {};
|
|
9
|
+
this.helperFunctions = `
|
|
10
|
+
vec3 fxComputeSphericalNormal(vec2 uv) {
|
|
11
|
+
vec2 centeredUV = uv * 2.0 - 1.0;
|
|
12
|
+
return normalize(vec3(centeredUV, sqrt(max(0.0, 1.0 - dot(centeredUV, centeredUV)))));
|
|
13
|
+
}
|
|
14
|
+
`;
|
|
15
|
+
this.normalExpression = "fxComputeSphericalNormal(p_uv)";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { FXSphericalNormalNode };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Texture } from "three";
|
|
2
|
+
import { FXTextureNode } from "./FXTextureNode";
|
|
3
|
+
export interface FXAnimatedTextureNodeOptions {
|
|
4
|
+
texture: Texture;
|
|
5
|
+
columns: number;
|
|
6
|
+
rows: number;
|
|
7
|
+
interpolate?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare class FXAnimatedTextureNode extends FXTextureNode {
|
|
10
|
+
constructor({ texture, columns, rows, interpolate }: FXAnimatedTextureNodeOptions);
|
|
11
|
+
}
|