pixi-effects 0.45.0 → 0.47.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 (72) 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 +2 -2
  5. package/ai/tools/lookup.mjs +6 -1
  6. package/ai/tools/pixi-effects.mjs +1 -0
  7. package/ai/tools/render.mjs +17 -1
  8. package/dist/Composition-NSY7A32Y.js +7 -0
  9. package/dist/{Composition-YGBQX3HZ.js.map → Composition-NSY7A32Y.js.map} +1 -1
  10. package/dist/Composition-PME3P5LP.cjs +16 -0
  11. package/dist/{Composition-N6BOROJ2.cjs.map → Composition-PME3P5LP.cjs.map} +1 -1
  12. package/dist/Controller.d.cts +2 -2
  13. package/dist/Controller.d.ts +2 -2
  14. package/dist/{Movie-C-d4zN-N.d.cts → Movie-BqrAWN0g.d.cts} +6 -1
  15. package/dist/{Movie-lMSS9JwX.d.ts → Movie-sV8nJr_f.d.ts} +6 -1
  16. package/dist/{Spans-XTIWYSNO.cjs → Spans-2TZCNREQ.cjs} +9 -9
  17. package/dist/{Spans-XTIWYSNO.cjs.map → Spans-2TZCNREQ.cjs.map} +1 -1
  18. package/dist/{Spans-6L2ZVECX.js → Spans-TESJPQKX.js} +4 -4
  19. package/dist/{Spans-6L2ZVECX.js.map → Spans-TESJPQKX.js.map} +1 -1
  20. package/dist/{chunk-O66JGPVV.cjs → chunk-23SXKSUI.cjs} +18 -4
  21. package/dist/chunk-23SXKSUI.cjs.map +1 -0
  22. package/dist/{chunk-K24FF4BT.js → chunk-4SEL6W7H.js} +18 -4
  23. package/dist/chunk-4SEL6W7H.js.map +1 -0
  24. package/dist/{chunk-3HZV3UHU.cjs → chunk-DKCMJILW.cjs} +67 -65
  25. package/dist/chunk-DKCMJILW.cjs.map +1 -0
  26. package/dist/{chunk-4QVQ5UJJ.js → chunk-DM2OLQOJ.js} +5 -6
  27. package/dist/chunk-DM2OLQOJ.js.map +1 -0
  28. package/dist/{chunk-ICI6ACHY.js → chunk-FPV3NPYC.js} +8 -6
  29. package/dist/chunk-FPV3NPYC.js.map +1 -0
  30. package/dist/{chunk-3IO5RWE4.cjs → chunk-I6VUQ2CR.cjs} +188 -74
  31. package/dist/chunk-I6VUQ2CR.cjs.map +1 -0
  32. package/dist/{chunk-X62H3RBG.cjs → chunk-LALEBV63.cjs} +5 -6
  33. package/dist/chunk-LALEBV63.cjs.map +1 -0
  34. package/dist/{chunk-Z6JQO7GX.js → chunk-W46VWAZC.js} +146 -32
  35. package/dist/chunk-W46VWAZC.js.map +1 -0
  36. package/dist/index.cjs +35 -35
  37. package/dist/index.d.cts +5 -3
  38. package/dist/index.d.ts +5 -3
  39. package/dist/index.js +4 -4
  40. package/dist/loader.css +1 -1
  41. package/dist/present.cjs +4 -4
  42. package/dist/present.d.cts +2 -2
  43. package/dist/present.d.ts +2 -2
  44. package/dist/present.js +4 -4
  45. package/dist/sound.cjs +2 -2
  46. package/dist/sound.js +1 -1
  47. package/dist/three.cjs +194 -15
  48. package/dist/three.cjs.map +1 -1
  49. package/dist/three.d.cts +75 -4
  50. package/dist/three.d.ts +75 -4
  51. package/dist/three.js +191 -13
  52. package/dist/three.js.map +1 -1
  53. package/dist/{types-C5xo0fzb.d.cts → types-CTSrPvY3.d.cts} +64 -58
  54. package/dist/{types-C5xo0fzb.d.ts → types-CTSrPvY3.d.ts} +64 -58
  55. package/docs/api.md +2 -1
  56. package/docs/dsl.md +25 -1
  57. package/llms-full.txt +65 -6
  58. package/package.json +1 -1
  59. package/skills/pixi-effects/SKILL.md +1 -1
  60. package/skills/pixi-effects/reference/cheatsheet.md +3 -3
  61. package/skills/pixi-effects/reference/recipes.md +34 -0
  62. package/skills/pixi-effects/template.html +6 -6
  63. package/dist/Composition-N6BOROJ2.cjs +0 -16
  64. package/dist/Composition-YGBQX3HZ.js +0 -7
  65. package/dist/chunk-3HZV3UHU.cjs.map +0 -1
  66. package/dist/chunk-3IO5RWE4.cjs.map +0 -1
  67. package/dist/chunk-4QVQ5UJJ.js.map +0 -1
  68. package/dist/chunk-ICI6ACHY.js.map +0 -1
  69. package/dist/chunk-K24FF4BT.js.map +0 -1
  70. package/dist/chunk-O66JGPVV.cjs.map +0 -1
  71. package/dist/chunk-X62H3RBG.cjs.map +0 -1
  72. package/dist/chunk-Z6JQO7GX.js.map +0 -1
