@neutrinoparticles/js-v1.1-phaser 1.0.0 → 1.1.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/CHANGELOG.md +13 -1
- package/README.md +37 -0
- package/dist/Effect.d.ts +11 -1
- package/dist/NeutrinoRenderer.d.ts +8 -0
- package/dist/PerspectiveProjection.d.ts +10 -0
- package/dist/neutrinoparticles.js-v1.1-phaser.cjs.js +202 -57
- package/dist/neutrinoparticles.js-v1.1-phaser.cjs.js.map +1 -1
- package/dist/neutrinoparticles.js-v1.1-phaser.d.ts +37 -1
- package/dist/neutrinoparticles.js-v1.1-phaser.es.js +307 -119
- package/dist/neutrinoparticles.js-v1.1-phaser.es.js.map +1 -1
- package/dist/neutrinoparticles.js-v1.1-phaser.umd.js +202 -57
- package/dist/neutrinoparticles.js-v1.1-phaser.umd.js.map +1 -1
- package/dist/types.d.ts +12 -0
- package/package.json +4 -3
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var K = Object.defineProperty;
|
|
2
|
-
var
|
|
3
|
-
var n = (s, e, t) =>
|
|
2
|
+
var z = (s, e, t) => e in s ? K(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
|
|
3
|
+
var n = (s, e, t) => z(s, typeof e != "symbol" ? e + "" : e, t);
|
|
4
4
|
import Phaser from "phaser";
|
|
5
5
|
import * as Neutrino from "@neutrinoparticles/js-v1.1";
|
|
6
6
|
const vertexShaderSource = `#version 300 es
|
|
@@ -54,7 +54,8 @@ vec4 fetchTexel(int globalTexelIndex) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Strip geometry helpers (port of GPU33 makeScreenSpaceStrip).
|
|
57
|
-
// 2D screen-space variant
|
|
57
|
+
// 2D screen-space variant used by the Faced strip frame; world Z reaches
|
|
58
|
+
// gl_Position only through uViewProjMatrix (ortho or v1.0-parity perspective).
|
|
58
59
|
vec2 stripOrtho(vec2 v) {
|
|
59
60
|
return vec2(-v.y, v.x);
|
|
60
61
|
}
|
|
@@ -91,9 +92,9 @@ void main() {
|
|
|
91
92
|
// 0 (Faced): screen-space ortho to dir12 — equivalent to camera-faced
|
|
92
93
|
// billboard normal in a 2D camera setup.
|
|
93
94
|
// 1 (Free): the quaternion stored in texel 2 nominally encodes a 3D
|
|
94
|
-
// orientation. The
|
|
95
|
-
//
|
|
96
|
-
// the quat is read but its 3D y-axis is projected to screen
|
|
95
|
+
// orientation. The PIXI7 renderer has no true 3D camera
|
|
96
|
+
// (uViewProjMatrix is ortho or the v1.0-parity perspective),
|
|
97
|
+
// so the quat is read but its 3D y-axis is projected to screen
|
|
97
98
|
// and used as the section normal. When the projection
|
|
98
99
|
// degenerates (looking along the y-axis) we fall back to
|
|
99
100
|
// the screen-space ortho. Real 3D free-rotation requires a
|
|
@@ -128,6 +129,10 @@ void main() {
|
|
|
128
129
|
float((packedColorNex >> 8u) & 0xFFu) / 255.0,
|
|
129
130
|
float((packedColorNex >> 16u) & 0xFFu) / 255.0,
|
|
130
131
|
float((packedColorNex >> 24u) & 0xFFu) / 255.0);
|
|
132
|
+
// HDR color scale S (RGBS, bloom TDD #175 §13.12): offset+60 (t3.a) per-particle
|
|
133
|
+
// float scale; multiply BEFORE the cross-fade. S=1 for LDR (no-op).
|
|
134
|
+
color1.rgb *= (isCur ? t3.a : ts3.a);
|
|
135
|
+
color2.rgb *= (isCur ? ts3.a : t3.a);
|
|
131
136
|
|
|
132
137
|
// neighborIdxP0 / neighborIdxP3 are stored on every meta of a pair
|
|
133
138
|
// (both meta_cur and meta_nex hold the same pair of indices), so we
|
|
@@ -174,6 +179,10 @@ void main() {
|
|
|
174
179
|
// by the sign of w). Forces a discard outside the [-w,w] cube at the end.
|
|
175
180
|
bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
|
|
176
181
|
|
|
182
|
+
// #236: set when the segment itself is degenerate (much shorter than
|
|
183
|
+
// the ribbon half-width) — the pair is discarded like nearPlaneClipped.
|
|
184
|
+
bool stripDegenerate = false;
|
|
185
|
+
|
|
177
186
|
// Aspect correction so the 2D Faced ortho gives visually-perpendicular
|
|
178
187
|
// dirs on non-square viewports (inverted before gl_Position).
|
|
179
188
|
vec2 aspectMul = vec2(1.0, 1.0 / max(uViewportAspect, 1e-6));
|
|
@@ -211,9 +220,15 @@ void main() {
|
|
|
211
220
|
float wlen01 = length(wp01);
|
|
212
221
|
float wlen12 = length(wp12);
|
|
213
222
|
float wlen23 = length(wp23);
|
|
223
|
+
// #236: degeneracy guards at visual scale (mirror of GPU33).
|
|
224
|
+
// A segment much shorter than the ribbon half-width cannot define
|
|
225
|
+
// a visually meaningful direction; a degenerate neighbour falls
|
|
226
|
+
// back to the straight cap instead of a noise knee.
|
|
227
|
+
float wDegenEps = max(max(abs(halfSize1), abs(halfSize2)) * 0.02, 1e-6);
|
|
228
|
+
if (wlen12 < wDegenEps) stripDegenerate = true;
|
|
214
229
|
vec3 wdir12 = (wlen12 > 1e-6) ? (wp12 / wlen12) : vec3(1.0, 0.0, 0.0);
|
|
215
|
-
vec3 wdir01 = (wlen01 >
|
|
216
|
-
vec3 wdir23 = (wlen23 >
|
|
230
|
+
vec3 wdir01 = (wlen01 > wDegenEps) ? (wp01 / wlen01) : wdir12;
|
|
231
|
+
vec3 wdir23 = (wlen23 > wDegenEps) ? (wp23 / wlen23) : wdir12;
|
|
217
232
|
noKnee1 = dot(wdir01, wdir12) > 0.999;
|
|
218
233
|
noKnee2 = dot(wdir12, wdir23) > 0.999;
|
|
219
234
|
pathLen12 = wlen12;
|
|
@@ -225,9 +240,12 @@ void main() {
|
|
|
225
240
|
vec3 K1 = Kmul1 * normalize(cross(wdir01 + wdir12, n1));
|
|
226
241
|
Bmul1 = sign(dot(B1raw, K1));
|
|
227
242
|
vec3 B1 = B1raw * Bmul1;
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
// #236: inner vertex at the ribbon half-width along the
|
|
244
|
+
// bisector, symmetric to the outer vertex (mirror of GPU33).
|
|
245
|
+
// The old true-miter-with-neighbour-length-clamp cut an
|
|
246
|
+
// uncovered wedge out of sharp bends whenever halfSize
|
|
247
|
+
// exceeded the particle spacing.
|
|
248
|
+
float ik1 = halfSize1;
|
|
231
249
|
worldK1 = p1 + K1 * halfSize1;
|
|
232
250
|
worldB1 = p1 + B1 * halfSize1;
|
|
233
251
|
worldIK1 = p1 - K1 * ik1;
|
|
@@ -248,9 +266,8 @@ void main() {
|
|
|
248
266
|
vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
|
|
249
267
|
Bmul2 = sign(dot(B2raw, K2));
|
|
250
268
|
vec3 B2 = B2raw * Bmul2;
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
if (dot( wdir23, ik2 * -K2) > wlen23) ik2 = wlen23;
|
|
269
|
+
// #236: see ik1 — inner vertex at the ribbon half-width.
|
|
270
|
+
float ik2 = halfSize2;
|
|
254
271
|
worldK2 = p2 + K2 * halfSize2;
|
|
255
272
|
worldB2 = p2 + B2 * halfSize2;
|
|
256
273
|
worldIK2 = p2 - K2 * ik2;
|
|
@@ -267,10 +284,16 @@ void main() {
|
|
|
267
284
|
// Faced: screen-space NDC frame around dir12.
|
|
268
285
|
vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
|
|
269
286
|
vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
|
|
270
|
-
vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
|
|
271
287
|
vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
|
|
272
288
|
vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
|
|
273
|
-
|
|
289
|
+
// Neighbor anchors behind the near plane produce garbage NDC after
|
|
290
|
+
// the perspective divide (possible only with Projection3D, where w
|
|
291
|
+
// varies); collapse them onto their segment anchor so the existing
|
|
292
|
+
// zero-length degeneracy path (dir01 = dir12 / dir23 = dir12)
|
|
293
|
+
// handles the knee instead of a poisoned miter. spU1/spU2 need no
|
|
294
|
+
// guard: uCameraUp has zero Z, so their w equals sp1.w / sp2.w.
|
|
295
|
+
vec2 ndc0 = (sp0.w < 1e-3) ? ndc1 : (sp0.xy / sp0.w) * aspectMul;
|
|
296
|
+
vec2 ndc3 = (sp3.w < 1e-3) ? ndc2 : (sp3.xy / sp3.w) * aspectMul;
|
|
274
297
|
|
|
275
298
|
vec2 d01 = ndc1 - ndc0;
|
|
276
299
|
vec2 d12 = ndc2 - ndc1;
|
|
@@ -278,12 +301,6 @@ void main() {
|
|
|
278
301
|
float len01 = length(d01);
|
|
279
302
|
float len12 = length(d12);
|
|
280
303
|
float len23 = length(d23);
|
|
281
|
-
vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
|
|
282
|
-
vec2 dir01 = (len01 > 1e-6) ? (d01 / len01) : dir12;
|
|
283
|
-
vec2 dir23 = (len23 > 1e-6) ? (d23 / len23) : dir12;
|
|
284
|
-
noKnee1 = dot(dir01, dir12) > 0.999;
|
|
285
|
-
noKnee2 = dot(dir12, dir23) > 0.999;
|
|
286
|
-
pathLen12 = len12;
|
|
287
304
|
|
|
288
305
|
// Section magnitude: project camera_up*halfSize per anchor into NDC.
|
|
289
306
|
vec4 spU1 = mvpMatrix * vec4(p1 + uCameraUp * halfSize1, 1.0);
|
|
@@ -291,15 +308,25 @@ void main() {
|
|
|
291
308
|
float ndcHalfSize1 = length((spU1.xy / spU1.w) * aspectMul - ndc1);
|
|
292
309
|
float ndcHalfSize2 = length((spU2.xy / spU2.w) * aspectMul - ndc2);
|
|
293
310
|
|
|
311
|
+
// #236: degeneracy guards at visual scale (mirror of GPU33).
|
|
312
|
+
float degenEps = max(max(ndcHalfSize1, ndcHalfSize2) * 0.02, 1e-6);
|
|
313
|
+
if (len12 < degenEps) stripDegenerate = true;
|
|
314
|
+
vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
|
|
315
|
+
vec2 dir01 = (len01 > degenEps) ? (d01 / len01) : dir12;
|
|
316
|
+
vec2 dir23 = (len23 > degenEps) ? (d23 / len23) : dir12;
|
|
317
|
+
noKnee1 = dot(dir01, dir12) > 0.999;
|
|
318
|
+
noKnee2 = dot(dir12, dir23) > 0.999;
|
|
319
|
+
pathLen12 = len12;
|
|
320
|
+
|
|
294
321
|
vec2 B = stripOrtho(dir12);
|
|
295
322
|
if (!noKnee1) {
|
|
296
323
|
float Kmul1 = -sign(dot(stripOrtho(dir01), dir12));
|
|
297
324
|
vec2 K1 = Kmul1 * normalize(stripOrtho(dir01 + dir12));
|
|
298
325
|
Bmul1 = sign(dot(B, K1));
|
|
299
326
|
vec2 B1 = B * Bmul1;
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
327
|
+
// #236: inner vertex at the ribbon half-width (see the Free
|
|
328
|
+
// branch comment above).
|
|
329
|
+
float ik1 = ndcHalfSize1;
|
|
303
330
|
k1 = ndc1 + K1 * ndcHalfSize1;
|
|
304
331
|
b1 = ndc1 + B1 * ndcHalfSize1;
|
|
305
332
|
IK1 = ndc1 - K1 * ik1;
|
|
@@ -317,9 +344,8 @@ void main() {
|
|
|
317
344
|
vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
|
|
318
345
|
Bmul2 = sign(dot(B, K2));
|
|
319
346
|
vec2 B2 = B * Bmul2;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
if (dot(dir23, ik2 * -K2) > len23) ik2 = len23;
|
|
347
|
+
// #236: see ik1 — inner vertex at the ribbon half-width.
|
|
348
|
+
float ik2 = ndcHalfSize2;
|
|
323
349
|
k2 = ndc2 + K2 * ndcHalfSize2;
|
|
324
350
|
b2 = ndc2 + B2 * ndcHalfSize2;
|
|
325
351
|
IK2 = ndc2 - K2 * ik2;
|
|
@@ -334,6 +360,34 @@ void main() {
|
|
|
334
360
|
}
|
|
335
361
|
}
|
|
336
362
|
|
|
363
|
+
// #236: a noKnee (straight-cap) end carries no bend side of its own —
|
|
364
|
+
// its raw side label is always the +B side. If the segment's other
|
|
365
|
+
// end is a real knee on the -B side, the mismatched labels would send
|
|
366
|
+
// the ladder into the opposite-sides (twist) branch and flip the
|
|
367
|
+
// texture V across a segment that does not actually change bend side.
|
|
368
|
+
// Relabel the straight end to the knee end's side (swap its k/IK and
|
|
369
|
+
// re-collapse b onto the new k) so the ladder stays untwisted. Both
|
|
370
|
+
// frames are swapped; only the active one is meaningful.
|
|
371
|
+
if (noKnee1 && !noKnee2 && Bmul2 < 0.0) {
|
|
372
|
+
// Swap only the ACTIVE frame's variables - the inactive frame's
|
|
373
|
+
// are uninitialized and must not be read.
|
|
374
|
+
if (isFreeRotation) {
|
|
375
|
+
vec3 tRw1 = worldK1; worldK1 = worldIK1; worldIK1 = tRw1; worldB1 = worldK1;
|
|
376
|
+
} else {
|
|
377
|
+
vec2 tRl1 = k1; k1 = IK1; IK1 = tRl1; b1 = k1;
|
|
378
|
+
}
|
|
379
|
+
Bmul1 = Bmul2;
|
|
380
|
+
}
|
|
381
|
+
if (noKnee2 && !noKnee1 && Bmul1 < 0.0) {
|
|
382
|
+
if (isFreeRotation) {
|
|
383
|
+
vec3 tRw2 = worldK2; worldK2 = worldIK2; worldIK2 = tRw2; worldB2 = worldK2;
|
|
384
|
+
} else {
|
|
385
|
+
vec2 tRl2 = k2; k2 = IK2; IK2 = tRl2; b2 = k2;
|
|
386
|
+
}
|
|
387
|
+
Bmul2 = Bmul1;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
|
|
337
391
|
// V coords (mirror of preview3 / GPU33 make*Strip):
|
|
338
392
|
// V at outer knee (k) = (Bmul + 1) * 0.5
|
|
339
393
|
// V at inner knee (IK) = 1 - V_outer ; bend (b) = outer
|
|
@@ -435,7 +489,10 @@ void main() {
|
|
|
435
489
|
|
|
436
490
|
// Free projects the world-space vertex through MVP directly; Faced
|
|
437
491
|
// reconstructs clip space from aspect-corrected NDC (mirror of preview3).
|
|
438
|
-
if (
|
|
492
|
+
if (stripDegenerate) {
|
|
493
|
+
// #236: sub-visual segment — discard the pair entirely.
|
|
494
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
495
|
+
} else if (isFreeRotation) {
|
|
439
496
|
gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
|
|
440
497
|
} else if (nearPlaneClipped) {
|
|
441
498
|
// At least one anchor is at / behind the near plane → the NDC math
|
|
@@ -472,6 +529,10 @@ void main() {
|
|
|
472
529
|
float((packedColor >> 16u) & 0xFFu) / 255.0,
|
|
473
530
|
float((packedColor >> 24u) & 0xFFu) / 255.0
|
|
474
531
|
);
|
|
532
|
+
// HDR color scale S (RGBS, bloom TDD #175 §13.12): offset+60 (t3.a) is a
|
|
533
|
+
// per-particle float scale. RGB was normalized by S at pack time; restore HDR
|
|
534
|
+
// here. S=1 for LDR colors (no-op). Alpha is untouched.
|
|
535
|
+
vColor.rgb *= t3.a;
|
|
475
536
|
|
|
476
537
|
uint gridConfig = floatBitsToUint(t3.g);
|
|
477
538
|
float gridWidth = max(float(gridConfig & 0xFFFFu), 1.0);
|
|
@@ -519,7 +580,23 @@ void main() {
|
|
|
519
580
|
|
|
520
581
|
mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
|
|
521
582
|
vec3 worldPos = position + posDisp;
|
|
522
|
-
gl_Position = mvpMatrix * vec4(worldPos
|
|
583
|
+
gl_Position = mvpMatrix * vec4(worldPos, 1.0);
|
|
584
|
+
|
|
585
|
+
// v1.0-parity near-plane culling for the perspective projection: the
|
|
586
|
+
// whole quad is discarded when ANY of its 4 corners is behind the near
|
|
587
|
+
// plane (w < 0.01 == pos.z > 0.99 * z_cam in PerspectiveProjection
|
|
588
|
+
// terms). w is linear in position, so the corner minimum comes from the
|
|
589
|
+
// w-row and the corner offsets (x in {-ox, 1-ox}, y in {-oy, 1-oy}).
|
|
590
|
+
// Without a projection every w is 1.0 and this is a no-op.
|
|
591
|
+
vec4 wRow = vec4(mvpMatrix[0].w, mvpMatrix[1].w, mvpMatrix[2].w, mvpMatrix[3].w);
|
|
592
|
+
float wx = dot(wRow.xyz, xaxis);
|
|
593
|
+
float wy = dot(wRow.xyz, yaxis);
|
|
594
|
+
float minW = dot(wRow.xyz, position) + wRow.w
|
|
595
|
+
- origin.x * wx + min(wx, 0.0)
|
|
596
|
+
- origin.y * wy + min(wy, 0.0);
|
|
597
|
+
if (minW < 0.01) {
|
|
598
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
599
|
+
}
|
|
523
600
|
|
|
524
601
|
// Texture coordinates with grid + atlas remap
|
|
525
602
|
vec2 baseTexCoord = TEX_COORD[vertexIndex];
|
|
@@ -739,6 +816,11 @@ void main() {
|
|
|
739
816
|
// Colors: the meta M1 sample is the normalized RGBA directly.
|
|
740
817
|
vec4 color1 = fetchMeta(curBase + 1.0);
|
|
741
818
|
vec4 color2 = fetchMeta(nexBase + 1.0);
|
|
819
|
+
// HDR color scale S (RGBS, bloom TDD #175 §13.12): a plain float at offset+60,
|
|
820
|
+
// read straight from the float DATA texture (texel 3, .a) — like gridIndex — not
|
|
821
|
+
// the meta texture. Multiply BEFORE the cross-fade. S=1 → no-op.
|
|
822
|
+
color1.rgb *= fetchData(curBase + 3.0).a;
|
|
823
|
+
color2.rgb *= fetchData(nexBase + 3.0).a;
|
|
742
824
|
|
|
743
825
|
// Neighbor RECORD indices (system.js convention: neighborIdxP0 on the
|
|
744
826
|
// pair-cur meta, neighborIdxP3 on the pair-nex meta; boundary
|
|
@@ -770,6 +852,10 @@ void main() {
|
|
|
770
852
|
// unaffected by the sign of w).
|
|
771
853
|
bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
|
|
772
854
|
|
|
855
|
+
// #236: set when the segment itself is degenerate (much shorter than
|
|
856
|
+
// the ribbon half-width) - the pair is discarded like nearPlaneClipped.
|
|
857
|
+
bool stripDegenerate = false;
|
|
858
|
+
|
|
773
859
|
// Aspect correction for the 2D Faced ortho on non-square viewports.
|
|
774
860
|
vec2 aspectMul = vec2(1.0, 1.0 / max(uViewportAspect, 1e-6));
|
|
775
861
|
|
|
@@ -812,9 +898,12 @@ void main() {
|
|
|
812
898
|
float wlen01 = length(wp01);
|
|
813
899
|
float wlen12 = length(wp12);
|
|
814
900
|
float wlen23 = length(wp23);
|
|
901
|
+
// #236: degeneracy guards at visual scale (mirror of GPU33).
|
|
902
|
+
float wDegenEps = max(max(abs(halfSize1), abs(halfSize2)) * 0.02, 1e-6);
|
|
903
|
+
if (wlen12 < wDegenEps) stripDegenerate = true;
|
|
815
904
|
vec3 wdir12 = (wlen12 > 1e-6) ? (wp12 / wlen12) : vec3(1.0, 0.0, 0.0);
|
|
816
|
-
vec3 wdir01 = (wlen01 >
|
|
817
|
-
vec3 wdir23 = (wlen23 >
|
|
905
|
+
vec3 wdir01 = (wlen01 > wDegenEps) ? (wp01 / wlen01) : wdir12;
|
|
906
|
+
vec3 wdir23 = (wlen23 > wDegenEps) ? (wp23 / wlen23) : wdir12;
|
|
818
907
|
noKnee1 = dot(wdir01, wdir12) > 0.999;
|
|
819
908
|
noKnee2 = dot(wdir12, wdir23) > 0.999;
|
|
820
909
|
pathLen12 = wlen12;
|
|
@@ -826,9 +915,9 @@ void main() {
|
|
|
826
915
|
vec3 K1 = Kmul1 * normalize(cross(wdir01 + wdir12, n1));
|
|
827
916
|
Bmul1 = sign(dot(B1raw, K1));
|
|
828
917
|
vec3 B1 = B1raw * Bmul1;
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
918
|
+
// #236: inner vertex at the ribbon half-width (mirror of
|
|
919
|
+
// GPU33; see the ES 3.00 shader for the rationale).
|
|
920
|
+
float ik1 = halfSize1;
|
|
832
921
|
worldK1 = p1 + K1 * halfSize1;
|
|
833
922
|
worldB1 = p1 + B1 * halfSize1;
|
|
834
923
|
worldIK1 = p1 - K1 * ik1;
|
|
@@ -849,9 +938,8 @@ void main() {
|
|
|
849
938
|
vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
|
|
850
939
|
Bmul2 = sign(dot(B2raw, K2));
|
|
851
940
|
vec3 B2 = B2raw * Bmul2;
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (dot( wdir23, ik2 * -K2) > wlen23) ik2 = wlen23;
|
|
941
|
+
// #236: see ik1 - inner vertex at the ribbon half-width.
|
|
942
|
+
float ik2 = halfSize2;
|
|
855
943
|
worldK2 = p2 + K2 * halfSize2;
|
|
856
944
|
worldB2 = p2 + B2 * halfSize2;
|
|
857
945
|
worldIK2 = p2 - K2 * ik2;
|
|
@@ -868,10 +956,16 @@ void main() {
|
|
|
868
956
|
// Faced: screen-space NDC frame around dir12.
|
|
869
957
|
vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
|
|
870
958
|
vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
|
|
871
|
-
vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
|
|
872
959
|
vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
|
|
873
960
|
vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
|
|
874
|
-
|
|
961
|
+
// Neighbor anchors behind the near plane produce garbage NDC after
|
|
962
|
+
// the perspective divide (possible only with Projection3D, where w
|
|
963
|
+
// varies); collapse them onto their segment anchor so the existing
|
|
964
|
+
// zero-length degeneracy path (dir01 = dir12 / dir23 = dir12)
|
|
965
|
+
// handles the knee instead of a poisoned miter. spU1/spU2 need no
|
|
966
|
+
// guard: uCameraUp has zero Z, so their w equals sp1.w / sp2.w.
|
|
967
|
+
vec2 ndc0 = (sp0.w < 1e-3) ? ndc1 : (sp0.xy / sp0.w) * aspectMul;
|
|
968
|
+
vec2 ndc3 = (sp3.w < 1e-3) ? ndc2 : (sp3.xy / sp3.w) * aspectMul;
|
|
875
969
|
|
|
876
970
|
vec2 d01 = ndc1 - ndc0;
|
|
877
971
|
vec2 d12 = ndc2 - ndc1;
|
|
@@ -879,12 +973,6 @@ void main() {
|
|
|
879
973
|
float len01 = length(d01);
|
|
880
974
|
float len12 = length(d12);
|
|
881
975
|
float len23 = length(d23);
|
|
882
|
-
vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
|
|
883
|
-
vec2 dir01 = (len01 > 1e-6) ? (d01 / len01) : dir12;
|
|
884
|
-
vec2 dir23 = (len23 > 1e-6) ? (d23 / len23) : dir12;
|
|
885
|
-
noKnee1 = dot(dir01, dir12) > 0.999;
|
|
886
|
-
noKnee2 = dot(dir12, dir23) > 0.999;
|
|
887
|
-
pathLen12 = len12;
|
|
888
976
|
|
|
889
977
|
// Section magnitude: project camera_up*halfSize per anchor into NDC.
|
|
890
978
|
vec4 spU1 = mvpMatrix * vec4(p1 + uCameraUp * halfSize1, 1.0);
|
|
@@ -892,15 +980,24 @@ void main() {
|
|
|
892
980
|
float ndcHalfSize1 = length((spU1.xy / spU1.w) * aspectMul - ndc1);
|
|
893
981
|
float ndcHalfSize2 = length((spU2.xy / spU2.w) * aspectMul - ndc2);
|
|
894
982
|
|
|
983
|
+
// #236: degeneracy guards at visual scale (mirror of GPU33).
|
|
984
|
+
float degenEps = max(max(ndcHalfSize1, ndcHalfSize2) * 0.02, 1e-6);
|
|
985
|
+
if (len12 < degenEps) stripDegenerate = true;
|
|
986
|
+
vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
|
|
987
|
+
vec2 dir01 = (len01 > degenEps) ? (d01 / len01) : dir12;
|
|
988
|
+
vec2 dir23 = (len23 > degenEps) ? (d23 / len23) : dir12;
|
|
989
|
+
noKnee1 = dot(dir01, dir12) > 0.999;
|
|
990
|
+
noKnee2 = dot(dir12, dir23) > 0.999;
|
|
991
|
+
pathLen12 = len12;
|
|
992
|
+
|
|
895
993
|
vec2 B = stripOrtho(dir12);
|
|
896
994
|
if (!noKnee1) {
|
|
897
995
|
float Kmul1 = -sign(dot(stripOrtho(dir01), dir12));
|
|
898
996
|
vec2 K1 = Kmul1 * normalize(stripOrtho(dir01 + dir12));
|
|
899
997
|
Bmul1 = sign(dot(B, K1));
|
|
900
998
|
vec2 B1 = B * Bmul1;
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
if (dot(dir12, ik1 * -K1) > len12) ik1 = len12;
|
|
999
|
+
// #236: inner vertex at the ribbon half-width.
|
|
1000
|
+
float ik1 = ndcHalfSize1;
|
|
904
1001
|
k1 = ndc1 + K1 * ndcHalfSize1;
|
|
905
1002
|
b1 = ndc1 + B1 * ndcHalfSize1;
|
|
906
1003
|
IK1 = ndc1 - K1 * ik1;
|
|
@@ -918,9 +1015,8 @@ void main() {
|
|
|
918
1015
|
vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
|
|
919
1016
|
Bmul2 = sign(dot(B, K2));
|
|
920
1017
|
vec2 B2 = B * Bmul2;
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
if (dot(dir23, ik2 * -K2) > len23) ik2 = len23;
|
|
1018
|
+
// #236: see ik1 - inner vertex at the ribbon half-width.
|
|
1019
|
+
float ik2 = ndcHalfSize2;
|
|
924
1020
|
k2 = ndc2 + K2 * ndcHalfSize2;
|
|
925
1021
|
b2 = ndc2 + B2 * ndcHalfSize2;
|
|
926
1022
|
IK2 = ndc2 - K2 * ik2;
|
|
@@ -935,6 +1031,33 @@ void main() {
|
|
|
935
1031
|
}
|
|
936
1032
|
}
|
|
937
1033
|
|
|
1034
|
+
// #236: a noKnee (straight-cap) end carries no bend side of its own -
|
|
1035
|
+
// its raw side label is always the +B side. If the segment's other
|
|
1036
|
+
// end is a real knee on the -B side, the mismatched labels would send
|
|
1037
|
+
// the ladder into the opposite-sides (twist) branch and flip the
|
|
1038
|
+
// texture V across a segment that does not actually change bend side.
|
|
1039
|
+
// Relabel the straight end to the knee end's side (swap its k/IK and
|
|
1040
|
+
// re-collapse b onto the new k) so the ladder stays untwisted. Both
|
|
1041
|
+
// frames are swapped; only the active one is meaningful.
|
|
1042
|
+
if (noKnee1 && !noKnee2 && Bmul2 < 0.0) {
|
|
1043
|
+
// Swap only the ACTIVE frame's variables - the inactive frame's
|
|
1044
|
+
// are uninitialized and must not be read.
|
|
1045
|
+
if (isFreeRotation) {
|
|
1046
|
+
vec3 tRw1 = worldK1; worldK1 = worldIK1; worldIK1 = tRw1; worldB1 = worldK1;
|
|
1047
|
+
} else {
|
|
1048
|
+
vec2 tRl1 = k1; k1 = IK1; IK1 = tRl1; b1 = k1;
|
|
1049
|
+
}
|
|
1050
|
+
Bmul1 = Bmul2;
|
|
1051
|
+
}
|
|
1052
|
+
if (noKnee2 && !noKnee1 && Bmul1 < 0.0) {
|
|
1053
|
+
if (isFreeRotation) {
|
|
1054
|
+
vec3 tRw2 = worldK2; worldK2 = worldIK2; worldIK2 = tRw2; worldB2 = worldK2;
|
|
1055
|
+
} else {
|
|
1056
|
+
vec2 tRl2 = k2; k2 = IK2; IK2 = tRl2; b2 = k2;
|
|
1057
|
+
}
|
|
1058
|
+
Bmul2 = Bmul1;
|
|
1059
|
+
}
|
|
1060
|
+
|
|
938
1061
|
// V coords: V at outer knee (k) = (Bmul + 1) * 0.5; inner = 1 - outer.
|
|
939
1062
|
float Vtop1 = (Bmul1 + 1.0) * 0.5;
|
|
940
1063
|
float Vbot1 = 1.0 - Vtop1;
|
|
@@ -1006,7 +1129,10 @@ void main() {
|
|
|
1006
1129
|
|
|
1007
1130
|
// Free projects the world-space vertex through MVP directly; Faced
|
|
1008
1131
|
// reconstructs clip space from aspect-corrected NDC.
|
|
1009
|
-
if (
|
|
1132
|
+
if (stripDegenerate) {
|
|
1133
|
+
// #236: sub-visual segment - discard the pair entirely.
|
|
1134
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
1135
|
+
} else if (isFreeRotation) {
|
|
1010
1136
|
gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
|
|
1011
1137
|
} else if (nearPlaneClipped) {
|
|
1012
1138
|
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
@@ -1031,6 +1157,9 @@ void main() {
|
|
|
1031
1157
|
|
|
1032
1158
|
// Meta M1: the packedColor u32 LE bytes ARE the normalized RGBA sample.
|
|
1033
1159
|
vColor = fetchMeta(baseTexel + 1.0);
|
|
1160
|
+
// HDR color scale S (RGBS, bloom TDD #175 §13.12): a plain float at offset+60,
|
|
1161
|
+
// read directly from the float DATA texture (t3.a) like gridIndex. S=1 → no-op.
|
|
1162
|
+
vColor.rgb *= t3.a;
|
|
1034
1163
|
|
|
1035
1164
|
// Meta M2: gridConfig — [gridW lo, gridW hi, gridH lo, gridH hi].
|
|
1036
1165
|
vec4 m2 = fetchMeta(baseTexel + 2.0);
|
|
@@ -1077,7 +1206,23 @@ void main() {
|
|
|
1077
1206
|
|
|
1078
1207
|
mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
|
|
1079
1208
|
vec3 worldPos = position + posDisp;
|
|
1080
|
-
gl_Position = mvpMatrix * vec4(worldPos
|
|
1209
|
+
gl_Position = mvpMatrix * vec4(worldPos, 1.0);
|
|
1210
|
+
|
|
1211
|
+
// v1.0-parity near-plane culling for the perspective projection: the
|
|
1212
|
+
// whole quad is discarded when ANY of its 4 corners is behind the near
|
|
1213
|
+
// plane (w < 0.01 == pos.z > 0.99 * z_cam in PerspectiveProjection
|
|
1214
|
+
// terms). w is linear in position, so the corner minimum comes from the
|
|
1215
|
+
// w-row and the corner offsets (x in {-ox, 1-ox}, y in {-oy, 1-oy}).
|
|
1216
|
+
// Without a projection every w is 1.0 and this is a no-op.
|
|
1217
|
+
vec4 wRow = vec4(mvpMatrix[0].w, mvpMatrix[1].w, mvpMatrix[2].w, mvpMatrix[3].w);
|
|
1218
|
+
float wx = dot(wRow.xyz, xaxis);
|
|
1219
|
+
float wy = dot(wRow.xyz, yaxis);
|
|
1220
|
+
float minW = dot(wRow.xyz, position) + wRow.w
|
|
1221
|
+
- origin.x * wx + min(wx, 0.0)
|
|
1222
|
+
- origin.y * wy + min(wy, 0.0);
|
|
1223
|
+
if (minW < 0.01) {
|
|
1224
|
+
gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
|
|
1225
|
+
}
|
|
1081
1226
|
|
|
1082
1227
|
// Texture coordinates with grid + atlas remap
|
|
1083
1228
|
vec2 baseTexCoord = texCoordOf(corner);
|
|
@@ -1152,7 +1297,7 @@ void main() {
|
|
|
1152
1297
|
// world alpha scales the whole RGBA (mirror of the ES 3.00 shader).
|
|
1153
1298
|
gl_FragColor = texColor * vColor * uWorldAlpha;
|
|
1154
1299
|
}
|
|
1155
|
-
`,
|
|
1300
|
+
`, A = class A {
|
|
1156
1301
|
constructor(e, t) {
|
|
1157
1302
|
n(this, "neutrino");
|
|
1158
1303
|
// On a WebGL1 context this actually holds a WebGLRenderingContext; the
|
|
@@ -1165,7 +1310,7 @@ void main() {
|
|
|
1165
1310
|
n(this, "_vao", null);
|
|
1166
1311
|
// WebGL1 render path state (TDD_js-v1.1-webgl1.md)
|
|
1167
1312
|
n(this, "_isWebGL1", !1);
|
|
1168
|
-
n(this, "_maxBatchTextures",
|
|
1313
|
+
n(this, "_maxBatchTextures", A.MAX_BATCH_TEXTURES);
|
|
1169
1314
|
n(this, "_hasElementIndexUint", !1);
|
|
1170
1315
|
n(this, "_vaoExt", null);
|
|
1171
1316
|
n(this, "_aIdBuffer", null);
|
|
@@ -1325,7 +1470,7 @@ void main() {
|
|
|
1325
1470
|
const r = e.getParameter(e.MAX_COMBINED_TEXTURE_IMAGE_UNITS), o = e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS);
|
|
1326
1471
|
this._maxBatchTextures = Math.max(
|
|
1327
1472
|
1,
|
|
1328
|
-
Math.min(
|
|
1473
|
+
Math.min(A.MAX_BATCH_TEXTURES, r - 2, o)
|
|
1329
1474
|
);
|
|
1330
1475
|
}
|
|
1331
1476
|
/**
|
|
@@ -1354,29 +1499,29 @@ void main() { gl_FragColor = vC; }
|
|
|
1354
1499
|
e.activeTexture(e.TEXTURE0);
|
|
1355
1500
|
const u = e.getParameter(e.TEXTURE_BINDING_2D);
|
|
1356
1501
|
e.activeTexture(e.TEXTURE1);
|
|
1357
|
-
const _ = e.getParameter(e.TEXTURE_BINDING_2D),
|
|
1358
|
-
let
|
|
1359
|
-
const
|
|
1502
|
+
const _ = e.getParameter(e.TEXTURE_BINDING_2D), y = e.isEnabled(e.BLEND), E = e.isEnabled(e.DEPTH_TEST), I = e.isEnabled(e.SCISSOR_TEST);
|
|
1503
|
+
let m = null, w = null, B = null, f = null, v = null, g = null, d = -1, b = !1, x = null, h = 4, U = 0, p = 0, R = 0, D = !1, S = !1;
|
|
1504
|
+
const T = new Uint8Array(4);
|
|
1360
1505
|
try {
|
|
1361
|
-
|
|
1362
|
-
const C = (
|
|
1506
|
+
m = e.createProgram(), e.attachShader(m, this._compileShader(e.VERTEX_SHADER, t)), e.attachShader(m, this._compileShader(e.FRAGMENT_SHADER, a)), e.linkProgram(m);
|
|
1507
|
+
const C = (P, F) => {
|
|
1363
1508
|
const M = e.createTexture();
|
|
1364
|
-
return e.bindTexture(e.TEXTURE_2D, M), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, 1, 1, 0, e.RGBA,
|
|
1509
|
+
return e.bindTexture(e.TEXTURE_2D, M), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MIN_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_MAG_FILTER, e.NEAREST), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_S, e.CLAMP_TO_EDGE), e.texParameteri(e.TEXTURE_2D, e.TEXTURE_WRAP_T, e.CLAMP_TO_EDGE), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, 1, 1, 0, e.RGBA, P, F), M;
|
|
1365
1510
|
};
|
|
1366
|
-
e.activeTexture(e.TEXTURE0),
|
|
1511
|
+
e.activeTexture(e.TEXTURE0), w = C(e.FLOAT, new Float32Array([1, 0.5, 0.25, 1])), B = C(e.UNSIGNED_BYTE, new Uint8Array([51, 102, 153, 255])), f = e.createTexture(), e.bindTexture(e.TEXTURE_2D, f), e.texImage2D(e.TEXTURE_2D, 0, e.RGBA, 1, 1, 0, e.RGBA, e.UNSIGNED_BYTE, null), v = e.createFramebuffer(), e.bindFramebuffer(e.FRAMEBUFFER, v), e.framebufferTexture2D(e.FRAMEBUFFER, e.COLOR_ATTACHMENT0, e.TEXTURE_2D, f, 0), g = e.createBuffer(), e.bindBuffer(e.ARRAY_BUFFER, g), e.bufferData(e.ARRAY_BUFFER, new Float32Array([0]), e.STATIC_DRAW), S = e.getProgramParameter(m, e.LINK_STATUS), S && (e.useProgram(m), e.activeTexture(e.TEXTURE0), e.bindTexture(e.TEXTURE_2D, w), e.activeTexture(e.TEXTURE1), e.bindTexture(e.TEXTURE_2D, B), e.uniform1i(e.getUniformLocation(m, "uF"), 0), e.uniform1i(e.getUniformLocation(m, "uB"), 1), d = e.getAttribLocation(m, "aId"), b = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_ENABLED), x = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING), h = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_SIZE), U = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_TYPE), D = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_NORMALIZED), p = e.getVertexAttrib(d, e.VERTEX_ATTRIB_ARRAY_STRIDE), R = e.getVertexAttribOffset(d, e.VERTEX_ATTRIB_ARRAY_POINTER), e.enableVertexAttribArray(d), e.vertexAttribPointer(d, 1, e.FLOAT, !1, 0, 0), e.viewport(0, 0, 1, 1), e.disable(e.BLEND), e.disable(e.DEPTH_TEST), e.disable(e.SCISSOR_TEST), e.drawArrays(e.POINTS, 0, 1), e.readPixels(0, 0, 1, 1, e.RGBA, e.UNSIGNED_BYTE, T), S = Math.abs(T[0] - 51) <= 2 && Math.abs(T[1] - 51) <= 2 && Math.abs(T[2] - 38) <= 2);
|
|
1367
1512
|
} finally {
|
|
1368
|
-
d >= 0 && (
|
|
1513
|
+
d >= 0 && (x && (e.bindBuffer(e.ARRAY_BUFFER, x), e.vertexAttribPointer(
|
|
1369
1514
|
d,
|
|
1370
1515
|
h,
|
|
1371
|
-
|
|
1516
|
+
U,
|
|
1372
1517
|
D,
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
)),
|
|
1518
|
+
p,
|
|
1519
|
+
R
|
|
1520
|
+
)), b ? e.enableVertexAttribArray(d) : e.disableVertexAttribArray(d)), v && e.deleteFramebuffer(v), f && e.deleteTexture(f), w && e.deleteTexture(w), B && e.deleteTexture(B), g && e.deleteBuffer(g), m && e.deleteProgram(m), e.bindFramebuffer(e.FRAMEBUFFER, c), e.bindBuffer(e.ARRAY_BUFFER, l), e.useProgram(o), e.activeTexture(e.TEXTURE0), e.bindTexture(e.TEXTURE_2D, u), e.activeTexture(e.TEXTURE1), e.bindTexture(e.TEXTURE_2D, _), e.activeTexture(i), y && e.enable(e.BLEND), E && e.enable(e.DEPTH_TEST), I && e.enable(e.SCISSOR_TEST), e.viewport(r[0], r[1], r[2], r[3]);
|
|
1376
1521
|
}
|
|
1377
|
-
if (!
|
|
1522
|
+
if (!S)
|
|
1378
1523
|
throw new Error(
|
|
1379
|
-
`NeutrinoParticles (js-v1.1): WebGL1 vertex texture fetch smoke test failed (got ${
|
|
1524
|
+
`NeutrinoParticles (js-v1.1): WebGL1 vertex texture fetch smoke test failed (got ${T[0]},${T[1]},${T[2]}); WebGL2 or WebGL1 with working vertex float textures is required.`
|
|
1380
1525
|
);
|
|
1381
1526
|
}
|
|
1382
1527
|
_initShader() {
|
|
@@ -1384,7 +1529,7 @@ void main() { gl_FragColor = vC; }
|
|
|
1384
1529
|
if (this._shaderProgram = e.createProgram(), e.attachShader(this._shaderProgram, r), e.attachShader(this._shaderProgram, o), e.linkProgram(this._shaderProgram), !e.getProgramParameter(this._shaderProgram, e.LINK_STATUS))
|
|
1385
1530
|
throw new Error("Shader link error: " + e.getProgramInfoLog(this._shaderProgram));
|
|
1386
1531
|
e.deleteShader(r), e.deleteShader(o), this._uDataTexture = e.getUniformLocation(this._shaderProgram, "uDataTexture"), this._uCameraRight = e.getUniformLocation(this._shaderProgram, "uCameraRight"), this._uCameraUp = e.getUniformLocation(this._shaderProgram, "uCameraUp"), this._uCameraDir = e.getUniformLocation(this._shaderProgram, "uCameraDir"), this._uViewProjMatrix = e.getUniformLocation(this._shaderProgram, "uViewProjMatrix"), this._uModelMatrix = e.getUniformLocation(this._shaderProgram, "uModelMatrix"), this._uViewportAspect = e.getUniformLocation(this._shaderProgram, "uViewportAspect"), this._uWorldAlpha = e.getUniformLocation(this._shaderProgram, "uWorldAlpha"), this._isWebGL1 ? (this._uMetaTexture = e.getUniformLocation(this._shaderProgram, "uMetaTexture"), this._uDataTexSize = e.getUniformLocation(this._shaderProgram, "uDataTexSize"), this._aIdLocation = e.getAttribLocation(this._shaderProgram, "aId")) : this._uDataTextureWidth = e.getUniformLocation(this._shaderProgram, "uDataTextureWidth"), this._uTextures = [], this._uTexRemaps = [];
|
|
1387
|
-
for (let l = 0; l <
|
|
1532
|
+
for (let l = 0; l < A.MAX_BATCH_TEXTURES; l++)
|
|
1388
1533
|
this._uTextures.push(e.getUniformLocation(this._shaderProgram, `uTextures[${l}]`)), this._uTexRemaps.push(e.getUniformLocation(this._shaderProgram, `uTexRemaps[${l}]`));
|
|
1389
1534
|
}
|
|
1390
1535
|
_compileShader(e, t) {
|
|
@@ -1396,8 +1541,8 @@ void main() { gl_FragColor = vC; }
|
|
|
1396
1541
|
return r;
|
|
1397
1542
|
}
|
|
1398
1543
|
};
|
|
1399
|
-
n(
|
|
1400
|
-
let NeutrinoContext =
|
|
1544
|
+
n(A, "MAX_BATCH_TEXTURES", 8);
|
|
1545
|
+
let NeutrinoContext = A;
|
|
1401
1546
|
class GamePlugin extends Phaser.Plugins.BasePlugin {
|
|
1402
1547
|
constructor() {
|
|
1403
1548
|
super(...arguments);
|
|
@@ -1729,6 +1874,9 @@ class DrawBatch {
|
|
|
1729
1874
|
class NeutrinoRenderer {
|
|
1730
1875
|
constructor() {
|
|
1731
1876
|
n(this, "_orthoMatrix", new Float32Array(16));
|
|
1877
|
+
n(this, "_perspMatrix", new Float32Array(16));
|
|
1878
|
+
n(this, "_vpMatrix", new Float32Array(16));
|
|
1879
|
+
n(this, "_projFrame", { x: 0, y: 0, width: 0, height: 0 });
|
|
1732
1880
|
n(this, "_modelMatrix", new Float32Array(16));
|
|
1733
1881
|
n(this, "_texSlotMap", /* @__PURE__ */ new Map());
|
|
1734
1882
|
n(this, "_batchPool");
|
|
@@ -1739,66 +1887,70 @@ class NeutrinoRenderer {
|
|
|
1739
1887
|
}
|
|
1740
1888
|
render(e, t, a, r, o, l, c) {
|
|
1741
1889
|
if (l === 0) return;
|
|
1742
|
-
const i = e.gl, u = e.isWebGL1, _ = e.maxBatchTextures,
|
|
1890
|
+
const i = e.gl, u = e.isWebGL1, _ = e.maxBatchTextures, y = c.renderStyles, E = c.materials, I = u ? t.meta : null;
|
|
1743
1891
|
I && I.build(r, l), this._batchCount = 0;
|
|
1744
|
-
const
|
|
1892
|
+
const m = this._texSlotMap;
|
|
1745
1893
|
for (; this._batchPool.length < o.length; )
|
|
1746
1894
|
this._batchPool.push(new DrawBatch());
|
|
1747
|
-
let
|
|
1748
|
-
for (;
|
|
1749
|
-
const
|
|
1750
|
-
|
|
1751
|
-
let v =
|
|
1752
|
-
for (; v < o.length && this._resolveBlend(o[v].blendMode, E) ===
|
|
1753
|
-
const
|
|
1754
|
-
if (!
|
|
1755
|
-
if (
|
|
1756
|
-
const
|
|
1757
|
-
|
|
1895
|
+
let w = 0;
|
|
1896
|
+
for (; w < o.length; ) {
|
|
1897
|
+
const f = this._batchPool[this._batchCount];
|
|
1898
|
+
f.reset(), f.blendMode = this._resolveBlend(o[w].blendMode, E), f.startParticle = o[w].startParticleIndex, m.clear();
|
|
1899
|
+
let v = w;
|
|
1900
|
+
for (; v < o.length && this._resolveBlend(o[v].blendMode, E) === f.blendMode; ) {
|
|
1901
|
+
const g = y[o[v].renderStyleIndex].textureIndices[0];
|
|
1902
|
+
if (!m.has(g)) {
|
|
1903
|
+
if (m.size >= _) break;
|
|
1904
|
+
const x = m.size;
|
|
1905
|
+
m.set(g, x), f.glTextures[x] = c.glTextures[g] || null, f.remaps[x] = c.remaps[g] || null, f.numTextures = x + 1;
|
|
1758
1906
|
}
|
|
1759
|
-
const d =
|
|
1907
|
+
const d = m.get(g), b = o[v];
|
|
1760
1908
|
if (I)
|
|
1761
|
-
I.patchSlot(
|
|
1762
|
-
else
|
|
1763
|
-
for (let
|
|
1764
|
-
const h = (
|
|
1765
|
-
a.setUint32(h,
|
|
1909
|
+
I.patchSlot(b.startParticleIndex, b.numParticles, d);
|
|
1910
|
+
else if (d !== 0)
|
|
1911
|
+
for (let x = 0; x < b.numParticles; x++) {
|
|
1912
|
+
const h = (b.startParticleIndex + x) * BYTES_PER_PARTICLE, U = a.getUint32(h, !0);
|
|
1913
|
+
a.setUint32(h, U & 4294967040 | d, !0);
|
|
1766
1914
|
}
|
|
1767
|
-
|
|
1915
|
+
f.numParticles += b.numParticles, v++;
|
|
1768
1916
|
}
|
|
1769
|
-
this._batchCount++,
|
|
1917
|
+
this._batchCount++, w = v;
|
|
1770
1918
|
}
|
|
1771
1919
|
const B = i.isEnabled(i.SCISSOR_TEST);
|
|
1772
1920
|
try {
|
|
1773
1921
|
if (i.disable(i.DEPTH_TEST), i.disable(i.STENCIL_TEST), i.disable(i.SCISSOR_TEST), i.disable(i.CULL_FACE), i.depthMask(!1), i.colorMask(!0, !0, !0, !0), i.useProgram(e.shaderProgram), u) {
|
|
1774
|
-
const
|
|
1775
|
-
|
|
1922
|
+
const p = t;
|
|
1923
|
+
p.upload(l, DATA_TEXTURE_UNIT, META_TEXTURE_UNIT), i.uniform1i(e.uDataTexture, DATA_TEXTURE_UNIT), i.uniform1i(e.uMetaTexture, META_TEXTURE_UNIT), i.uniform2f(e.uDataTexSize, p.textureWidth, p.textureHeight);
|
|
1776
1924
|
} else {
|
|
1777
|
-
const
|
|
1778
|
-
|
|
1925
|
+
const p = t;
|
|
1926
|
+
p.uploadAndBind(DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTexture, DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTextureWidth, p.textureWidth);
|
|
1779
1927
|
}
|
|
1780
1928
|
i.uniform3f(e.uCameraRight, 1, 0, 0), i.uniform3f(e.uCameraUp, 0, -1, 0), i.uniform3f(e.uCameraDir, 0, 0, -1);
|
|
1781
|
-
const
|
|
1782
|
-
d.fill(0), d[0] = 2 /
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
const
|
|
1929
|
+
const f = c.viewportWidth, v = c.viewportHeight, g = c.zoom || 1, d = this._orthoMatrix;
|
|
1930
|
+
if (d.fill(0), d[0] = 2 / f * g, d[5] = -2 / v * g, d[10] = 0, d[15] = 1, d[12] = -1 - 2 / f * g * c.scrollX, d[13] = 1 + 2 / v * g * c.scrollY, c.projection) {
|
|
1931
|
+
const p = this._projFrame;
|
|
1932
|
+
p.x = 0, p.y = 0, p.width = f, p.height = v, c.projection.setScreenFrame(p), c.projection.writeMatrix(this._perspMatrix), this._mulMatrix(this._vpMatrix, d, this._perspMatrix), i.uniformMatrix4fv(e.uViewProjMatrix, !1, this._vpMatrix);
|
|
1933
|
+
} else
|
|
1934
|
+
i.uniformMatrix4fv(e.uViewProjMatrix, !1, d);
|
|
1935
|
+
const b = v > 0 ? f / v : 1;
|
|
1936
|
+
e.uViewportAspect && i.uniform1f(e.uViewportAspect, b), e.uWorldAlpha && i.uniform1f(e.uWorldAlpha, c.worldAlpha);
|
|
1937
|
+
const x = c.model, h = this._modelMatrix;
|
|
1938
|
+
h[0] = x.a, h[1] = x.b, h[2] = 0, h[3] = 0, h[4] = x.c, h[5] = x.d, h[6] = 0, h[7] = 0, h[8] = 0, h[9] = 0, h[10] = 1, h[11] = 0, h[12] = x.tx, h[13] = x.ty, h[14] = 0, h[15] = 1, i.uniformMatrix4fv(e.uModelMatrix, !1, h), u ? (e.unbindVao(), i.bindBuffer(i.ARRAY_BUFFER, e.aIdBuffer), i.enableVertexAttribArray(e.aIdLocation), i.vertexAttribPointer(e.aIdLocation, 1, i.FLOAT, !1, 0, 0), i.bindBuffer(i.ELEMENT_ARRAY_BUFFER, e.indexBuffer)) : i.bindVertexArray(e.vao);
|
|
1939
|
+
const U = u ? PARTICLE_TEXTURE_START_UNIT_GL1 : PARTICLE_TEXTURE_START_UNIT;
|
|
1788
1940
|
i.enable(i.BLEND);
|
|
1789
|
-
for (let
|
|
1790
|
-
const
|
|
1791
|
-
this._applyBlendMode(i,
|
|
1792
|
-
for (let
|
|
1793
|
-
const C =
|
|
1794
|
-
i.activeTexture(i.TEXTURE0 + C), i.bindTexture(i.TEXTURE_2D,
|
|
1795
|
-
const
|
|
1796
|
-
|
|
1941
|
+
for (let p = 0; p < this._batchCount; p++) {
|
|
1942
|
+
const R = this._batchPool[p];
|
|
1943
|
+
this._applyBlendMode(i, R.blendMode);
|
|
1944
|
+
for (let T = 0; T < R.numTextures; T++) {
|
|
1945
|
+
const C = U + T;
|
|
1946
|
+
i.activeTexture(i.TEXTURE0 + C), i.bindTexture(i.TEXTURE_2D, R.glTextures[T]), i.uniform1i(e.uTextures[T], C);
|
|
1947
|
+
const P = R.remaps[T];
|
|
1948
|
+
P ? i.uniform4f(e.uTexRemaps[T], P.x, P.y, P.width, P.height) : i.uniform4f(e.uTexRemaps[T], 0, 0, 1, 1);
|
|
1797
1949
|
}
|
|
1798
|
-
const D =
|
|
1950
|
+
const D = R.startParticle * 6, S = R.numParticles * 6;
|
|
1799
1951
|
i.drawElements(
|
|
1800
1952
|
i.TRIANGLES,
|
|
1801
|
-
|
|
1953
|
+
S,
|
|
1802
1954
|
e.indexType,
|
|
1803
1955
|
D * (e.indexType === i.UNSIGNED_INT ? 4 : 2)
|
|
1804
1956
|
);
|
|
@@ -1813,6 +1965,13 @@ class NeutrinoRenderer {
|
|
|
1813
1965
|
// materials[], NOT the blend itself — without this deref a single-material effect
|
|
1814
1966
|
// (materialIndex 0) would always render as materials[0]'s slot 0 (Normal). Falls back to
|
|
1815
1967
|
// the raw value if materials is missing/out of range (mirror of cocos _resolveBlendMode).
|
|
1968
|
+
// Column-major 4x4 multiply: out = a * b. Preallocated outputs only.
|
|
1969
|
+
_mulMatrix(e, t, a) {
|
|
1970
|
+
for (let r = 0; r < 16; r += 4) {
|
|
1971
|
+
const o = a[r], l = a[r + 1], c = a[r + 2], i = a[r + 3];
|
|
1972
|
+
e[r] = t[0] * o + t[4] * l + t[8] * c + t[12] * i, e[r + 1] = t[1] * o + t[5] * l + t[9] * c + t[13] * i, e[r + 2] = t[2] * o + t[6] * l + t[10] * c + t[14] * i, e[r + 3] = t[3] * o + t[7] * l + t[11] * c + t[15] * i;
|
|
1973
|
+
}
|
|
1974
|
+
}
|
|
1816
1975
|
_resolveBlend(e, t) {
|
|
1817
1976
|
return t && e >= 0 && e < t.length ? t[e] : e;
|
|
1818
1977
|
}
|
|
@@ -1858,6 +2017,8 @@ class Effect extends Phaser.GameObjects.GameObject {
|
|
|
1858
2017
|
n(this, "alpha", 1);
|
|
1859
2018
|
n(this, "depth", 0);
|
|
1860
2019
|
n(this, "blendMode", Phaser.BlendModes.NORMAL);
|
|
2020
|
+
/** Optional v1.0-parity perspective projection (Projection3D). */
|
|
2021
|
+
n(this, "projection");
|
|
1861
2022
|
n(this, "_dataTextureManager", null);
|
|
1862
2023
|
n(this, "_worldPosition");
|
|
1863
2024
|
n(this, "_worldScaledPosition");
|
|
@@ -1867,7 +2028,7 @@ class Effect extends Phaser.GameObjects.GameObject {
|
|
|
1867
2028
|
n(this, "_tempMatrix2");
|
|
1868
2029
|
n(this, "_unpauseOnUpdateRender");
|
|
1869
2030
|
n(this, "_glTexturesScratch", []);
|
|
1870
|
-
this.gamePlugin = a.plugins.get("neutrino"), this.effectModel = t, this.x = r.position[0], this.y = r.position[1], this.z = r.position[2], this.angle = r.angle, this.scaleX = r.scale, this.scaleY = r.scale, this._worldPosition = new Phaser.Math.Vector2(), this._worldScaledPosition = [0, 0, 0], this._worldScale = 1, this._tempMatrix1 = new Phaser.GameObjects.Components.TransformMatrix(), this._tempMatrix2 = new Phaser.GameObjects.Components.TransformMatrix(), this._unpauseOnUpdateRender = r.pause === Pause.BEFORE_UPDATE_OR_RENDER, this._updateWorldTransform();
|
|
2031
|
+
this.gamePlugin = a.plugins.get("neutrino"), this.effectModel = t, this.x = r.position[0], this.y = r.position[1], this.z = r.position[2], this.angle = r.angle, this.scaleX = r.scale, this.scaleY = r.scale, this.projection = r.projection, this._worldPosition = new Phaser.Math.Vector2(), this._worldScaledPosition = [0, 0, 0], this._worldScale = 1, this._tempMatrix1 = new Phaser.GameObjects.Components.TransformMatrix(), this._tempMatrix2 = new Phaser.GameObjects.Components.TransformMatrix(), this._unpauseOnUpdateRender = r.pause === Pause.BEFORE_UPDATE_OR_RENDER, this._updateWorldTransform();
|
|
1871
2032
|
const o = {
|
|
1872
2033
|
paused: r.pause !== Pause.NO,
|
|
1873
2034
|
generatorsPaused: r.generatorsPaused
|
|
@@ -1906,7 +2067,7 @@ class Effect extends Phaser.GameObjects.GameObject {
|
|
|
1906
2067
|
_.length = u;
|
|
1907
2068
|
for (let E = 0; E < u; ++E)
|
|
1908
2069
|
_[E] = this.effectModel.glTexture(E);
|
|
1909
|
-
const
|
|
2070
|
+
const y = this._worldScale;
|
|
1910
2071
|
try {
|
|
1911
2072
|
sharedRenderer.render(
|
|
1912
2073
|
o,
|
|
@@ -1916,13 +2077,14 @@ class Effect extends Phaser.GameObjects.GameObject {
|
|
|
1916
2077
|
c,
|
|
1917
2078
|
i,
|
|
1918
2079
|
{
|
|
1919
|
-
model: { a:
|
|
2080
|
+
model: { a: y, b: 0, c: 0, d: y, tx: 0, ty: 0 },
|
|
1920
2081
|
viewportWidth: t.width,
|
|
1921
2082
|
viewportHeight: t.height,
|
|
1922
2083
|
scrollX: r.scrollX * this.scrollFactorX,
|
|
1923
2084
|
scrollY: r.scrollY * this.scrollFactorY,
|
|
1924
2085
|
zoom: r.zoom,
|
|
1925
2086
|
worldAlpha: this.alpha,
|
|
2087
|
+
projection: this.projection,
|
|
1926
2088
|
glTextures: _,
|
|
1927
2089
|
remaps: this.effectModel.texturesRemap,
|
|
1928
2090
|
renderStyles: this.effect.model.renderStyles,
|
|
@@ -1960,6 +2122,19 @@ class Effect extends Phaser.GameObjects.GameObject {
|
|
|
1960
2122
|
setPropertyInAllEmitters(t, a) {
|
|
1961
2123
|
this.ready && this.effect.setPropertyInAllEmitters(t, a);
|
|
1962
2124
|
}
|
|
2125
|
+
setPropertyInEmitter(t, a, r) {
|
|
2126
|
+
this.ready && this.effect.setPropertyInEmitter(t, a, r);
|
|
2127
|
+
}
|
|
2128
|
+
getEmitterPropertyValue(t, a) {
|
|
2129
|
+
if (this.ready)
|
|
2130
|
+
return a === void 0 ? this.effect.getEmitterPropertyValue(t) : this.effect.getEmitterPropertyValue(t, a);
|
|
2131
|
+
}
|
|
2132
|
+
hasEmitterProperty(t, a) {
|
|
2133
|
+
return this.ready ? a === void 0 ? this.effect.hasEmitterProperty(t) : this.effect.hasEmitterProperty(t, a) : !1;
|
|
2134
|
+
}
|
|
2135
|
+
getEmitterProperties() {
|
|
2136
|
+
return this.ready ? this.effect.getEmitterProperties() : [];
|
|
2137
|
+
}
|
|
1963
2138
|
getNumParticles() {
|
|
1964
2139
|
return this.ready ? this.effect.getNumParticles() : 0;
|
|
1965
2140
|
}
|
|
@@ -2063,6 +2238,19 @@ class PerspectiveProjection {
|
|
|
2063
2238
|
_getScale(e) {
|
|
2064
2239
|
return this._z / (this._z - e[2]);
|
|
2065
2240
|
}
|
|
2241
|
+
/**
|
|
2242
|
+
* Writes the projection as a 4x4 column-major matrix operating in screen
|
|
2243
|
+
* space, for the GPU render path. Homogeneous equivalent of
|
|
2244
|
+
* transformPosition(): clip.xy = p.xy - center * (p.z / z),
|
|
2245
|
+
* clip.w = 1 - p.z / z; the z row is zeroed (no depth on this path).
|
|
2246
|
+
* setScreenFrame() must have been called before.
|
|
2247
|
+
*
|
|
2248
|
+
* @param {Float32Array} out 16-element column-major output matrix.
|
|
2249
|
+
*/
|
|
2250
|
+
writeMatrix(e) {
|
|
2251
|
+
const t = this._z, a = Number.isFinite(t) && t > 0 ? 1 / t : 0;
|
|
2252
|
+
e.fill(0), e[0] = 1, e[5] = 1, e[8] = -this._screenPosX * a, e[9] = -this._screenPosY * a, e[11] = -a, e[15] = 1;
|
|
2253
|
+
}
|
|
2066
2254
|
}
|
|
2067
2255
|
let _installed = !1;
|
|
2068
2256
|
function installPlugin() {
|