Add dedicated skinned rendering, pose restoration, shared Material and Material Instance slots, registry-driven components, Surface/Solari integration, transactional schema upgrades, navigation authoring, documentation, and evaluation evidence.
106 lines
6.4 KiB
Markdown
106 lines
6.4 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 its animation-manifest content
|
|
hash, preserves the UUID, and regenerates both manifests with the new source path. 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.
|
|
|
|
## 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.
|