@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.
package/CHANGELOG.md CHANGED
@@ -1,9 +1,18 @@
1
1
  # Changelog
2
2
 
3
- ## [1.0.1] - 2026-07-25
3
+ ## [1.1.0] - 2026-08-03
4
+
5
+ - Emitter properties can now be **read** as well as written: `getEmitterPropertyValue()`, `hasEmitterProperty()` and `getEmitterProperties()` (which lists every property an effect exposes, with its live value). `setPropertyInEmitter()`, for addressing a single emitter by name, is now available on the effect too
4
6
 
5
7
  - Initial Phaser 3 integration for the JS v1.1 export format (GPU geometry construction, data-texture renderer). Works on WebGL2 and, automatically, on WebGL1 contexts with float vertex textures — Phaser's default WebGL1 context works out of the box.
8
+ - Added perspective projection (Projection3D) support, matching the PIXI and Phaser 4 integrations: pass `projection: new PhaserNeutrino.PerspectiveProjection(angle)` in the effect options and particles scale with their distance from the camera. Particles that cross the near plane are discarded as whole quads.
6
9
  - Fixed strip texture seams: a ribbon whose atlas texture repeats along its length no longer shows a muddy "chewed" column at tile junctions when the texture has mipmaps (WebGL2).
7
10
  - Emitter-anchored strips no longer show a brief mis-oriented sliver at the emitter end right after a new particle spawns.