package/dist/three.d.cts CHANGED
@@ -1,11 +1,66 @@
1
- import { b as SpaceNow, c as SequenceCommon, d as PropValue, e as Sequence, f as PathRouters, S as SequenceSpec } from './types-C5xo0fzb.cjs';
2
- import { Scene, Camera, WebGLRenderer, Object3D, Group, Vector3, PerspectiveCamera } from 'three';
1
+ import { b as Sequence, c as SpaceNow, d as SequenceCommon, e as PropValue, f as PathRouters, S as SequenceSpec } from './types-CTSrPvY3.cjs';
2
+ import { Renderer } from 'pixi.js';
3
+ import { Material, Texture, Scene, Camera, WebGLRenderer, Object3D, Group, Vector3, PerspectiveCamera } from 'three';
3
4
  import { WebGPURenderer } from 'three/webgpu';
4
- import 'pixi.js';
5
5
 
6
6
  /** The page-level default renderer of the three layers: `registerThree({ renderer })` sets it, a layer's own `renderer` wins. */
7
7
  type ThreeRendererKind = 'webgl' | 'webgpu';
8
8
 
9
+ /**
10
+ * `threeLayerTexture(ctx, 'screen')`: a live 2D layer (a `composition`) as the picture on a mesh of a `three` layer (a phone's screen, a television, a billboard, a label).
11
+ *
12
+ * The 2D layer stays a layer of the movie: the timeline drives it, its own `at` / `duration` / keyframes / filters apply. It is not drawn on the stage any more: just before the three layer draws,
13
+ * PixiJS draws it into a texture, and the mesh shows that texture. Two routes, chosen by what the layer has:
14
+ * - the three layer draws on PixiJS's own GPU device (`renderer: 'webgpu'` in a WebGPU movie, the default): the texture is PixiJS's GPUTexture itself, used through three's `ExternalTexture`
15
+ * (no copy; 0.3 ms a frame measured). It is not decoded from sRGB by three (the texture is plain rgba8), so the material decodes it in its shader and the picture is flipped by its UV.
16
+ * - otherwise (the layer has a canvas of its own, or PixiJS is on WebGL): the pixels are read back into a canvas (1.6 ms a frame at 480 x 270, 4 ms at 1280 x 720) and three treats it as an ordinary
17
+ * sRGB canvas texture.
18
+ * Both give the 2D layer's colours exactly (tests/films/three-layer-texture).
19
+ */
20
+
21
+ interface ThreeLayerTextureOptions {
22
+ /** Pixels of the texture per pixel of the 2D layer: 2 for a screen that is seen large. Default 1. */
23
+ resolution?: number;
24
+ /**
25
+ * The 2D layer's empty and half-transparent parts let what is behind the mesh show through (a sticker, a label with rounded corners, a hologram). Default `false`: an opaque screen, empty parts black,
26
+ * which is cheaper and never has the sorting trouble of transparent meshes (give the composition a background rect and use that). With `true` the material blends and does not write depth.
27
+ */
28
+ transparent?: boolean;
29
+ }
30
+ interface ThreeLayerTexture {
31
+ /** An unlit material that shows the 2D layer: put it on a mesh (or on one face of a box: an array of materials). A screen shines by itself, so scene lights do not shade it. */
32
+ material: Material;
33
+ /** The three texture behind the material (colours are only right through `material`, which decodes them). */
34
+ texture: Texture;
35
+ /** The size of the texture in pixels. */
36
+ width: number;
37
+ height: number;
38
+ /** Stop showing the layer (it is drawn on the stage again). Called when the three layer is destroyed. */
39
+ dispose(): void;
40
+ }
41
+ /** What a three layer lends to `threeLayerTexture` (set on `ctx._host` by the layer itself). */
42
+ interface ThreeLayerHost {
43
+ /** A layer of the same composition by its name, among those built before this one. */
44
+ findLayer(name: string): Sequence | null;
45
+ /** The names of those layers, for the message when the name is wrong. */
46
+ layerNames(): string[];
47
+ pixiRenderer: Renderer | null;
48
+ /** The three layer draws on PixiJS's GPU device (no copy is needed). */
49
+ shared: boolean;
50
+ /** Run `fn` just before every draw of the three layer; returns the function that stops it. */
51
+ beforeDraw(fn: () => void): () => void;
52
+ /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`rebind`). */
53
+ track(handle: Rebindable): void;
54
+ }
55
+ /** What a rebuild that keeps the three layer needs: show the layer of the new tree with the same name. */
56
+ interface Rebindable {
57
+ material: Material;
58
+ source(): Sequence;
59
+ rebind(host: ThreeLayerHost): void;
60
+ dispose(): void;
61
+ }
62
+ declare function threeLayerTexture(ctx: ThreeContext, name: string, options?: ThreeLayerTextureOptions): Promise<ThreeLayerTexture>;
63
+
9
64
  /** Handed to setup / update / dispose. One per three sequence. */
