@neutrinoparticles/js-v1.1-phaser 1.0.1 → 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,6 +1,6 @@
1
- var F = Object.defineProperty;
2
- var N = (s, e, t) => e in s ? F(s, e, { enumerable: !0, configurable: !0, writable: !0, value: t }) : s[e] = t;
3
- var n = (s, e, t) => N(s, typeof e != "symbol" ? e + "" : e, t);
1
+ var K = Object.defineProperty;
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 PIXI7 renderer is 2D, world Z is dropped at gl_Position.
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 current PIXI7 renderer is fundamentally
95
- // 2D (worldPos.z is dropped at gl_Position for quads), so
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
@@ -175,7 +180,7 @@ void main() {
175
180
  bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
176
181
 
177
182
  // #236: set when the segment itself is degenerate (much shorter than
178
- // the ribbon half-width) - the pair is discarded like nearPlaneClipped.
183
+ // the ribbon half-width) the pair is discarded like nearPlaneClipped.
179
184
  bool stripDegenerate = false;
180
185
 
181
186
  // Aspect correction so the 2D Faced ortho gives visually-perpendicular
@@ -261,7 +266,7 @@ void main() {
261
266
  vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
262
267
  Bmul2 = sign(dot(B2raw, K2));
263
268
  vec3 B2 = B2raw * Bmul2;
264
- // #236: see ik1 - inner vertex at the ribbon half-width.
269
+ // #236: see ik1 inner vertex at the ribbon half-width.
265
270
  float ik2 = halfSize2;
266
271
  worldK2 = p2 + K2 * halfSize2;
267
272
  worldB2 = p2 + B2 * halfSize2;
@@ -279,10 +284,16 @@ void main() {
279
284
  // Faced: screen-space NDC frame around dir12.
280
285
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
281
286
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
282
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
283
287
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
284
288
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
285
- vec2 ndc3 = (sp3.xy / sp3.w) * aspectMul;
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;
286
297
 
287
298
  vec2 d01 = ndc1 - ndc0;
288
299
  vec2 d12 = ndc2 - ndc1;
@@ -333,7 +344,7 @@ void main() {
333
344
  vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
334
345
  Bmul2 = sign(dot(B, K2));
335
346
  vec2 B2 = B * Bmul2;
336
- // #236: see ik1 - inner vertex at the ribbon half-width.
347
+ // #236: see ik1 inner vertex at the ribbon half-width.
337
348
  float ik2 = ndcHalfSize2;
338
349
  k2 = ndc2 + K2 * ndcHalfSize2;
339
350
  b2 = ndc2 + B2 * ndcHalfSize2;
@@ -349,7 +360,7 @@ void main() {
349
360
  }
350
361
  }
351
362
 
352
- // #236: a noKnee (straight-cap) end carries no bend side of its own -
363
+ // #236: a noKnee (straight-cap) end carries no bend side of its own
353
364
  // its raw side label is always the +B side. If the segment's other
354
365
  // end is a real knee on the -B side, the mismatched labels would send
355
366
  // the ladder into the opposite-sides (twist) branch and flip the
@@ -376,6 +387,7 @@ void main() {
376
387
  Bmul2 = Bmul1;
377
388
  }
378
389
 
390
+
379
391
  // V coords (mirror of preview3 / GPU33 make*Strip):
380
392
  // V at outer knee (k) = (Bmul + 1) * 0.5
381
393
  // V at inner knee (IK) = 1 - V_outer ; bend (b) = outer
@@ -478,7 +490,7 @@ void main() {
478
490
  // Free projects the world-space vertex through MVP directly; Faced
479
491
  // reconstructs clip space from aspect-corrected NDC (mirror of preview3).
480
492
  if (stripDegenerate) {
481
- // #236: sub-visual segment - discard the pair entirely.
493
+ // #236: sub-visual segment discard the pair entirely.
482
494
  gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
483
495
  } else if (isFreeRotation) {
484
496
  gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
@@ -517,6 +529,10 @@ void main() {
517
529
  float((packedColor >> 16u) & 0xFFu) / 255.0,
518
530
  float((packedColor >> 24u) & 0xFFu) / 255.0
519
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;
520
536
 
521
537
  uint gridConfig = floatBitsToUint(t3.g);
522
538
  float gridWidth = max(float(gridConfig & 0xFFFFu), 1.0);
@@ -564,7 +580,23 @@ void main() {
564
580
 
565
581
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
566
582
  vec3 worldPos = position + posDisp;
567
- gl_Position = mvpMatrix * vec4(worldPos.xy, 0.0, 1.0);
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
+ }
568
600
 
569
601
  // Texture coordinates with grid + atlas remap
570
602
  vec2 baseTexCoord = TEX_COORD[vertexIndex];
@@ -784,6 +816,11 @@ void main() {
784
816
  // Colors: the meta M1 sample is the normalized RGBA directly.
785
817
  vec4 color1 = fetchMeta(curBase + 1.0);
786
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;
787
824
 
788
825
  // Neighbor RECORD indices (system.js convention: neighborIdxP0 on the
789
826
  // pair-cur meta, neighborIdxP3 on the pair-nex meta; boundary
@@ -919,10 +956,16 @@ void main() {
919
956
  // Faced: screen-space NDC frame around dir12.
920
957
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
921
958
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
922
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
923
959
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
924
960
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
925
- vec2 ndc3 = (sp3.xy / sp3.w) * aspectMul;
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;
926
969
 
927
970
  vec2 d01 = ndc1 - ndc0;
928
971
  vec2 d12 = ndc2 - ndc1;
@@ -1114,6 +1157,9 @@ void main() {
1114
1157
 
1115
1158
  // Meta M1: the packedColor u32 LE bytes ARE the normalized RGBA sample.
1116
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;
1117
1163
 
1118
1164
  // Meta M2: gridConfig — [gridW lo, gridW hi, gridH lo, gridH hi].
1119
1165
  vec4 m2 = fetchMeta(baseTexel + 2.0);
@@ -1160,7 +1206,23 @@ void main() {
1160
1206
 
1161
1207
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
1162
1208
  vec3 worldPos = position + posDisp;
1163
- gl_Position = mvpMatrix * vec4(worldPos.xy, 0.0, 1.0);
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
+ }
1164
1226
 
1165
1227
  // Texture coordinates with grid + atlas remap
1166
1228
  vec2 baseTexCoord = texCoordOf(corner);
@@ -1351,12 +1413,12 @@ void main() {
1351
1413
  throw new Error(
1352
1414
  `NeutrinoParticles (js-v1.1): the effect needs 32-bit indices (${e} particles) but this WebGL1 context lacks OES_element_index_uint.`
1353
1415
  );
1354
- const d = l ? new Uint32Array(e * 6) : new Uint16Array(e * 6);
1416
+ const c = l ? new Uint32Array(e * 6) : new Uint16Array(e * 6);
1355
1417
  for (let i = 0; i < e; i++) {
1356
- const u = i * 4, g = i * 6;
1357
- d[g + 0] = u + 0, d[g + 1] = u + 1, d[g + 2] = u + 2, d[g + 3] = u + 0, d[g + 4] = u + 2, d[g + 5] = u + 3;
1418
+ const u = i * 4, _ = i * 6;
1419
+ c[_ + 0] = u + 0, c[_ + 1] = u + 1, c[_ + 2] = u + 2, c[_ + 3] = u + 0, c[_ + 4] = u + 2, c[_ + 5] = u + 3;
1358
1420
  }
1359
- if (this._indexBuffer = t.createBuffer(), t.bindBuffer(t.ELEMENT_ARRAY_BUFFER, this._indexBuffer), t.bufferData(t.ELEMENT_ARRAY_BUFFER, d, t.STATIC_DRAW), this._isWebGL1) {
1421
+ 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) {
1360
1422
  const i = new Float32Array(e * 4);
1361
1423
  for (let u = 0; u < i.length; u++) i[u] = u;
1362
1424
  this._aIdBuffer = t.createBuffer(), t.bindBuffer(t.ARRAY_BUFFER, this._aIdBuffer), t.bufferData(t.ARRAY_BUFFER, i, t.STATIC_DRAW);
@@ -1433,33 +1495,33 @@ void main() {
1433
1495
  `, a = `precision mediump float;
1434
1496
  varying vec4 vC;
1435
1497
  void main() { gl_FragColor = vC; }
1436
- `, r = e.getParameter(e.VIEWPORT), o = e.getParameter(e.CURRENT_PROGRAM), l = e.getParameter(e.ARRAY_BUFFER_BINDING), d = e.getParameter(e.FRAMEBUFFER_BINDING), i = e.getParameter(e.ACTIVE_TEXTURE);
1498
+ `, 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);
1437
1499
  e.activeTexture(e.TEXTURE0);
1438
1500
  const u = e.getParameter(e.TEXTURE_BINDING_2D);
1439
1501
  e.activeTexture(e.TEXTURE1);
1440
- const g = e.getParameter(e.TEXTURE_BINDING_2D), I = e.isEnabled(e.BLEND), E = e.isEnabled(e.DEPTH_TEST), U = e.isEnabled(e.SCISSOR_TEST);
1441
- let x = null, b = null, B = null, p = null, v = null, T = null, c = -1, w = !1, f = null, h = 4, y = 0, _ = 0, P = 0, D = !1, S = !1;
1442
- const m = new Uint8Array(4);
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);
1443
1505
  try {
1444
- x = e.createProgram(), e.attachShader(x, this._compileShader(e.VERTEX_SHADER, t)), e.attachShader(x, this._compileShader(e.FRAGMENT_SHADER, a)), e.linkProgram(x);
1445
- const C = (R, K) => {
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) => {
1446
1508
  const M = e.createTexture();
1447
- 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, K), M;
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;
1448
1510
  };
1449
- e.activeTexture(e.TEXTURE0), b = C(e.FLOAT, new Float32Array([1, 0.5, 0.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), c = e.getAttribLocation(x, "aId"), w = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_ENABLED), f = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_BUFFER_BINDING), h = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_SIZE), y = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_TYPE), D = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_NORMALIZED), _ = e.getVertexAttrib(c, e.VERTEX_ATTRIB_ARRAY_STRIDE), P = e.getVertexAttribOffset(c, e.VERTEX_ATTRIB_ARRAY_POINTER), e.enableVertexAttribArray(c), e.vertexAttribPointer(c, 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);
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);
1450
1512
  } finally {
1451
- c >= 0 && (f && (e.bindBuffer(e.ARRAY_BUFFER, f), e.vertexAttribPointer(
1452
- c,
1513
+ d >= 0 && (x && (e.bindBuffer(e.ARRAY_BUFFER, x), e.vertexAttribPointer(
1514
+ d,
1453
1515
  h,
1454
- y,
1516
+ U,
1455
1517
  D,
1456
- _,
1457
- P
1458
- )), w ? e.enableVertexAttribArray(c) : e.disableVertexAttribArray(c)), 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, d), 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, g), e.activeTexture(i), I && e.enable(e.BLEND), E && e.enable(e.DEPTH_TEST), U && e.enable(e.SCISSOR_TEST), e.viewport(r[0], r[1], r[2], r[3]);
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]);
1459
1521
  }
1460
1522
  if (!S)
1461
1523
  throw new Error(
1462
- `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.`
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.`
1463
1525
  );
1464
1526
  }
1465
1527
  _initShader() {
@@ -1548,14 +1610,14 @@ return new NeutrinoEffect(ctx);
1548
1610
  const a = this.effectModel.textures[t], r = this.gamePlugin.texturesBasePath + a, o = noext(r);
1549
1611
  let l = this._findFrameInAtlases(s, a);
1550
1612
  if (l || (l = this._findFrameInAtlases(s, noext(a))), !l && this.scene.sys.textures.exists(o)) {
1551
- const d = this.scene.sys.textures.get(o);
1552
- l = d.get(d.firstFrame);
1613
+ const c = this.scene.sys.textures.get(o);
1614
+ l = c.get(c.firstFrame);
1553
1615
  }
1554
1616
  l ? this._onTextureLoaded(t, l) : (this.scene.load.once(
1555
1617
  "filecomplete-image-" + o,
1556
- /* @__PURE__ */ ((d, i) => () => {
1618
+ /* @__PURE__ */ ((c, i) => () => {
1557
1619
  const u = this.scene.sys.textures.get(i);
1558
- this._onTextureLoaded(d, u.get(u.firstFrame));
1620
+ this._onTextureLoaded(c, u.get(u.firstFrame));
1559
1621
  })(t, o)
1560
1622
  ), this.scene.load.image(o, r));
1561
1623
  }
@@ -1598,14 +1660,14 @@ return new NeutrinoEffect(ctx);
1598
1660
  }
1599
1661
  class NeutrinoFile extends Phaser.Loader.File {
1600
1662
  constructor(t, a) {
1601
- const { key: r, url: o, options: l, xhrSettings: d } = a;
1663
+ const { key: r, url: o, options: l, xhrSettings: c } = a;
1602
1664
  super(t, {
1603
1665
  type: "binary",
1604
1666
  extension: "js",
1605
1667
  responseType: "text",
1606
1668
  key: r,
1607
1669
  url: o,
1608
- xhrSettings: d
1670
+ xhrSettings: c
1609
1671
  });
1610
1672
  n(this, "_options");
1611
1673
  this.cache = t.cacheManager.binary, this._options = l;
@@ -1641,8 +1703,8 @@ class DataTextureManager {
1641
1703
  );
1642
1704
  this._gl = e, this.textureWidth = a, this.textureHeight = r, this._floatView = new Float32Array(t.buffer, t.byteOffset, t.length);
1643
1705
  for (let l = 0; l < RING_BUFFER_SIZE$1; l++) {
1644
- const d = e.createTexture();
1645
- e.bindTexture(e.TEXTURE_2D, d), 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(
1706
+ const c = e.createTexture();
1707
+ 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(
1646
1708
  e.TEXTURE_2D,
1647
1709
  0,
1648
1710
  e.RGBA32F,
@@ -1652,7 +1714,7 @@ class DataTextureManager {
1652
1714
  e.RGBA,
1653
1715
  e.FLOAT,
1654
1716
  null
1655
- ), this._textures.push(d);
1717
+ ), this._textures.push(c);
1656
1718
  }
1657
1719
  e.bindTexture(e.TEXTURE_2D, null);
1658
1720
  }
@@ -1729,10 +1791,10 @@ class DataTextureManagerGL1 {
1729
1791
  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);
1730
1792
  const l = e.getParameter(e.ACTIVE_TEXTURE);
1731
1793
  e.activeTexture(e.TEXTURE0);
1732
- const d = e.getParameter(e.TEXTURE_BINDING_2D);
1794
+ const c = e.getParameter(e.TEXTURE_BINDING_2D);
1733
1795
  for (let i = 0; i < RING_BUFFER_SIZE; i++)
1734
1796
  this._dataTextures.push(this._createTexture(e.FLOAT)), this._metaTextures.push(this._createTexture(e.UNSIGNED_BYTE));
1735
- e.bindTexture(e.TEXTURE_2D, d), e.activeTexture(l);
1797
+ e.bindTexture(e.TEXTURE_2D, c), e.activeTexture(l);
1736
1798
  }
1737
1799
  get currentDataTexture() {
1738
1800
  return this._dataTextures[this._currentIndex];
@@ -1748,7 +1810,7 @@ class DataTextureManagerGL1 {
1748
1810
  upload(e, t, a) {
1749
1811
  const r = this._gl, o = this.textureWidth, l = Math.min(this.textureHeight, Math.ceil(e * 4 / o));
1750
1812
  if (l === 0) return;
1751
- const d = l * o * 4;
1813
+ const c = l * o * 4;
1752
1814
  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(
1753
1815
  r.TEXTURE_2D,
1754
1816
  0,
@@ -1758,7 +1820,7 @@ class DataTextureManagerGL1 {
1758
1820
  l,
1759
1821
  r.RGBA,
1760
1822
  r.FLOAT,
1761
- this._floatView.subarray(0, d)
1823
+ this._floatView.subarray(0, c)
1762
1824
  ), r.activeTexture(r.TEXTURE0 + a), r.bindTexture(r.TEXTURE_2D, this.currentMetaTexture), r.texSubImage2D(
1763
1825
  r.TEXTURE_2D,
1764
1826
  0,
@@ -1768,7 +1830,7 @@ class DataTextureManagerGL1 {
1768
1830
  l,
1769
1831
  r.RGBA,
1770
1832
  r.UNSIGNED_BYTE,
1771
- this.meta.bytes.subarray(0, d)
1833
+ this.meta.bytes.subarray(0, c)
1772
1834
  );
1773
1835
  }
1774
1836
  advance() {
@@ -1812,6 +1874,9 @@ class DrawBatch {
1812
1874
  class NeutrinoRenderer {
1813
1875
  constructor() {
1814
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 });
1815
1880
  n(this, "_modelMatrix", new Float32Array(16));
1816
1881
  n(this, "_texSlotMap", /* @__PURE__ */ new Map());
1817
1882
  n(this, "_batchPool");
@@ -1820,65 +1885,69 @@ class NeutrinoRenderer {
1820
1885
  for (let e = 0; e < MAX_BATCHES; e++)
1821
1886
  this._batchPool.push(new DrawBatch());
1822
1887
  }
1823
- render(e, t, a, r, o, l, d) {
1888
+ render(e, t, a, r, o, l, c) {
1824
1889
  if (l === 0) return;
1825
- const i = e.gl, u = e.isWebGL1, g = e.maxBatchTextures, I = d.renderStyles, E = d.materials, U = u ? t.meta : null;
1826
- U && U.build(r, l), this._batchCount = 0;
1827
- const x = this._texSlotMap;
1890
+ const i = e.gl, u = e.isWebGL1, _ = e.maxBatchTextures, y = c.renderStyles, E = c.materials, I = u ? t.meta : null;
1891
+ I && I.build(r, l), this._batchCount = 0;
1892
+ const m = this._texSlotMap;
1828
1893
  for (; this._batchPool.length < o.length; )
1829
1894
  this._batchPool.push(new DrawBatch());
1830
- let b = 0;
1831
- for (; b < o.length; ) {
1832
- const p = this._batchPool[this._batchCount];
1833
- p.reset(), p.blendMode = this._resolveBlend(o[b].blendMode, E), p.startParticle = o[b].startParticleIndex, x.clear();
1834
- let v = b;
1835
- for (; v < o.length && this._resolveBlend(o[v].blendMode, E) === p.blendMode; ) {
1836
- const T = I[o[v].renderStyleIndex].textureIndices[0];
1837
- if (!x.has(T)) {
1838
- if (x.size >= g) break;
1839
- const f = x.size;
1840
- x.set(T, f), p.glTextures[f] = d.glTextures[T] || null, p.remaps[f] = d.remaps[T] || null, p.numTextures = f + 1;
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;
1841
1906
  }
1842
- const c = x.get(T), w = o[v];
1843
- if (U)
1844
- U.patchSlot(w.startParticleIndex, w.numParticles, c);
1845
- else
1846
- for (let f = 0; f < w.numParticles; f++) {
1847
- const h = (w.startParticleIndex + f) * BYTES_PER_PARTICLE, y = a.getUint32(h, !0);
1848
- a.setUint32(h, y & 4294967040 | c, !0);
1907
+ const d = m.get(g), b = o[v];
1908
+ if (I)
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);
1849
1914
  }
1850
- p.numParticles += w.numParticles, v++;
1915
+ f.numParticles += b.numParticles, v++;
1851
1916
  }
1852
- this._batchCount++, b = v;
1917
+ this._batchCount++, w = v;
1853
1918
  }
1854
1919
  const B = i.isEnabled(i.SCISSOR_TEST);
1855
1920
  try {
1856
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) {
1857
- const _ = t;
1858
- _.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, _.textureWidth, _.textureHeight);
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);
1859
1924
  } else {
1860
- const _ = t;
1861
- _.uploadAndBind(DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTexture, DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTextureWidth, _.textureWidth);
1925
+ const p = t;
1926
+ p.uploadAndBind(DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTexture, DATA_TEXTURE_UNIT), i.uniform1i(e.uDataTextureWidth, p.textureWidth);
1862
1927
  }
1863
1928
  i.uniform3f(e.uCameraRight, 1, 0, 0), i.uniform3f(e.uCameraUp, 0, -1, 0), i.uniform3f(e.uCameraDir, 0, 0, -1);
1864
- const p = d.viewportWidth, v = d.viewportHeight, T = d.zoom || 1, c = this._orthoMatrix;
1865
- c.fill(0), c[0] = 2 / p * T, c[5] = -2 / v * T, c[10] = 1, c[15] = 1, c[12] = -1 - 2 / p * T * d.scrollX, c[13] = 1 + 2 / v * T * d.scrollY, i.uniformMatrix4fv(e.uViewProjMatrix, !1, c);
1866
- const w = v > 0 ? p / v : 1;
1867
- e.uViewportAspect && i.uniform1f(e.uViewportAspect, w), e.uWorldAlpha && i.uniform1f(e.uWorldAlpha, d.worldAlpha);
1868
- const f = d.model, h = this._modelMatrix;
1869
- 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);
1870
- const y = u ? PARTICLE_TEXTURE_START_UNIT_GL1 : PARTICLE_TEXTURE_START_UNIT;
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;
1871
1940
  i.enable(i.BLEND);
1872
- for (let _ = 0; _ < this._batchCount; _++) {
1873
- const P = this._batchPool[_];
1874
- this._applyBlendMode(i, P.blendMode);
1875
- for (let m = 0; m < P.numTextures; m++) {
1876
- const C = y + m;
1877
- i.activeTexture(i.TEXTURE0 + C), i.bindTexture(i.TEXTURE_2D, P.glTextures[m]), i.uniform1i(e.uTextures[m], C);
1878
- const R = P.remaps[m];
1879
- R ? i.uniform4f(e.uTexRemaps[m], R.x, R.y, R.width, R.height) : i.uniform4f(e.uTexRemaps[m], 0, 0, 1, 1);
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);
1880
1949
  }
1881
- const D = P.startParticle * 6, S = P.numParticles * 6;
1950
+ const D = R.startParticle * 6, S = R.numParticles * 6;
1882
1951
  i.drawElements(
1883
1952
  i.TRIANGLES,
1884
1953
  S,
@@ -1896,6 +1965,13 @@ class NeutrinoRenderer {
1896
1965
  // materials[], NOT the blend itself — without this deref a single-material effect
1897
1966
  // (materialIndex 0) would always render as materials[0]'s slot 0 (Normal). Falls back to
1898
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
+ }
1899
1975
  _resolveBlend(e, t) {
1900
1976
  return t && e >= 0 && e < t.length ? t[e] : e;
1901
1977
  }
@@ -1941,6 +2017,8 @@ class Effect extends Phaser.GameObjects.GameObject {
1941
2017
  n(this, "alpha", 1);
1942
2018
  n(this, "depth", 0);
1943
2019
  n(this, "blendMode", Phaser.BlendModes.NORMAL);
2020
+ /** Optional v1.0-parity perspective projection (Projection3D). */
2021
+ n(this, "projection");
1944
2022
  n(this, "_dataTextureManager", null);
1945
2023
  n(this, "_worldPosition");
1946
2024
  n(this, "_worldScaledPosition");
@@ -1950,7 +2028,7 @@ class Effect extends Phaser.GameObjects.GameObject {
1950
2028
  n(this, "_tempMatrix2");
1951
2029
  n(this, "_unpauseOnUpdateRender");
1952
2030
  n(this, "_glTexturesScratch", []);
1953
- 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();
1954
2032
  const o = {
1955
2033
  paused: r.pause !== Pause.NO,
1956
2034
  generatorsPaused: r.generatorsPaused
@@ -1982,31 +2060,32 @@ class Effect extends Phaser.GameObjects.GameObject {
1982
2060
  this._checkUnpauseOnUpdateRender();
1983
2061
  const o = this.gamePlugin.ctx, l = this._dataTextureManager;
1984
2062
  this.effect.construct([0, 0, -1]);
1985
- const d = this.effect.renderInstructions, i = d._totalParticles;
2063
+ const c = this.effect.renderInstructions, i = c._totalParticles;
1986
2064
  if (i === 0) return;
1987
2065
  t.flush();
1988
- const u = this.effectModel.effectModel.textures.length, g = this._glTexturesScratch;
1989
- g.length = u;
2066
+ const u = this.effectModel.effectModel.textures.length, _ = this._glTexturesScratch;
2067
+ _.length = u;
1990
2068
  for (let E = 0; E < u; ++E)
1991
- g[E] = this.effectModel.glTexture(E);
1992
- const I = this._worldScale;
2069
+ _[E] = this.effectModel.glTexture(E);
2070
+ const y = this._worldScale;
1993
2071
  try {
1994
2072
  sharedRenderer.render(
1995
2073
  o,
1996
2074
  l,
1997
2075
  this.effect.particleDataView,
1998
2076
  this.effect.particleDataUint32,
1999
- d,
2077
+ c,
2000
2078
  i,
2001
2079
  {
2002
- model: { a: I, b: 0, c: 0, d: I, tx: 0, ty: 0 },
2080
+ model: { a: y, b: 0, c: 0, d: y, tx: 0, ty: 0 },
2003
2081
  viewportWidth: t.width,
2004
2082
  viewportHeight: t.height,
2005
2083
  scrollX: r.scrollX * this.scrollFactorX,
2006
2084
  scrollY: r.scrollY * this.scrollFactorY,
2007
2085
  zoom: r.zoom,
2008
2086
  worldAlpha: this.alpha,
2009
- glTextures: g,
2087
+ projection: this.projection,
2088
+ glTextures: _,
2010
2089
  remaps: this.effectModel.texturesRemap,
2011
2090
  renderStyles: this.effect.model.renderStyles,
2012
2091
  materials: this.effect.model.materials
@@ -2043,6 +2122,19 @@ class Effect extends Phaser.GameObjects.GameObject {
2043
2122
  setPropertyInAllEmitters(t, a) {
2044
2123
  this.ready && this.effect.setPropertyInAllEmitters(t, a);
2045
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
+ }
2046
2138
  getNumParticles() {
2047
2139
  return this.ready ? this.effect.getNumParticles() : 0;
2048
2140
  }
@@ -2064,8 +2156,8 @@ class Effect extends Phaser.GameObjects.GameObject {
2064
2156
  _updateWorldTransform() {
2065
2157
  const t = (o, l) => {
2066
2158
  if (!o) return;
2067
- const d = this._tempMatrix2;
2068
- d.applyITRS(o.x, o.y, o.rotation, o.scaleX, o.scaleY), d.multiply(l.matrix, l.matrix), l.angle += o.angle, l.scale *= (o.scaleX + o.scaleY) * 0.5, t(o.parentContainer, l);
2159
+ const c = this._tempMatrix2;
2160
+ 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) * 0.5, t(o.parentContainer, l);
2069
2161
  }, a = {
2070
2162
  matrix: this._tempMatrix1.loadIdentity(),
2071
2163
  angle: this.angle,
@@ -2146,6 +2238,19 @@ class PerspectiveProjection {
2146
2238
  _getScale(e) {
2147
2239
  return this._z / (this._z - e[2]);
2148
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
+ }
2149
2254
  }
2150
2255
  let _installed = !1;
2151
2256
  function installPlugin() {