pixi-effects 0.42.0 → 0.44.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 (40) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +5 -5
  3. package/ai/chat-template.html +3 -3
  4. package/ai/tools/check.mjs +131 -2
  5. package/dist/Controller.d.cts +2 -2
  6. package/dist/Controller.d.ts +2 -2
  7. package/dist/{Movie-BHGhg-fJ.d.cts → Movie-B3VOAgwc.d.cts} +5 -3
  8. package/dist/{Movie-CBVFZ1kg.d.ts → Movie-CQCVUqRs.d.ts} +5 -3
  9. package/dist/{chunk-NFJKBWYO.js → chunk-4MBTYREC.js} +100 -31
  10. package/dist/chunk-4MBTYREC.js.map +1 -0
  11. package/dist/{chunk-WVZLAIKK.cjs → chunk-MAAM45ZX.cjs} +99 -30
  12. package/dist/chunk-MAAM45ZX.cjs.map +1 -0
  13. package/dist/index.cjs +25 -25
  14. package/dist/index.d.cts +3 -4
  15. package/dist/index.d.ts +3 -4
  16. package/dist/index.js +1 -1
  17. package/dist/loader.css +1 -1
  18. package/dist/present.cjs +1 -1
  19. package/dist/present.d.cts +2 -2
  20. package/dist/present.d.ts +2 -2
  21. package/dist/present.js +1 -1
  22. package/dist/three.cjs +166 -38
  23. package/dist/three.cjs.map +1 -1
  24. package/dist/three.d.cts +37 -2
  25. package/dist/three.d.ts +37 -2
  26. package/dist/three.js +168 -40
  27. package/dist/three.js.map +1 -1
  28. package/dist/{types-DIh93lFb.d.cts → types-C5xo0fzb.d.cts} +92 -1
  29. package/dist/{types-DIh93lFb.d.ts → types-C5xo0fzb.d.ts} +92 -1
  30. package/docs/api.md +4 -2
  31. package/docs/dsl.md +4 -2
  32. package/llms-full.txt +12 -8
  33. package/package.json +2 -1
  34. package/skills/pixi-effects/SKILL.md +2 -2
  35. package/skills/pixi-effects/reference/recipes.md +2 -2
  36. package/skills/pixi-effects/template.html +6 -6
  37. package/dist/Base-DQWIJ6bJ.d.cts +0 -60
  38. package/dist/Base-Jgkud1Aw.d.ts +0 -60
  39. package/dist/chunk-NFJKBWYO.js.map +0 -1
  40. package/dist/chunk-WVZLAIKK.cjs.map +0 -1
package/dist/three.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-4QVQ5UJJ.js';
2
2
  import './chunk-32MMNDDN.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
- import { Texture, Sprite } from 'pixi.js';
5
- import { WebGLRenderer, Scene, PerspectiveCamera, Raycaster, Vector2, Vector3, Quaternion, Plane, Box3, Group, AnimationMixer, LoopOnce, LoopRepeat, AmbientLight, DirectionalLight, SpotLight, PointLight, Color, SRGBColorSpace, Fog } from 'three';
4
+ import { Texture, Sprite, TextureSource } from 'pixi.js';
5
+ import { WebGLRenderer, Scene, PerspectiveCamera, Raycaster, Vector2, Vector3, Quaternion, Plane, Box3, RenderTarget, Group, AnimationMixer, LoopOnce, LoopRepeat, AmbientLight, DirectionalLight, SpotLight, PointLight, Color, SRGBColorSpace, Fog } from 'three';
6
6
 
7
7
  // src/three/defaults.ts
8
8
  var pageRenderer = "webgl";
