@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.
@@ -1,4 +1,4 @@
1
- "use strict";var K=Object.defineProperty;var N=(s,e,t)=>e in s?K(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var n=(s,e,t)=>N(s,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Phaser=require("phaser"),Neutrino=require("@neutrinoparticles/js-v1.1");function _interopNamespaceDefault(s){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const t in s)if(t!=="default"){const a=Object.getOwnPropertyDescriptor(s,t);Object.defineProperty(e,t,a.get?a:{enumerable:!0,get:()=>s[t]})}}return e.default=s,Object.freeze(e)}const Neutrino__namespace=_interopNamespaceDefault(Neutrino),vertexShaderSource=`#version 300 es
1
+ "use strict";var K=Object.defineProperty;var z=(s,e,t)=>e in s?K(s,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):s[e]=t;var a=(s,e,t)=>z(s,typeof e!="symbol"?e+"":e,t);Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const Phaser=require("phaser"),Neutrino=require("@neutrinoparticles/js-v1.1");function _interopNamespaceDefault(s){const e=Object.create(null,{[Symbol.toStringTag]:{value:"Module"}});if(s){for(const t in s)if(t!=="default"){const n=Object.getOwnPropertyDescriptor(s,t);Object.defineProperty(e,t,n.get?n:{enumerable:!0,get:()=>s[t]})}}return e.default=s,Object.freeze(e)}const Neutrino__namespace=_interopNamespaceDefault(Neutrino),vertexShaderSource=`#version 300 es
2
2
  precision highp float;
3
3
 
4
4
  uniform sampler2D uDataTexture;
@@ -49,7 +49,8 @@ vec4 fetchTexel(int globalTexelIndex) {
49
49
  }
50
50
 
51
51
  // Strip geometry helpers (port of GPU33 makeScreenSpaceStrip).
52
- // 2D screen-space variant PIXI7 renderer is 2D, world Z is dropped at gl_Position.
52
+ // 2D screen-space variant used by the Faced strip frame; world Z reaches
53
+ // gl_Position only through uViewProjMatrix (ortho or v1.0-parity perspective).
53
54
  vec2 stripOrtho(vec2 v) {
54
55
  return vec2(-v.y, v.x);
55
56
  }
