|
| 1 | +# Feature Name: `animation-primitives` |
| 2 | + |
| 3 | +## Summary |
| 4 | + |
| 5 | +Animation is a particularly complex, with many stateful and intersecting |
| 6 | +elements. This RFC aims to detail a set of lowest-level APIs for authoring and |
| 7 | +playing animations within Bevy. |
| 8 | + |
| 9 | +## Motivation |
| 10 | + |
| 11 | +Animation is at the heart of modern game development. A game engine without an |
| 12 | +animation system is generally considered not yet production ready. |
| 13 | + |
| 14 | +This RFC aims to detail the absolute lowest level APIs to unblock ecosystem |
| 15 | +level experimentation with building more complex animation systems (i.e. inverse |
| 16 | +kinematics, animation state machine, humanoid retargetting, etc.) |
| 17 | + |
| 18 | +## Scope |
| 19 | + |
| 20 | +Animation is a huge area that spans multiple problem domains: |
| 21 | + |
| 22 | + 1. **Storage**: this generally covers on-disk storage in the form of assets as |
| 23 | + well as the in-memory representation of static animation data. |
| 24 | + 2. **Sampling**: this is how we sample the animation assets over time and |
| 25 | + transform it into what is applied to the animated entities. |
| 26 | + 3. **Application**: this is how we applied the sampled values to animated |
| 27 | + entities. |
| 28 | + 4. **Composition**: this is how we compose simple clips to make more complex |
| 29 | + animated behaviors. |
| 30 | + 4. **Authoring**: this is how we create the animation assets used by the engine. |
| 31 | + |
| 32 | +This RFC specifically aims to resolve only problems within the domains of storage |
| 33 | +and sampling. Application can be distinctly decoupled from these earlier two |
| 34 | +stages, treating the sampled values as a black box output, and composition and |
| 35 | +authoring can be built separately upon the primitives provided by this RFC and |
| 36 | +thus are explicit non-goals here. |
| 37 | + |
| 38 | +## User-facing explanation |
| 39 | + |
| 40 | +The core of this system is a trait called `Sample<T>` which allows sampling |
| 41 | +values of `T` from an underlying type at a provided time. `T` here can be |
| 42 | +anything considered animatable. A few examples of high priority types to be |
| 43 | +supported here are: |
| 44 | + |
| 45 | + - `f32`/`f64` |
| 46 | + - `Vec2`/`Vec3`/`Vec3A`/`Vec4` (and their `f64` variants) |
| 47 | + - `Color` |
| 48 | + - `Option<T>` where `T` can also be sampled |
| 49 | + - `bool` for toggling on and off features. |
| 50 | + - `Range<T>` for a range for randomly sampling from (think particle systems) |
| 51 | + - `Handle<T>` for sprite animation, though can be generically used for any asset |
| 52 | + swapping. |
| 53 | + - etc. |
| 54 | + |
| 55 | +Built on top of this trait is the concept of a **animation graph**, a runtime |
| 56 | +mutable directed acyclic graph of nodes for altering the sampling behavior for |
| 57 | +a given property. There is always one root level node that is directly sampled |
| 58 | +from the app world. It can either be a terminal node, or be a composition node |
| 59 | +that samples it's children for values and combines the outputs before passing it |
| 60 | +upstream. Some examples include: |
| 61 | + |
| 62 | + - `MixerNode<T>` - a stateful multi-input composition node that outputs a |
| 63 | + weighted sum of it's inputs. Can be used to make arbitrary blending of it's |
| 64 | + inputs. |
| 65 | + - `SelectorNode<T>` - a multi-input composition node that outputs only the |
| 66 | + currently selected input as it's output. All other inputs are not evaluated. |
| 67 | + Like a MixerNode with a one-hot weight blend, but more computationally |
| 68 | + efficient. |
| 69 | + - `ConstantNode<T>` - only outputs a constant value all times. |
| 70 | + - `RepeatNode<T>` - a single input node that loops it's initial input over time. |
| 71 | + - `PingPongNode<T>` - a single input node that loops it's initial input over |
| 72 | + time, will |
| 73 | + - `Arc<dyn Sample<T>>`/`Box<dyn Sample<T>>` - Anything that can be sampled can |
| 74 | + be used as an input to the graph. |
| 75 | + |
| 76 | +The final lowest level and the concrete implementors of Sample are implemetors |
| 77 | +of the trait `Curve<T>`. Curves are meant to store the raw data for time-sampled |
| 78 | +values. There may be multiple implementations of this trait, and they're |
| 79 | +typically what is serialized and stored in assets. |
| 80 | + |
| 81 | +Finally the last major introduction is the `AnimationClip` asset, which bundles a |
| 82 | +set of curves to the asoociated `Reflect` path they're bound to. This is the main |
| 83 | +metadata source for actually binding sampled outputs to the fields they're |
| 84 | +animating. |
| 85 | + |
| 86 | +## Implementation strategy |
| 87 | + |
| 88 | +Protoytpe implementation: https://github.com/HouraiTeahouse/bevy_prototype_animation |
| 89 | + |
| 90 | +TODO: Complete this section. |
| 91 | + |
| 92 | +## Drawbacks |
| 93 | + |
| 94 | +The main drawback to this approach is code complexity. There are a lot of `dyn |
| 95 | +Trait` or `impl Trait` in these APIs and it might get a bit difficult to follow |
| 96 | +without external 1000ft views of the entire system. However, this is meant to be |
| 97 | +a flexible low-level API so this might be easier to gloss over once a more higher |
| 98 | +level solution built on top of it is made. |
| 99 | + |
| 100 | +## Rationale and alternatives |
| 101 | + |
| 102 | +Bevy absolutely needs some form of a first-party animation system, no modern game |
| 103 | +engine can be called production ready without one. Having this exist solely as a |
| 104 | +third-party ecosystem crate is definitely unacceptable as it would promote a |
| 105 | +facturing of the ecosystem with multiple incompatible baseline animation system |
| 106 | +implementations. |
| 107 | + |
| 108 | +The design chosen here was explicitly to allow for maximum flexibility for both |
| 109 | +engine and game developers alike. The main alternative is to completely remove |
| 110 | +`Sample<T>`, the animation graph, and it's nodes, and let developers directly |
| 111 | +hook up animation curves from clips to entities. However, this lacks flexibility |
| 112 | +and does not allow for users of the API to inject their own alterations to the |
| 113 | +animation stream. |
| 114 | + |
| 115 | +The main potential issue with the current implementation is the very heavy use of |
| 116 | +`Arc<dyn Sample<T>>` which has CPU cache, lifetime, and performance implications |
| 117 | +on the stored data. Atomic operations disrupt the CPU cache; however, they're |
| 118 | +only used when cloning or dropping an `Arc`. The structure of the animation |
| 119 | +graphs are, for the most part, static. Likewise, the trait object use is likely |
| 120 | +unavoidable so long as we rely on traits as a point of abstraction within the |
| 121 | +graph. An alternative mgiht be to we want to transfer ownership of the curve, and |
| 122 | +just make multiple copies of a potentially large and immutable animation data |
| 123 | +buffer, but that comes with a signfigant memory and CPU cache performance cost. |
| 124 | + |
| 125 | +## Prior art |
| 126 | + |
| 127 | +This proposal is largely inspired by Unity's [Playable][playable] API, which has |
| 128 | +a similar goal of building composable time-sequenced graphs for animation, audio, |
| 129 | +and game logic. Several other game engines have very similar APIs and features: |
| 130 | + |
| 131 | + - Unreal has [AnimGraph][animgraph] for creating dynamic animations in |
| 132 | + Blueprints. |
| 133 | + - Godot has [animation trees][animation-trees] for creating dynamic animations in |
| 134 | + Blueprints. |
| 135 | + |
| 136 | +The proposed API here doesn't purport or aim to directly replicate the features |
| 137 | +seen in these other engines, but provide the absolute bare minimum API so that |
| 138 | +engine developers or game developers can build them if they need to. |
| 139 | + |
| 140 | +Currently nothing like this exists in the entire Rust ecosystem. |
| 141 | + |
| 142 | +Note that while precedent set by other engines is some motivation, it does not on its own motivate an RFC. |
| 143 | + |
| 144 | +[playable]: https://docs.unity3d.com/Manual/Playables.html |
| 145 | +[animgraph]: https://docs.unrealengine.com/4.27/en-US/AnimatingObjects/SkeletalMeshAnimation/AnimBlueprints/AnimGraph/ |
| 146 | +[animation-trees]: https://docs.godotengine.org/en/stable/tutorials/animation/animation_tree.html |
| 147 | + |
| 148 | +## Unresolved questions |
| 149 | + |
| 150 | +TODO: Complete |
| 151 | + |
| 152 | +## Future possibilities |
| 153 | + |
| 154 | +TODO: Complete |
0 commit comments