113 lines
6.8 KiB
Markdown
113 lines
6.8 KiB
Markdown
# Animation Authoring
|
|
|
|
Blacksite's first production animation path supports skinned glTF 2.0 models (`.gltf` and `.glb`).
|
|
It normalizes imported rig and clip metadata into generated manifests, keeps authored scene data
|
|
independent of Bevy's runtime graph/player components, and uses the same controller contract in
|
|
editor preview, PIE, and standalone builds.
|
|
|
|
## Import And Placement
|
|
|
|
Place animated sources under `assets/` and refresh or reimport them from the Asset Browser. The
|
|
registry writes `assets/animations/generated/<asset-id>.animation.ron` beside the existing static
|
|
mesh artifact. Expanding a model row shows:
|
|
|
|
- skeleton entries with joint count and signature summary;
|
|
- animation clips with duration and event count;
|
|
- mesh, material, and texture entries from the static import contract.
|
|
|
|
Model Import Settings also exposes **Default animation**. Choosing a clip stores its stable
|
|
sub-asset ID and makes normal renderable-model placement create a one-state controller for that
|
|
exact clip. The editor samples the clip at its authored range start and pauses it as the edit-mode
|
|
rest presentation. **Imported rest pose** creates no controller and preserves the source node pose;
|
|
Blacksite never guesses the first clip. Generic Scene Instance placement remains animation-free.
|
|
|
|
Skeleton rows are inspect-only. Dragging an animation clip into the viewport creates one
|
|
`SkinnedMesh` actor with a stable `SkinnedMeshRenderer` and `AnimationControllerDesc` and selects it for
|
|
immediate inspection. When a skinned-mesh actor is selected, the same drag assigns the clip as a
|
|
new controller state instead. Assignment is one undoable command and requires an exact
|
|
skeleton-signature match.
|
|
|
|
Skinned primitive rows place their owning model through `SkinnedMeshRenderer` because their vertex
|
|
data requires the source skin hierarchy. Their thumbnails render the complete source scene; no
|
|
drag, details action, or context-menu action can create an invalid detached static-mesh slot.
|
|
Unrigged primitives continue to place independently through `StaticMeshRenderer`.
|
|
|
|
## Controller Inspector
|
|
|
|
The Animation Controller component exposes the selected skeleton, default crossfade, named states,
|
|
default state, clip, loop, speed, and optional playback range. State IDs are gameplay keys and must
|
|
be non-empty and unique; labels are presentation only. Component add, remove, reset, copy, paste,
|
|
and property changes use typed editor history and participate in scene dirty tracking.
|
|
|
|
The Preview section selects a state, plays or pauses it, stops playback, and scrubs within the
|
|
authored range. Preview requests use the game runtime adapter and do not change the controller,
|
|
history, or scene dirty state. Entering PIE clears any paused edit-mode scrub so the authored
|
|
default state can run; leaving PIE clears transient playback while retaining authored data. The
|
|
runtime captures imported descendant transforms and morph weights before evaluating animation.
|
|
Stop, controller removal, invalid graph rebuild, and PIE exit restore that baseline before the
|
|
configured edit-rest state is sampled again, so a preview cannot strand the actor in its last pose.
|
|
|
|
## Runtime API
|
|
|
|
Gameplay changes state by sending a validated request through `game::animation`:
|
|
|
|
```rust
|
|
game::animation::request_animation_state(
|
|
world,
|
|
game::animation::AnimationStateRequest::new(actor, "run").with_crossfade(0.15),
|
|
)?;
|
|
```
|
|
|
|
An omitted crossfade uses the controller default. Invalid actor or state requests return an error
|
|
and preserve current playback. If edited controller data cannot rebuild its runtime graph, the old
|
|
graph is stopped and cleared instead of continuing stale playback. `AnimationGraph`,
|
|
`AnimationPlayer`, transitions, preview state, and hydrated skinned-mesh roots are runtime-only and never
|
|
serialize into authored scenes.
|
|
|
|
## Stable Identity And Moves
|
|
|
|
`SkinnedMeshRenderer`, skeleton references, and clip references carry the model registry UUID. Generated
|
|
artifacts are keyed by that UUID; source paths are cached resolution and repair hints. When one
|
|
model is moved or renamed on disk, catalog refresh reconciles it by the registry's shared
|
|
content-addressed source fingerprint, preserves the UUID, and regenerates both manifests with the
|
|
new source path. The same registry policy covers textures and audio clips. An identical copy is not
|
|
treated as a move while the original still exists. Ambiguous content matches intentionally receive
|
|
a new identity rather than silently binding the wrong asset.
|
|
|
|
Scene schema v3 migrates legacy animated actors that paired `ModelRef` with
|
|
`AnimationControllerDesc` to the dedicated skinned renderer. Generic `ImportedModel + ModelRef`
|
|
scene instances remain readable and intentionally do not become animation actors.
|
|
|
|
Skeleton compatibility hashes the exact Bevy 0.19 glTF node-name segments and bind poses, including
|
|
whitespace and slash characters. The readable joint paths shown by the editor are deliberately not
|
|
used as identity. Animation-manifest schema v1 artifacts must be reimported to generate schema v2
|
|
signatures; schema v3 adds the explicit default-clip ID. Schema v2 deserializes as Imported rest
|
|
pose and never receives an inferred default.
|
|
|
|
Source identity uses exact byte length plus BLAKE3 and never checkout mtime. Existing animation
|
|
schema-v3 files may retain a legacy `modified_unix_secs` field in their exact bytes; the current
|
|
reader ignores it, and an equivalent refresh preserves the whole file unchanged. Static-mesh schema
|
|
v4 uses the same content fingerprint. See
|
|
[ADR 0043](../adr/0043-content-addressed-import-fingerprints.md).
|
|
|
|
## Validation And Format Boundary
|
|
|
|
Interactive diagnostics and `cargo validate-levels` resolve registry UUIDs, generated manifests,
|
|
stable subasset IDs, source dependencies, default/state IDs, playback ranges, speeds, crossfades,
|
|
runtime format support, explicit default-clip resolution, exact source-content fingerprints, and
|
|
exact rig compatibility. Findings
|
|
identify the owning actor and controller property and include a repair action. Packaging includes
|
|
the same source files and generated artifacts checked by validation.
|
|
|
|
FBX remains supported for static model import only. An FBX containing skin deformers or animation
|
|
stacks emits a blocking diagnostic directing the author to convert it to glTF. Animation v1 does
|
|
not support one glTF asset containing animation channels under multiple top-level roots, because
|
|
Bevy creates a separate `AnimationPlayer` for each root. Split those rigs into separate assets or
|
|
export them beneath one common root. Animation v1 also does not provide implicit retargeting, blend
|
|
trees, a visual state graph, root motion, IK, or motion matching.
|
|
|
|
See [ADR 0031](../adr/0031-animation-authoring-runtime-contract.md) for the controller/runtime
|
|
boundary, [ADR 0033](../adr/0033-dedicated-skinned-mesh-renderer.md) for renderer ownership, and
|
|
[the working implementation plan](../../.cursor/plans/animation_authoring_2026-07-11.plan.md)
|
|
for acceptance gates.
|