8
11
  - Fixed broken ribbon (Strip) geometry on sharp bends: a dark wedge was cut out of the ribbon wherever the path bent sharper than the ribbon's own width, and the segments next to the ribbon's ends (near the emitter and at the tail) could render as a twisted, mostly-invisible band or a stray misoriented quad — most visible right where the ribbon is being created when the head particle sits close to the emitter (issue #236).
9
12
 
13
+ ## [1.0.1] - 2026-07-25
14
+
15
+ - Initial Phaser 3 integration for the JS v1.1 export format (GPU geometry construction, data-texture renderer). Works on WebGL2 and, automatically, on WebGL1 contexts with float vertex textures — Phaser's default WebGL1 context works out of the box.
16
+ - Fixed strip texture seams: a ribbon whose atlas texture repeats along its length no longer shows a muddy "chewed" column at tile junctions when the texture has mipmaps (WebGL2).
17
+ - Emitter-anchored strips no longer show a brief mis-oriented sliver at the emitter end right after a new particle spawns.
18
+ - Fixed broken ribbon (Strip) geometry on sharp bends: a dark wedge was cut out of the ribbon wherever the path bent sharper than the ribbon's own width, and the segments next to the ribbon's ends (near the emitter and at the tail) could render as a twisted, mostly-invisible band or a stray misoriented quad — most visible right where the ribbon is being created when the head particle sits close to the emitter (issue #236).
package/README.md CHANGED
@@ -187,10 +187,41 @@ effect.unpause();
187
187
  Control exposed emitter properties at runtime. Properties are defined in Emitter Guide or Emitter Scheme in the Editor.
188
188
 
189
189
  ```javascript
190
+ // Write to every emitter that declares the property...
190
191
  effect.setPropertyInAllEmitters('MyParticlesPerSecond', 100);
191
192
  effect.setPropertyInAllEmitters('MyColor', [1.0, 0.5, 0.2]);
193
+
194
+ // ...or address a single emitter by name.
195
+ effect.setPropertyInEmitter('Sparks', 'MyColor', [1.0, 0.5, 0.2]);
196
+ ```
197
+
198
+ Read the current values back. A scalar property returns a `number`, a vector
199
+ property returns a `number[]`, and an unknown property returns `undefined`:
200
+
201
+ ```javascript
202
+ effect.getEmitterPropertyValue('Sparks', 'MyColor'); // [1.0, 0.5, 0.2]
203
+ effect.getEmitterPropertyValue('MyParticlesPerSecond'); // 100 — first emitter with it
204
+
205
+ effect.hasEmitterProperty('Sparks', 'MyColor'); // true
206
+ effect.hasEmitterProperty('NoSuchProperty'); // false
192
207
  ```
193
208
 
209
+ Discover what an effect exposes without knowing the names up front:
210
+
211
+ ```javascript
212
+ for (const p of effect.getEmitterProperties()) {
213
+ // { emitterName: 'Sparks', name: 'MyColor', arity: 3, value: [1, 0.5, 0.2] }
214
+ console.log(p.emitterName, p.name, p.arity, p.value);
215
+ }
216
+ ```
217
+
218
+ Values are read live from the simulation buffer at call time.
219
+
220
+ > **Use only the documented methods above.** Exported effects are minified, and
221
+ > the internal property names/objects they contain (`_globParamMeta`, or whatever
222
+ > short name it was mangled to) are not a stable API — they change between editor
223
+ > versions and differ between commercial and non-commercial exports.
224
+
194
225
  ## Number of Particles
195
226
 
196
227
  ```javascript
@@ -203,6 +234,12 @@ const numParticles = effect.getNumParticles();
203
234
  - **No particles visible:** Make sure the export target in the editor is set to JavaScript v1.1. Verify `texturesBasePath` points to the correct textures directory.
204
235
  - **Turbulence not working:** Set `generateNoise: true` in the plugin config, or call the plugin's `generateNoise()` before creating effects that use noise.
205
236
 
237
+ ## HDR Bloom (optional add-on)
238
+
239
+ The scene-preserving HDR glow that matches the editor preview is currently available for
240
+ **PIXI.js** ([`bloom-v1.1-pixi7`](../bloom-pixi7) / [`bloom-v1.1-pixi8`](../bloom-pixi8)) and
241
+ **MonoGame**. A Phaser bloom add-on is not available yet.
242
+
206
243
  ## Documentation
207
244
 
208
245
  Full documentation at [neutrinoparticles.com](https://neutrinoparticles.com/en/docs/199).
package/dist/Effect.d.ts CHANGED
@@ -2,13 +2,15 @@ import Phaser from 'phaser';
2
2
  import { Pause, Vec2, Vec3 } from './types';
3
3
  import { GamePlugin } from './GamePlugin';
4
4
  import { EffectModel } from './EffectModel';
5
- import { NeutrinoSystem } from './types';
5
+ import { NeutrinoEmitterProperty, NeutrinoSystem } from './types';
6
+ import { PerspectiveProjection } from './PerspectiveProjection';
6
7
  export interface EffectOptions {
7
8
  position?: Vec3;
8
9
  angle?: number;
9
10
  scale?: number;
10
11
  pause?: Pause;
11
12
  generatorsPaused?: boolean;
13
+ projection?: PerspectiveProjection;
12
14
  }
13
15
  /**
14
16
  * A NeutrinoParticles effect on a Phaser scene (JS v1.1 runtime).
@@ -34,6 +36,8 @@ export declare class Effect extends Phaser.GameObjects.GameObject {
34
36
  alpha: number;
35
37
  depth: number;
36
38
  blendMode: number;
39
+ /** Optional v1.0-parity perspective projection (Projection3D). */
40
+ projection?: PerspectiveProjection;
37
41
  private _dataTextureManager;
38
42
  private _worldPosition;
39
43
  private _worldScaledPosition;
@@ -64,6 +68,12 @@ export declare class Effect extends Phaser.GameObjects.GameObject {
64
68
  unpauseGenerators(): void;
65
69
  get generatorsPaused(): boolean;
66
70
  setPropertyInAllEmitters(name: string, value: Vec3 | Vec2 | number): void;
71
+ setPropertyInEmitter(emitterName: string, name: string, value: Vec3 | Vec2 | number): void;
72
+ getEmitterPropertyValue(name: string): number | number[] | undefined;
73
+ getEmitterPropertyValue(emitterName: string, name: string): number | number[] | undefined;
74
+ hasEmitterProperty(name: string): boolean;
75
+ hasEmitterProperty(emitterName: string, name: string): boolean;
76
+ getEmitterProperties(): NeutrinoEmitterProperty[];
67
77
  getNumParticles(): number;
68
78
  setScrollFactor(x: number, y?: number): this;
69
79
  setAlpha(topLeft?: number, _topRight?: number, _bottomLeft?: number, _bottomRight?: number): this;
@@ -2,6 +2,7 @@ import { NeutrinoContext } from "./NeutrinoContext";
2
2
  import { DataTextureManager } from "./DataTextureManager";
3
3
  import { DataTextureManagerGL1 } from "./gl1/DataTextureManagerGL1";
4
4
  import { NeutrinoSubRect } from "./types";
5
+ import { PerspectiveProjection } from "./PerspectiveProjection";
5
6
  export interface RenderParams {
6
7
  /** Effect world transform (2x3) already including world scale. */
7
8
  model: {
@@ -21,6 +22,9 @@ export interface RenderParams {
21
22
  zoom: number;
22
23
  /** Host scene-graph world alpha, multiplied into each particle's alpha. */
23
24
  worldAlpha: number;
25
+ /** Optional v1.0-parity perspective projection (Projection3D); when set,
26
+ * uViewProjMatrix = ortho * perspective (both in screen space). */
27
+ projection?: PerspectiveProjection;
24
28
  /** Per-particle GL textures (one per render-style texture index). */
25
29
  glTextures: (WebGLTexture | null)[];
26
30
  remaps: Array<NeutrinoSubRect | null>;
@@ -43,12 +47,16 @@ export interface RenderParams {
43
47
  */
44
48
  export declare class NeutrinoRenderer {
45
49
  private _orthoMatrix;
50
+ private _perspMatrix;
51
+ private _vpMatrix;
52
+ private _projFrame;
46
53
  private _modelMatrix;
47
54
  private _texSlotMap;
48
55
  private _batchPool;
49
56
  private _batchCount;
50
57
  constructor();
51
58
  render(ctx: NeutrinoContext, dtm: DataTextureManager | DataTextureManagerGL1, dataView: DataView, dataUint32: Uint32Array, instructions: any[], numParticles: number, params: RenderParams): void;
59
+ private _mulMatrix;
52
60
  private _resolveBlend;
53
61
  private _applyBlendMode;
54
62
  }
@@ -62,4 +62,14 @@ export declare class PerspectiveProjection {
62
62
  */
63
63
  transformSize(outSize: Vec2, pos: Vec3, size: Vec2): void;
64
64
  _getScale(pos: Vec3): number;
65
+ /**
66
+ * Writes the projection as a 4x4 column-major matrix operating in screen
67
+ * space, for the GPU render path. Homogeneous equivalent of
68
+ * transformPosition(): clip.xy = p.xy - center * (p.z / z),
69
+ * clip.w = 1 - p.z / z; the z row is zeroed (no depth on this path).
70
+ * setScreenFrame() must have been called before.
71
+ *
72
+ * @param {Float32Array} out 16-element column-major output matrix.
73
+ */
74
+ writeMatrix(out: Float32Array): void;
65
75
  }
@@ -1,4 +1,4 @@
1
- "use strict";var F=Object.defineProperty;var N=(s,e,t)=>e in s?F(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
@@ -170,7 +175,7 @@ void main() {
170
175
  bool nearPlaneClipped = (sp1.w < 1e-3) || (sp2.w < 1e-3);
171
176
 
172
177
  // #236: set when the segment itself is degenerate (much shorter than
173
- // the ribbon half-width) - the pair is discarded like nearPlaneClipped.
178
+ // the ribbon half-width) the pair is discarded like nearPlaneClipped.
174
179
  bool stripDegenerate = false;
175
180
 
176
181
  // Aspect correction so the 2D Faced ortho gives visually-perpendicular
@@ -256,7 +261,7 @@ void main() {
256
261
  vec3 K2 = Kmul2 * normalize(cross(wdir12 + wdir23, n2));
257
262
  Bmul2 = sign(dot(B2raw, K2));
258
263
  vec3 B2 = B2raw * Bmul2;
259
- // #236: see ik1 - inner vertex at the ribbon half-width.
264
+ // #236: see ik1 inner vertex at the ribbon half-width.
260
265
  float ik2 = halfSize2;
261
266
  worldK2 = p2 + K2 * halfSize2;
262
267
  worldB2 = p2 + B2 * halfSize2;
@@ -274,10 +279,16 @@ void main() {
274
279
  // Faced: screen-space NDC frame around dir12.
275
280
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
276
281
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
277
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
278
282
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
279
283
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
280
- 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;
281
292
 
282
293
  vec2 d01 = ndc1 - ndc0;
283
294
  vec2 d12 = ndc2 - ndc1;
@@ -328,7 +339,7 @@ void main() {
328
339
  vec2 K2 = Kmul2 * normalize(stripOrtho(dir12 + dir23));
329
340
  Bmul2 = sign(dot(B, K2));
330
341
  vec2 B2 = B * Bmul2;
331
- // #236: see ik1 - inner vertex at the ribbon half-width.
342
+ // #236: see ik1 inner vertex at the ribbon half-width.
332
343
  float ik2 = ndcHalfSize2;
333
344
  k2 = ndc2 + K2 * ndcHalfSize2;
334
345
  b2 = ndc2 + B2 * ndcHalfSize2;
@@ -344,7 +355,7 @@ void main() {
344
355
  }
345
356
  }
346
357
 
347
- // #236: a noKnee (straight-cap) end carries no bend side of its own -
358
+ // #236: a noKnee (straight-cap) end carries no bend side of its own
348
359
  // its raw side label is always the +B side. If the segment's other
349
360
  // end is a real knee on the -B side, the mismatched labels would send
350
361
  // the ladder into the opposite-sides (twist) branch and flip the
@@ -371,6 +382,7 @@ void main() {
371
382
  Bmul2 = Bmul1;
372
383
  }
373
384
 
385
+
374
386
  // V coords (mirror of preview3 / GPU33 make*Strip):
375
387
  // V at outer knee (k) = (Bmul + 1) * 0.5
376
388
  // V at inner knee (IK) = 1 - V_outer ; bend (b) = outer
@@ -473,7 +485,7 @@ void main() {
473
485
  // Free projects the world-space vertex through MVP directly; Faced
474
486
  // reconstructs clip space from aspect-corrected NDC (mirror of preview3).
475
487
  if (stripDegenerate) {
476
- // #236: sub-visual segment - discard the pair entirely.
488
+ // #236: sub-visual segment discard the pair entirely.
477
489
  gl_Position = vec4(2.0, 2.0, 2.0, 1.0);
478
490
  } else if (isFreeRotation) {
479
491
  gl_Position = mvpMatrix * vec4(vertPos3D, 1.0);
@@ -512,6 +524,10 @@ void main() {
512
524
  float((packedColor >> 16u) & 0xFFu) / 255.0,
513
525
  float((packedColor >> 24u) & 0xFFu) / 255.0
514
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;
515
531
 
516
532
  uint gridConfig = floatBitsToUint(t3.g);
517
533
  float gridWidth = max(float(gridConfig & 0xFFFFu), 1.0);
@@ -559,7 +575,23 @@ void main() {
559
575
 
560
576
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
561
577
  vec3 worldPos = position + posDisp;
562
- 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
+ }
563
595
 
564
596
  // Texture coordinates with grid + atlas remap
565
597
  vec2 baseTexCoord = TEX_COORD[vertexIndex];
@@ -779,6 +811,11 @@ void main() {
779
811
  // Colors: the meta M1 sample is the normalized RGBA directly.
780
812
  vec4 color1 = fetchMeta(curBase + 1.0);
781
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;
782
819
 
783
820
  // Neighbor RECORD indices (system.js convention: neighborIdxP0 on the
784
821
  // pair-cur meta, neighborIdxP3 on the pair-nex meta; boundary
@@ -914,10 +951,16 @@ void main() {
914
951
  // Faced: screen-space NDC frame around dir12.
915
952
  vec4 sp0 = mvpMatrix * vec4(p0, 1.0);
916
953
  vec4 sp3 = mvpMatrix * vec4(p3, 1.0);
917
- vec2 ndc0 = (sp0.xy / sp0.w) * aspectMul;
918
954
  vec2 ndc1 = (sp1.xy / sp1.w) * aspectMul;
919
955
  vec2 ndc2 = (sp2.xy / sp2.w) * aspectMul;
920
- 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;
921
964
 
922
965
  vec2 d01 = ndc1 - ndc0;
923
966
  vec2 d12 = ndc2 - ndc1;
@@ -1109,6 +1152,9 @@ void main() {
1109
1152
 
1110
1153
  // Meta M1: the packedColor u32 LE bytes ARE the normalized RGBA sample.
1111
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;
1112
1158
 
1113
1159
  // Meta M2: gridConfig — [gridW lo, gridW hi, gridH lo, gridH hi].
1114
1160
  vec4 m2 = fetchMeta(baseTexel + 2.0);
@@ -1155,7 +1201,23 @@ void main() {
1155
1201
 
1156
1202
  mat4 mvpMatrix = uViewProjMatrix * uModelMatrix;
1157
1203
  vec3 worldPos = position + posDisp;
1158
- 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
+ }
1159
1221
 
1160
1222
  // Texture coordinates with grid + atlas remap
1161
1223
  vec2 baseTexCoord = texCoordOf(corner);
@@ -1230,7 +1292,7 @@ void main() {
1230
1292
  // world alpha scales the whole RGBA (mirror of the ES 3.00 shader).
1231
1293
  gl_FragColor = texColor * vColor * uWorldAlpha;
1232
1294
  }
1233
- `,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 d=l?new Uint32Array(e*6):new Uint16Array(e*6);for(let i=0;i<e;i++){const u=i*4,g=i*6;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}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){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;
1234
1296
  attribute float aId;
1235
1297
  uniform sampler2D uF;
1236
1298
  uniform sampler2D uB;
@@ -1242,11 +1304,11 @@ void main() {
1242
1304
  gl_Position = vec4(aId, 0.0, 0.0, 1.0);
1243
1305
  gl_PointSize = 1.0;
1244
1306
  }
1245
- `,a=`precision mediump float;
1307
+ `,n=`precision mediump float;
1246
1308
  varying vec4 vC;
1247
1309
  void main() { gl_FragColor = vC; }
1248
- `,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);e.activeTexture(e.TEXTURE0);const u=e.getParameter(e.TEXTURE_BINDING_2D);e.activeTexture(e.TEXTURE1);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);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;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,K)=>{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,K),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),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)}finally{c>=0&&(f&&(e.bindBuffer(e.ARRAY_BUFFER,f),e.vertexAttribPointer(c,h,y,D,_,P)),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])}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) {
1249
1311
  `+scriptText+`
1250
1312
  return new NeutrinoEffect(ctx);
1251
- })(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 d=this.scene.sys.textures.get(o);l=d.get(d.firstFrame)}l?this._onTextureLoaded(t,l):(this.scene.load.once("filecomplete-image-"+o,((d,i)=>()=>{const u=this.scene.sys.textures.get(i);this._onTextureLoaded(d,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:d}=a;super(t,{type:"binary",extension:"js",responseType:"text",key:r,url:o,xhrSettings:d});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 d=e.createTexture();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(e.TEXTURE_2D,0,e.RGBA32F,a,r,0,e.RGBA,e.FLOAT,null),this._textures.push(d)}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 d=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,d),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 d=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,d)),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,d))}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,d){if(l===0)return;const i=e.gl,u=e.isWebGL1,g=e.maxBatchTextures,I=d.renderStyles,E=d.materials,U=u?t.meta:null;U&&U.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=I[o[v].renderStyleIndex].textureIndices[0];if(!x.has(T)){if(x.size>=g)break;const f=x.size;x.set(T,f),p.glTextures[f]=d.glTextures[T]||null,p.remaps[f]=d.remaps[T]||null,p.numTextures=f+1}const c=x.get(T),w=o[v];if(U)U.patchSlot(w.startParticleIndex,w.numParticles,c);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|c,!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 _=t;_.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)}else{const _=t;_.uploadAndBind(DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTexture,DATA_TEXTURE_UNIT),i.uniform1i(e.uDataTextureWidth,_.textureWidth)}i.uniform3f(e.uCameraRight,1,0,0),i.uniform3f(e.uCameraUp,0,-1,0),i.uniform3f(e.uCameraDir,0,0,-1);const p=d.viewportWidth,v=d.viewportHeight,T=d.zoom||1,c=this._orthoMatrix;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);const w=v>0?p/v:1;e.uViewportAspect&&i.uniform1f(e.uViewportAspect,w),e.uWorldAlpha&&i.uniform1f(e.uWorldAlpha,d.worldAlpha);const f=d.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 _=0;_<this._batchCount;_++){const P=this._batchPool[_];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 d=this.effect.renderInstructions,i=d._totalParticles;if(i===0)return;t.flush();const u=this.effectModel.effectModel.textures.length,g=this._glTexturesScratch;g.length=u;for(let E=0;E<u;++E)g[E]=this.effectModel.glTexture(E);const I=this._worldScale;try{sharedRenderer.render(o,l,this.effect.particleDataView,this.effect.particleDataUint32,d,i,{model:{a:I,b:0,c:0,d:I,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:g,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 d=this._tempMatrix2;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)*.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;
1252
1314
  //# sourceMappingURL=neutrinoparticles.js-v1.1-phaser.cjs.js.map