88 lines
5.5 KiB
Markdown
88 lines
5.5 KiB
Markdown
# ADR 0031: Animation Authoring And Runtime Contract
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
Blacksite currently records animation and skin counts while importing glTF and FBX models, but the
|
|
authored scene contract contains only a path-based `ModelRef`. Scene-instance hydration loads a
|
|
model world without selecting or driving its clips, and the editor has no stable clip references,
|
|
controller inspector, preview, or compatibility validation.
|
|
|
|
Bevy 0.19 already exposes complete glTF scene, skin, `AnimationClip`, `AnimationGraph`, and
|
|
`AnimationPlayer` primitives. The patched FBX loader does not: it parses some skin metadata but does
|
|
not build the source node hierarchy, attach Bevy skinned-mesh bindings, or convert animation curves
|
|
into clips. Treating its metadata counts as playback support would create scenes that appear valid
|
|
but remain static at runtime.
|
|
|
|
Animation authoring also needs a boundary between persistent intent and transient Bevy player state.
|
|
Clip references must survive project asset moves, preview must agree with PIE, and a game extension
|
|
must be able to request authored states without depending on editor types.
|
|
|
|
## Decision
|
|
|
|
Animation authoring v1 supports glTF 2.0 sources (`.gltf` and `.glb`) through Bevy's native loader.
|
|
FBX remains a supported static-model source only. Import or validation of an FBX containing
|
|
animation stacks or skin deformers must produce an actionable blocking diagnostic until the loader
|
|
can construct hierarchy, Bevy skinned meshes, and animation clips.
|
|
|
|
Each animated model has a versioned generated manifest under `assets/animations/generated/`, keyed
|
|
by the model's stable asset-registry UUID. The manifest records the source path and fingerprint,
|
|
skeletons and their exact signatures, stable clip sub-asset IDs, clip label/index/duration, target
|
|
skeleton signature, normalized event markers, and import diagnostics. Registry UUID plus sub-asset
|
|
ID is authoritative; cached `ModelRef` and `EditorAssetRef` source paths are only runtime-resolution
|
|
and repair hints. UUID-keyed generated manifests provide the current source path after a uniquely
|
|
reconciled move.
|
|
|
|
`shared` owns reflected, serializable `AnimationControllerDesc` and its named state/event value
|
|
types. A controller sits beside the model scene reference and uses `EditorAssetRef` for skeleton and
|
|
clip references. It contains one layer of named states, one default state, a default crossfade
|
|
duration, and per-state playback range, loop, and speed. State IDs are stable authored identifiers;
|
|
display labels are not runtime keys.
|
|
|
|
Retargeting v1 requires an exact skeleton-signature match. The signature is a deterministic hash of
|
|
ordered joint target paths and bind-pose structure. For glTF, each path is hashed as a
|
|
length-prefixed vector of the exact UTF-8 node-name bytes Bevy 0.19 uses, including whitespace and
|
|
path separators; unnamed nodes use Bevy's `GltfNode{index}` fallback. The readable normalized paths
|
|
stored in the manifest are display metadata and never compatibility keys. Blacksite does not infer
|
|
humanoid roles, rename joints, or perform proportional remapping in this version. This exact node
|
|
identity contract is animation-manifest schema v2; v1 artifacts must be reimported.
|
|
|
|
The runtime adapter waits for the model `WorldAssetRoot` instance to report readiness, finds its
|
|
animation player, and derives Bevy graph/player state from the controller. Generated instance,
|
|
graph, player, transition, and playback bookkeeping is transient and excluded from authored scene
|
|
serialization. Rehydration owns a dedicated generated instance root and must not despawn authored
|
|
children attached to the actor.
|
|
|
|
`game` exposes a small request API that changes an animated actor to a named state and applies the
|
|
authored/default crossfade. Invalid state requests are rejected without disturbing the active
|
|
state. The editor's preview and scrub controls use the same runtime adapter; starting, stopping, or
|
|
scrubbing a preview never changes the authored descriptor, history, or scene dirty state.
|
|
|
|
Interactive diagnostics, headless validation, and packaging resolve the same manifest and enforce
|
|
source availability, stable reference resolution, supported formats, valid playback ranges/events,
|
|
and exact skeleton compatibility.
|
|
|
|
Animation v1 supports one top-level animated hierarchy per glTF asset. Import scans every animation
|
|
channel using the same top-level roots Bevy uses for `AnimationPlayer` placement. A source requiring
|
|
multiple players is marked runtime-unsupported with a blocking repair diagnostic; artists must split
|
|
the rigs into separate assets or export all clips beneath one common root.
|
|
|
|
## Consequences
|
|
|
|
- glTF/GLB is the only animation interchange format promised by v1; artists must convert animated
|
|
FBX sources before import.
|
|
- Stable registry and sub-asset identities allow source moves without rewriting every controller,
|
|
while ambiguous or removed clips become explicit validation failures.
|
|
- One-layer named states provide a deterministic gameplay integration point without committing the
|
|
editor to a visual graph format prematurely.
|
|
- Exact-signature retargeting is intentionally restrictive but cannot silently deform the wrong rig.
|
|
- Rejecting multiple animation roots prevents the runtime adapter from selecting an arbitrary first
|
|
player and driving only part of a source.
|
|
- A future visual graph, blend trees, root motion, IK, advanced retargeting, or animated FBX support
|
|
must extend or supersede this contract explicitly rather than leaking runtime Bevy components into
|
|
authored scenes.
|
|
- Implementation and production acceptance are tracked in Gitea issue #46.
|