Merge remote-tracking branch 'origin/main' into HEAD

This commit is contained in:
bflatastic
2026-10-30 09:01:36 +07:00
48 changed files with 1938 additions and 50 deletions
+6 -1
View File
@@ -463,7 +463,12 @@ and Origin), `camera.createFromSolve {kind: text|solid|null|shadowCatcher, point
{center, normal, size?}, multiple?}` (Create Text / Solid / Null / Shadow Catcher, Camera and Light,
Create Multiple …), `camera.deletePoints {points?, wait?}` (re-solves; with Auto-delete Points
Across Time the same feature's other tracks go too) and `camera.create` (the Create Camera button).
Each is one undo step; the first create adds the one-node "3D Tracker Camera" keyed on every frame.
Each edit is one undo step; the first create adds the one-node "3D Tracker Camera" keyed on every frame.
`camera.exportBlender {path: ".py", layer?, effect?, sensorWidth?: 36, unitScale?: 0.01,
startFrame?: 1, includePoints?: true}` exports the solve as an executable Blender scene script:
baked camera/lens/focus, synced video background, points and ground. See
[Blender camera export](blender-camera-export.md) for framing, assumptions and limitations.
Changing the layer's frames clears the analysis; changing `effects/#1/shotType`,
`effects/#1/horizontalAngleOfView`, `effects/#1/advanced/solveMethod` re-solves the stored tracks.
+101
View File
@@ -0,0 +1,101 @@
# Exporting a tracked camera to Blender
After the **3D Camera Tracker** finishes solving, click **Export Scene to Blender...** in its
Effect Controls and save the `.py` file. In Blender 4.0 or newer, open it in the Scripting
workspace's Text Editor and choose **Run Script**. The script creates and activates a new scene;
it preserves existing scenes and objects. Save the resulting `.blend` normally.
The script uses Blender's public Python API, with no add-on or Python dependencies beyond
Blender itself. For unattended use: `blender --background --python "Tracked Scene.py"`.
That imports the scene in memory; it does not automatically save a `.blend`.
## Transferred data
- Camera position and quaternion rotation baked on every analyzed frame, with consistent
quaternion signs and linear interpolation; zoom/lens animation, including Variable Zoom.
- Tracking input/video resolution, horizontal sensor fit, exact rational composition FPS,
frame range and composition markers within the analyzed interval. The first analyzed frame
becomes Blender frame 1 by default; the original composition timestamps remain in metadata.
- The original video or still as the camera's background image, at full opacity, refreshed on
frame changes. Source-frame offsets are baked too, accounting for trim, stretch, source FPS
and Time Remap. This is a viewport background, not a render/compositor background.
- Focus distance, DOF switch, f-stop, iris blade count, rotation and aspect sampled from the
tracker's linked **3D Tracker Camera**, including keyframes and expressions. Create that
camera and adjust its Camera Options before exporting to transfer custom focus settings.
- Solved 3D points as a non-rendering vertex mesh, with point attributes for track ID,
reprojection error and first/last visible frame. A chosen ground plane becomes Blender's XY
plane, with its origin preserved and a non-rendering wire plane as a placement guide.
- Shutter duration (not automatically enabled), plus the original shutter phase in metadata.
- A Blender Text datablock named `EffectCraft Tracking Metadata.json` containing the complete
solve, optional original 2D tracks, per-frame timings, source interpretation, diagnostics and
export warnings. Radial distortion coefficients are also camera custom properties.
Coordinates are converted from EffectCraft's right/down/forward world to Blender's
right/forward/up world, including the camera's local up and negative-Z viewing direction.
Export uses the tracking input's framing, not the footage layer's final 2D placement in the
composition. Camera pose and lens always come from the solve; subsequent manual pose/lens edits
on the linked camera are not substituted for the tracked motion.
## What cannot be inferred
Monocular tracking cannot recover absolute scene scale, physical sensor size, optical focus,
f-stop or exposure from feature tracks. The default sensor width is an assumption of **36 mm**;
the focal length in millimeters is derived from it while preserving the solved field of view.
The default scale is **0.01 Blender units per composition-world pixel**, not a measurement.
Without a linked camera, DOF is disabled and focus distance uses the default solved scene depth.
Blender's physical DOF approximates EffectCraft's blur model: Blur Level and Iris Roundness are
retained in metadata, but have no direct Blender equivalent.
Brown-model radial `k1`/`k2` coefficients use radius normalized to half the image diagonal.
They are preserved, **not** assigned to Blender's different lens-distortion model. For a precise
match when distortion is nonzero, use an undistorted plate; the script prints a warning.
The tracker assumes square pixels, so Blender renders square pixels; any non-square source
interpretation is retained in metadata and flagged.
The `.py` references the video; it does not contain a copy of the video's bytes. When moving it,
keep the video next to the script (same filename), or relink the background image in Blender.
Missing/unsupported media produces a warning while retaining the imported camera and geometry.
Masks and effects applied before the tracker are not baked into the original media. Export
requires a direct video/still footage layer at the analyzed size; render precomps, sequences or
size-changing preprocessing to a video and track that video first.
## Agent / control-channel command
```json
{
"command": "camera.exportBlender",
"params": {
"layer": "#1",
"path": "/output/Tracked Scene.py",
"sensorWidth": 36,
"unitScale": 0.01,
"startFrame": 1,
"includePoints": true
}
}
```
`comp`, `layer` and `effect` select the tracker as with `camera.analyze`. Without explicit
selection, the selected tracker (or first tracker in the composition) is used. `sensorWidth`,
`unitScale`, `startFrame` and `includePoints` are optional. The reply contains file size,
exported frame/point counts and warnings. Writing uses the host's atomic file service.
Calls issued by scripts require **Allow Scripts to Write Files and Access Network** in
Settings ▸ Scripting & Expressions. Export from the UI or control channel remains available
with that preference off.
Export is limited to 100,000 analyzed frames, 100,000 points and 64 MiB of serialized scene data;
invalid, stale or incomplete solves return an actionable error without modifying the document.
## Verification
Engine tests check argument validation, escaped/Unicode paths, fractional FPS, trim/stretch,
focus/f-stop, zoom, ground placement, malformed solves and write failures. To additionally run
the generated artifact against an installed Blender's public API:
```sh
EFFECTCRAFT_BLENDER=/path/to/blender cargo test -p effectcraft-engine tests::blender -- --nocapture
```
Optionally set `EFFECTCRAFT_BLENDER_PLATE` to a local synthetic 640×360 video to exercise media
loading and source-frame offsets. The oracle checks every synthetic point's projection through
the imported camera on every frame, as well as animation, DOF, timing and scene preservation.
+1 -1
View File
@@ -20,7 +20,7 @@ our own scripts; no real-world script library has been run against both apps.
| Expression performance | Cached, multi-frame | Per-thread boa contexts | unknown | No benchmark |
| Scripting object model | `app`, `Project`, items, layers, properties, keys, eases, render queue, `TextDocument`, `Shape`, markers, `File`/`Folder`, `Socket`, `app.settings` | Same classes, edits run through undoable engine commands; match names table; layers, groups and properties are callable (`layer("Effects")(1)` is `.property(...)`) | good | `crates/script` tests (≈ 10 test files) |
| `$.evalFile`, `BridgeTalk` | Evaluate another script file; identify the host application and message other Adobe applications | `$.evalFile` evaluates a file relative to the calling file (nested at most 32 deep, under the scripts' file gate) and returns its last value; `BridgeTalk` names the host honestly (`appName` `effectcraft`, its version); messaging other applications throws "not supported" | partial | `tests_evalfile.rs` (#760); no inter-application messaging |
| ScriptUI | Dialogs, palettes, dockable panels, resource strings, `onDraw` / ScriptUIGraphics, mouse events | Same, dockable panels reopen at launch | good | `ui_scriptui`, `tests_ui*` |
| ScriptUI | Dialogs, palettes, dockable panels, resource strings, `onDraw` / ScriptUIGraphics, mouse events | Same, dockable panels reopen at launch; a docked panel gets its dock's size and `onResize` (`scriptui.resize`, #2585); a drag keeps reaching the control it started on past the window edge, and `MouseEvent.buttons` reports the held buttons (#2584) | good | `ui_scriptui`, `tests_ui*` |
| `app.scheduleTask` / `cancelTask` | One-shot and repeating tasks (polling, panel refresh) until `cancelTask` | Same: repeating tasks run every `delay` ms (at least 50 ms, at most 64 pending per script) until `cancelTask`, until they throw (reported once, then cancelled), or until their owner goes: a script's last window closing, or the project closing for scripts without windows (a Startup script's tasks go on until EffectCraft quits); frontends tick them every frame, agents with `script.runTasks` | good | `tests_tasks.rs` (#773); one-shot tasks of a script file still run right after it, not after their delay |
| Scripts menu, ScriptUI Panels folder | yes | yes; a subfolder (one level) of either folder is a submenu named after it | same | [plugins.md](plugins.md) |
| Startup / Shutdown folders | yes | `Scripts/Startup` runs at launch and `Scripts/Shutdown` runs as the desktop app quits, after the project is closed, each in alphabetical order | partial | `crates/script/src/tests/startup.rs`; each script has its own global scope, so a Startup script's variables and functions are not available to later scripts ([plugins.md](plugins.md)) |