pixi-effects 0.22.0 → 0.25.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.
Files changed (85) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +9 -7
  3. package/ai/SKILL.md +5 -3
  4. package/ai/chat-template.html +3 -3
  5. package/ai/reference/cheatsheet.md +8 -5
  6. package/ai/reference/pitfalls.md +13 -1
  7. package/ai/reference/recipes.md +131 -0
  8. package/ai/template.html +5 -5
  9. package/ai/tools/check.mjs +7 -0
  10. package/dist/{Base-gu-5X0Cv.d.ts → Base-BgnRXbtJ.d.ts} +1 -1
  11. package/dist/{Base-BzlPgd-D.d.cts → Base-DHVKqA46.d.cts} +1 -1
  12. package/dist/Composition-2GJSCHPP.cjs +16 -0
  13. package/dist/{Composition-BK5SH6J7.cjs.map → Composition-2GJSCHPP.cjs.map} +1 -1
  14. package/dist/Composition-C4FSWJZF.js +7 -0
  15. package/dist/{Composition-Q5TVG3HM.js.map → Composition-C4FSWJZF.js.map} +1 -1
  16. package/dist/Controller.cjs +2 -2
  17. package/dist/Controller.d.cts +2 -2
  18. package/dist/Controller.d.ts +2 -2
  19. package/dist/Controller.js +1 -1
  20. package/dist/{Movie-DMxOoTdW.d.cts → Movie-CTpN1wYL.d.cts} +3 -1
  21. package/dist/{Movie--_TJvdJa.d.ts → Movie-FM5m67V0.d.ts} +3 -1
  22. package/dist/Presenter.cjs +2 -2
  23. package/dist/Presenter.d.cts +2 -2
  24. package/dist/Presenter.d.ts +2 -2
  25. package/dist/Presenter.js +1 -1
  26. package/dist/{blendModes-HCP5XTNP.js → blendModes-G3NOAU3T.js} +5 -5
  27. package/dist/{blendModes-HCP5XTNP.js.map → blendModes-G3NOAU3T.js.map} +1 -1
  28. package/dist/{blendModes-5E4G2ZZQ.cjs → blendModes-YV43K6E7.cjs} +6 -6
  29. package/dist/{blendModes-5E4G2ZZQ.cjs.map → blendModes-YV43K6E7.cjs.map} +1 -1
  30. package/dist/{chunk-K4JVI5HK.cjs → chunk-3DLYAPK7.cjs} +7 -9
  31. package/dist/{chunk-K4JVI5HK.cjs.map → chunk-3DLYAPK7.cjs.map} +1 -1
  32. package/dist/{chunk-YEPDQ7I3.js → chunk-AXZUKNXF.js} +6 -6
  33. package/dist/{chunk-YEPDQ7I3.js.map → chunk-AXZUKNXF.js.map} +1 -1
  34. package/dist/{chunk-NHICCDTC.js → chunk-BXOCEZ4U.js} +12 -3
  35. package/dist/chunk-BXOCEZ4U.js.map +1 -0
  36. package/dist/{chunk-IFZOI3JQ.cjs → chunk-DP37TTMO.cjs} +842 -40
  37. package/dist/chunk-DP37TTMO.cjs.map +1 -0
  38. package/dist/{chunk-BPTW7L25.cjs → chunk-ECA2WNRX.cjs} +23 -9
  39. package/dist/chunk-ECA2WNRX.cjs.map +1 -0
  40. package/dist/{chunk-DC4JPY6A.js → chunk-PRZ4U6JN.js} +814 -22
  41. package/dist/chunk-PRZ4U6JN.js.map +1 -0
  42. package/dist/chunk-SY27HZC5.cjs +1727 -0
  43. package/dist/chunk-SY27HZC5.cjs.map +1 -0
  44. package/dist/{chunk-E7RONUZ7.js → chunk-TLYTYBWX.js} +20 -6
  45. package/dist/chunk-TLYTYBWX.js.map +1 -0
  46. package/dist/{chunk-UZTKEYFP.cjs → chunk-TWZNOUM7.cjs} +12 -3
  47. package/dist/chunk-TWZNOUM7.cjs.map +1 -0
  48. package/dist/chunk-XS6GSJCN.js +1724 -0
  49. package/dist/chunk-XS6GSJCN.js.map +1 -0
  50. package/dist/index.cjs +227 -77
  51. package/dist/index.cjs.map +1 -1
  52. package/dist/index.d.cts +5 -5
  53. package/dist/index.d.ts +5 -5
  54. package/dist/index.js +166 -16
  55. package/dist/index.js.map +1 -1
  56. package/dist/loader.css +1 -1
  57. package/dist/{music-FUN7N225.js → music-AF4VTFAF.js} +3 -3
  58. package/dist/{music-FUN7N225.js.map → music-AF4VTFAF.js.map} +1 -1
  59. package/dist/{music-GEAH2XCT.cjs → music-KB7YZLHP.cjs} +7 -7
  60. package/dist/{music-GEAH2XCT.cjs.map → music-KB7YZLHP.cjs.map} +1 -1
  61. package/dist/{sfx-2DYTYNYW.cjs → sfx-2O6UQEUP.cjs} +8 -8
  62. package/dist/{sfx-2DYTYNYW.cjs.map → sfx-2O6UQEUP.cjs.map} +1 -1
  63. package/dist/{sfx-3IDAJ3UZ.js → sfx-VUKARHDX.js} +3 -3
  64. package/dist/{sfx-3IDAJ3UZ.js.map → sfx-VUKARHDX.js.map} +1 -1
  65. package/dist/three.cjs +7 -7
  66. package/dist/three.d.cts +2 -2
  67. package/dist/three.d.ts +2 -2
  68. package/dist/three.js +3 -3
  69. package/dist/{types-DBbZVKGS.d.cts → types-CoT0ypcx.d.cts} +60 -4
  70. package/dist/{types-DBbZVKGS.d.ts → types-CoT0ypcx.d.ts} +60 -4
  71. package/docs/dsl.md +138 -5
  72. package/llms-full.txt +295 -14
  73. package/package.json +1 -1
  74. package/dist/Composition-BK5SH6J7.cjs +0 -16
  75. package/dist/Composition-Q5TVG3HM.js +0 -7
  76. package/dist/chunk-BPTW7L25.cjs.map +0 -1
  77. package/dist/chunk-DC4JPY6A.js.map +0 -1
  78. package/dist/chunk-E7RONUZ7.js.map +0 -1
  79. package/dist/chunk-IFZOI3JQ.cjs.map +0 -1
  80. package/dist/chunk-MHOO2CVU.cjs +0 -840
  81. package/dist/chunk-MHOO2CVU.cjs.map +0 -1
  82. package/dist/chunk-NHICCDTC.js.map +0 -1
  83. package/dist/chunk-UQDEDYVM.js +0 -838
  84. package/dist/chunk-UQDEDYVM.js.map +0 -1
  85. package/dist/chunk-UZTKEYFP.cjs.map +0 -1