@@ -159,7 +159,7 @@ function trackThree(kind) {
159
159
  live[kind]++;
160
160
  if (live[kind] > WARN_ABOVE[kind] && !warned.has(kind)) {
161
161
  warned.add(kind);
162
- console.warn(`pixi-effects: ${live[kind]} three layers are alive with renderer: '${kind}', and a browser keeps ${LIMIT[kind]} for the whole page; layers beyond that draw nothing, without an error. Put several objects in one three layer, destroy layers that are over, ` + (kind === "webgl" ? `or use renderer: 'webgpu' for some (its limit is about 32)` : `or use renderer: 'webgl' for some of them (a different pool of contexts)`));
162
+ console.warn(`pixi-effects: ${live[kind]} three layers are alive with renderer: '${kind}', and a browser keeps ${LIMIT[kind]} for the whole page; layers beyond that draw nothing, without an error. Put several objects in one three layer, destroy layers that are over, ` + (kind === "webgl" ? `or use renderer: 'webgpu' for some (in a movie PixiJS draws on WebGPU they share its GPU device and have no limit; otherwise it is about 32)` : `or use renderer: 'webgl' for some of them (a different pool of contexts), or leave shareGpu on in a movie PixiJS draws on WebGPU (the layers then share its device)`));
163
163
  }
164
164
  }
