pixi-effects 0.48.2 → 0.50.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +53 -8
- package/ai/chat-template.html +8 -4
- package/ai/tools/check.mjs +6 -6
- package/dist/3d.cjs +967 -0
- package/dist/3d.cjs.map +1 -0
- package/dist/3d.d.cts +305 -0
- package/dist/3d.d.ts +305 -0
- package/dist/3d.js +960 -0
- package/dist/3d.js.map +1 -0
- package/dist/Canvas-BfZkAFJm.d.cts +12 -0
- package/dist/Canvas-BfZkAFJm.d.ts +12 -0
- package/dist/Composition-47U5U637.cjs +16 -0
- package/dist/{Composition-TJ5D7Z63.cjs.map → Composition-47U5U637.cjs.map} +1 -1
- package/dist/Composition-GVWWLFDL.js +7 -0
- package/dist/{Composition-5FKV2IBP.js.map → Composition-GVWWLFDL.js.map} +1 -1
- package/dist/Controller.d.cts +2 -2
- package/dist/Controller.d.ts +2 -2
- package/dist/{Movie-svZQF-9Z.d.cts → Movie-DogzDBuW.d.cts} +36 -5
- package/dist/{Movie--qSYKgAQ.d.ts → Movie-Y7iHg2V8.d.ts} +36 -5
- package/dist/{Spans-6A3VLHRH.js → Spans-3SEZSTQV.js} +6 -6
- package/dist/{Spans-6A3VLHRH.js.map → Spans-3SEZSTQV.js.map} +1 -1
- package/dist/{Spans-5ENZ3XGS.cjs → Spans-R426NVY3.cjs} +11 -11
- package/dist/{Spans-5ENZ3XGS.cjs.map → Spans-R426NVY3.cjs.map} +1 -1
- package/dist/{blendModes-V5XHC7U4.cjs → blendModes-2IDILRN6.cjs} +5 -5
- package/dist/{blendModes-V5XHC7U4.cjs.map → blendModes-2IDILRN6.cjs.map} +1 -1
- package/dist/{blendModes-535EMYGK.js → blendModes-4QH44GOL.js} +4 -4
- package/dist/{blendModes-535EMYGK.js.map → blendModes-4QH44GOL.js.map} +1 -1
- package/dist/{chunk-NGJXVJB7.cjs → chunk-3FCDRLW3.cjs} +5 -3
- package/dist/chunk-3FCDRLW3.cjs.map +1 -0
- package/dist/{chunk-JBDBICS2.js → chunk-C2HL5236.js} +4 -4
- package/dist/{chunk-JBDBICS2.js.map → chunk-C2HL5236.js.map} +1 -1
- package/dist/{chunk-WYR2AU4W.cjs → chunk-GXGIISFS.cjs} +5 -5
- package/dist/{chunk-WYR2AU4W.cjs.map → chunk-GXGIISFS.cjs.map} +1 -1
- package/dist/{chunk-BFRWSV3R.js → chunk-J4ZP76IL.js} +5 -4
- package/dist/chunk-J4ZP76IL.js.map +1 -0
- package/dist/{chunk-CCPJD76D.cjs → chunk-O2MD3L4J.cjs} +93 -93
- package/dist/chunk-O2MD3L4J.cjs.map +1 -0
- package/dist/{chunk-IVKI6CPH.js → chunk-SSVHAF3Q.js} +483 -20
- package/dist/chunk-SSVHAF3Q.js.map +1 -0
- package/dist/{chunk-6D4W2KZQ.js → chunk-TXNQLMRT.js} +6 -6
- package/dist/chunk-TXNQLMRT.js.map +1 -0
- package/dist/{chunk-RCKLUCB7.js → chunk-UTUUOQ47.js} +46 -20
- package/dist/chunk-UTUUOQ47.js.map +1 -0
- package/dist/{chunk-Z3DM4ETD.cjs → chunk-VVUSZVP7.cjs} +507 -42
- package/dist/chunk-VVUSZVP7.cjs.map +1 -0
- package/dist/{chunk-45ALIMAF.cjs → chunk-YOKRPAKA.cjs} +89 -63
- package/dist/chunk-YOKRPAKA.cjs.map +1 -0
- package/dist/index.cjs +35 -35
- package/dist/index.d.cts +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.js +5 -5
- package/dist/loader.css +1 -1
- package/dist/{music-P4JRT73P.js → music-ATX22VAT.js} +3 -3
- package/dist/{music-P4JRT73P.js.map → music-ATX22VAT.js.map} +1 -1
- package/dist/{music-XQEPBIDT.cjs → music-OFVM3PYH.cjs} +3 -3
- package/dist/{music-XQEPBIDT.cjs.map → music-OFVM3PYH.cjs.map} +1 -1
- package/dist/present.cjs +5 -5
- package/dist/present.d.cts +2 -2
- package/dist/present.d.ts +2 -2
- package/dist/present.js +5 -5
- package/dist/three.cjs +13 -10
- package/dist/three.cjs.map +1 -1
- package/dist/three.d.cts +4 -12
- package/dist/three.d.ts +4 -12
- package/dist/three.js +8 -5
- package/dist/three.js.map +1 -1
- package/dist/{types-CTSrPvY3.d.cts → types-BZngm4nT.d.cts} +48 -5
- package/dist/{types-CTSrPvY3.d.ts → types-BZngm4nT.d.ts} +48 -5
- package/docs/api.md +45 -2
- package/docs/dsl.md +115 -1
- package/llms-full.txt +307 -6
- package/package.json +19 -3
- package/skills/pixi-effects/SKILL.md +4 -2
- package/skills/pixi-effects/reference/cheatsheet.md +4 -0
- package/skills/pixi-effects/reference/pitfalls.md +20 -0
- package/skills/pixi-effects/reference/recipes.md +119 -1
- package/skills/pixi-effects/template.html +14 -8
- package/dist/Composition-5FKV2IBP.js +0 -7
- package/dist/Composition-TJ5D7Z63.cjs +0 -16
- package/dist/chunk-45ALIMAF.cjs.map +0 -1
- package/dist/chunk-6D4W2KZQ.js.map +0 -1
- package/dist/chunk-BFRWSV3R.js.map +0 -1
- package/dist/chunk-CCPJD76D.cjs.map +0 -1
- package/dist/chunk-IVKI6CPH.js.map +0 -1
- package/dist/chunk-NGJXVJB7.cjs.map +0 -1
- package/dist/chunk-RCKLUCB7.js.map +0 -1
- package/dist/chunk-Z3DM4ETD.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as pixi_js from 'pixi.js';
|
|
2
|
-
import { Container, TextureSource } from 'pixi.js';
|
|
2
|
+
import { Container, TextureSource, Renderer, Texture } from 'pixi.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Resolves a routed keyframe path to a concrete tween target.
|
|
@@ -186,9 +186,10 @@ interface SharedGpu {
|
|
|
186
186
|
* is thrown away before that leaves the old layers as they were.
|
|
187
187
|
*/
|
|
188
188
|
|
|
189
|
-
/** What a layer that can borrow calls. */
|
|
189
|
+
/** What a layer that can borrow calls: `claim(key, by)` hands it a layer of the old tree (or null), and remembers `by` so the swap commits it wherever it sits in the new tree. */
|
|
190
190
|
interface ReusePool {
|
|
191
|
-
claim(key: string): Sequence | null;
|
|
191
|
+
claim(key: string, by?: Sequence): Sequence | null;
|
|
192
|
+
readonly borrowers?: readonly Sequence[];
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
/**
|
|
@@ -776,6 +777,48 @@ interface CanvasSequenceSpec extends SequenceCommon {
|
|
|
776
777
|
/** `false`: hard pixels when the picture is scaled (pixel art). Default `true`. */
|
|
777
778
|
smoothing?: boolean;
|
|
778
779
|
}
|
|
780
|
+
/** Handed to a scene2d layer's setup / update / dispose. One per layer. */
|
|
781
|
+
interface Scene2DContext {
|
|
782
|
+
/** The layer's own PixiJS container, in the layer's pixels (0, 0 is the top-left of its box): add Graphics, Sprites, Text, a ParticleContainer, Meshes here. */
|
|
783
|
+
root: Container;
|
|
784
|
+
/** PixiJS's renderer (the one that draws every layer). */
|
|
785
|
+
renderer: Renderer;
|
|
786
|
+
/** The layer's box in composition px. Nothing is clipped to it (add a `mask` to clip); `anchorX` / `anchorY` name a point of it and `check` draws it for the determinism test (the layout check measures what is drawn). */
|
|
787
|
+
width: number;
|
|
788
|
+
height: number;
|
|
789
|
+
/** What setup returned in `objects`, plus `root`: the names keyframes reach as `scene2d.<name>.<path>`. */
|
|
790
|
+
objects: Record<string, object>;
|
|
791
|
+
/** The texture of a movie asset (`init({ assets })`) by its name. An unknown name throws with the known names. */
|
|
792
|
+
texture(name: string): Texture;
|
|
793
|
+
}
|
|
794
|
+
interface Scene2DSetupResult {
|
|
795
|
+
/** Objects addressable from keyframes as `scene2d.<name>.<path>` (`root` is added unless you name your own). */
|
|
796
|
+
objects?: Record<string, object>;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* A scene2d layer: PixiJS 2D display objects built by your code (`setup`) and set from the time (`update`), shown in the layer's place in the stack. Everything a layer can do works on
|
|
800
|
+
* it (initial, keyframes, mask, filters, blendMode, threeD). A spec that has one holds functions, so it is no longer pure JSON: an escape hatch like `canvas`, `scene3d` and `three`.
|
|
801
|
+
*/
|
|
802
|
+
interface Scene2DSequenceSpec extends SequenceCommon {
|
|
803
|
+
type: 'scene2d';
|
|
804
|
+
/** The layer's box in px (expressions allowed). Default: the composition's size. */
|
|
805
|
+
width?: number | string;
|
|
806
|
+
height?: number | string;
|
|
807
|
+
/** Build the scene: add display objects to `ctx.root`. May be async (a texture loads): Movie.init waits for it. */
|
|
808
|
+
setup: (ctx: Scene2DContext) => Promise<Scene2DSetupResult | void> | Scene2DSetupResult | void;
|
|
809
|
+
/**
|
|
810
|
+
* Per frame; `t` is the layer's own time in seconds. MUST set everything from `t` (no clock, no unseeded randomness, no state carried from the last call): playback, a seek and an
|
|
811
|
+
* export each draw a frame on their own. An AnimatedSprite left playing is posed from the layer's time by the layer.
|
|
812
|
+
*/
|
|
813
|
+
update?: (t: number, ctx: Scene2DContext) => void;
|
|
814
|
+
/** Cleanup for resources you made (the root and everything under it are destroyed by the layer; textures you made are yours). */
|
|
815
|
+
dispose?: (ctx: Scene2DContext) => void;
|
|
816
|
+
/**
|
|
817
|
+
* A rebuild (`setVars`, `resize`, `rebuild`) keeps this layer's scene when the layer did not change (same `name`, same `setup` function, same size): `setup` runs once. Default `true`;
|
|
818
|
+
* a layer with no `name` is always built again. `false`: `setup` runs again at every rebuild (when it reads something a variable changes).
|
|
819
|
+
*/
|
|
820
|
+
reuse?: boolean;
|
|
821
|
+
}
|
|
779
822
|
/**
|
|
780
823
|
* A gradient fill for a shape (replaces `fillColor`). Positions are relative to the shape's own
|
|
781
824
|
* bounds (0–1), so it follows the shape's size. Colours may have alpha (`'rgba(0,0,0,0.6)'`), so a
|
|
@@ -919,7 +962,7 @@ type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | Ar
|
|
|
919
962
|
interface NullSequenceSpec extends SequenceCommon {
|
|
920
963
|
type: 'null';
|
|
921
964
|
}
|
|
922
|
-
type SequenceSpec = NullSequenceSpec | VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | LightSequenceSpec | ShaderSequenceSpec | CanvasSequenceSpec | ShapeSequenceSpec;
|
|
965
|
+
type SequenceSpec = NullSequenceSpec | VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | LightSequenceSpec | ShaderSequenceSpec | CanvasSequenceSpec | Scene2DSequenceSpec | ShapeSequenceSpec;
|
|
923
966
|
/** Top-level composition (root node) — same as `CompositionSequenceSpec` minus the discriminant. */
|
|
924
967
|
interface CompositionSpec extends SequenceCommon {
|
|
925
968
|
width?: number;
|
|
@@ -963,4 +1006,4 @@ interface SpaceNow {
|
|
|
963
1006
|
fog: FogState | null;
|
|
964
1007
|
}
|
|
965
1008
|
|
|
966
|
-
export { type
|
|
1009
|
+
export { type Scene2DSetupResult as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type Scene2DContext as Z, type Scene2DSequenceSpec as _, type Stop as a, type SfxKnobs as a0, type SfxOptions as a1, type SfxPreset as a2, type SfxVoice as a3, type ShaderSequenceSpec as a4, type ShapeSequenceSpec as a5, type SlideTransition as a6, type StopSpec as a7, type TextSpan as a8, type TransitionCommon as a9, type VideoSequenceSpec as aa, type WipeTransition as ab, type ZoomTransition as ac, type SpaceNow as b, type SequenceCommon as c, type PropValue as d, Sequence as e, type PathRouters as f, type CompositionShape as g, type CameraSequenceSpec as h, type TextSequenceSpec as i, type ArcShapeSpec as j, type AudioAssetSpec as k, type AudioMusicSpec as l, type AudioSequenceSpec as m, type AudioSfxSpec as n, type CanvasInfo as o, type CanvasSequenceSpec as p, type ChromaKeyFilterSpec as q, type CircleShapeSpec as r, type CompositionSequenceSpec as s, type CrossfadeTransition as t, type CustomFilterSpec as u, type DissolveTransition as v, type Expr as w, type IrisTransition as x, type LineShapeSpec as y, type LumaTransition as z };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as pixi_js from 'pixi.js';
|
|
2
|
-
import { Container, TextureSource } from 'pixi.js';
|
|
2
|
+
import { Container, TextureSource, Renderer, Texture } from 'pixi.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Resolves a routed keyframe path to a concrete tween target.
|
|
@@ -186,9 +186,10 @@ interface SharedGpu {
|
|
|
186
186
|
* is thrown away before that leaves the old layers as they were.
|
|
187
187
|
*/
|
|
188
188
|
|
|
189
|
-
/** What a layer that can borrow calls. */
|
|
189
|
+
/** What a layer that can borrow calls: `claim(key, by)` hands it a layer of the old tree (or null), and remembers `by` so the swap commits it wherever it sits in the new tree. */
|
|
190
190
|
interface ReusePool {
|
|
191
|
-
claim(key: string): Sequence | null;
|
|
191
|
+
claim(key: string, by?: Sequence): Sequence | null;
|
|
192
|
+
readonly borrowers?: readonly Sequence[];
|
|
192
193
|
}
|
|
193
194
|
|
|
194
195
|
/**
|
|
@@ -776,6 +777,48 @@ interface CanvasSequenceSpec extends SequenceCommon {
|
|
|
776
777
|
/** `false`: hard pixels when the picture is scaled (pixel art). Default `true`. */
|
|
777
778
|
smoothing?: boolean;
|
|
778
779
|
}
|
|
780
|
+
/** Handed to a scene2d layer's setup / update / dispose. One per layer. */
|
|
781
|
+
interface Scene2DContext {
|
|
782
|
+
/** The layer's own PixiJS container, in the layer's pixels (0, 0 is the top-left of its box): add Graphics, Sprites, Text, a ParticleContainer, Meshes here. */
|
|
783
|
+
root: Container;
|
|
784
|
+
/** PixiJS's renderer (the one that draws every layer). */
|
|
785
|
+
renderer: Renderer;
|
|
786
|
+
/** The layer's box in composition px. Nothing is clipped to it (add a `mask` to clip); `anchorX` / `anchorY` name a point of it and `check` draws it for the determinism test (the layout check measures what is drawn). */
|
|
787
|
+
width: number;
|
|
788
|
+
height: number;
|
|
789
|
+
/** What setup returned in `objects`, plus `root`: the names keyframes reach as `scene2d.<name>.<path>`. */
|
|
790
|
+
objects: Record<string, object>;
|
|
791
|
+
/** The texture of a movie asset (`init({ assets })`) by its name. An unknown name throws with the known names. */
|
|
792
|
+
texture(name: string): Texture;
|
|
793
|
+
}
|
|
794
|
+
interface Scene2DSetupResult {
|
|
795
|
+
/** Objects addressable from keyframes as `scene2d.<name>.<path>` (`root` is added unless you name your own). */
|
|
796
|
+
objects?: Record<string, object>;
|
|
797
|
+
}
|
|
798
|
+
/**
|
|
799
|
+
* A scene2d layer: PixiJS 2D display objects built by your code (`setup`) and set from the time (`update`), shown in the layer's place in the stack. Everything a layer can do works on
|
|
800
|
+
* it (initial, keyframes, mask, filters, blendMode, threeD). A spec that has one holds functions, so it is no longer pure JSON: an escape hatch like `canvas`, `scene3d` and `three`.
|
|
801
|
+
*/
|
|
802
|
+
interface Scene2DSequenceSpec extends SequenceCommon {
|
|
803
|
+
type: 'scene2d';
|
|
804
|
+
/** The layer's box in px (expressions allowed). Default: the composition's size. */
|
|
805
|
+
width?: number | string;
|
|
806
|
+
height?: number | string;
|
|
807
|
+
/** Build the scene: add display objects to `ctx.root`. May be async (a texture loads): Movie.init waits for it. */
|
|
808
|
+
setup: (ctx: Scene2DContext) => Promise<Scene2DSetupResult | void> | Scene2DSetupResult | void;
|
|
809
|
+
/**
|
|
810
|
+
* Per frame; `t` is the layer's own time in seconds. MUST set everything from `t` (no clock, no unseeded randomness, no state carried from the last call): playback, a seek and an
|
|
811
|
+
* export each draw a frame on their own. An AnimatedSprite left playing is posed from the layer's time by the layer.
|
|
812
|
+
*/
|
|
813
|
+
update?: (t: number, ctx: Scene2DContext) => void;
|
|
814
|
+
/** Cleanup for resources you made (the root and everything under it are destroyed by the layer; textures you made are yours). */
|
|
815
|
+
dispose?: (ctx: Scene2DContext) => void;
|
|
816
|
+
/**
|
|
817
|
+
* A rebuild (`setVars`, `resize`, `rebuild`) keeps this layer's scene when the layer did not change (same `name`, same `setup` function, same size): `setup` runs once. Default `true`;
|
|
818
|
+
* a layer with no `name` is always built again. `false`: `setup` runs again at every rebuild (when it reads something a variable changes).
|
|
819
|
+
*/
|
|
820
|
+
reuse?: boolean;
|
|
821
|
+
}
|
|
779
822
|
/**
|
|
780
823
|
* A gradient fill for a shape (replaces `fillColor`). Positions are relative to the shape's own
|
|
781
824
|
* bounds (0–1), so it follows the shape's size. Colours may have alpha (`'rgba(0,0,0,0.6)'`), so a
|
|
@@ -919,7 +962,7 @@ type ShapeSequenceSpec = RectShapeSpec | CircleShapeSpec | EllipseShapeSpec | Ar
|
|
|
919
962
|
interface NullSequenceSpec extends SequenceCommon {
|
|
920
963
|
type: 'null';
|
|
921
964
|
}
|
|
922
|
-
type SequenceSpec = NullSequenceSpec | VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | LightSequenceSpec | ShaderSequenceSpec | CanvasSequenceSpec | ShapeSequenceSpec;
|
|
965
|
+
type SequenceSpec = NullSequenceSpec | VideoSequenceSpec | ImageSequenceSpec | TextSequenceSpec | AudioSequenceSpec | CompositionSequenceSpec | CameraSequenceSpec | LightSequenceSpec | ShaderSequenceSpec | CanvasSequenceSpec | Scene2DSequenceSpec | ShapeSequenceSpec;
|
|
923
966
|
/** Top-level composition (root node) — same as `CompositionSequenceSpec` minus the discriminant. */
|
|
924
967
|
interface CompositionSpec extends SequenceCommon {
|
|
925
968
|
width?: number;
|
|
@@ -963,4 +1006,4 @@ interface SpaceNow {
|
|
|
963
1006
|
fog: FogState | null;
|
|
964
1007
|
}
|
|
965
1008
|
|
|
966
|
-
export { type
|
|
1009
|
+
export { type Scene2DSetupResult as $, type AssetSpec as A, type BlendModeName as B, type CompositionSpec as C, type DipTransition as D, type EllipseShapeSpec as E, type FilterSpec as F, type GradientSpec as G, type MaskRefSpec as H, type ImageSequenceSpec as I, type MatteRefSpec as J, type Keyframe as K, type LightSequenceSpec as L, type MusicOptions as M, type MusicDrum as N, type MusicDrums as O, type Props as P, type MusicInstrument as Q, type MusicLevel as R, type SequenceSpec as S, type TransitionSpec as T, type MusicTrack as U, type NullSequenceSpec as V, type PathShapeSpec as W, type PolygonShapeSpec as X, type RectShapeSpec as Y, type Scene2DContext as Z, type Scene2DSequenceSpec as _, type Stop as a, type SfxKnobs as a0, type SfxOptions as a1, type SfxPreset as a2, type SfxVoice as a3, type ShaderSequenceSpec as a4, type ShapeSequenceSpec as a5, type SlideTransition as a6, type StopSpec as a7, type TextSpan as a8, type TransitionCommon as a9, type VideoSequenceSpec as aa, type WipeTransition as ab, type ZoomTransition as ac, type SpaceNow as b, type SequenceCommon as c, type PropValue as d, Sequence as e, type PathRouters as f, type CompositionShape as g, type CameraSequenceSpec as h, type TextSequenceSpec as i, type ArcShapeSpec as j, type AudioAssetSpec as k, type AudioMusicSpec as l, type AudioSequenceSpec as m, type AudioSfxSpec as n, type CanvasInfo as o, type CanvasSequenceSpec as p, type ChromaKeyFilterSpec as q, type CircleShapeSpec as r, type CompositionSequenceSpec as s, type CrossfadeTransition as t, type CustomFilterSpec as u, type DissolveTransition as v, type Expr as w, type IrisTransition as x, type LineShapeSpec as y, type LumaTransition as z };
|
package/docs/api.md
CHANGED
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
- [`Movie`](#movie) — composition runtime: init, playback, render
|
|
4
4
|
- [`Controller`](#controller) — drop-in player UI overlay
|
|
5
|
-
- [`pixi-effects/
|
|
5
|
+
- [`pixi-effects/3d`](#pixi-effects3d) — 3D scenes drawn by PixiJS 3D (the main 3D engine)
|
|
6
|
+
- [`pixi-effects/three`](#pixi-effectsthree) — optional three.js integration (the sub 3D engine)
|
|
6
7
|
|
|
7
8
|
For DSL types (composition spec, sequences, filters, keyframes, expressions), see [DSL reference](./dsl.md).
|
|
8
9
|
|
|
@@ -174,10 +175,13 @@ What is under a point of the canvas, at the frame that is on screen: the layer o
|
|
|
174
175
|
```ts
|
|
175
176
|
interface PickHit {
|
|
176
177
|
name?: string; path: string; type: string; // path: as in inspect(), names or type#index joined by '/'
|
|
177
|
-
kind: 'flat' | 'card' | 'three';
|
|
178
|
+
kind: 'flat' | 'card' | 'scene2d' | 'scene3d' | 'three'; // a 2D layer, a threeD layer (its projected card), a scene2d / scene3d / three layer
|
|
178
179
|
point: { x: number; y: number }; // the point asked about
|
|
179
180
|
local?: { x: number; y: number }; // a flat layer: the point in the layer's own pixels
|
|
180
181
|
via?: { path: string; object: string }; // the layer is shown on a mesh of a `three` layer (threeLayerTexture) and the point is on that mesh: the three layer and the object it came through
|
|
182
|
+
scene2d?: { object: string; node: unknown; point: [number, number]; // a scene2d layer: the top object of its scene under the point (its label)
|
|
183
|
+
grab: () => { move: (x, y) => { point, delta } } }; // drag it: delta in the node's parent's pixels
|
|
184
|
+
scene3d?: { object: string; point: [number, number, number]; distance: number; node?: unknown; grab? }; // a scene3d layer: what the ray met first
|
|
181
185
|
three?: { object: string; point: [number, number, number]; distance: number; object3d: unknown; // a three layer: what the ray met first
|
|
182
186
|
grab?: (plane?) => { move: (x, y) => { point, delta } | null } }; // drag it: see below
|
|
183
187
|
}
|
|
@@ -644,6 +648,45 @@ Active when `keyboard: true` and the key target is not `<input>`/`<textarea>`/`<
|
|
|
644
648
|
|
|
645
649
|
The bar uses fixed colors (`#007AFF` for the active track / fill / confirm button, white for icons, `rgba(0,0,0,0.75)` gradient background). Override by adding stricter CSS rules under `.movie-controller`. A theming API (CSS custom properties) is on the roadmap.
|
|
646
650
|
|
|
651
|
+
## `pixi-effects/3d`
|
|
652
|
+
|
|
653
|
+
```ts
|
|
654
|
+
import { register3D, scene3d, layerTexture, loadModel, fitCamera, Scene3DSequence } from 'pixi-effects/3d';
|
|
655
|
+
import { Mesh3D, Model3D, DirectionalLight } from '@pixi/3d';
|
|
656
|
+
```
|
|
657
|
+
|
|
658
|
+
**Install:** `npm i @pixi/3d` (it needs `pixi.js` 8.23.0 or later). `@pixi/3d` is a peer dependency marked optional — a page that never imports `pixi-effects/3d` does not load it. On a page with an import map: `"pixi-effects/3d"`, `"@pixi/3d": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d.min.mjs"` and `"@pixi/3d/extras": "https://cdn.jsdelivr.net/npm/@pixi/3d@0.1.0/dist/pixijs-3d-extras.min.mjs"` (the template has them). **Import it before `movie.init`.** The layer is described in [DSL: scene3d](./dsl.md#scene3d).
|
|
659
|
+
|
|
660
|
+
### `register3D(): void`
|
|
661
|
+
|
|
662
|
+
Registers the `scene3d` layer type (explicit, so a bundler cannot drop it; calling it again is fine) and turns off the self-updating of `Model3D` and `AnimationPlayer` (`defaultOptions.autoUpdate = false`): a video draws from the time. Warns once when PixiJS is older than 8.23.
|
|
663
|
+
|
|
664
|
+
### `scene3d(spec: Scene3DSequenceSpec): SequenceSpec`
|
|
665
|
+
|
|
666
|
+
Typing helper: takes a typed scene3d spec and returns it as a core `SequenceSpec`.
|
|
667
|
+
|
|
668
|
+
### `layerTexture(ctx, name, options?): Promise<LayerTexture>`
|
|
669
|
+
|
|
670
|
+
In a scene3d layer's `setup`: the 2D layer `name` (a composition listed before the scene3d layer) shown on a mesh. Returns `{ material, texture, width, height, dispose }`; options `resolution`, `transparent`. See [DSL: scene3d](./dsl.md#a-2d-layer-on-a-mesh-layertexture).
|
|
671
|
+
|
|
672
|
+
### `loadModel(url, options?): Promise<LoadedModel>`
|
|
673
|
+
|
|
674
|
+
Loads a glTF / GLB as a PixiJS 3D `Model3D`: `{ object, model, clips, size(), fit(n), pose(t) }`; options `animation` (a name, an index, `false`), `speed`, `offset`, `loop`.
|
|
675
|
+
|
|
676
|
+
### `fitCamera(ctx, object, options?): void`
|
|
677
|
+
|
|
678
|
+
Puts the layer's camera where the whole of `object` fits; options `direction` (default `[0.45, 0.3, 1]`), `margin` (default 1.1).
|
|
679
|
+
|
|
680
|
+
### `Scene3DSequence`
|
|
681
|
+
|
|
682
|
+
The layer class; `view` is its `View3D`. `checkDeterminism(times)` is what `check` runs; `pickAt(u, v)` and `dragPlane(point, plane)` are what `movie.pick` and `hit.scene3d.grab` call.
|
|
683
|
+
|
|
684
|
+
### Exported types
|
|
685
|
+
|
|
686
|
+
```ts
|
|
687
|
+
import type { Scene3DContext, Scene3DSetupResult, Scene3DSequenceSpec, LayerTexture, LayerTextureOptions, LoadedModel, LoadModelOptions, FitCameraOptions } from 'pixi-effects/3d';
|
|
688
|
+
```
|
|
689
|
+
|
|
647
690
|
## `pixi-effects/three`
|
|
648
691
|
|
|
649
692
|
Optional three.js integration, imported from its own entry so the core `pixi-effects` entry never touches three.js:
|
package/docs/dsl.md
CHANGED
|
@@ -553,6 +553,40 @@ It is a layer like any other: `name`, `at`, `duration`, `initial`, `keyframes`,
|
|
|
553
553
|
|
|
554
554
|
Mistakes are said at build time with the layer's name: no `draw`, a `draw` or `setup` that throws (the frame is a magenta checkerboard and the next frames are tried again), a bad `width` / `height` / `resolution` / `params`, an unknown key (did you mean …), a `params.name` keyframe that names nothing. A draw costs time on every frame that changes (`draw` is called once for each distinct time, so a still picture still redraws every frame of a moving movie: draw what never changes once into a canvas from `info.canvas` in `setup` and `drawImage` it): a slow `draw` slows the export in proportion, and `check` prints the usual time of one draw. The layer's `duration` defaults to its parent's length: with `at` greater than 0 and no `duration`, `info.progress` stops short of 1 when the movie ends, so give a `duration` when you draw from `progress`. `check` draws each canvas layer in a scratch canvas (so reading its pixels does not change how the real one is drawn) at four moments: one draw of each is thrown away (a browser can draw a canvas from `setup` differently the first time), then each moment is drawn twice and in two orders. It prints a `canvas` line when every layer passed (`deterministic · slowest draw 3.2 ms`), so silence is never the only sign that it ran, and a layer that is not deterministic is reported under that heading with the moments, how many channel values differ and by how much (up to N of 255).
|
|
555
555
|
|
|
556
|
+
### `scene2d`
|
|
557
|
+
|
|
558
|
+
**PixiJS 2D built by your own code**: display objects (`Graphics`, `Sprite`, `Text`, `AnimatedSprite`, `ParticleContainer`, `Mesh`, …) made in `setup` and set from the time in `update`, shown in the layer's place in the stack. Use it when the data cannot say it (160 pieces that each fly their own way, a generated pattern, a sprite sheet) and you want PixiJS's objects rather than a 2D context (`canvas`) or a formula (`shader`). It is in core `pixi-effects`: nothing to import or register. Its shape is the `scene3d` layer's.
|
|
559
|
+
|
|
560
|
+
```ts
|
|
561
|
+
import { Graphics } from 'pixi.js';
|
|
562
|
+
{ type: 'scene2d', name: 'dots', duration: 4,
|
|
563
|
+
setup: ({ root, width, height }) => { // once: build the scene in ctx.root (the layer's pixels, 0, 0 = top-left of its box)
|
|
564
|
+
const dot = new Graphics({ label: 'dot' }).circle(0, 0, 30).fill('#ff4f1a');
|
|
565
|
+
root.addChild(dot);
|
|
566
|
+
return { objects: { dot } }; // keyframes reach it as scene2d.dot.<path>
|
|
567
|
+
},
|
|
568
|
+
update: (t, { objects, width }) => { objects.dot.position.set(width * (t / 4), 360); }, // per frame: SET everything from t
|
|
569
|
+
keyframes: [{ at: 1, to: { 'scene2d.dot.alpha': 0.3 }, duration: 1 }] }
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
It is a layer like any other: `name`, `at`, `duration`, `initial` (`anchorX` / `anchorY` name a point of its box, as on a canvas layer), `keyframes`, `filters`, `mask`, `blendMode`, `parent`, `threeD`, transitions, mattes, export all work on it.
|
|
573
|
+
|
|
574
|
+
| Key | Meaning |
|
|
575
|
+
|---|---|
|
|
576
|
+
| `setup(ctx)` | **Required.** Builds the scene once (may be async: Movie.init waits). Returns `{ objects }`: the names `scene2d.<name>.<path>` keyframes reach (`root` is added). |
|
|
577
|
+
| `update(t, ctx)` | Per frame, `t` the layer's own seconds. Sets the scene from the time. |
|
|
578
|
+
| `dispose(ctx)` | Frees what you made (the root and everything under it are destroyed by the layer; textures you made are yours). |
|
|
579
|
+
| `width`, `height` | The layer's box in px or an expression; default the composition's size. Nothing is clipped to it (give a `mask` to clip); `anchorX` / `anchorY` name a point of it and `check` draws it for the determinism test (the layout check measures what is drawn). |
|
|
580
|
+
| `reuse` | Default `true`: a rebuild (`setVars`, `resize`) keeps the scene of a named layer whose `setup` function and size did not change, so `setup` runs once. `false`: setup runs at every rebuild. |
|
|
581
|
+
|
|
582
|
+
`ctx` is `{ root, renderer, width, height, objects, texture(name) }`: `root` is a PixiJS `Container` of the layer's own, `texture('photo')` is the texture of the movie asset `photo` (`init({ assets })`; an unknown name says which there are). Keyframe paths are raw PixiJS values: `scene2d.dot.rotation` is in radians (`angle` is degrees), `scene2d.dot.scale.x`, `scene2d.dot.tint`.
|
|
583
|
+
|
|
584
|
+
**The contract: a function of the time.** The same `t` must give the same picture in playback, a seek and an export, so `update` sets every object from `t` (never `x += …`), uses no `Math.random`, `Date.now`, `performance.now` or `new Date` (a warning at build says so; `random(seed)` from `pixi-effects` is repeatable: draw its numbers before the layer or in `setup`), and keeps nothing from the last call. What runs on PixiJS's ticker by itself is turned off after `setup`: an **`AnimatedSprite` left playing is posed by the layer from its time** (frame `floor(t × 60 × animationSpeed)` from its first frame, as fast as in a plain PixiJS page; its loop and per-frame times kept; one that does not loop holds its last frame, and `onComplete` / `onLoop` are not called: for any other timing pose it yourself with `gotoAndStop(frame)` in `update`: a stopped sprite is left alone), anything else with `autoUpdate` is turned off with one warning, a sprite that shows a `<video>` warns (footage is a `video` layer). A `Ticker.shared.add` of your own is not seen: don't. `check` draws each scene2d layer at four moments in two orders and names one whose same frame differs.
|
|
585
|
+
|
|
586
|
+
**Pick.** `movie.pick(x, y)` returns `kind: 'scene2d'` and `scene2d: { object, node, point, grab }` for the top object of the scene under the point, by PixiJS's own `containsPoint` (a `Graphics` by its shape, a `Sprite` or `Text` by its box) or its `hitArea`; `eventMode: 'none'`, `interactiveChildren: false`, a `hitArea` that does not hold the point (nothing inside it is hit) and a node used as a mask (never hit) are honoured as PixiJS's own events honour them; the order of a `RenderLayer` is not looked at (the tree's order is). `object` is the node's `label`, or its nearest labelled ancestor's (a node made without a label carries its kind, `'Graphics'`, which counts as no name: label a `Container` to name a group). Empty space in the box is not the layer: the point falls through to the layer below. `hit.scene2d.grab().move(x, y)` gives `{ point, delta }` in the node's parent's pixels (`node.x += delta[0]`); `check --pick X,Y` prints `world (scene2d: box)`. PixiJS's own pointer events (`node.eventMode = 'static'; node.on('pointerdown', …)`) work too in a playing page; prefer `movie.pick` (one vocabulary, testable by `check`).
|
|
587
|
+
|
|
588
|
+
Mistakes are said with the layer's name: no `setup`, a `setup` that throws (with its message), an `update` that throws (once), a keyframe path that names nothing (with the known objects), an unknown key (did you mean …). A spec that holds a `scene2d` layer holds functions, so it is not pure JSON (like `canvas`, `scene3d` and `three`). Recipe `scene2d-basics`.
|
|
589
|
+
|
|
556
590
|
## 3D layers & camera
|
|
557
591
|
|
|
558
592
|
Place any 2D layer in depth and view it through a camera — After Effects style, no three.js. Everything uses the normal `initial` / `keyframes` / expression vocabulary.
|
|
@@ -735,9 +769,89 @@ The camera takes `fogNear`, `fogFar` (distances along the view direction, px), `
|
|
|
735
769
|
|
|
736
770
|
---
|
|
737
771
|
|
|
772
|
+
## scene3d
|
|
773
|
+
|
|
774
|
+
A real 3D scene (meshes, glTF models, PBR materials, lights, shadows) as a layer, drawn by [PixiJS 3D](https://pixijs.com/3d) (`@pixi/3d`), the main 3D engine of pixi-effects. The scene is drawn **inside PixiJS's own renderer**, in the layer's place in the stack: no second renderer, no canvas per layer, no copy. Everything a layer has works on it: `at` / `duration`, transform keyframes, `alpha` (and a parent's alpha), `blendMode`, [filters](#filters), [masks](#masks), nesting in compositions and in `threeD` cards.
|
|
775
|
+
|
|
776
|
+
`type: 'scene3d'` lives in the optional `pixi-effects/3d` entry (see [API reference](./api.md#pixi-effects3d)). **Import it before `movie.init`**: PixiJS loads the 3D drawing code while its renderer starts, so a renderer that started before `@pixi/3d` was imported cannot draw a scene3d layer (the build says so).
|
|
777
|
+
|
|
778
|
+
```ts
|
|
779
|
+
import { register3D, scene3d } from 'pixi-effects/3d';
|
|
780
|
+
import { Mesh3D, CubeGeometry, Material3D, DirectionalLight } from '@pixi/3d';
|
|
781
|
+
|
|
782
|
+
register3D(); // once, before Movie.init
|
|
783
|
+
|
|
784
|
+
scene3d({
|
|
785
|
+
type: 'scene3d',
|
|
786
|
+
name: 'hero',
|
|
787
|
+
view: { toneMapping: 'aces', environment: { skyColor: 0xbcd4f0, groundColor: 0x6b6250, intensity: 0.7 } },
|
|
788
|
+
setup: ({ root, camera }) => {
|
|
789
|
+
const cube = new Mesh3D({ geometry: new CubeGeometry(), material: new Material3D({ baseColor: 0xdd4444, roughness: 0.5 }) });
|
|
790
|
+
const sun = new DirectionalLight({ intensity: 3 });
|
|
791
|
+
sun.rotation.set(-0.8, 0.5, 0);
|
|
792
|
+
root.addChild(cube, sun);
|
|
793
|
+
camera.position.set(0, 1.5, 5);
|
|
794
|
+
camera.lookAt({ x: 0, y: 0, z: 0 });
|
|
795
|
+
return { objects: { cube } }; // exposes `cube` to keyframes
|
|
796
|
+
},
|
|
797
|
+
update: (t, { objects }) => { objects.cube.rotation.y = t * 1.2; }, // set everything from t
|
|
798
|
+
keyframes: [{ at: 1, to: { 'scene3d.cube.position.x': 1.5 }, duration: 1 }],
|
|
799
|
+
})
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
| Field | Type | Notes |
|
|
803
|
+
|---|---|---|
|
|
804
|
+
| `width?`, `height?` | `PropValue` | layer size in composition px; expressions allowed. Default: the parent composition's size. Place it like any layer (`initial: { x, y, anchorX, anchorY }`). |
|
|
805
|
+
| `view?` | `View3DOptions` | options of the PixiJS 3D view: `environment`, `toneMapping` (`'none'` default; `'aces'` for strong lights), `exposure`, `shadowQuality`, `passes` (bloom, SSAO … from `@pixi/3d/extras`), `cull`. The layer owns `width` / `height` / `autoResize`, and draws with `cull: 'cpu'` unless you name one. |
|
|
806
|
+
| `resolution?` | `number` | the view's resolution (a value other than 1 draws the scene at that scale and composites it). |
|
|
807
|
+
| `followCamera?`, `followLights?`, `followFog?` | `boolean` | live in the composition's space (see below). |
|
|
808
|
+
| `reuse?` | `boolean` | a rebuild (`setVars`, `resize`, `rebuild`) keeps the scene of a layer that did not change (same `name`, `setup`, size and options): `setup` runs once. Default `true`; a layer with no `name` is always built again; `false` runs `setup` at every rebuild. |
|
|
809
|
+
| `setup` | `(ctx) => { objects? } \| Promise<…>` | builds the scene once; async, so `Movie.init` waits for a model to load. A setup that throws stops `init` with the layer's name and the reason. |
|
|
810
|
+
| `update?` | `(t, ctx) => void` | per frame, `t` = the layer's own time in seconds. Set everything from `t`. |
|
|
811
|
+
| `dispose?` | `(ctx) => void` | cleanup for what you made (the view and everything under its root are destroyed by the layer). |
|
|
812
|
+
|
|
813
|
+
`ctx` is `{ view, root, camera, renderer, width, height, objects, space? }`: the `View3D`, its `root` (add meshes, models and lights here), its `camera` (`fov` in degrees, `mode: 'perspective' | 'orthographic'`; place it with `position.set` and `lookAt`), PixiJS's renderer, the layer size, and the `objects` setup returned (plus `camera` and `view`). Keyframes reach any of them as **`scene3d.<object>.<path>`** (`'scene3d.cube.rotation.y'`, `'scene3d.view.exposure'`); a path to an object setup did not name warns once with the known names.
|
|
814
|
+
|
|
815
|
+
**Time.** A frame is a function of the time, in playback, a seek and an export:
|
|
816
|
+
- `register3D()` turns off the self-updating of models and animation players (`autoUpdate`); anything else under the root that still updates itself on PixiJS's ticker (a particle system, an `autoUpdate: true` you passed) is turned off after `setup`, with one warning.
|
|
817
|
+
- **A model's clip:** pose it in `update`: `model.player.getState('Run').gotoAndStop(t % clip.duration)`. A clip left playing (a `Model3D` made with `autoPlay`) and posed by nobody is played on the layer's time by the layer (its speed and loop kept).
|
|
818
|
+
- PixiJS 3D's shader time `uTime` reads the layer's time (it is the wall clock in PixiJS 3D itself).
|
|
819
|
+
- `update` that calls `Math.random`, `Date.now`, `performance.now` or `new Date` warns at build; `check` draws the layer at four moments in two orders and reports a layer whose same frame is not the same picture (state kept from call to call, a particle system or a `MotionBlurEffect`, which reads the frame drawn before).
|
|
820
|
+
- Particles (`ParticleSystem3D`): give a `seed`; their births add up from frame to frame, so a seek does not reproduce them: drive a `ParticleContainer3D` from `t` instead, or keep particles in 2D (`particles()`).
|
|
821
|
+
|
|
822
|
+
**Look.** PixiJS 3D ignores `alpha` and `blendMode` on its view; the layer applies them (and a parent composition's alpha) through one filter of its own, added only while the layer is not at full strength or blends (`add`, `screen`, `multiply`; the other modes work as on any layer). `tint` does nothing on a scene3d layer (it warns): colour the materials, or use a filter. The two backends draw nearly the same scene (measured 2026-11-13 on one Apple machine: a mean difference of 0.03–0.04 of 255 between WebGL and WebGPU for a lit cube), but they are separate pipelines in PixiJS 3D and are not required to match.
|
|
823
|
+
|
|
824
|
+
### Living in the composition's space: `followCamera`, `followLights`, `followFog`
|
|
825
|
+
|
|
826
|
+
With `followCamera: true` the view's camera is put where the composition's `camera` layer is, every frame (position, look-at, vertical fov), so a `cameraPath()` flies through flat `threeD` layers and the 3D scene together. The scene is in **composition pixels with y flipped**: a point the composition calls (x, y, z) is (x, −y, z) in the scene; +z is toward the viewer in both. Give the layer the composition's size (the default; another shape warns). Measured 2026-11-13: planes of the scene land on the composition's own cards within 0.02 of 255 at 1280 × 720.
|
|
827
|
+
|
|
828
|
+
`followLights: true` makes the composition's `light` layers PixiJS 3D lights in the scene (ambient, point, spot, parallel; position, look-at, colour, intensity, cone, falloff), moved every frame; a light that ends is switched off. `followFog: true` makes the camera's fog the view's linear fog. `ctx.space` holds the raw numbers (camera, lights, fog) before `update` runs.
|
|
829
|
+
|
|
830
|
+
- **Brightness differs from the cards.** PixiJS 3D lights in linear light; the composition's cards shade the sRGB values. Positions, directions, cones and falloff match; a 3D surface is brighter in the shade (ambient 0.3: a card is 0.3 × its colour, a 3D surface 0.58 ×). Measured mean against the cards: ambient 7.2–7.7, parallel 2.6–2.8, spot 1.7–2.0, point 1.1–1.2 of 255. Pick intensities by looking.
|
|
831
|
+
- **Falloff.** `none` is constant over the scene; `inverseSquare` is exact beyond the radius (0.71 at the radius where the composition holds 1); `smooth` is fitted (largest difference 0.10–0.12 of full light for ordinary falls, 0.20–0.28 for a fall that is steep against its radius).
|
|
832
|
+
- **Shadows.** A `parallel` or `spot` light with `castsShadows` casts in the scene; meshes cast and receive by default in PixiJS 3D (`castShadow` / `receiveShadow` on a mesh to change it). A point light casts no shadow in PixiJS 3D (it warns). `shadowDarkness` / `shadowDiffusion` are not mirrored (warns once); the shadow box of a parallel light is the whole composition, and a spot light's shadow reaches far beyond it (PixiJS 3D's defaults are for a scene of a few units: in pixels they lost every shadow).
|
|
833
|
+
- **Fog** fades by smoothstep (the composition: linearly) and mixes in linear light: measured mean 1.3–3.5 of 255 against the cards; `fogAmount` below 1 warns (no amount in PixiJS 3D).
|
|
834
|
+
- With no light layer alive, the scene's own lights stay as they are.
|
|
835
|
+
|
|
836
|
+
### Picking: `movie.pick` into the scene
|
|
837
|
+
|
|
838
|
+
`movie.pick(x, y)` on a scene3d layer returns `kind: 'scene3d'` and `scene3d: { object, point, distance, node, grab }`: what the ray through that canvas point meets first, as the frame on screen shows it. `object` is the node's `label` (or its nearest labelled ancestor's: label a group to name a model); `point` is in the scene's units. `hit.scene3d.grab('floor' | 'wall' | 'screen' | [nx, ny, nz]).move(x, y)` gives where the pointer's ray meets a plane through the hit point and the `delta` to move the object by (recipes `pick-click`, `drag-drop`, `drag-3d` work the same way). The layer makes its meshes pickable per triangle (`eventMode: 'static'`, `hitVolume: 'geometry'`) unless you set your own. Skinned and morphed meshes are picked in their rest pose. `check --at T --pick X,Y` prints `world (scene3d: red)`. PixiJS 3D's own pointer events also work in a playing page (`mesh.eventMode = 'static'; mesh.on('pointerdown' | 'pointerover' | 'pointerout', …)` in `setup`; set `hitVolume = 'geometry'` too, or it hits the bounding box), for the frame on screen like `movie.pick`. Prefer `movie.pick` / `movie.hover`: one vocabulary for every layer, and `check --pick` can test it without a pointer.
|
|
839
|
+
|
|
840
|
+
### A 2D layer on a mesh: `layerTexture`
|
|
841
|
+
|
|
842
|
+
`const screen = await layerTexture(ctx, 'screen')` in `setup`, then `screen.material` on a mesh: the 2D layer named `screen` (a `composition` listed **before** the scene3d layer) is shown on it, upright on a `PlaneGeometry` and on a `CubeGeometry`'s faces. The 2D layer stays a layer of the movie (its timeline, keyframes, filters); it is drawn into a PixiJS texture at every frame (measured: identical to the 2D layer drawn flat, mean 0.000 of 255) and is no longer drawn on the stage. Options: `resolution` (2 for a screen seen large), `transparent` (its empty parts let the scene through). A pick on the mesh names the 2D layer under the point (`via` says which mesh). An unlit material: a screen shines by itself.
|
|
843
|
+
|
|
844
|
+
### glTF models: `loadModel`, `fitCamera`
|
|
845
|
+
|
|
846
|
+
`const model = await loadModel('model.glb', { animation, speed, offset, loop })` in `setup`: `model.object` (a group centred on the model's box: add it to `root`, place, turn and scale it about its centre), `model.clips` (the clip names), `model.size()`, `model.fit(n)` (the longest side n units: n px under `followCamera`), `model.pose(t)` (the clip at `t × speed + offset`, looped or held: call it in `update`). `animation` is a clip name, an index or `false` (the bind pose); default the first clip. `fitCamera(ctx, model.object, { direction, margin })` puts the camera where the whole model fits (a three-quarter view by default). Recipes `scene3d-basics` and `scene3d-in-space`.
|
|
847
|
+
|
|
848
|
+
### Keeping the scene across rebuilds: `reuse`
|
|
849
|
+
|
|
850
|
+
A rebuild builds the new composition while the old one plays. A named scene3d layer whose `setup`, size and options did not change takes over the old layer's scene at the swap: `setup` (and a model's loading) runs once, and the objects it made are the same objects. The view itself is a new one (a PixiJS display object has one parent, and the old tree draws until the swap); what `setup` did to it (background, environment, exposure, tone mapping, post effects, shadow settings, the camera it switched to) moves with the scene. Use `ctx.view` (or `objects.view`), not a `view` kept from `setup`. A rebuild that fails leaves the old layer drawing. Known limit: the new composition's `initial` values on `scene3d.<object>.<path>` are written to the shared objects while it is built, before the swap, and stay if the rebuild then fails (it matters only when such an initial value comes from a variable). `reuse: false` builds the layer again every time.
|
|
851
|
+
|
|
738
852
|
## three
|
|
739
853
|
|
|
740
|
-
Composites a [three.js](https://threejs.org/) scene as a normal layer. Each frame, the scene is rendered into an offscreen WebGL canvas and uploaded into the sequence's PIXI texture — so once built, the layer is a plain sprite: 2D `initial` / keyframe props, [masks](#masks), and [filters](#filters) all apply to it exactly like any other sequence type.
|
|
854
|
+
The sub 3D engine. Choose it over [`scene3d`](#scene3d) for three.js's own ecosystem (its loaders, materials, shaders and post-processing) or a scene you already have in three.js. Composites a [three.js](https://threejs.org/) scene as a normal layer. Each frame, the scene is rendered into an offscreen WebGL canvas and uploaded into the sequence's PIXI texture — so once built, the layer is a plain sprite: 2D `initial` / keyframe props, [masks](#masks), and [filters](#filters) all apply to it exactly like any other sequence type.
|
|
741
855
|
|
|
742
856
|
`type: 'three'` lives in the optional `pixi-effects/three` entry, not core `pixi-effects` — see [API reference](./api.md#pixi-effectsthree) for install/import details.
|
|
743
857
|
|