10
65
  interface ThreeContext {
11
66
  scene: Scene;
@@ -18,6 +73,8 @@ interface ThreeContext {
18
73
  height: number;
19
74
  /** The composition's camera and the lights alive at this moment, set before every `update` when the layer has `followCamera` or `followLights`; undefined otherwise. */
20
75
  space?: SpaceNow;
76
+ /** Internal: what `threeLayerTexture` needs from the layer that owns this context. */
77
+ _host?: ThreeLayerHost;
21
78
  }
22
79
  interface ThreeSetupResult {
23
80
  /** Objects addressable from keyframes as `three.<name>.<path>`. `camera` is added implicitly unless present. */
@@ -111,6 +168,9 @@ declare class ThreeSequence extends Sequence {
111
168
  private _borrowed;
112
169
  /** True once another layer owns this layer's state: destroying this one must not let it go. */
113
170
  private _donated;
171
+ /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
172
+ private _drawHooks;
173
+ private _layerTextures;
114
174
  private _scene;
115
175
  private _camera;
116
176
  private _ctx;
@@ -128,6 +188,12 @@ declare class ThreeSequence extends Sequence {
128
188
  protected pathRouters(): PathRouters;
129
189
  build(): Promise<void>;
130
190
  private _makeSprite;
191
+ /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
192
+ screenOf(material: unknown): {
193
+ source: Sequence;
194
+ } | null;
195
+ /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
196
+ private _makeHost;
131
197
  /** Reuse (core/reuse.ts): the key this layer is matched by, while it can still lend its state. */
132
198
  reuseKey(): string | null;
133
199
  private _lendable;
@@ -151,6 +217,11 @@ declare class ThreeSequence extends Sequence {
151
217
  point: [number, number, number];
152
218
  distance: number;
153
219
  object3d: Object3D;
220
+ uv?: {
221
+ u: number;
222
+ v: number;
223
+ };
224
+ material?: unknown;
154
225
  } | null;
155
226
  /**
156
227
  * `hit.three.grab(plane)`: drag a grabbed point of the scene. Returns a function of a point (u, v) of the layer's picture (0..1, v down) that gives where the pointer's ray through the layer's
@@ -268,4 +339,4 @@ declare function registerThree(options?: {
268
339
  */
269
340
  declare function three(spec: ThreeSequenceSpec): SequenceSpec;
270
341
 
271
- export { type ThreeContext, type ThreeFrameCameraOptions, type ThreeGltfModel, type ThreeGltfOptions, type ThreeRendererKind, ThreeSequence, type ThreeSequenceSpec, type ThreeSetupResult, registerThree, three, threeFrameCamera, threeGltf };
342
+ export { type ThreeContext, type ThreeFrameCameraOptions, type ThreeGltfModel, type ThreeGltfOptions, type ThreeLayerTexture, type ThreeLayerTextureOptions, type ThreeRendererKind, ThreeSequence, type ThreeSequenceSpec, type ThreeSetupResult, registerThree, three, threeFrameCamera, threeGltf, threeLayerTexture };
package/dist/three.d.ts CHANGED
@@ -1,11 +1,66 @@
1
- import { b as SpaceNow, c as SequenceCommon, d as PropValue, e as Sequence, f as PathRouters, S as SequenceSpec } from './types-C5xo0fzb.js';
2
- import { Scene, Camera, WebGLRenderer, Object3D, Group, Vector3, PerspectiveCamera } from 'three';
1
+ import { b as Sequence, c as SpaceNow, d as SequenceCommon, e as PropValue, f as PathRouters, S as SequenceSpec } from './types-CTSrPvY3.js';
2
+ import { Renderer } from 'pixi.js';
3
+ import { Material, Texture, Scene, Camera, WebGLRenderer, Object3D, Group, Vector3, PerspectiveCamera } from 'three';
3
4
  import { WebGPURenderer } from 'three/webgpu';
4
- import 'pixi.js';
5
5
 
6
6
  /** The page-level default renderer of the three layers: `registerThree({ renderer })` sets it, a layer's own `renderer` wins. */
7
7
  type ThreeRendererKind = 'webgl' | 'webgpu';
8
8
 
9
+ /**
10
+ * `threeLayerTexture(ctx, 'screen')`: a live 2D layer (a `composition`) as the picture on a mesh of a `three` layer (a phone's screen, a television, a billboard, a label).
11
+ *
12
+ * The 2D layer stays a layer of the movie: the timeline drives it, its own `at` / `duration` / keyframes / filters apply. It is not drawn on the stage any more: just before the three layer draws,
13
+ * PixiJS draws it into a texture, and the mesh shows that texture. Two routes, chosen by what the layer has:
14
+ * - the three layer draws on PixiJS's own GPU device (`renderer: 'webgpu'` in a WebGPU movie, the default): the texture is PixiJS's GPUTexture itself, used through three's `ExternalTexture`
15
+ * (no copy; 0.3 ms a frame measured). It is not decoded from sRGB by three (the texture is plain rgba8), so the material decodes it in its shader and the picture is flipped by its UV.
16
+ * - otherwise (the layer has a canvas of its own, or PixiJS is on WebGL): the pixels are read back into a canvas (1.6 ms a frame at 480 x 270, 4 ms at 1280 x 720) and three treats it as an ordinary
17
+ * sRGB canvas texture.
18
+ * Both give the 2D layer's colours exactly (tests/films/three-layer-texture).
19
+ */
20
+
21
+ interface ThreeLayerTextureOptions {
22
+ /** Pixels of the texture per pixel of the 2D layer: 2 for a screen that is seen large. Default 1. */
23
+ resolution?: number;
24
+ /**
25
+ * The 2D layer's empty and half-transparent parts let what is behind the mesh show through (a sticker, a label with rounded corners, a hologram). Default `false`: an opaque screen, empty parts black,
26
+ * which is cheaper and never has the sorting trouble of transparent meshes (give the composition a background rect and use that). With `true` the material blends and does not write depth.
27
+ */
28
+ transparent?: boolean;
29
+ }
30
+ interface ThreeLayerTexture {
31
+ /** An unlit material that shows the 2D layer: put it on a mesh (or on one face of a box: an array of materials). A screen shines by itself, so scene lights do not shade it. */
32
+ material: Material;
33
+ /** The three texture behind the material (colours are only right through `material`, which decodes them). */
34
+ texture: Texture;
35
+ /** The size of the texture in pixels. */
36
+ width: number;
37
+ height: number;
38
+ /** Stop showing the layer (it is drawn on the stage again). Called when the three layer is destroyed. */
39
+ dispose(): void;
40
+ }
41
+ /** What a three layer lends to `threeLayerTexture` (set on `ctx._host` by the layer itself). */
42
+ interface ThreeLayerHost {
43
+ /** A layer of the same composition by its name, among those built before this one. */
44
+ findLayer(name: string): Sequence | null;
45
+ /** The names of those layers, for the message when the name is wrong. */
46
+ layerNames(): string[];
47
+ pixiRenderer: Renderer | null;
48
+ /** The three layer draws on PixiJS's GPU device (no copy is needed). */
49
+ shared: boolean;
50
+ /** Run `fn` just before every draw of the three layer; returns the function that stops it. */
51
+ beforeDraw(fn: () => void): () => void;
52
+ /** Remember a handle: a rebuild that keeps the three layer gives it the new tree's layer (`rebind`). */
53
+ track(handle: Rebindable): void;
54
+ }
55
+ /** What a rebuild that keeps the three layer needs: show the layer of the new tree with the same name. */
56
+ interface Rebindable {
57
+ material: Material;
58
+ source(): Sequence;
59
+ rebind(host: ThreeLayerHost): void;
60
+ dispose(): void;
61
+ }
62
+ declare function threeLayerTexture(ctx: ThreeContext, name: string, options?: ThreeLayerTextureOptions): Promise<ThreeLayerTexture>;
63
+
9
64
  /** Handed to setup / update / dispose. One per three sequence. */
10
65
  interface ThreeContext {
11
66
  scene: Scene;
@@ -18,6 +73,8 @@ interface ThreeContext {
18
73
  height: number;
19
74
  /** The composition's camera and the lights alive at this moment, set before every `update` when the layer has `followCamera` or `followLights`; undefined otherwise. */
20
75
  space?: SpaceNow;
76
+ /** Internal: what `threeLayerTexture` needs from the layer that owns this context. */
77
+ _host?: ThreeLayerHost;
21
78
  }
22
79
  interface ThreeSetupResult {
23
80
  /** Objects addressable from keyframes as `three.<name>.<path>`. `camera` is added implicitly unless present. */
@@ -111,6 +168,9 @@ declare class ThreeSequence extends Sequence {
111
168
  private _borrowed;
112
169
  /** True once another layer owns this layer's state: destroying this one must not let it go. */
113
170
  private _donated;
171
+ /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
172
+ private _drawHooks;
173
+ private _layerTextures;
114
174
  private _scene;
115
175
  private _camera;
116
176
  private _ctx;
@@ -128,6 +188,12 @@ declare class ThreeSequence extends Sequence {
128
188
  protected pathRouters(): PathRouters;
129
189
  build(): Promise<void>;
130
190
  private _makeSprite;
191
+ /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
192
+ screenOf(material: unknown): {
193
+ source: Sequence;
194
+ } | null;
195
+ /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
196
+ private _makeHost;
131
197
  /** Reuse (core/reuse.ts): the key this layer is matched by, while it can still lend its state. */
132
198
  reuseKey(): string | null;
133
199
  private _lendable;
@@ -151,6 +217,11 @@ declare class ThreeSequence extends Sequence {
151
217
  point: [number, number, number];
152
218
  distance: number;
153
219
  object3d: Object3D;
220
+ uv?: {
221
+ u: number;
222
+ v: number;
223
+ };
224
+ material?: unknown;
154
225
  } | null;
155
226
  /**
156
227
  * `hit.three.grab(plane)`: drag a grabbed point of the scene. Returns a function of a point (u, v) of the layer's picture (0..1, v down) that gives where the pointer's ray through the layer's
@@ -268,4 +339,4 @@ declare function registerThree(options?: {
268
339
  */
269
340
  declare function three(spec: ThreeSequenceSpec): SequenceSpec;
270
341
 
271
- export { type ThreeContext, type ThreeFrameCameraOptions, type ThreeGltfModel, type ThreeGltfOptions, type ThreeRendererKind, ThreeSequence, type ThreeSequenceSpec, type ThreeSetupResult, registerThree, three, threeFrameCamera, threeGltf };
342
+ export { type ThreeContext, type ThreeFrameCameraOptions, type ThreeGltfModel, type ThreeGltfOptions, type ThreeLayerTexture, type ThreeLayerTextureOptions, type ThreeRendererKind, ThreeSequence, type ThreeSequenceSpec, type ThreeSetupResult, registerThree, three, threeFrameCamera, threeGltf, threeLayerTexture };
package/dist/three.js CHANGED
@@ -1,7 +1,7 @@
1
- import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-4QVQ5UJJ.js';
1
+ import { Sequence, buildScope, evaluateExpr, registerSequenceType } from './chunk-DM2OLQOJ.js';
2
2
  import './chunk-32MMNDDN.js';
3
3
  import './chunk-BXOCEZ4U.js';
4
- import { Texture, Sprite, TextureSource } from 'pixi.js';
4
+ import { Texture, Sprite, TextureSource, RenderTexture, Matrix } from 'pixi.js';
5
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
@@ -189,6 +189,9 @@ var ThreeSequence = class extends Sequence {
189
189
  _borrowed = null;
190
190
  /** True once another layer owns this layer's state: destroying this one must not let it go. */
191
191
  _donated = false;
192
+ /** Things to do just before every draw (the 2D layers shown on meshes: `threeLayerTexture`), and those layers' handles. Both go with the state to a layer that takes it over. */
193
+ _drawHooks = /* @__PURE__ */ new Set();
194
+ _layerTextures = [];
192
195
  _scene = null;
193
196
  _camera = null;
194
197
  _ctx = null;
@@ -287,7 +290,7 @@ var ThreeSequence = class extends Sequence {
287
290
  renderer.setClearColor(0, 0);
288
291
  const scene = new Scene();
289
292
  const camera = new PerspectiveCamera(50, width / height, 0.1, 2e3);
290
- const ctx = { scene, camera, renderer, width, height };
293
+ const ctx = { scene, camera, renderer, width, height, _host: this._makeHost() };
291
294
  if (spec.followCamera) this._checkFollowShape(width, height, spec.name);
292
295
  let result;
293
296
  try {
@@ -320,6 +323,29 @@ var ThreeSequence = class extends Sequence {
320
323
  this.intrinsicHeight = height;
321
324
  this.buildFilters();
322
325
  }
326
+ /** The 2D layer that a material shows (`threeLayerTexture`), for a click on the mesh to reach it (movie.pick); null for any other material. */
327
+ screenOf(material) {
328
+ const handle = this._layerTextures.find((h) => h.material === material);
329
+ return handle ? { source: handle.source() } : null;
330
+ }
331
+ /** What `threeLayerTexture` asks of this layer: the layers listed before it, PixiJS's renderer, whether the GPU device is shared, and a place to hook in before every draw. */
332
+ _makeHost() {
333
+ return {
334
+ findLayer: (name) => this.parent?.findLayer?.(name) ?? null,
335
+ layerNames: () => this.parent?.layerNames?.() ?? [],
336
+ pixiRenderer: this.root.pixiRenderer ?? null,
337
+ shared: this.sharedGpu,
338
+ beforeDraw: (fn) => {
339
+ this._drawHooks.add(fn);
340
+ return () => {
341
+ this._drawHooks.delete(fn);
342
+ };
343
+ },
344
+ track: (handle) => {
345
+ this._layerTextures.push(handle);
346
+ }
347
+ };
348
+ }
323
349
  /** Reuse (core/reuse.ts): the key this layer is matched by, while it can still lend its state. */
324
350
  reuseKey() {
325
351
  return this._renderer && !this._donated && !this._closing ? this._key : null;
@@ -345,6 +371,10 @@ var ThreeSequence = class extends Sequence {
345
371
  this._fogMirror = d._fogMirror;
346
372
  this._tracked = d._tracked;
347
373
  this._borrowed = d;
374
+ this._drawHooks = d._drawHooks;
375
+ this._layerTextures = d._layerTextures;
376
+ const host = this._makeHost();
377
+ for (const h of this._layerTextures) h.rebind(host);
348
378
  if (this.spec && this._threeSpec.followCamera) this._checkFollowShape(width, height, name);
349
379
  this._makeSprite(this._texture, width, height, name);
350
380
  }
@@ -402,7 +432,9 @@ var ThreeSequence = class extends Sequence {
402
432
  name = n.name;
403
433
  break;
404
434
  }
405
- return { object: name, point: hit.point.toArray(), distance: hit.distance, object3d: hit.object };
435
+ const m = hit.object.material;
436
+ const material = Array.isArray(m) ? m[hit.face?.materialIndex ?? 0] : m;
437
+ return { object: name, point: hit.point.toArray(), distance: hit.distance, object3d: hit.object, ...hit.uv ? { uv: { u: hit.uv.x, v: hit.uv.y } } : {}, material };
406
438
  }
407
439
  return null;
408
440
  }
@@ -573,6 +605,7 @@ var ThreeSequence = class extends Sequence {
573
605
  */
574
606
  _draw(t, toCanvas = false) {
575
607
  if (!this._renderer || !this._scene || !this._camera) return;
608
+ for (const hook of this._drawHooks) hook();
576
609
  if (this._gpu) this._advanceNodeFrame(t);
577
610
  if (!this._rt || toCanvas) {
578
611
  this._renderer.render(this._scene, this._camera);
@@ -623,9 +656,9 @@ var ThreeSequence = class extends Sequence {
623
656
  if (!m || typeof m !== "object" || seen.has(m)) return;
624
657
  seen.add(m);
625
658
  const mat = m;
626
- const label = `${mat.name ? `"${mat.name}" ` : ""}${mat.type ?? "material"}`;
627
- if (mat.isShaderMaterial || mat.isRawShaderMaterial) bad.push(`${label} is a ShaderMaterial (it cannot run on the WebGPU renderer)`);
628
- else if (Object.prototype.hasOwnProperty.call(mat, "onBeforeCompile")) bad.push(`${label} sets onBeforeCompile (the WebGPU renderer ignores it)`);
659
+ const label2 = `${mat.name ? `"${mat.name}" ` : ""}${mat.type ?? "material"}`;
660
+ if (mat.isShaderMaterial || mat.isRawShaderMaterial) bad.push(`${label2} is a ShaderMaterial (it cannot run on the WebGPU renderer)`);
661
+ else if (Object.prototype.hasOwnProperty.call(mat, "onBeforeCompile")) bad.push(`${label2} sets onBeforeCompile (the WebGPU renderer ignores it)`);
629
662
  };
630
663
  scene.traverse?.((o) => {
631
664
  const mats = o.material;
@@ -722,6 +755,12 @@ var ThreeSequence = class extends Sequence {
722
755
  }
723
756
  const texture = this._texture;
724
757
  if (!kept) {
758
+ for (const h of this._layerTextures) {
759
+ try {
760
+ h.dispose();
761
+ } catch {
762
+ }
763
+ }
725
764
  this._lightMirror?.dispose();
726
765
  this._fogMirror?.dispose();
727
766
  this._disposeRenderer();
@@ -757,19 +796,19 @@ function idOf(fn) {
757
796
  return id;
758
797
  }
759
798
  async function threeGltf(url, options = {}) {
760
- const label = "pixi-effects: threeGltf";
761
- if (typeof url !== "string" || !url) throw new Error(`${label}: the first argument is the URL of a .glb or .gltf file`);
799
+ const label2 = "pixi-effects: threeGltf";
800
+ if (typeof url !== "string" || !url) throw new Error(`${label2}: the first argument is the URL of a .glb or .gltf file`);
762
801
  let GLTFLoader;
763
802
  try {
764
803
  ({ GLTFLoader } = await import('three/addons/loaders/GLTFLoader.js'));
765
804
  } catch (err) {
766
- throw new Error(`${label}: needs "three/addons/" in the import map next to "three" (${err instanceof Error ? err.message : err})`);
805
+ throw new Error(`${label2}: needs "three/addons/" in the import map next to "three" (${err instanceof Error ? err.message : err})`);
767
806
  }
768
807
  let gltf;
769
808
  try {
770
809
  gltf = await new GLTFLoader().loadAsync(url);
771
810
  } catch (err) {
772
- throw new Error(`${label}: could not load "${url}" (${err instanceof Error ? err.message : err})`, { cause: err });
811
+ throw new Error(`${label2}: could not load "${url}" (${err instanceof Error ? err.message : err})`, { cause: err });
773
812
  }
774
813
  const clips = gltf.animations ?? [];
775
814
  let clip;
@@ -778,7 +817,7 @@ async function threeGltf(url, options = {}) {
778
817
  else if (wanted === void 0) clip = clips[0];
779
818
  else {
780
819
  clip = typeof wanted === "number" ? clips[wanted] : clips.find((c) => c.name === wanted);
781
- if (!clip) throw new Error(`${label}: "${url}" has no animation ${JSON.stringify(wanted)}; its clips: ${clips.length ? clips.map((c, i) => `${i}: "${c.name}"`).join(", ") : "(none)"}`);
820
+ if (!clip) throw new Error(`${label2}: "${url}" has no animation ${JSON.stringify(wanted)}; its clips: ${clips.length ? clips.map((c, i) => `${i}: "${c.name}"`).join(", ") : "(none)"}`);
782
821
  }
783
822
  const root = gltf.scene;
784
823
  root.traverse((o) => {
@@ -840,6 +879,145 @@ function threeFrameCamera(camera, object, options = {}) {
840
879
  cam.lookAt(centre);
841
880
  cam.updateProjectionMatrix();
842
881
  }
882
+ var label = "pixi-effects: threeLayerTexture";
883
+ async function threeLayerTexture(ctx, name, options = {}) {
884
+ const host = ctx._host;
885
+ if (!host) throw new Error(`${label}: call it from the setup of a three layer: const screen = await threeLayerTexture(ctx, 'screen')`);
886
+ if (typeof name !== "string" || !name) throw new Error(`${label}: the second argument is the name of a 2D layer: threeLayerTexture(ctx, 'screen')`);
887
+ const resolution = options.resolution ?? 1;
888
+ const transparent = options.transparent === true;
889
+ if (!(Number.isFinite(resolution) && resolution > 0 && resolution <= 4)) throw new Error(`${label}: resolution is a number from above 0 to 4 (2 = twice the pixels), got ${JSON.stringify(options.resolution)}`);
890
+ const pixi = host.pixiRenderer;
891
+ if (!pixi) throw new Error(`${label}: PixiJS's renderer is not available here (the movie is not ready)`);
892
+ const source = host.findLayer(name);
893
+ if (!source) {
894
+ const names = host.layerNames();
895
+ throw new Error(`${label}: no layer named "${name}" before this three layer${names.length ? ` (the layers built before it: ${names.map((n) => `"${n}"`).join(", ")})` : ""}. A layer shown on a mesh must be listed BEFORE the three layer, in the same composition, and have that name.`);
896
+ }
897
+ if (source.spec.type !== "composition") {
898
+ throw new Error(`${label}: "${name}" is a ${source.spec.type} layer: a layer shown on a mesh must be a composition (wrap it: { type: 'composition', name: '${name}', width: 480, height: 270, sequences: [ \u2026 ] })`);
899
+ }
900
+ const width = Math.max(1, Math.round(source.intrinsicWidth * resolution)), height = Math.max(1, Math.round(source.intrinsicHeight * resolution));
901
+ const sharedRoute = host.shared && pixi.name === "webgpu";
902
+ const rt = RenderTexture.create({ width, height, resolution: 1, ...sharedRoute ? { autoGenerateMipmaps: true } : {} });
903
+ const gpuTexture = sharedRoute ? pixi.texture.getGpuSource?.(rt.source) : void 0;
904
+ let texture, material, readCanvas = null;
905
+ if (gpuTexture) {
906
+ let webgpu, tsl;
907
+ try {
908
+ [webgpu, tsl] = await Promise.all([import('three/webgpu'), import('three/tsl')]);
909
+ } catch (err) {
910
+ throw new Error(`${label}: needs "three/webgpu" and "three/tsl" in the import map (${err instanceof Error ? err.message : err})`);
911
+ }
912
+ const ext = new webgpu.ExternalTexture(gpuTexture);
913
+ ext.colorSpace = webgpu.NoColorSpace;
914
+ ext.minFilter = webgpu.LinearMipmapLinearFilter;
915
+ ext.magFilter = webgpu.LinearFilter;
916
+ ext.generateMipmaps = false;
917
+ ext.anisotropy = 4;
918
+ ext.repeat.set(1, -1);
919
+ ext.offset.set(0, 1);
920
+ const m = new webgpu.MeshBasicNodeMaterial();
921
+ const t = tsl.texture(ext);
922
+ const tex = t, vec4 = tsl.vec4, eotf = tsl.sRGBTransferEOTF, max = tsl.max;
923
+ const straight = eotf(vec4(tex.rgb.div(max(tex.a, 1e-4)), 1));
924
+ m.colorNode = transparent ? vec4(straight.rgb, tex.a) : vec4(eotf(vec4(tex.rgb, 1)).rgb, 1);
925
+ m.toneMapped = false;
926
+ if (transparent) {
927
+ m.transparent = true;
928
+ m.depthWrite = false;
929
+ }
930
+ texture = ext;
931
+ material = m;
932
+ } else {
933
+ const three2 = await import('three');
934
+ readCanvas = document.createElement("canvas");
935
+ readCanvas.width = width;
936
+ readCanvas.height = height;
937
+ const ct = new three2.CanvasTexture(readCanvas);
938
+ ct.colorSpace = three2.SRGBColorSpace;
939
+ texture = ct;
940
+ material = new three2.MeshBasicMaterial({ map: ct, toneMapped: false, ...transparent ? { transparent: true, depthWrite: false } : {} });
941
+ }
942
+ let current = source;
943
+ let display = source.target;
944
+ let sx = width / source.intrinsicWidth, sy = height / source.intrinsicHeight;
945
+ let hidden = false;
946
+ const hide = () => {
947
+ display.visible = false;
948
+ hidden = true;
949
+ };
950
+ hide();
951
+ const uncull = (c) => {
952
+ if (c.culled) c.culled = false;
953
+ for (const k of c.children) uncull(k);
954
+ };
955
+ const refresh = () => {
956
+ const m = new Matrix().scale(sx, sy);
957
+ display.visible = true;
958
+ uncull(display);
959
+ try {
960
+ pixi.render({ container: display, target: rt, transform: m, clear: true, clearColor: [0, 0, 0, 0] });
961
+ } finally {
962
+ display.visible = false;
963
+ }
964
+ if (gpuTexture) rt.source.updateMipmaps();
965
+ if (readCanvas) {
966
+ const g = readCanvas.getContext("2d");
967
+ if (transparent) {
968
+ const { pixels, width: w, height: h } = pixi.extract.pixels(rt);
969
+ for (let i = 0; i < pixels.length; i += 4) {
970
+ const a = pixels[i + 3];
971
+ if (a > 0 && a < 255) {
972
+ const k = 255 / a;
973
+ pixels[i] = pixels[i] * k;
974
+ pixels[i + 1] = pixels[i + 1] * k;
975
+ pixels[i + 2] = pixels[i + 2] * k;
976
+ }
977
+ }
978
+ g.putImageData(new ImageData(pixels, w, h), 0, 0);
979
+ } else {
980
+ g.clearRect(0, 0, width, height);
981
+ g.drawImage(pixi.extract.canvas(rt), 0, 0);
982
+ }
983
+ texture.needsUpdate = true;
984
+ }
985
+ };
986
+ const stop = host.beforeDraw(refresh);
987
+ refresh();
988
+ const handle = {
989
+ material,
990
+ texture,
991
+ width,
992
+ height,
993
+ source: () => current,
994
+ rebind(next) {
995
+ const again = next.findLayer(name);
996
+ if (!again) throw new Error(`${label}: after a rebuild there is no layer "${name}" before the three layer any more`);
997
+ current = again;
998
+ display = again.target;
999
+ sx = width / again.intrinsicWidth;
1000
+ sy = height / again.intrinsicHeight;
1001
+ hide();
1002
+ refresh();
1003
+ },
1004
+ dispose() {
1005
+ stop();
1006
+ if (hidden) {
1007
+ display.visible = true;
1008
+ hidden = false;
1009
+ }
1010
+ try {
1011
+ rt.destroy(true);
1012
+ } catch {
1013
+ }
1014
+ texture.dispose?.();
1015
+ material.dispose?.();
1016
+ }
1017
+ };
1018
+ host.track(handle);
1019
+ return handle;
1020
+ }
843
1021
 
844
1022
  // src/three/index.ts
845
1023
  function registerThree(options = {}) {
@@ -852,6 +1030,6 @@ function three(spec) {
852
1030
  return spec;
853
1031
  }
854
1032
 
855
- export { ThreeSequence, registerThree, three, threeFrameCamera, threeGltf };
1033
+ export { ThreeSequence, registerThree, three, threeFrameCamera, threeGltf, threeLayerTexture };
856
1034
  //# sourceMappingURL=three.js.map
857
1035
  //# sourceMappingURL=three.js.map