@@ -0,0 +1,1727 @@
1
+ 'use strict';
2
+
3
+ var chunkDP37TTMO_cjs = require('./chunk-DP37TTMO.cjs');
4
+ var chunk3DLYAPK7_cjs = require('./chunk-3DLYAPK7.cjs');
5
+ var chunkECA2WNRX_cjs = require('./chunk-ECA2WNRX.cjs');
6
+ var chunkTWZNOUM7_cjs = require('./chunk-TWZNOUM7.cjs');
7
+ var pixi_js = require('pixi.js');
8
+ var gsap = require('gsap');
9
+
10
+ // src/space/lightChecks.ts
11
+ var SOFT_LIGHTS = 3;
12
+ var bagsOf = (spec) => {
13
+ const s = spec;
14
+ const out = [];
15
+ if (s.initial) out.push(s.initial);
16
+ for (const k of s.keyframes ?? []) for (const b of [k.set, k.to, k.from]) if (b) out.push(b);
17
+ return out;
18
+ };
19
+ var numbersOf = (spec, key) => bagsOf(spec).map((b) => b[key]).filter((v) => typeof v === "number" && Number.isFinite(v));
20
+ var nameOf = (s, i) => s.name ? `"${s.name}"` : `#${i + 1}`;
21
+ var KIND_ALIASES = {
22
+ directional: "parallel",
23
+ sun: "parallel",
24
+ sunlight: "parallel",
25
+ distant: "parallel",
26
+ omni: "point",
27
+ lamp: "point",
28
+ bulb: "point",
29
+ pointlight: "point",
30
+ spotlight: "spot",
31
+ cone: "spot",
32
+ ambience: "ambient",
33
+ global: "ambient",
34
+ fill: "ambient"
35
+ };
36
+ function lightKindProblem(kind) {
37
+ if (kind === void 0 || typeof kind === "string" && chunkDP37TTMO_cjs.LIGHT_KINDS.includes(kind)) return null;
38
+ const guess = typeof kind === "string" ? KIND_ALIASES[kind.toLowerCase()] ?? chunkTWZNOUM7_cjs.suggestName(kind, chunkDP37TTMO_cjs.LIGHT_KINDS) : null;
39
+ const shown = typeof kind === "string" ? `"${kind}"` : String(kind);
40
+ return `kind ${shown} is not a light kind${guess ? `; did you mean "${guess}"?` : ""} (use ${chunkDP37TTMO_cjs.LIGHT_KINDS.join(", ")}); point is used`;
41
+ }
42
+ var LIGHT_ALIASES = {
43
+ pointOfInterest: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
44
+ target: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
45
+ lookAt: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
46
+ angle: 'did you mean "coneAngle"?',
47
+ cone: 'did you mean "coneAngle"?',
48
+ spread: 'did you mean "coneAngle"?',
49
+ feather: 'did you mean "coneFeather"?',
50
+ penumbra: 'did you mean "coneFeather" (the cone edge) or "shadowDiffusion" (the shadow edge)?',
51
+ castShadows: 'did you mean "castsShadows"?',
52
+ castShadow: 'did you mean "castsShadows"?',
53
+ shadows: 'did you mean "castsShadows"?',
54
+ shadow: 'did you mean "castsShadows"?',
55
+ brightness: 'did you mean "intensity"?',
56
+ strength: 'did you mean "intensity"?',
57
+ distance: 'did you mean "radius" (where the falloff starts) or "falloffDistance"?',
58
+ range: 'did you mean "radius" or "falloffDistance"?'
59
+ };
60
+ function lightLayerProblems(spec) {
61
+ const out = [];
62
+ const raw = spec;
63
+ const kind = lightKindProblem(raw.kind);
64
+ if (kind) out.push(kind);
65
+ if (raw.falloff !== void 0 && !chunkDP37TTMO_cjs.FALLOFFS.includes(raw.falloff)) {
66
+ const guess = typeof raw.falloff === "string" ? chunkTWZNOUM7_cjs.suggestName(raw.falloff, chunkDP37TTMO_cjs.FALLOFFS) : null;
67
+ out.push(`falloff ${JSON.stringify(raw.falloff)} is not supported${guess ? `; did you mean "${guess}"?` : ""} (use ${chunkDP37TTMO_cjs.FALLOFFS.join(", ")}); none is used`);
68
+ }
69
+ for (const k of Object.keys(raw)) {
70
+ if (chunkDP37TTMO_cjs.LIGHT_PROPS.includes(k) || k === "intensity" || k === "shadowDarkness" || k === "shadowDiffusion") {
71
+ out.push(`"${k}" must go inside initial / keyframes, not on the light itself`);
72
+ } else if (LIGHT_ALIASES[k]) out.push(`"${k}" is not a property \u2014 ${LIGHT_ALIASES[k]}`);
73
+ }
74
+ const explained = new Set(Object.keys(LIGHT_ALIASES));
75
+ for (const k of chunkECA2WNRX_cjs.collectPropKeys(spec)) {
76
+ if (LIGHT_ALIASES[k]) out.push(`"${k}" is not a property \u2014 ${LIGHT_ALIASES[k]}`);
77
+ else if (!explained.has(k) && !chunkDP37TTMO_cjs.LIGHT_PROPS.includes(k)) out.push(`"${k}" has no effect on a light (use ${chunkDP37TTMO_cjs.LIGHT_PROPS.join(", ")})`);
78
+ }
79
+ if (numbersOf(spec, "coneFeather").some((v) => v > 1 || v < 0)) out.push("coneFeather is 0..1 (the soft part of the cone edge, as a fraction of the half angle); it is clamped");
80
+ const big = numbersOf(spec, "intensity").find((v) => v > 10);
81
+ if (big !== void 0) out.push(`intensity ${big} looks like a percentage: it is a multiplier (1 = full, 0.25 = a quarter); did you mean ${big / 100}?`);
82
+ const kindNow = typeof raw.kind === "string" && chunkDP37TTMO_cjs.LIGHT_KINDS.includes(raw.kind) ? raw.kind : "point";
83
+ const has = (...keys) => keys.filter((k) => bagsOf(spec).some((b) => b[k] !== void 0));
84
+ const falloffOn = raw.falloff === "smooth" || raw.falloff === "inverseSquare";
85
+ if (kindNow === "ambient" && raw.castsShadows) out.push("an ambient light has no direction, so it makes no shadow: castsShadows is ignored (put it on a point, spot or parallel light)");
86
+ const dist = has("radius", "falloffDistance");
87
+ if (dist.length && !falloffOn && kindNow !== "ambient" && kindNow !== "parallel") out.push(`${dist.join(" / ")} only matter with falloff: 'smooth' or 'inverseSquare' (the default 'none' does not fade with distance): write falloff on the layer`);
88
+ const shadowNums = has("shadowDarkness", "shadowDiffusion");
89
+ if (shadowNums.length && !raw.castsShadows) out.push(`${shadowNums.join(" / ")} only matter with castsShadows: true on the light (and on the layers that cast)`);
90
+ const cone = has("coneAngle", "coneFeather");
91
+ if (cone.length && kindNow !== "spot") out.push(`${cone.join(" / ")} only matter for kind: 'spot' (this light is ${kindNow})`);
92
+ if (kindNow === "parallel" && raw.falloff !== void 0 && raw.falloff !== "none") out.push("a parallel light has no distance, so falloff has no effect on it");
93
+ const init = spec.initial ?? {};
94
+ const pos = ["x", "y", "z"].map((k) => init[k]), look = ["lookAtX", "lookAtY", "lookAtZ"].map((k) => init[k]);
95
+ if ((kindNow === "spot" || kindNow === "parallel") && pos.every((v) => typeof v === "number") && pos.every((v, i) => v === look[i])) {
96
+ out.push("lookAt equals the position, so the light has no direction and lights nothing: move lookAtX / lookAtY / lookAtZ to where it should point");
97
+ }
98
+ if (spec.threeD) out.push("a light has no picture, so threeD has no effect on it (write it without threeD)");
99
+ return out;
100
+ }
101
+ function lightSetProblems(layers) {
102
+ const lights = layers.filter((l) => l.type === "light");
103
+ if (lights.length === 0) return [];
104
+ const out = [];
105
+ const kindOf = (l) => l.kind ?? "point";
106
+ if (!lights.some((l) => kindOf(l) === "ambient")) {
107
+ out.push("there is no ambient light, so what no light reaches is black; add { type: 'light', kind: 'ambient', initial: { intensity: 0.25 } }");
108
+ }
109
+ const direct = lights.filter((l) => kindOf(l) !== "ambient");
110
+ if (direct.length > chunkDP37TTMO_cjs.MAX_LIGHTS) {
111
+ const dropped = direct.slice(chunkDP37TTMO_cjs.MAX_LIGHTS).map((l) => nameOf(l, layers.indexOf(l)));
112
+ out.push(`${direct.length} lights, but at most ${chunkDP37TTMO_cjs.MAX_LIGHTS} are used (the first ${chunkDP37TTMO_cjs.MAX_LIGHTS}, in layer order); ${dropped.join(", ")} and later are dropped. Use fewer lights`);
113
+ }
114
+ const lightsCast = lights.some((l) => kindOf(l) !== "ambient" && l.castsShadows);
115
+ const casters = layers.filter((l) => l.type !== "light" && l.castsShadows && l.threeD);
116
+ if (casters.length > 0 && !lightsCast) out.push(`${casters.length} layer${casters.length > 1 ? "s have" : " has"} castsShadows but no light has castsShadows: add castsShadows: true to the light that should make the shadows`);
117
+ if (lightsCast && casters.length === 0) out.push("a light has castsShadows but no layer has castsShadows: add castsShadows: true to the threeD layers that should cast a shadow");
118
+ if (lightsCast && casters.length > chunkDP37TTMO_cjs.MAX_CASTERS) {
119
+ const dropped = casters.slice(chunkDP37TTMO_cjs.MAX_CASTERS).map((c) => nameOf(c, layers.indexOf(c)));
120
+ out.push(`${casters.length} layers cast shadows, but a layer receives shadows from at most ${chunkDP37TTMO_cjs.MAX_CASTERS} (the first ${chunkDP37TTMO_cjs.MAX_CASTERS}, in layer order); ${dropped.join(", ")} and later cast none`);
121
+ }
122
+ const softLights = lights.filter((l) => l.castsShadows && numbersOf(l, "shadowDiffusion").some((v) => v > 0));
123
+ if (softLights.length >= SOFT_LIGHTS) {
124
+ out.push(`${softLights.length} lights make soft shadows (shadowDiffusion above 0): each is a 16-sample search per pixel and per caster, costly (about 20 ms more a frame at 1080p with 20 lit layers in our measurement). Give shadowDiffusion to one or two lights; the others keep hard shadows`);
125
+ }
126
+ layers.forEach((l, i) => {
127
+ if (l.type === "light" || l.threeD) return;
128
+ const bad = ["castsShadows", "lit"].filter((k) => l[k] !== void 0);
129
+ if (bad.length) out.push(`layer ${nameOf(l, i)}: ${bad.map((k) => `"${k}"`).join(" and ")} only work${bad.length > 1 ? "" : "s"} on threeD layers (add threeD: true; a 2D layer is not lit and casts no shadow)`);
130
+ });
131
+ return out;
132
+ }
133
+ function fogProblems(camera) {
134
+ const out = [];
135
+ const near = numbersOf(camera, "fogNear")[0], far = numbersOf(camera, "fogFar")[0];
136
+ if (near !== void 0 && far !== void 0 && far <= near) out.push(`fogFar (${far}) is not beyond fogNear (${near}): the fog is a hard step at fogNear. Make fogFar larger (the distance where the fog is complete)`);
137
+ if (numbersOf(camera, "fogAmount").some((v) => v < 0 || v > 1)) out.push("fogAmount is 0..1 (how much of the fog colour the farthest things get); it is clamped");
138
+ return out;
139
+ }
140
+
141
+ // src/space/camera.ts
142
+ function pickActiveCamera(cams, t, compEnd) {
143
+ for (let i = cams.length - 1; i >= 0; i--) {
144
+ const c = cams[i];
145
+ const inside = t >= c.start && (t < c.end || c.end >= compEnd && t <= compEnd);
146
+ if (inside) return c;
147
+ }
148
+ return null;
149
+ }
150
+ function findOverlap(cams) {
151
+ for (let i = 0; i < cams.length; i++) {
152
+ for (let j = i + 1; j < cams.length; j++) {
153
+ const a = cams[i];
154
+ const b = cams[j];
155
+ if (a.start < b.end && b.start < a.end) return [i, j];
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+
161
+ // src/space/depth.ts
162
+ var finite = (n) => Number.isFinite(n) ? n : 0;
163
+ function assignDepthOrder(items) {
164
+ const out = items.map((i2) => i2.stackIndex);
165
+ let i = 0;
166
+ while (i < items.length) {
167
+ if (!items[i].threeD) {
168
+ i++;
169
+ continue;
170
+ }
171
+ let j = i;
172
+ while (j + 1 < items.length && items[j + 1].threeD) j++;
173
+ const run = items.slice(i, j + 1).map((it, k) => ({ it, pos: i + k }));
174
+ const slots = run.map((r) => r.it.stackIndex);
175
+ const farthestFirst = [...run].sort((a, b) => finite(b.it.depth) - finite(a.it.depth));
176
+ farthestFirst.forEach((r, k) => {
177
+ out[r.pos] = slots[k];
178
+ });
179
+ i = j + 1;
180
+ }
181
+ return out;
182
+ }
183
+ var DISC_TAPS = 48;
184
+ var GL_VERTEX = `#version 300 es
185
+ in vec2 aPosition;
186
+ out vec2 vTextureCoord;
187
+ uniform vec4 uInputSize;
188
+ uniform vec4 uOutputFrame;
189
+ uniform vec4 uOutputTexture;
190
+ void main(void) {
191
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
192
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
193
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
194
+ gl_Position = vec4(position, 0.0, 1.0);
195
+ vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);
196
+ }`;
197
+ var GL_FRAGMENT = `#version 300 es
198
+ precision highp float;
199
+ in vec2 vTextureCoord;
200
+ uniform sampler2D uTexture;
201
+ uniform vec4 uInputSize;
202
+ uniform float uRadius;
203
+ out vec4 finalColor;
204
+ void main(void) {
205
+ vec4 acc = vec4(0.0);
206
+ for (int i = 0; i < ${DISC_TAPS}; i++) {
207
+ float r = sqrt((float(i) + 0.5) / ${DISC_TAPS}.0) * uRadius;
208
+ float a = float(i) * 2.39996323;
209
+ acc += texture(uTexture, vTextureCoord + vec2(cos(a), sin(a)) * r * uInputSize.zw);
210
+ }
211
+ finalColor = acc / ${DISC_TAPS}.0;
212
+ }`;
213
+ var WGSL = `
214
+ struct GlobalFilterUniforms {
215
+ uInputSize: vec4<f32>, uInputPixel: vec4<f32>, uInputClamp: vec4<f32>,
216
+ uOutputFrame: vec4<f32>, uGlobalFrame: vec4<f32>, uOutputTexture: vec4<f32>,
217
+ };
218
+ struct DiscUniforms { uRadius: f32 };
219
+ @group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
220
+ @group(0) @binding(1) var uTexture: texture_2d<f32>;
221
+ @group(0) @binding(2) var uSampler : sampler;
222
+ @group(1) @binding(0) var<uniform> disc : DiscUniforms;
223
+ struct VSOutput { @builtin(position) position: vec4<f32>, @location(0) uv : vec2<f32> };
224
+ fn filterVertexPosition(aPosition : vec2<f32>) -> vec4<f32> {
225
+ var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
226
+ position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
227
+ position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
228
+ return vec4<f32>(position, 0.0, 1.0);
229
+ }
230
+ fn filterTextureCoord(aPosition : vec2<f32>) -> vec2<f32> {
231
+ return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
232
+ }
233
+ @vertex
234
+ fn mainVertex(@location(0) aPosition : vec2<f32>) -> VSOutput {
235
+ return VSOutput(filterVertexPosition(aPosition), filterTextureCoord(aPosition));
236
+ }
237
+ @fragment
238
+ fn mainFragment(@location(0) uv : vec2<f32>) -> @location(0) vec4<f32> {
239
+ var acc = vec4<f32>(0.0);
240
+ for (var i = 0; i < ${DISC_TAPS}; i++) {
241
+ let r = sqrt((f32(i) + 0.5) / ${DISC_TAPS}.0) * disc.uRadius;
242
+ let a = f32(i) * 2.39996323;
243
+ acc += textureSampleLevel(uTexture, uSampler, uv + vec2<f32>(cos(a), sin(a)) * r * gfu.uInputSize.zw, 0.0);
244
+ }
245
+ return acc / ${DISC_TAPS}.0;
246
+ }`;
247
+ var DiscBlurFilter = class extends pixi_js.Filter {
248
+ constructor(radius = 8) {
249
+ super({
250
+ glProgram: pixi_js.GlProgram.from({ vertex: GL_VERTEX, fragment: GL_FRAGMENT, name: "disc-blur" }),
251
+ gpuProgram: pixi_js.GpuProgram.from({ vertex: { source: WGSL, entryPoint: "mainVertex" }, fragment: { source: WGSL, entryPoint: "mainFragment" } }),
252
+ resources: { disc: new pixi_js.UniformGroup({ uRadius: { value: radius, type: "f32" } }) }
253
+ });
254
+ this.radius = radius;
255
+ }
256
+ /** Blur radius in pixels. The padding follows it, so the blurred edge is not cut off. */
257
+ get radius() {
258
+ return this.resources.disc.uniforms.uRadius;
259
+ }
260
+ set radius(v) {
261
+ const r = Math.max(0, v);
262
+ this.resources.disc.uniforms.uRadius = r;
263
+ this.padding = Math.ceil(r) + 2;
264
+ }
265
+ };
266
+ var localUniformBit = {
267
+ name: "local-uniform-bit",
268
+ vertex: {
269
+ header: (
270
+ /* wgsl */
271
+ `
272
+ struct LocalUniforms { uTransformMatrix:mat3x3<f32>, uColor:vec4<f32>, uRound:f32, }
273
+ @group(1) @binding(0) var<uniform> localUniforms : LocalUniforms;`
274
+ ),
275
+ main: (
276
+ /* wgsl */
277
+ `
278
+ vColor *= localUniforms.uColor;
279
+ modelMatrix *= localUniforms.uTransformMatrix;`
280
+ ),
281
+ end: (
282
+ /* wgsl */
283
+ `
284
+ if(localUniforms.uRound == 1) { vPosition = vec4(roundPixels(vPosition.xy, globalUniforms.uResolution), vPosition.zw); }`
285
+ )
286
+ }
287
+ };
288
+ var textureBit = {
289
+ name: "texture-bit",
290
+ vertex: {
291
+ header: `struct TextureUniforms { uTextureMatrix:mat3x3<f32>, }
292
+ @group(2) @binding(2) var<uniform> textureUniforms : TextureUniforms;`,
293
+ main: `uv = (textureUniforms.uTextureMatrix * vec3(uv, 1.0)).xy;`
294
+ },
295
+ fragment: {
296
+ header: `@group(2) @binding(0) var uTexture: texture_2d<f32>;
297
+ @group(2) @binding(1) var uSampler: sampler;`,
298
+ main: `outColor = textureSample(uTexture, uSampler, vUV);`
299
+ }
300
+ };
301
+ var roundPixelsBit = {
302
+ name: "round-pixels-bit",
303
+ vertex: { header: `fn roundPixels(position: vec2<f32>, targetSize: vec2<f32>) -> vec2<f32> { return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; }` }
304
+ };
305
+ var ML = chunkDP37TTMO_cjs.MAX_LIGHTS;
306
+ var MC = chunkDP37TTMO_cjs.MAX_CASTERS;
307
+ var TAPS = chunkDP37TTMO_cjs.SHADOW_TAPS;
308
+ var SHADOW_KEYS = Array.from({ length: MC }, (_, j) => `uShadow${j}`);
309
+ var WGSL_SHADOW_DECLS = SHADOW_KEYS.map((k, j) => ` @group(2) @binding(${4 + j}) var ${k}: texture_2d<f32>;`).join("\n");
310
+ var WGSL_SHADOW_PICK = SHADOW_KEYS.map((k, j) => (j === 0 ? "" : "else ") + `if (j == ${j}) { a = textureSampleLevel(${k}, uSampler, uv, 0.0).a; }`).join(" ");
311
+ var GLSL_SHADOW_DECLS = SHADOW_KEYS.map((k) => `uniform sampler2D ${k};`).join(" ");
312
+ var GLSL_SHADOW_PICK = SHADOW_KEYS.map((k, j) => (j === MC - 1 ? "" : `j == ${j} ? textureLod(${k}, uv, 0.0).a : `) + (j === MC - 1 ? `textureLod(${k}, uv, 0.0).a` : "")).join("");
313
+ var STRUCT_WGSL = (
314
+ /* wgsl */
315
+ `
316
+ struct LightUniforms {
317
+ uOrigin: vec4<f32>, uAxisU: vec4<f32>, uAxisV: vec4<f32>, uCam: vec4<f32>, uCamFwd: vec4<f32>,
318
+ uAmbient: vec4<f32>, uFogColor: vec4<f32>, uFogRange: vec4<f32>,
319
+ uLPos: array<vec4<f32>, ${ML}>, uLDir: array<vec4<f32>, ${ML}>, uLCol: array<vec4<f32>, ${ML}>, uLFall: array<vec4<f32>, ${ML}>, uLShadow: array<vec4<f32>, ${ML}>,
320
+ uCO: array<vec4<f32>, ${MC}>, uCU: array<vec4<f32>, ${MC}>, uCV: array<vec4<f32>, ${MC}>,
321
+ };
322
+ @group(2) @binding(3) var<uniform> lightUniforms : LightUniforms;
323
+ `
324
+ );
325
+ var lightBit = {
326
+ name: "light-bit",
327
+ vertex: {
328
+ header: (
329
+ /* wgsl */
330
+ `${STRUCT_WGSL}
331
+ @out vPW: vec4<f32>;`
332
+ ),
333
+ // world position over camera depth, and 1 / depth: interpolated per fragment and divided, they give the exact (perspective-correct) point
334
+ end: (
335
+ /* wgsl */
336
+ `
337
+ {
338
+ let Pv = lightUniforms.uOrigin.xyz + vUV.x * lightUniforms.uAxisU.xyz + vUV.y * lightUniforms.uAxisV.xyz;
339
+ let zc = max(dot(Pv - lightUniforms.uCam.xyz, lightUniforms.uCamFwd.xyz), 1e-3);
340
+ vPW = vec4<f32>(Pv / zc, 1.0 / zc);
341
+ }`
342
+ )
343
+ },
344
+ fragment: {
345
+ header: (
346
+ /* wgsl */
347
+ `
348
+ ${STRUCT_WGSL}
349
+ @in vPW: vec4<f32>;
350
+ ${WGSL_SHADOW_DECLS}
351
+
352
+ fn casterAlpha(j: i32, uv: vec2<f32>) -> f32 {
353
+ let inside = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);
354
+ var a = 0.0;
355
+ ${WGSL_SHADOW_PICK}
356
+ return a * inside;
357
+ }
358
+
359
+ fn shadowAt(P: vec3<f32>, i: i32) -> f32 {
360
+ let lp = lightUniforms.uLPos[i]; let ld = lightUniforms.uLDir[i];
361
+ let parallel = lp.w > 2.5;
362
+ var occl = 0.0;
363
+ for (var j = 0; j < ${MC}; j++) {
364
+ if (f32(j) >= lightUniforms.uAxisU.w) { break; }
365
+ let Co = lightUniforms.uCO[j].xyz; let Cu = lightUniforms.uCU[j].xyz; let Cv = lightUniforms.uCV[j].xyz;
366
+ let Nc = cross(Cu, Cv);
367
+ var D = lp.xyz - P;
368
+ if (parallel) { D = -ld.xyz * 100000.0; }
369
+ let denom = dot(D, Nc);
370
+ if (abs(denom) < 1e-9) { continue; }
371
+ let t = dot(Co - P, Nc) / denom;
372
+ if (t <= 1e-4 || t >= 1.0) { continue; }
373
+ let H = P + t * D;
374
+ let uv = vec2<f32>(dot(H - Co, Cu) / dot(Cu, Cu), dot(H - Co, Cv) / dot(Cv, Cv));
375
+ let lenD = length(D);
376
+ let far = select((1.0 - t) * lenD, 1000.0, parallel);
377
+ let w = lightUniforms.uLShadow[i].y * (t * lenD) / max(far, 1.0);
378
+ let r = vec2<f32>(w / length(Cu), w / length(Cv));
379
+ var a = 0.0;
380
+ if (w * w > 0.0) {
381
+ for (var k = 0; k < ${TAPS}; k++) {
382
+ let rr = sqrt((f32(k) + 0.5) / ${TAPS}.0);
383
+ let an = f32(k) * 2.39996323;
384
+ a += casterAlpha(j, uv + vec2<f32>(cos(an), sin(an)) * rr * r);
385
+ }
386
+ a = a / ${TAPS}.0;
387
+ } else { a = casterAlpha(j, uv); }
388
+ occl = max(occl, a);
389
+ }
390
+ return 1.0 - occl * lightUniforms.uLShadow[i].x;
391
+ }
392
+ `
393
+ ),
394
+ end: (
395
+ /* wgsl */
396
+ `
397
+ {
398
+ let P = vPW.xyz / vPW.w;
399
+ if (lightUniforms.uCam.w > 0.5) {
400
+ var N = normalize(cross(lightUniforms.uAxisU.xyz, lightUniforms.uAxisV.xyz));
401
+ if (dot(N, lightUniforms.uCam.xyz - P) < 0.0) { N = -N; }
402
+ var light = lightUniforms.uAmbient.rgb;
403
+ for (var i = 0; i < ${ML}; i++) {
404
+ if (f32(i) >= lightUniforms.uOrigin.w) { break; }
405
+ let lp = lightUniforms.uLPos[i]; let ld = lightUniforms.uLDir[i]; let lc = lightUniforms.uLCol[i]; let lf = lightUniforms.uLFall[i];
406
+ var L = -ld.xyz;
407
+ var dist = 0.0;
408
+ if (lp.w < 2.5) { let d = lp.xyz - P; dist = length(d); L = d / max(dist, 1e-4); }
409
+ let ndl = max(dot(N, L), 0.0);
410
+ var att = 1.0;
411
+ if (lp.w < 2.5) {
412
+ if (lf.x > 0.5 && lf.x < 1.5) { att = 1.0 - smoothstep(lf.y, lf.y + max(lf.z, 1e-3), dist); }
413
+ else if (lf.x > 1.5) { att = min(1.0, (lf.y * lf.y) / max(dist * dist, 1e-6)); }
414
+ }
415
+ var cone = 1.0;
416
+ if (lp.w > 1.5 && lp.w < 2.5) { cone = smoothstep(ld.w, lc.w, dot(-L, ld.xyz)); }
417
+ var sh = 1.0;
418
+ if (lf.w > 0.5 && ndl * att * cone > 0.0) { sh = shadowAt(P, i); }
419
+ light += lc.rgb * (ndl * att * cone * sh);
420
+ }
421
+ finalColor = vec4<f32>(min(finalColor.rgb * light, vec3<f32>(finalColor.a)), finalColor.a);
422
+ }
423
+ if (lightUniforms.uFogRange.w > 0.5) {
424
+ let depth = dot(P - lightUniforms.uCam.xyz, lightUniforms.uCamFwd.xyz);
425
+ let f = clamp((depth - lightUniforms.uFogRange.x) / max(lightUniforms.uFogRange.y - lightUniforms.uFogRange.x, 1e-3), 0.0, 1.0) * lightUniforms.uFogColor.a;
426
+ finalColor = vec4<f32>(mix(finalColor.rgb, lightUniforms.uFogColor.rgb * finalColor.a, f), finalColor.a);
427
+ }
428
+ }
429
+ `
430
+ )
431
+ }
432
+ };
433
+ var lightBitGl = {
434
+ fragment: {
435
+ header: (
436
+ /* glsl */
437
+ `
438
+ uniform vec4 uOrigin; uniform vec4 uAxisU; uniform vec4 uAxisV; uniform vec4 uCam; uniform vec4 uCamFwd;
439
+ uniform vec4 uAmbient; uniform vec4 uFogColor; uniform vec4 uFogRange;
440
+ uniform vec4 uLPos[${ML}]; uniform vec4 uLDir[${ML}]; uniform vec4 uLCol[${ML}]; uniform vec4 uLFall[${ML}]; uniform vec4 uLShadow[${ML}];
441
+ uniform vec4 uCO[${MC}]; uniform vec4 uCU[${MC}]; uniform vec4 uCV[${MC}];
442
+ ${GLSL_SHADOW_DECLS}
443
+
444
+ float casterAlpha(int j, vec2 uv) {
445
+ float inside = step(0.0, uv.x) * step(uv.x, 1.0) * step(0.0, uv.y) * step(uv.y, 1.0);
446
+ float a = ${GLSL_SHADOW_PICK};
447
+ return a * inside;
448
+ }
449
+
450
+ float shadowAt(vec3 P, int i) {
451
+ vec4 lp = uLPos[i]; vec4 ld = uLDir[i];
452
+ bool parallel = lp.w > 2.5;
453
+ float occl = 0.0;
454
+ for (int j = 0; j < ${MC}; j++) {
455
+ if (float(j) >= uAxisU.w) break;
456
+ vec3 Co = uCO[j].xyz; vec3 Cu = uCU[j].xyz; vec3 Cv = uCV[j].xyz;
457
+ vec3 Nc = cross(Cu, Cv);
458
+ vec3 D = parallel ? -ld.xyz * 100000.0 : lp.xyz - P;
459
+ float denom = dot(D, Nc);
460
+ if (abs(denom) < 1e-9) continue;
461
+ float t = dot(Co - P, Nc) / denom;
462
+ if (t <= 1e-4 || t >= 1.0) continue;
463
+ vec3 H = P + t * D;
464
+ vec2 uv = vec2(dot(H - Co, Cu) / dot(Cu, Cu), dot(H - Co, Cv) / dot(Cv, Cv));
465
+ float lenD = length(D);
466
+ float far = parallel ? 1000.0 : (1.0 - t) * lenD;
467
+ float w = uLShadow[i].y * (t * lenD) / max(far, 1.0);
468
+ vec2 r = vec2(w / length(Cu), w / length(Cv));
469
+ float a = 0.0;
470
+ if (w * w > 0.0) {
471
+ for (int k = 0; k < ${TAPS}; k++) {
472
+ float rr = sqrt((float(k) + 0.5) / ${TAPS}.0);
473
+ float an = float(k) * 2.39996323;
474
+ a += casterAlpha(j, uv + vec2(cos(an), sin(an)) * rr * r);
475
+ }
476
+ a /= ${TAPS}.0;
477
+ } else { a = casterAlpha(j, uv); }
478
+ occl = max(occl, a);
479
+ }
480
+ return 1.0 - occl * uLShadow[i].x;
481
+ }
482
+ `
483
+ ),
484
+ end: (
485
+ /* glsl */
486
+ `
487
+ {
488
+ vec3 P = vPW.xyz / vPW.w;
489
+ if (uCam.w > 0.5) {
490
+ vec3 N = normalize(cross(uAxisU.xyz, uAxisV.xyz));
491
+ if (dot(N, uCam.xyz - P) < 0.0) N = -N;
492
+ vec3 light = uAmbient.rgb;
493
+ for (int i = 0; i < ${ML}; i++) {
494
+ if (float(i) >= uOrigin.w) break;
495
+ vec4 lp = uLPos[i]; vec4 ld = uLDir[i]; vec4 lc = uLCol[i]; vec4 lf = uLFall[i];
496
+ vec3 L = -ld.xyz;
497
+ float dist = 0.0;
498
+ if (lp.w < 2.5) { vec3 d = lp.xyz - P; dist = length(d); L = d / max(dist, 1e-4); }
499
+ float ndl = max(dot(N, L), 0.0);
500
+ float att = 1.0;
501
+ if (lp.w < 2.5) {
502
+ if (lf.x > 0.5 && lf.x < 1.5) att = 1.0 - smoothstep(lf.y, lf.y + max(lf.z, 1e-3), dist);
503
+ else if (lf.x > 1.5) att = min(1.0, (lf.y * lf.y) / max(dist * dist, 1e-6));
504
+ }
505
+ float cone = 1.0;
506
+ if (lp.w > 1.5 && lp.w < 2.5) cone = smoothstep(ld.w, lc.w, dot(-L, ld.xyz));
507
+ float sh = 1.0;
508
+ if (lf.w > 0.5 && ndl * att * cone > 0.0) sh = shadowAt(P, i);
509
+ light += lc.rgb * (ndl * att * cone * sh);
510
+ }
511
+ finalColor.rgb = min(finalColor.rgb * light, vec3(finalColor.a));
512
+ }
513
+ if (uFogRange.w > 0.5) {
514
+ float depth = dot(P - uCam.xyz, uCamFwd.xyz);
515
+ float f = clamp((depth - uFogRange.x) / max(uFogRange.y - uFogRange.x, 1e-3), 0.0, 1.0) * uFogColor.a;
516
+ finalColor.rgb = mix(finalColor.rgb, uFogColor.rgb * finalColor.a, f);
517
+ }
518
+ }
519
+ `
520
+ )
521
+ }
522
+ };
523
+ var GL_VERTEX2 = `#version 300 es
524
+ in vec2 aPosition;
525
+ in vec2 aUV;
526
+ out vec4 vColor;
527
+ out vec2 vUV;
528
+ out vec4 vPW;
529
+ uniform vec4 uOrigin; uniform vec4 uAxisU; uniform vec4 uAxisV; uniform vec4 uCam; uniform vec4 uCamFwd;
530
+ uniform mat3 uProjectionMatrix; uniform mat3 uWorldTransformMatrix; uniform vec4 uWorldColorAlpha; uniform vec2 uResolution;
531
+ uniform mat3 uTransformMatrix; uniform vec4 uColor; uniform float uRound;
532
+ uniform mat3 uTextureMatrix;
533
+ vec2 roundPixels(vec2 position, vec2 targetSize) { return (floor(((position * 0.5 + 0.5) * targetSize) + 0.5) / targetSize) * 2.0 - 1.0; }
534
+ void main(void) {
535
+ mat3 modelMatrix = uTransformMatrix;
536
+ vColor = uColor;
537
+ vUV = (uTextureMatrix * vec3(aUV, 1.0)).xy;
538
+ mat3 mvp = uProjectionMatrix * uWorldTransformMatrix * modelMatrix;
539
+ gl_Position = vec4((mvp * vec3(aPosition, 1.0)).xy, 0.0, 1.0);
540
+ vColor *= uWorldColorAlpha;
541
+ if (uRound == 1.) gl_Position.xy = roundPixels(gl_Position.xy, uResolution);
542
+ vec3 Pv = uOrigin.xyz + vUV.x * uAxisU.xyz + vUV.y * uAxisV.xyz;
543
+ float zc = max(dot(Pv - uCam.xyz, uCamFwd.xyz), 1e-3);
544
+ vPW = vec4(Pv / zc, 1.0 / zc);
545
+ }`;
546
+ var GL_FRAGMENT2 = `#version 300 es
547
+ precision highp float;
548
+ in vec4 vColor;
549
+ in vec2 vUV;
550
+ in vec4 vPW;
551
+ out vec4 finalColor;
552
+ uniform sampler2D uTexture;
553
+ ${lightBitGl.fragment.header}
554
+ void main(void) {
555
+ vec4 outColor = texture(uTexture, vUV);
556
+ finalColor = outColor * vColor;
557
+ ${lightBitGl.fragment.end}
558
+ }`;
559
+ var programs = null;
560
+ function getPrograms() {
561
+ programs ??= {
562
+ // pixi compiles high-shader GLSL as WebGL1-compatible (no textureLod, no dynamic uniform-array index): the GL program is written as ES 3.00
563
+ gl: pixi_js.GlProgram.from({ name: "lit-mesh", vertex: GL_VERTEX2, fragment: GL_FRAGMENT2 }),
564
+ gpu: pixi_js.compileHighShaderGpuProgram({ name: "lit-mesh", bits: [localUniformBit, textureBit, roundPixelsBit, lightBit] })
565
+ };
566
+ return programs;
567
+ }
568
+ var LitMaterial = class {
569
+ shader;
570
+ u;
571
+ constructor() {
572
+ const { gl, gpu } = getPrograms();
573
+ const v4 = () => ({ value: new Float32Array(4), type: "vec4<f32>" });
574
+ const arr = (n) => ({ value: new Float32Array(4 * n), type: "vec4<f32>", size: n });
575
+ this.u = new pixi_js.UniformGroup({
576
+ uOrigin: v4(),
577
+ uAxisU: v4(),
578
+ uAxisV: v4(),
579
+ uCam: v4(),
580
+ uCamFwd: v4(),
581
+ uAmbient: v4(),
582
+ uFogColor: v4(),
583
+ uFogRange: v4(),
584
+ uLPos: arr(ML),
585
+ uLDir: arr(ML),
586
+ uLCol: arr(ML),
587
+ uLFall: arr(ML),
588
+ uLShadow: arr(ML),
589
+ uCO: arr(MC),
590
+ uCU: arr(MC),
591
+ uCV: arr(MC)
592
+ });
593
+ this.shader = new pixi_js.Shader({
594
+ glProgram: gl,
595
+ gpuProgram: gpu,
596
+ resources: {
597
+ uTexture: pixi_js.Texture.EMPTY.source,
598
+ uSampler: pixi_js.Texture.EMPTY.source.style,
599
+ textureUniforms: { uTextureMatrix: { type: "mat3x3<f32>", value: new pixi_js.Matrix() } },
600
+ lightUniforms: this.u,
601
+ ...Object.fromEntries(SHADOW_KEYS.map((k) => [k, pixi_js.Texture.EMPTY.source]))
602
+ }
603
+ });
604
+ }
605
+ setTexture(src) {
606
+ if (this.shader.resources.uTexture !== src) {
607
+ this.shader.resources.uTexture = src;
608
+ this.shader.resources.uSampler = src.style;
609
+ }
610
+ }
611
+ update(frame, cam, lights, fog, casters) {
612
+ const U = this.u.uniforms;
613
+ const nc = Math.min(casters.length, MC);
614
+ U.uOrigin.set([frame.o.x, frame.o.y, frame.o.z, lights ? lights.count : 0]);
615
+ U.uAxisU.set([frame.u.x, frame.u.y, frame.u.z, nc]);
616
+ U.uAxisV.set([frame.v.x, frame.v.y, frame.v.z, 0]);
617
+ U.uCam.set([cam.x, cam.y, cam.z, lights ? 1 : 0]);
618
+ U.uCamFwd.set([cam.fx, cam.fy, cam.fz, 0]);
619
+ if (lights) {
620
+ U.uAmbient.set([lights.ambient[0], lights.ambient[1], lights.ambient[2], 0]);
621
+ U.uLPos.set(lights.pos);
622
+ U.uLDir.set(lights.dir);
623
+ U.uLCol.set(lights.col);
624
+ U.uLFall.set(lights.fall);
625
+ U.uLShadow.set(lights.shadow);
626
+ }
627
+ if (fog) {
628
+ U.uFogColor.set([fog.r, fog.g, fog.b, fog.amount]);
629
+ U.uFogRange.set([fog.near, fog.far, 0, 1]);
630
+ } else U.uFogRange.set([0, 0, 0, 0]);
631
+ const keys = SHADOW_KEYS;
632
+ for (let j = 0; j < MC; j++) {
633
+ const c = casters[j];
634
+ if (c && j < nc) {
635
+ U.uCO.set([c.frame.o.x, c.frame.o.y, c.frame.o.z, 0], 4 * j);
636
+ U.uCU.set([c.frame.u.x, c.frame.u.y, c.frame.u.z, 0], 4 * j);
637
+ U.uCV.set([c.frame.v.x, c.frame.v.y, c.frame.v.z, 0], 4 * j);
638
+ if (this.shader.resources[keys[j]] !== c.source) this.shader.resources[keys[j]] = c.source;
639
+ } else if (this.shader.resources[keys[j]] !== pixi_js.Texture.EMPTY.source) this.shader.resources[keys[j]] = pixi_js.Texture.EMPTY.source;
640
+ }
641
+ this.u.update();
642
+ }
643
+ destroy() {
644
+ this.shader.destroy();
645
+ }
646
+ };
647
+
648
+ // src/space/Layer3D.ts
649
+ var MAX_TEXTURE_SIZE = 4096;
650
+ var SUPERSAMPLE = 2;
651
+ var MAX_TEXTURE_PIXELS = 2048 * 2048;
652
+ var ANTIALIAS_MAX_PIXELS = 1024 * 1024;
653
+ var MESH_GRID = 20;
654
+ function filterPadding(target) {
655
+ const filters = target.filters;
656
+ if (!Array.isArray(filters)) return 0;
657
+ let pad = 0;
658
+ for (const f of filters) pad += f?.padding ?? 0;
659
+ return Number.isFinite(pad) && pad > 0 ? Math.ceil(pad) : 0;
660
+ }
661
+ function readLayerTransform(target) {
662
+ const c = target;
663
+ return {
664
+ x: c.x,
665
+ y: c.y,
666
+ z: c.z ?? 0,
667
+ rotationX: (c.rotationX ?? 0) * chunkDP37TTMO_cjs.DEG,
668
+ rotationY: (c.rotationY ?? 0) * chunkDP37TTMO_cjs.DEG,
669
+ rotationZ: c.rotation,
670
+ scaleX: c.scale.x,
671
+ scaleY: c.scale.y,
672
+ pivotX: c.pivot.x,
673
+ pivotY: c.pivot.y
674
+ };
675
+ }
676
+ var Layer3D = class {
677
+ constructor(seq, frameOf2) {
678
+ this.seq = seq;
679
+ this.frameOf = frameOf2;
680
+ const target = seq.target;
681
+ target.z ??= 0;
682
+ target.rotationX ??= 0;
683
+ target.rotationY ??= 0;
684
+ this.display = new pixi_js.PerspectiveMesh({ texture: pixi_js.Texture.WHITE, verticesX: MESH_GRID, verticesY: MESH_GRID });
685
+ if (seq.spec.name) this.display.label = seq.spec.name;
686
+ this.display.visible = false;
687
+ }
688
+ seq;
689
+ frameOf;
690
+ display;
691
+ /** Camera-space depth from the last update (larger = farther). */
692
+ depth = 0;
693
+ rt = null;
694
+ /**
695
+ * Textures replaced by a bigger / smaller one. Destroying one right away warns "destroyed while still
696
+ * bound": the mesh's bind group still points at it until the next render. They go at the next update,
697
+ * by which time a frame has been drawn with the new texture.
698
+ */
699
+ retired = [];
700
+ size = null;
701
+ warnedBehind = false;
702
+ /** Depth-of-field blur radius (px) from the last `setBlur`; 0 = no filter. */
703
+ blur = 0;
704
+ blurFilter = null;
705
+ blendWas = "normal";
706
+ blendFilter = null;
707
+ /** The plane in world space from the last update (a texture uv of 0..1 maps to `o + u·U + v·V`): what shading and shadows need. */
708
+ world = null;
709
+ /** The lit shader: made only when the composition has a light or fog (a composition with neither makes nothing). */
710
+ material = null;
711
+ /** This layer's own texture (what a shadow reads), or null before the first draw. */
712
+ get source() {
713
+ return this.rt?.source ?? null;
714
+ }
715
+ warnedLit = false;
716
+ /** The spec of the layer this draws (for `lit`, `castsShadows`). */
717
+ get spec() {
718
+ return this.seq.spec;
719
+ }
720
+ update(host, basis) {
721
+ this.flushRetired();
722
+ const target = this.seq.target;
723
+ if (!target || !target.renderable || !target.visible) return this.hide();
724
+ const bounds = this.frameOf();
725
+ const pad = filterPadding(target);
726
+ const needW = Math.ceil(bounds.width + 2 * pad);
727
+ const needH = Math.ceil(bounds.height + 2 * pad);
728
+ if (!(needW >= 1 && needH >= 1) || !Number.isFinite(bounds.x) || !Number.isFinite(bounds.y)) return this.hide();
729
+ const size = this.sizeFor(needW, needH);
730
+ const frame = { x: bounds.x - pad, y: bounds.y - pad, width: size.w, height: size.h };
731
+ const projected = chunkDP37TTMO_cjs.projectLayer(readLayerTransform(target), frame, basis);
732
+ this.depth = projected.depth;
733
+ {
734
+ const lt = readLayerTransform(target);
735
+ const o = chunkDP37TTMO_cjs.layerToWorld(lt, frame.x, frame.y);
736
+ const pu = chunkDP37TTMO_cjs.layerToWorld(lt, frame.x + frame.width, frame.y);
737
+ const pv = chunkDP37TTMO_cjs.layerToWorld(lt, frame.x, frame.y + frame.height);
738
+ this.world = { o, u: { x: pu.x - o.x, y: pu.y - o.y, z: pu.z - o.z }, v: { x: pv.x - o.x, y: pv.y - o.y, z: pv.z - o.z } };
739
+ }
740
+ if (!projected.visible) {
741
+ if (!this.warnedBehind) {
742
+ this.warnedBehind = true;
743
+ if (this.seq.spec.hideBehindCamera) return this.hide();
744
+ if (projected.depth > chunkDP37TTMO_cjs.NEAR) {
745
+ console.warn(
746
+ `pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.seq.spec)}: part of it (a corner) reaches the camera plane (z = ${(target.z ?? 0).toFixed(0)}, rotationX ${(target.rotationX ?? 0).toFixed(0)}\xB0, rotationY ${(target.rotationY ?? 0).toFixed(0)}\xB0), and a layer is hidden whole when any corner is at or behind it. Tilt it less (rotationX / rotationY), make it smaller, or keep the camera further away from it (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
747
+ );
748
+ } else {
749
+ console.warn(
750
+ `pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.seq.spec)} is at or behind the camera plane (z = ${(target.z ?? 0).toFixed(0)}) and is hidden. If that is unintended, lower its z or move the camera back (the default camera sits at z = (height/2)/tan(fov/2), about 989 for 720p at fov 40).`
751
+ );
752
+ }
753
+ }
754
+ return this.hide();
755
+ }
756
+ this.ensureTexture(size);
757
+ host.render({
758
+ container: target,
759
+ target: this.rt,
760
+ transform: new pixi_js.Matrix().translate(-frame.x, -frame.y),
761
+ clear: true,
762
+ clearColor: [0, 0, 0, 0]
763
+ });
764
+ this.display.geometry.setCorners(...projected.corners);
765
+ this.material?.setTexture(this.rt.source);
766
+ this.display.visible = true;
767
+ }
768
+ /**
769
+ * Depth-of-field blur on the projected mesh (screen space), so a tilted or scaled layer blurs by the size it is drawn at.
770
+ * A radius under MIN_BLUR removes the filter: a sharp layer costs nothing. A blend mode moves to the filter while it is on
771
+ * (a filtered object blends as a whole), and comes back when it goes.
772
+ */
773
+ setBlur(radius) {
774
+ const d = this.display;
775
+ const r = radius > chunkDP37TTMO_cjs.MIN_BLUR ? Math.min(radius, chunkDP37TTMO_cjs.MAX_BLUR) : 0;
776
+ this.blur = r;
777
+ if (r === 0) {
778
+ if (this.blurFilter) {
779
+ d.filters = null;
780
+ d.blendMode = this.blendWas;
781
+ this.blurFilter.destroy();
782
+ this.blurFilter = null;
783
+ this.blendFilter?.destroy();
784
+ this.blendFilter = null;
785
+ }
786
+ return;
787
+ }
788
+ if (!this.blurFilter) {
789
+ this.blurFilter = new DiscBlurFilter(r);
790
+ this.blendWas = d.blendMode ?? "normal";
791
+ const chain = [this.blurFilter];
792
+ if (chunk3DLYAPK7_cjs.isAdvancedBlend(this.blendWas)) {
793
+ this.blendFilter = chunk3DLYAPK7_cjs.blendFilterFor(this.blendWas);
794
+ if (this.blendFilter) {
795
+ chain.push(this.blendFilter);
796
+ d.blendMode = "normal";
797
+ }
798
+ } else if (this.blendWas !== "normal" && this.blendWas !== "inherit") {
799
+ this.blurFilter.blendMode = this.blendWas;
800
+ d.blendMode = "normal";
801
+ }
802
+ d.filters = chain;
803
+ }
804
+ this.blurFilter.radius = r;
805
+ }
806
+ /**
807
+ * Turn the lit shader on (made once) or off. Returns it, or null when it is off or cannot be made on this renderer (said once: the layer
808
+ * then draws unlit, as it did before there were lights).
809
+ */
810
+ setLit(on) {
811
+ if (on && !this.material) {
812
+ try {
813
+ this.material = new LitMaterial();
814
+ } catch (err) {
815
+ if (!this.warnedLit) {
816
+ this.warnedLit = true;
817
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.seq.spec)}: the lighting shader could not be made on this renderer (${err?.message ?? err}); the layer is drawn unlit`);
818
+ }
819
+ return null;
820
+ }
821
+ if (this.rt) this.material.setTexture(this.rt.source);
822
+ this.display.shader = this.material.shader;
823
+ } else if (!on && this.material) {
824
+ this.display.shader = null;
825
+ this.material.destroy();
826
+ this.material = null;
827
+ }
828
+ return this.material;
829
+ }
830
+ destroy() {
831
+ this.setLit(false);
832
+ this.setBlur(0);
833
+ this.display.destroy();
834
+ this.rt?.destroy(true);
835
+ this.rt = null;
836
+ this.flushRetired();
837
+ }
838
+ flushRetired() {
839
+ for (const t of this.retired) t.destroy(true);
840
+ this.retired = [];
841
+ }
842
+ hide() {
843
+ this.display.visible = false;
844
+ }
845
+ /**
846
+ * Texture size for content of w×h. An existing texture is kept while it is at
847
+ * least that big and at most 2x too big, so content whose bounds change every
848
+ * frame (typewriter text, growing shapes) does not reallocate each frame.
849
+ */
850
+ sizeFor(w, h) {
851
+ const cur = this.size;
852
+ if (cur && cur.w >= w && cur.h >= h && cur.w <= 2 * w && cur.h <= 2 * h) return cur;
853
+ const resolution = Math.min(
854
+ SUPERSAMPLE,
855
+ MAX_TEXTURE_SIZE / Math.max(w, h),
856
+ Math.sqrt(MAX_TEXTURE_PIXELS / (w * h))
857
+ );
858
+ const antialias = w * h * resolution * resolution <= ANTIALIAS_MAX_PIXELS;
859
+ return { w, h, resolution, antialias };
860
+ }
861
+ ensureTexture(size) {
862
+ if (this.rt && this.size === size) return;
863
+ if (this.rt && this.size && this.size.antialias === size.antialias) {
864
+ this.rt.resize(size.w, size.h, size.resolution);
865
+ this.size = size;
866
+ return;
867
+ }
868
+ const next = pixi_js.RenderTexture.create({
869
+ width: size.w,
870
+ height: size.h,
871
+ resolution: size.resolution,
872
+ antialias: size.antialias
873
+ });
874
+ this.display.texture = next;
875
+ if (this.rt) this.retired.push(this.rt);
876
+ this.rt = next;
877
+ this.size = size;
878
+ }
879
+ };
880
+
881
+ // src/space/lint.ts
882
+ var defaultWarn = (m) => console.warn(m);
883
+ var FOV_HINT = `perspective is the camera's "fov" \u2014 add a { type: "camera" } layer and set fov in its initial / keyframes`;
884
+ var DOF_HINT = `depth of field is the camera's "focus" (the plane that is sharp: a layer name or a z) and "aperture" (how shallow: 0 = off, 30 = default) \u2014 put them in the camera's initial / keyframes`;
885
+ var LAYER_ALIASES = {
886
+ rotateX: 'did you mean "rotationX"?',
887
+ rotateY: 'did you mean "rotationY"?',
888
+ rotateZ: 'did you mean "rotation"?',
889
+ rotationZ: 'did you mean "rotation" (it is the Z rotation)?',
890
+ translateZ: 'did you mean "z"?',
891
+ depth: 'did you mean "z"?',
892
+ posZ: 'did you mean "z"?',
893
+ perspective: FOV_HINT,
894
+ zoom: FOV_HINT
895
+ };
896
+ var CAMERA_ALIASES = {
897
+ pointOfInterest: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
898
+ lookAt: 'did you mean "lookAtX / lookAtY / lookAtZ"?',
899
+ depthOfField: DOF_HINT,
900
+ dof: DOF_HINT,
901
+ focalDistance: DOF_HINT,
902
+ focusDistance: DOF_HINT,
903
+ fStop: DOF_HINT,
904
+ blurAmount: DOF_HINT
905
+ };
906
+ var CAMERA_PROPS = ["x", "y", "z", "fov", "focus", "aperture", "lookAtX", "lookAtY", "lookAtZ", "offsetX", "offsetY", "offsetZ", "lookOffsetX", "lookOffsetY", "lookOffsetZ", "fogNear", "fogFar", "fogColor", "fogAmount"];
907
+ var NEEDS_THREE_D = ["z", "rotationX", "rotationY"];
908
+ var CAMERA_ONLY = ["focus", "aperture", "fogNear", "fogFar", "fogColor", "fogAmount"];
909
+ var SKEW_KEYS = ["skew", "skewX", "skewY"];
910
+ function lintSequence(spec, warn = defaultWarn) {
911
+ const who = chunkECA2WNRX_cjs.describeLayer(spec);
912
+ const keys = chunkECA2WNRX_cjs.collectPropKeys(spec);
913
+ const explained = /* @__PURE__ */ new Set();
914
+ for (const k of keys) {
915
+ const hint = LAYER_ALIASES[k] ?? (spec.type === "camera" ? CAMERA_ALIASES[k] : void 0);
916
+ if (hint) {
917
+ explained.add(k);
918
+ warn(`pixi-effects: ${who}: "${k}" is not a property \u2014 ${hint}`);
919
+ }
920
+ }
921
+ if (spec.type === "light") {
922
+ for (const m of lightLayerProblems(spec)) warn(`pixi-effects: ${who}: ${m}`);
923
+ return;
924
+ }
925
+ if (spec.type === "camera") {
926
+ for (const m of fogProblems(spec)) warn(`pixi-effects: ${who}: ${m}`);
927
+ const raw = spec;
928
+ for (const p of CAMERA_PROPS) {
929
+ if (p in raw) warn(`pixi-effects: ${who}: "${p}" must go inside initial / keyframes, not on the camera itself`);
930
+ }
931
+ for (const k of Object.keys(raw)) {
932
+ const hint = CAMERA_ALIASES[k] ?? LAYER_ALIASES[k];
933
+ if (hint) warn(`pixi-effects: ${who}: "${k}" is not a property \u2014 ${hint} (inside initial)`);
934
+ }
935
+ for (const k of keys) {
936
+ if (!explained.has(k) && !CAMERA_PROPS.includes(k)) {
937
+ warn(`pixi-effects: ${who}: "${k}" has no effect on a camera (use ${CAMERA_PROPS.join(", ")})`);
938
+ }
939
+ }
940
+ return;
941
+ }
942
+ for (const k of CAMERA_ONLY) {
943
+ if (keys.has(k)) warn(`pixi-effects: ${who}: "${k}" is a camera property and does nothing on this layer: write it in the initial / keyframes of a { type: 'camera' } layer`);
944
+ }
945
+ if (spec.type === "audio") {
946
+ if (spec.threeD) warn(`pixi-effects: ${who}: threeD has no effect on audio`);
947
+ return;
948
+ }
949
+ if (!spec.threeD) {
950
+ for (const k of NEEDS_THREE_D) {
951
+ if (keys.has(k)) warn(`pixi-effects: ${who}: "${k}" needs threeD: true (it is ignored otherwise)`);
952
+ }
953
+ } else {
954
+ for (const k of SKEW_KEYS) {
955
+ if (keys.has(k)) warn(`pixi-effects: ${who}: "${k}" is ignored on threeD layers`);
956
+ }
957
+ }
958
+ }
959
+ function lintFocus(camera, siblings, warn = defaultWarn) {
960
+ if (camera.type !== "camera") return;
961
+ const who = chunkECA2WNRX_cjs.describeLayer(camera);
962
+ for (const p of chunkDP37TTMO_cjs.focusProblems(camera, siblings)) {
963
+ if (p.kind === "missing") {
964
+ warn(`pixi-effects: ${who}: focus: no layer named "${p.name}"${p.hint ? `; did you mean "${p.hint}"?` : ""} (the z = 0 plane is used). Write a threeD layer's name or a z number`);
965
+ } else if (p.kind === "not-threeD") {
966
+ warn(`pixi-effects: ${who}: focus "${p.name}" is not a threeD layer, so it has no depth (the z = 0 plane is used). Write a threeD layer's name or a z number`);
967
+ } else if (p.kind === "duplicate") {
968
+ warn(`pixi-effects: ${who}: focus "${p.name}": ${p.count} layers are named "${p.name}"; the first threeD one is used. Give the layers different names`);
969
+ } else {
970
+ warn(`pixi-effects: ${who}: focus "${p.name}" reads only the first z of that layer; its z moves later and the focus does not follow. Animate focus itself with numbers to follow it`);
971
+ }
972
+ }
973
+ }
974
+
975
+ // src/core/matte.ts
976
+ var MATTE_CHANNELS = ["alpha", "luma"];
977
+ var isObject = (v) => !!v && typeof v === "object" && !Array.isArray(v);
978
+ function maskSourceOf(mask) {
979
+ if (isObject(mask) && typeof mask.type === "string") return { kind: "inline", spec: mask };
980
+ const problems = [];
981
+ const refs = [];
982
+ const one = (r, inList) => {
983
+ if (typeof r === "string") {
984
+ refs.push({ layer: r, channel: "alpha", invert: false });
985
+ return;
986
+ }
987
+ if (isObject(r) && typeof r.type === "string") {
988
+ if (inList) problems.push("a layer written inside a list of masks is ignored: give that layer a name and write the name");
989
+ return;
990
+ }
991
+ if (isObject(r) && typeof r.layer === "string") {
992
+ let channel = "alpha";
993
+ if (r.channel !== void 0) {
994
+ if (r.channel === "alpha" || r.channel === "luma") channel = r.channel;
995
+ else {
996
+ const guess = typeof r.channel === "string" ? chunkTWZNOUM7_cjs.suggestName(r.channel, MATTE_CHANNELS) : null;
997
+ const alias = typeof r.channel === "string" && ["luminance", "brightness", "grayscale", "greyscale", "gray", "grey", "value"].includes(r.channel.toLowerCase()) ? "luma" : null;
998
+ const meant = guess ?? alias;
999
+ problems.push(`mask "${r.layer}": channel ${JSON.stringify(r.channel)} is not a matte channel${meant ? `; did you mean "${meant}"?` : ""} (use alpha or luma); alpha is used`);
1000
+ }
1001
+ }
1002
+ let invert = false;
1003
+ if (r.invert !== void 0) {
1004
+ if (typeof r.invert === "boolean") invert = r.invert;
1005
+ else problems.push(`mask "${r.layer}": invert must be true or false (got ${JSON.stringify(r.invert)}); false is used`);
1006
+ }
1007
+ refs.push({ layer: r.layer, channel, invert });
1008
+ }
1009
+ };
1010
+ if (Array.isArray(mask)) for (const r of mask) one(r, true);
1011
+ else one(mask, false);
1012
+ return refs.length || problems.length ? { kind: "refs", refs, problems } : { kind: "none" };
1013
+ }
1014
+ function refNames(mask) {
1015
+ const s = maskSourceOf(mask);
1016
+ return s.kind === "refs" ? [...new Set(s.refs.map((r) => r.layer))] : [];
1017
+ }
1018
+ function usesMatteRoute(spec) {
1019
+ const s = maskSourceOf(spec.mask);
1020
+ if (s.kind !== "inline") return false;
1021
+ const t = s.spec.type;
1022
+ return spec.maskInverted === true || t === "text" || t === "image" || t === "video" || t === "shader";
1023
+ }
1024
+ var MANY_MATTES = 8;
1025
+ var startOf = (s) => Math.max(0, s.at ?? 0);
1026
+ var endOf = (s, span) => s.duration === void 0 ? span : (s.at ?? 0) + s.duration;
1027
+ var num = (n) => String(Number(n.toFixed(3)));
1028
+ function canBeMatte(spec) {
1029
+ return spec.type !== "camera" && spec.type !== "audio" && spec.type !== "null" && !spec.threeD;
1030
+ }
1031
+ function matteProblems(layer, siblings, span) {
1032
+ const src = maskSourceOf(layer.mask);
1033
+ if (src.kind !== "refs") return [];
1034
+ const out = [...src.problems];
1035
+ const names = siblings.filter((s) => s.name && s.type !== "camera").map((s) => s.name);
1036
+ if (layer.maskInverted && src.refs.length) {
1037
+ out.push('maskInverted is for a mask written in place: with a reference write { layer: "name", invert: true } (and use a list to subtract one matte from another)');
1038
+ }
1039
+ if (layer.parent && src.refs.length) {
1040
+ out.push(`it has a parent, so it is drawn in the null layer's space and the matte is in the composition's: mask the null layer ("${layer.parent}") instead`);
1041
+ }
1042
+ for (const ref of src.refs) {
1043
+ if (ref.layer === layer.name) {
1044
+ out.push(`mask "${ref.layer}": the layer masks itself; use another layer`);
1045
+ continue;
1046
+ }
1047
+ const same = siblings.filter((s) => s.name === ref.layer && s.type !== "camera");
1048
+ if (same.length === 0) {
1049
+ const guess = chunkTWZNOUM7_cjs.suggestName(ref.layer, names.filter((n) => n !== layer.name));
1050
+ out.push(`mask "${ref.layer}": no layer with that name${guess ? `; did you mean "${guess}"?` : ""} (a matte is another layer of the same composition; the layer is drawn without it)`);
1051
+ continue;
1052
+ }
1053
+ if (same.length > 1) out.push(`mask "${ref.layer}": ${same.length} layers are named "${ref.layer}"; the first is used`);
1054
+ const matte = same[0];
1055
+ if (!canBeMatte(matte)) {
1056
+ const what = matte.threeD ? "a threeD layer" : matte.type === "audio" ? "an audio layer (it draws nothing)" : "a null layer (it draws nothing)";
1057
+ out.push(`mask "${ref.layer}": "${ref.layer}" is ${what} and cannot be a matte (the layer is drawn without it)`);
1058
+ continue;
1059
+ }
1060
+ if (maskSourceOf(matte.mask).kind !== "none") out.push(`mask "${ref.layer}": "${ref.layer}" has a mask of its own, which is ignored when it is used as a matte`);
1061
+ const ms = startOf(matte), me = endOf(matte, span), ls = startOf(layer), le = endOf(layer, span);
1062
+ if (ms > ls + 1e-6 || me < le - 1e-6) {
1063
+ out.push(`mask "${ref.layer}": "${ref.layer}" is on screen from ${num(ms)}s to ${num(me)}s but the layer from ${num(ls)}s to ${num(le)}s: where the matte is missing the layer is invisible (give the matte the layer's at and duration, or a longer one)`);
1064
+ }
1065
+ }
1066
+ return out;
1067
+ }
1068
+ var VERT = `in vec2 aPosition;
1069
+ out vec2 vTextureCoord;
1070
+ out vec2 vMatteUv;
1071
+ uniform vec4 uInputSize;
1072
+ uniform vec4 uOutputFrame;
1073
+ uniform vec4 uOutputTexture;
1074
+ uniform mat3 uMatteMatrix;
1075
+ void main(void) {
1076
+ vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;
1077
+ position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;
1078
+ position.y = position.y * (2.0 * uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;
1079
+ gl_Position = vec4(position, 0.0, 1.0);
1080
+ vTextureCoord = aPosition * (uOutputFrame.zw * uInputSize.zw);
1081
+ vMatteUv = (uMatteMatrix * vec3(vTextureCoord, 1.0)).xy;
1082
+ }`;
1083
+ var FRAG = `in vec2 vTextureCoord;
1084
+ in vec2 vMatteUv;
1085
+ out vec4 finalColor;
1086
+ uniform sampler2D uTexture;
1087
+ uniform sampler2D uMatteTexture;
1088
+ uniform float uLuma;
1089
+ uniform float uInvert;
1090
+ void main(void) {
1091
+ vec4 src = texture(uTexture, vTextureCoord);
1092
+ vec4 m = texture(uMatteTexture, vMatteUv);
1093
+ // outside the matte texture there is no matte: the clamp-to-edge sampler would repeat the border
1094
+ float inside = step(0.0, vMatteUv.x) * step(0.0, vMatteUv.y) * step(vMatteUv.x, 1.0) * step(vMatteUv.y, 1.0);
1095
+ m *= inside;
1096
+ // luma of the premultiplied colour = luma(colour) * alpha: a transparent matte counts as black (Rec. 709 weights)
1097
+ float v = uLuma > 0.5 ? dot(m.rgb, vec3(0.2126, 0.7152, 0.0722)) : m.a;
1098
+ if (uInvert > 0.5) v = 1.0 - v;
1099
+ finalColor = src * v;
1100
+ }`;
1101
+ var WGSL2 = `
1102
+ struct GlobalFilterUniforms {
1103
+ uInputSize:vec4<f32>,
1104
+ uInputPixel:vec4<f32>,
1105
+ uInputClamp:vec4<f32>,
1106
+ uOutputFrame:vec4<f32>,
1107
+ uGlobalFrame:vec4<f32>,
1108
+ uOutputTexture:vec4<f32>,
1109
+ };
1110
+ struct MatteUniforms {
1111
+ uMatteMatrix:mat3x3<f32>,
1112
+ uLuma:f32,
1113
+ uInvert:f32,
1114
+ };
1115
+ @group(0) @binding(0) var<uniform> gfu: GlobalFilterUniforms;
1116
+ @group(0) @binding(1) var uTexture: texture_2d<f32>;
1117
+ @group(0) @binding(2) var uSampler : sampler;
1118
+ @group(1) @binding(0) var<uniform> matteUniforms : MatteUniforms;
1119
+ @group(1) @binding(1) var uMatteTexture: texture_2d<f32>;
1120
+ @group(1) @binding(2) var uMatteSampler : sampler;
1121
+
1122
+ struct VSOutput {
1123
+ @builtin(position) position: vec4<f32>,
1124
+ @location(0) uv : vec2<f32>,
1125
+ @location(1) matteUv : vec2<f32>,
1126
+ };
1127
+
1128
+ @vertex
1129
+ fn mainVertex(
1130
+ @location(0) aPosition : vec2<f32>,
1131
+ ) -> VSOutput {
1132
+ var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;
1133
+ position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;
1134
+ position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;
1135
+ let uv = aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);
1136
+ let matteUv = (matteUniforms.uMatteMatrix * vec3<f32>(uv, 1.0)).xy;
1137
+ return VSOutput(vec4<f32>(position, 0.0, 1.0), uv, matteUv);
1138
+ }
1139
+
1140
+ @fragment
1141
+ fn mainFragment(
1142
+ @location(0) uv: vec2<f32>,
1143
+ @location(1) matteUv: vec2<f32>,
1144
+ ) -> @location(0) vec4<f32> {
1145
+ let src = textureSample(uTexture, uSampler, uv);
1146
+ var m = textureSample(uMatteTexture, uMatteSampler, matteUv);
1147
+ let inside = step(0.0, matteUv.x) * step(0.0, matteUv.y) * step(matteUv.x, 1.0) * step(matteUv.y, 1.0);
1148
+ m = m * inside;
1149
+ var v = select(m.a, dot(m.rgb, vec3<f32>(0.2126, 0.7152, 0.0722)), matteUniforms.uLuma > 0.5);
1150
+ if (matteUniforms.uInvert > 0.5) { v = 1.0 - v; }
1151
+ return src * v;
1152
+ }`;
1153
+ var MatteFilter = class extends pixi_js.Filter {
1154
+ _sprite;
1155
+ constructor({ sprite, channel = "alpha", invert = false }) {
1156
+ const source = sprite.texture.source;
1157
+ super({
1158
+ glProgram: pixi_js.GlProgram.from({ vertex: VERT, fragment: FRAG, name: "pe-matte-filter" }),
1159
+ gpuProgram: pixi_js.GpuProgram.from({ vertex: { source: WGSL2, entryPoint: "mainVertex" }, fragment: { source: WGSL2, entryPoint: "mainFragment" } }),
1160
+ resources: {
1161
+ matteUniforms: new pixi_js.UniformGroup({
1162
+ uMatteMatrix: { value: new pixi_js.Matrix(), type: "mat3x3<f32>" },
1163
+ uLuma: { value: channel === "luma" ? 1 : 0, type: "f32" },
1164
+ uInvert: { value: invert ? 1 : 0, type: "f32" }
1165
+ }),
1166
+ uMatteTexture: source,
1167
+ uMatteSampler: source.style
1168
+ }
1169
+ });
1170
+ this._sprite = sprite;
1171
+ }
1172
+ apply(filterManager, input, output, clearMode) {
1173
+ const u = this.resources.matteUniforms.uniforms;
1174
+ filterManager.calculateSpriteMatrix(u.uMatteMatrix, this._sprite);
1175
+ this.resources.uMatteTexture = this._sprite.texture.source;
1176
+ filterManager.applyFilter(this, input, output, clearMode);
1177
+ }
1178
+ };
1179
+
1180
+ // src/core/MatteSet.ts
1181
+ var EMPTY = new pixi_js.Container();
1182
+ var MatteSet = class {
1183
+ constructor(inner, width, height) {
1184
+ this.inner = inner;
1185
+ this.width = width;
1186
+ this.height = height;
1187
+ }
1188
+ inner;
1189
+ width;
1190
+ height;
1191
+ mattes = /* @__PURE__ */ new Map();
1192
+ sources = /* @__PURE__ */ new Set();
1193
+ get size() {
1194
+ return this.mattes.size;
1195
+ }
1196
+ has(key) {
1197
+ return this.mattes.has(key);
1198
+ }
1199
+ /** Is this layer a matte's source (so it is not drawn on screen)? */
1200
+ isSource(seq) {
1201
+ return this.sources.has(seq);
1202
+ }
1203
+ /** `chain`: the null layers the source sits in, nearest first (the source is in no container, so their transforms are composed by hand). */
1204
+ add(key, seq, chain = []) {
1205
+ if (this.mattes.has(key)) return;
1206
+ const rt = pixi_js.RenderTexture.create({ width: this.width, height: this.height, resolution: 1, antialias: true });
1207
+ const sprite = new pixi_js.Sprite(rt);
1208
+ sprite.renderable = false;
1209
+ this.inner.addChild(sprite);
1210
+ this.mattes.set(key, { seq, chain, rt, sprite });
1211
+ this.sources.add(seq);
1212
+ }
1213
+ filterFor(key, opts) {
1214
+ const m = this.mattes.get(key);
1215
+ return m ? new MatteFilter({ sprite: m.sprite, channel: opts.channel, invert: opts.invert }) : null;
1216
+ }
1217
+ /** Draw every matte into its texture from the state the timeline has just set; a hidden matte is an empty texture (it cuts everything away). */
1218
+ render(host) {
1219
+ for (const { seq, chain, rt } of this.mattes.values()) {
1220
+ const target = seq.target;
1221
+ if (target && target.renderable && target.visible) {
1222
+ target.updateLocalTransform();
1223
+ const m = target.localTransform.clone();
1224
+ for (const p of chain) {
1225
+ p.target?.updateLocalTransform();
1226
+ if (p.target) m.prepend(p.target.localTransform);
1227
+ }
1228
+ host.render({ container: target, target: rt, transform: m, clear: true, clearColor: [0, 0, 0, 0] });
1229
+ } else {
1230
+ host.render({ container: EMPTY, target: rt, transform: new pixi_js.Matrix(), clear: true, clearColor: [0, 0, 0, 0] });
1231
+ }
1232
+ }
1233
+ }
1234
+ destroy() {
1235
+ for (const m of this.mattes.values()) {
1236
+ m.sprite.destroy();
1237
+ m.rt.destroy(true);
1238
+ }
1239
+ this.mattes.clear();
1240
+ this.sources.clear();
1241
+ }
1242
+ };
1243
+
1244
+ // src/sequences/Composition.ts
1245
+ var CompositionSequence = class extends chunkDP37TTMO_cjs.Sequence {
1246
+ _innerContainer = null;
1247
+ _compositionShape = null;
1248
+ _children = [];
1249
+ _cameras = [];
1250
+ _lights = [];
1251
+ /** The lights alive and the camera position of the last lit frame (what `inspect` reads: a layer's light level is computed from them on demand). */
1252
+ _lighting = null;
1253
+ _layers3d = [];
1254
+ _dofSaid = { behind: false, many: false };
1255
+ /** What this composition made for blend modes (filters added to layers' chains, groups and their carrier filters): destroyed with it. */
1256
+ _blendOwned = [];
1257
+ /** The mattes (layers drawn into textures that cut other layers), when any layer of this composition names or has one. */
1258
+ _mattes = null;
1259
+ _inlineMattes = 0;
1260
+ /** Drawn children in stack order (cameras and target-less sequences excluded). */
1261
+ _visual = [];
1262
+ /** Layers drawn inside a null layer (`parent`): they are not in the composition's own stack. */
1263
+ _nested = [];
1264
+ /** The null layer each parented layer is drawn inside. */
1265
+ _parentOf = /* @__PURE__ */ new Map();
1266
+ /** Set when this composition's own time is remapped: the children live on `_inner` in LOCAL time, driven by `_clock`. */
1267
+ _inner = null;
1268
+ _clock = null;
1269
+ _remap = null;
1270
+ _childBase = 0;
1271
+ _contentSpan = 0;
1272
+ /** Where the children's time starts: the composition's absolute start, or 0 when their time is local (remapped). */
1273
+ get childBase() {
1274
+ return this._childBase;
1275
+ }
1276
+ /** How long the children's time runs: the composition's duration, or its content length when its time is remapped. */
1277
+ get contentSpan() {
1278
+ return this._contentSpan;
1279
+ }
1280
+ /** The time the children live in at movie time `t`: the movie's own, or this composition's local clock when it is remapped. */
1281
+ childTime(t) {
1282
+ return this._clock ? this._clock.time : t;
1283
+ }
1284
+ /** The clock reader for filters that want the time (grain), or null when the children run on the movie's time. */
1285
+ localClock() {
1286
+ return this._clock ? () => this._clock.time : null;
1287
+ }
1288
+ /** How the time is remapped, for the timeline chart: `×0.5`, `×-1`, `time keyframes`, or null. */
1289
+ remapLabel() {
1290
+ const r = this._remap;
1291
+ if (!r) return null;
1292
+ return r.timeKfs.length ? "time keyframes" : `\xD7${r.speed ?? 1}`;
1293
+ }
1294
+ async build() {
1295
+ const width = this.spec.width ?? this.parent?.width ?? this.root.width;
1296
+ const height = this.spec.height ?? this.parent?.height ?? this.root.height;
1297
+ if (this.duration === void 0) {
1298
+ this.duration = this.spec.duration ?? this.parent?.duration ?? this.root.duration;
1299
+ }
1300
+ const wrapper = new pixi_js.Container();
1301
+ wrapper.cullable = true;
1302
+ wrapper.cullableChildren = true;
1303
+ wrapper.cullArea = new pixi_js.Rectangle(0, 0, width, height);
1304
+ const inner = new pixi_js.Container();
1305
+ inner.cullable = true;
1306
+ inner.cullableChildren = true;
1307
+ wrapper.addChild(inner);
1308
+ this.target = wrapper;
1309
+ this.intrinsicWidth = width;
1310
+ this.intrinsicHeight = height;
1311
+ this._innerContainer = inner;
1312
+ const base = chunkDP37TTMO_cjs.timeRemapOf(this.spec);
1313
+ const span = base ? chunkDP37TTMO_cjs.contentLength(base, this.duration) : this.duration;
1314
+ this._remap = base ? chunkDP37TTMO_cjs.remapOf(this.spec, "time", span) : null;
1315
+ this._contentSpan = span;
1316
+ this._compositionShape = { width, height, duration: span };
1317
+ const lateKeyframes = [];
1318
+ for (const s of this.spec.sequences ?? []) {
1319
+ lintSequence(s);
1320
+ lintFocus(s, this.spec.sequences ?? []);
1321
+ for (const message of matteProblems(s, this.spec.sequences ?? [], span)) console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(s)}: ${message}`);
1322
+ chunkECA2WNRX_cjs.lintTiming(s, span, (message, kind) => kind === "late-keyframe" ? lateKeyframes.push(message) : console.warn(message));
1323
+ chunkECA2WNRX_cjs.lintText(s);
1324
+ chunkECA2WNRX_cjs.lintKeys(s);
1325
+ }
1326
+ for (const message of chunkECA2WNRX_cjs.summarizeWarnings(lateKeyframes)) console.warn(message);
1327
+ const crowd = chunk3DLYAPK7_cjs.maxConcurrentAdvanced(this.spec.sequences ?? [], span);
1328
+ if (crowd >= chunk3DLYAPK7_cjs.MANY_ADVANCED) {
1329
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: ${crowd} layers with an advanced blendMode are on screen at once (a composition with one counts each layer inside it, since its children blend one by one); each is a full-frame pass (about 2 ms on WebGL). Put fewer layers in the blend (or draw the glow as one layer), or use add / screen / multiply`);
1330
+ }
1331
+ this._children = await chunkDP37TTMO_cjs.buildSequenceTree(
1332
+ this.spec.sequences ?? [],
1333
+ this._compositionShape,
1334
+ this.root
1335
+ );
1336
+ this._resolveParents();
1337
+ const matteNames = /* @__PURE__ */ new Set();
1338
+ for (const c of this._children) for (const n of refNames(c.spec.mask)) matteNames.add(n);
1339
+ if (matteNames.size > 0) {
1340
+ this._mattes = new MatteSet(inner, width, height);
1341
+ for (const name of matteNames) {
1342
+ const source = this._children.find((c) => c.spec.name === name && !(c instanceof chunkDP37TTMO_cjs.CameraSequence));
1343
+ if (!source || !source.target || !canBeMatte(source.spec)) continue;
1344
+ const chain = [];
1345
+ for (let p = this._parentOf.get(source); p; p = this._parentOf.get(p)) chain.push(p);
1346
+ this._mattes.add(name, source, chain);
1347
+ }
1348
+ }
1349
+ if (this._children.some((c) => c instanceof chunkDP37TTMO_cjs.LightSequence)) {
1350
+ for (const message of lightSetProblems(this.spec.sequences ?? [])) console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: ${message}`);
1351
+ }
1352
+ const layerNames = new Set(this._children.map((c) => c.spec.name).filter((n) => !!n));
1353
+ for (const cam of this._children) {
1354
+ if (!(cam instanceof chunkDP37TTMO_cjs.CameraSequence)) continue;
1355
+ cam.resolveFocusNames((name) => {
1356
+ const hit = this._children.find((c) => c.spec.name === name && c.spec.threeD);
1357
+ return hit ? chunkDP37TTMO_cjs.layerInitialZ(hit.spec, hit.scope()) : void 0;
1358
+ }, layerNames);
1359
+ }
1360
+ const blendSaid = /* @__PURE__ */ new Map();
1361
+ const report = (message, spec) => {
1362
+ const seen = blendSaid.get(message);
1363
+ if (seen) seen.n++;
1364
+ else blendSaid.set(message, { spec, n: 1 });
1365
+ };
1366
+ for (const child of this._children) {
1367
+ if (child instanceof chunkDP37TTMO_cjs.CameraSequence) {
1368
+ this._cameras.push(child);
1369
+ continue;
1370
+ }
1371
+ if (child instanceof chunkDP37TTMO_cjs.LightSequence) {
1372
+ this._lights.push(child);
1373
+ continue;
1374
+ }
1375
+ if (!child.target) continue;
1376
+ if (this._mattes?.isSource(child)) continue;
1377
+ const holder = this._parentOf.get(child);
1378
+ const into = holder?.target ?? inner;
1379
+ const maskSrc = maskSourceOf(child.spec.mask);
1380
+ if (child.spec.threeD) {
1381
+ const layer = new Layer3D(child, frameOf(child));
1382
+ inner.addChild(layer.display);
1383
+ this._layers3d.push(layer);
1384
+ this._visual.push({ seq: child, layer });
1385
+ const added3d = chunk3DLYAPK7_cjs.applyBlendMode(child.spec, layer.display, report);
1386
+ if (added3d) this._blendOwned.push(added3d);
1387
+ if (maskSrc.kind !== "none") {
1388
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(child.spec)}: mask is not supported on threeD layers yet; ignored`);
1389
+ }
1390
+ continue;
1391
+ }
1392
+ into.addChild(child.target);
1393
+ if (holder) this._nested.push(child);
1394
+ else this._visual.push({ seq: child, layer: null });
1395
+ const matteFilters = [];
1396
+ if (maskSrc.kind === "refs" && this._mattes) {
1397
+ for (const ref of maskSrc.refs) {
1398
+ const f = this._mattes.filterFor(ref.layer, { channel: ref.channel, invert: ref.invert });
1399
+ if (f) matteFilters.push(f);
1400
+ }
1401
+ }
1402
+ if (maskSrc.kind === "inline") {
1403
+ const maskSpec = maskSrc.spec;
1404
+ const lifetime = maskSpec.at === void 0 ? { at: child.at, duration: maskSpec.duration ?? child.duration } : {};
1405
+ const built = await chunkDP37TTMO_cjs.buildSequenceTree([{ ...maskSpec, ...lifetime }], this._compositionShape, this.root);
1406
+ const maskSeq = built[0];
1407
+ if (maskSeq?.target) {
1408
+ child.maskSequence = maskSeq;
1409
+ if (usesMatteRoute(child.spec)) {
1410
+ if (!this._mattes) this._mattes = new MatteSet(inner, width, height);
1411
+ const key = `\0inline-${this._inlineMattes++}`;
1412
+ const chain = [];
1413
+ for (let p = this._parentOf.get(child); p; p = this._parentOf.get(p)) chain.push(p);
1414
+ this._mattes.add(key, maskSeq, chain);
1415
+ const f = this._mattes.filterFor(key, { channel: "alpha", invert: child.spec.maskInverted === true });
1416
+ if (f) matteFilters.push(f);
1417
+ } else {
1418
+ into.addChild(maskSeq.target);
1419
+ const t = child.target;
1420
+ if (typeof t.setMask === "function") t.setMask({ mask: maskSeq.target, inverse: false });
1421
+ else t.mask = maskSeq.target;
1422
+ }
1423
+ }
1424
+ }
1425
+ if (matteFilters.length > 0) {
1426
+ const t = child.target;
1427
+ t.filters = [...Array.isArray(t.filters) ? t.filters : [], ...matteFilters];
1428
+ this._blendOwned.push(...matteFilters);
1429
+ }
1430
+ const added = chunk3DLYAPK7_cjs.applyBlendMode(child.spec, child.target, report);
1431
+ if (added) this._blendOwned.push(added);
1432
+ }
1433
+ if (this._mattes && this._mattes.size > MANY_MATTES) {
1434
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: ${this._mattes.size} mattes: each is a ${width}\xD7${height} texture (about ${Math.round(width * height * 4 / 1048576)} MiB, more with anti-aliasing). Share one matte between layers, or use fewer`);
1435
+ }
1436
+ for (const [message, seen] of blendSaid) {
1437
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(seen.spec)}: ${message}${seen.n > 1 ? ` (and ${seen.n - 1} more layers have the same mistake)` : ""}`);
1438
+ }
1439
+ if (this._layers3d.length > 0) {
1440
+ inner.sortableChildren = true;
1441
+ this._visual.forEach((v, i) => {
1442
+ (v.layer?.display ?? v.seq.target).zIndex = i;
1443
+ });
1444
+ } else if (this._cameras.length > 0) {
1445
+ console.warn("pixi-effects: camera has no effect: this composition has no threeD layers");
1446
+ }
1447
+ if (this._layers3d.length === 0 && this._lights.length > 0) {
1448
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: a light has no effect: this composition has no threeD layers (a light lights only the threeD layers of its own composition, not those of the compositions nested in it: put the light inside the composition that holds them)`);
1449
+ }
1450
+ this.buildFilters();
1451
+ }
1452
+ displayProps() {
1453
+ const r = this._remap;
1454
+ return r ? { initial: r.restInitial, keyframes: r.restKfs } : super.displayProps();
1455
+ }
1456
+ bindTimeline(timeline, offset = 0) {
1457
+ super.bindTimeline(timeline, offset);
1458
+ const remap = this._remap;
1459
+ const childOffset = remap ? 0 : offset + this.at;
1460
+ this._childBase = childOffset;
1461
+ const target = remap ? gsap.gsap.timeline({ paused: true, defaults: { ease: "none" } }) : timeline;
1462
+ for (const child of this._children) {
1463
+ const own = gsap.gsap.timeline({ defaults: { ease: "none" } });
1464
+ child.bindTimeline(own, childOffset);
1465
+ child.maskSequence?.bindTimeline(own, childOffset);
1466
+ target.add(own, 0);
1467
+ }
1468
+ if (remap) this._bindClock(timeline, target, remap, offset);
1469
+ const overlap = findOverlap(this._cameras.map((c) => c.window()));
1470
+ if (overlap) {
1471
+ console.warn(`pixi-effects: cameras #${overlap[0] + 1} and #${overlap[1] + 1} overlap in time; the top-most one wins`);
1472
+ }
1473
+ }
1474
+ /** Drive the local timeline `inner` from the clock, and put the clock's tweens on the outer `timeline`. */
1475
+ _bindClock(timeline, inner, remap, offset) {
1476
+ let last = NaN;
1477
+ let cur = 0;
1478
+ const clock = {
1479
+ get time() {
1480
+ return cur;
1481
+ },
1482
+ set time(v) {
1483
+ cur = v;
1484
+ if (v === last) return;
1485
+ last = v;
1486
+ inner.time(v);
1487
+ }
1488
+ };
1489
+ this._inner = inner;
1490
+ this._clock = clock;
1491
+ const scope = this.scope();
1492
+ if (remap.start !== void 0 && remap.timeKfs.length === 0) clock.time = remap.start;
1493
+ chunkDP37TTMO_cjs.bindClock(timeline, clock, "time", remap, this.duration, offset + this.at, scope);
1494
+ inner.time(inner.duration());
1495
+ inner.time(0);
1496
+ last = NaN;
1497
+ this._warnUnreachable(chunkDP37TTMO_cjs.clockTable(remap, "time", this.duration, offset + this.at, scope, offset + this.at + this.duration), offset + this.at, offset + this.at + this.duration);
1498
+ }
1499
+ /**
1500
+ * A child the clock never dwells in is never visible: say so. Its `at` is in THIS composition's local time, so the usual mistake is
1501
+ * writing it in the outer time (or forgetting that speed 0.5 reaches only half as far). Decided by dwell, not by the clock's end
1502
+ * points: a clock that arrives at the child's `at` and HOLDS there shows it for the whole hold; one that only touches it in a single
1503
+ * instant (speed 0.5, a child at exactly the farthest local time) does not.
1504
+ */
1505
+ _warnUnreachable(table, from, to) {
1506
+ const remap = this._remap;
1507
+ const how = remap.timeKfs.length ? "time keyframes" : `speed ${remap.speed ?? 1}`;
1508
+ const span = this._contentSpan;
1509
+ const EPS = 1e-9;
1510
+ for (const child of this._children) {
1511
+ const at = child.at;
1512
+ if (at >= span) continue;
1513
+ const end = Math.min(at + (child.duration ?? span), span);
1514
+ let dwell = 0;
1515
+ for (let t = from; t <= to && dwell < 2; t += 1e-3) {
1516
+ const v = table.at(t);
1517
+ if (v >= at - EPS && v < end + EPS) dwell++;
1518
+ }
1519
+ if (dwell >= 2) continue;
1520
+ console.warn(
1521
+ `pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(child.spec)} starts at ${at}s of ${chunkECA2WNRX_cjs.describeLayer(this.spec)}'s own time, but its clock only reaches ${Number(table.min.toFixed(3))}\u2013${Number(table.max.toFixed(3))}s (${how}), so it is never visible. Layers inside a remapped composition are placed in that composition's local time.`
1522
+ );
1523
+ }
1524
+ }
1525
+ /**
1526
+ * Drawn children in stack order, with the display object that stands in for each (the mesh for a threeD layer).
1527
+ * Null layers draw nothing and are left out; the layers inside them follow, with the null layers that carry them.
1528
+ */
1529
+ layers() {
1530
+ const carriersOf = (seq) => {
1531
+ const out = [];
1532
+ for (let p = this._parentOf.get(seq); p; p = this._parentOf.get(p)) out.push(p);
1533
+ return out;
1534
+ };
1535
+ return [...this._visual.map((v) => v.seq), ...this._nested].filter((seq) => !(seq instanceof chunkDP37TTMO_cjs.NullSequence)).map((seq) => {
1536
+ const layer = this._visual.find((v) => v.seq === seq)?.layer ?? null;
1537
+ return {
1538
+ seq,
1539
+ display: layer?.display ?? seq.target,
1540
+ threeD: layer !== null,
1541
+ carriers: carriersOf(seq),
1542
+ depthBlur: layer?.blur ?? 0,
1543
+ // how lit it is (threeD layers that receive the lights of the last frame only); computed here, not per frame
1544
+ light: layer && this._lighting && layer.world && layer.spec.lit !== false ? chunkDP37TTMO_cjs.lightLevel(layer.world, this._lighting.cam, this._lighting.states) : void 0
1545
+ };
1546
+ });
1547
+ }
1548
+ /** Match every `parent: 'name'` to a null layer; anything that cannot work is said out loud and drawn unparented. */
1549
+ _resolveParents() {
1550
+ const nulls = this._children.filter((c) => c instanceof chunkDP37TTMO_cjs.NullSequence);
1551
+ const nullNames = nulls.map((n) => n.spec.name).filter((n) => !!n);
1552
+ const warned = /* @__PURE__ */ new Set();
1553
+ for (const child of this._children) {
1554
+ const want = child.spec.parent;
1555
+ if (want === void 0) continue;
1556
+ const who = chunkECA2WNRX_cjs.describeLayer(child.spec);
1557
+ if (child instanceof chunkDP37TTMO_cjs.CameraSequence) {
1558
+ console.warn(`pixi-effects: ${who}: a camera cannot have a parent; ignored`);
1559
+ continue;
1560
+ }
1561
+ if (child instanceof chunkDP37TTMO_cjs.LightSequence) {
1562
+ console.warn(`pixi-effects: ${who}: a light cannot have a parent (parent "${want}" ignored: move the light with its own x / y / z keyframes)`);
1563
+ continue;
1564
+ }
1565
+ if (child.spec.threeD) {
1566
+ console.warn(`pixi-effects: ${who}: a threeD layer cannot have a parent (parent "${want}" ignored)`);
1567
+ continue;
1568
+ }
1569
+ const same = this._children.filter((c) => c.spec.name === want);
1570
+ if (same.length === 0) {
1571
+ const hint = chunkTWZNOUM7_cjs.suggestName(want, nullNames);
1572
+ console.warn(`pixi-effects: ${who}: parent "${want}": no layer with that name${hint ? `; did you mean "${hint}"?` : ""} (the layer is drawn without a parent)`);
1573
+ continue;
1574
+ }
1575
+ const holder = same.find((c) => c instanceof chunkDP37TTMO_cjs.NullSequence);
1576
+ if (!holder) {
1577
+ console.warn(`pixi-effects: ${who}: parent "${want}" is a ${same[0].spec.type} layer; only { type: 'null' } layers can be parents (the layer is drawn without a parent)`);
1578
+ continue;
1579
+ }
1580
+ if (same.length > 1 && !warned.has(want)) {
1581
+ warned.add(want);
1582
+ console.warn(`pixi-effects: ${same.length} layers are named "${want}"; parent "${want}" uses the first null layer with that name`);
1583
+ }
1584
+ if (holder === child) {
1585
+ console.warn(`pixi-effects: ${who} is its own parent; ignored`);
1586
+ continue;
1587
+ }
1588
+ this._parentOf.set(child, holder);
1589
+ }
1590
+ for (const start of this._children) {
1591
+ const seen = /* @__PURE__ */ new Set([start]);
1592
+ for (let p = this._parentOf.get(start); p; p = this._parentOf.get(p)) {
1593
+ if (p === start) {
1594
+ const names = [...seen].map((s) => s.spec.name ?? s.spec.type).join(" \u2192 ");
1595
+ console.warn(`pixi-effects: parent cycle: ${names} \u2192 ${start.spec.name ?? start.spec.type}; the parent of "${start.spec.name ?? start.spec.type}" is ignored`);
1596
+ this._parentOf.delete(start);
1597
+ break;
1598
+ }
1599
+ if (seen.has(p)) break;
1600
+ seen.add(p);
1601
+ }
1602
+ }
1603
+ }
1604
+ syncFrame() {
1605
+ super.syncFrame();
1606
+ for (const child of this._children) child.syncFrame();
1607
+ }
1608
+ updateSpace(t, host) {
1609
+ const childT = this.childTime(t);
1610
+ for (const child of this._children) child.updateSpace(childT, host);
1611
+ this._mattes?.render(host);
1612
+ if (this._layers3d.length === 0 || !this._compositionShape) return;
1613
+ const { width, height } = this._compositionShape;
1614
+ const compEnd = this._childBase + (this.duration ?? 0);
1615
+ const active = pickActiveCamera(
1616
+ this._cameras.map((cam2) => ({ cam: cam2, ...cam2.window() })),
1617
+ childT,
1618
+ compEnd
1619
+ );
1620
+ const cam = active ? active.cam.state() : chunkDP37TTMO_cjs.homeCamera(width, height);
1621
+ const basis = chunkDP37TTMO_cjs.cameraBasis(cam, width, height);
1622
+ for (const layer of this._layers3d) layer.update(host, basis);
1623
+ this._applyLighting(childT, compEnd, basis, active?.cam ?? null);
1624
+ this._applyDepthOfField(cam, basis);
1625
+ const order = assignDepthOrder(
1626
+ this._visual.map((v, i) => ({ stackIndex: i, threeD: v.layer !== null, depth: v.layer?.depth ?? 0 }))
1627
+ );
1628
+ this._visual.forEach((v, i) => {
1629
+ if (v.layer) v.layer.display.zIndex = order[i];
1630
+ });
1631
+ }
1632
+ /**
1633
+ * Lights and fog: when the composition has a light layer or the camera writes fog, every threeD layer draws with the lit shader, fed with
1634
+ * the lights alive NOW (a pure function of the lights, the camera and the layers; nothing is remembered between frames). With neither,
1635
+ * nothing is made and nothing is drawn differently. With no light alive at this moment (all outside their `at` / `duration`) a layer is
1636
+ * drawn unlit, as it is in a composition with no light.
1637
+ */
1638
+ _applyLighting(t, compEnd, basis, cam) {
1639
+ const fog = cam?.fogState() ?? null;
1640
+ if (this._lights.length === 0 && !this._cameras.some((c) => c.fog)) return;
1641
+ const live = this._lights.filter((l) => {
1642
+ const w = l.window();
1643
+ return t >= w.start && (t < w.end || w.end >= compEnd && t <= w.end);
1644
+ });
1645
+ const states = live.map((l) => l.state());
1646
+ const packed = states.length > 0 ? chunkDP37TTMO_cjs.packLights(states) : null;
1647
+ this._lighting = states.length > 0 ? { states, cam: { x: basis.cx, y: basis.cy, z: basis.cz } } : null;
1648
+ const shadowing = states.some((s) => s.castsShadows);
1649
+ const casters = shadowing ? this._layers3d.filter((l) => l.spec.castsShadows && l.display.visible && l.world && l.source) : [];
1650
+ const camInfo = { x: basis.cx, y: basis.cy, z: basis.cz, fx: basis.fx, fy: basis.fy, fz: basis.fz };
1651
+ for (const l of this._layers3d) {
1652
+ const mat = l.setLit(true);
1653
+ if (!mat || !l.world) continue;
1654
+ const lit = l.spec.lit !== false;
1655
+ const mine = lit ? casters.filter((c) => c !== l).slice(0, chunkDP37TTMO_cjs.MAX_CASTERS).map((c) => ({ frame: c.world, source: c.source })) : [];
1656
+ mat.update(l.world, camInfo, lit ? packed : null, fog, mine);
1657
+ }
1658
+ }
1659
+ /** Depth of field: each threeD layer is blurred by how far its depth is from the focal plane (a pure function of the camera and the layers). */
1660
+ _applyDepthOfField(cam, basis) {
1661
+ const aperture = cam.aperture ?? 0;
1662
+ if (!(aperture > 0)) {
1663
+ for (const l of this._layers3d) l.setBlur(0);
1664
+ return;
1665
+ }
1666
+ const focusDepth = chunkDP37TTMO_cjs.projectPoint(basis, { x: cam.lookAtX, y: cam.lookAtY, z: cam.focus ?? 0 }).depth;
1667
+ if (!(focusDepth > chunkDP37TTMO_cjs.NEAR)) {
1668
+ if (!this._dofSaid.behind) {
1669
+ this._dofSaid.behind = true;
1670
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: the camera's focus (z = ${(cam.focus ?? 0).toFixed(0)}) is at or behind the camera, so nothing is blurred. Put focus in front of the camera (a z below the camera's z)`);
1671
+ }
1672
+ for (const l of this._layers3d) l.setBlur(0);
1673
+ return;
1674
+ }
1675
+ let blurred = 0;
1676
+ for (const l of this._layers3d) {
1677
+ l.setBlur(l.display.visible ? chunkDP37TTMO_cjs.blurRadius(aperture, basis.focal, l.depth, focusDepth) : 0);
1678
+ if (l.blur > 0) blurred++;
1679
+ }
1680
+ if (blurred >= chunkDP37TTMO_cjs.MANY_BLURRED && !this._dofSaid.many) {
1681
+ this._dofSaid.many = true;
1682
+ console.warn(`pixi-effects: ${chunkECA2WNRX_cjs.describeLayer(this.spec)}: ${blurred} layers are blurred by depth of field at once; each costs a filter pass per frame (WebGPU more). Merge the far layers or lower aperture`);
1683
+ }
1684
+ }
1685
+ collectAudio(out, baseTime) {
1686
+ super.collectAudio(out, baseTime);
1687
+ const remap = this._remap;
1688
+ if (!remap) {
1689
+ const childBase = baseTime + this.at;
1690
+ for (const child of this._children) child.collectAudio(out, childBase);
1691
+ return;
1692
+ }
1693
+ const inner = [];
1694
+ for (const child of this._children) child.collectAudio(inner, 0);
1695
+ const lo = baseTime + this.at, hi = lo + this.duration;
1696
+ const table = chunkDP37TTMO_cjs.clockTable(remap, "time", this.duration, lo, this.scope(), hi);
1697
+ for (const d of inner) {
1698
+ const prev = d.warp;
1699
+ out.push({ ...d, warp: (t) => t < lo || t > hi ? NaN : prev ? prev(table.at(t)) : table.at(t) });
1700
+ }
1701
+ }
1702
+ destroy() {
1703
+ this._inner?.kill();
1704
+ this._inner = null;
1705
+ this._clock = null;
1706
+ for (const l of this._layers3d) l.setLit(false);
1707
+ for (const l of this._layers3d) l.destroy();
1708
+ this._layers3d = [];
1709
+ this._mattes?.destroy();
1710
+ this._mattes = null;
1711
+ for (const c of this._children) c.destroy();
1712
+ for (const o of this._blendOwned) o.destroy();
1713
+ this._blendOwned = [];
1714
+ super.destroy();
1715
+ }
1716
+ };
1717
+ function frameOf(child) {
1718
+ if (child instanceof CompositionSequence) {
1719
+ return () => ({ x: 0, y: 0, width: child.intrinsicWidth, height: child.intrinsicHeight });
1720
+ }
1721
+ return () => child.target.getLocalBounds();
1722
+ }
1723
+
1724
+ exports.CompositionSequence = CompositionSequence;
1725
+ exports.maskSourceOf = maskSourceOf;
1726
+ //# sourceMappingURL=chunk-SY27HZC5.cjs.map
1727
+ //# sourceMappingURL=chunk-SY27HZC5.cjs.map