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.
108 lines
7.0 KiB
Markdown
108 lines
7.0 KiB
Markdown
# ADR 0031: Animation Authoring And Runtime Contract
|
|
|
|
## Status
|
|
|
|
Accepted
|
|
|
|
## Context
|
|
|
|
Blacksite previously recorded animation and skin counts while importing glTF and FBX models, but the
|
|
authored scene contract contained only a path-based `ModelRef`. Generic scene-instance hydration loaded 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 `SkinnedMeshRenderer` 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 dedicated `SkinnedMeshRenderer` 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.
|
|
|
|
Animation-manifest schema v3 adds an optional stable default clip sub-asset ID sourced from model
|
|
import settings. It is an explicit presentation policy: renderable animated-model placement creates
|
|
a one-state controller only when this ID resolves. An absent value preserves the imported node pose;
|
|
the importer and placement workflow never infer a first clip. Generic scene-instance placement does
|
|
not consume this setting.
|
|
|
|
The runtime adapter waits for the renderer's `HydratedSkinnedMeshRoot` 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.
|
|
|
|
When a hydrated hierarchy becomes ready, the runtime captures its descendant `Transform` and
|
|
`MorphWeights` components before evaluating authored playback. Edit mode samples the configured
|
|
default state at its range start and pauses it. Preview stop, controller removal, failed graph
|
|
reconfiguration, and PIE exit first stop playback and restore the captured baseline; if a valid
|
|
default remains, it is then sampled again without a crossfade. Skin validation checks each imported
|
|
skin independently for joint/inverse-bind count and hierarchy ownership, including sources that
|
|
reuse one joint hierarchy across multiple skin records.
|
|
|
|
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.
|
|
- Explicit edit-rest clips avoid malformed source presentation poses without coupling rendering to
|
|
animation; assets without a configured clip keep their imported rest pose.
|
|
- Baseline restoration prevents stopped previews and mode changes from leaving sampled transforms or
|
|
morph weights behind.
|
|
- 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.
|
|
- The static/skinned renderer boundary and v2-to-v3 scene migration are defined by
|
|
[ADR 0033](0033-dedicated-skinned-mesh-renderer.md).
|