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.
6.4 KiB
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:
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 for the controller/runtime boundary, ADR 0033 for renderer ownership, and the working implementation plan for acceptance gates.