greenlight-motion 0.3.4 → 0.4.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/README.md +15 -10
- package/dist/bundle-inputs.json +23 -1
- package/dist/greenlight-motion-script.mjs +549 -80
- package/dist/greenlight-motion.js +1078 -747
- package/dist/greenlight-motion.mjs +42670 -40592
- package/dist/script-inputs.json +3 -1
- package/package.json +1 -1
- package/schema/capabilities.json +1 -1
- package/skill/SKILL.md +10 -10
- package/skill/references/scene-script.md +2 -2
- package/skill/references/video-3d.md +107 -100
- package/skill/references/video-backgrounds.md +89 -41
- package/skill/references/video-camera.md +158 -83
- package/skill/references/video-design.md +39 -7
- package/skill/references/video-effects.md +936 -620
- package/skill/references/video-grid.md +24 -24
- package/skill/references/video-keyframes.md +145 -52
- package/skill/references/video-particles.md +48 -29
- package/skill/references/video-paths.md +69 -65
- package/skill/references/video-projects.md +159 -89
- package/skill/references/video-shapes.md +94 -31
- package/skill/references/video-text-kinetic.md +78 -27
- package/skill/references/video-text-presets.md +738 -17
- package/skill/references/video-text.md +171 -79
- package/skill/references/video-transitions.md +72 -17
package/skill/SKILL.md
CHANGED
|
@@ -18,7 +18,7 @@ You never write UI or animation code. If something is not in the format, it is n
|
|
|
18
18
|
## Setup (once per machine)
|
|
19
19
|
|
|
20
20
|
```
|
|
21
|
-
npx greenlight-motion@0.
|
|
21
|
+
npx greenlight-motion@0.4.0 doctor
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
It checks Node 18+, Chrome or Edge, WebGPU and H.264 encoding, and prints a fix for anything missing. Do not install
|
|
@@ -39,7 +39,7 @@ animation presets (the same converter the GreenLight Motion connector uses in Cl
|
|
|
39
39
|
keyframes rarely look as good. The format, the design rules and the catalogues: `skill/references/scene-script.md`.
|
|
40
40
|
|
|
41
41
|
```
|
|
42
|
-
npx greenlight-motion@0.
|
|
42
|
+
npx greenlight-motion@0.4.0 script <folder> --from scenes.json # or write <folder>/script.json, then: script <folder>
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
Then `check` and `stills` (below), fix the SCRIPT and run `script` again — it rewrites `project.json`. A scene's
|
|
@@ -85,14 +85,14 @@ What the engine does there, so you don't learn it from a broken frame:
|
|
|
85
85
|
|
|
86
86
|
## Workflow
|
|
87
87
|
|
|
88
|
-
1. `npx greenlight-motion@0.
|
|
88
|
+
1. `npx greenlight-motion@0.4.0 init <folder>` — starts from a working example (title, subtitle, accent line, four
|
|
89
89
|
options). `--from file.json` starts from a project you have (for example one exported from GreenLight Dash).
|
|
90
90
|
2. Edit `<folder>/project.json` — for changes to an EXISTING project prefer `apply` (below): it checks every edit and
|
|
91
91
|
applies all or nothing. Put pictures, video, sound and fonts in `<folder>/assets/` and refer to them as
|
|
92
92
|
`"asset:<file>"` (e.g. `"url": "asset:product.png"`).
|
|
93
|
-
3. `npx greenlight-motion@0.
|
|
93
|
+
3. `npx greenlight-motion@0.4.0 check <folder>` — fix everything it lists (missing files, template mistakes, blank
|
|
94
94
|
frames, page errors), and read its notes: text the engine wraps where you did not break it. `--json` for machine output.
|
|
95
|
-
4. `npx greenlight-motion@0.
|
|
95
|
+
4. `npx greenlight-motion@0.4.0 stills <folder>` — renders frames and **a labelled contact sheet**. Open and look at
|
|
96
96
|
it before you go on: layout, readability, timing. Fix and repeat. `--at 0.5,1.2,3` for exact times.
|
|
97
97
|
5. Hand it over: tell the user to open `<folder>/index.html` (Chrome or Edge), or `pack` it into one file, or
|
|
98
98
|
`render` the MP4 yourself (`--fps 60 --motion-blur` for fast motion).
|
|
@@ -157,7 +157,7 @@ editor's own normalizer), so write only what you mean.
|
|
|
157
157
|
Full references (generated from the editor's own docs; ignore the `/api/` routes they mention):
|
|
158
158
|
`skill/references/` — video-design, scene-script, video-projects, video-text, video-text-kinetic, video-text-presets, video-shapes, video-keyframes,
|
|
159
159
|
video-camera, video-3d, video-effects, video-transitions, video-particles, video-paths, video-backgrounds, video-grid.
|
|
160
|
-
Machine-readable lists: `npx greenlight-motion@0.
|
|
160
|
+
Machine-readable lists: `npx greenlight-motion@0.4.0 schema effects | transitions | easings | paths | expressions`.
|
|
161
161
|
|
|
162
162
|
## Template
|
|
163
163
|
|
|
@@ -204,7 +204,7 @@ an absolute moment; `align` `sync` (default) / `start` / `end`; `offset` seconds
|
|
|
204
204
|
|
|
205
205
|
The library is 77 hand-picked sounds in categories (whooshes and swooshes, transitions, clicks, pop-ups and
|
|
206
206
|
notifications, data blips, typing and writing, foley, bubbles, impacts and bass drops, camera, gears, voice clips).
|
|
207
|
-
`npx greenlight-motion@0.
|
|
207
|
+
`npx greenlight-motion@0.4.0 schema sounds` lists every one with its `category`, `use` (what it is for), `duration`
|
|
208
208
|
and `sync`. Pick by `category` and `use`, then by name.
|
|
209
209
|
|
|
210
210
|
- **Where the files come from:** the GreenLight sound CDN. `build` downloads each sound a cue uses into
|
|
@@ -220,7 +220,7 @@ The page has **Notes**: the user marks what should change right on the frame (a
|
|
|
220
220
|
and writes a note for each mark. Every note carries the moment (seconds and frame), the mark in composition pixels
|
|
221
221
|
and the layers under it (item ids in project.json), so you change exactly that and nothing else.
|
|
222
222
|
|
|
223
|
-
- **They review on this computer:** run `npx greenlight-motion@0.
|
|
223
|
+
- **They review on this computer:** run `npx greenlight-motion@0.4.0 review <folder>` and give them the URL it prints
|
|
224
224
|
(it keeps running; `--open` opens their browser, only when they ask). Their notes land in `<folder>/review/`: read
|
|
225
225
|
`review/NOTES.md`, look at the `at-<t>s.png` pictures (the marks are numbered), make the changes (`apply`), `build`
|
|
226
226
|
again and tell them to reload the page.
|
|
@@ -249,11 +249,11 @@ that fails (`plugin`); look at `stills` for the look. A package that carries its
|
|
|
249
249
|
|
|
250
250
|
The same project opens in GreenLight Dash (the desktop editor this engine comes from) for full editing: the page's
|
|
251
251
|
edit note offers **Open in GreenLight Dash** (it imports the folder as a new project on the open board; a page with
|
|
252
|
-
changes saves a `.glvideo`). `npx greenlight-motion@0.
|
|
252
|
+
changes saves a `.glvideo`). `npx greenlight-motion@0.4.0 pack <folder> --glvideo` writes that file yourself: ONE
|
|
253
253
|
`.glvideo` (the project + its files) that opens in GreenLight Dash on a double-click, or dropped on its board. Hand it
|
|
254
254
|
over when the user wants to keep editing in the app. A
|
|
255
255
|
GreenLight Dash project comes the other way through the app's **Export for agents** — a ready folder like the one
|
|
256
|
-
`init` makes, with the options the author ticked. `npx greenlight-motion@0.
|
|
256
|
+
`init` makes, with the options the author ticked. `npx greenlight-motion@0.4.0 init <folder> --from <its project.json>`
|
|
257
257
|
starts from one too. Never tell the user they must install it: the page plays, changes options and renders on its own.
|
|
258
258
|
|
|
259
259
|
## Files
|
|
@@ -112,7 +112,7 @@ the first version and check every version against it.
|
|
|
112
112
|
|
|
113
113
|
## Catalogues
|
|
114
114
|
|
|
115
|
-
**Text animations** (`textAnimation`; ★ = preferred): ★ `glide-text` Glide Text · `glide-continuous` Glide Continuous · `smart-overshoot` Smart Overshoot · `glide-soft-stop` Glide Soft Stop · ★ `ember-type` Ember Type · `molten-edge` Molten Edge · `focus-type` Focus Type · `tumble-type` Tumble Type · `flap-down` Flap Down · `drop-swell` Drop Swell · `rain-type` Rain Type · `counter-sweep` Counter Sweep · `sunset-shrink` Sunset Shrink · `boomerang` Boomerang · ★ `swing-pop` Swing Pop · ★ `word-swing` Word Swing · ★ `letter-vortex` Letter Vortex · `center-burst` Center Burst · ★ `pendulum` Pendulum · `spin-pop` Spin Pop · `domino` Domino · `flip-pop` Flip Pop · `card-turn` Card Turn · `slam-zoom` Slam Zoom · `orbit` Orbit · ★ `fan-deal` Fan Deal · ★ `whip` Whip · `rubber-letters` Rubber Letters · `pop-letters` Pop Letters · `drop-swing` Drop Swing · `coin-spin` Coin Spin · `converge` Converge · `roll-in` Roll In · `door-swing` Door Swing · `pill-type` Pill Type · `card-type` Card Type · `word-bars` Word Bars · `line-stack` Word Stack · `marker-type` Marker Type · `card-erase` Card Erase · `colour-sweep` Colour Sweep · `glow-pass` Glow Pass · `wave-pass` Wave Pass · `pop-through` Pop Through · `shine` Shine · ★ `marker` Marker · ★ `outline-pass` Outline Pass · ★ `neon-flow` Neon Flow · `rainbow` Rainbow · `gradient-flow` Gradient Flow · `jitter` Jitter · `float` Float · `flicker` Flicker · `breathe` Breathing Wave · `shimmer` Shimmer · `decode` Decode · `binary` Binary Decode · `alphabet-roll` Alphabet Roll · ★ `glitch` Glitch · `classic-fade-up` Fade Up · `classic-fade-down` Fade Down · `classic-rise-mask` Rise (Mask) · `classic-blur-in` Blur In · `classic-slide-in` Slide In · `classic-side-mask` Side Mask · `classic-tilt-drop` Tilt Drop · `classic-flip-up` Flip Up · `classic-typewriter` Typewriter · `classic-wave-rise` Wave Rise · `classic-bounce-letters` Bounce Letters · `classic-materialize` Materialize · `classic-neon-runner` Neon Runner · `classic-ink-highlight` Ink Highlight · `classic-flash-type` Flash Type · `classic-karaoke-sweep` Karaoke Sweep · `classic-chase-loop` Chase Loop · `classic-sung-words` Sung Words · `classic-marker-lines` Marker Words · `classic-pill-grow` Pill Grow · `classic-lower-third` Lower Third · `classic-drop-bar` Drop Bar · `classic-outline-tag` Outline Tag · `classic-prompt-field` Prompt Field · `classic-random-pop` Random Pop · `classic-chaos-letters` Chaos Letters · `classic-random-rise` Random Rise · `classic-ransom-note` Ransom Note · `classic-editorial-typer` Editorial Typer
|
|
115
|
+
**Text animations** (`textAnimation`; ★ = preferred): ★ `glide-text` Glide Text · `glide-continuous` Glide Continuous · `smart-overshoot` Smart Overshoot · `glide-soft-stop` Glide Soft Stop · ★ `ember-type` Ember Type · `molten-edge` Molten Edge · `focus-type` Focus Type · `tumble-type` Tumble Type · `flap-down` Flap Down · `drop-swell` Drop Swell · `rain-type` Rain Type · `counter-sweep` Counter Sweep · `sunset-shrink` Sunset Shrink · `boomerang` Boomerang · ★ `swing-pop` Swing Pop · ★ `word-swing` Word Swing · ★ `letter-vortex` Letter Vortex · `center-burst` Center Burst · ★ `pendulum` Pendulum · `spin-pop` Spin Pop · `domino` Domino · `flip-pop` Flip Pop · `card-turn` Card Turn · `slam-zoom` Slam Zoom · `orbit` Orbit · ★ `fan-deal` Fan Deal · ★ `whip` Whip · `rubber-letters` Rubber Letters · `pop-letters` Pop Letters · `drop-swing` Drop Swing · `coin-spin` Coin Spin · `converge` Converge · `roll-in` Roll In · `door-swing` Door Swing · `title-push-3d` Title Push 3D · `perspective-type` Perspective Type · `rack-focus` Rack Focus · `stand-up` Stand Up · `strata` Strata · `spotlight` Spotlight · `ripple` Ripple · `pill-type` Pill Type · `card-type` Card Type · `word-bars` Word Bars · `line-stack` Word Stack · `marker-type` Marker Type · `card-erase` Card Erase · `colour-sweep` Colour Sweep · `glow-pass` Glow Pass · `wave-pass` Wave Pass · `pop-through` Pop Through · `shine` Shine · ★ `marker` Marker · ★ `outline-pass` Outline Pass · ★ `neon-flow` Neon Flow · `rainbow` Rainbow · `gradient-flow` Gradient Flow · `jitter` Jitter · `float` Float · `flicker` Flicker · `breathe` Breathing Wave · `shimmer` Shimmer · `decode` Decode · `binary` Binary Decode · `alphabet-roll` Alphabet Roll · ★ `glitch` Glitch · `classic-fade-up` Fade Up · `classic-fade-down` Fade Down · `classic-rise-mask` Rise (Mask) · `classic-blur-in` Blur In · `classic-slide-in` Slide In · `classic-side-mask` Side Mask · `classic-tilt-drop` Tilt Drop · `classic-flip-up` Flip Up · `classic-typewriter` Typewriter · `classic-wave-rise` Wave Rise · `classic-bounce-letters` Bounce Letters · `classic-materialize` Materialize · `classic-neon-runner` Neon Runner · `classic-ink-highlight` Ink Highlight · `classic-flash-type` Flash Type · `classic-karaoke-sweep` Karaoke Sweep · `classic-chase-loop` Chase Loop · `classic-sung-words` Sung Words · `classic-marker-lines` Marker Words · `classic-pill-grow` Pill Grow · `classic-lower-third` Lower Third · `classic-drop-bar` Drop Bar · `classic-outline-tag` Outline Tag · `classic-prompt-field` Prompt Field · `classic-random-pop` Random Pop · `classic-chaos-letters` Chaos Letters · `classic-random-rise` Random Rise · `classic-ransom-note` Ransom Note · `classic-editorial-typer` Editorial Typer
|
|
116
116
|
|
|
117
117
|
**Entrances** (`enter`): `slide` Slide · direction · `glide` Glide (in and out) · direction · `glide-stop` Glide Stop (in and out) · direction · `glide-floaty` Glide Floaty (in and out) · direction · `glide-endless` Glide Endless (in and out) · direction · `glide-diagonal` Glide Diagonal (in and out) · direction · `glide-arc` Glide Arc (in and out) · direction · `glide-close-pass` Glide Close Pass (in and out) · direction · `glide-fade-drift` Glide Fade Drift (in and out) · direction · `glide-bank` Glide Bank (in and out) · direction · `glide-swing-3d` Glide Swing 3D (in and out) · direction · `glide-linger` Glide Linger (in and out) · direction · `glide-soft-entry` Glide Soft Entry (in and out) · direction · `glide-blur` Glide Blur (in and out) · direction · `glide-spin-through` Glide Spin Through (in and out) · direction · `glide-depth-pass` Glide Depth Pass (in and out) · direction · `glide-tilt` Glide Tilt (in and out) · direction · `zoom-in` Zoom In · `mockup-close-up` Mockup Close-Up · `mockup-orbit-left` Orbit Left · `deck-drift` Deck Drift · `hero-punch` Hero Punch · `dark-spotlight` Dark Spotlight · `phone-app-swipe` Phone Swipe Reveal · `phone-app-punch` Phone App Punch In · `phone-app-tilt-drift` Phone Tilt Drift · `phone-app-cta-hold` CTA Hold Focus · `button-press` Button Press · `button-tap-flash` Tap Flash · `pop-overshoot` Pop Overshoot · `spring-in` Spring In · `rubber-wobble` Rubber Wobble · `tap-nudge` Tap Nudge · `whip-swing` Whip Swing · `flick-drop` Flick Drop · `jelly-shake` Jelly Shake · `heartbeat` Heartbeat · `impact-punch` Impact Punch · `pendulum-swing` Pendulum Swing · `loose-wiggle` Loose Wiggle · `shiver` Shiver · `float` Float · `flicker-in` Flicker In · `earthquake` Earthquake · `tumble-drop` Tumble Drop · `rubber-band` Rubber Band · `spring-twist` Spring Twist · `slingshot` Slingshot · `pendulum-in` Pendulum In · `smooth-overshoot-rise` Smooth Overshoot Rise · `spin-in` Spin In · `twist-in` Twist In · `roll-in` Roll In · `tilt-settle` Tilt Settle · `wind-up-spin` Wind-Up Spin
|
|
118
118
|
|
|
@@ -126,7 +126,7 @@ the first version and check every version against it.
|
|
|
126
126
|
|
|
127
127
|
**Zoom feels** (`camera.feel`): `studio` Studio · `calm` Calm · `precise` Precise · `lively` Lively · `follow` Follow. Full camera layers (paths, Smart Zoom on a recording's clicks, the drawn cursor): references/video-camera.md.
|
|
128
128
|
|
|
129
|
-
**Overlay effects** (`effects`): `glow`, ★ `bloom`, `auraBlur`, `gradientBeam`, ★ `gradientSweep`, `gradientGlow`, `fractalNoise`, ★ `rgbSplit`, `glitch`, `scanlines`, `flashBlink`, `vhs`, `fisheye`, `opticsCompensation`, `vignette`, ★ `echo`, `timeshift`, `radialBlur`, ★ `radialFastBlur`, `sharpen`, `mirror`, `waveWarp`, `warpStyles`, `shockwave`, `waveLines`, `waveRipple`, `fill`, `shapeRulers`, `copyColor`, `curves`, `turbulentDisplace`, `alphaChoker`, `extract`, ★ `colorama`, `posterize`, ★ `halftone`, ★ `fastCameraWiggle`, ★ `lightRays`, `longShadow`, `outlineBands`, `kaleidoscope`, `crtMatrix`, `pixelate`, `vectorFieldWarp`, `pixelStretch`, `neonGlow`, `edgeDetect`, `doubleExposure`, ★ `filmStock`, `fastBlur`, `gridLines`, `strip`, `ascii`, `objectDither`, `objectIsolines`, `objectWindow`, `objectSlitScan`, `objectCurtain`, `objectGrid`, `objectContour`, `objectNodes`, `objectLinks`, `objectFacets`, `objectMeasure`
|
|
129
|
+
**Overlay effects** (`effects`): `glow`, ★ `bloom`, `auraBlur`, `gradientBeam`, ★ `gradientSweep`, `gradientGlow`, `fractalNoise`, ★ `rgbSplit`, `glitch`, `scanlines`, `flashBlink`, `vhs`, `fisheye`, `opticsCompensation`, `vignette`, ★ `echo`, `timeshift`, `radialBlur`, ★ `radialFastBlur`, `sharpen`, `mirror`, `waveWarp`, `warpStyles`, `shockwave`, `waveLines`, `waveRipple`, `fill`, `shapeRulers`, `copyColor`, `curves`, `turbulentDisplace`, `alphaChoker`, `gooey`, `extract`, ★ `colorama`, `posterize`, ★ `halftone`, ★ `fastCameraWiggle`, ★ `lightRays`, `lightSweep`, `longShadow`, `outlineBands`, `kaleidoscope`, `crtMatrix`, `pixelate`, `vectorFieldWarp`, `pixelStretch`, `neonGlow`, `edgeDetect`, `doubleExposure`, ★ `filmStock`, `fastBlur`, `gridLines`, `strip`, `ascii`, `objectDither`, `objectIsolines`, `objectWindow`, `objectSlitScan`, `objectCurtain`, `objectGrid`, `objectContour`, `objectNodes`, `objectLinks`, `objectFacets`, `objectMeasure`
|
|
130
130
|
|
|
131
131
|
**Layer effects** (`effects`): `mockupFrame`, ★ `runningGlow`, `depthMap`, `depth3d`, `depthScan`, `fresnelRim`, ★ `motionBlur`, `posterizeTime`, ★ `motionTrail`, `autoFill`, ★ `repetile`, `regionDistort`, `bend`, `cornerPin`, `meshWarp`, `bezierWarp`, `rig`, ★ `droste`, `pryzm`, ★ `glass`, `chromaKey`, `layerMask`
|
|
132
132
|
|
|
@@ -10,32 +10,34 @@
|
|
|
10
10
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
|
-
- [3D model layers (`mediaType: "model3d"`) — a live GLB/GLTF/OBJ rendered per frame](
|
|
14
|
-
- [Scene Rig — one set for every 3D layer](
|
|
15
|
-
- [
|
|
13
|
+
- [3D model layers (`mediaType: "model3d"`) — a live GLB/GLTF/OBJ rendered per frame](#3d-model-layers-mediatype-model3d--a-live-glbgltfobj-rendered-per-frame)
|
|
14
|
+
- [Scene Rig — one set for every 3D layer](#scene-rig--one-set-for-every-3d-layer)
|
|
15
|
+
- [Turning flat layers in 3D — pivot and stacking](#turning-flat-layers-in-3d--pivot-and-stacking)
|
|
16
|
+
- [Flat layers in the 3D scene — `item.shadowPlate`](#flat-layers-in-the-3d-scene--itemshadowplate)
|
|
16
17
|
|
|
17
18
|
### 3D model layers (`mediaType: "model3d"`) — a live GLB/GLTF/OBJ rendered per frame
|
|
18
19
|
|
|
19
|
-
Unlike a device mockup (pre-baked to a video file), a `model3d` layer is a **real 3D scene rendered live every frame** by the Player (three.js, its own depth-buffered pass), then composited like any other layer. The model can be one of the bundled device presets or a custom uploaded asset
|
|
20
|
+
Unlike a device mockup (pre-baked to a video file), a `model3d` layer is a **real 3D scene rendered live every frame** by the Player (three.js, its own depth-buffered pass), then composited like any other layer. The model can be one of the bundled device presets, a primitive stand-in or a custom uploaded asset. It takes the project camera, keyframes and layer parenting like any layer. Effects work on it only in part: see [Real 3D on 3D model layers](video-effects.md#real-3d-on-3d-model-layers); layer effects marked `disabledWhen3d` in capabilities (Motion Blur, Rig (Puppet), the warps, Layer Mask…) do not run on it. It takes no transitions ([video-transitions.md](video-transitions.md)).
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
**Never leave the clip `url` empty: the layer disappears without a warning.** A `model3d` clip is loaded like a video clip, so a clip with no `url` is dropped on load and render, and every timeline item that uses it goes with it — even one carrying a complete, valid `model3dConfig`. The project still saves and a headless render still "succeeds", with the model missing from every frame. The valid url forms:
|
|
22
23
|
|
|
23
24
|
- **Preset device:** `"url": "model-preset:<presetId>"` — a stable sentinel, NOT a fetchable path. (The bundled .glb's real URL is a content-hashed build artifact that changes every release; the sentinel survives app updates.)
|
|
24
25
|
- **Primitive shape:** `"url": "model-primitive:<kind>"` — a stand-in built in code, no asset file. Pair it with `"source": "primitive"` and `"primitive": { "kind", "color", "text" }` in `model3dConfig` (see below).
|
|
25
26
|
- **Custom model:** a real uploaded asset URL ending in `.glb`, `.gltf` or `.obj` (upload via the media API, then use the returned `/media/...` url).
|
|
27
|
+
- **Gaussian splat:** a `.ply`, `.splat` or `.spz` url. Only a 3D Scene draws splats (world3d.md (GreenLight Dash only)).
|
|
26
28
|
|
|
27
29
|
**Clip + item pair (preset device):**
|
|
28
30
|
|
|
29
31
|
```json
|
|
30
32
|
// clip — url is the preset sentinel, source REMOTE, no proxy/thumbnail
|
|
31
33
|
{ "id": "m1", "source": "REMOTE", "mediaType": "model3d",
|
|
32
|
-
"url": "model-preset:
|
|
34
|
+
"url": "model-preset:iphone_pro_max", "proxyUrl": null, "name": "iPhone 17 Pro Max", "duration": 4 }
|
|
33
35
|
// item — model3dConfig holds everything about fit, look and animation
|
|
34
36
|
{ "id": "mi1", "clipId": "m1", "trimStart": 0, "trimEnd": 4,
|
|
35
37
|
"model3dConfig": {
|
|
36
|
-
"source": "preset", "preset": "
|
|
38
|
+
"source": "preset", "preset": "iphone_pro_max",
|
|
37
39
|
"fit": "height", "fitValue": 60, "anchor": "center", "placeOnRigFloor": true,
|
|
38
|
-
"baseRotation": { "x": 0, "y":
|
|
40
|
+
"baseRotation": { "x": 0, "y": 180, "z": 0 },
|
|
39
41
|
"env": { "hdr": "potsdamer_platz_1k", "intensity": 1, "exposure": 1, "rotation": 0 },
|
|
40
42
|
"lights": { "key": 1, "fill": 0.45, "rim": 0.35 },
|
|
41
43
|
"animation": { "clip": null, "speed": 1, "loop": "loop", "offset": 0 },
|
|
@@ -65,14 +67,13 @@ For a custom model use `"source": "custom", "preset": null` and point the clip `
|
|
|
65
67
|
| `primitive.color` | any CSS colour, ≤ 64 chars | `#d9d9d9` | Tints the whole shape (the car's glass and tyres keep their own colour). |
|
|
66
68
|
| `primitive.text` | string, ≤ 40 chars | `TEXT` | Only read by `kind: "text"` (extruded letters). |
|
|
67
69
|
|
|
68
|
-
On a floor-bearing rig, `placeOnRigFloor: true` stands the shape on the cyclorama; `panX` (% of comp width) and `panZ` (px, + toward the camera) place it on the floor. Fit values the editor uses for a set at comp scale: cube/sphere 24, card 26, torus 22, cone/cylinder 28, human 58, car 20, text 14. Keep `source`/`preset` consistent with the clip url: the renderer resolves the asset from `model3dConfig` first (`source: "preset"` + a valid `preset` id wins), then falls back to the clip's sentinel/url — but the clip url is what keeps the clip **alive**
|
|
70
|
+
On a floor-bearing rig, `placeOnRigFloor: true` stands the shape on the cyclorama; `panX` (% of comp width) and `panZ` (px, + toward the camera) place it on the floor. Fit values the editor uses for a set at comp scale: cube/sphere 24, card 26, torus 22, cone/cylinder 28, human 58, car 20, text 14. Keep `source`/`preset` consistent with the clip url: the renderer resolves the asset from `model3dConfig` first (`source: "preset"` + a valid `preset` id wins), then falls back to the clip's sentinel/url — but the clip url is what keeps the clip **alive** on load, so always set both sides to the same device.
|
|
69
71
|
|
|
70
|
-
**Bundled device presets
|
|
72
|
+
**Bundled device presets:**
|
|
71
73
|
|
|
72
74
|
| `preset` id | Device | Screen artwork | `baseRotation.y` to face the camera |
|
|
73
75
|
| --- | --- | --- | --- |
|
|
74
76
|
| `iphone_pro_max` | iPhone 17 Pro Max | yes | `180` |
|
|
75
|
-
| `iphone_17` | iPhone 17 | yes | `0` |
|
|
76
77
|
| `iphone_air` | iPhone Air | yes | `0` |
|
|
77
78
|
| `double_iphone` | Two iPhones | yes | `180` |
|
|
78
79
|
| `ipad` | iPad | yes | `0` |
|
|
@@ -81,18 +82,21 @@ On a floor-bearing rig, `placeOnRigFloor: true` stands the shape on the cycloram
|
|
|
81
82
|
| `macbook_air` | MacBook Air | yes | `0` |
|
|
82
83
|
| `imac` | iMac | yes | `180` |
|
|
83
84
|
|
|
84
|
-
|
|
85
|
+
`iphone_17` is an alias of `iphone_pro_max`: it loads the same model, so it also needs `baseRotation.y: 180`. Write `iphone_pro_max`.
|
|
85
86
|
|
|
86
|
-
|
|
87
|
+
Some GLBs are authored facing away from the viewer — for those, set `baseRotation.y: 180` (the last column; the editor sets it when it adds the layer). A wrong value shows the device's back.
|
|
88
|
+
|
|
89
|
+
**`model3dConfig` fields** (out-of-range values are clamped, unknown enum values fall back to the default):
|
|
87
90
|
|
|
88
91
|
| Field | Values / range | Default | Meaning |
|
|
89
92
|
| --- | --- | --- | --- |
|
|
90
|
-
| `source` | `"preset"` \| `"custom"` | `custom` | Where the geometry comes from. Anything else coerces to `custom`. |
|
|
93
|
+
| `source` | `"preset"` \| `"primitive"` \| `"custom"` | `custom` | Where the geometry comes from. Anything else coerces to `custom`. |
|
|
91
94
|
| `preset` | preset id string | `null` | Required when `source: "preset"`. An unknown id falls through to the clip url. |
|
|
92
95
|
| `fit` | `"height"` \| `"width"` \| `"unit"` | `height` | How `fitValue` sizes the model: % of frame height, % of frame width, or raw pixels-per-model-unit. |
|
|
93
96
|
| `fitValue` | `0.01–10000` | `60` | With `fit: "height"`, `60` = the model's bounding box spans 60 % of the frame height. |
|
|
94
97
|
| `anchor` | `"center"` \| `"base"` \| `"origin"` | `center` | Pivot: bbox centre, bbox floor (for objects standing on a surface), or the asset's own origin — no recentring, so a GLB exported in scene coordinates (Blender → GLEA) lands exactly where its scene put it, whatever its rest pose. |
|
|
95
98
|
| `placeOnRigFloor` | bool | `true` | With a floor-bearing Scene Rig, place the model's real post-transform lower bound on the cyclorama floor. The ordinary Y transform becomes an offset from that floor. Set `false` for free/flying placement. |
|
|
99
|
+
| `physics` | `"off"` \| `"falls"` \| `"solid"` | `off` | Rigid-body physics, only in a 3D Scene (Rigid bodies (GreenLight Dash only)): `falls` makes the model a rigid body that falls, hits and stacks from where the layer stands; `solid` a fixed one others hit. The classic renderer ignores it. |
|
|
96
100
|
| `castShadow` | bool | `true` | With a Scene Rig casting real shadows: the model is in the shadow map. `false` = it casts nothing (a huge set piece that should only receive). Blended parts of a model (glTF BLEND — a baked contact-shadow card, a glow) never cast either way. Only stored when `false`. |
|
|
97
101
|
| `receiveShadow` | bool | `true` | With a Scene Rig: rig shadows darken the model. `false` = they never land on it. Only stored when `false`. |
|
|
98
102
|
| `baseRotation` | `{x,y,z}` deg, `±360` | `{0,0,0}` | Static orientation applied before keyframed rotation. |
|
|
@@ -101,7 +105,7 @@ Some GLBs are authored facing away from the viewer — for those, set `baseRotat
|
|
|
101
105
|
| `env.exposure` | `0.05–8` | `1` | Tone-mapping exposure of the model pass. |
|
|
102
106
|
| `env.rotation` | `±360` deg | `0` | Spins the environment (moves reflections). |
|
|
103
107
|
| `lights.key/fill/rim` | `0–10` each | `1 / 0.45 / 0.35` | The three directional lights' intensities. **Accepted, normalised on read** — see "Scene Rig" below. Still the right shape to emit when a project has no rig layer; ignored on every model once one exists. |
|
|
104
|
-
| `animation.clip` | glTF animation name \| `"*"` \| `null` | `null` | Plays the named animation baked into the asset; `"*"` plays EVERY clip together, each on its own length (Blender's exporter writes one clip per animated object); `null` = static. Names must match the glTF exactly. `loop: "once"` holds the last frame. A machine running or a device coming apart is made as such a GLB:
|
|
108
|
+
| `animation.clip` | glTF animation name \| `"*"` \| `null` | `null` | Plays the named animation baked into the asset; `"*"` plays EVERY clip together, each on its own length (Blender's exporter writes one clip per animated object); `null` = static. Names must match the glTF exactly. `loop: "once"` holds the last frame. A machine running or a device coming apart is made as such a GLB: mechanisms.md (GreenLight Dash only). |
|
|
105
109
|
| `animation.speed` | `-8–8` | `1` | Playback rate (negative = reversed). |
|
|
106
110
|
| `animation.loop` | `"once"` \| `"loop"` \| `"pingpong"` | `loop` | Loop mode. |
|
|
107
111
|
| `animation.offset` | `±600` s | `0` | Start offset into the animation. |
|
|
@@ -131,17 +135,19 @@ Some GLBs are authored facing away from the viewer — for those, set `baseRotat
|
|
|
131
135
|
}]
|
|
132
136
|
```
|
|
133
137
|
|
|
134
|
-
A `screen`-shaded mesh shows the media in **its own colours**: its pixels skip the 3D tone curve (a depth-tested coverage mask, so anything in front still hides it), so a UI on a slab matches the same file as a 2D layer. Being self-lit it takes no light — and so no shadow: a character walking on a page or a UI looks as if it floats. Set `receiveShadow: true` on such a screen and the Scene Rig's real shadows darken it where they land (by the casting lights' share of the set's light), while every other pixel keeps the media's exact colours. Use it for any screen something stands on or passes over. Videos are driven by the layer clock and are **frame-exact in exports and stills** for MP4/MOV (
|
|
138
|
+
A `screen`-shaded mesh shows the media in **its own colours**: its pixels skip the 3D tone curve (a depth-tested coverage mask, so anything in front still hides it), so a UI on a slab matches the same file as a 2D layer. Being self-lit it takes no light — and so no shadow: a character walking on a page or a UI looks as if it floats. Set `receiveShadow: true` on such a screen and the Scene Rig's real shadows darken it where they land (by the casting lights' share of the set's light), while every other pixel keeps the media's exact colours. Use it for any screen something stands on or passes over. Videos are driven by the layer clock and are **frame-exact in exports and stills** for MP4/MOV (the device `screen` uses the same path). WebM falls back to a seeked `<video>` (best effort).
|
|
135
139
|
|
|
136
|
-
**Transforms & keyframes.** The item's ordinary transform fields and `propertyKeyframes` (`scale`, `xPercent`/`yPercent`, `rotateX`/`rotateY`/`
|
|
140
|
+
**Transforms & keyframes.** The item's ordinary transform fields and `propertyKeyframes` (`scale`, `xPercent`/`yPercent`, `translateZ`, `rotateX`/`rotateY`/`rotate`, `opacity`, …) are applied **inside the 3D pass** — a keyframed `rotateY` really orbits the object in 3D rather than spinning a flat card. The project camera films model layers like everything else (`cameraLocked` on the channel opts out).
|
|
137
141
|
|
|
138
|
-
**Look.** The 3D pass is tone-mapped with **AgX** (the curve Blender renders with
|
|
142
|
+
**Look.** The 3D pass is tone-mapped with **AgX** (the curve Blender renders with) plus a GLEA look (×1.35 gain and 1.2 saturation) in its own pass, so models, the Scene Rig and real-3D effects share one filmic curve while 2D layers are untouched. A 3D Scene takes its film look from `rig.look` and `rig.exposure` (world3d.md (GreenLight Dash only)).
|
|
139
143
|
|
|
140
144
|
**Rendering.** Model layers render in the editor preview, the desktop Download, **`/render-onserver`** / **`/render-onserver-job`**, and **`/render-still`**. The fast `/render` does **not** support them (video/image only). Very heavy assets (≳900 k triangles) render slowly enough that an export can look hung — prefer decimated models.
|
|
141
145
|
|
|
142
146
|
### Scene Rig — one set for every 3D layer
|
|
143
147
|
|
|
144
|
-
|
|
148
|
+
> **Every Rig is a 3D Scene**, drawn by the world renderer unless `rig.renderer` is `"classic"` (or the machine has no WebGPU, below): the backdrop, the Rig's lights and environment and the model layers become one lit, shadowed world, path traced in Final 3D, and you can build in it yourself, with no Blender, by writing `rig.scene` (world3d.md (GreenLight Dash only)). The fields below hold in both; where the world reads them differently, the section says so.
|
|
149
|
+
|
|
150
|
+
A **rig** clip carries the project's lights, backdrop and environment. It is a singleton, like the camera: `mediaType: "rig"` on a channel with `kind: "rig"`, and the item carries `item.rig`. **When a rig exists it lights every model layer** and `model3dConfig.lights` is ignored; with no rig, each model keeps its own three lights. Adding a rig is therefore additive — it never changes an existing project until you add one.
|
|
145
151
|
|
|
146
152
|
```jsonc
|
|
147
153
|
"rig": {
|
|
@@ -150,7 +156,7 @@ A **rig** clip carries the project's lights, backdrop and environment. It is a s
|
|
|
150
156
|
"width": 4, "height": 2.25, "depth": 3, // MULTIPLES OF COMP HEIGHT, not px
|
|
151
157
|
"curve": 0.35, // floor→wall sweep, as a fraction of height
|
|
152
158
|
"x": 0, "y": 0, "z": 0, "rotX": 0, "rotY": 0, "rotZ": 0, "scale": 1,
|
|
153
|
-
"material": { "mode": "solid", // solid | shadowCatcher
|
|
159
|
+
"material": { "mode": "solid", // solid | shadowCatcher | image (a picture, `imageUrl`)
|
|
154
160
|
"color": "#d8d8d8", "roughness": 0.8,
|
|
155
161
|
"metalness": 0, "opacity": 1,
|
|
156
162
|
"receiveShadow": true, "castShadow": false }
|
|
@@ -168,17 +174,22 @@ A **rig** clip carries the project's lights, backdrop and environment. It is a s
|
|
|
168
174
|
}
|
|
169
175
|
```
|
|
170
176
|
|
|
171
|
-
**
|
|
177
|
+
**Which renderer draws it.** On a machine with WebGPU, every Rig is drawn by the **world renderer** (world3d.md (GreenLight Dash only)) unless `rig.renderer` is `"classic"`; without WebGPU it falls back to the classic Rig passes. Both read the same `rig`, with these differences:
|
|
178
|
+
|
|
179
|
+
- The world renderer ignores the `shadows` block (`mode`, `maxCasters`) and a light's `shadowBias`, `shadowNormalBias` and `shadowMapSize`: a light casts when its own `castShadow` is true. The shadow-map paragraphs below (**Shadows**, **The shadow map follows the camera**, the area light's companion caster) describe the classic renderer.
|
|
180
|
+
- `shadowSoftness` is the light's size in the world renderer (see the light fields below); in the classic renderer it widens the shadow-map penumbra.
|
|
181
|
+
|
|
182
|
+
**Shadows (classic renderer).** `mode: "map"` gives real shadow maps: a model casts onto the cyclorama and
|
|
172
183
|
onto other models. A light only casts when it ALSO has `castShadow: true` — the mode
|
|
173
184
|
alone does nothing. `maxCasters` (0-4, default 2) caps how many casting lights are
|
|
174
185
|
honoured, because each one is a full extra render of the set every frame; lights past
|
|
175
186
|
the cap are silently not casting, in array order. The cyclorama receives by default
|
|
176
187
|
(`cyclorama.material.receiveShadow`) and does not cast. `mode: "contact"` keeps only
|
|
177
188
|
the cheap soft blob under each model standing on the rig floor (`placeOnRigFloor`).
|
|
178
|
-
A
|
|
179
|
-
|
|
189
|
+
A rig you write gets exactly what you write. In a 3D Scene none of this applies: every light with
|
|
190
|
+
`castShadow: true` casts (see **Which renderer draws it** above).
|
|
180
191
|
|
|
181
|
-
**The shadow map follows the camera.** Each frame a casting light's map is fitted to
|
|
192
|
+
**The shadow map follows the camera (classic renderer).** Each frame a casting light's map is fitted to
|
|
182
193
|
what the camera actually sees of the set (the frustum clipped to the receivers, in the
|
|
183
194
|
casters' reach), in 1/8-octave size steps with a texel-snapped centre so a moving
|
|
184
195
|
camera does not make shadow edges crawl. A small character on a long set (a ten-metre
|
|
@@ -189,38 +200,76 @@ override it with `shadowBias` / `shadowNormalBias` (0–20, in texels of its map
|
|
|
189
200
|
them out for automatic — raise them if a surface still shows stripes, lower them if a
|
|
190
201
|
shadow looks detached).
|
|
191
202
|
|
|
192
|
-
**One shared world, but only when a rig exists.** With
|
|
193
|
-
in one scene so the shadow map can contain every caster
|
|
194
|
-
its own pass, so opacity, blend modes, masks, effects and DOF are
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
occluding shadow plate hands the compositor its depth buffer, and every pixel blurs by
|
|
203
|
-
the CoC of its OWN distance (`camAperture · |camFocusDistance − d| / d · camBlurLevel/100`,
|
|
204
|
-
capped at 64 px, still held off inside the focus area) — a character in focus stays
|
|
205
|
-
sharp in the same model as a wall metres behind it. The gather is depth-aware: a
|
|
206
|
-
blurred background never smears over a sharp foreground edge, a sharp foreground
|
|
207
|
-
leaves no halo on the blurred background, and a foreground in front of the focus plane
|
|
208
|
-
blurs out over what is behind it and past its own silhouette. Taps read the picture
|
|
209
|
-
prefiltered (mip level of the tap spacing), so thin bright detail under a wide blur —
|
|
210
|
-
lettering far behind the focus — averages instead of sparkling; 2D layers get the same.
|
|
211
|
-
A whole set (backdrop included) can therefore live in one model layer. Flat 2D layers
|
|
212
|
-
keep one CoC per layer — the depth of their plane.
|
|
213
|
-
|
|
214
|
-
**Light types and their fields.** `directional` / `point` / `spot` / `area` take `az` (±360°), `elev` (±90°), `dist` (world px); `ambient` and `hemisphere` take neither. `spot` adds `angle` (1–89°) and `penumbra` (0–1); `spot` and `area` aim at `targetX` / `targetY` / `targetZ` (world px, default the rig origin); `hemisphere` adds `groundColor`. Fields a type does not have are dropped by the sanitizer.
|
|
215
|
-
|
|
216
|
-
**`area` — a softbox.** A lit rectangle of `width` × `height` world px (default 800 × 800) at its spherical position, facing its target: broad, soft highlights and soft wrap-around light, the way a large source lights in Blender or a studio — use it for keys and fills on products, glossy and metal surfaces. Bigger at the same `intensity` = the same light on the subject, only softer. It has no shadow map of its own: with `castShadow: true` a companion light from the same place carries half its light and casts the shadow (a lighter, softer shadow, as from a large source), and counts against `shadows.maxCasters`. Set the softness to match the box: `shadowSoftness` ≈ (box size / distance to the target) ÷ 0.22.
|
|
217
|
-
|
|
218
|
-
**`intensity` means the same on every light: what the set receives.** A `point` or `spot` of `2` at `dist: 2400` (or an `area` of `2` at any size, at its target) lights the subject at the centre of the set like a `directional` of `2`, then falls off physically around it (closer is brighter, farther darker). A casting light's `shadowSoftness` (0–10, default 1) widens its penumbra, scaled by the light's distance — contact points stay sharp and the shadow softens away from them.
|
|
203
|
+
**One shared world, but only when a rig exists.** With `renderer: "classic"`, every 3D
|
|
204
|
+
layer is placed in one scene so the shadow map can contain every caster, and each layer
|
|
205
|
+
still renders in its own pass, so opacity, blend modes, masks, effects and DOF are
|
|
206
|
+
unchanged. In a 3D Scene the world draws the models, and a model it draws takes no
|
|
207
|
+
`opacity` or `blendMode`. With NO rig, each model keeps its own scene and its own
|
|
208
|
+
`model3dConfig.env`, which is why adding a rig to an existing project is additive and
|
|
209
|
+
never re-lights it by surprise. With a rig, the rig's `env` (and exposure) is the one in
|
|
210
|
+
effect for every model; key `sc:env:intensity` to change reflections over time (e.g.
|
|
211
|
+
only once a metal object takes the stage). A 3D Scene turns its environment by `rotY`
|
|
212
|
+
alone.
|
|
219
213
|
|
|
220
|
-
|
|
214
|
+
**Camera depth of field is per pixel on 3D passes** (a model, the cyclorama, an occluding shadow plate), so a whole set, backdrop included, can live in one model layer; each pixel blurs by a radius in px: [video-camera.md](video-camera.md#depth-of-field).
|
|
215
|
+
|
|
216
|
+
**Light types and their fields.** Every light has `lid`, `type`, `name`, `on`, `color` and `intensity` (0–100, default 1). `directional` / `point` / `spot` / `area` also take `az` (±360°), `elev` (±90°), `dist` (0–100000 world px, default 3000), `castShadow` (default false; obeyed in Draft and Final on every kind of light: leave it off on a fill, or it throws a second shadow) and `shadowSoftness` (0–10, default 1); `ambient` and `hemisphere` take neither. `spot` adds `angle` (1–89°, default 32) and `penumbra` (0–1, default 0.5); `point` adds `range` (world px, default 0) and `decay` (0–4, default 2, the falloff exponent); `area` adds `width` × `height` (world px, default 800 × 800) and `spread` (below); `spot` and `area` aim at `targetX` / `targetY` / `targetZ` (world px, default the rig origin); `hemisphere` adds `groundColor`. Fields a type does not have are dropped on load.
|
|
217
|
+
|
|
218
|
+
- **`castShadow`** — a fill leaves it off, or it throws a second shadow.
|
|
219
|
+
- **`shadowSoftness`** — in the world renderer it is the light's size, and the subject stays as bright: a directional light becomes a sun `1 + 2 × shadowSoftness` degrees wide, an area light's panel is scaled by it, and a spot or point lamp grows with it and with its distance. In the classic renderer a casting light's `shadowSoftness` widens its penumbra, scaled by the light's distance — contact points stay sharp and the shadow softens away from them.
|
|
220
|
+
- **`spread`** (area only, 1–180, default 180) narrows a panel to a pool of light, like a grid on a soft box. The subject stays as bright. Stored only below 180.
|
|
221
|
+
- **`texture`** (on `directional`, `spot` and `area`) is what the light shines through: `{ "pattern": "window" }` (or `blinds`, `leaves`), or an image of the user's: `{ "url": "/media/…/gobo.png", "name": "gobo.png" }`. A spot projects it in colour, in Draft and Final. A directional and an area light cast the pattern from something out of frame. An image on an area light sits on the panel itself, so it shows in reflections in Final and tints the set in Draft.
|
|
222
|
+
|
|
223
|
+
**`area` — a softbox.** A lit rectangle of `width` × `height` world px at its spherical position, facing its target: broad, soft highlights and soft wrap-around light, the way a large source lights in Blender or a studio — use it for keys and fills on products, glossy and metal surfaces. Bigger at the same `intensity` = the same light on the subject, only softer. In the classic renderer it has no shadow map of its own: with `castShadow: true` a companion light from the same place carries half its light and casts the shadow (a lighter, softer shadow, as from a large source), and counts against `shadows.maxCasters`; match the softness to the box there with `shadowSoftness` ≈ (box size / distance to the target) ÷ 0.22.
|
|
224
|
+
|
|
225
|
+
**`intensity` means the same on every light: what the set receives.** A `point` or `spot` of `2` at `dist: 2400` (or an `area` of `2` at any size, at its target) lights the subject at the centre of the set like a `directional` of `2`, then falls off physically around it (closer is brighter, farther darker).
|
|
226
|
+
|
|
227
|
+
**`lid`** matches `/^[A-Za-z0-9_-]{1,12}$/` and must be unique within the rig — it is the key keyframe tracks address.
|
|
228
|
+
|
|
229
|
+
**Sizes are multiples of composition height**, so a rig authored at 1080p is correct at 4K with no migration. `width: 4` on a 1080-tall comp is 4320 world px.
|
|
230
|
+
|
|
231
|
+
**Switch a light off with `intensity: 0` or `on: false`, never by removing it.** The light count is a compiled shader constant, so changing it recompiles every material in the scene. `on: false` turns the light off the same way and keeps its `intensity` for later.
|
|
232
|
+
|
|
233
|
+
**Cyclorama shape `"none"`** draws nothing: use it for a rig that only carries lights. Any other shape with `material.mode: "solid"` writes depth, so layers behind it are cut by its surface.
|
|
234
|
+
|
|
235
|
+
**`material.mode: "image"`** with `material.imageUrl` (a media url) puts a picture on the backdrop in the world renderer; the picture is multiplied by `color`, so leave `color` white to show its own colours. The classic renderer does not read `imageUrl`.
|
|
236
|
+
|
|
237
|
+
**`material.mode: "shadowCatcher"`** keeps only the shadows the set receives and is transparent everywhere else, for models standing on the composition `background`. It writes no depth, so it never cuts a layer. Its shadows use `material.shadowColor` (default `#000000`), not `color` — `color` stays the Solid surface's colour, so a paper set switched to shadows does not paint them in the paper's own colour. `opacity` is the shadow strength (0-1), and the backdrop colour track `scc:cyc:col` drives `shadowColor` in this mode.
|
|
238
|
+
|
|
239
|
+
**Rig keyframes** live on the rig item's `propertyKeyframes` under two prefixes (`sc:` numeric, `scc:` colour — the prefix alone decides numeric vs RGBA interpolation):
|
|
240
|
+
|
|
241
|
+
| Key | Range |
|
|
242
|
+
| --- | --- |
|
|
243
|
+
| `sc:<lid>:intensity` | 0–100 |
|
|
244
|
+
| `sc:<lid>:az` \| `elev` \| `dist` \| `shadowSoftness` | per the fields above; not on `ambient` / `hemisphere` |
|
|
245
|
+
| `sc:<lid>:angle` \| `penumbra` | spot only |
|
|
246
|
+
| `sc:<lid>:width` \| `height` \| `spread` | area only (world px; spread in degrees) |
|
|
247
|
+
| `scc:<lid>:col` | colour |
|
|
248
|
+
| `sc:cyc:x` \| `y` \| `z` \| `rotX` \| `rotY` \| `rotZ` \| `scale` \| `opacity` | — |
|
|
249
|
+
| `scc:cyc:col` | colour |
|
|
250
|
+
| `sc:env:intensity` \| `exposure` \| `rotX` \| `rotY` \| `rotZ` | — |
|
|
251
|
+
|
|
252
|
+
**Refused, and dropped on load:** light `type`, adding/removing a light, `castShadow`, `shadowMapSize`, cyclorama `shape` and dimensions, `env.source`/`preset`/`url`. Each is a shader recompile or an environment re-bake, not a value — animating one would rebuild every frame. A track naming a `lid` that no longer exists is dropped on the next save.
|
|
253
|
+
|
|
254
|
+
**The playhead outside every rig clip does NOT mean darkness.** The nearest rig still lights the scene, and a project with no rig falls back to the default three directional lights.
|
|
255
|
+
|
|
256
|
+
**24 built-in presets**, each a **complete** rig — applying one replaces `item.rig` wholesale:
|
|
257
|
+
|
|
258
|
+
| Category | Preset ids |
|
|
259
|
+
| --- | --- |
|
|
260
|
+
| Studio | `threePoint` (the same lights as a project with no rig — use it to match one), `studioRoom`, `clamshellBeauty`, `highKeyWhite` |
|
|
261
|
+
| Product | `productSoftbox`, `topDownProduct`, `turntable`, `pastelSweep`, `pianoBlack`, `cosmeticGlow`, `windowFlatlay` |
|
|
262
|
+
| Dramatic | `rimLitDark`, `filmNoir`, `stageSpotlight`, `campfireGlow`, `rimHero` |
|
|
263
|
+
| Daylight | `sunsetWindow`, `overcastExterior`, `desertNoon`, `blueHour` |
|
|
264
|
+
| Location | `warehouse`, `sciFiRoom` |
|
|
265
|
+
| Stylized | `neonTwoTone`, `duotonePop` |
|
|
266
|
+
|
|
267
|
+
**Other rig-item fields the editor writes.** `item.rigLook = { id, name, hash }` records which look was applied (a preset id, or `saved:<id>` for a look the user saved) and a hash of the rig at that moment, so the editor can mark the look as edited once the rig drifts from it. It is informational — omit it when emitting a rig; a hand-made `hash` never matches, so the look would show as edited from the start. `item.rigAi` holds the AI video state — the prerender used as the source, the takes and which one is placed on the timeline. It is written by the editor from real render and generation results; do not write it yourself.
|
|
221
268
|
|
|
222
|
-
|
|
223
|
-
|
|
269
|
+
### Turning flat layers in 3D — pivot and stacking
|
|
270
|
+
|
|
271
|
+
**The pivot.** Every flat layer (image, video, shape, text, html clip) turns in 3D
|
|
272
|
+
with the static `rotateX` / `rotateY` / `rotateZ` fields and the `rotateX` / `rotateY` / `rotate` keyframe tracks alike: a static angle and the
|
|
224
273
|
same angle reached by a key give the same pose, and mixing static and keyed channels never moves the layer. The
|
|
225
274
|
pivot is `keyframeTransformOrigin` (`"center"` default; `"top"`, `"bottom"`, `"left"`, `"right"`, the four
|
|
226
275
|
corners such as `"bottom left"`, or CSS-style `"50% 100%"`): rotation and the keyed `scale` / `scaleX` / `scaleY`
|
|
@@ -235,6 +284,8 @@ in 3D is ordered against every flat layer it does not cross by which side of tha
|
|
|
235
284
|
a card standing on a page or floor layer always draws over it, wherever the camera looks. Two layers that cut
|
|
236
285
|
through each other still need `shadowPlate.occludes` (below) to intersect per pixel.
|
|
237
286
|
|
|
287
|
+
### Flat layers in the 3D scene — `item.shadowPlate`
|
|
288
|
+
|
|
238
289
|
`text`, `shape`, `image` and `video` items can join the 3D scene without becoming
|
|
239
290
|
solids. The layer stays flat and keeps its normal renderer, so text animators, shape and
|
|
240
291
|
video animation stay native and its alpha updates every frame.
|
|
@@ -255,53 +306,9 @@ ordinary 2D layer. The field is ignored on any other media type.
|
|
|
255
306
|
- **`castShadow`** — the layer's live silhouette is added to the rig's shadow map, so it
|
|
256
307
|
drops a shadow on models and on the cyclorama. Needs a rig with
|
|
257
308
|
`shadows.mode: "map"` and at least one light with `castShadow: true`. The layer's own
|
|
258
|
-
pixels are untouched.
|
|
309
|
+
pixels are untouched. A 3D Scene's world takes no flat silhouette, so there a scene
|
|
310
|
+
object or a model layer has to cast the shadow.
|
|
259
311
|
- **`receiveShadow`** — the set's shadows fall across the layer. This is the only one
|
|
260
312
|
that re-films the layer inside the lit scene, so **Motion Blur, Rig (Puppet) and 3D
|
|
261
313
|
Depth are unavailable on that layer** while it is on; the other two leave them
|
|
262
314
|
working.
|
|
263
|
-
|
|
264
|
-
**`lid`** matches `/^[A-Za-z0-9_-]{1,12}$/` and must be unique within the rig — it is the key keyframe tracks address.
|
|
265
|
-
|
|
266
|
-
**Sizes are multiples of composition height**, so a rig authored at 1080p is correct at 4K with no migration. `width: 4` on a 1080-tall comp is 4320 world px.
|
|
267
|
-
|
|
268
|
-
**A light is switched off with `intensity: 0`, never by removing it** — the light count is a compiled shader constant, so changing it recompiles every material in the scene. `on: false` is presentation only and does the same thing.
|
|
269
|
-
|
|
270
|
-
**Cyclorama shape `"none"`** draws nothing: use it for a rig that only carries lights. Any other shape with `material.mode: "solid"` writes depth, so layers behind it are cut by its surface.
|
|
271
|
-
|
|
272
|
-
**`material.mode: "shadowCatcher"`** keeps only the shadows the set receives and is transparent everywhere else, for models standing on the composition `background`. It writes no depth, so it never cuts a layer. Its shadows use `material.shadowColor` (default `#000000`), not `color` — `color` stays the Solid surface's colour, so a paper set switched to shadows does not paint them in the paper's own colour. `opacity` is the shadow strength (0-1), and the backdrop colour track `scc:cyc:col` drives `shadowColor` in this mode.
|
|
273
|
-
|
|
274
|
-
**Rig keyframes** live on the rig item's `propertyKeyframes` under two prefixes (`sc:` numeric, `scc:` colour — the prefix alone decides numeric vs RGBA interpolation):
|
|
275
|
-
|
|
276
|
-
In the editor, the stopwatch beside an animatable rig field promotes that exact
|
|
277
|
-
property into a flat 0%/100% track seeded from the field's current authored value.
|
|
278
|
-
Once any rig track exists, moving a selected light in the 3D world view auto-keys
|
|
279
|
-
its `az`, `elev` and `dist` tracks at the playhead instead of overwriting the base
|
|
280
|
-
rig. API-authored projects use the same track shape directly.
|
|
281
|
-
|
|
282
|
-
| Key | Range |
|
|
283
|
-
| --- | --- |
|
|
284
|
-
| `sc:<lid>:intensity` \| `az` \| `elev` \| `dist` | per the light's type |
|
|
285
|
-
| `sc:<lid>:angle` \| `penumbra` | spot only |
|
|
286
|
-
| `sc:<lid>:width` \| `height` | area only (world px) |
|
|
287
|
-
| `scc:<lid>:col` | colour |
|
|
288
|
-
| `sc:cyc:x` \| `y` \| `z` \| `rotX` \| `rotY` \| `rotZ` \| `scale` \| `opacity` | — |
|
|
289
|
-
| `scc:cyc:col` | colour |
|
|
290
|
-
| `sc:env:intensity` \| `exposure` \| `rotX` \| `rotY` \| `rotZ` | — |
|
|
291
|
-
|
|
292
|
-
**Refused, and the sanitizer drops them:** light `type`, adding/removing a light, `castShadow`, `shadowMapSize`, cyclorama `shape` and dimensions, `env.source`/`preset`/`url`. Each is a shader recompile or an environment re-bake, not a value — animating one would rebuild every frame. A track naming a `lid` that no longer exists is dropped on the next save.
|
|
293
|
-
|
|
294
|
-
**The playhead outside every rig clip does NOT mean darkness.** The nearest rig still lights the scene, and a project with no rig falls back to the legacy three directionals.
|
|
295
|
-
|
|
296
|
-
**21 built-in presets** (`rigPresets.js`), each a **complete** rig — applying one replaces `item.rig` wholesale:
|
|
297
|
-
|
|
298
|
-
| Category | Preset ids |
|
|
299
|
-
| --- | --- |
|
|
300
|
-
| Studio | `threePoint` (the exact legacy default — use it to match a pre-rig project), `studioRoom`, `clamshellBeauty`, `highKeyWhite` |
|
|
301
|
-
| Product | `productSoftbox`, `topDownProduct`, `turntable`, `pastelSweep`, `pianoBlack` |
|
|
302
|
-
| Dramatic | `rimLitDark`, `filmNoir`, `stageSpotlight`, `campfireGlow` |
|
|
303
|
-
| Daylight | `sunsetWindow`, `overcastExterior`, `desertNoon`, `blueHour` |
|
|
304
|
-
| Location | `warehouse`, `sciFiRoom` |
|
|
305
|
-
| Stylized | `neonTwoTone`, `duotonePop` |
|
|
306
|
-
|
|
307
|
-
**Other rig-item fields the editor writes.** `item.rigLook = { id, name, hash }` records which look was applied (a preset id, or `saved:<id>` for a look the user saved) and a hash of the rig at that moment, so the editor can mark the look as edited once the rig drifts from it. It is informational — omit it when emitting a rig; a hand-made `hash` never matches, so the look would show as edited from the start. `item.rigAi` holds the AI video state — the prerender used as the source, the takes and which one is placed on the timeline. It is written by the editor from real render and generation results; do not write it yourself.
|