greenlight-motion 0.4.0 → 0.5.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/NOTICE +4 -0
- package/README.md +37 -9
- package/THIRD_PARTY_LICENSES.md +1 -1
- package/cli/index.mjs +118 -13
- package/cli/plugin3d.mjs +202 -0
- package/dist/assets/rigWorker-CGpWyn_f.js +1 -0
- package/dist/bundle-inputs.json +36 -0
- package/dist/greenlight-motion-script.mjs +16524 -202
- package/dist/greenlight-motion.js +698 -688
- package/dist/greenlight-motion.mjs +78778 -74492
- package/dist/script-inputs.json +6 -1
- package/package.json +9 -1
- package/schema/capabilities.json +1 -1
- package/skill/SKILL.md +22 -11
- package/skill/references/scene-script.md +86 -0
- package/skill/references/video-3d.md +223 -8
- package/skill/references/video-camera.md +3 -2
- package/skill/references/video-effects.md +13 -1
- package/skill/references/video-keyframes.md +6 -0
- package/skill/references/video-particles.md +5 -11
- package/skill/references/video-paths.md +2 -1
- package/skill/references/video-projects.md +18 -11
- package/skill/references/video-shapes.md +1 -0
- package/skill/references/video-text.md +43 -7
- package/skill/references/video-world3d.md +1548 -0
- package/skill/references/world3d-schema.json +15118 -0
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.5.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.5.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
|
|
@@ -47,6 +47,16 @@ Then `check` and `stills` (below), fix the SCRIPT and run `script` again — it
|
|
|
47
47
|
`z`). For what a script can't say (3D models, particles, motion paths, masks, text animators by hand), go on from `project.json` with the
|
|
48
48
|
workflow below and stop running `script` (it refuses to overwrite a project edited since, unless `--force`).
|
|
49
49
|
|
|
50
|
+
**A real 3D shot** (a product on a table, glass and liquids, studio light, a camera move): the script's `world`, GreenLight
|
|
51
|
+
Dash's own 3D Scene JSON — objects built from ops, materials, lights, `scene.camera` or a ready move, Draft as it plays and
|
|
52
|
+
Final (path traced) for the render. `script` checks it strictly: a field the 3D engine would ignore fails with its path and
|
|
53
|
+
the right name. Format and example: `skill/references/scene-script.md` (A 3D Scene); every field, op and material:
|
|
54
|
+
`references/video-world3d.md` and `references/world3d-schema.json`. A folder with a Scene plays from a local server
|
|
55
|
+
(`review`), not by double-click; `check`, `stills` and `render` open it that way themselves. Keep Final short (seconds per
|
|
56
|
+
frame) and judge glass and liquids in a Final still: Draft shows no liquid inside glass. The 3D engine is a plugin,
|
|
57
|
+
`greenlight-motion-3d`: the CLI downloads it by itself the first time a folder has a `world` (once, into the user's cache);
|
|
58
|
+
`npx greenlight-motion@0.5.0 add-3d` gets it ahead, `doctor` shows it. Never tell the user to install it.
|
|
59
|
+
|
|
50
60
|
## Beyond the script: the signature visual
|
|
51
61
|
|
|
52
62
|
The script gives structure; the picture that makes the video THIS product's usually needs the engine's full format.
|
|
@@ -85,14 +95,14 @@ What the engine does there, so you don't learn it from a broken frame:
|
|
|
85
95
|
|
|
86
96
|
## Workflow
|
|
87
97
|
|
|
88
|
-
1. `npx greenlight-motion@0.
|
|
98
|
+
1. `npx greenlight-motion@0.5.0 init <folder>` — starts from a working example (title, subtitle, accent line, four
|
|
89
99
|
options). `--from file.json` starts from a project you have (for example one exported from GreenLight Dash).
|
|
90
100
|
2. Edit `<folder>/project.json` — for changes to an EXISTING project prefer `apply` (below): it checks every edit and
|
|
91
101
|
applies all or nothing. Put pictures, video, sound and fonts in `<folder>/assets/` and refer to them as
|
|
92
102
|
`"asset:<file>"` (e.g. `"url": "asset:product.png"`).
|
|
93
|
-
3. `npx greenlight-motion@0.
|
|
103
|
+
3. `npx greenlight-motion@0.5.0 check <folder>` — fix everything it lists (missing files, template mistakes, blank
|
|
94
104
|
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.
|
|
105
|
+
4. `npx greenlight-motion@0.5.0 stills <folder>` — renders frames and **a labelled contact sheet**. Open and look at
|
|
96
106
|
it before you go on: layout, readability, timing. Fix and repeat. `--at 0.5,1.2,3` for exact times.
|
|
97
107
|
5. Hand it over: tell the user to open `<folder>/index.html` (Chrome or Edge), or `pack` it into one file, or
|
|
98
108
|
`render` the MP4 yourself (`--fps 60 --motion-blur` for fast motion).
|
|
@@ -156,8 +166,9 @@ editor's own normalizer), so write only what you mean.
|
|
|
156
166
|
|
|
157
167
|
Full references (generated from the editor's own docs; ignore the `/api/` routes they mention):
|
|
158
168
|
`skill/references/` — video-design, scene-script, video-projects, video-text, video-text-kinetic, video-text-presets, video-shapes, video-keyframes,
|
|
159
|
-
video-camera, video-3d, video-effects, video-transitions, video-particles, video-paths, video-backgrounds, video-grid
|
|
160
|
-
|
|
169
|
+
video-camera, video-3d, video-effects, video-transitions, video-particles, video-paths, video-backgrounds, video-grid,
|
|
170
|
+
video-world3d (the 3D Scene, with world3d-schema.json).
|
|
171
|
+
Machine-readable lists: `npx greenlight-motion@0.5.0 schema effects | transitions | easings | paths | expressions`.
|
|
161
172
|
|
|
162
173
|
## Template
|
|
163
174
|
|
|
@@ -204,7 +215,7 @@ an absolute moment; `align` `sync` (default) / `start` / `end`; `offset` seconds
|
|
|
204
215
|
|
|
205
216
|
The library is 77 hand-picked sounds in categories (whooshes and swooshes, transitions, clicks, pop-ups and
|
|
206
217
|
notifications, data blips, typing and writing, foley, bubbles, impacts and bass drops, camera, gears, voice clips).
|
|
207
|
-
`npx greenlight-motion@0.
|
|
218
|
+
`npx greenlight-motion@0.5.0 schema sounds` lists every one with its `category`, `use` (what it is for), `duration`
|
|
208
219
|
and `sync`. Pick by `category` and `use`, then by name.
|
|
209
220
|
|
|
210
221
|
- **Where the files come from:** the GreenLight sound CDN. `build` downloads each sound a cue uses into
|
|
@@ -220,7 +231,7 @@ The page has **Notes**: the user marks what should change right on the frame (a
|
|
|
220
231
|
and writes a note for each mark. Every note carries the moment (seconds and frame), the mark in composition pixels
|
|
221
232
|
and the layers under it (item ids in project.json), so you change exactly that and nothing else.
|
|
222
233
|
|
|
223
|
-
- **They review on this computer:** run `npx greenlight-motion@0.
|
|
234
|
+
- **They review on this computer:** run `npx greenlight-motion@0.5.0 review <folder>` and give them the URL it prints
|
|
224
235
|
(it keeps running; `--open` opens their browser, only when they ask). Their notes land in `<folder>/review/`: read
|
|
225
236
|
`review/NOTES.md`, look at the `at-<t>s.png` pictures (the marks are numbered), make the changes (`apply`), `build`
|
|
226
237
|
again and tell them to reload the page.
|
|
@@ -249,11 +260,11 @@ that fails (`plugin`); look at `stills` for the look. A package that carries its
|
|
|
249
260
|
|
|
250
261
|
The same project opens in GreenLight Dash (the desktop editor this engine comes from) for full editing: the page's
|
|
251
262
|
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.
|
|
263
|
+
changes saves a `.glvideo`). `npx greenlight-motion@0.5.0 pack <folder> --glvideo` writes that file yourself: ONE
|
|
253
264
|
`.glvideo` (the project + its files) that opens in GreenLight Dash on a double-click, or dropped on its board. Hand it
|
|
254
265
|
over when the user wants to keep editing in the app. A
|
|
255
266
|
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.
|
|
267
|
+
`init` makes, with the options the author ticked. `npx greenlight-motion@0.5.0 init <folder> --from <its project.json>`
|
|
257
268
|
starts from one too. Never tell the user they must install it: the page plays, changes options and renders on its own.
|
|
258
269
|
|
|
259
270
|
## Files
|
|
@@ -105,6 +105,92 @@ matte, masks, particles, 3D) goes on from `project.json`: SKILL.md, "Beyond the
|
|
|
105
105
|
- **music** (script level): `"music": { "src": file, "volume": 80, "fadeIn": 0.3, "fadeOut": 1.2, "startAt": 0 }` — one track under the video.
|
|
106
106
|
The file must exist when you run `script` (it reads its length): write a sound you make in code before the first run.
|
|
107
107
|
|
|
108
|
+
## A 3D Scene: `world`
|
|
109
|
+
|
|
110
|
+
For a real 3D shot — a product on a table, a bottle with liquid, a gadget turning under studio light — give the script a
|
|
111
|
+
`world`: GreenLight Dash's own 3D Scene (the same `rig.scene` JSON its editor stores), drawn in Draft as it plays and
|
|
112
|
+
path traced in Final. Keep it SIMPLE: objects built from ops (box, cylinder, lathe, extrude, bevel…), materials (glass,
|
|
113
|
+
liquids, metals, wood and other procedural patterns), up to a few lights, a bundled HDR environment. No files of your own
|
|
114
|
+
in a 3D Scene here (big scenes with meshes and textures belong in GreenLight Dash or a local run with the npm package).
|
|
115
|
+
`scenes` become optional: 2D titles over the world, see-through unless a scene sets its own `background`.
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{ "format": "16:9", "title": "Éclat", "world": {
|
|
119
|
+
"duration": 4,
|
|
120
|
+
"camera": { "move": "orbit", "amount": 30 },
|
|
121
|
+
"render": { "mode": "draft" },
|
|
122
|
+
"look": { "contrast": 1.15, "bloom": 0.06, "vignette": 0.2 },
|
|
123
|
+
"scene": {
|
|
124
|
+
"camera": { "at": [0, 0.2, 0.75], "target": [0, 0.1, 0], "fov": 30 },
|
|
125
|
+
"env": { "intensity": 0.3, "opaque": true },
|
|
126
|
+
"lights": [{ "id": "key", "type": "area", "at": [-0.5, 0.6, 0.5], "target": [0, 0.1, 0], "width": 0.5, "height": 0.5, "intensity": 6 },
|
|
127
|
+
{ "id": "rim", "type": "spot", "at": [0.3, 0.5, -0.4], "target": [0, 0.1, 0], "angle": 25, "penumbra": 0.4, "intensity": 4 }],
|
|
128
|
+
"materials": {
|
|
129
|
+
"wood": { "procedural": { "pattern": "wood", "planks": 3 }, "uvScale": [2, 2] },
|
|
130
|
+
"glass": { "roughness": 0.02, "transmission": 1, "ior": 1.5, "priority": 1 },
|
|
131
|
+
"perfume": { "transmission": 1, "ior": 1.36, "attenuationColor": "#e8a440", "attenuationDistance": 0.3, "priority": 2 },
|
|
132
|
+
"gold": { "metal": "gold", "roughness": 0.2 } },
|
|
133
|
+
"objects": [
|
|
134
|
+
{ "id": "table", "material": "wood", "at": [0, -0.02, 0], "graph": [{ "op": "box", "size": [1.2, 0.04, 0.7], "bevel": 0.004 }, { "op": "uvBox" }] },
|
|
135
|
+
{ "id": "bottle", "material": "glass", "at": [0, 0.001, 0], "graph": [{ "op": "cylinder", "radiusTop": 0.045, "radiusBottom": 0.045, "height": 0.16, "bevel": 0.006 }, { "op": "transform", "at": [0, 0.08, 0] }] },
|
|
136
|
+
{ "id": "liquid", "material": "perfume", "at": [0, 0.001, 0], "graph": [{ "op": "cylinder", "radiusTop": 0.0395, "radiusBottom": 0.0395, "height": 0.11 }, { "op": "transform", "at": [0, 0.06, 0] }] },
|
|
137
|
+
{ "id": "cap", "material": "gold", "at": [0, 0.177, 0], "graph": [{ "op": "cylinder", "radiusTop": 0.02, "radiusBottom": 0.02, "height": 0.03, "bevel": 0.003 }] } ] } },
|
|
138
|
+
"scenes": [{ "duration": 4, "elements": [{ "type": "text", "text": "ÉCLAT", "role": "headline", "at": "top" }] }] }
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- `world` takes `scene`, `render`, `look`, `exposure`, `camera`, `duration`. `scene`, `render` and `look` are GreenLight Dash's
|
|
142
|
+
`rig.scene`, `rig.render` and `rig.look`: every field, op and material is in references/world3d.md and world3d-schema.json. Units are metres (model things at their
|
|
143
|
+
real size: the perfume bottle above is 16 cm tall), Y is up.
|
|
144
|
+
- **What `world` sets for you:** no Rig lights and no Rig backdrop (it writes `renderer: "world"`, `lights: []`,
|
|
145
|
+
`cyclorama: none`): the light is `scene.lights` plus `scene.env` (a bundled HDRI, `studio_small_03_1k` unless
|
|
146
|
+
`env.hdri` names another `/hdr/<name>.hdr` of the ten presets), and a floor, table or studio sweep is an object you model
|
|
147
|
+
(a box; a sweep is an `extrude` of an L-shaped profile with a rounded corner).
|
|
148
|
+
- **Checked strictly.** A field the 3D engine would ignore (a typo, a three.js name such as `emissive`, `position`, `color` on a
|
|
149
|
+
liquid) fails the build with its path and the right name (`world.scene.materials.glass.transmision: … did you mean
|
|
150
|
+
`transmission`?`), as does an unknown op or parameter, a material or parent that is not there, a broken lathe profile.
|
|
151
|
+
An op that fails only when the geometry is built (a boolean that cannot be made) shows in look_at_video's critique:
|
|
152
|
+
"the scene did not build: …".
|
|
153
|
+
- **The camera:** `scene.camera` (`at`, `target`, `fov`, `keys` for your own moves) or a ready move in `world.camera`: one
|
|
154
|
+
smooth in-out move over the whole clip, always looking at `target`. `push-in` starts at `scene.camera.at` and ends
|
|
155
|
+
`amount` of the way to the target (0–0.9 [0.25]); `pull-out` is the same backwards. `orbit` turns around the target by
|
|
156
|
+
`amount` degrees in all [40]; `rise` goes up by `amount` of the distance [0.25]; `slide-left` / `slide-right` move
|
|
157
|
+
sideways by `amount` of the distance [0.25], the target with them. These three are centred on `scene.camera.at`: half
|
|
158
|
+
the amount before it, half after, so frame the middle of the clip there. A scene's own 2D `camera` defaults to `none`
|
|
159
|
+
over a world (titles hold still).
|
|
160
|
+
- **Camera moves, worked.** Frame the subject with room to spare: the critique samples the whole move (every key and evenly
|
|
161
|
+
between them) and names an object the move takes out of the frame, and when.
|
|
162
|
+
- *Orbit* around a product: `"camera": { "move": "orbit", "amount": 40 }` with `"scene": { "camera": { "at": [0, 0.22, 0.8],
|
|
163
|
+
"target": [0, 0.09, 0], "fov": 30 } }`. The middle of the clip is shot from `at`; the turn goes 20° to each side, so a
|
|
164
|
+
framing that is tight from the front clips a wide object at the ends: step back (a longer `at`) or turn less.
|
|
165
|
+
- *Push-in* to a detail: `"camera": { "move": "push-in", "amount": 0.35 }` starts at `scene.camera.at` and ends 35 % of the
|
|
166
|
+
way to `target`, so the last frame is the tightest: aim `target` at the detail (the label, the cap). Your own keys do the
|
|
167
|
+
same with a lens change: `"keys": { "at": [{ "t": 0, "v": [0, 0.2, 1], "ease": "inOut" }, { "t": 4, "v": [0, 0.14, 0.5] }],
|
|
168
|
+
"fov": [{ "t": 0, "v": 30, "ease": "inOut" }, { "t": 4, "v": 24 }] }`.
|
|
169
|
+
- *Rack focus* (depth of field): `"camera": { "at": [0.25, 0.12, 0.55], "target": [0, 0.08, 0], "fov": 28, "fStop": 2,
|
|
170
|
+
"keys": { "focus": [{ "t": 0.8, "v": 0.42, "ease": "inOut" }, { "t": 2.2, "v": 0.78 }] } }`: sharp on what stands 0.42 m from
|
|
171
|
+
the camera, then on what stands 0.78 m away. `focus` is a distance in metres along the view (for an object at p:
|
|
172
|
+
(p − at)·(target − at) / |target − at|); `"@id"` holds the focus on an object, but keys from one `"@id"` to another jump
|
|
173
|
+
instead of racking, so key distances. A low `fStop` (1.4–2.8) makes the blur strong; leave `fStop` out for all sharp.
|
|
174
|
+
Draft blurs roughly; judge it in Final. Focus keys go with a ready move too: a push-in with `"focus": "@bottle"` stays sharp on the bottle.
|
|
175
|
+
- **Titles over it:** `scenes` are 2D layers over the world, see-through unless a scene sets its own `background`
|
|
176
|
+
(`style.background` does not cover the world). Keep a title clear of the objects: a 2D layer stands in the world's depth
|
|
177
|
+
(about 2.2 m from the camera at 1080 px high), so an object nearer the camera that overlaps it hides its letters. Put it where
|
|
178
|
+
the frame is calm and has the contrast its colour needs: a light title on a light set vanishes. The critique below gives each
|
|
179
|
+
object's span in % of the frame and checks every 2D layer: how much of it the objects hide, its contrast against what is
|
|
180
|
+
under it, whether it leaves the frame or the title-safe area (5 % in from each edge), and texts that overlap. The video is as long as the scenes and the world
|
|
181
|
+
lasts all of it (`world.duration` counts only for a world with no scenes).
|
|
182
|
+
- **Draft and Final.** `render.mode` `"draft"` (default) plays live; `"final"` path traces every frame (`spp` 16–64 here):
|
|
183
|
+
seconds per frame, so render Final only for the finished version, and short. Judge glass and liquids in Final: Draft shows no
|
|
184
|
+
liquid inside glass and no refraction, and only a rough depth of field.
|
|
185
|
+
- **No length limit here** (the chat caps a Final MP4 at 10 s at 64 samples; locally the only limit is your GPU's time).
|
|
186
|
+
- **The 3D plugin.** The 3D engine is a separate package, `greenlight-motion-3d`: `script`, `build`, `check`, `stills`
|
|
187
|
+
and `render` get it by themselves the first time a folder has a `world` (into the user's cache, once per version);
|
|
188
|
+
`npx greenlight-motion add-3d` gets it now, `doctor` says which version is there. Offline, point
|
|
189
|
+
`GREENLIGHT_MOTION_3D` at an unpacked copy or its .tgz.
|
|
190
|
+
- **Its critique.** look_at_video on a build with a world adds a critique of the middle frame: what the shot shows object by
|
|
191
|
+
object (its share of the frame, where it stands, what hides it, what is out of the camera), where each light stands against
|
|
192
|
+
the subject, the picture's numbers, and hints naming the field to change. Fix what it names, then build again.
|
|
193
|
+
|
|
108
194
|
## What makes it look made, not generated
|
|
109
195
|
|
|
110
196
|
references/video-design.md — the same design rules GreenLight Dash's agents and the chat connector use. Read it before
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
## Contents
|
|
12
12
|
|
|
13
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
|
+
- [Characters — the pose pipeline (`model3dConfig.pose`)](#characters--the-pose-pipeline-model3dconfigpose)
|
|
15
|
+
- [Skeletons for meshes that have none (`skeleton`)](#skeletons-for-meshes-that-have-none-skeleton)
|
|
14
16
|
- [Scene Rig — one set for every 3D layer](#scene-rig--one-set-for-every-3d-layer)
|
|
15
17
|
- [Turning flat layers in 3D — pivot and stacking](#turning-flat-layers-in-3d--pivot-and-stacking)
|
|
16
18
|
- [Flat layers in the 3D scene — `item.shadowPlate`](#flat-layers-in-the-3d-scene--itemshadowplate)
|
|
@@ -24,7 +26,7 @@ Unlike a device mockup (pre-baked to a video file), a `model3d` layer is a **rea
|
|
|
24
26
|
- **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.)
|
|
25
27
|
- **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).
|
|
26
28
|
- **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
|
|
29
|
+
- **Gaussian splat:** a `.ply`, `.splat` or `.spz` url. Only a 3D Scene draws splats ([video-world3d.md](video-world3d.md#gaussian-splats)).
|
|
28
30
|
|
|
29
31
|
**Clip + item pair (preset device):**
|
|
30
32
|
|
|
@@ -92,11 +94,11 @@ Some GLBs are authored facing away from the viewer — for those, set `baseRotat
|
|
|
92
94
|
| --- | --- | --- | --- |
|
|
93
95
|
| `source` | `"preset"` \| `"primitive"` \| `"custom"` | `custom` | Where the geometry comes from. Anything else coerces to `custom`. |
|
|
94
96
|
| `preset` | preset id string | `null` | Required when `source: "preset"`. An unknown id falls through to the clip url. |
|
|
95
|
-
| `fit` | `"height"` \| `"width"` \| `"unit"` | `height` | How `fitValue` sizes the model: % of frame height, % of frame width, or raw pixels-per-model-unit. |
|
|
97
|
+
| `fit` | `"height"` \| `"width"` \| `"unit"` | `height` | How `fitValue` sizes the model: % of frame height, % of frame width, or raw pixels-per-model-unit. The box is the file's rest pose as it draws: a skinned character through its bones. |
|
|
96
98
|
| `fitValue` | `0.01–10000` | `60` | With `fit: "height"`, `60` = the model's bounding box spans 60 % of the frame height. |
|
|
97
99
|
| `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. |
|
|
98
100
|
| `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
|
|
101
|
+
| `physics` | `"off"` \| `"falls"` \| `"solid"` | `off` | Rigid-body physics, only in a 3D Scene ([Rigid bodies](video-world3d.md#rigid-bodies)): `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. |
|
|
100
102
|
| `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`. |
|
|
101
103
|
| `receiveShadow` | bool | `true` | With a Scene Rig: rig shadows darken the model. `false` = they never land on it. Only stored when `false`. |
|
|
102
104
|
| `baseRotation` | `{x,y,z}` deg, `±360` | `{0,0,0}` | Static orientation applied before keyframed rotation. |
|
|
@@ -109,6 +111,8 @@ Some GLBs are authored facing away from the viewer — for those, set `baseRotat
|
|
|
109
111
|
| `animation.speed` | `-8–8` | `1` | Playback rate (negative = reversed). |
|
|
110
112
|
| `animation.loop` | `"once"` \| `"loop"` \| `"pingpong"` | `loop` | Loop mode. |
|
|
111
113
|
| `animation.offset` | `±600` s | `0` | Start offset into the animation. |
|
|
114
|
+
| `skeleton` | object, optional | absent | Bones and skin weights made for a file that has no skeleton — see [Skeletons](#skeletons-for-meshes-that-have-none-skeleton). Not on a procedural shape (`source: "primitive"`): build a figure to rig in the Scene's graph. |
|
|
115
|
+
| `pose` | object, optional | absent | A character's pose: clips crossfaded, morphs, bones, constraints, layers — see [Characters](#characters--the-pose-pipeline-model3dconfigpose). With it, `animation` only names the base clip when `pose.clips` is empty. |
|
|
112
116
|
| `shadow.enabled` | bool | `false` | Soft contact-shadow blob under the model. |
|
|
113
117
|
| `shadow.opacity` | `0–1` | `0.35` | Shadow strength. |
|
|
114
118
|
| `shadow.blur` | `0–10` | `1` | Shadow softness. |
|
|
@@ -135,17 +139,225 @@ Some GLBs are authored facing away from the viewer — for those, set `baseRotat
|
|
|
135
139
|
}]
|
|
136
140
|
```
|
|
137
141
|
|
|
142
|
+
**A layer of the same project on a screen — `layer`.** Instead of `url`, a `screen` or a `screens` entry can take `"layer": "<item id>"` (or a channel id: its item at the playhead): the screen shows that layer's picture at the playhead, drawn live every frame. An HTML clip (including a HyperFrames page) runs its own page and needs no bake: an edit of the `.html` shows on the screen as soon as the editor sees the file change, and renders draw the same frames. A video layer plays in step with the timeline, an image layer shows its picture. Text, shape and sequence layers are not shown (make them an HTML clip). Hide the source layer's channel (`"visible": false`) to show it only on the screen. The screen's canvas is the source's own size (an HTML clip's `html.width` × `html.height`, a video's frame), so it is only as sharp as that; `"resolution": [w, h]` lays an HTML page out at another size. Nothing is uploaded again while the layer shows the same frame (paused, a still page), so Final keeps its samples.
|
|
143
|
+
|
|
144
|
+
```jsonc
|
|
145
|
+
"screens": [{ "node": "Screen", "layer": "i-ui", "aspect": 2.3846, "fitMode": "cover", "look": "monitor" }]
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**A real monitor's look — `look: "monitor"`** (on `screen` or a `screens` entry; a 3D Scene draws it): a glossy glass coat with soft reflections of the room over a near-black panel, and blacks lifted a few levels as an LCD's backlight shows through (`blackLevel`, 0–0.5, default 0.02 with the look, 0 without). `strength` scales the glow [1]; `glass` is the coat's roughness [0.1] (higher: a matte anti-glare screen). In Final a screen lights the room as a real light (sampled directly, so a keyboard under it is clean at the usual samples), and its weight as a light follows the picture it shows. In Draft the look adds a soft light from the panel in the colour its picture averages to, following it as it changes (no shadows; `"light": false` leaves it out). Without `look` a screen keeps the plain glowing panel.
|
|
149
|
+
|
|
138
150
|
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).
|
|
139
151
|
|
|
140
152
|
**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).
|
|
141
153
|
|
|
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
|
|
154
|
+
**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` ([video-world3d.md](video-world3d.md)).
|
|
143
155
|
|
|
144
156
|
**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.
|
|
145
157
|
|
|
158
|
+
### Characters — the pose pipeline (`model3dConfig.pose`)
|
|
159
|
+
|
|
160
|
+
A model layer's `pose` animates a character from its GLB: several clips crossfaded, morph targets, bones, constraints (look-at, two-bone
|
|
161
|
+
IK, attach, parent, follow path) and animation layers with masks. The file's clips are only read, never changed. Every frame runs one
|
|
162
|
+
fixed order, the same in the editor, Draft, Final, stills and exports:
|
|
163
|
+
|
|
164
|
+
1. **clips** — the first is the base; each next one lies over what is below by its weight (a crossfade is the next clip's `fadeIn` while
|
|
165
|
+
the one before keeps playing);
|
|
166
|
+
2. **morphs** — morph target weights by name;
|
|
167
|
+
3. **bones** — local transform overrides by bone name;
|
|
168
|
+
4. **constraints** — solved against the layer's transform of this frame;
|
|
169
|
+
5. **world** — the layer's transform and floor (the floor is found on the pose before step 4, so an arm reaching down never lifts the feet).
|
|
170
|
+
|
|
171
|
+
`order` stores that list in the project (it is the only order; another is ignored with a warning). Layers (`layers`) run inside each
|
|
172
|
+
step after the pose's own entries, in their order. Names (clips, bones, morphs) are looked up in the file each frame, so a re-imported
|
|
173
|
+
file keeps the pose: a name it no longer has is skipped with a warning (`scene.warnings` of a still in a Scene). A bone's name as the file
|
|
174
|
+
writes it works (`"Palm2.L"`), and so does the loader's (`"Palm2L"`: it drops `.`, `:`, `/`, `[`, `]`). Nothing in a pose costs anything on
|
|
175
|
+
a layer without one: that layer plays `animation` exactly as before.
|
|
176
|
+
|
|
177
|
+
**Every number is keyable**: `keys: { "<field>": [{ "t", "v", "ease" }] }` on the control itself, in the **layer's** seconds (its clip's
|
|
178
|
+
clock, like `animation`), holding before the first key and after the last, `ease` `inOut` by default (`linear`, `in`, `out`, `back`,
|
|
179
|
+
`bounce`).
|
|
180
|
+
|
|
181
|
+
**Clips** (`clips: [...]`):
|
|
182
|
+
|
|
183
|
+
| Field | Values | Default | Meaning |
|
|
184
|
+
| --- | --- | --- | --- |
|
|
185
|
+
| `clip` | animation name | required | The GLB's animation (names exactly as in the file). |
|
|
186
|
+
| `start` / `end` | layer seconds | `0` / none | It lays nothing before `start` or after `end`. |
|
|
187
|
+
| `fadeIn` / `fadeOut` | seconds | `0` | Rises to its weight after `start` / falls to 0 before `end`, on an S-curve (the blend starts and ends gently: no pop). |
|
|
188
|
+
| `weight` | `0–1` | `1` | How much of it lies over what is below. Keyable. |
|
|
189
|
+
| `speed` / `offset` | `-8–8` / s | `1` / `0` | Playback rate; seconds into the clip at `start`. Keyable. |
|
|
190
|
+
| `time` | clip seconds | none | Set the clip's own time outright (key it to scrub by hand); `speed` and `offset` then do nothing. Keyable. |
|
|
191
|
+
| `loop` | `loop` \| `once` \| `pingpong` | `loop` | `once` holds the last frame. |
|
|
192
|
+
| `blend` | `over` \| `add` | `over` | `add` lays the clip's change from the file's rest pose on top (breathing over a walk). |
|
|
193
|
+
|
|
194
|
+
**Root motion** (`root`): `"normal"` (the clip moves the character as it was made), `"inPlace"` (the root's travel along the ground taken
|
|
195
|
+
out, its up and down kept), `"accumulate"` (a looping walk carries on from where the last loop ended instead of jumping back). `{ "mode":
|
|
196
|
+
"inPlace", "bone": "Hips" }` names the root bone; left out, of the bones the clips move by translation the one with the most of the
|
|
197
|
+
skeleton under it (the hips, never a foot).
|
|
198
|
+
|
|
199
|
+
**Morphs** (`morphs: { "<target>": 0.4 }` or `{ "value", "mode": "replace" | "add", "weight", "mesh", "keys" }`): replace sets the weight
|
|
200
|
+
over what the clips made, add adds to it. Draft and Final draw the mixed shape (Final traces it).
|
|
201
|
+
|
|
202
|
+
**Bones** (`bones: { "<bone>": { "rot": [x, y, z], "at", "scale", "mode": "add" | "replace", "weight", "keys" } }`): `rot` degrees in
|
|
203
|
+
the bone's own frame. `add` (default) turns, moves and scales on top of the animation; `replace` sets the local transform, by `weight`.
|
|
204
|
+
Any node of the file counts, not only bones.
|
|
205
|
+
|
|
206
|
+
**Constraints** (`constraints: [...]`), each with `type`, `bone`, `weight` (key it 0 → 1 to hand the bone from its animation to the
|
|
207
|
+
constraint: space switching) and `on`. A target is `"@id"` (a Scene object in `rig.scene.objects`, where it stands at the Scene's time,
|
|
208
|
+
keys and parents included), `[x, y, z]` (a point in the Scene's metres: the editor's 3D space, one composition pixel = 1 mm, the same in a
|
|
209
|
+
Scene and on a classic Rig) or `{ "bone": "<name>" }` (one of the character's own):
|
|
210
|
+
|
|
211
|
+
| `type` | Fields | What it does |
|
|
212
|
+
| --- | --- | --- |
|
|
213
|
+
| `lookAt` | `target`, `chain` [1, up to 4: the bone and its parents share the turn], `limit` [180 degrees from the animated pose], `axis` (the bone's front in its frame; default: the model's front, +Z of the file, as the bone holds it at rest) | Aims the bone at the target by the shortest turn. |
|
|
214
|
+
| `ik` | `target`, `pole`, `tip` | Two-bone IK: `bone` is the end (a wrist `Palm2.L`, an ankle); its parent and grandparent bend so it reaches the target, the middle joint toward `pole` (left out: the bend the pose has). `tip` (a bone under the end: `Middle2.L_end`, a toe) lands on the target instead of the wrist, so the hand touches what it reaches. Out of reach: the limb points at it, straight. |
|
|
215
|
+
| `parent` | `target`, `at`, `rot`, `orient` [true] | The bone is held by the target (a hand on a wheel), offset by `at` (metres) / `rot` (degrees). |
|
|
216
|
+
| `path` | `path` (2+ points), `progress` [0–1, keyable], `closed`, `orient` | The bone moves along a smooth curve through the points (a root along a route, a hand drawing a line). |
|
|
217
|
+
| `attach` | `object` (a Scene object id), `at`, `rot`, `keep`, `grab` | The Scene object is held by the bone (a cup in a hand): Draft and Final draw it there; `weight` between 0 and 1 puts it part way from its own place (key 0 → 1 the moment the hand reaches it). `keep: true` holds it where it was against the bone at the pick-up (`grab` seconds; left out, where `weight` starts to rise) instead of at `at`/`rot`, so it never jumps. |
|
|
218
|
+
|
|
219
|
+
`"@id"` targets are read from `rig.scene`'s JSON (keys and parents included), so they aim the same on a classic Rig; `attach` moves a
|
|
220
|
+
Scene object, which only a 3D Scene (`renderer: "world"`) draws.
|
|
221
|
+
|
|
222
|
+
**Layers** (`layers: [{ "id", "on", "weight", "mask", "clips", "morphs", "bones", "constraints", "keys" }]`): each lies over the pose below,
|
|
223
|
+
in each step, by its `weight` (keyable), only where its `mask` lets it: `bones` and `objects` (names, with their children unless
|
|
224
|
+
`"children": false`), `exclude`, `properties` (`rotation`, `position`, `scale`, `morphs`), `morphs` (names). `"on": false` leaves a layer
|
|
225
|
+
out without deleting it.
|
|
226
|
+
|
|
227
|
+
**What a file offers.** A still with `"inspect": true` answers, for each posed model layer in a Scene, `scene.poses["<item id>"]`: its
|
|
228
|
+
`clips` (name, seconds), `morphs`, `nodes` (every node with its parent, `bone` and `mesh` flags) and `solved` (this frame's constraints:
|
|
229
|
+
`lookAt` `errorDeg`, `ik` `distance` from the target in mm with `reach` and `targetDistance`) plus the pose's `warnings`. Write a small pose
|
|
230
|
+
(one clip), render one still with `inspect`, then name what the file really has. `POST /api/world3d/validate` with `{ "data": <project> }`
|
|
231
|
+
checks every pose with the project (unknown fields, a constraint without its `bone` or `target`, a Scene object that does not exist).
|
|
232
|
+
|
|
233
|
+
**Worked example** — a character idles, waves, turns its head to a flying target on a layer of its own and reaches a ball with its left
|
|
234
|
+
hand (three.js's RobotExpressive.glb, CC0; here placed by `fit: "unit"` from its own origin, so the pose's targets are in its file's units; `fit: "height"` / `"width"` measure a skinned character as it stands too, its bones included):
|
|
235
|
+
|
|
236
|
+
```json
|
|
237
|
+
"model3dConfig": {
|
|
238
|
+
"source": "custom", "fit": "unit", "fitValue": 160, "anchor": "origin", "placeOnRigFloor": false,
|
|
239
|
+
"pose": {
|
|
240
|
+
"order": ["clips", "morphs", "bones", "constraints", "world"],
|
|
241
|
+
"clips": [
|
|
242
|
+
{ "clip": "Idle" },
|
|
243
|
+
{ "clip": "Wave", "start": 1.0, "fadeIn": 0.8, "end": 4.2, "fadeOut": 0.8 }
|
|
244
|
+
],
|
|
245
|
+
"morphs": { "Surprised": { "value": 0, "keys": { "value": [{ "t": 0.9, "v": 0 }, { "t": 1.4, "v": 0.8 }, { "t": 2.4, "v": 0 }] } } },
|
|
246
|
+
"bones": { "Torso": { "rot": [0, 0, 0], "keys": { "rot": [{ "t": 2.8, "v": [0, 0, 0] }, { "t": 3.8, "v": [0, 0, -10] }] } } },
|
|
247
|
+
"layers": [
|
|
248
|
+
{ "id": "look", "mask": { "bones": ["Neck"] }, "weight": 0, "keys": { "weight": [{ "t": 0.5, "v": 0 }, { "t": 1.2, "v": 1 }] },
|
|
249
|
+
"constraints": [{ "type": "lookAt", "bone": "Head", "chain": 2, "target": "@fly", "limit": 80 }] },
|
|
250
|
+
{ "id": "reach", "mask": { "bones": ["UpperArm.L"] },
|
|
251
|
+
"constraints": [{ "type": "ik", "bone": "Palm2.L", "tip": "Middle2.L_end", "target": "@grip", "pole": [0.45, -0.3, -0.25],
|
|
252
|
+
"weight": 0, "keys": { "weight": [{ "t": 3.2, "v": 0 }, { "t": 4.2, "v": 1 }] } }] }
|
|
253
|
+
]
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
with `fly`, `ball` and `grip` as objects of the Scene (`rig.scene.objects`): `{ "id": "ball", "at": [0.24, -0.03, 0.17], "graph": [{ "op":
|
|
259
|
+
"sphere", "radius": 0.04 }] }`, the point the fingertips land on as an object with no graph on the ball's surface toward the shoulder
|
|
260
|
+
`{ "id": "grip", "parent": "ball", "at": [-0.027, -0.004, -0.032] }`, and `fly` keyed along `at`. The target must be within the arm's
|
|
261
|
+
reach of the shoulder (`solved` says `targetDistance` against `reach`). To pick the ball up afterwards add
|
|
262
|
+
`{ "type": "attach", "bone": "Palm2.L", "object": "ball", "keep": true, "weight": 0, "keys": { "weight": [{ "t": 4.3, "v": 0, "ease": "linear" }, { "t": 4.4, "v": 1 }] } }`
|
|
263
|
+
and key the IK's weight back to 0: the arm returns to its clip with the ball in its hand.
|
|
264
|
+
|
|
265
|
+
### Skeletons for meshes that have none (`skeleton`)
|
|
266
|
+
|
|
267
|
+
`skeleton` gives a mesh with no skeleton one: joints, skin weights, and clips made of keys on the joints. It goes on a model layer
|
|
268
|
+
(`model3dConfig.skeleton`, a GLB/GLTF without a skeleton) or on a Scene object (`rig.scene.objects[i].skeleton`, a mesh built by its
|
|
269
|
+
`graph`, or a `model` file). The mesh is then a skinned character like a file that came with one: a `pose` moves its joints (on a model
|
|
270
|
+
layer `model3dConfig.pose`, on a Scene object the object's own `pose`, on the Scene's clock), Draft skins it, Final traces it skinned,
|
|
271
|
+
stills and exports alike. A mesh without `skeleton` is untouched.
|
|
272
|
+
|
|
273
|
+
Points are in the **mesh's own space**: a Scene object's local metres (before its `at`/`rot`/`scale`); a model file's own units (before the
|
|
274
|
+
layer's fit). The figure stands up +Y, faces +Z, its left is +X (`.L` names).
|
|
275
|
+
|
|
276
|
+
| Field | Values | Default | Meaning |
|
|
277
|
+
| --- | --- | --- | --- |
|
|
278
|
+
| `template` | `biped` \| `quadruped` | none | A ready joint tree placed by `landmarks`. biped: `hips` (root), `spine`, `chest`, `neck`, `head`; `upperArm`, `forearm`, `hand`; `thigh`, `shin`, `foot`, `toe` (each `.L` / `.R`). quadruped: `hips`, `spine`, `chest`, `neck`, `head`, `tail`; `upperArm`, `forearm`, `frontFoot`; `thigh`, `shin`, `hindFoot` (`.L` / `.R`). |
|
|
279
|
+
| `landmarks` | `{ name: [x, y, z] }` | `{}` | Points on the body. biped: `hips`, `spine`, `chest`, `neck` (foot of the neck), `head` (top of the neck), `headTop`, `shoulder`, `elbow`, `wrist`, `handTip`, `hip`, `knee`, `ankle`, `ball`, `toe` (the last ten `.L` / `.R`). quadruped: `hips`, `spine`, `chest`, `neck`, `head`, `nose`, `tail`, `tailTip`, `shoulder`, `elbow`, `wrist`, `frontFoot`, `hip`, `knee`, `hock`, `hindFoot`. **Every one left out is found from the mesh's shape** (cross-sections: the legs where a cut is two rings, a bent knee where the leg's line bends most; the neck at the narrowest cut; each arm, out in a T or A pose or hanging clear of the body at its side, followed from the fingertips by cuts square to it to where it meets the torso: the shoulder half the arm's radius past the armpit, an arm's radius under the shoulder's top; a quadruped's four legs near the ground, a hind leg's hock and knee where it zigzags, its head and neck followed back from the nose: `head` at the narrowest of the neck behind the skull, `neck` where it meets the body). |
|
|
280
|
+
| `symmetric` | bool | `true` | A `.L` point (given or found) also places its `.R`, mirrored across the middle. |
|
|
281
|
+
| `joints` | `[{ name, parent, at, tail, rot }]` | none | Joints written out: the whole tree without a template, or ones that replace a template's joint of the same name or are added to it (fingers, ears, a tail). `at` is where it turns, `tail` where a joint with no children ends, `rot` its rest turn (degrees; left out, its axes are the mesh's). |
|
|
282
|
+
| `bind` | `skin` \| `rigid` | `skin` | `skin`: smooth weights that bend the surface. `rigid`: each connected part follows the one joint that holds most of it (a robot of separate parts). |
|
|
283
|
+
| `skinning` | `dualQuaternion` \| `linear` | `dualQuaternion` | How the joints move the skin. Dual quaternions: every point follows a rigid blend of its joints, so a 90-degree elbow keeps its full thickness and a wrist twisted half a turn stays round; a joint's `scale` (a pose's or a clip's) scales its part of the skin about the joint, as linear blending does. `linear`: the joints' matrices averaged, as a file's own skeleton is drawn: a 90-degree bend thins to 0.71 at the crease, a half-turn twist collapses. |
|
|
284
|
+
| `parts` | `{ "<mesh name>": "<joint>" }` | none | A model file's meshes by name, each following one joint whole. |
|
|
285
|
+
| `weights` | `{ smooth, threshold, visibility }` | `0`, `0.01`, `true` | Blur passes after the solve; the smallest share kept (four joints a vertex at most); whether heat reaches only the points that see a bone from inside the body. |
|
|
286
|
+
| `fixes` | list | none | Manual weight edits, in order — below. |
|
|
287
|
+
| `clips` | `[{ name, duration, bones }]` | none | Animations made of keys — below. |
|
|
288
|
+
|
|
289
|
+
**Automatic weights** are heat diffusion over the surface (each joint's bone held hot, the surface's steady temperature is its weight):
|
|
290
|
+
smooth over every bend, never through the air, so two legs or two fingers that touch nothing but the body share nothing; a joint's heat
|
|
291
|
+
also stops where the nearest joint is neither it, its parent nor its child, so nothing creeps from one leg round the crotch into the other.
|
|
292
|
+
Four joints a vertex at most, adding up to exactly 1: the rest pose is the mesh itself (moves 0). Draft, Final and a model layer drawn
|
|
293
|
+
outside a Scene skin by the same dual quaternions.
|
|
294
|
+
|
|
295
|
+
**Fixes** (`fixes: [...]`), each `{ "joint", "value", "mode": "set" | "add" | "multiply" | "max" | "min", "strength" }` over a region, or
|
|
296
|
+
`{ "smooth": passes }` that blurs every joint's weights there. The other joints share what the fixed one leaves as they did. Regions (all
|
|
297
|
+
given at once multiply): `group` (a vertex group the graph wrote: `weightGradient`, `weightProximity`, `weightMask`, `weightNoise`),
|
|
298
|
+
`mesh` (a model file's mesh by name), `of` (the points a joint holds most now), `box: { min, max }` and `sphere: { at, radius }` with
|
|
299
|
+
`soft` metres of fade, `near: { at, radius, inner, falloff: "smooth" | "linear" | "sharp" }` (a brush).
|
|
300
|
+
|
|
301
|
+
**Clips** (`clips: [{ "name": "wave", "duration": 1, "bones": { "forearm.R": { "rot": [{ "t": 0, "v": [0, 0, -15] }, { "t": 0.5, "v": [0, 0, -55] }, { "t": 1, "v": [0, 0, -15] }] } } }]`):
|
|
302
|
+
`rot` turns from the joint's rest (degrees XYZ: the mesh's axes when the joint has no `rot`), `at` moves from it, `scale` multiplies;
|
|
303
|
+
keys in the clip's own seconds. A pose plays them like a file's own (`"clips": [{ "clip": "wave", "start": 2.8, "fadeIn": 0.6 }]`:
|
|
304
|
+
crossfades, loops, root motion), and its IK, look-at, bones and layers work on the joints by name (`"thigh.L"` or `"thighL"`).
|
|
305
|
+
|
|
306
|
+
**Check before posing.** A still with `"inspect": true` answers `scene.skeletons["<id>"]`: the `joints` (name, parent, where), the
|
|
307
|
+
`landmarks` used and which were `found` from the shape, and a `report` (`vertices`, `parts`, `unseen` points no bone could see,
|
|
308
|
+
`neutralMaxMove` — 0 —, `ms`, the `fixes` with the points each touched). A landmark found in the wrong place: give it in `landmarks`.
|
|
309
|
+
A joint's region that bends wrong in a pose: fix it (`fixes`) and render again. `scene.warnings` names a landmark a template could not
|
|
310
|
+
find, a fix that touched nothing, a joint name that is not a joint. `POST /api/world3d/validate` checks the fields and every joint name
|
|
311
|
+
a part names (a parent, a fix, a clip's track).
|
|
312
|
+
|
|
313
|
+
**Worked example** — a figure built from the graph, rigged, walking with its feet planted by IK, then waving (a Scene object; the walk's
|
|
314
|
+
clips are keys on the template's joints; each foot's IK target is a point keyed on the floor — held where the foot is planted for 60 % of
|
|
315
|
+
a step, then an arc (`ease: "linear"` keys, or one every 1/30 s) to the next plant one stride on; the pole moves with the body; the object's
|
|
316
|
+
own `at` keys carry it forward):
|
|
317
|
+
|
|
318
|
+
```json
|
|
319
|
+
{ "id": "walker", "at": [-1.8, -0.42, 0], "rot": [0, 90, 0], "material": "clay",
|
|
320
|
+
"keys": { "at": [{ "t": 0, "v": [-1.8, -0.42, 0], "ease": "linear" }, { "t": 2.2, "v": [-0.48, -0.42, 0] }] },
|
|
321
|
+
"graph": [{ "op": "skin", "blend": 0.03, "voxel": 0.012, "smooth": 2, "paths": [
|
|
322
|
+
[[0, 0.93, 0, 0.15], [0, 1.1, 0.005, 0.135], [0, 1.3, 0, 0.155], [0, 1.47, 0, 0.06], [0, 1.53, 0.005, 0.065], [0, 1.635, 0.012, 0.11]],
|
|
323
|
+
[[0.095, 0.9, 0, 0.085], [0.105, 0.5, 0.015, 0.06], [0.11, 0.085, 0, 0.045], [0.11, 0.035, 0.15, 0.035]],
|
|
324
|
+
[[-0.095, 0.9, 0, 0.085], [-0.105, 0.5, 0.015, 0.06], [-0.11, 0.085, 0, 0.045], [-0.11, 0.035, 0.15, 0.035]],
|
|
325
|
+
[[0.08, 1.36, 0, 0.09], [0.2, 1.4, 0, 0.055], [0.42, 1.24, 0, 0.045], [0.6, 1.1, 0.01, 0.035], [0.7, 1.02, 0.02, 0.028]],
|
|
326
|
+
[[-0.08, 1.36, 0, 0.09], [-0.2, 1.4, 0, 0.055], [-0.42, 1.24, 0, 0.045], [-0.6, 1.1, 0.01, 0.035], [-0.7, 1.02, 0.02, 0.028]] ] }],
|
|
327
|
+
"skeleton": { "template": "biped",
|
|
328
|
+
"landmarks": { "hips": [0, 0.95, 0], "chest": [0, 1.3, 0], "neck": [0, 1.46, 0], "head": [0, 1.55, 0.005], "headTop": [0, 1.75, 0.012],
|
|
329
|
+
"shoulder.L": [0.2, 1.4, 0], "elbow.L": [0.42, 1.24, 0], "wrist.L": [0.6, 1.1, 0.01], "handTip.L": [0.7, 1.02, 0.02],
|
|
330
|
+
"hip.L": [0.095, 0.88, 0], "knee.L": [0.105, 0.5, 0.015], "ankle.L": [0.11, 0.085, 0], "toe.L": [0.11, 0.035, 0.185] },
|
|
331
|
+
"clips": [
|
|
332
|
+
{ "name": "walk", "duration": 1.1, "bones": {
|
|
333
|
+
"hips": { "at": [{ "t": 0, "v": [0, -0.038, 0] }, { "t": 0.275, "v": [0, -0.062, 0] }, { "t": 0.55, "v": [0, -0.038, 0] }, { "t": 0.825, "v": [0, -0.062, 0] }, { "t": 1.1, "v": [0, -0.038, 0] }] },
|
|
334
|
+
"upperArm.L": { "rot": [{ "t": 0, "v": [22, 0, -50] }, { "t": 0.55, "v": [-22, 0, -50] }, { "t": 1.1, "v": [22, 0, -50] }] },
|
|
335
|
+
"upperArm.R": { "rot": [{ "t": 0, "v": [-22, 0, 50] }, { "t": 0.55, "v": [22, 0, 50] }, { "t": 1.1, "v": [-22, 0, 50] }] } } },
|
|
336
|
+
{ "name": "wave", "duration": 1, "bones": {
|
|
337
|
+
"upperArm.R": { "rot": [{ "t": 0, "v": [0, 0, -125] }] },
|
|
338
|
+
"forearm.R": { "rot": [{ "t": 0, "v": [0, 0, -15] }, { "t": 0.5, "v": [0, 0, -55] }, { "t": 1, "v": [0, 0, -15] }] } } } ] },
|
|
339
|
+
"pose": {
|
|
340
|
+
"clips": [{ "clip": "walk", "end": 2.55, "fadeOut": 0.5 }, { "clip": "wave", "start": 2.8, "fadeIn": 0.6 }],
|
|
341
|
+
"constraints": [
|
|
342
|
+
{ "type": "ik", "bone": "foot.L", "target": [-1.6, -0.335, -0.11], "pole": [-0.8, 0.08, -0.11],
|
|
343
|
+
"keys": { "target": [{ "t": 0, "v": [-1.6, -0.335, -0.11] }, { "t": 0.66, "v": [-1.6, -0.335, -0.11], "ease": "linear" },
|
|
344
|
+
{ "t": 0.88, "v": [-1.28, -0.265, -0.11], "ease": "linear" }, { "t": 1.1, "v": [-0.96, -0.335, -0.11] }, { "t": 1.76, "v": [-0.96, -0.335, -0.11] }] } },
|
|
345
|
+
{ "type": "ik", "bone": "foot.R", "target": [-1.92, -0.335, 0.11], "pole": [-0.8, 0.08, 0.11], "keys": { "target": [ "… the same steps, half a cycle later" ] } }
|
|
346
|
+
],
|
|
347
|
+
"layers": [{ "id": "look", "mask": { "bones": ["neck"] }, "weight": 0, "keys": { "weight": [{ "t": 2.6, "v": 0 }, { "t": 3.3, "v": 1 }] },
|
|
348
|
+
"constraints": [{ "type": "lookAt", "bone": "head", "chain": 2, "target": [0.1, 1.08, 4], "limit": 75 }] }]
|
|
349
|
+
} }
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
A model file without a skeleton is rigged the same way on its layer: `"model3dConfig": { "fit": "unit", "fitValue": 1000, "skeleton":
|
|
353
|
+
{ "template": "biped", "landmarks": { "shoulder.L": [0.16, 1.29, -0.06] }, "fixes": [{ "joint": "head", "value": 1, "box": { "min":
|
|
354
|
+
[-0.21, 1.4, -0.2], "max": [0.21, 1.76, 0.14] }, "soft": 0.03 }], "clips": [ … ] }, "pose": { … } }` — every other landmark found from its
|
|
355
|
+
shape, and one fix that gives the whole head to the head joint (its automatic weights left a flap at the back of the neck behind when
|
|
356
|
+
the head turned).
|
|
357
|
+
|
|
146
358
|
### Scene Rig — one set for every 3D layer
|
|
147
359
|
|
|
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
|
|
360
|
+
> **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` ([video-world3d.md](video-world3d.md)). The fields below hold in both; where the world reads them differently, the section says so.
|
|
149
361
|
|
|
150
362
|
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.
|
|
151
363
|
|
|
@@ -174,7 +386,7 @@ A **rig** clip carries the project's lights, backdrop and environment. It is a s
|
|
|
174
386
|
}
|
|
175
387
|
```
|
|
176
388
|
|
|
177
|
-
**Which renderer draws it.** On a machine with WebGPU, every Rig is drawn by the **world renderer** (world3d.md
|
|
389
|
+
**Which renderer draws it.** On a machine with WebGPU, every Rig is drawn by the **world renderer** ([video-world3d.md](video-world3d.md)) unless `rig.renderer` is `"classic"`; without WebGPU it falls back to the classic Rig passes. Both read the same `rig`, with these differences:
|
|
178
390
|
|
|
179
391
|
- 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
392
|
- `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.
|
|
@@ -236,6 +448,8 @@ alone.
|
|
|
236
448
|
|
|
237
449
|
**`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
450
|
|
|
451
|
+
**`material.light`** (0–4, absent = 0) makes a `shadowCatcher` floor take more than shadows onto the footage under it, in the 3D Scene's Final: the light the objects add — their bounce in their own colour, their reflections on a glossy floor (`roughness`) — goes over the footage, and Final traces the floor in its own `color`, or wearing `imageUrl` (a picture of the real floor laid flat across the floor's `width` × `depth`, x along `width`, the far edge at the picture's top). Draft shows the shadow alone. The footage call (GreenLight Dash only) sets all of it from the clip; set it yourself for a model over any picture of a floor.
|
|
452
|
+
|
|
239
453
|
**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
454
|
|
|
241
455
|
| Key | Range |
|
|
@@ -244,10 +458,11 @@ alone.
|
|
|
244
458
|
| `sc:<lid>:az` \| `elev` \| `dist` \| `shadowSoftness` | per the fields above; not on `ambient` / `hemisphere` |
|
|
245
459
|
| `sc:<lid>:angle` \| `penumbra` | spot only |
|
|
246
460
|
| `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` |
|
|
461
|
+
| `scc:<lid>:col` | colour; on an `ambient` / `hemisphere` in a world Scene it tints the light it adds to the environment (a `hemisphere`'s `groundColor` tints the lower half) |
|
|
462
|
+
| `sc:cyc:x` \| `y` \| `z` \| `rotX` \| `rotY` \| `rotZ` \| `scale` \| `opacity` | `opacity` is the shadow strength on a `shadowCatcher`; on a Solid or picture backdrop it is the backdrop's own opacity (in a world Scene too) |
|
|
249
463
|
| `scc:cyc:col` | colour |
|
|
250
464
|
| `sc:env:intensity` \| `exposure` \| `rotX` \| `rotY` \| `rotZ` | — |
|
|
465
|
+
| `sc:g~<material>:<param>` | a number of a Scene material's animated texture graph (`edgeGlow`, `fractalNoise`, `motionBeam`, `sweep`, or a library name for one): the preset's range ([video-world3d.md](video-world3d.md#animated-materials-edge-glow-live-noise-beam-sweep-graph-presets)) |
|
|
251
466
|
|
|
252
467
|
**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
468
|
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- The camera lives on a channel marked `"kind": "camera"`. **A timeline may have at most one camera channel, and it must be LAST in its `channels` array** (the project's, and each sequence's for its own camera — see [A sequence's own camera](#a-sequences-own-camera)); channel order is z-order, so last renders highest. The editor re-pins it on every write and on load, so a project authored with the camera elsewhere is silently reordered — emit it last to avoid surprising diffs. Put several camera clips on it to chain moves; the clip under the playhead wins, and outside every camera clip the camera is neutral.
|
|
35
35
|
- Camera clips are synthetic (no `url`) and are keyframed with their own property registry (below), using the ordinary `propertyKeyframes` shape: `position` 0–100 % of the clip, `value` a string.
|
|
36
36
|
- Any channel can opt out with `"cameraLocked": true` — it renders exactly as if there were no camera (watermarks, fixed captions).
|
|
37
|
-
- In a 3D Scene the Camera layer films the world too, depth of field included, unless the Scene names its own `rig.scene.camera` (world3d.md
|
|
37
|
+
- In a 3D Scene the Camera layer films the world too, depth of field included, unless the Scene names its own `rig.scene.camera` ([video-world3d.md](video-world3d.md#turn-it-on-and-write-a-scene)).
|
|
38
38
|
- A camera-locked layer is a 2D layer, as in After Effects: it composites in STACK order and never depth-tests against 3D. Above a model layer in the stack it draws over the model (a flash, a title at the comp plane needs no pulling toward the camera); at the very bottom of the stack (a full-frame backdrop) it stays behind everything, the Scene Rig's cyclorama included. 3D layers on either side of a locked layer still occlude each other by depth.
|
|
39
39
|
|
|
40
40
|
```jsonc
|
|
@@ -81,6 +81,7 @@ Every field below can be a **base value on the camera item** (`item.camAperture
|
|
|
81
81
|
| `camPathT` | % | `0` | Progress along `cameraPath` or a Camera Rig's move (0–100). |
|
|
82
82
|
| `camPathOffsetX/Y/Z` | px | `0` | Constant displacement from `cameraPath`, on world axes (+X right, +Y **down**, +Z toward the viewer). The aim is computed **after** it, so a camera pushed off a shared path still faces its target. No-op unless the path is live. |
|
|
83
83
|
| `camFollowTarget` | 1-based index | `1` | Which `cameraRig.targets` entry a `follow` rig looks at (see [Camera Rig](#camera-rig--a-parametric-camera-move)). |
|
|
84
|
+
| `camLensK1` | — | `0` | Base value only. A real wide lens's one radial distortion term (x_bent = x · (1 + k1 · r²), x in focal lengths; negative = barrel), as a footage solve finds it (tracking-data.md (GreenLight Dash only)). The 3D Scene's picture is bent by it so it lines up with footage shot through that lens; other layers are not. Keep the solve's value; `0` or absent bends nothing. |
|
|
84
85
|
|
|
85
86
|
Animating `camDolly` and `camZoom` in opposite directions is the vertigo effect.
|
|
86
87
|
|
|
@@ -136,7 +137,7 @@ Depth of field is physical and needs `camDofEnabled: 1`. Each layer's circle of
|
|
|
136
137
|
- **Model layers, the Scene Rig's cyclorama and occluding shadow plates blur per pixel.** They hand the compositor their depth buffer, and every pixel blurs by the CoC of its own distance (capped at 64 px, still held sharp inside the focus area). A character in focus stays sharp in the same model as a wall metres behind it, so a whole set, backdrop included, can live in one model layer.
|
|
137
138
|
- **The gather is depth-aware:** a blurred background never smears over a sharp foreground edge, a sharp foreground leaves no halo on the blurred background, and a foreground in front of the focus plane blurs out over what is behind it and past its own silhouette. Taps read the picture prefiltered, so thin bright detail under a wide blur (lettering far behind the focus) averages instead of sparkling; flat layers get the same.
|
|
138
139
|
- **Both distances are view depth from the camera**, so a camera that moves carries the sharp plane with it. Under a dolly the subject at depth `z` (its `panZ`) sits at `perspectiveDistance − camDolly − z` (`perspectiveDistance = (H/2)/tan(14°)`, ≈ 2166 on a 1080-tall frame). To keep a subject sharp through a move, key `camFocusDistance` on the dolly's own keys with that value; under a rig, key it with the subject's depth along the rig's view at a few points of the move (a GreenLight Motion scene script's `dof.focus` does both).
|
|
139
|
-
- **In a 3D Scene** the same numbers set Final's thin lens, so Final blurs as Draft does (see world3d.md
|
|
140
|
+
- **In a 3D Scene** the same numbers set Final's thin lens, so Final blurs as Draft does (see [video-world3d.md](video-world3d.md#turn-it-on-and-write-a-scene)).
|
|
140
141
|
- **Matching a real lens.** For a lens of focal length `f` mm at f-number `N` on a sensor `S` mm wide, with the focus `F` in px (a composition pixel is a millimetre of a 3D Scene) and the frame `W` px wide: `camAperture = f² · W / (2 · N · (F − f) · S)`, at `camBlurLevel` 100. Past the field's 300, raise `camBlurLevel` instead (a `.blend` import keys it this way).
|
|
141
142
|
- **Focus area:** `camFocusWidth` above 0 keeps a screen region around `camFocusX`/`camFocusY` sharp whatever its depth, easing to the full blur by its edge.
|
|
142
143
|
- `camAperture: 0` or `camBlurLevel: 0` renders byte-identically to DOF off. There are no per-layer `dof*` fields — `dof*` fields on a layer animate nothing. The Focus presets in [Built-in camera moves](#built-in-camera-moves) are ready rack-focus moves.
|
|
@@ -1586,7 +1586,7 @@ On a `model3d` item, these Overlay Effects have a REAL 3D version that renders i
|
|
|
1586
1586
|
|
|
1587
1587
|
`rgbSplit`, `pixelate`, `crtMatrix`, `halftone`, `objectDither`, `ascii`, `fractalNoise`, `fill`, `colorama`, `gradientBeam`, `gradientSweep`, `objectContour`, `objectCurtain`, `bloom`, `auraBlur`, `lightRays`, `neonGlow`, `linesDraw`, `edgeDetect`, `flashBlink`, `glitch`, `objectSlitScan`, `pixelStretch`, `shapeRulers`, `curves`.
|
|
1588
1588
|
|
|
1589
|
-
- In `capabilities.json` these effects carry `real3dOnModels: true`; 3D-only params and options carry `model3d: true`.
|
|
1589
|
+
- In `capabilities.json` these effects carry `real3dOnModels: true`; 3D-only params and options carry `model3d: true`. `inWorldScene` says what each one does on a model inside a world Scene (below): `"onObject"`, `"ownPass"` or `"skipped"`.
|
|
1590
1590
|
- The 3D version runs only when the entry says `"real3d": true` AND its id is in the list. The editor sets it when one of these is ADDED to a model layer; `real3d` switches an entry between the two. Any other effect on a model stays on the flat chain.
|
|
1591
1591
|
- Same `params`, Mix, Active Range and `fx:<eid>:*` keyframes as the flat effect. `clip` and the Object family's tracking params are ignored in 3D — the model IS the subject.
|
|
1592
1592
|
- What changes in 3D, in short: patterns (Mosaic Layer grid, Pixel Matrix, Halftone, ASCII, Dither, Fractal Noise) are locked to the SURFACE and turn with the model; lighting effects (Halftone dot size, Fill's shading, Colorama, Flash) use the model's real light and normals; RGB Split re-films the model from two shifted cameras (real parallax); Bloom thresholds the displayed light but blooms the HDR light; Beam / Sweep / Contour / Glitch / Slit Scan / Pixel Stretch run along the MODEL's own axes; Neon Glow, Lines Draw and Edge Detect trace its real outline, creases and depth steps; Light Rays' origin sits in the model's box and the body occludes its own rays; Shape Rulers draws its real 3D bounding box; Curves applies to the HDR light in tone space. Bloom's Threshold / Soft Knee are judged on the displayed light (same meaning as on the flat effect), and what blooms is the HDR light, so a strong highlight glows more than a clipped one.
|
|
@@ -1594,6 +1594,18 @@ On a `model3d` item, these Overlay Effects have a REAL 3D version that renders i
|
|
|
1594
1594
|
- Light that leaves the silhouette (halos, streaks, the ruler box, RGB Split's copies) is composited at the model's centre depth, so it is occluded by — and occludes — other 3D layers like the model itself.
|
|
1595
1595
|
- Cost: nothing until a model carries one of these. The passes and their buffers load and allocate on first use.
|
|
1596
1596
|
|
|
1597
|
+
### In a world Scene (`rig.renderer: "world"`)
|
|
1598
|
+
|
|
1599
|
+
A model inside a world Scene is an object in the 3D world. Write its effects exactly as above (`"real3d": true`, same params, Mix, Active Range, keyframes); what each one does there is its `inWorldScene`:
|
|
1600
|
+
|
|
1601
|
+
- `"onObject"`: `curves`, `neonGlow`, `pixelate` (Voxels too), `fractalNoise`, `gradientBeam`, `gradientSweep`, `bloom`, `linesDraw`, `shapeRulers`, `edgeDetect`. They run in the world, on that object: it keeps its shadow, Final traces it, it shows in reflections, and the effect looks the same in Draft and Final. They run on the world's picture inside the model's outline, before the Scene's tone and film, so the Film panel's look applies over them; a glow or a halo spills onto what is around the model.
|
|
1602
|
+
- `"ownPass"`: `rgbSplit`, `glitch`, `pixelStretch`, `objectSlitScan`, `objectCurtain`, `flashBlink`. They move the picture off the surface, so while one of them is live on the model (enabled, Mix above 0, inside its Active Range, the layer's effect Amount above 0) the model leaves the world: its own pass draws it over the Scene, it casts no shadow and Final does not trace it, and its other effects run in that pass. On every other frame it is in the world. Give the effect an Active Range to keep the model in the world for the rest of the shot. Each switch rebuilds the world's scene, so do not key one on and off many times. Out of the world the model stands where it stood in it, filmed by the same camera (`rig.scene.camera` included), lit by the world's lights and environment, its contact shadows (GTAO) on it in Draft and the floor's bounce light in Final, and filmed through the Scene's Look and exposure; what it does not get is the world's shadows on its own faces and, in Final, the rest of the traced bounce light and its reflections.
|
|
1603
|
+
- `"skipped"`: `crtMatrix`, `halftone`, `objectDither`, `ascii`, `fill`, `colorama`, `objectContour`, `auraBlur`, `lightRays`. Not available on a model in a world Scene: a saved entry, flat or 3D, stays in the project and is skipped there. Do not add them to a model in a world Scene; put them on an adjustment layer above the Scene, or use a Rig with `renderer: "classic"`.
|
|
1604
|
+
- Outside a world Scene (no Scene layer, or a Rig with `renderer: "classic"`) all of them work on a model as described above.
|
|
1605
|
+
- The layer's colour filters (`filterBrightness`, `filterContrast`, `filterSaturation`, `filterHueRotate`, `filterGrayscale`, `filterSepia`, `filterInvert`, keyed or not) run there too, after the effects. `filterBlur` moves the picture: a blurred model leaves the world for its own pass (no shadow, not traced), and so does a model with any other flat effect (an entry without `"real3d": true` that is not `"skipped"`).
|
|
1606
|
+
- Grow Fill on a model in a world Scene is the object's own material: the fill is lit, bounces its colour and shows in reflections in Final; its glows light what is near. Unfill and Reveal cut the surface away (shadows follow).
|
|
1607
|
+
- A setting that lets the model's own picture go (Edge Detect's, Neon Glow's or Lines Draw's Source Level below 100; Gradient Beam's Layer Mask below 100; Pixelate's Voxels where no cube stands) shows the set behind the model there: the world draws itself once more without that model, in raster, while such a setting is on (in Final too, so that part is not traced).
|
|
1608
|
+
|
|
1597
1609
|
# GreenLight Dash — Layer masks
|
|
1598
1610
|
|
|
1599
1611
|
> Part of the `greenlight-dash` skill. Read [video-effects.md](#greenlight-dash--video-editor-layer-effects-and-overlay-effects) for the Layer Effects storage contract. A roto result from the tracker is an ordinary mask; its data contract is in tracking-data.md (GreenLight Dash only).
|
|
@@ -34,6 +34,12 @@ Every timeline item can animate its visual properties over the clip's duration.
|
|
|
34
34
|
- **`easing`** shapes the segment **after** its key (see [Easings](#easings)).
|
|
35
35
|
- A **single-key track holds its value** for the whole clip.
|
|
36
36
|
- No two keys of one track may share a `position`; omit a track with zero keys.
|
|
37
|
+
- **A move is two keys and the easing between them**, never a key on every frame: per-frame keys cannot be edited in
|
|
38
|
+
the Keyframes panel and run straight chords between frames. An overshoot or a settle is an easing or an expression,
|
|
39
|
+
not more keys. A curve you computed or measured is fitted to the fewest eased keys first
|
|
40
|
+
(motiongram/keys.md (GreenLight Dash only), `motiongram/seam-craft/tools/lib/keyfit.py`). A curved trajectory (an
|
|
41
|
+
arc, a loop, an orbit) is a `motionPath` with a two-key `motionPathT`, not X and Y keys ([video-paths.md](video-paths.md)).
|
|
42
|
+
Scale or turn about a point with `keyframeTransformOrigin`, not with translate keys that compensate.
|
|
37
43
|
|
|
38
44
|
```json
|
|
39
45
|
{
|