165
165
  function releaseThree(kind) {
@@ -176,6 +176,19 @@ var ThreeSequence = class extends Sequence {
176
176
  backend = null;
177
177
  _gpu = false;
178
178
  // renderer: 'webgpu': async init and async render
179
+ /** True when the layer draws on PixiJS's own GPU device into a texture PixiJS samples directly (no canvas of its own, no copy, no share of the page's context cap). */
180
+ sharedGpu = false;
181
+ _rt = null;
182
+ // the texture a shared layer draws into
183
+ _sharedTexture = null;
184
+ _texture = null;
185
+ // what the layer's sprite shows (the canvas's, or the shared render target's)
186
+ /** What a rebuilt layer is compared with to take this one's place (core/reuse.ts); null: this layer is never kept (`reuse: false`, no `name`). */
187
+ _key = null;
188
+ /** The layer of the tree being replaced whose state this one is using until the new tree is on stage (`commitReuse`): not this layer's to let go. */
189
+ _borrowed = null;
190
+ /** True once another layer owns this layer's state: destroying this one must not let it go. */
191
+ _donated = false;
179
192
  _scene = null;
180
193
  _camera = null;
181
194
  _ctx = null;
@@ -207,12 +220,18 @@ var ThreeSequence = class extends Sequence {
207
220
  const width = resolveDim(spec.width, scope) ?? this.parent?.width ?? this.root.width;
208
221
  const height = resolveDim(spec.height, scope) ?? this.parent?.height ?? this.root.height;
209
222
  const resolution = spec.resolution ?? 1;
210
- const canvas = document.createElement("canvas");
211
223
  const antialias = spec.antialias ?? true;
212
224
  const kind = spec.renderer ?? getPageRenderer();
213
225
  checkRendererKind(kind, "a three layer's renderer");
214
226
  this._gpu = kind === "webgpu";
215
227
  this.renderer = kind;
228
+ this._key = spec.reuse === false || !spec.name ? null : JSON.stringify([spec.name, idOf(spec.setup), width, height, resolution, antialias, kind, spec.shareGpu !== false && !!this.root.gpu, !!spec.followCamera, !!spec.followLights, !!spec.followFog]);
229
+ const donor = this._key !== null ? this.root.reuse?.claim(this._key) : null;
230
+ if (donor && donor._lendable()) {
231
+ this._borrow(donor, width, height, spec.name);
232
+ return;
233
+ }
234
+ const canvas = document.createElement("canvas");
216
235
  let renderer;
217
236
  if (this._gpu) {
218
237
  let mod;
@@ -221,24 +240,36 @@ var ThreeSequence = class extends Sequence {
221
240
  } catch (err) {
222
241
  throw new Error(`pixi-effects: renderer: 'webgpu' needs three 0.17x or later with "three/webgpu" in the import map (${err instanceof Error ? err.message : err})`);
223
242
  }
224
- renderer = new mod.WebGPURenderer({ canvas, alpha: true, antialias });
225
- this._renderer = renderer;
226
- this._track();
227
- const warn = console.warn;
228
- let noGpu = false;
229
- console.warn = (...args) => {
230
- if (/WebGPU is not available/.test(String(args[0]))) noGpu = true;
231
- else warn.apply(console, args);
232
- };
233
- try {
234
- await renderer.init();
235
- } catch (err) {
236
- this._disposeRenderer();
237
- throw err;
238
- } finally {
239
- console.warn = warn;
243
+ let shared = spec.shareGpu === false ? void 0 : this.root.gpu;
244
+ for (; ; ) {
245
+ renderer = new mod.WebGPURenderer(shared ? { device: shared.device, alpha: true, antialias } : { canvas, alpha: true, antialias });
246
+ this._renderer = renderer;
247
+ if (!shared) this._track();
248
+ const warn = console.warn;
249
+ let noGpu = false;
250
+ console.warn = (...args) => {
251
+ if (/WebGPU is not available/.test(String(args[0]))) noGpu = true;
252
+ else warn.apply(console, args);
253
+ };
254
+ try {
255
+ await renderer.init();
256
+ if (shared) {
257
+ renderer.setSize(Math.round(width * resolution), Math.round(height * resolution), false);
258
+ renderer.setClearColor(0, 0);
259
+ this._prepareShared(renderer, shared, width, height, resolution, antialias);
260
+ }
261
+ this.backend = noGpu ? "webgl2" : "webgpu";
262
+ break;
263
+ } catch (err) {
264
+ this._disposeRenderer();
265
+ if (!shared) throw err;
266
+ console.warn(`pixi-effects: three layer${spec.name ? ` "${spec.name}"` : ""}: could not draw on PixiJS's GPU device (${err instanceof Error ? err.message : err}); it uses a canvas of its own (shareGpu: false does this on purpose)`);
267
+ shared = void 0;
268
+ this._closing = false;
269
+ } finally {
270
+ console.warn = warn;
271
+ }
240
272
  }
241
- this.backend = noGpu ? "webgl2" : "webgpu";
242
273
  } else {
243
274
  renderer = new WebGLRenderer({ canvas, alpha: true, antialias });
244
275
  this._renderer = renderer;
@@ -272,20 +303,59 @@ var ThreeSequence = class extends Sequence {
272
303
  if (!("camera" in this._objects)) this._objects.camera = this._camera;
273
304
  if (this._gpu) this._checkWebGpuMaterials(scene, spec.name);
274
305
  this._draw(0);
275
- const texture = Texture.from(canvas);
276
- const sprite = new Sprite({ texture, label: spec.name });
277
- sprite.cullable = true;
278
- sprite.width = width;
279
- sprite.height = height;
280
- this.target = sprite;
281
- this.intrinsicWidth = width;
282
- this.intrinsicHeight = height;
283
- this.buildFilters();
306
+ this._texture = this._sharedTexture ?? Texture.from(canvas);
307
+ this._makeSprite(this._texture, width, height, spec.name);
284
308
  } catch (err) {
285
309
  this._disposeRenderer();
286
310
  throw err;
287
311
  }
288
312
  }
313
+ _makeSprite(texture, width, height, name) {
314
+ const sprite = new Sprite({ texture, label: name });
315
+ sprite.cullable = true;
316
+ sprite.width = width;
317
+ sprite.height = height;
318
+ this.target = sprite;
319
+ this.intrinsicWidth = width;
320
+ this.intrinsicHeight = height;
321
+ this.buildFilters();
322
+ }
323
+ /** Reuse (core/reuse.ts): the key this layer is matched by, while it can still lend its state. */
324
+ reuseKey() {
325
+ return this._renderer && !this._donated && !this._closing ? this._key : null;
326
+ }
327
+ _lendable() {
328
+ return this.reuseKey() !== null && !!this._scene && !!this._ctx && !!this._texture;
329
+ }
330
+ /** Take over a layer of the tree being replaced: its scene, renderer, texture and mirrors. Nothing is drawn until the new tree is on stage; the old layer goes on drawing meanwhile. */
331
+ _borrow(d, width, height, name) {
332
+ this.renderer = d.renderer;
333
+ this.backend = d.backend;
334
+ this._gpu = d._gpu;
335
+ this.sharedGpu = d.sharedGpu;
336
+ this._renderer = d._renderer;
337
+ this._scene = d._scene;
338
+ this._camera = d._camera;
339
+ this._ctx = d._ctx;
340
+ this._objects = d._objects;
341
+ this._rt = d._rt;
342
+ this._sharedTexture = d._sharedTexture;
343
+ this._texture = d._texture;
344
+ this._lightMirror = d._lightMirror;
345
+ this._fogMirror = d._fogMirror;
346
+ this._tracked = d._tracked;
347
+ this._borrowed = d;
348
+ if (this.spec && this._threeSpec.followCamera) this._checkFollowShape(width, height, name);
349
+ this._makeSprite(this._texture, width, height, name);
350
+ }
351
+ /** The new tree is on stage: the layer this one borrowed from has given its state away for good. */
352
+ commitReuse() {
353
+ const d = this._borrowed;
354
+ if (!d) return;
355
+ d._donated = true;
356
+ d._tracked = false;
357
+ this._borrowed = null;
358
+ }
289
359
  /**
290
360
  * Per-frame sync hook. Movie._awaitVideoFrames duck-types on this method
291
361
  * (same contract as VideoSequence) and awaits it inside gotoFrame — the
@@ -308,7 +378,7 @@ var ThreeSequence = class extends Sequence {
308
378
  }
309
379
  }
310
380
  this._draw(t);
311
- this.target?.texture.source.update();
381
+ if (!this.sharedGpu) this.target?.texture.source.update();
312
382
  }
313
383
  /**
314
384
  * `movie.pick`: a ray through the point (u, v) of the layer's picture (0..1, v down) from the layer's camera, and what it meets first: the object's name (its own, or its nearest
@@ -398,7 +468,7 @@ var ThreeSequence = class extends Sequence {
398
468
  try {
399
469
  this._followSpace(t);
400
470
  this._threeSpec.update?.(t, ctx);
401
- this._draw(t);
471
+ this._draw(t, true);
402
472
  g.clearRect(0, 0, w, h);
403
473
  g.drawImage(source, 0, 0);
404
474
  } catch {
@@ -501,10 +571,45 @@ var ThreeSequence = class extends Sequence {
501
571
  * follow each other in one task (a seek, an export) would all show the lights of the last animation frame: a lamp keyframed with `three.lamp.intensity` would not move.
502
572
  * So before each render the layer advances the node frame itself, and gives `time` the layer's own time (not three's wall clock).
503
573
  */
504
- _draw(t) {
574
+ _draw(t, toCanvas = false) {
505
575
  if (!this._renderer || !this._scene || !this._camera) return;
506
576
  if (this._gpu) this._advanceNodeFrame(t);
507
- this._renderer.render(this._scene, this._camera);
577
+ if (!this._rt || toCanvas) {
578
+ this._renderer.render(this._scene, this._camera);
579
+ return;
580
+ }
581
+ const r = this._renderer;
582
+ r.setOutputRenderTarget(this._rt);
583
+ try {
584
+ this._renderer.render(this._scene, this._camera);
585
+ } finally {
586
+ r.setOutputRenderTarget(null);
587
+ r.setRenderTarget(null);
588
+ }
589
+ }
590
+ /**
591
+ * A shared layer's texture: a render target of the layer's size, drawn into once (the GPU texture exists only after a draw) and adopted by a PixiJS texture source. Throws when something
592
+ * is not as this expects (the build then makes the layer again on a canvas of its own). Done before `setup`, so nothing the author made yet depends on the renderer.
593
+ */
594
+ _prepareShared(renderer, shared, width, height, resolution, antialias) {
595
+ const w = Math.round(width * resolution), h = Math.round(height * resolution);
596
+ const rt = new RenderTarget(w, h, { depthBuffer: true, samples: antialias ? 4 : 0 });
597
+ this._rt = rt;
598
+ const r = renderer;
599
+ if (typeof r.setOutputRenderTarget !== "function") throw new Error("this three has no setOutputRenderTarget");
600
+ r.setOutputRenderTarget(rt);
601
+ try {
602
+ renderer.render(new Scene(), new PerspectiveCamera());
603
+ } finally {
604
+ r.setOutputRenderTarget(null);
605
+ r.setRenderTarget(null);
606
+ }
607
+ const gpuTexture = r.backend?.get?.(rt.texture)?.texture;
608
+ if (!gpuTexture || typeof gpuTexture.createView !== "function") throw new Error("three keeps no GPU texture for a render target");
609
+ const source = new TextureSource({ width: w, height: h, format: gpuTexture.format, label: "three layer (shared GPU)" });
610
+ shared.adopt(source, gpuTexture);
611
+ this._sharedTexture = new Texture({ source });
612
+ this.sharedGpu = true;
508
613
  }
509
614
  /**
510
615
  * The author chose renderer: 'webgpu'. Three's WebGPU renderer turns materials into node materials: a ShaderMaterial / RawShaderMaterial cannot run there (three logs an error on
@@ -599,28 +704,40 @@ var ThreeSequence = class extends Sequence {
599
704
  console.warn("pixi-effects: three renderer dispose threw \u2014", err);
600
705
  }
601
706
  this._renderer = null;
707
+ try {
708
+ this._rt?.dispose();
709
+ } catch {
710
+ }
711
+ this._rt = null;
602
712
  }
603
713
  destroy() {
604
714
  const spec = this._threeSpec;
605
- if (this._ctx && spec.dispose) {
715
+ const kept = this._donated || this._borrowed !== null;
716
+ if (!kept && this._ctx && spec.dispose) {
606
717
  try {
607
718
  spec.dispose(this._ctx);
608
719
  } catch (err) {
609
720
  console.warn("pixi-effects: three dispose() threw \u2014", err);
610
721
  }
611
722
  }
612
- const texture = this.target?.texture;
613
- this._lightMirror?.dispose();
723
+ const texture = this._texture;
724
+ if (!kept) {
725
+ this._lightMirror?.dispose();
726
+ this._fogMirror?.dispose();
727
+ this._disposeRenderer();
728
+ }
614
729
  this._lightMirror = null;
615
- this._fogMirror?.dispose();
616
730
  this._fogMirror = null;
617
- this._disposeRenderer();
731
+ this._renderer = null;
732
+ this._rt = null;
618
733
  this._scene = null;
619
734
  this._camera = null;
620
735
  this._ctx = null;
621
736
  this._objects = {};
622
737
  super.destroy();
623
- texture?.destroy(true);
738
+ if (!kept) texture?.destroy(true);
739
+ this._texture = null;
740
+ this._sharedTexture = null;
624
741
  }
625
742
  };
626
743
  function resolveDim(v, scope) {
@@ -628,6 +745,17 @@ function resolveDim(v, scope) {
628
745
  if (typeof v === "number") return v;
629
746
  return evaluateExpr(v, scope);
630
747
  }
748
+ var ids = /* @__PURE__ */ new WeakMap();
749
+ var lastId = 0;
750
+ function idOf(fn) {
751
+ if (typeof fn !== "function") return 0;
752
+ let id = ids.get(fn);
753
+ if (id === void 0) {
754
+ id = ++lastId;
755
+ ids.set(fn, id);
756
+ }
757
+ return id;
758
+ }
631
759
  async function threeGltf(url, options = {}) {
632
760
  const label = "pixi-effects: threeGltf";
633
761
  if (typeof url !== "string" || !url) throw new Error(`${label}: the first argument is the URL of a .glb or .gltf file`);