@@ -86,9 +87,9 @@ void main() {
86
87
  // 0 (Faced): screen-space ortho to dir12 — equivalent to camera-faced
87
88
  // billboard normal in a 2D camera setup.
88
89
  // 1 (Free): the quaternion stored in texel 2 nominally encodes a 3D
89
- // orientation. The current PIXI7 renderer is fundamentally
90
- // 2D (worldPos.z is dropped at gl_Position for quads), so
91
- // the quat is read but its 3D y-axis is projected to screen
90
+ // orientation. The PIXI7 renderer has no true 3D camera
91
+ // (uViewProjMatrix is ortho or the v1.0-parity perspective),
92
+ // so the quat is read but its 3D y-axis is projected to screen
92
93
  // and used as the section normal. When the projection
93
94
  // degenerates (looking along the y-axis) we fall back to
94
95
  // the screen-space ortho. Real 3D free-rotation requires a
@@ -123,6 +124,10 @@ void main() {
123
124
  float((packedColorNex >> 8u) & 0xFFu) / 255.0,
124
125
  float((packedColorNex >> 16u) & 0xFFu) / 255.0,
125
126
  float((packedColorNex >> 24u) & 0xFFu) / 255.0);
127
+ // HDR color scale S (RGBS, bloom TDD #175 §13.12): offset+60 (t3.a) per-particle
128
+ // float scale; multiply BEFORE the cross-fade. S=1 for LDR (no-op).
129
+ color1.rgb *= (isCur ? t3.a : ts3.a);
130
+ color2.rgb *= (isCur ? ts3.a : t3.a);
126
131
 
127
132
  // neighborIdxP0 / neighborIdxP3 are stored on every meta of a pair
128
133
  // (both meta_cur and meta_nex hold the same pair of indices), so we
@@ -169,6 +174,10 @@ void main() {
169
174
  // by the sign of w). Forces a discard outside the [-w,w] cube at the end.
170
175
  bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
171
176
 
177
+ // #236: set when the segment itself is degenerate (much shorter than
178
+ // the ribbon half-width) — the pair is discarded like nearPlaneClipped.
179
+ bool stripDegenerate = false;
180
+
172
181
  // Aspect correction so the 2D Faced ortho gives visually-perpendicular
173
182
  // dirs on non-square viewports (inverted before gl_Position).
174
183
  vec2 aspectMul = vec2(1.0, 1.0 / max(uViewportAspect, 1e-6));
@@ -206,9 +215,15 @@ void main() {
206
215
  float wlen01 = length(wp01);
207
216
  float wlen12 = length(wp12);
208
217
  float wlen23 = length(wp23);
218
+ // #236: degeneracy guards at visual scale (mirror of GPU33).
219
+ // A segment much shorter than the ribbon half-width cannot define
220
+ // a visually meaningful direction; a degenerate neighbour falls
221
+ // back to the straight cap instead of a noise knee.
222
+ float wDegenEps = max(max(abs(halfSize1), abs(halfSize2)) * 0.02, 1e-6);
223
+ if (wlen12 < wDegenEps) stripDegenerate = true;
209
224
  vec3 wdir12 = (wlen12 > 1e-6) ? (wp12 / wlen12) : vec3(1.0, 0.0, 0.0);
210
- vec3 wdir01 = (wlen01 > 1e-6) ? (wp01 / wlen01) : wdir12;
211
- vec3 wdir23 = (wlen23 > 1e-6) ? (wp23 / wlen23) : wdir12;
225
+ vec3 wdir01 = (wlen01 > wDegenEps) ? (wp01 / wlen01) : wdir12;
226
+ vec3 wdir23 = (wlen23 > wDegenEps) ? (wp23 / wlen23) : wdir12;
212
227
  noKnee1 = dot(wdir01, wdir12) > 0.999;
213
228
  noKnee2 = dot(wdir12, wdir23) > 0.999;
214
229
  pathLen12 = wlen12;
@@ -220,9 +235,12 @@ void main() {
220
235
  vec3 K1 = Kmul1 * normalize(cross(wdir01 + wdir12, n1));
221
236
  Bmul1 = sign(dot(B1raw, K1));
222
237
  vec3 B1 = B1raw * Bmul1;
223
- float ik1 = halfSize1 / max(dot(B1, K1), 1e-6);
224
- if (dot(-wdir01, ik1 * -K1) > wlen01) ik1 = wlen01;
225
- if (dot( wdir12, ik1 * -K1) > wlen12) ik1 = wlen12;
238
+ // #236: inner vertex at the ribbon half-width along the
239
+ // bisector, symmetric to the outer vertex (mirror of GPU33).
240
+ // The old true-miter-with-neighbour-length-clamp cut an
241
+ // uncovered wedge out of sharp bends whenever halfSize
242
+ // exceeded the particle spacing.
243
+ float ik1 = halfSize1;
226
244
  worldK1 = p1 + K1 * halfSize1;
227
245
  worldB1 = p1 + B1 * halfSize1;
228
246
  worldIK1 = p1 - K1 * ik1;
@@ -243,9 +261,8 @@ void main() {
243
261
  vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
244
262
  Bmul2 = sign(dot(B2raw, K2));
245
263
  vec3 B2 = B2raw * Bmul2;
246
- float ik2 = halfSize2 / max(dot(B2, K2), 1e-6);
247
- if (dot(-wdir12, ik2 * -K2) > wlen12) ik2 = wlen12;
248
- if (dot( wdir23, ik2 * -K2) > wlen23) ik2 = wlen23;
264
+ // #236: see ik1 inner vertex at the ribbon half-width.
265
+ float ik2 = halfSize2;
249
266
  worldK2 = p2 + K2 * halfSize2;
250
267
  worldB2 = p2 + B2 * halfSize2;
251
268
  worldIK2 = p2 - K2 * ik2;
@@ -262,10 +279,16 @@ void main() {
262
279
  // Faced: screen-space NDC frame around dir12.
263
280
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
264
281
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
265
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
266
282
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
267
283
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
268
- vec2 ndc3 = (sp3.xy / sp3.w) * aspectMul;
284
+ // Neighbor anchors behind the near plane produce garbage NDC after
285
+ // the perspective divide (possible only with Projection3D, where w
286
+ // varies); collapse them onto their segment anchor so the existing
287
+ // zero-length degeneracy path (dir01 = dir12 / dir23 = dir12)
288
+ // handles the knee instead of a poisoned miter. spU1/spU2 need no
289
+ // guard: uCameraUp has zero Z, so their w equals sp1.w / sp2.w.
290
+ vec2 ndc0 = (sp0.w < 1e-3) ? ndc1 : (sp0.xy / sp0.w) * aspectMul;
291
+ vec2 ndc3 = (sp3.w < 1e-3) ? ndc2 : (sp3.xy / sp3.w) * aspectMul;
269
292
 
270
293
  vec2 d01 = ndc1 - ndc0;
271
294
  vec2 d12 = ndc2 - ndc1;
@@ -273,12 +296,6 @@ void main() {
273
296
  float len01 = length(d01);
274
297
  float len12 = length(d12);
275
298
  float len23 = length(d23);
276
- vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
277
- vec2 dir01 = (len01 > 1e-6) ? (d01 / len01) : dir12;
278
- vec2 dir23 = (len23 > 1e-6) ? (d23 / len23) : dir12;
279
- noKnee1 = dot(dir01, dir12) > 0.999;
280
- noKnee2 = dot(dir12, dir23) > 0.999;
281
- pathLen12 = len12;
282
299
 
283
300
  // Section magnitude: project camera_up*halfSize per anchor into NDC.
284
301
  vec4 spU1 = mvpMatrix * vec4(p1 + uCameraUp * halfSize1, 1.0);
@@ -286,15 +303,25 @@ void main() {
286
303
  float ndcHalfSize1 = length((spU1.xy / spU1.w) * aspectMul - ndc1);
287
304
  float ndcHalfSize2 = length((spU2.xy / spU2.w) * aspectMul - ndc2);
288
305
 
306
+ // #236: degeneracy guards at visual scale (mirror of GPU33).
307
+ float degenEps = max(max(ndcHalfSize1, ndcHalfSize2) * 0.02, 1e-6);
308
+ if (len12 < degenEps) stripDegenerate = true;
309
+ vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
310
+ vec2 dir01 = (len01 > degenEps) ? (d01 / len01) : dir12;
311
+ vec2 dir23 = (len23 > degenEps) ? (d23 / len23) : dir12;
312
+ noKnee1 = dot(dir01, dir12) > 0.999;
313
+ noKnee2 = dot(dir12, dir23) > 0.999;
314
+ pathLen12 = len12;
315
+
289
316
  vec2 B = stripOrtho(dir12);
290
317
  if (!noKnee1) {
291
318
  float Kmul1 = -sign(dot(stripOrtho(dir01), dir12));
292
319
  vec2 K1 = Kmul1 * normalize(stripOrtho(dir01 + dir12));
293
320
  Bmul1 = sign(dot(B, K1));
294
321
  vec2 B1 = B * Bmul1;
295
- float ik1 = ndcHalfSize1 / max(dot(B1, K1), 1e-6);
296
- if (dot(-dir01, ik1 * -K1) > len01) ik1 = len01;
297
- if (dot(dir12, ik1 * -K1) > len12) ik1 = len12;
322
+ // #236: inner vertex at the ribbon half-width (see the Free
323
+ // branch comment above).
324
+ float ik1 = ndcHalfSize1;
298
325
  k1 = ndc1 + K1 * ndcHalfSize1;
299
326
  b1 = ndc1 + B1 * ndcHalfSize1;
300
327
  IK1 = ndc1 - K1 * ik1;
@@ -312,9 +339,8 @@ void main() {
312
339
  vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
313
340
  Bmul2 = sign(dot(B, K2));
314
341
  vec2 B2 = B * Bmul2;
315
- float ik2 = ndcHalfSize2 / max(dot(B2, K2), 1e-6);
316
- if (dot(-dir12, ik2 * -K2) > len12) ik2 = len12;
317
- if (dot(dir23, ik2 * -K2) > len23) ik2 = len23;
342
+ // #236: see ik1 inner vertex at the ribbon half-width.
343
+ float ik2 = ndcHalfSize2;
318
344
  k2 = ndc2 + K2 * ndcHalfSize2;
319
345
  b2 = ndc2 + B2 * ndcHalfSize2;
320
346
  IK2 = ndc2 - K2 * ik2;
@@ -329,6 +355,34 @@ void main() {
329
355
  }
330
356
  }
331
357
 
358
+ // #236: a noKnee (straight-cap) end carries no bend side of its own —
359
+ // its raw side label is always the +B side. If the segment's other
360
+ // end is a real knee on the -B side, the mismatched labels would send
361
+ // the ladder into the opposite-sides (twist) branch and flip the
362
+ // texture V across a segment that does not actually change bend side.
363
+ // Relabel the straight end to the knee end's side (swap its k/IK and
364
+ // re-collapse b onto the new k) so the ladder stays untwisted. Both
365
+ // frames are swapped; only the active one is meaningful.
366
+ if (noKnee1 && !noKnee2 && Bmul2 < 0.0) {
367
+ // Swap only the ACTIVE frame's variables - the inactive frame's
368
+ // are uninitialized and must not be read.
369
+ if (isFreeRotation) {
370
+ vec3 tRw1 = worldK1; worldK1 = worldIK1; worldIK1 = tRw1; worldB1 = worldK1;
371
+ } else {
372
+ vec2 tRl1 = k1; k1 = IK1; IK1 = tRl1; b1 = k1;
373
+ }
374
+ Bmul1 = Bmul2;
375
+ }
376
+ if (noKnee2 && !noKnee1 && Bmul1 < 0.0) {
377
+ if (isFreeRotation) {
378
+ vec3 tRw2 = worldK2; worldK2 = worldIK2; worldIK2 = tRw2; worldB2 = worldK2;
379
+ } else {
380
+ vec2 tRl2 = k2; k2 = IK2; IK2 = tRl2; b2 = k2;
381
+ }
382
+ Bmul2 = Bmul1;
383
+ }
384
+
385
+
332
386
  // V coords (mirror of preview3 / GPU33 make*Strip):
333
387
  // V at outer knee (k) = (Bmul + 1) * 0.5
334
388
  // V at inner knee (IK) = 1 - V_outer ; bend (b) = outer
@@ -430,7 +484,10 @@ void main() {
430
484
 
431
485
  // Free projects the world-space vertex through MVP directly; Faced
432
486
  // reconstructs clip space from aspect-corrected NDC (mirror of preview3).
433
- if (isFreeRotation) {
487
+ if (stripDegenerate) {
488
+ // #236: sub-visual segment — discard the pair entirely.
489
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
490
+ } else if (isFreeRotation) {
434
491
  gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
435
492
  } else if (nearPlaneClipped) {
436
493
  // At least one anchor is at / behind the near plane → the NDC math
@@ -467,6 +524,10 @@ void main() {
467
524
  float((packedColor >> 16u) & 0xFFu) / 255.0,
468
525
  float((packedColor >> 24u) & 0xFFu) / 255.0
469
526
  );
527
+ // HDR color scale S (RGBS, bloom TDD #175 §13.12): offset+60 (t3.a) is a
528
+ // per-particle float scale. RGB was normalized by S at pack time; restore HDR
529
+ // here. S=1 for LDR colors (no-op). Alpha is untouched.
530
+ vColor.rgb *= t3.a;
470
531
 
471
532
  uint gridConfig = floatBitsToUint(t3.g);
472
533
  float gridWidth = max(float(gridConfig & 0xFFFFu), 1.0);
@@ -514,7 +575,23 @@ void main() {
514
575
 
515
576
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
516
577
  vec3 worldPos = position + posDisp;
517
- gl_Position = mvpMatrix * vec4(worldPos.xy, 0.0, 1.0);
578
+ gl_Position = mvpMatrix * vec4(worldPos, 1.0);
579
+
580
+ // v1.0-parity near-plane culling for the perspective projection: the
581
+ // whole quad is discarded when ANY of its 4 corners is behind the near
582
+ // plane (w < 0.01 == pos.z > 0.99 * z_cam in PerspectiveProjection
583
+ // terms). w is linear in position, so the corner minimum comes from the
584
+ // w-row and the corner offsets (x in {-ox, 1-ox}, y in {-oy, 1-oy}).
585
+ // Without a projection every w is 1.0 and this is a no-op.
586
+ vec4 wRow = vec4(mvpMatrix[0].w, mvpMatrix[1].w, mvpMatrix[2].w, mvpMatrix[3].w);
587
+ float wx = dot(wRow.xyz, xaxis);
588
+ float wy = dot(wRow.xyz, yaxis);
589
+ float minW = dot(wRow.xyz, position) + wRow.w
590
+ - origin.x * wx + min(wx, 0.0)
591
+ - origin.y * wy + min(wy, 0.0);
592
+ if (minW < 0.01) {
593
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
594
+ }
518
595
 
519
596
  // Texture coordinates with grid + atlas remap
520
597
  vec2 baseTexCoord = TEX_COORD[vertexIndex];
@@ -734,6 +811,11 @@ void main() {
734
811
  // Colors: the meta M1 sample is the normalized RGBA directly.
735
812
  vec4 color1 = fetchMeta(curBase + 1.0);
736
813
  vec4 color2 = fetchMeta(nexBase + 1.0);
814
+ // HDR color scale S (RGBS, bloom TDD #175 §13.12): a plain float at offset+60,
815
+ // read straight from the float DATA texture (texel 3, .a) — like gridIndex — not
816
+ // the meta texture. Multiply BEFORE the cross-fade. S=1 → no-op.
817
+ color1.rgb *= fetchData(curBase + 3.0).a;
818
+ color2.rgb *= fetchData(nexBase + 3.0).a;
737
819
 
738
820
  // Neighbor RECORD indices (system.js convention: neighborIdxP0 on the
739
821
  // pair-cur meta, neighborIdxP3 on the pair-nex meta; boundary
@@ -765,6 +847,10 @@ void main() {
765
847
  // unaffected by the sign of w).
766
848
  bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
767
849
 
850
+ // #236: set when the segment itself is degenerate (much shorter than
851
+ // the ribbon half-width) - the pair is discarded like nearPlaneClipped.
852
+ bool stripDegenerate = false;
853
+
768
854
  // Aspect correction for the 2D Faced ortho on non-square viewports.
769
855
  vec2 aspectMul = vec2(1.0, 1.0 / max(uViewportAspect, 1e-6));
770
856
 
@@ -807,9 +893,12 @@ void main() {
807
893
  float wlen01 = length(wp01);
808
894
  float wlen12 = length(wp12);
809
895
  float wlen23 = length(wp23);
896
+ // #236: degeneracy guards at visual scale (mirror of GPU33).
897
+ float wDegenEps = max(max(abs(halfSize1), abs(halfSize2)) * 0.02, 1e-6);
898
+ if (wlen12 < wDegenEps) stripDegenerate = true;
810
899
  vec3 wdir12 = (wlen12 > 1e-6) ? (wp12 / wlen12) : vec3(1.0, 0.0, 0.0);
811
- vec3 wdir01 = (wlen01 > 1e-6) ? (wp01 / wlen01) : wdir12;
812
- vec3 wdir23 = (wlen23 > 1e-6) ? (wp23 / wlen23) : wdir12;
900
+ vec3 wdir01 = (wlen01 > wDegenEps) ? (wp01 / wlen01) : wdir12;
901
+ vec3 wdir23 = (wlen23 > wDegenEps) ? (wp23 / wlen23) : wdir12;
813
902
  noKnee1 = dot(wdir01, wdir12) > 0.999;
814
903
  noKnee2 = dot(wdir12, wdir23) > 0.999;
815
904
  pathLen12 = wlen12;
@@ -821,9 +910,9 @@ void main() {
821
910
  vec3 K1 = Kmul1 * normalize(cross(wdir01 + wdir12, n1));
822
911
  Bmul1 = sign(dot(B1raw, K1));
823
912
  vec3 B1 = B1raw * Bmul1;
824
- float ik1 = halfSize1 / max(dot(B1, K1), 1e-6);
825
- if (dot(-wdir01, ik1 * -K1) > wlen01) ik1 = wlen01;
826
- if (dot( wdir12, ik1 * -K1) > wlen12) ik1 = wlen12;
913
+ // #236: inner vertex at the ribbon half-width (mirror of
914
+ // GPU33; see the ES 3.00 shader for the rationale).
915
+ float ik1 = halfSize1;
827
916
  worldK1 = p1 + K1 * halfSize1;
828
917
  worldB1 = p1 + B1 * halfSize1;
829
918
  worldIK1 = p1 - K1 * ik1;
@@ -844,9 +933,8 @@ void main() {
844
933
  vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
845
934
  Bmul2 = sign(dot(B2raw, K2));
846
935
  vec3 B2 = B2raw * Bmul2;
847
- float ik2 = halfSize2 / max(dot(B2, K2), 1e-6);
848
- if (dot(-wdir12, ik2 * -K2) > wlen12) ik2 = wlen12;
849
- if (dot( wdir23, ik2 * -K2) > wlen23) ik2 = wlen23;
936
+ // #236: see ik1 - inner vertex at the ribbon half-width.
937
+ float ik2 = halfSize2;
850
938
  worldK2 = p2 + K2 * halfSize2;
851
939
  worldB2 = p2 + B2 * halfSize2;
852
940
  worldIK2 = p2 - K2 * ik2;
@@ -863,10 +951,16 @@ void main() {
863
951
  // Faced: screen-space NDC frame around dir12.
864
952
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
865
953
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
866
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
867
954
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
868
955
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
869
- vec2 ndc3 = (sp3.xy / sp3.w) * aspectMul;
956
+ // Neighbor anchors behind the near plane produce garbage NDC after
957
+ // the perspective divide (possible only with Projection3D, where w
958
+ // varies); collapse them onto their segment anchor so the existing
959
+ // zero-length degeneracy path (dir01 = dir12 / dir23 = dir12)
960
+ // handles the knee instead of a poisoned miter. spU1/spU2 need no
961
+ // guard: uCameraUp has zero Z, so their w equals sp1.w / sp2.w.
962
+ vec2 ndc0 = (sp0.w < 1e-3) ? ndc1 : (sp0.xy / sp0.w) * aspectMul;
963
+ vec2 ndc3 = (sp3.w < 1e-3) ? ndc2 : (sp3.xy / sp3.w) * aspectMul;
870
964
 
871
965
  vec2 d01 = ndc1 - ndc0;
872
966
  vec2 d12 = ndc2 - ndc1;
@@ -874,12 +968,6 @@ void main() {
874
968
  float len01 = length(d01);
875
969
  float len12 = length(d12);
876
970
  float len23 = length(d23);
877
- vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
878
- vec2 dir01 = (len01 > 1e-6) ? (d01 / len01) : dir12;
879
- vec2 dir23 = (len23 > 1e-6) ? (d23 / len23) : dir12;
880
- noKnee1 = dot(dir01, dir12) > 0.999;
881
- noKnee2 = dot(dir12, dir23) > 0.999;
882
- pathLen12 = len12;
883
971
 
884
972
  // Section magnitude: project camera_up*halfSize per anchor into NDC.
885
973
  vec4 spU1 = mvpMatrix * vec4(p1 + uCameraUp * halfSize1, 1.0);
@@ -887,15 +975,24 @@ void main() {
887
975
  float ndcHalfSize1 = length((spU1.xy / spU1.w) * aspectMul - ndc1);
888
976
  float ndcHalfSize2 = length((spU2.xy / spU2.w) * aspectMul - ndc2);
889
977
 
978
+ // #236: degeneracy guards at visual scale (mirror of GPU33).
979
+ float degenEps = max(max(ndcHalfSize1, ndcHalfSize2) * 0.02, 1e-6);
980
+ if (len12 < degenEps) stripDegenerate = true;
981
+ vec2 dir12 = (len12 > 1e-6) ? (d12 / len12) : vec2(1.0, 0.0);
982
+ vec2 dir01 = (len01 > degenEps) ? (d01 / len01) : dir12;
983
+ vec2 dir23 = (len23 > degenEps) ? (d23 / len23) : dir12;
984
+ noKnee1 = dot(dir01, dir12) > 0.999;
985
+ noKnee2 = dot(dir12, dir23) > 0.999;
986
+ pathLen12 = len12;
987
+
890
988
  vec2 B = stripOrtho(dir12);
891
989
  if (!noKnee1) {
892
990
  float Kmul1 = -sign(dot(stripOrtho(dir01), dir12));
893
991
  vec2 K1 = Kmul1 * normalize(stripOrtho(dir01 + dir12));
894
992
  Bmul1 = sign(dot(B, K1));
895
993
  vec2 B1 = B * Bmul1;
896
- float ik1 = ndcHalfSize1 / max(dot(B1, K1), 1e-6);
897
- if (dot(-dir01, ik1 * -K1) > len01) ik1 = len01;
898
- if (dot(dir12, ik1 * -K1) > len12) ik1 = len12;
994
+ // #236: inner vertex at the ribbon half-width.
995
+ float ik1 = ndcHalfSize1;
899
996
  k1 = ndc1 + K1 * ndcHalfSize1;
900
997
  b1 = ndc1 + B1 * ndcHalfSize1;
901
998
  IK1 = ndc1 - K1 * ik1;
@@ -913,9 +1010,8 @@ void main() {
913
1010
  vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
914
1011
  Bmul2 = sign(dot(B, K2));
915
1012
  vec2 B2 = B * Bmul2;
916
- float ik2 = ndcHalfSize2 / max(dot(B2, K2), 1e-6);
917
- if (dot(-dir12, ik2 * -K2) > len12) ik2 = len12;
918
- if (dot(dir23, ik2 * -K2) > len23) ik2 = len23;
1013
+ // #236: see ik1 - inner vertex at the ribbon half-width.
1014
+ float ik2 = ndcHalfSize2;
919
1015
  k2 = ndc2 + K2 * ndcHalfSize2;
920
1016
  b2 = ndc2 + B2 * ndcHalfSize2;
921
1017
  IK2 = ndc2 - K2 * ik2;
@@ -930,6 +1026,33 @@ void main() {
930
1026
  }
931
1027
  }
932
1028
 
1029
+ // #236: a noKnee (straight-cap) end carries no bend side of its own -
1030
+ // its raw side label is always the +B side. If the segment's other
1031
+ // end is a real knee on the -B side, the mismatched labels would send
1032
+ // the ladder into the opposite-sides (twist) branch and flip the
1033
+ // texture V across a segment that does not actually change bend side.
1034
+ // Relabel the straight end to the knee end's side (swap its k/IK and
1035
+ // re-collapse b onto the new k) so the ladder stays untwisted. Both
1036
+ // frames are swapped; only the active one is meaningful.
1037
+ if (noKnee1 && !noKnee2 && Bmul2 < 0.0) {
1038
+ // Swap only the ACTIVE frame's variables - the inactive frame's
1039
+ // are uninitialized and must not be read.
1040
+ if (isFreeRotation) {
1041
+ vec3 tRw1 = worldK1; worldK1 = worldIK1; worldIK1 = tRw1; worldB1 = worldK1;
1042
+ } else {
1043
+ vec2 tRl1 = k1; k1 = IK1; IK1 = tRl1; b1 = k1;
1044
+ }
1045
+ Bmul1 = Bmul2;
1046
+ }
1047
+ if (noKnee2 && !noKnee1 && Bmul1 < 0.0) {
1048
+ if (isFreeRotation) {
1049
+ vec3 tRw2 = worldK2; worldK2 = worldIK2; worldIK2 = tRw2; worldB2 = worldK2;
1050
+ } else {
1051
+ vec2 tRl2 = k2; k2 = IK2; IK2 = tRl2; b2 = k2;
1052
+ }
1053
+ Bmul2 = Bmul1;
1054
+ }
1055
+
933
1056
  // V coords: V at outer knee (k) = (Bmul + 1) * 0.5; inner = 1 - outer.
934
1057
  float Vtop1 = (Bmul1 + 1.0) * 0.5;
935
1058
  float Vbot1 = 1.0 - Vtop1;
@@ -1001,7 +1124,10 @@ void main() {
1001
1124
 
1002
1125
  // Free projects the world-space vertex through MVP directly; Faced
1003
1126
  // reconstructs clip space from aspect-corrected NDC.
1004
- if (isFreeRotation) {
1127
+ if (stripDegenerate) {
1128
+ // #236: sub-visual segment - discard the pair entirely.
1129
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
1130
+ } else if (isFreeRotation) {
1005
1131
  gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
1006
1132
  } else if (nearPlaneClipped) {
1007
1133
  gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
@@ -1026,6 +1152,9 @@ void main() {
1026
1152
 
1027
1153
  // Meta M1: the packedColor u32 LE bytes ARE the normalized RGBA sample.
1028
1154
  vColor = fetchMeta(baseTexel + 1.0);
1155
+ // HDR color scale S (RGBS, bloom TDD #175 §13.12): a plain float at offset+60,
1156
+ // read directly from the float DATA texture (t3.a) like gridIndex. S=1 → no-op.
1157
+ vColor.rgb *= t3.a;
1029
1158
 
1030
1159
  // Meta M2: gridConfig — [gridW lo, gridW hi, gridH lo, gridH hi].
1031
1160
  vec4 m2 = fetchMeta(baseTexel + 2.0);
@@ -1072,7 +1201,23 @@ void main() {
1072
1201
 
1073
1202
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
1074
1203
  vec3 worldPos = position + posDisp;
1075
- gl_Position = mvpMatrix * vec4(worldPos.xy, 0.0, 1.0);
1204
+ gl_Position = mvpMatrix * vec4(worldPos, 1.0);
1205
+
1206
+ // v1.0-parity near-plane culling for the perspective projection: the
1207
+ // whole quad is discarded when ANY of its 4 corners is behind the near
1208
+ // plane (w < 0.01 == pos.z > 0.99 * z_cam in PerspectiveProjection
1209
+ // terms). w is linear in position, so the corner minimum comes from the
1210
+ // w-row and the corner offsets (x in {-ox, 1-ox}, y in {-oy, 1-oy}).
1211
+ // Without a projection every w is 1.0 and this is a no-op.
1212
+ vec4 wRow = vec4(mvpMatrix[0].w, mvpMatrix[1].w, mvpMatrix[2].w, mvpMatrix[3].w);
1213
+ float wx = dot(wRow.xyz, xaxis);
1214
+ float wy = dot(wRow.xyz, yaxis);
1215
+ float minW = dot(wRow.xyz, position) + wRow.w
1216
+ - origin.x * wx + min(wx, 0.0)
1217
+ - origin.y * wy + min(wy, 0.0);
1218
+ if (minW < 0.01) {
1219
+ gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
1220
+ }
1076
1221
 
1077
1222
  // Texture coordinates with grid + atlas remap
1078
1223
  vec2 baseTexCoord = texCoordOf(corner);
@@ -1147,7 +1292,7 @@ void main() {
1147
1292
  // world alpha scales the whole RGBA (mirror of the ES 3.00 shader).
1148
1293
  gl_FragColor = texColor * vColor * uWorldAlpha;
1149
1294
  }
1150
- `,A=class A{constructor(e,t){n(this,"neutrino");n(this,"gl");n(this,"_shaderProgram",null);n(this,"_indexBuffer",null);n(this,"_dummyVB",null);n(this,"_indexBufferCapacity",0);n(this,"_vao",null);n(this,"_isWebGL1",!1);n(this,"_maxBatchTextures",A.MAX_BATCH_TEXTURES);n(this,"_hasElementIndexUint",!1);n(this,"_vaoExt",null);n(this,"_aIdBuffer",null);n(this,"_aIdLocation",-1);n(this,"_uDataTexture",null);n(this,"_uDataTextureWidth",null);n(this,"_uMetaTexture",null);n(this,"_uDataTexSize",null);n(this,"_uCameraRight",null);n(this,"_uCameraUp",null);n(this,"_uCameraDir",null);n(this,"_uViewProjMatrix",null);n(this,"_uModelMatrix",null);n(this,"_uViewportAspect",null);n(this,"_uWorldAlpha",null);n(this,"_uTextures",[]);n(this,"_uTexRemaps",[]);n(this,"_noiseInitialized",!1);this.gl=e,this.neutrino=t,this._isWebGL1=typeof e.createVertexArray!="function",this._isWebGL1&&this._probeWebGL1Capabilities(),this._initShader(),this._isWebGL1&&this._smokeTestVertexTextureFetch()}initializeNoise(e,t,a){if(this._noiseInitialized){t&&t();return}this.neutrino.initializeNoise(e,()=>{this._noiseInitialized=!0,t&&t()},a)}generateNoise(){if(this._noiseInitialized)return;const e=new this.neutrino.NoiseGenerator;for(;!e.step(););this._noiseInitialized=!0}get shaderProgram(){return this._shaderProgram}get isWebGL1(){return this._isWebGL1}get maxBatchTextures(){return this._maxBatchTextures}get uDataTexture(){return this._uDataTexture}get uDataTextureWidth(){return this._uDataTextureWidth}get uMetaTexture(){return this._uMetaTexture}get uDataTexSize(){return this._uDataTexSize}get uCameraRight(){return this._uCameraRight}get uCameraUp(){return this._uCameraUp}get uCameraDir(){return this._uCameraDir}get uViewProjMatrix(){return this._uViewProjMatrix}get uModelMatrix(){return this._uModelMatrix}get uViewportAspect(){return this._uViewportAspect}get uWorldAlpha(){return this._uWorldAlpha}get uTextures(){return this._uTextures}get uTexRemaps(){return this._uTexRemaps}ensureIndexBuffer(e){if(e<=this._indexBufferCapacity)return;const t=this.gl,a=this._isWebGL1?null:t.getParameter(t.VERTEX_ARRAY_BINDING),r=t.getParameter(t.ARRAY_BUFFER_BINDING),o=t.getParameter(t.ELEMENT_ARRAY_BUFFER_BINDING);this._indexBuffer&&t.deleteBuffer(this._indexBuffer),this._dummyVB&&(t.deleteBuffer(this._dummyVB),this._dummyVB=null),this._vao&&(t.deleteVertexArray(this._vao),this._vao=null),this._aIdBuffer&&(t.deleteBuffer(this._aIdBuffer),this._aIdBuffer=null);const l=e>16383;if(l&&this._isWebGL1&&!this._hasElementIndexUint)throw new Error(`NeutrinoParticles (js-v1.1): the effect needs 32-bit indices (${e} particles) but this WebGL1 context lacks OES_element_index_uint.`);const c=l?new Uint32Array(e*6):new Uint16Array(e*6);for(let i=0;i<e;i++){const u=i*4,_=i*6;c[_+0]=u+0,c[_+1]=u+1,c[_+2]=u+2,c[_+3]=u+0,c[_+4]=u+2,c[_+5]=u+3}if(this._indexBuffer=t.createBuffer(),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this._indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,c,t.STATIC_DRAW),this._isWebGL1){const i=new Float32Array(e*4);for(let u=0;u<i.length;u++)i[u]=u;this._aIdBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this._aIdBuffer),t.bufferData(t.ARRAY_BUFFER,i,t.STATIC_DRAW)}else this._dummyVB=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this._dummyVB),t.bufferData(t.ARRAY_BUFFER,e*4,t.STATIC_DRAW),this._vao=t.createVertexArray(),t.bindVertexArray(this._vao),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this._indexBuffer),t.bindBuffer(t.ARRAY_BUFFER,this._dummyVB),t.enableVertexAttribArray(0),t.vertexAttribPointer(0,1,t.UNSIGNED_BYTE,!1,0,0),t.bindVertexArray(a);t.bindBuffer(t.ARRAY_BUFFER,r),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,o),this._indexBufferCapacity=e}get vao(){return this._vao}get indexBuffer(){return this._indexBuffer}get aIdBuffer(){return this._aIdBuffer}get aIdLocation(){return this._aIdLocation}get indexType(){return this._indexBufferCapacity>16383?this.gl.UNSIGNED_INT:this.gl.UNSIGNED_SHORT}unbindVao(){this._isWebGL1?this._vaoExt&&this._vaoExt.bindVertexArrayOES(null):this.gl.bindVertexArray(null)}destroy(){const e=this.gl;this._shaderProgram&&e.deleteProgram(this._shaderProgram),this._indexBuffer&&e.deleteBuffer(this._indexBuffer),this._dummyVB&&e.deleteBuffer(this._dummyVB),this._vao&&e.deleteVertexArray(this._vao),this._aIdBuffer&&e.deleteBuffer(this._aIdBuffer)}_probeWebGL1Capabilities(){const e=this.gl;if(!e.getExtension("OES_texture_float"))throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context lacks OES_texture_float; WebGL2 or WebGL1 with vertex float textures is required.");const t=e.getParameter(e.MAX_VERTEX_TEXTURE_IMAGE_UNITS);if(t<2)throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context has "+t+" vertex texture units (2 required); WebGL2 or WebGL1 with vertex float textures is required.");const a=e.getShaderPrecisionFormat(e.FRAGMENT_SHADER,e.HIGH_FLOAT);if(!a||a.precision===0)throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context lacks highp float in fragment shaders; WebGL2 or WebGL1 with fragment highp is required.");this._hasElementIndexUint=!!e.getExtension("OES_element_index_uint"),this._vaoExt=e.getExtension("OES_vertex_array_object");const r=e.getParameter(e.MAX_COMBINED_TEXTURE_IMAGE_UNITS),o=e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS);this._maxBatchTextures=Math.max(1,Math.min(A.MAX_BATCH_TEXTURES,r-2,o))}_smokeTestVertexTextureFetch(){const e=this.gl,t=`precision highp float;
1295
+ `,A=class A{constructor(e,t){a(this,"neutrino");a(this,"gl");a(this,"_shaderProgram",null);a(this,"_indexBuffer",null);a(this,"_dummyVB",null);a(this,"_indexBufferCapacity",0);a(this,"_vao",null);a(this,"_isWebGL1",!1);a(this,"_maxBatchTextures",A.MAX_BATCH_TEXTURES);a(this,"_hasElementIndexUint",!1);a(this,"_vaoExt",null);a(this,"_aIdBuffer",null);a(this,"_aIdLocation",-1);a(this,"_uDataTexture",null);a(this,"_uDataTextureWidth",null);a(this,"_uMetaTexture",null);a(this,"_uDataTexSize",null);a(this,"_uCameraRight",null);a(this,"_uCameraUp",null);a(this,"_uCameraDir",null);a(this,"_uViewProjMatrix",null);a(this,"_uModelMatrix",null);a(this,"_uViewportAspect",null);a(this,"_uWorldAlpha",null);a(this,"_uTextures",[]);a(this,"_uTexRemaps",[]);a(this,"_noiseInitialized",!1);this.gl=e,this.neutrino=t,this._isWebGL1=typeof e.createVertexArray!="function",this._isWebGL1&&this._probeWebGL1Capabilities(),this._initShader(),this._isWebGL1&&this._smokeTestVertexTextureFetch()}initializeNoise(e,t,n){if(this._noiseInitialized){t&&t();return}this.neutrino.initializeNoise(e,()=>{this._noiseInitialized=!0,t&&t()},n)}generateNoise(){if(this._noiseInitialized)return;const e=new this.neutrino.NoiseGenerator;for(;!e.step(););this._noiseInitialized=!0}get shaderProgram(){return this._shaderProgram}get isWebGL1(){return this._isWebGL1}get maxBatchTextures(){return this._maxBatchTextures}get uDataTexture(){return this._uDataTexture}get uDataTextureWidth(){return this._uDataTextureWidth}get uMetaTexture(){return this._uMetaTexture}get uDataTexSize(){return this._uDataTexSize}get uCameraRight(){return this._uCameraRight}get uCameraUp(){return this._uCameraUp}get uCameraDir(){return this._uCameraDir}get uViewProjMatrix(){return this._uViewProjMatrix}get uModelMatrix(){return this._uModelMatrix}get uViewportAspect(){return this._uViewportAspect}get uWorldAlpha(){return this._uWorldAlpha}get uTextures(){return this._uTextures}get uTexRemaps(){return this._uTexRemaps}ensureIndexBuffer(e){if(e<=this._indexBufferCapacity)return;const t=this.gl,n=this._isWebGL1?null:t.getParameter(t.VERTEX_ARRAY_BINDING),r=t.getParameter(t.ARRAY_BUFFER_BINDING),o=t.getParameter(t.ELEMENT_ARRAY_BUFFER_BINDING);this._indexBuffer&&t.deleteBuffer(this._indexBuffer),this._dummyVB&&(t.deleteBuffer(this._dummyVB),this._dummyVB=null),this._vao&&(t.deleteVertexArray(this._vao),this._vao=null),this._aIdBuffer&&(t.deleteBuffer(this._aIdBuffer),this._aIdBuffer=null);const l=e>16383;if(l&&this._isWebGL1&&!this._hasElementIndexUint)throw new Error(`NeutrinoParticles (js-v1.1): the effect needs 32-bit indices (${e} particles) but this WebGL1 context lacks OES_element_index_uint.`);const c=l?new Uint32Array(e*6):new Uint16Array(e*6);for(let i=0;i<e;i++){const u=i*4,_=i*6;c[_+0]=u+0,c[_+1]=u+1,c[_+2]=u+2,c[_+3]=u+0,c[_+4]=u+2,c[_+5]=u+3}if(this._indexBuffer=t.createBuffer(),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this._indexBuffer),t.bufferData(t.ELEMENT_ARRAY_BUFFER,c,t.STATIC_DRAW),this._isWebGL1){const i=new Float32Array(e*4);for(let u=0;u<i.length;u++)i[u]=u;this._aIdBuffer=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this._aIdBuffer),t.bufferData(t.ARRAY_BUFFER,i,t.STATIC_DRAW)}else this._dummyVB=t.createBuffer(),t.bindBuffer(t.ARRAY_BUFFER,this._dummyVB),t.bufferData(t.ARRAY_BUFFER,e*4,t.STATIC_DRAW),this._vao=t.createVertexArray(),t.bindVertexArray(this._vao),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,this._indexBuffer),t.bindBuffer(t.ARRAY_BUFFER,this._dummyVB),t.enableVertexAttribArray(0),t.vertexAttribPointer(0,1,t.UNSIGNED_BYTE,!1,0,0),t.bindVertexArray(n);t.bindBuffer(t.ARRAY_BUFFER,r),t.bindBuffer(t.ELEMENT_ARRAY_BUFFER,o),this._indexBufferCapacity=e}get vao(){return this._vao}get indexBuffer(){return this._indexBuffer}get aIdBuffer(){return this._aIdBuffer}get aIdLocation(){return this._aIdLocation}get indexType(){return this._indexBufferCapacity>16383?this.gl.UNSIGNED_INT:this.gl.UNSIGNED_SHORT}unbindVao(){this._isWebGL1?this._vaoExt&&this._vaoExt.bindVertexArrayOES(null):this.gl.bindVertexArray(null)}destroy(){const e=this.gl;this._shaderProgram&&e.deleteProgram(this._shaderProgram),this._indexBuffer&&e.deleteBuffer(this._indexBuffer),this._dummyVB&&e.deleteBuffer(this._dummyVB),this._vao&&e.deleteVertexArray(this._vao),this._aIdBuffer&&e.deleteBuffer(this._aIdBuffer)}_probeWebGL1Capabilities(){const e=this.gl;if(!e.getExtension("OES_texture_float"))throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context lacks OES_texture_float; WebGL2 or WebGL1 with vertex float textures is required.");const t=e.getParameter(e.MAX_VERTEX_TEXTURE_IMAGE_UNITS);if(t<2)throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context has "+t+" vertex texture units (2 required); WebGL2 or WebGL1 with vertex float textures is required.");const n=e.getShaderPrecisionFormat(e.FRAGMENT_SHADER,e.HIGH_FLOAT);if(!n||n.precision===0)throw new Error("NeutrinoParticles (js-v1.1): WebGL1 context lacks highp float in fragment shaders; WebGL2 or WebGL1 with fragment highp is required.");this._hasElementIndexUint=!!e.getExtension("OES_element_index_uint"),this._vaoExt=e.getExtension("OES_vertex_array_object");const r=e.getParameter(e.MAX_COMBINED_TEXTURE_IMAGE_UNITS),o=e.getParameter(e.MAX_TEXTURE_IMAGE_UNITS);this._maxBatchTextures=Math.max(1,Math.min(A.MAX_BATCH_TEXTURES,r-2,o))}_smokeTestVertexTextureFetch(){const e=this.gl,t=`precision highp float;
1151
1296
  attribute float aId;
1152
1297
  uniform sampler2D uF;
1153
1298
  uniform sampler2D uB;
@@ -1159,11 +1304,11 @@ void main() {
1159
1304
  gl_Position = vec4(aId, 0.0, 0.0, 1.0);
1160
1305
  gl_PointSize = 1.0;
1161
1306
  }
1162
- `,a=`precision mediump float;
1307
+ `,n=`precision mediump float;
1163
1308
  varying vec4 vC;
1164
1309
  void main() { gl_FragColor = vC; }
1165
- `,r=e.getParameter(e.VIEWPORT),o=e.getParameter(e.CURRENT_PROGRAM),l=e.getParameter(e.ARRAY_BUFFER_BINDING),c=e.getParameter(e.FRAMEBUFFER_BINDING),i=e.getParameter(e.ACTIVE_TEXTURE);e.activeTexture(e.TEXTURE0);const u=e.getParameter(e.TEXTURE_BINDING_2D);e.activeTexture(e.TEXTURE1);const _=e.getParameter(e.TEXTURE_BINDING_2D),U=e.isEnabled(e.BLEND),E=e.isEnabled(e.DEPTH_TEST),I=e.isEnabled(e.SCISSOR_TEST);let x=null,b=null,B=null,p=null,v=null,T=null,d=-1,w=!1,f=null,h=4,y=0,g=0,P=0,D=!1,S=!1;const m=new Uint8Array(4);try{x=e.createProgram(),e.attachShader(x,this._compileShader(e.VERTEX_SHADER,t)),e.attachShader(x,this._compileShader(e.FRAGMENT_SHADER,a)),e.linkProgram(x);const C=(R,F)=>{const M=e.createTexture();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,R,F),M};e.activeTexture(e.TEXTURE0),b=C(e.FLOAT,new Float32Array([1,.5,.25,1])),B=C(e.UNSIGNED_BYTE,new Uint8Array([51,102,153,255])),p=e.createTexture(),e.bindTexture(e.TEXTURE_2D,p),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,p,0),T=e.createBuffer(),e.bindBuffer(e.ARRAY_BUFFER,T),e.bufferData(e.ARRAY_BUFFER,new Float32Array([0]),e.STATIC_DRAW),S=e.getProgramParameter(x,e.LINK_STATUS),S&&(e.useProgram(x),e.activeTexture(e.TEXTURE0),e.bindTexture(e.TEXTURE_2D,b),e.activeTexture(e.TEXTURE1),e.bindTexture(e.TEXTURE_2D,B),e.uniform1i(e.getUniformLocation(x,"uF"),0),e.uniform1i(e.getUniformLocation(x,"uB"),1),d=e.getAttribLocation(x,"aId"),w=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_ENABLED),f=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING),h=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_SIZE),y=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_TYPE),D=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_NORMALIZED),g=e.getVertexAttrib(d,e.VERTEX_ATTRIB_ARRAY_STRIDE),P=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,m),S=Math.abs(m[0]-51)<=2&&Math.abs(m[1]-51)<=2&&Math.abs(m[2]-38)<=2)}finally{d>=0&&(f&&(e.bindBuffer(e.ARRAY_BUFFER,f),e.vertexAttribPointer(d,h,y,D,g,P)),w?e.enableVertexAttribArray(d):e.disableVertexAttribArray(d)),v&&e.deleteFramebuffer(v),p&&e.deleteTexture(p),b&&e.deleteTexture(b),B&&e.deleteTexture(B),T&&e.deleteBuffer(T),x&&e.deleteProgram(x),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),U&&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])}if(!S)throw new Error(`NeutrinoParticles (js-v1.1): WebGL1 vertex texture fetch smoke test failed (got ${m[0]},${m[1]},${m[2]}); WebGL2 or WebGL1 with working vertex float textures is required.`)}_initShader(){const e=this.gl,t=this._isWebGL1?vertexShaderSourceGL1:vertexShaderSource,a=this._isWebGL1?fragmentShaderSourceGL1:fragmentShaderSource,r=this._compileShader(e.VERTEX_SHADER,t),o=this._compileShader(e.FRAGMENT_SHADER,a);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))throw new Error("Shader link error: "+e.getProgramInfoLog(this._shaderProgram));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=[];for(let l=0;l<A.MAX_BATCH_TEXTURES;l++)this._uTextures.push(e.getUniformLocation(this._shaderProgram,`uTextures[${l}]`)),this._uTexRemaps.push(e.getUniformLocation(this._shaderProgram,`uTexRemaps[${l}]`))}_compileShader(e,t){const a=this.gl,r=a.createShader(e);if(a.shaderSource(r,t),a.compileShader(r),!a.getShaderParameter(r,a.COMPILE_STATUS)){const o=a.getShaderInfoLog(r);throw a.deleteShader(r),new Error("Shader compile error: "+o)}return r}};n(A,"MAX_BATCH_TEXTURES",8);let NeutrinoContext=A;class GamePlugin extends Phaser.Plugins.BasePlugin{constructor(){super(...arguments);n(this,"neutrino");n(this,"ctx");n(this,"texturesBasePath","")}init(t){if(t=Object.assign({texturesBasePath:"",generateNoise:!1},t),this.game.renderer.type!==Phaser.WEBGL)throw new Error("NeutrinoParticles (js-v1.1): a WebGL renderer is required (Canvas not supported).");const r=this.game.renderer.gl;this.texturesBasePath=t.texturesBasePath,this.neutrino=new Neutrino__namespace.Context,this.ctx=new NeutrinoContext(r,this.neutrino),t.generateNoise&&this.generateNoise()}start(){}stop(){}destroy(){this.ctx&&this.ctx.destroy(),this.neutrino=null,super.destroy()}loadNoise(t,a,r){this.ctx.initializeNoise(t,a||(()=>{}),r||(()=>{}))}generateNoise(){this.ctx.generateNoise()}}function noext(s){return s.replace(/\.[^/.]+$/,"")}class EffectModel{constructor(scene,scriptText,options){n(this,"gamePlugin");n(this,"scene");n(this,"effectModel");n(this,"textureFrames",[]);n(this,"texturesRemap",[]);n(this,"_numTexturesToLoadLeft",0);options=Object.assign({atlases:[]},options),this.gamePlugin=scene.plugins.get("neutrino"),this.scene=scene;const evalScript=`(function(ctx) {
1310
+ `,r=e.getParameter(e.VIEWPORT),o=e.getParameter(e.CURRENT_PROGRAM),l=e.getParameter(e.ARRAY_BUFFER_BINDING),c=e.getParameter(e.FRAMEBUFFER_BINDING),i=e.getParameter(e.ACTIVE_TEXTURE);e.activeTexture(e.TEXTURE0);const u=e.getParameter(e.TEXTURE_BINDING_2D);e.activeTexture(e.TEXTURE1);const _=e.getParameter(e.TEXTURE_BINDING_2D),y=e.isEnabled(e.BLEND),E=e.isEnabled(e.DEPTH_TEST),I=e.isEnabled(e.SCISSOR_TEST);let m=null,b=null,B=null,f=null,v=null,g=null,d=-1,w=!1,x=null,h=4,U=0,p=0,P=0,D=!1,S=!1;const T=new Uint8Array(4);try{m=e.createProgram(),e.attachShader(m,this._compileShader(e.VERTEX_SHADER,t)),e.attachShader(m,this._compileShader(e.FRAGMENT_SHADER,n)),e.linkProgram(m);const C=(R,F)=>{const M=e.createTexture();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,R,F),M};e.activeTexture(e.TEXTURE0),b=C(e.FLOAT,new Float32Array([1,.5,.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,b),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"),w=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),P=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)}finally{d>=0&&(x&&(e.bindBuffer(e.ARRAY_BUFFER,x),e.vertexAttribPointer(d,h,U,D,p,P)),w?e.enableVertexAttribArray(d):e.disableVertexAttribArray(d)),v&&e.deleteFramebuffer(v),f&&e.deleteTexture(f),b&&e.deleteTexture(b),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])}if(!S)throw new Error(`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.`)}_initShader(){const e=this.gl,t=this._isWebGL1?vertexShaderSourceGL1:vertexShaderSource,n=this._isWebGL1?fragmentShaderSourceGL1:fragmentShaderSource,r=this._compileShader(e.VERTEX_SHADER,t),o=this._compileShader(e.FRAGMENT_SHADER,n);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))throw new Error("Shader link error: "+e.getProgramInfoLog(this._shaderProgram));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=[];for(let l=0;l<A.MAX_BATCH_TEXTURES;l++)this._uTextures.push(e.getUniformLocation(this._shaderProgram,`uTextures[${l}]`)),this._uTexRemaps.push(e.getUniformLocation(this._shaderProgram,`uTexRemaps[${l}]`))}_compileShader(e,t){const n=this.gl,r=n.createShader(e);if(n.shaderSource(r,t),n.compileShader(r),!n.getShaderParameter(r,n.COMPILE_STATUS)){const o=n.getShaderInfoLog(r);throw n.deleteShader(r),new Error("Shader compile error: "+o)}return r}};a(A,"MAX_BATCH_TEXTURES",8);let NeutrinoContext=A;class GamePlugin extends Phaser.Plugins.BasePlugin{constructor(){super(...arguments);a(this,"neutrino");a(this,"ctx");a(this,"texturesBasePath","")}init(t){if(t=Object.assign({texturesBasePath:"",generateNoise:!1},t),this.game.renderer.type!==Phaser.WEBGL)throw new Error("NeutrinoParticles (js-v1.1): a WebGL renderer is required (Canvas not supported).");const r=this.game.renderer.gl;this.texturesBasePath=t.texturesBasePath,this.neutrino=new Neutrino__namespace.Context,this.ctx=new NeutrinoContext(r,this.neutrino),t.generateNoise&&this.generateNoise()}start(){}stop(){}destroy(){this.ctx&&this.ctx.destroy(),this.neutrino=null,super.destroy()}loadNoise(t,n,r){this.ctx.initializeNoise(t,n||(()=>{}),r||(()=>{}))}generateNoise(){this.ctx.generateNoise()}}function noext(s){return s.replace(/\.[^/.]+$/,"")}class EffectModel{constructor(scene,scriptText,options){a(this,"gamePlugin");a(this,"scene");a(this,"effectModel");a(this,"textureFrames",[]);a(this,"texturesRemap",[]);a(this,"_numTexturesToLoadLeft",0);options=Object.assign({atlases:[]},options),this.gamePlugin=scene.plugins.get("neutrino"),this.scene=scene;const evalScript=`(function(ctx) {
1166
1311
  `+scriptText+`
1167
1312
  return new NeutrinoEffect(ctx);
1168
- })(this.gamePlugin.neutrino);`;this.effectModel=eval(evalScript),this._startLoadTextures(options.atlases)}get ready(){return this._numTexturesToLoadLeft===0}glTexture(s){const e=this.textureFrames[s];return e?e.glTexture.webGLTexture:null}_startLoadTextures(s){const e=this.effectModel.textures.length;this._numTexturesToLoadLeft=e;for(let t=0;t<e;++t){const a=this.effectModel.textures[t],r=this.gamePlugin.texturesBasePath+a,o=noext(r);let l=this._findFrameInAtlases(s,a);if(l||(l=this._findFrameInAtlases(s,noext(a))),!l&&this.scene.sys.textures.exists(o)){const c=this.scene.sys.textures.get(o);l=c.get(c.firstFrame)}l?this._onTextureLoaded(t,l):(this.scene.load.once("filecomplete-image-"+o,((c,i)=>()=>{const u=this.scene.sys.textures.get(i);this._onTextureLoaded(c,u.get(u.firstFrame))})(t,o)),this.scene.load.image(o,r))}}_findFrameInAtlases(s,e){for(let t=0;t<s.length;++t){const a=s[t];if(!this.scene.sys.textures.exists(a))continue;const r=this.scene.sys.textures.get(a);if(r.has(e))return r.get(e)}return null}_onTextureLoaded(s,e){this.textureFrames[s]=e,this._numTexturesToLoadLeft--,this._numTexturesToLoadLeft===0&&this._initTexturesRemapIfNeeded()}_fullCover(s){return s.width===s.source.width&&s.height===s.source.height}_initTexturesRemapIfNeeded(){let s=!1;for(let e=0;e<this.textureFrames.length;++e)if(!this._fullCover(this.textureFrames[e])){s=!0;break}if(s)for(let e=0;e<this.textureFrames.length;++e){const t=this.textureFrames[e];this.texturesRemap[e]={x:t.u0,y:1-t.v1,width:t.u1-t.u0,height:t.v1-t.v0}}}}class NeutrinoFile extends Phaser.Loader.File{constructor(t,a){const{key:r,url:o,options:l,xhrSettings:c}=a;super(t,{type:"binary",extension:"js",responseType:"text",key:r,url:o,xhrSettings:c});n(this,"_options");this.cache=t.cacheManager.binary,this._options=l}onProcess(){this.state=Phaser.Loader.FILE_PROCESSING;const t=this.xhrLoader.response;this.data=new EffectModel(this.loader.scene,t,this._options),this.onProcessComplete()}}var Pause=(s=>(s[s.NO=0]="NO",s[s.BEFORE_UPDATE_OR_RENDER=1]="BEFORE_UPDATE_OR_RENDER",s[s.YES=2]="YES",s))(Pause||{});const RING_BUFFER_SIZE$1=3;class DataTextureManager{constructor(e,t,a,r){n(this,"textureWidth");n(this,"textureHeight");n(this,"_gl");n(this,"_floatView");n(this,"_textures",[]);n(this,"_currentIndex",0);const o=e.getParameter(e.MAX_TEXTURE_SIZE);if(a>o||r>o)throw new Error(`NeutrinoParticles: data texture ${a}x${r} exceeds GL MAX_TEXTURE_SIZE (${o}). Reduce effect particle capacity.`);this._gl=e,this.textureWidth=a,this.textureHeight=r,this._floatView=new Float32Array(t.buffer,t.byteOffset,t.length);for(let l=0;l<RING_BUFFER_SIZE$1;l++){const c=e.createTexture();e.bindTexture(e.TEXTURE_2D,c),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.RGBA32F,a,r,0,e.RGBA,e.FLOAT,null),this._textures.push(c)}e.bindTexture(e.TEXTURE_2D,null)}get currentTexture(){return this._textures[this._currentIndex]}uploadAndBind(e){const t=this._gl;t.activeTexture(t.TEXTURE0+e),t.bindTexture(t.TEXTURE_2D,this._textures[this._currentIndex]),t.pixelStorei(t.UNPACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!1),t.texSubImage2D(t.TEXTURE_2D,0,0,0,this.textureWidth,this.textureHeight,t.RGBA,t.FLOAT,this._floatView)}advance(){this._currentIndex=(this._currentIndex+1)%RING_BUFFER_SIZE$1}destroy(){const e=this._gl;for(const t of this._textures)t&&e.deleteTexture(t);this._textures=[]}}class MetaTextureManager{constructor(e,t){n(this,"bytes");n(this,"_u32");this.bytes=new Uint8Array(e*t*4),this._u32=new Uint32Array(this.bytes.buffer)}build(e,t){const a=this._u32;for(let r=0;r<t;r++){const o=r*16,l=r*4;a[l]=e[o],a[l+1]=e[o+12],a[l+2]=e[o+13],a[l+3]=e[o+14]}}patchSlot(e,t,a){const r=this.bytes;let o=e*16;for(let l=0;l<t;l++)r[o]=a,o+=16}}const RING_BUFFER_SIZE=3;class DataTextureManagerGL1{constructor(e,t,a,r){n(this,"textureWidth");n(this,"textureHeight");n(this,"meta");n(this,"_gl");n(this,"_floatView");n(this,"_dataTextures",[]);n(this,"_metaTextures",[]);n(this,"_currentIndex",0);const o=e.getParameter(e.MAX_TEXTURE_SIZE);if(a>o||r>o)throw new Error(`NeutrinoParticles: data texture ${a}x${r} exceeds GL MAX_TEXTURE_SIZE (${o}). Reduce effect particle capacity.`);if(a*r>16777216)throw new Error(`NeutrinoParticles: data texture ${a}x${r} exceeds the WebGL1 render path texel cap (2^24). Reduce effect particle capacity.`);this._gl=e,this.textureWidth=a,this.textureHeight=r,this._floatView=new Float32Array(t.buffer,t.byteOffset,t.length),this.meta=new MetaTextureManager(a,r);const l=e.getParameter(e.ACTIVE_TEXTURE);e.activeTexture(e.TEXTURE0);const c=e.getParameter(e.TEXTURE_BINDING_2D);for(let i=0;i<RING_BUFFER_SIZE;i++)this._dataTextures.push(this._createTexture(e.FLOAT)),this._metaTextures.push(this._createTexture(e.UNSIGNED_BYTE));e.bindTexture(e.TEXTURE_2D,c),e.activeTexture(l)}get currentDataTexture(){return this._dataTextures[this._currentIndex]}get currentMetaTexture(){return this._metaTextures[this._currentIndex]}upload(e,t,a){const r=this._gl,o=this.textureWidth,l=Math.min(this.textureHeight,Math.ceil(e*4/o));if(l===0)return;const c=l*o*4;r.pixelStorei(r.UNPACK_FLIP_Y_WEBGL,!1),r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),r.pixelStorei(r.UNPACK_ALIGNMENT,4),r.activeTexture(r.TEXTURE0+t),r.bindTexture(r.TEXTURE_2D,this.currentDataTexture),r.texSubImage2D(r.TEXTURE_2D,0,0,0,o,l,r.RGBA,r.FLOAT,this._floatView.subarray(0,c)),r.activeTexture(r.TEXTURE0+a),r.bindTexture(r.TEXTURE_2D,this.currentMetaTexture),r.texSubImage2D(r.TEXTURE_2D,0,0,0,o,l,r.RGBA,r.UNSIGNED_BYTE,this.meta.bytes.subarray(0,c))}advance(){this._currentIndex=(this._currentIndex+1)%RING_BUFFER_SIZE}destroy(){const e=this._gl;for(const t of this._dataTextures)e.deleteTexture(t);for(const t of this._metaTextures)e.deleteTexture(t);this._dataTextures=[],this._metaTextures=[]}_createTexture(e){const t=this._gl,a=t.createTexture();return t.bindTexture(t.TEXTURE_2D,a),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.textureWidth,this.textureHeight,0,t.RGBA,e,null),a}}const DATA_TEXTURE_UNIT=0,META_TEXTURE_UNIT=1,PARTICLE_TEXTURE_START_UNIT=1,PARTICLE_TEXTURE_START_UNIT_GL1=2,FLOATS_PER_PARTICLE=16,BYTES_PER_PARTICLE=FLOATS_PER_PARTICLE*4,MAX_BATCHES=64;class DrawBatch{constructor(){n(this,"blendMode",0);n(this,"startParticle",0);n(this,"numParticles",0);n(this,"numTextures",0);n(this,"glTextures",new Array(NeutrinoContext.MAX_BATCH_TEXTURES).fill(null));n(this,"remaps",new Array(NeutrinoContext.MAX_BATCH_TEXTURES).fill(null))}reset(){this.blendMode=0,this.startParticle=0,this.numParticles=0,this.numTextures=0}}class NeutrinoRenderer{constructor(){n(this,"_orthoMatrix",new Float32Array(16));n(this,"_modelMatrix",new Float32Array(16));n(this,"_texSlotMap",new Map);n(this,"_batchPool");n(this,"_batchCount",0);this._batchPool=[];for(let e=0;e<MAX_BATCHES;e++)this._batchPool.push(new DrawBatch)}render(e,t,a,r,o,l,c){if(l===0)return;const i=e.gl,u=e.isWebGL1,_=e.maxBatchTextures,U=c.renderStyles,E=c.materials,I=u?t.meta:null;I&&I.build(r,l),this._batchCount=0;const x=this._texSlotMap;for(;this._batchPool.length<o.length;)this._batchPool.push(new DrawBatch);let b=0;for(;b<o.length;){const p=this._batchPool[this._batchCount];p.reset(),p.blendMode=this._resolveBlend(o[b].blendMode,E),p.startParticle=o[b].startParticleIndex,x.clear();let v=b;for(;v<o.length&&this._resolveBlend(o[v].blendMode,E)===p.blendMode;){const T=U[o[v].renderStyleIndex].textureIndices[0];if(!x.has(T)){if(x.size>=_)break;const f=x.size;x.set(T,f),p.glTextures[f]=c.glTextures[T]||null,p.remaps[f]=c.remaps[T]||null,p.numTextures=f+1}const d=x.get(T),w=o[v];if(I)I.patchSlot(w.startParticleIndex,w.numParticles,d);else for(let f=0;f<w.numParticles;f++){const h=(w.startParticleIndex+f)*BYTES_PER_PARTICLE,y=a.getUint32(h,!0);a.setUint32(h,y&4294967040|d,!0)}p.numParticles+=w.numParticles,v++}this._batchCount++,b=v}const B=i.isEnabled(i.SCISSOR_TEST);try{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){const g=t;g.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,g.textureWidth,g.textureHeight)}else{const g=t;g.uploadAndBind(DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTexture,DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTextureWidth,g.textureWidth)}i.uniform3f(e.uCameraRight,1,0,0),i.uniform3f(e.uCameraUp,0,-1,0),i.uniform3f(e.uCameraDir,0,0,-1);const p=c.viewportWidth,v=c.viewportHeight,T=c.zoom||1,d=this._orthoMatrix;d.fill(0),d[0]=2/p*T,d[5]=-2/v*T,d[10]=1,d[15]=1,d[12]=-1-2/p*T*c.scrollX,d[13]=1+2/v*T*c.scrollY,i.uniformMatrix4fv(e.uViewProjMatrix,!1,d);const w=v>0?p/v:1;e.uViewportAspect&&i.uniform1f(e.uViewportAspect,w),e.uWorldAlpha&&i.uniform1f(e.uWorldAlpha,c.worldAlpha);const f=c.model,h=this._modelMatrix;h[0]=f.a,h[1]=f.b,h[2]=0,h[3]=0,h[4]=f.c,h[5]=f.d,h[6]=0,h[7]=0,h[8]=0,h[9]=0,h[10]=1,h[11]=0,h[12]=f.tx,h[13]=f.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);const y=u?PARTICLE_TEXTURE_START_UNIT_GL1:PARTICLE_TEXTURE_START_UNIT;i.enable(i.BLEND);for(let g=0;g<this._batchCount;g++){const P=this._batchPool[g];this._applyBlendMode(i,P.blendMode);for(let m=0;m<P.numTextures;m++){const C=y+m;i.activeTexture(i.TEXTURE0+C),i.bindTexture(i.TEXTURE_2D,P.glTextures[m]),i.uniform1i(e.uTextures[m],C);const R=P.remaps[m];R?i.uniform4f(e.uTexRemaps[m],R.x,R.y,R.width,R.height):i.uniform4f(e.uTexRemaps[m],0,0,1,1)}const D=P.startParticle*6,S=P.numParticles*6;i.drawElements(i.TRIANGLES,S,e.indexType,D*(e.indexType===i.UNSIGNED_INT?4:2))}}finally{u?(i.disableVertexAttribArray(e.aIdLocation),i.bindBuffer(i.ARRAY_BUFFER,null),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,null)):i.bindVertexArray(null),i.useProgram(null),B&&i.enable(i.SCISSOR_TEST)}}_resolveBlend(e,t){return t&&e>=0&&e<t.length?t[e]:e}_applyBlendMode(e,t){switch(t){default:case 0:e.blendFuncSeparate(e.ONE,e.ONE_MINUS_SRC_ALPHA,e.ONE,e.ONE_MINUS_SRC_ALPHA);break;case 1:e.blendFunc(e.ONE,e.ONE);break;case 2:e.blendFunc(e.DST_COLOR,e.ONE_MINUS_SRC_ALPHA);break}}}const sharedRenderer=new NeutrinoRenderer;class Effect extends Phaser.GameObjects.GameObject{constructor(t,a,r){r=Object.assign({position:[0,0,0],angle:0,scale:1,pause:Pause.BEFORE_UPDATE_OR_RENDER,generatorsPaused:!1},r||{});super(a,"Neutrino");n(this,"gamePlugin");n(this,"effectModel");n(this,"effect",null);n(this,"x",0);n(this,"y",0);n(this,"z",0);n(this,"angle",0);n(this,"rotation",0);n(this,"scaleX",1);n(this,"scaleY",1);n(this,"scrollFactorX",1);n(this,"scrollFactorY",1);n(this,"alpha",1);n(this,"depth",0);n(this,"blendMode",Phaser.BlendModes.NORMAL);n(this,"_dataTextureManager",null);n(this,"_worldPosition");n(this,"_worldScaledPosition");n(this,"_worldRotation",[0,0,0,1]);n(this,"_worldScale");n(this,"_tempMatrix1");n(this,"_tempMatrix2");n(this,"_unpauseOnUpdateRender");n(this,"_glTexturesScratch",[]);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();const o={paused:r.pause!==Pause.NO,generatorsPaused:r.generatorsPaused};this.effectModel&&(this.effect=this.effectModel.effectModel.createInstance(this._renderPosition(),this._renderRotation(),o),this._dataTextureManager=this.gamePlugin.ctx.isWebGL1?new DataTextureManagerGL1(this.gamePlugin.ctx.gl,this.effect.particleDataUint32,this.effect.dataTextureWidth,this.effect.dataTextureHeight):new DataTextureManager(this.gamePlugin.ctx.gl,this.effect.particleDataUint32,this.effect.dataTextureWidth,this.effect.dataTextureHeight),this.gamePlugin.ctx.ensureIndexBuffer(this.effectModel.effectModel.totalParticles))}get ready(){return this.effectModel!=null&&this.effectModel.ready&&this.effect!=null}preUpdate(t,a){this.ready&&(this._checkUnpauseOnUpdateRender()||this._updateWorldTransform(),this.effect.update(a/1e3,this._renderPosition(),this._renderRotation()))}renderWebGL(t,a,r){if(!this.ready||this.alpha<=0)return;this._checkUnpauseOnUpdateRender();const o=this.gamePlugin.ctx,l=this._dataTextureManager;this.effect.construct([0,0,-1]);const c=this.effect.renderInstructions,i=c._totalParticles;if(i===0)return;t.flush();const u=this.effectModel.effectModel.textures.length,_=this._glTexturesScratch;_.length=u;for(let E=0;E<u;++E)_[E]=this.effectModel.glTexture(E);const U=this._worldScale;try{sharedRenderer.render(o,l,this.effect.particleDataView,this.effect.particleDataUint32,c,i,{model:{a:U,b:0,c:0,d:U,tx:0,ty:0},viewportWidth:t.width,viewportHeight:t.height,scrollX:r.scrollX*this.scrollFactorX,scrollY:r.scrollY*this.scrollFactorY,zoom:r.zoom,worldAlpha:this.alpha,glTextures:_,remaps:this.effectModel.texturesRemap,renderStyles:this.effect.model.renderStyles,materials:this.effect.model.materials}),l.advance()}finally{t.pipelines.rebind()}}restart(t){this.ready&&(this._applyTransformOptions(t),this._updateWorldTransform(),this.effect.restart(this._renderPosition(),this._renderRotation()))}resetPosition(t){this.ready&&(this._applyTransformOptions(t),this._updateWorldTransform(),this.effect.resetPosition(this._renderPosition(),this._renderRotation()))}pause(){this.ready&&this.effect.pauseAllEmitters()}unpause(){this.ready&&this.effect.unpauseAllEmitters()}get paused(){return this.ready?this.effect.areAllEmittersPaused():!1}pauseGenerators(){this.ready&&this.effect.pauseGeneratorsInAllEmitters()}unpauseGenerators(){this.ready&&this.effect.unpauseGeneratorsInAllEmitters()}get generatorsPaused(){return this.ready?this.effect.areGeneratorsInAllEmittersPaused():!1}setPropertyInAllEmitters(t,a){this.ready&&this.effect.setPropertyInAllEmitters(t,a)}getNumParticles(){return this.ready?this.effect.getNumParticles():0}setScrollFactor(t,a){return this.scrollFactorX=t,this.scrollFactorY=a===void 0?t:a,this}setAlpha(t,a,r,o){return this.alpha=t===void 0?1:t,this}destroy(t){this._dataTextureManager&&(this._dataTextureManager.destroy(),this._dataTextureManager=null),super.destroy(t)}_applyTransformOptions(t){t&&(t.position&&(this.x=t.position[0],this.y=t.position[1],this.z=t.position[2]),t.angle!==void 0?(this.angle=t.angle,this.rotation=Phaser.Math.DegToRad(t.angle)):t.rotation!==void 0&&(this.rotation=t.rotation,this.angle=Phaser.Math.RadToDeg(t.rotation)))}_updateWorldTransform(){const t=(o,l)=>{if(!o)return;const c=this._tempMatrix2;c.applyITRS(o.x,o.y,o.rotation,o.scaleX,o.scaleY),c.multiply(l.matrix,l.matrix),l.angle+=o.angle,l.scale*=(o.scaleX+o.scaleY)*.5,t(o.parentContainer,l)},a={matrix:this._tempMatrix1.loadIdentity(),angle:this.angle,scale:(this.scaleX+this.scaleY)*.5};t(this.parentContainer,a),a.matrix.transformPoint(this.x,this.y,this._worldPosition),this._worldRotation=this.gamePlugin.neutrino.axisangle2quat_([0,0,1],a.angle),this._worldScale=a.scale;const r=this._worldScale!==0?1/this._worldScale:0;this._worldScaledPosition[0]=this._worldPosition.x*r,this._worldScaledPosition[1]=this._worldPosition.y*r,this._worldScaledPosition[2]=this.z*r}_renderPosition(){return this._worldScaledPosition}_renderRotation(){return this._worldRotation}_checkUnpauseOnUpdateRender(){return this._unpauseOnUpdateRender?(this.resetPosition(),this.unpause(),this._unpauseOnUpdateRender=!1,!0):!1}}class PerspectiveProjection{constructor(e){n(this,"_angleTan",0);n(this,"_screenWidth",0);n(this,"_screenPosX",0);n(this,"_screenPosY",0);n(this,"_z",0);n(this,"_near",0);this.horizontalAngle=e}set horizontalAngle(e){this._angleTan=Math.tan(e*.5/180*Math.PI)}setScreenFrame(e){this._screenWidth=e.width,this._screenPosX=e.x+e.width*.5,this._screenPosY=e.y+e.height*.5,this._z=this._screenWidth*.5/this._angleTan,this._near=this._z*.99}transformPosition(e,t){if(t[2]>this._near)return!1;const a=this._getScale(t);return e[0]=(t[0]-this._screenPosX)*a+this._screenPosX,e[1]=(t[1]-this._screenPosY)*a+this._screenPosY,!0}transformSize(e,t,a){const r=this._getScale(t);e[0]=a[0]*r,e[1]=a[1]*r}_getScale(e){return this._z/(this._z-e[2])}}let _installed=!1;function installPlugin(){_installed||(_installed=!0,Phaser.Loader.FileTypesManager.register("neutrino",function(s,e,t,a){if(Array.isArray(s))for(const r of s)this.addFile(new NeutrinoFile(this,r));else typeof s=="object"?this.addFile(new NeutrinoFile(this,s)):this.addFile(new NeutrinoFile(this,{key:s,url:e,options:t,xhrSettings:a}));return this}),Phaser.GameObjects.GameObjectFactory.register("neutrino",function(s,e){const t=new Effect(s,this.scene,e);return this.displayList.add(t),this.updateList.add(t),t}),Phaser.GameObjects.GameObjectCreator.register("neutrino",function(s){const e=new Effect(s.effectModel,this.scene,s);return s.add!==!1&&(this.scene.sys.displayList.add(e),this.scene.sys.updateList.add(e)),e}))}installPlugin();exports.DataTextureManager=DataTextureManager;exports.Effect=Effect;exports.EffectModel=EffectModel;exports.GamePlugin=GamePlugin;exports.NeutrinoContext=NeutrinoContext;exports.NeutrinoFile=NeutrinoFile;exports.NeutrinoRenderer=NeutrinoRenderer;exports.Pause=Pause;exports.PerspectiveProjection=PerspectiveProjection;exports.installPlugin=installPlugin;
1313
+ })(this.gamePlugin.neutrino);`;this.effectModel=eval(evalScript),this._startLoadTextures(options.atlases)}get ready(){return this._numTexturesToLoadLeft===0}glTexture(s){const e=this.textureFrames[s];return e?e.glTexture.webGLTexture:null}_startLoadTextures(s){const e=this.effectModel.textures.length;this._numTexturesToLoadLeft=e;for(let t=0;t<e;++t){const n=this.effectModel.textures[t],r=this.gamePlugin.texturesBasePath+n,o=noext(r);let l=this._findFrameInAtlases(s,n);if(l||(l=this._findFrameInAtlases(s,noext(n))),!l&&this.scene.sys.textures.exists(o)){const c=this.scene.sys.textures.get(o);l=c.get(c.firstFrame)}l?this._onTextureLoaded(t,l):(this.scene.load.once("filecomplete-image-"+o,((c,i)=>()=>{const u=this.scene.sys.textures.get(i);this._onTextureLoaded(c,u.get(u.firstFrame))})(t,o)),this.scene.load.image(o,r))}}_findFrameInAtlases(s,e){for(let t=0;t<s.length;++t){const n=s[t];if(!this.scene.sys.textures.exists(n))continue;const r=this.scene.sys.textures.get(n);if(r.has(e))return r.get(e)}return null}_onTextureLoaded(s,e){this.textureFrames[s]=e,this._numTexturesToLoadLeft--,this._numTexturesToLoadLeft===0&&this._initTexturesRemapIfNeeded()}_fullCover(s){return s.width===s.source.width&&s.height===s.source.height}_initTexturesRemapIfNeeded(){let s=!1;for(let e=0;e<this.textureFrames.length;++e)if(!this._fullCover(this.textureFrames[e])){s=!0;break}if(s)for(let e=0;e<this.textureFrames.length;++e){const t=this.textureFrames[e];this.texturesRemap[e]={x:t.u0,y:1-t.v1,width:t.u1-t.u0,height:t.v1-t.v0}}}}class NeutrinoFile extends Phaser.Loader.File{constructor(t,n){const{key:r,url:o,options:l,xhrSettings:c}=n;super(t,{type:"binary",extension:"js",responseType:"text",key:r,url:o,xhrSettings:c});a(this,"_options");this.cache=t.cacheManager.binary,this._options=l}onProcess(){this.state=Phaser.Loader.FILE_PROCESSING;const t=this.xhrLoader.response;this.data=new EffectModel(this.loader.scene,t,this._options),this.onProcessComplete()}}var Pause=(s=>(s[s.NO=0]="NO",s[s.BEFORE_UPDATE_OR_RENDER=1]="BEFORE_UPDATE_OR_RENDER",s[s.YES=2]="YES",s))(Pause||{});const RING_BUFFER_SIZE$1=3;class DataTextureManager{constructor(e,t,n,r){a(this,"textureWidth");a(this,"textureHeight");a(this,"_gl");a(this,"_floatView");a(this,"_textures",[]);a(this,"_currentIndex",0);const o=e.getParameter(e.MAX_TEXTURE_SIZE);if(n>o||r>o)throw new Error(`NeutrinoParticles: data texture ${n}x${r} exceeds GL MAX_TEXTURE_SIZE (${o}). Reduce effect particle capacity.`);this._gl=e,this.textureWidth=n,this.textureHeight=r,this._floatView=new Float32Array(t.buffer,t.byteOffset,t.length);for(let l=0;l<RING_BUFFER_SIZE$1;l++){const c=e.createTexture();e.bindTexture(e.TEXTURE_2D,c),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.RGBA32F,n,r,0,e.RGBA,e.FLOAT,null),this._textures.push(c)}e.bindTexture(e.TEXTURE_2D,null)}get currentTexture(){return this._textures[this._currentIndex]}uploadAndBind(e){const t=this._gl;t.activeTexture(t.TEXTURE0+e),t.bindTexture(t.TEXTURE_2D,this._textures[this._currentIndex]),t.pixelStorei(t.UNPACK_ALIGNMENT,4),t.pixelStorei(t.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),t.pixelStorei(t.UNPACK_FLIP_Y_WEBGL,!1),t.texSubImage2D(t.TEXTURE_2D,0,0,0,this.textureWidth,this.textureHeight,t.RGBA,t.FLOAT,this._floatView)}advance(){this._currentIndex=(this._currentIndex+1)%RING_BUFFER_SIZE$1}destroy(){const e=this._gl;for(const t of this._textures)t&&e.deleteTexture(t);this._textures=[]}}class MetaTextureManager{constructor(e,t){a(this,"bytes");a(this,"_u32");this.bytes=new Uint8Array(e*t*4),this._u32=new Uint32Array(this.bytes.buffer)}build(e,t){const n=this._u32;for(let r=0;r<t;r++){const o=r*16,l=r*4;n[l]=e[o],n[l+1]=e[o+12],n[l+2]=e[o+13],n[l+3]=e[o+14]}}patchSlot(e,t,n){const r=this.bytes;let o=e*16;for(let l=0;l<t;l++)r[o]=n,o+=16}}const RING_BUFFER_SIZE=3;class DataTextureManagerGL1{constructor(e,t,n,r){a(this,"textureWidth");a(this,"textureHeight");a(this,"meta");a(this,"_gl");a(this,"_floatView");a(this,"_dataTextures",[]);a(this,"_metaTextures",[]);a(this,"_currentIndex",0);const o=e.getParameter(e.MAX_TEXTURE_SIZE);if(n>o||r>o)throw new Error(`NeutrinoParticles: data texture ${n}x${r} exceeds GL MAX_TEXTURE_SIZE (${o}). Reduce effect particle capacity.`);if(n*r>16777216)throw new Error(`NeutrinoParticles: data texture ${n}x${r} exceeds the WebGL1 render path texel cap (2^24). Reduce effect particle capacity.`);this._gl=e,this.textureWidth=n,this.textureHeight=r,this._floatView=new Float32Array(t.buffer,t.byteOffset,t.length),this.meta=new MetaTextureManager(n,r);const l=e.getParameter(e.ACTIVE_TEXTURE);e.activeTexture(e.TEXTURE0);const c=e.getParameter(e.TEXTURE_BINDING_2D);for(let i=0;i<RING_BUFFER_SIZE;i++)this._dataTextures.push(this._createTexture(e.FLOAT)),this._metaTextures.push(this._createTexture(e.UNSIGNED_BYTE));e.bindTexture(e.TEXTURE_2D,c),e.activeTexture(l)}get currentDataTexture(){return this._dataTextures[this._currentIndex]}get currentMetaTexture(){return this._metaTextures[this._currentIndex]}upload(e,t,n){const r=this._gl,o=this.textureWidth,l=Math.min(this.textureHeight,Math.ceil(e*4/o));if(l===0)return;const c=l*o*4;r.pixelStorei(r.UNPACK_FLIP_Y_WEBGL,!1),r.pixelStorei(r.UNPACK_PREMULTIPLY_ALPHA_WEBGL,!1),r.pixelStorei(r.UNPACK_ALIGNMENT,4),r.activeTexture(r.TEXTURE0+t),r.bindTexture(r.TEXTURE_2D,this.currentDataTexture),r.texSubImage2D(r.TEXTURE_2D,0,0,0,o,l,r.RGBA,r.FLOAT,this._floatView.subarray(0,c)),r.activeTexture(r.TEXTURE0+n),r.bindTexture(r.TEXTURE_2D,this.currentMetaTexture),r.texSubImage2D(r.TEXTURE_2D,0,0,0,o,l,r.RGBA,r.UNSIGNED_BYTE,this.meta.bytes.subarray(0,c))}advance(){this._currentIndex=(this._currentIndex+1)%RING_BUFFER_SIZE}destroy(){const e=this._gl;for(const t of this._dataTextures)e.deleteTexture(t);for(const t of this._metaTextures)e.deleteTexture(t);this._dataTextures=[],this._metaTextures=[]}_createTexture(e){const t=this._gl,n=t.createTexture();return t.bindTexture(t.TEXTURE_2D,n),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MIN_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_MAG_FILTER,t.NEAREST),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_S,t.CLAMP_TO_EDGE),t.texParameteri(t.TEXTURE_2D,t.TEXTURE_WRAP_T,t.CLAMP_TO_EDGE),t.texImage2D(t.TEXTURE_2D,0,t.RGBA,this.textureWidth,this.textureHeight,0,t.RGBA,e,null),n}}const DATA_TEXTURE_UNIT=0,META_TEXTURE_UNIT=1,PARTICLE_TEXTURE_START_UNIT=1,PARTICLE_TEXTURE_START_UNIT_GL1=2,FLOATS_PER_PARTICLE=16,BYTES_PER_PARTICLE=FLOATS_PER_PARTICLE*4,MAX_BATCHES=64;class DrawBatch{constructor(){a(this,"blendMode",0);a(this,"startParticle",0);a(this,"numParticles",0);a(this,"numTextures",0);a(this,"glTextures",new Array(NeutrinoContext.MAX_BATCH_TEXTURES).fill(null));a(this,"remaps",new Array(NeutrinoContext.MAX_BATCH_TEXTURES).fill(null))}reset(){this.blendMode=0,this.startParticle=0,this.numParticles=0,this.numTextures=0}}class NeutrinoRenderer{constructor(){a(this,"_orthoMatrix",new Float32Array(16));a(this,"_perspMatrix",new Float32Array(16));a(this,"_vpMatrix",new Float32Array(16));a(this,"_projFrame",{x:0,y:0,width:0,height:0});a(this,"_modelMatrix",new Float32Array(16));a(this,"_texSlotMap",new Map);a(this,"_batchPool");a(this,"_batchCount",0);this._batchPool=[];for(let e=0;e<MAX_BATCHES;e++)this._batchPool.push(new DrawBatch)}render(e,t,n,r,o,l,c){if(l===0)return;const i=e.gl,u=e.isWebGL1,_=e.maxBatchTextures,y=c.renderStyles,E=c.materials,I=u?t.meta:null;I&&I.build(r,l),this._batchCount=0;const m=this._texSlotMap;for(;this._batchPool.length<o.length;)this._batchPool.push(new DrawBatch);let b=0;for(;b<o.length;){const f=this._batchPool[this._batchCount];f.reset(),f.blendMode=this._resolveBlend(o[b].blendMode,E),f.startParticle=o[b].startParticleIndex,m.clear();let v=b;for(;v<o.length&&this._resolveBlend(o[v].blendMode,E)===f.blendMode;){const g=y[o[v].renderStyleIndex].textureIndices[0];if(!m.has(g)){if(m.size>=_)break;const x=m.size;m.set(g,x),f.glTextures[x]=c.glTextures[g]||null,f.remaps[x]=c.remaps[g]||null,f.numTextures=x+1}const d=m.get(g),w=o[v];if(I)I.patchSlot(w.startParticleIndex,w.numParticles,d);else if(d!==0)for(let x=0;x<w.numParticles;x++){const h=(w.startParticleIndex+x)*BYTES_PER_PARTICLE,U=n.getUint32(h,!0);n.setUint32(h,U&4294967040|d,!0)}f.numParticles+=w.numParticles,v++}this._batchCount++,b=v}const B=i.isEnabled(i.SCISSOR_TEST);try{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){const p=t;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)}else{const p=t;p.uploadAndBind(DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTexture,DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTextureWidth,p.textureWidth)}i.uniform3f(e.uCameraRight,1,0,0),i.uniform3f(e.uCameraUp,0,-1,0),i.uniform3f(e.uCameraDir,0,0,-1);const f=c.viewportWidth,v=c.viewportHeight,g=c.zoom||1,d=this._orthoMatrix;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){const p=this._projFrame;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)}else i.uniformMatrix4fv(e.uViewProjMatrix,!1,d);const w=v>0?f/v:1;e.uViewportAspect&&i.uniform1f(e.uViewportAspect,w),e.uWorldAlpha&&i.uniform1f(e.uWorldAlpha,c.worldAlpha);const x=c.model,h=this._modelMatrix;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);const U=u?PARTICLE_TEXTURE_START_UNIT_GL1:PARTICLE_TEXTURE_START_UNIT;i.enable(i.BLEND);for(let p=0;p<this._batchCount;p++){const P=this._batchPool[p];this._applyBlendMode(i,P.blendMode);for(let T=0;T<P.numTextures;T++){const C=U+T;i.activeTexture(i.TEXTURE0+C),i.bindTexture(i.TEXTURE_2D,P.glTextures[T]),i.uniform1i(e.uTextures[T],C);const R=P.remaps[T];R?i.uniform4f(e.uTexRemaps[T],R.x,R.y,R.width,R.height):i.uniform4f(e.uTexRemaps[T],0,0,1,1)}const D=P.startParticle*6,S=P.numParticles*6;i.drawElements(i.TRIANGLES,S,e.indexType,D*(e.indexType===i.UNSIGNED_INT?4:2))}}finally{u?(i.disableVertexAttribArray(e.aIdLocation),i.bindBuffer(i.ARRAY_BUFFER,null),i.bindBuffer(i.ELEMENT_ARRAY_BUFFER,null)):i.bindVertexArray(null),i.useProgram(null),B&&i.enable(i.SCISSOR_TEST)}}_mulMatrix(e,t,n){for(let r=0;r<16;r+=4){const o=n[r],l=n[r+1],c=n[r+2],i=n[r+3];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}}_resolveBlend(e,t){return t&&e>=0&&e<t.length?t[e]:e}_applyBlendMode(e,t){switch(t){default:case 0:e.blendFuncSeparate(e.ONE,e.ONE_MINUS_SRC_ALPHA,e.ONE,e.ONE_MINUS_SRC_ALPHA);break;case 1:e.blendFunc(e.ONE,e.ONE);break;case 2:e.blendFunc(e.DST_COLOR,e.ONE_MINUS_SRC_ALPHA);break}}}const sharedRenderer=new NeutrinoRenderer;class Effect extends Phaser.GameObjects.GameObject{constructor(t,n,r){r=Object.assign({position:[0,0,0],angle:0,scale:1,pause:Pause.BEFORE_UPDATE_OR_RENDER,generatorsPaused:!1},r||{});super(n,"Neutrino");a(this,"gamePlugin");a(this,"effectModel");a(this,"effect",null);a(this,"x",0);a(this,"y",0);a(this,"z",0);a(this,"angle",0);a(this,"rotation",0);a(this,"scaleX",1);a(this,"scaleY",1);a(this,"scrollFactorX",1);a(this,"scrollFactorY",1);a(this,"alpha",1);a(this,"depth",0);a(this,"blendMode",Phaser.BlendModes.NORMAL);a(this,"projection");a(this,"_dataTextureManager",null);a(this,"_worldPosition");a(this,"_worldScaledPosition");a(this,"_worldRotation",[0,0,0,1]);a(this,"_worldScale");a(this,"_tempMatrix1");a(this,"_tempMatrix2");a(this,"_unpauseOnUpdateRender");a(this,"_glTexturesScratch",[]);this.gamePlugin=n.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();const o={paused:r.pause!==Pause.NO,generatorsPaused:r.generatorsPaused};this.effectModel&&(this.effect=this.effectModel.effectModel.createInstance(this._renderPosition(),this._renderRotation(),o),this._dataTextureManager=this.gamePlugin.ctx.isWebGL1?new DataTextureManagerGL1(this.gamePlugin.ctx.gl,this.effect.particleDataUint32,this.effect.dataTextureWidth,this.effect.dataTextureHeight):new DataTextureManager(this.gamePlugin.ctx.gl,this.effect.particleDataUint32,this.effect.dataTextureWidth,this.effect.dataTextureHeight),this.gamePlugin.ctx.ensureIndexBuffer(this.effectModel.effectModel.totalParticles))}get ready(){return this.effectModel!=null&&this.effectModel.ready&&this.effect!=null}preUpdate(t,n){this.ready&&(this._checkUnpauseOnUpdateRender()||this._updateWorldTransform(),this.effect.update(n/1e3,this._renderPosition(),this._renderRotation()))}renderWebGL(t,n,r){if(!this.ready||this.alpha<=0)return;this._checkUnpauseOnUpdateRender();const o=this.gamePlugin.ctx,l=this._dataTextureManager;this.effect.construct([0,0,-1]);const c=this.effect.renderInstructions,i=c._totalParticles;if(i===0)return;t.flush();const u=this.effectModel.effectModel.textures.length,_=this._glTexturesScratch;_.length=u;for(let E=0;E<u;++E)_[E]=this.effectModel.glTexture(E);const y=this._worldScale;try{sharedRenderer.render(o,l,this.effect.particleDataView,this.effect.particleDataUint32,c,i,{model:{a:y,b:0,c:0,d:y,tx:0,ty:0},viewportWidth:t.width,viewportHeight:t.height,scrollX:r.scrollX*this.scrollFactorX,scrollY:r.scrollY*this.scrollFactorY,zoom:r.zoom,worldAlpha:this.alpha,projection:this.projection,glTextures:_,remaps:this.effectModel.texturesRemap,renderStyles:this.effect.model.renderStyles,materials:this.effect.model.materials}),l.advance()}finally{t.pipelines.rebind()}}restart(t){this.ready&&(this._applyTransformOptions(t),this._updateWorldTransform(),this.effect.restart(this._renderPosition(),this._renderRotation()))}resetPosition(t){this.ready&&(this._applyTransformOptions(t),this._updateWorldTransform(),this.effect.resetPosition(this._renderPosition(),this._renderRotation()))}pause(){this.ready&&this.effect.pauseAllEmitters()}unpause(){this.ready&&this.effect.unpauseAllEmitters()}get paused(){return this.ready?this.effect.areAllEmittersPaused():!1}pauseGenerators(){this.ready&&this.effect.pauseGeneratorsInAllEmitters()}unpauseGenerators(){this.ready&&this.effect.unpauseGeneratorsInAllEmitters()}get generatorsPaused(){return this.ready?this.effect.areGeneratorsInAllEmittersPaused():!1}setPropertyInAllEmitters(t,n){this.ready&&this.effect.setPropertyInAllEmitters(t,n)}setPropertyInEmitter(t,n,r){this.ready&&this.effect.setPropertyInEmitter(t,n,r)}getEmitterPropertyValue(t,n){if(this.ready)return n===void 0?this.effect.getEmitterPropertyValue(t):this.effect.getEmitterPropertyValue(t,n)}hasEmitterProperty(t,n){return this.ready?n===void 0?this.effect.hasEmitterProperty(t):this.effect.hasEmitterProperty(t,n):!1}getEmitterProperties(){return this.ready?this.effect.getEmitterProperties():[]}getNumParticles(){return this.ready?this.effect.getNumParticles():0}setScrollFactor(t,n){return this.scrollFactorX=t,this.scrollFactorY=n===void 0?t:n,this}setAlpha(t,n,r,o){return this.alpha=t===void 0?1:t,this}destroy(t){this._dataTextureManager&&(this._dataTextureManager.destroy(),this._dataTextureManager=null),super.destroy(t)}_applyTransformOptions(t){t&&(t.position&&(this.x=t.position[0],this.y=t.position[1],this.z=t.position[2]),t.angle!==void 0?(this.angle=t.angle,this.rotation=Phaser.Math.DegToRad(t.angle)):t.rotation!==void 0&&(this.rotation=t.rotation,this.angle=Phaser.Math.RadToDeg(t.rotation)))}_updateWorldTransform(){const t=(o,l)=>{if(!o)return;const c=this._tempMatrix2;c.applyITRS(o.x,o.y,o.rotation,o.scaleX,o.scaleY),c.multiply(l.matrix,l.matrix),l.angle+=o.angle,l.scale*=(o.scaleX+o.scaleY)*.5,t(o.parentContainer,l)},n={matrix:this._tempMatrix1.loadIdentity(),angle:this.angle,scale:(this.scaleX+this.scaleY)*.5};t(this.parentContainer,n),n.matrix.transformPoint(this.x,this.y,this._worldPosition),this._worldRotation=this.gamePlugin.neutrino.axisangle2quat_([0,0,1],n.angle),this._worldScale=n.scale;const r=this._worldScale!==0?1/this._worldScale:0;this._worldScaledPosition[0]=this._worldPosition.x*r,this._worldScaledPosition[1]=this._worldPosition.y*r,this._worldScaledPosition[2]=this.z*r}_renderPosition(){return this._worldScaledPosition}_renderRotation(){return this._worldRotation}_checkUnpauseOnUpdateRender(){return this._unpauseOnUpdateRender?(this.resetPosition(),this.unpause(),this._unpauseOnUpdateRender=!1,!0):!1}}class PerspectiveProjection{constructor(e){a(this,"_angleTan",0);a(this,"_screenWidth",0);a(this,"_screenPosX",0);a(this,"_screenPosY",0);a(this,"_z",0);a(this,"_near",0);this.horizontalAngle=e}set horizontalAngle(e){this._angleTan=Math.tan(e*.5/180*Math.PI)}setScreenFrame(e){this._screenWidth=e.width,this._screenPosX=e.x+e.width*.5,this._screenPosY=e.y+e.height*.5,this._z=this._screenWidth*.5/this._angleTan,this._near=this._z*.99}transformPosition(e,t){if(t[2]>this._near)return!1;const n=this._getScale(t);return e[0]=(t[0]-this._screenPosX)*n+this._screenPosX,e[1]=(t[1]-this._screenPosY)*n+this._screenPosY,!0}transformSize(e,t,n){const r=this._getScale(t);e[0]=n[0]*r,e[1]=n[1]*r}_getScale(e){return this._z/(this._z-e[2])}writeMatrix(e){const t=this._z,n=Number.isFinite(t)&&t>0?1/t:0;e.fill(0),e[0]=1,e[5]=1,e[8]=-this._screenPosX*n,e[9]=-this._screenPosY*n,e[11]=-n,e[15]=1}}let _installed=!1;function installPlugin(){_installed||(_installed=!0,Phaser.Loader.FileTypesManager.register("neutrino",function(s,e,t,n){if(Array.isArray(s))for(const r of s)this.addFile(new NeutrinoFile(this,r));else typeof s=="object"?this.addFile(new NeutrinoFile(this,s)):this.addFile(new NeutrinoFile(this,{key:s,url:e,options:t,xhrSettings:n}));return this}),Phaser.GameObjects.GameObjectFactory.register("neutrino",function(s,e){const t=new Effect(s,this.scene,e);return this.displayList.add(t),this.updateList.add(t),t}),Phaser.GameObjects.GameObjectCreator.register("neutrino",function(s){const e=new Effect(s.effectModel,this.scene,s);return s.add!==!1&&(this.scene.sys.displayList.add(e),this.scene.sys.updateList.add(e)),e}))}installPlugin();exports.DataTextureManager=DataTextureManager;exports.Effect=Effect;exports.EffectModel=EffectModel;exports.GamePlugin=GamePlugin;exports.NeutrinoContext=NeutrinoContext;exports.NeutrinoFile=NeutrinoFile;exports.NeutrinoRenderer=NeutrinoRenderer;exports.Pause=Pause;exports.PerspectiveProjection=PerspectiveProjection;exports.installPlugin=installPlugin;
1169
1314
  //# sourceMappingURL=neutrinoparticles.js-v1.1-phaser.cjs.js.map