sparcoon 0.9.1 → 0.9.2
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/README.md +11 -6
- package/dist/artifact/FXArtifact.d.ts +51 -3
- package/dist/behaviorTransformFeedbackLayout.d.ts +24 -0
- package/dist/behaviorTransformFeedbackLayout.js +24 -0
- package/dist/coreLayout.d.ts +9 -5
- package/dist/coreLayout.js +17 -4
- package/dist/editor/index.d.ts +1 -0
- package/dist/editor/index.js +1 -1
- package/dist/effect/FXEffect.d.ts +2 -0
- package/dist/effect/FXEffect.js +30 -10
- package/dist/effect/FXEffectSpec.d.ts +21 -4
- package/dist/emitter/FXApplyRenderArtifactOptions.d.ts +15 -0
- package/dist/emitter/FXApplyRenderArtifactOptions.js +1 -0
- package/dist/emitter/FXEmitter.d.ts +32 -13
- package/dist/emitter/FXEmitter.js +107 -148
- package/dist/emitter/FXEmitterDriver.Internal.d.ts +26 -0
- package/dist/emitter/FXEmitterDriver.Internal.js +70 -0
- package/dist/emitter/FXFromArtifactsOptions.d.ts +12 -1
- package/dist/emitter/FXGPUEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXGPUEmitterDriver.Internal.js +137 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.d.ts +1 -0
- package/dist/emitter/FXJSEmitterDriver.Internal.js +133 -0
- package/dist/emitter/FXSimulationHolder.js +7 -5
- package/dist/emitter/FXTransformFeedbackSimulationHolder.d.ts +1 -0
- package/dist/emitter/FXTransformFeedbackSimulationHolder.js +181 -0
- package/dist/index.d.ts +4 -3
- package/dist/index.js +3 -2
- package/dist/instancedParticle/FXInstancedParticle.js +28 -1
- package/dist/instancedParticle/FXTransformFeedbackParticle.d.ts +1 -0
- package/dist/instancedParticle/FXTransformFeedbackParticle.js +169 -0
- package/dist/miscellaneous/fxMath.d.ts +18 -11
- package/dist/miscellaneous/fxMath.js +89 -209
- package/dist/render/FXArtifactMaterial.Internal.js +66 -40
- package/dist/render/FXArtifactMaterial.js +1 -1
- package/dist/render/FXMesh.js +2 -4
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `
|
|
2
|
+
* CPU-side twins of the compiler's math/noise builtins (`fxMix`, `fxNoise3`, ...). A behavior
|
|
3
3
|
* kernel's authored `spawn`/`update` body calls these by name - the editor's compiler prints those
|
|
4
4
|
* exact calls - so every exported name here is a frozen contract with already-compiled kernels,
|
|
5
5
|
* like the `a_`/`p_` buffer prefixes. Never rename one without updating the compiler side in
|
|
@@ -18,28 +18,34 @@ export function fxSmoothstep(edge0, edge1, x) {
|
|
|
18
18
|
const t = Math.min(Math.max((x - edge0) / (edge1 - edge0), 0.0), 1.0);
|
|
19
19
|
return t * t * (3.0 - 2.0 * t);
|
|
20
20
|
}
|
|
21
|
-
export function fxHash(n) {
|
|
22
|
-
const s = Math.sin(n * 127.1) * 43758.5453123;
|
|
23
|
-
return s - Math.floor(s);
|
|
24
|
-
}
|
|
25
|
-
/** Cheap deterministic 1D value-noise (smoothed hash interpolation), range [-1, 1]. */
|
|
26
|
-
export function fxValueNoise(x) {
|
|
27
|
-
const i = Math.floor(x);
|
|
28
|
-
const fractional = x - i;
|
|
29
|
-
const u = fractional * fractional * (3 - 2 * fractional);
|
|
30
|
-
return (fxHash(i) * (1 - u) + fxHash(i + 1) * u) * 2 - 1;
|
|
31
|
-
}
|
|
32
21
|
// Mirrors FBM_MAX_OCTAVES in the editor engine: clamps a runaway octave count (a
|
|
33
22
|
// value parameter rebinds with no recompile/sanity gate) so it cannot blow up the update kernel.
|
|
34
23
|
const FBM_MAX_OCTAVES = 8;
|
|
35
|
-
/** Fractal Brownian motion over {@link
|
|
24
|
+
/** Fractal Brownian motion over {@link fxNoise1} (gain 0.5, lacunarity 2). */
|
|
36
25
|
export function fxFbm(x, octaves) {
|
|
37
26
|
const octaveCount = Math.min(Math.max(Math.floor(octaves), 0), FBM_MAX_OCTAVES);
|
|
38
27
|
let sum = 0;
|
|
39
28
|
let amplitude = 1;
|
|
40
29
|
let frequency = 1;
|
|
41
30
|
for (let i = 0; i < octaveCount; i += 1) {
|
|
42
|
-
sum +=
|
|
31
|
+
sum += fxNoise1(x * frequency) * amplitude;
|
|
32
|
+
amplitude *= 0.5;
|
|
33
|
+
frequency *= 2;
|
|
34
|
+
}
|
|
35
|
+
return sum;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Fractal Brownian motion over the full {@link fxNoise3} domain (gain 0.5, lacunarity 2) - unlike
|
|
39
|
+
* {@link fxFbm}, which walks a single scalar axis, this samples all three input components
|
|
40
|
+
* together so the field varies with the whole position, not just one of its axes.
|
|
41
|
+
*/
|
|
42
|
+
export function fxFbm3(x, y, z, octaves) {
|
|
43
|
+
const octaveCount = Math.min(Math.max(Math.floor(octaves), 0), FBM_MAX_OCTAVES);
|
|
44
|
+
let sum = 0;
|
|
45
|
+
let amplitude = 1;
|
|
46
|
+
let frequency = 1;
|
|
47
|
+
for (let i = 0; i < octaveCount; i += 1) {
|
|
48
|
+
sum += fxNoise3(x * frequency, y * frequency, z * frequency) * amplitude;
|
|
43
49
|
amplitude *= 0.5;
|
|
44
50
|
frequency *= 2;
|
|
45
51
|
}
|
|
@@ -57,205 +63,79 @@ export function fxSampleLut(lut, t) {
|
|
|
57
63
|
const upperValue = lut[i + 1 < length ? i + 1 : last];
|
|
58
64
|
return lowerValue + (upperValue - lowerValue) * fraction;
|
|
59
65
|
}
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Integer-hash sub-primitive, private to `fxNoise1/2/3` - Ken Perlin's classic `IntNoise` bit
|
|
68
|
+
* scramble. `Math.imul` plus `| 0` truncation at each step forces genuine 32-bit wraparound so
|
|
69
|
+
* this tracks the compiler's GLSL `int` version bit-for-bit (see the `noise` case in the editor's
|
|
70
|
+
* `FXFunctions.Internal.ts` standard-tier helper).
|
|
71
|
+
*/
|
|
72
|
+
function fxIntHash1(n) {
|
|
73
|
+
const shifted = (n << 13) | 0;
|
|
74
|
+
const mixed = (shifted ^ n) | 0;
|
|
75
|
+
const squared = Math.imul(mixed, mixed);
|
|
76
|
+
const step1 = (Math.imul(squared, 15731) + Math.imul(mixed, 789221)) | 0;
|
|
77
|
+
const step2 = (Math.imul(step1, step1) + 1376312589) | 0;
|
|
78
|
+
return (step2 & 0x00ffffff) / 16777216.0;
|
|
63
79
|
}
|
|
64
|
-
function
|
|
65
|
-
return
|
|
80
|
+
function fxIntHash2(ix, iy) {
|
|
81
|
+
return fxIntHash1((ix + Math.imul(iy, 57)) | 0);
|
|
66
82
|
}
|
|
67
|
-
function
|
|
68
|
-
return
|
|
83
|
+
function fxIntHash3(ix, iy, iz) {
|
|
84
|
+
return fxIntHash1((ix + Math.imul(iy, 57) + Math.imul(iz, 113)) | 0);
|
|
69
85
|
}
|
|
70
86
|
/**
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
* it; the arithmetic tracks the GLSL version op-for-op. Output is roughly `[-1, 1]`.
|
|
87
|
+
* Cheap deterministic 1D value-noise (smoothed integer-hash interpolation), range [-1, 1]. The
|
|
88
|
+
* CPU twin of the compiler's GLSL `noise(float)` standard-tier form - see {@link fxIntHash1}.
|
|
74
89
|
*/
|
|
75
|
-
export function
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
const s = (vx + vy) * cy;
|
|
81
|
-
let ix = Math.floor(vx + s);
|
|
82
|
-
let iy = Math.floor(vy + s);
|
|
83
|
-
const t = (ix + iy) * cx;
|
|
84
|
-
const x0x = vx - ix + t;
|
|
85
|
-
const x0y = vy - iy + t;
|
|
86
|
-
let i1x;
|
|
87
|
-
let i1y;
|
|
88
|
-
if (x0x > x0y) {
|
|
89
|
-
i1x = 1.0;
|
|
90
|
-
i1y = 0.0;
|
|
91
|
-
}
|
|
92
|
-
else {
|
|
93
|
-
i1x = 0.0;
|
|
94
|
-
i1y = 1.0;
|
|
95
|
-
}
|
|
96
|
-
const x1x = x0x - i1x + cx;
|
|
97
|
-
const x1y = x0y - i1y + cx;
|
|
98
|
-
const x2x = x0x + cz;
|
|
99
|
-
const x2y = x0y + cz;
|
|
100
|
-
ix = fxsnMod289(ix);
|
|
101
|
-
iy = fxsnMod289(iy);
|
|
102
|
-
const p0 = fxsnPermute(fxsnPermute(iy + 0.0) + ix + 0.0);
|
|
103
|
-
const p1 = fxsnPermute(fxsnPermute(iy + i1y) + ix + i1x);
|
|
104
|
-
const p2 = fxsnPermute(fxsnPermute(iy + 1.0) + ix + 1.0);
|
|
105
|
-
let m0 = Math.max(0.5 - (x0x * x0x + x0y * x0y), 0.0);
|
|
106
|
-
let m1 = Math.max(0.5 - (x1x * x1x + x1y * x1y), 0.0);
|
|
107
|
-
let m2 = Math.max(0.5 - (x2x * x2x + x2y * x2y), 0.0);
|
|
108
|
-
m0 = m0 * m0;
|
|
109
|
-
m0 = m0 * m0;
|
|
110
|
-
m1 = m1 * m1;
|
|
111
|
-
m1 = m1 * m1;
|
|
112
|
-
m2 = m2 * m2;
|
|
113
|
-
m2 = m2 * m2;
|
|
114
|
-
const gx0 = 2.0 * fxsnFract(p0 * cw) - 1.0;
|
|
115
|
-
const gx1 = 2.0 * fxsnFract(p1 * cw) - 1.0;
|
|
116
|
-
const gx2 = 2.0 * fxsnFract(p2 * cw) - 1.0;
|
|
117
|
-
const h0 = Math.abs(gx0) - 0.5;
|
|
118
|
-
const h1 = Math.abs(gx1) - 0.5;
|
|
119
|
-
const h2 = Math.abs(gx2) - 0.5;
|
|
120
|
-
const ox0 = Math.floor(gx0 + 0.5);
|
|
121
|
-
const ox1 = Math.floor(gx1 + 0.5);
|
|
122
|
-
const ox2 = Math.floor(gx2 + 0.5);
|
|
123
|
-
const a0 = gx0 - ox0;
|
|
124
|
-
const a1 = gx1 - ox1;
|
|
125
|
-
const a2 = gx2 - ox2;
|
|
126
|
-
m0 *= 1.79284291400159 - 0.85373472095314 * (a0 * a0 + h0 * h0);
|
|
127
|
-
m1 *= 1.79284291400159 - 0.85373472095314 * (a1 * a1 + h1 * h1);
|
|
128
|
-
m2 *= 1.79284291400159 - 0.85373472095314 * (a2 * a2 + h2 * h2);
|
|
129
|
-
const g0 = a0 * x0x + h0 * x0y;
|
|
130
|
-
const g1 = a1 * x1x + h1 * x1y;
|
|
131
|
-
const g2 = a2 * x2x + h2 * x2y;
|
|
132
|
-
return 130.0 * (m0 * g0 + m1 * g1 + m2 * g2);
|
|
90
|
+
export function fxNoise1(x) {
|
|
91
|
+
const i = Math.floor(x);
|
|
92
|
+
const f = x - i;
|
|
93
|
+
const u = f * f * (3 - 2 * f);
|
|
94
|
+
return (fxIntHash1(i) * (1 - u) + fxIntHash1(i + 1) * u) * 2 - 1;
|
|
133
95
|
}
|
|
134
|
-
/**
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
145
|
-
const
|
|
146
|
-
const
|
|
147
|
-
const
|
|
148
|
-
const
|
|
149
|
-
const
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
const
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
const
|
|
165
|
-
const
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
const
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const nsy = oneOverSeven * 0.5 - 1.0;
|
|
180
|
-
const nsz = oneOverSeven;
|
|
181
|
-
const xx = [0.0, 0.0, 0.0, 0.0];
|
|
182
|
-
const yy = [0.0, 0.0, 0.0, 0.0];
|
|
183
|
-
const hh = [0.0, 0.0, 0.0, 0.0];
|
|
184
|
-
for (let k = 0; k < 4; k += 1) {
|
|
185
|
-
const j = p[k] - 49.0 * Math.floor(p[k] * nsz * nsz);
|
|
186
|
-
const xf = Math.floor(j * nsz);
|
|
187
|
-
const yf = Math.floor(j - 7.0 * xf);
|
|
188
|
-
xx[k] = xf * nsx + nsy;
|
|
189
|
-
yy[k] = yf * nsx + nsy;
|
|
190
|
-
hh[k] = 1.0 - Math.abs(xx[k]) - Math.abs(yy[k]);
|
|
191
|
-
}
|
|
192
|
-
const b0 = [xx[0], xx[1], yy[0], yy[1]];
|
|
193
|
-
const b1 = [xx[2], xx[3], yy[2], yy[3]];
|
|
194
|
-
const s0 = [
|
|
195
|
-
Math.floor(b0[0]) * 2.0 + 1.0,
|
|
196
|
-
Math.floor(b0[1]) * 2.0 + 1.0,
|
|
197
|
-
Math.floor(b0[2]) * 2.0 + 1.0,
|
|
198
|
-
Math.floor(b0[3]) * 2.0 + 1.0,
|
|
199
|
-
];
|
|
200
|
-
const s1 = [
|
|
201
|
-
Math.floor(b1[0]) * 2.0 + 1.0,
|
|
202
|
-
Math.floor(b1[1]) * 2.0 + 1.0,
|
|
203
|
-
Math.floor(b1[2]) * 2.0 + 1.0,
|
|
204
|
-
Math.floor(b1[3]) * 2.0 + 1.0,
|
|
205
|
-
];
|
|
206
|
-
const sh = [
|
|
207
|
-
-(hh[0] <= 0.0 ? 1.0 : 0.0),
|
|
208
|
-
-(hh[1] <= 0.0 ? 1.0 : 0.0),
|
|
209
|
-
-(hh[2] <= 0.0 ? 1.0 : 0.0),
|
|
210
|
-
-(hh[3] <= 0.0 ? 1.0 : 0.0),
|
|
211
|
-
];
|
|
212
|
-
const a00 = b0[0] + s0[0] * sh[0];
|
|
213
|
-
const a01 = b0[2] + s0[2] * sh[0];
|
|
214
|
-
const a02 = b0[1] + s0[1] * sh[1];
|
|
215
|
-
const a03 = b0[3] + s0[3] * sh[1];
|
|
216
|
-
const a10 = b1[0] + s1[0] * sh[2];
|
|
217
|
-
const a11 = b1[2] + s1[2] * sh[2];
|
|
218
|
-
const a12 = b1[1] + s1[1] * sh[3];
|
|
219
|
-
const a13 = b1[3] + s1[3] * sh[3];
|
|
220
|
-
let p0x = a00;
|
|
221
|
-
let p0y = a01;
|
|
222
|
-
let p0z = hh[0];
|
|
223
|
-
let p1x = a02;
|
|
224
|
-
let p1y = a03;
|
|
225
|
-
let p1z = hh[1];
|
|
226
|
-
let p2x = a10;
|
|
227
|
-
let p2y = a11;
|
|
228
|
-
let p2z = hh[2];
|
|
229
|
-
let p3x = a12;
|
|
230
|
-
let p3y = a13;
|
|
231
|
-
let p3z = hh[3];
|
|
232
|
-
const nrm0 = 1.79284291400159 - 0.85373472095314 * (p0x * p0x + p0y * p0y + p0z * p0z);
|
|
233
|
-
const nrm1 = 1.79284291400159 - 0.85373472095314 * (p1x * p1x + p1y * p1y + p1z * p1z);
|
|
234
|
-
const nrm2 = 1.79284291400159 - 0.85373472095314 * (p2x * p2x + p2y * p2y + p2z * p2z);
|
|
235
|
-
const nrm3 = 1.79284291400159 - 0.85373472095314 * (p3x * p3x + p3y * p3y + p3z * p3z);
|
|
236
|
-
p0x *= nrm0;
|
|
237
|
-
p0y *= nrm0;
|
|
238
|
-
p0z *= nrm0;
|
|
239
|
-
p1x *= nrm1;
|
|
240
|
-
p1y *= nrm1;
|
|
241
|
-
p1z *= nrm1;
|
|
242
|
-
p2x *= nrm2;
|
|
243
|
-
p2y *= nrm2;
|
|
244
|
-
p2z *= nrm2;
|
|
245
|
-
p3x *= nrm3;
|
|
246
|
-
p3y *= nrm3;
|
|
247
|
-
p3z *= nrm3;
|
|
248
|
-
let m0 = Math.max(0.6 - (x0x * x0x + x0y * x0y + x0z * x0z), 0.0);
|
|
249
|
-
let m1 = Math.max(0.6 - (x1x * x1x + x1y * x1y + x1z * x1z), 0.0);
|
|
250
|
-
let m2 = Math.max(0.6 - (x2x * x2x + x2y * x2y + x2z * x2z), 0.0);
|
|
251
|
-
let m3 = Math.max(0.6 - (x3x * x3x + x3y * x3y + x3z * x3z), 0.0);
|
|
252
|
-
m0 = m0 * m0;
|
|
253
|
-
m1 = m1 * m1;
|
|
254
|
-
m2 = m2 * m2;
|
|
255
|
-
m3 = m3 * m3;
|
|
256
|
-
const dp0 = p0x * x0x + p0y * x0y + p0z * x0z;
|
|
257
|
-
const dp1 = p1x * x1x + p1y * x1y + p1z * x1z;
|
|
258
|
-
const dp2 = p2x * x2x + p2y * x2y + p2z * x2z;
|
|
259
|
-
const dp3 = p3x * x3x + p3y * x3y + p3z * x3z;
|
|
260
|
-
return 42.0 * (m0 * m0 * dp0 + m1 * m1 * dp1 + m2 * m2 * dp2 + m3 * m3 * dp3);
|
|
96
|
+
/**
|
|
97
|
+
* 2D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec2)` standard-tier overload op-for-op.
|
|
98
|
+
* Component arguments (rather than a vector) so it mirrors how the scalarized JS backend calls it.
|
|
99
|
+
*/
|
|
100
|
+
export function fxNoise2(vx, vy) {
|
|
101
|
+
const ix = Math.floor(vx);
|
|
102
|
+
const iy = Math.floor(vy);
|
|
103
|
+
const fx = vx - ix;
|
|
104
|
+
const fy = vy - iy;
|
|
105
|
+
const ux = fx * fx * (3 - 2 * fx);
|
|
106
|
+
const uy = fy * fy * (3 - 2 * fy);
|
|
107
|
+
const a = fxIntHash2(ix, iy);
|
|
108
|
+
const b = fxIntHash2(ix + 1, iy);
|
|
109
|
+
const c = fxIntHash2(ix, iy + 1);
|
|
110
|
+
const d = fxIntHash2(ix + 1, iy + 1);
|
|
111
|
+
const ab = a + (b - a) * ux;
|
|
112
|
+
const cd = c + (d - c) * ux;
|
|
113
|
+
return (ab + (cd - ab) * uy) * 2 - 1;
|
|
114
|
+
}
|
|
115
|
+
/** 3D twin of {@link fxNoise1}, mirroring the GLSL `noise(vec3)` standard-tier overload op-for-op. */
|
|
116
|
+
export function fxNoise3(vx, vy, vz) {
|
|
117
|
+
const ix = Math.floor(vx);
|
|
118
|
+
const iy = Math.floor(vy);
|
|
119
|
+
const iz = Math.floor(vz);
|
|
120
|
+
const fx = vx - ix;
|
|
121
|
+
const fy = vy - iy;
|
|
122
|
+
const fz = vz - iz;
|
|
123
|
+
const ux = fx * fx * (3 - 2 * fx);
|
|
124
|
+
const uy = fy * fy * (3 - 2 * fy);
|
|
125
|
+
const uz = fz * fz * (3 - 2 * fz);
|
|
126
|
+
const a = fxIntHash3(ix, iy, iz);
|
|
127
|
+
const b = fxIntHash3(ix + 1, iy, iz);
|
|
128
|
+
const c = fxIntHash3(ix, iy + 1, iz);
|
|
129
|
+
const d = fxIntHash3(ix + 1, iy + 1, iz);
|
|
130
|
+
const e = fxIntHash3(ix, iy, iz + 1);
|
|
131
|
+
const f = fxIntHash3(ix + 1, iy, iz + 1);
|
|
132
|
+
const g = fxIntHash3(ix, iy + 1, iz + 1);
|
|
133
|
+
const h = fxIntHash3(ix + 1, iy + 1, iz + 1);
|
|
134
|
+
const ab = a + (b - a) * ux;
|
|
135
|
+
const cd = c + (d - c) * ux;
|
|
136
|
+
const abcd = ab + (cd - ab) * uy;
|
|
137
|
+
const ef = e + (f - e) * ux;
|
|
138
|
+
const gh = g + (h - g) * ux;
|
|
139
|
+
const efgh = ef + (gh - ef) * uy;
|
|
140
|
+
return (abcd + (efgh - abcd) * uz) * 2 - 1;
|
|
261
141
|
}
|
|
@@ -48,11 +48,23 @@ function transformPreamble(outputs, withNormal) {
|
|
|
48
48
|
return lines.join("\n");
|
|
49
49
|
}
|
|
50
50
|
// `#include <project_vertex>` replacement: projects `fxModelPos`, records camera distance, feeds alpha-hash.
|
|
51
|
+
// World -> view via `viewMatrix` only, deliberately skipping `modelMatrix`: particle simulation
|
|
52
|
+
// authors `fxModelPos` directly in world space (reference space == world by convention), so the
|
|
53
|
+
// emitter's own scene transform must never re-enter here - see fxWorldMatrix for the explicit
|
|
54
|
+
// opt-in a graph uses when it wants particles attached to the emitter instead.
|
|
51
55
|
function projectFromModelPos() {
|
|
52
56
|
return [
|
|
53
|
-
"vec4 mvPosition =
|
|
57
|
+
"vec4 mvPosition = viewMatrix * vec4(fxModelPos, 1.0);",
|
|
54
58
|
"p_cameraDistance = length(mvPosition.xyz);",
|
|
55
59
|
"gl_Position = projectionMatrix * mvPosition;",
|
|
60
|
+
// A dead particle (age >= lifetime, coreLayout.ts's convention) is pushed outside the clip
|
|
61
|
+
// volume on every axis so the GPU's trivial-reject clip test drops the whole primitive before
|
|
62
|
+
// rasterization - a fragment discard would defeat opaque mode's early-Z instead (see
|
|
63
|
+
// alphaDiscardLines, which already skips discard for that reason). A JS-driven emitter never
|
|
64
|
+
// draws a dead particle at all (removeDeadParticles keeps it out of the instance range), so
|
|
65
|
+
// this is a no-op there; a GPU-driven emitter always draws its full fixed capacity with no
|
|
66
|
+
// such compaction (FXTransformFeedbackParticle), which is what this actually guards.
|
|
67
|
+
"if (PARTICLE_AGE >= PARTICLE_LIFETIME) { gl_Position = vec4(2.0, 2.0, 2.0, 1.0); }",
|
|
56
68
|
"#ifdef USE_ALPHAHASH",
|
|
57
69
|
" vPosition = fxModelPos;",
|
|
58
70
|
"#endif",
|
|
@@ -161,7 +173,7 @@ export function buildUnlitArtifactMaterial(render, varyings, timeUniform, deltaU
|
|
|
161
173
|
"uniform vec3 objectVelocity;",
|
|
162
174
|
"uniform vec3 objectAngularVelocity;",
|
|
163
175
|
"varying float p_cameraDistance;",
|
|
164
|
-
// World-space surface frame + position
|
|
176
|
+
// World-space surface frame + position: the canonical author space for
|
|
165
177
|
// normals. Written in the vertex stage, read as the geometryNormal/geometryTangent/worldPosition
|
|
166
178
|
// builtins in the fragment stage.
|
|
167
179
|
"varying vec3 vWorldNormal;",
|
|
@@ -185,12 +197,13 @@ export function buildUnlitArtifactMaterial(render, varyings, timeUniform, deltaU
|
|
|
185
197
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
186
198
|
"#include <defaultnormal_vertex>",
|
|
187
199
|
"#include <normal_vertex>",
|
|
188
|
-
// World-space surface frame + position
|
|
189
|
-
//
|
|
190
|
-
// roll/orientation is baked in here ONCE for
|
|
191
|
-
|
|
192
|
-
"
|
|
193
|
-
"
|
|
200
|
+
// World-space surface frame + position: fxModelPos/objectNormal are already world space (no
|
|
201
|
+
// modelMatrix step - see fxWorldMatrix for the explicit emitter-attach opt-in). The tangent is
|
|
202
|
+
// the plane's local +X (the u axis), so billboard roll/orientation is baked in here ONCE for
|
|
203
|
+
// every consumer (normal maps, spherical normal, fresnel).
|
|
204
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
205
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
206
|
+
"vWorldPos = fxModelPos;",
|
|
194
207
|
projectFromModelPos(),
|
|
195
208
|
"#include <fog_vertex>",
|
|
196
209
|
"}",
|
|
@@ -205,7 +218,7 @@ export function buildUnlitArtifactMaterial(render, varyings, timeUniform, deltaU
|
|
|
205
218
|
"uniform vec3 objectVelocity;",
|
|
206
219
|
"uniform vec3 objectAngularVelocity;",
|
|
207
220
|
"varying float p_cameraDistance;",
|
|
208
|
-
// World-space surface frame + position
|
|
221
|
+
// World-space surface frame + position: the canonical author space for
|
|
209
222
|
// normals. Written in the vertex stage, read as the geometryNormal/geometryTangent/worldPosition
|
|
210
223
|
// builtins in the fragment stage.
|
|
211
224
|
"varying vec3 vWorldNormal;",
|
|
@@ -223,7 +236,7 @@ export function buildUnlitArtifactMaterial(render, varyings, timeUniform, deltaU
|
|
|
223
236
|
"void main() {",
|
|
224
237
|
// Front-facing sign for the surface frame below (a back-facing fragment flips the normal).
|
|
225
238
|
"float fxFaceDirection = gl_FrontFacing ? 1.0 : - 1.0;",
|
|
226
|
-
// World-space surface frame + position - the canonical author space
|
|
239
|
+
// World-space surface frame + position - the canonical author space. The
|
|
227
240
|
// lighting intrinsics convert world->view at their own boundary; geometryTangent/worldPosition feed
|
|
228
241
|
// normal maps and camera-correct fresnel. Declared before the node body so any reader resolves.
|
|
229
242
|
"vec3 geometryNormal = normalize( vWorldNormal ) * fxFaceDirection;",
|
|
@@ -258,13 +271,18 @@ export function buildUnlitArtifactMaterial(render, varyings, timeUniform, deltaU
|
|
|
258
271
|
forceSinglePass: true,
|
|
259
272
|
premultipliedAlpha: true,
|
|
260
273
|
});
|
|
274
|
+
// `alphaHash` pulls in Three's own alphahash_pars_fragment chunk, which calls dFdx/dFdy - under
|
|
275
|
+
// a real WebGL1 context those need an explicit extension request or the shader fails to
|
|
276
|
+
// compile ("extension is disabled"); under WebGL2 this is a no-op (dFdx/dFdy are core there,
|
|
277
|
+
// and three skips extension generation entirely for a WebGL2 context either way).
|
|
278
|
+
material.extensions.derivatives = materialFlags(renderMode).alphaHash;
|
|
261
279
|
material.customProgramCacheKey = () => `fx-unlit_${cacheKey}`;
|
|
262
280
|
return material;
|
|
263
281
|
}
|
|
264
282
|
// The lighting-node intrinsics the graph calls by name (`fn.raw(... "fxLambertShade" ...)`) but never
|
|
265
283
|
// defines - the frozen `fx_` cross-repo ABI. Each wraps the exact stock Lambert light sequence in a
|
|
266
|
-
// function of (color, WORLD-space normal) -> shaded color: the graph authors normals in world
|
|
267
|
-
//
|
|
284
|
+
// function of (color, WORLD-space normal) -> shaded color: the graph authors normals in world
|
|
285
|
+
// space, and each intrinsic converts world->view on its first line before Three's
|
|
268
286
|
// view-space light chain runs, so view space stays a private detail behind this ABI. A node emits a
|
|
269
287
|
// plain expression that can sit anywhere in the graph (mid-graph mix included). Placed AFTER the pars chunks (which supply the
|
|
270
288
|
// `ReflectedLight`/`LambertMaterial` types, the `RE_*` macros, `vViewPosition`, the light uniforms) and
|
|
@@ -330,7 +348,7 @@ function buildLitArtifactMaterial(render, varyings, timeUniform, deltaUniform, v
|
|
|
330
348
|
"uniform vec3 objectVelocity;",
|
|
331
349
|
"uniform vec3 objectAngularVelocity;",
|
|
332
350
|
"varying float p_cameraDistance;",
|
|
333
|
-
// World-space surface frame + position
|
|
351
|
+
// World-space surface frame + position: the canonical author space for
|
|
334
352
|
// normals. Written in the vertex stage, read as the geometryNormal/geometryTangent/worldPosition
|
|
335
353
|
// builtins in the fragment stage.
|
|
336
354
|
"varying vec3 vWorldNormal;",
|
|
@@ -353,17 +371,18 @@ function buildLitArtifactMaterial(render, varyings, timeUniform, deltaUniform, v
|
|
|
353
371
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
354
372
|
"#include <defaultnormal_vertex>",
|
|
355
373
|
"#include <normal_vertex>",
|
|
356
|
-
// World-space surface frame + position
|
|
357
|
-
//
|
|
358
|
-
// roll/orientation is baked in here ONCE for
|
|
359
|
-
|
|
360
|
-
"
|
|
361
|
-
"
|
|
374
|
+
// World-space surface frame + position: fxModelPos/objectNormal are already world space (no
|
|
375
|
+
// modelMatrix step - see fxWorldMatrix for the explicit emitter-attach opt-in). The tangent is
|
|
376
|
+
// the plane's local +X (the u axis), so billboard roll/orientation is baked in here ONCE for
|
|
377
|
+
// every consumer (normal maps, spherical normal, fresnel).
|
|
378
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
379
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
380
|
+
"vWorldPos = fxModelPos;",
|
|
362
381
|
"vec3 transformed = position;",
|
|
363
382
|
projectFromModelPos(),
|
|
364
383
|
"vViewPosition = - mvPosition.xyz;",
|
|
365
384
|
"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined( USE_SHADOWMAP ) || defined( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0",
|
|
366
|
-
" vec4 worldPosition =
|
|
385
|
+
" vec4 worldPosition = vec4(particleCenter, 1.0);",
|
|
367
386
|
"#endif",
|
|
368
387
|
"#include <shadowmap_vertex>",
|
|
369
388
|
"#include <fog_vertex>",
|
|
@@ -378,7 +397,7 @@ function buildLitArtifactMaterial(render, varyings, timeUniform, deltaUniform, v
|
|
|
378
397
|
"uniform vec3 objectVelocity;",
|
|
379
398
|
"uniform vec3 objectAngularVelocity;",
|
|
380
399
|
"varying float p_cameraDistance;",
|
|
381
|
-
// World-space surface frame + position
|
|
400
|
+
// World-space surface frame + position: the canonical author space for
|
|
382
401
|
// normals. Written in the vertex stage, read as the geometryNormal/geometryTangent/worldPosition
|
|
383
402
|
// builtins in the fragment stage.
|
|
384
403
|
"varying vec3 vWorldNormal;",
|
|
@@ -402,7 +421,7 @@ function buildLitArtifactMaterial(render, varyings, timeUniform, deltaUniform, v
|
|
|
402
421
|
// Front-facing sign (matching `normal_fragment_begin` under DOUBLE_SIDED): a back-facing fragment
|
|
403
422
|
// flips the surface frame below.
|
|
404
423
|
"float fxFaceDirection = gl_FrontFacing ? 1.0 : - 1.0;",
|
|
405
|
-
// World-space surface frame + position - the canonical author space
|
|
424
|
+
// World-space surface frame + position - the canonical author space. The
|
|
406
425
|
// lighting intrinsics convert world->view at their own boundary; geometryTangent/worldPosition feed
|
|
407
426
|
// normal maps and camera-correct fresnel. Declared before the node body so any reader resolves.
|
|
408
427
|
"vec3 geometryNormal = normalize( vWorldNormal ) * fxFaceDirection;",
|
|
@@ -439,6 +458,8 @@ function buildLitArtifactMaterial(render, varyings, timeUniform, deltaUniform, v
|
|
|
439
458
|
forceSinglePass: true,
|
|
440
459
|
premultipliedAlpha: true,
|
|
441
460
|
});
|
|
461
|
+
// See buildUnlitArtifactMaterial's identical line for why this is needed.
|
|
462
|
+
material.extensions.derivatives = materialFlags(renderMode).alphaHash;
|
|
442
463
|
material.customProgramCacheKey = () => `fx-lit-nodes_${cacheKey}`;
|
|
443
464
|
return material;
|
|
444
465
|
}
|
|
@@ -471,11 +492,12 @@ function meshTransformPreamble(outputs, withNormal) {
|
|
|
471
492
|
}
|
|
472
493
|
return lines.join("\n");
|
|
473
494
|
}
|
|
474
|
-
// Mesh `#include <project_vertex>` replacement: plain
|
|
475
|
-
//
|
|
495
|
+
// Mesh `#include <project_vertex>` replacement: plain world-space projection (`viewMatrix` only,
|
|
496
|
+
// no `modelMatrix` - see {@link projectFromModelPos}). No `p_cameraDistance` (a mesh has no
|
|
497
|
+
// per-particle camera-distance builtin); still feeds alpha-hash `vPosition`.
|
|
476
498
|
function meshProjectFromModelPos() {
|
|
477
499
|
return [
|
|
478
|
-
"vec4 mvPosition =
|
|
500
|
+
"vec4 mvPosition = viewMatrix * vec4(fxModelPos, 1.0);",
|
|
479
501
|
"gl_Position = projectionMatrix * mvPosition;",
|
|
480
502
|
"#ifdef USE_ALPHAHASH",
|
|
481
503
|
" vPosition = fxModelPos;",
|
|
@@ -516,9 +538,9 @@ function buildMeshUnlitArtifactMaterial(render, timeUniform, deltaUniform, veloc
|
|
|
516
538
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
517
539
|
"#include <defaultnormal_vertex>",
|
|
518
540
|
"#include <normal_vertex>",
|
|
519
|
-
"vWorldNormal = normalize(
|
|
520
|
-
"vWorldTangent = normalize(
|
|
521
|
-
"vWorldPos =
|
|
541
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
542
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
543
|
+
"vWorldPos = fxModelPos;",
|
|
522
544
|
meshProjectFromModelPos(),
|
|
523
545
|
"#include <fog_vertex>",
|
|
524
546
|
"}",
|
|
@@ -571,6 +593,8 @@ function buildMeshUnlitArtifactMaterial(render, timeUniform, deltaUniform, veloc
|
|
|
571
593
|
forceSinglePass: true,
|
|
572
594
|
premultipliedAlpha: true,
|
|
573
595
|
});
|
|
596
|
+
// See buildUnlitArtifactMaterial's identical line for why this is needed.
|
|
597
|
+
material.extensions.derivatives = materialFlags(renderMode).alphaHash;
|
|
574
598
|
material.customProgramCacheKey = () => `fx-mesh-unlit_${cacheKey}`;
|
|
575
599
|
return material;
|
|
576
600
|
}
|
|
@@ -611,14 +635,14 @@ function buildMeshLitArtifactMaterial(render, timeUniform, deltaUniform, velocit
|
|
|
611
635
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
612
636
|
"#include <defaultnormal_vertex>",
|
|
613
637
|
"#include <normal_vertex>",
|
|
614
|
-
"vWorldNormal = normalize(
|
|
615
|
-
"vWorldTangent = normalize(
|
|
616
|
-
"vWorldPos =
|
|
638
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
639
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
640
|
+
"vWorldPos = fxModelPos;",
|
|
617
641
|
"vec3 transformed = position;",
|
|
618
642
|
meshProjectFromModelPos(),
|
|
619
643
|
"vViewPosition = - mvPosition.xyz;",
|
|
620
644
|
"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined( USE_SHADOWMAP ) || defined( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0",
|
|
621
|
-
" vec4 worldPosition =
|
|
645
|
+
" vec4 worldPosition = vec4(fxModelPos, 1.0);",
|
|
622
646
|
"#endif",
|
|
623
647
|
"#include <shadowmap_vertex>",
|
|
624
648
|
"#include <fog_vertex>",
|
|
@@ -683,6 +707,8 @@ function buildMeshLitArtifactMaterial(render, timeUniform, deltaUniform, velocit
|
|
|
683
707
|
forceSinglePass: true,
|
|
684
708
|
premultipliedAlpha: true,
|
|
685
709
|
});
|
|
710
|
+
// See buildUnlitArtifactMaterial's identical line for why this is needed.
|
|
711
|
+
material.extensions.derivatives = materialFlags(renderMode).alphaHash;
|
|
686
712
|
material.customProgramCacheKey = () => `fx-mesh-lit-nodes_${cacheKey}`;
|
|
687
713
|
return material;
|
|
688
714
|
}
|
|
@@ -753,16 +779,16 @@ export function buildArtifactDepthMaterial(render, varyings, timeUniform, deltaU
|
|
|
753
779
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
754
780
|
"#include <defaultnormal_vertex>",
|
|
755
781
|
"#include <normal_vertex>",
|
|
756
|
-
"vWorldNormal = normalize(
|
|
757
|
-
"vWorldTangent = normalize(
|
|
758
|
-
"vWorldPos =
|
|
782
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
783
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
784
|
+
"vWorldPos = fxModelPos;",
|
|
759
785
|
...(lit ? ["vec3 transformed = position;"] : []),
|
|
760
786
|
projectFromModelPos(),
|
|
761
787
|
...(lit
|
|
762
788
|
? [
|
|
763
789
|
"vViewPosition = - mvPosition.xyz;",
|
|
764
790
|
"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined( USE_SHADOWMAP ) || defined( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0",
|
|
765
|
-
" vec4 worldPosition =
|
|
791
|
+
" vec4 worldPosition = vec4(particleCenter, 1.0);",
|
|
766
792
|
"#endif",
|
|
767
793
|
"#include <shadowmap_vertex>",
|
|
768
794
|
]
|
|
@@ -870,16 +896,16 @@ export function buildMeshArtifactDepthMaterial(render, timeUniform, deltaUniform
|
|
|
870
896
|
"vec3 objectNormal = fxNormalXform * normal;",
|
|
871
897
|
"#include <defaultnormal_vertex>",
|
|
872
898
|
"#include <normal_vertex>",
|
|
873
|
-
"vWorldNormal = normalize(
|
|
874
|
-
"vWorldTangent = normalize(
|
|
875
|
-
"vWorldPos =
|
|
899
|
+
"vWorldNormal = normalize( objectNormal );",
|
|
900
|
+
"vWorldTangent = normalize( fxNormalXform * vec3( 1.0, 0.0, 0.0 ) );",
|
|
901
|
+
"vWorldPos = fxModelPos;",
|
|
876
902
|
...(lit ? ["vec3 transformed = position;"] : []),
|
|
877
903
|
meshProjectFromModelPos(),
|
|
878
904
|
...(lit
|
|
879
905
|
? [
|
|
880
906
|
"vViewPosition = - mvPosition.xyz;",
|
|
881
907
|
"#if defined( USE_ENVMAP ) || defined( DISTANCE ) || defined( USE_SHADOWMAP ) || defined( USE_TRANSMISSION ) || NUM_SPOT_LIGHT_COORDS > 0",
|
|
882
|
-
" vec4 worldPosition =
|
|
908
|
+
" vec4 worldPosition = vec4(fxModelPos, 1.0);",
|
|
883
909
|
"#endif",
|
|
884
910
|
"#include <shadowmap_vertex>",
|
|
885
911
|
]
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildArtifactDepthMaterial, buildArtifactMaterial } from "./FXArtifactMaterial.Internal.js";
|
|
1
|
+
import { buildArtifactDepthMaterial, buildArtifactMaterial, } from "./FXArtifactMaterial.Internal.js";
|
|
2
2
|
/**
|
|
3
3
|
* @internal Runtime material driver: wraps a precompiled {@link FXRenderArtifact} and builds the
|
|
4
4
|
* concrete self-contained `ShaderMaterial` via {@link buildArtifactMaterial}, which picks the flat or
|
package/dist/render/FXMesh.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Mesh, Quaternion, Vector3
|
|
1
|
+
import { Mesh, Quaternion, Vector3 } from "three";
|
|
2
2
|
import { FXObjectMotionTracker } from "../emitter/FXObjectMotion.Internal.js";
|
|
3
3
|
import { resolveGeometrySource } from "../instancedParticle/primitiveGeometry.js";
|
|
4
4
|
import { FXWorld } from "../world/FXWorld.js";
|
|
@@ -39,9 +39,7 @@ export class FXMesh extends Mesh {
|
|
|
39
39
|
const driver = new FXMeshMaterial(render, options.textures);
|
|
40
40
|
const geometry = resolveGeometrySource(render.geometry, options.geometries ?? {});
|
|
41
41
|
const ownsGeometry = render.geometry === undefined || render.geometry.type === "primitive";
|
|
42
|
-
const depthMaterial = (options.castShadow ?? false)
|
|
43
|
-
? driver.buildThreeDepthMaterial()
|
|
44
|
-
: undefined;
|
|
42
|
+
const depthMaterial = (options.castShadow ?? false) ? driver.buildThreeDepthMaterial() : undefined;
|
|
45
43
|
const mesh = new FXMesh(geometry, driver, driver.buildThreeMaterial(), depthMaterial, ownsGeometry, world);
|
|
46
44
|
if (options.receiveShadow ?? false) {
|
|
47
45
|
mesh.receiveShadow = true;
|