//! The animation graph, which allows animations to be blended together.12use core::{3fmt::Write,4iter,5ops::{Index, IndexMut, Range},6};7use std::io;89use bevy_asset::{10io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext,11};12use bevy_derive::{Deref, DerefMut};13use bevy_ecs::{14component::Component,15event::EventReader,16reflect::ReflectComponent,17resource::Resource,18system::{Res, ResMut},19};20use bevy_platform::collections::HashMap;21use bevy_reflect::{prelude::ReflectDefault, Reflect};22use derive_more::derive::From;23use petgraph::{24graph::{DiGraph, NodeIndex},25Direction,26};27use ron::de::SpannedError;28use serde::{Deserialize, Serialize};29use smallvec::SmallVec;30use thiserror::Error;31use tracing::warn;3233use crate::{AnimationClip, AnimationTargetId};3435/// A graph structure that describes how animation clips are to be blended36/// together.37///38/// Applications frequently want to be able to play multiple animations at once39/// and to fine-tune the influence that animations have on a skinned mesh. Bevy40/// uses an *animation graph* to store this information. Animation graphs are a41/// directed acyclic graph (DAG) that describes how animations are to be42/// weighted and combined together. Every frame, Bevy evaluates the graph from43/// the root and blends the animations together in a bottom-up fashion to44/// produce the final pose.45///46/// There are three types of nodes: *blend nodes*, *add nodes*, and *clip47/// nodes*, all of which can have an associated weight. Blend nodes and add48/// nodes have no associated animation clip and combine the animations of their49/// children according to those children's weights. Clip nodes specify an50/// animation clip to play. When a graph is created, it starts with only a51/// single blend node, the root node.52///53/// For example, consider the following graph:54///55/// ```text56/// ┌────────────┐57/// │ │58/// │ Idle ├─────────────────────┐59/// │ │ │60/// └────────────┘ │61/// │62/// ┌────────────┐ │ ┌────────────┐63/// │ │ │ │ │64/// │ Run ├──┐ ├──┤ Root │65/// │ │ │ ┌────────────┐ │ │ │66/// └────────────┘ │ │ Blend │ │ └────────────┘67/// ├──┤ ├──┘68/// ┌────────────┐ │ │ 0.5 │69/// │ │ │ └────────────┘70/// │ Walk ├──┘71/// │ │72/// └────────────┘73/// ```74///75/// In this case, assuming that Idle, Run, and Walk are all playing with weight76/// 1.0, the Run and Walk animations will be equally blended together, then77/// their weights will be halved and finally blended with the Idle animation.78/// Thus the weight of Run and Walk are effectively half of the weight of Idle.79///80/// Nodes can optionally have a *mask*, a bitfield that restricts the set of81/// animation targets that the node and its descendants affect. Each bit in the82/// mask corresponds to a *mask group*, which is a set of animation targets83/// (bones). An animation target can belong to any number of mask groups within84/// the context of an animation graph.85///86/// When the appropriate bit is set in a node's mask, neither the node nor its87/// descendants will animate any animation targets belonging to that mask group.88/// That is, setting a mask bit to 1 *disables* the animation targets in that89/// group. If an animation target belongs to multiple mask groups, masking any90/// one of the mask groups that it belongs to will mask that animation target.91/// (Thus an animation target will only be animated if *all* of its mask groups92/// are unmasked.)93///94/// A common use of masks is to allow characters to hold objects. For this, the95/// typical workflow is to assign each character's hand to a mask group. Then,96/// when the character picks up an object, the application masks out the hand97/// that the object is held in for the character's animation set, then positions98/// the hand's digits as necessary to grasp the object. The character's99/// animations will continue to play but will not affect the hand, which will100/// continue to be depicted as holding the object.101///102/// Animation graphs are assets and can be serialized to and loaded from [RON]103/// files. Canonically, such files have an `.animgraph.ron` extension.104///105/// The animation graph implements [RFC 51]. See that document for more106/// information.107///108/// [RON]: https://github.com/ron-rs/ron109///110/// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md111#[derive(Asset, Reflect, Clone, Debug)]112#[reflect(Debug, Clone)]113pub struct AnimationGraph {114/// The `petgraph` data structure that defines the animation graph.115pub graph: AnimationDiGraph,116117/// The index of the root node in the animation graph.118pub root: NodeIndex,119120/// The mask groups that each animation target (bone) belongs to.121///122/// Each value in this map is a bitfield, in which 0 in bit position N123/// indicates that the animation target doesn't belong to mask group N, and124/// a 1 in position N indicates that the animation target does belong to125/// mask group N.126///127/// Animation targets not in this collection are treated as though they128/// don't belong to any mask groups.129pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,130}131132/// A [`Handle`] to the [`AnimationGraph`] to be used by the [`AnimationPlayer`](crate::AnimationPlayer) on the same entity.133#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]134#[reflect(Component, Default, Clone)]135pub struct AnimationGraphHandle(pub Handle<AnimationGraph>);136137impl From<AnimationGraphHandle> for AssetId<AnimationGraph> {138fn from(handle: AnimationGraphHandle) -> Self {139handle.id()140}141}142143impl From<&AnimationGraphHandle> for AssetId<AnimationGraph> {144fn from(handle: &AnimationGraphHandle) -> Self {145handle.id()146}147}148149/// A type alias for the `petgraph` data structure that defines the animation150/// graph.151pub type AnimationDiGraph = DiGraph<AnimationGraphNode, (), u32>;152153/// The index of either an animation or blend node in the animation graph.154///155/// These indices are the way that [animation players] identify each animation.156///157/// [animation players]: crate::AnimationPlayer158pub type AnimationNodeIndex = NodeIndex<u32>;159160/// An individual node within an animation graph.161///162/// The [`AnimationGraphNode::node_type`] field specifies the type of node: one163/// of a *clip node*, a *blend node*, or an *add node*. Clip nodes, the leaves164/// of the graph, contain animation clips to play. Blend and add nodes describe165/// how to combine their children to produce a final animation.166#[derive(Clone, Reflect, Debug)]167#[reflect(Clone)]168pub struct AnimationGraphNode {169/// Animation node data specific to the type of node (clip, blend, or add).170///171/// In the case of clip nodes, this contains the actual animation clip172/// associated with the node.173pub node_type: AnimationNodeType,174175/// A bitfield specifying the mask groups that this node and its descendants176/// will not affect.177///178/// A 0 in bit N indicates that this node and its descendants *can* animate179/// animation targets in mask group N, while a 1 in bit N indicates that180/// this node and its descendants *cannot* animate mask group N.181pub mask: AnimationMask,182183/// The weight of this node, which signifies its contribution in blending.184///185/// Note that this does not propagate down the graph hierarchy; rather,186/// each [Blend] and [Add] node uses the weights of its children to determine187/// the total animation that is accumulated at that node. The parent node's188/// weight is used only to determine the contribution of that total animation189/// in *further* blending.190///191/// In other words, it is as if the blend node is replaced by a single clip192/// node consisting of the blended animation with the weight specified at the193/// blend node.194///195/// For animation clips, this weight is also multiplied by the [active animation weight]196/// before being applied.197///198/// [Blend]: AnimationNodeType::Blend199/// [Add]: AnimationNodeType::Add200/// [active animation weight]: crate::ActiveAnimation::weight201pub weight: f32,202}203204/// Animation node data specific to the type of node (clip, blend, or add).205///206/// In the case of clip nodes, this contains the actual animation clip207/// associated with the node.208#[derive(Clone, Default, Reflect, Debug)]209#[reflect(Clone)]210pub enum AnimationNodeType {211/// A *clip node*, which plays an animation clip.212///213/// These are always the leaves of the graph.214Clip(Handle<AnimationClip>),215216/// A *blend node*, which blends its children according to their weights.217///218/// The weights of all the children of this node are normalized to 1.0.219#[default]220Blend,221222/// An *additive blend node*, which combines the animations of its children223/// additively.224///225/// The weights of all the children of this node are *not* normalized to226/// 1.0. Rather, each child is multiplied by its respective weight and227/// added in sequence.228///229/// Add nodes are primarily useful for superimposing an animation for a230/// portion of a rig on top of the main animation. For example, an add node231/// could superimpose a weapon attack animation for a character's limb on232/// top of a running animation to produce an animation of a character233/// attacking while running.234Add,235}236237/// An [`AssetLoader`] that can load [`AnimationGraph`]s as assets.238///239/// The canonical extension for [`AnimationGraph`]s is `.animgraph.ron`. Plain240/// `.animgraph` is supported as well.241#[derive(Default)]242pub struct AnimationGraphAssetLoader;243244/// Errors that can occur when serializing animation graphs to RON.245#[derive(Error, Debug)]246pub enum AnimationGraphSaveError {247/// An I/O error occurred.248#[error(transparent)]249Io(#[from] io::Error),250/// An error occurred in RON serialization.251#[error(transparent)]252Ron(#[from] ron::Error),253/// An error occurred converting the graph to its serialization form.254#[error(transparent)]255ConvertToSerialized(#[from] NonPathHandleError),256}257258/// Errors that can occur when deserializing animation graphs from RON.259#[derive(Error, Debug)]260pub enum AnimationGraphLoadError {261/// An I/O error occurred.262#[error(transparent)]263Io(#[from] io::Error),264/// An error occurred in RON deserialization.265#[error(transparent)]266Ron(#[from] ron::Error),267/// An error occurred in RON deserialization, and the location of the error268/// is supplied.269#[error(transparent)]270SpannedRon(#[from] SpannedError),271/// The deserialized graph contained legacy data that we no longer support.272#[error(273"The deserialized AnimationGraph contained an AnimationClip referenced by an AssetId, \274which is no longer supported. Consider manually deserializing the SerializedAnimationGraph \275type and determine how to migrate any SerializedAnimationClip::AssetId animation clips"276)]277GraphContainsLegacyAssetId,278}279280/// Acceleration structures for animation graphs that allows Bevy to evaluate281/// them quickly.282///283/// These are kept up to date as [`AnimationGraph`] instances are added,284/// modified, and removed.285#[derive(Default, Reflect, Resource)]286pub struct ThreadedAnimationGraphs(287pub(crate) HashMap<AssetId<AnimationGraph>, ThreadedAnimationGraph>,288);289290/// An acceleration structure for an animation graph that allows Bevy to291/// evaluate it quickly.292///293/// This is kept up to date as the associated [`AnimationGraph`] instance is294/// added, modified, or removed.295#[derive(Default, Reflect)]296pub struct ThreadedAnimationGraph {297/// A cached postorder traversal of the graph.298///299/// The node indices here are stored in postorder. Siblings are stored in300/// descending order. This is because the301/// [`AnimationCurveEvaluator`](`crate::animation_curves::AnimationCurveEvaluator`) uses a stack for302/// evaluation. Consider this graph:303///304/// ```text305/// ┌─────┐306/// │ │307/// │ 1 │308/// │ │309/// └──┬──┘310/// │311/// ┌───────┼───────┐312/// │ │ │313/// ▼ ▼ ▼314/// ┌─────┐ ┌─────┐ ┌─────┐315/// │ │ │ │ │ │316/// │ 2 │ │ 3 │ │ 4 │317/// │ │ │ │ │ │318/// └──┬──┘ └─────┘ └─────┘319/// │320/// ┌───┴───┐321/// │ │322/// ▼ ▼323/// ┌─────┐ ┌─────┐324/// │ │ │ │325/// │ 5 │ │ 6 │326/// │ │ │ │327/// └─────┘ └─────┘328/// ```329///330/// The postorder traversal in this case will be (4, 3, 6, 5, 2, 1).331///332/// The fact that the children of each node are sorted in reverse ensures333/// that, at each level, the order of blending proceeds in ascending order334/// by node index, as we guarantee. To illustrate this, consider the way335/// the graph above is evaluated. (Interpolation is represented with the ⊕336/// symbol.)337///338/// | Step | Node | Operation | Stack (after operation) | Blend Register |339/// | ---- | ---- | ---------- | ----------------------- | -------------- |340/// | 1 | 4 | Push | 4 | |341/// | 2 | 3 | Push | 4 3 | |342/// | 3 | 6 | Push | 4 3 6 | |343/// | 4 | 5 | Push | 4 3 6 5 | |344/// | 5 | 2 | Blend 5 | 4 3 6 | 5 |345/// | 6 | 2 | Blend 6 | 4 3 | 5 ⊕ 6 |346/// | 7 | 2 | Push Blend | 4 3 2 | |347/// | 8 | 1 | Blend 2 | 4 3 | 2 |348/// | 9 | 1 | Blend 3 | 4 | 2 ⊕ 3 |349/// | 10 | 1 | Blend 4 | | 2 ⊕ 3 ⊕ 4 |350/// | 11 | 1 | Push Blend | 1 | |351/// | 12 | | Commit | | |352pub threaded_graph: Vec<AnimationNodeIndex>,353354/// A mapping from each parent node index to the range within355/// [`Self::sorted_edges`].356///357/// This allows for quick lookup of the children of each node, sorted in358/// ascending order of node index, without having to sort the result of the359/// `petgraph` traversal functions every frame.360pub sorted_edge_ranges: Vec<Range<u32>>,361362/// A list of the children of each node, sorted in ascending order.363pub sorted_edges: Vec<AnimationNodeIndex>,364365/// A mapping from node index to a bitfield specifying the mask groups that366/// this node masks *out* (i.e. doesn't animate).367///368/// A 1 in bit position N indicates that this node doesn't animate any369/// targets of mask group N.370pub computed_masks: Vec<u64>,371}372373/// A version of [`AnimationGraph`] suitable for serializing as an asset.374///375/// Animation nodes can refer to external animation clips, and the [`AssetId`]376/// is typically not sufficient to identify the clips, since the377/// [`bevy_asset::AssetServer`] assigns IDs in unpredictable ways. That fact378/// motivates this type, which replaces the `Handle<AnimationClip>` with an379/// asset path. Loading an animation graph via the [`bevy_asset::AssetServer`]380/// actually loads a serialized instance of this type, as does serializing an381/// [`AnimationGraph`] through `serde`.382#[derive(Serialize, Deserialize)]383pub struct SerializedAnimationGraph {384/// Corresponds to the `graph` field on [`AnimationGraph`].385pub graph: DiGraph<SerializedAnimationGraphNode, (), u32>,386/// Corresponds to the `root` field on [`AnimationGraph`].387pub root: NodeIndex,388/// Corresponds to the `mask_groups` field on [`AnimationGraph`].389pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,390}391392/// A version of [`AnimationGraphNode`] suitable for serializing as an asset.393///394/// See the comments in [`SerializedAnimationGraph`] for more information.395#[derive(Serialize, Deserialize)]396pub struct SerializedAnimationGraphNode {397/// Corresponds to the `node_type` field on [`AnimationGraphNode`].398pub node_type: SerializedAnimationNodeType,399/// Corresponds to the `mask` field on [`AnimationGraphNode`].400pub mask: AnimationMask,401/// Corresponds to the `weight` field on [`AnimationGraphNode`].402pub weight: f32,403}404405/// A version of [`AnimationNodeType`] suitable for serializing as part of a406/// [`SerializedAnimationGraphNode`] asset.407#[derive(Serialize, Deserialize)]408pub enum SerializedAnimationNodeType {409/// Corresponds to [`AnimationNodeType::Clip`].410Clip(MigrationSerializedAnimationClip),411/// Corresponds to [`AnimationNodeType::Blend`].412Blend,413/// Corresponds to [`AnimationNodeType::Add`].414Add,415}416417/// A type to facilitate migration from the legacy format of [`SerializedAnimationGraph`] to the418/// new format.419///420/// By using untagged serde deserialization, we can try to deserialize the modern form, then421/// fallback to the legacy form. Users must migrate to the modern form by Bevy 0.18.422// TODO: Delete this after Bevy 0.17.423#[derive(Serialize, Deserialize)]424#[serde(untagged)]425pub enum MigrationSerializedAnimationClip {426/// This is the new type of this field.427Modern(AssetPath<'static>),428/// This is the legacy type of this field. Users must migrate away from this.429#[serde(skip_serializing)]430Legacy(SerializedAnimationClip),431}432433/// The legacy form of serialized animation clips. This allows raw asset IDs to be deserialized.434// TODO: Delete this after Bevy 0.17.435#[derive(Deserialize)]436pub enum SerializedAnimationClip {437/// Records an asset path.438AssetPath(AssetPath<'static>),439/// The fallback that records an asset ID.440///441/// Because asset IDs can change, this should not be relied upon. Prefer to442/// use asset paths where possible.443AssetId(AssetId<AnimationClip>),444}445446/// The type of an animation mask bitfield.447///448/// Bit N corresponds to mask group N.449///450/// Because this is a 64-bit value, there is currently a limitation of 64 mask451/// groups per animation graph.452pub type AnimationMask = u64;453454impl AnimationGraph {455/// Creates a new animation graph with a root node and no other nodes.456pub fn new() -> Self {457let mut graph = DiGraph::default();458let root = graph.add_node(AnimationGraphNode::default());459Self {460graph,461root,462mask_groups: HashMap::default(),463}464}465466/// A convenience function for creating an [`AnimationGraph`] from a single467/// [`AnimationClip`].468///469/// The clip will be a direct child of the root with weight 1.0. Both the470/// graph and the index of the added node are returned as a tuple.471pub fn from_clip(clip: Handle<AnimationClip>) -> (Self, AnimationNodeIndex) {472let mut graph = Self::new();473let node_index = graph.add_clip(clip, 1.0, graph.root);474(graph, node_index)475}476477/// A convenience method to create an [`AnimationGraph`]s with an iterator478/// of clips.479///480/// All of the animation clips will be direct children of the root with481/// weight 1.0.482///483/// Returns the graph and indices of the new nodes.484pub fn from_clips<'a, I>(clips: I) -> (Self, Vec<AnimationNodeIndex>)485where486I: IntoIterator<Item = Handle<AnimationClip>>,487<I as IntoIterator>::IntoIter: 'a,488{489let mut graph = Self::new();490let indices = graph.add_clips(clips, 1.0, graph.root).collect();491(graph, indices)492}493494/// Adds an [`AnimationClip`] to the animation graph with the given weight495/// and returns its index.496///497/// The animation clip will be the child of the given parent. The resulting498/// node will have no mask.499pub fn add_clip(500&mut self,501clip: Handle<AnimationClip>,502weight: f32,503parent: AnimationNodeIndex,504) -> AnimationNodeIndex {505let node_index = self.graph.add_node(AnimationGraphNode {506node_type: AnimationNodeType::Clip(clip),507mask: 0,508weight,509});510self.graph.add_edge(parent, node_index, ());511node_index512}513514/// Adds an [`AnimationClip`] to the animation graph with the given weight515/// and mask, and returns its index.516///517/// The animation clip will be the child of the given parent.518pub fn add_clip_with_mask(519&mut self,520clip: Handle<AnimationClip>,521mask: AnimationMask,522weight: f32,523parent: AnimationNodeIndex,524) -> AnimationNodeIndex {525let node_index = self.graph.add_node(AnimationGraphNode {526node_type: AnimationNodeType::Clip(clip),527mask,528weight,529});530self.graph.add_edge(parent, node_index, ());531node_index532}533534/// A convenience method to add multiple [`AnimationClip`]s to the animation535/// graph.536///537/// All of the animation clips will have the same weight and will be538/// parented to the same node.539///540/// Returns the indices of the new nodes.541pub fn add_clips<'a, I>(542&'a mut self,543clips: I,544weight: f32,545parent: AnimationNodeIndex,546) -> impl Iterator<Item = AnimationNodeIndex> + 'a547where548I: IntoIterator<Item = Handle<AnimationClip>>,549<I as IntoIterator>::IntoIter: 'a,550{551clips552.into_iter()553.map(move |clip| self.add_clip(clip, weight, parent))554}555556/// Adds a blend node to the animation graph with the given weight and557/// returns its index.558///559/// The blend node will be placed under the supplied `parent` node. During560/// animation evaluation, the descendants of this blend node will have their561/// weights multiplied by the weight of the blend. The blend node will have562/// no mask.563pub fn add_blend(&mut self, weight: f32, parent: AnimationNodeIndex) -> AnimationNodeIndex {564let node_index = self.graph.add_node(AnimationGraphNode {565node_type: AnimationNodeType::Blend,566mask: 0,567weight,568});569self.graph.add_edge(parent, node_index, ());570node_index571}572573/// Adds a blend node to the animation graph with the given weight and574/// returns its index.575///576/// The blend node will be placed under the supplied `parent` node. During577/// animation evaluation, the descendants of this blend node will have their578/// weights multiplied by the weight of the blend. Neither this node nor its579/// descendants will affect animation targets that belong to mask groups not580/// in the given `mask`.581pub fn add_blend_with_mask(582&mut self,583mask: AnimationMask,584weight: f32,585parent: AnimationNodeIndex,586) -> AnimationNodeIndex {587let node_index = self.graph.add_node(AnimationGraphNode {588node_type: AnimationNodeType::Blend,589mask,590weight,591});592self.graph.add_edge(parent, node_index, ());593node_index594}595596/// Adds a blend node to the animation graph with the given weight and597/// returns its index.598///599/// The blend node will be placed under the supplied `parent` node. During600/// animation evaluation, the descendants of this blend node will have their601/// weights multiplied by the weight of the blend. The blend node will have602/// no mask.603pub fn add_additive_blend(604&mut self,605weight: f32,606parent: AnimationNodeIndex,607) -> AnimationNodeIndex {608let node_index = self.graph.add_node(AnimationGraphNode {609node_type: AnimationNodeType::Add,610mask: 0,611weight,612});613self.graph.add_edge(parent, node_index, ());614node_index615}616617/// Adds a blend node to the animation graph with the given weight and618/// returns its index.619///620/// The blend node will be placed under the supplied `parent` node. During621/// animation evaluation, the descendants of this blend node will have their622/// weights multiplied by the weight of the blend. Neither this node nor its623/// descendants will affect animation targets that belong to mask groups not624/// in the given `mask`.625pub fn add_additive_blend_with_mask(626&mut self,627mask: AnimationMask,628weight: f32,629parent: AnimationNodeIndex,630) -> AnimationNodeIndex {631let node_index = self.graph.add_node(AnimationGraphNode {632node_type: AnimationNodeType::Add,633mask,634weight,635});636self.graph.add_edge(parent, node_index, ());637node_index638}639640/// Adds an edge from the edge `from` to `to`, making `to` a child of641/// `from`.642///643/// The behavior is unspecified if adding this produces a cycle in the644/// graph.645pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex) {646self.graph.add_edge(from, to, ());647}648649/// Removes an edge between `from` and `to` if it exists.650///651/// Returns true if the edge was successfully removed or false if no such652/// edge existed.653pub fn remove_edge(&mut self, from: NodeIndex, to: NodeIndex) -> bool {654self.graph655.find_edge(from, to)656.map(|edge| self.graph.remove_edge(edge))657.is_some()658}659660/// Returns the [`AnimationGraphNode`] associated with the given index.661///662/// If no node with the given index exists, returns `None`.663pub fn get(&self, animation: AnimationNodeIndex) -> Option<&AnimationGraphNode> {664self.graph.node_weight(animation)665}666667/// Returns a mutable reference to the [`AnimationGraphNode`] associated668/// with the given index.669///670/// If no node with the given index exists, returns `None`.671pub fn get_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut AnimationGraphNode> {672self.graph.node_weight_mut(animation)673}674675/// Returns an iterator over the [`AnimationGraphNode`]s in this graph.676pub fn nodes(&self) -> impl Iterator<Item = AnimationNodeIndex> {677self.graph.node_indices()678}679680/// Serializes the animation graph to the given [`Write`]r in RON format.681///682/// If writing to a file, it can later be loaded with the683/// [`AnimationGraphAssetLoader`] to reconstruct the graph.684pub fn save<W>(&self, writer: &mut W) -> Result<(), AnimationGraphSaveError>685where686W: Write,687{688let mut ron_serializer = ron::ser::Serializer::new(writer, None)?;689let serialized_graph: SerializedAnimationGraph = self.clone().try_into()?;690Ok(serialized_graph.serialize(&mut ron_serializer)?)691}692693/// Adds an animation target (bone) to the mask group with the given ID.694///695/// Calling this method multiple times with the same animation target but696/// different mask groups will result in that target being added to all of697/// the specified groups.698pub fn add_target_to_mask_group(&mut self, target: AnimationTargetId, mask_group: u32) {699*self.mask_groups.entry(target).or_default() |= 1 << mask_group;700}701}702703impl AnimationGraphNode {704/// Masks out the mask groups specified by the given `mask` bitfield.705///706/// A 1 in bit position N causes this function to mask out mask group N, and707/// thus neither this node nor its descendants will animate any animation708/// targets that belong to group N.709pub fn add_mask(&mut self, mask: AnimationMask) -> &mut Self {710self.mask |= mask;711self712}713714/// Unmasks the mask groups specified by the given `mask` bitfield.715///716/// A 1 in bit position N causes this function to unmask mask group N, and717/// thus this node and its descendants will be allowed to animate animation718/// targets that belong to group N, unless another mask masks those targets719/// out.720pub fn remove_mask(&mut self, mask: AnimationMask) -> &mut Self {721self.mask &= !mask;722self723}724725/// Masks out the single mask group specified by `group`.726///727/// After calling this function, neither this node nor its descendants will728/// animate any animation targets that belong to the given `group`.729pub fn add_mask_group(&mut self, group: u32) -> &mut Self {730self.add_mask(1 << group)731}732733/// Unmasks the single mask group specified by `group`.734///735/// After calling this function, this node and its descendants will be736/// allowed to animate animation targets that belong to the given `group`,737/// unless another mask masks those targets out.738pub fn remove_mask_group(&mut self, group: u32) -> &mut Self {739self.remove_mask(1 << group)740}741}742743impl Index<AnimationNodeIndex> for AnimationGraph {744type Output = AnimationGraphNode;745746fn index(&self, index: AnimationNodeIndex) -> &Self::Output {747&self.graph[index]748}749}750751impl IndexMut<AnimationNodeIndex> for AnimationGraph {752fn index_mut(&mut self, index: AnimationNodeIndex) -> &mut Self::Output {753&mut self.graph[index]754}755}756757impl Default for AnimationGraphNode {758fn default() -> Self {759Self {760node_type: Default::default(),761mask: 0,762weight: 1.0,763}764}765}766767impl Default for AnimationGraph {768fn default() -> Self {769Self::new()770}771}772773impl AssetLoader for AnimationGraphAssetLoader {774type Asset = AnimationGraph;775776type Settings = ();777778type Error = AnimationGraphLoadError;779780async fn load(781&self,782reader: &mut dyn Reader,783_: &Self::Settings,784load_context: &mut LoadContext<'_>,785) -> Result<Self::Asset, Self::Error> {786let mut bytes = Vec::new();787reader.read_to_end(&mut bytes).await?;788789// Deserialize a `SerializedAnimationGraph` directly, so that we can790// get the list of the animation clips it refers to and load them.791let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;792let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)793.map_err(|err| deserializer.span_error(err))?;794795// Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real796// `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return797// errors.798let mut animation_graph = DiGraph::with_capacity(799serialized_animation_graph.graph.node_count(),800serialized_animation_graph.graph.edge_count(),801);802803let mut already_warned = false;804for serialized_node in serialized_animation_graph.graph.node_weights() {805animation_graph.add_node(AnimationGraphNode {806node_type: match serialized_node.node_type {807SerializedAnimationNodeType::Clip(ref clip) => match clip {808MigrationSerializedAnimationClip::Modern(path) => {809AnimationNodeType::Clip(load_context.load(path.clone()))810}811MigrationSerializedAnimationClip::Legacy(812SerializedAnimationClip::AssetPath(path),813) => {814if !already_warned {815let path = load_context.asset_path();816warn!(817"Loaded an AnimationGraph asset at \"{path}\" which contains a \818legacy-style SerializedAnimationClip. Please re-save the asset \819using AnimationGraph::save to automatically migrate to the new \820format"821);822already_warned = true;823}824AnimationNodeType::Clip(load_context.load(path.clone()))825}826MigrationSerializedAnimationClip::Legacy(827SerializedAnimationClip::AssetId(_),828) => {829return Err(AnimationGraphLoadError::GraphContainsLegacyAssetId);830}831},832SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,833SerializedAnimationNodeType::Add => AnimationNodeType::Add,834},835mask: serialized_node.mask,836weight: serialized_node.weight,837});838}839for edge in serialized_animation_graph.graph.raw_edges() {840animation_graph.add_edge(edge.source(), edge.target(), ());841}842Ok(AnimationGraph {843graph: animation_graph,844root: serialized_animation_graph.root,845mask_groups: serialized_animation_graph.mask_groups,846})847}848849fn extensions(&self) -> &[&str] {850&["animgraph", "animgraph.ron"]851}852}853854impl TryFrom<AnimationGraph> for SerializedAnimationGraph {855type Error = NonPathHandleError;856857fn try_from(animation_graph: AnimationGraph) -> Result<Self, NonPathHandleError> {858// Convert all the `Handle<AnimationClip>` to AssetPath, so that859// `AnimationGraphAssetLoader` can load them. This is effectively just doing a860// `DiGraph::map`, except we need to return an error if any handles aren't associated to a861// path.862let mut serialized_graph = DiGraph::with_capacity(863animation_graph.graph.node_count(),864animation_graph.graph.edge_count(),865);866for node in animation_graph.graph.node_weights() {867serialized_graph.add_node(SerializedAnimationGraphNode {868weight: node.weight,869mask: node.mask,870node_type: match node.node_type {871AnimationNodeType::Clip(ref clip) => match clip.path() {872Some(path) => SerializedAnimationNodeType::Clip(873MigrationSerializedAnimationClip::Modern(path.clone()),874),875None => return Err(NonPathHandleError),876},877AnimationNodeType::Blend => SerializedAnimationNodeType::Blend,878AnimationNodeType::Add => SerializedAnimationNodeType::Add,879},880});881}882for edge in animation_graph.graph.raw_edges() {883serialized_graph.add_edge(edge.source(), edge.target(), ());884}885Ok(Self {886graph: serialized_graph,887root: animation_graph.root,888mask_groups: animation_graph.mask_groups,889})890}891}892893/// Error for when only path [`Handle`]s are supported.894#[derive(Error, Debug)]895#[error("AnimationGraph contains a handle to an AnimationClip that does not correspond to an asset path")]896pub struct NonPathHandleError;897898/// A system that creates, updates, and removes [`ThreadedAnimationGraph`]899/// structures for every changed [`AnimationGraph`].900///901/// The [`ThreadedAnimationGraph`] contains acceleration structures that allow902/// for quick evaluation of that graph's animations.903pub(crate) fn thread_animation_graphs(904mut threaded_animation_graphs: ResMut<ThreadedAnimationGraphs>,905animation_graphs: Res<Assets<AnimationGraph>>,906mut animation_graph_asset_events: EventReader<AssetEvent<AnimationGraph>>,907) {908for animation_graph_asset_event in animation_graph_asset_events.read() {909match *animation_graph_asset_event {910AssetEvent::Added { id }911| AssetEvent::Modified { id }912| AssetEvent::LoadedWithDependencies { id } => {913// Fetch the animation graph.914let Some(animation_graph) = animation_graphs.get(id) else {915continue;916};917918// Reuse the allocation if possible.919let mut threaded_animation_graph =920threaded_animation_graphs.0.remove(&id).unwrap_or_default();921threaded_animation_graph.clear();922923// Recursively thread the graph in postorder.924threaded_animation_graph.init(animation_graph);925threaded_animation_graph.build_from(926&animation_graph.graph,927animation_graph.root,9280,929);930931// Write in the threaded graph.932threaded_animation_graphs933.0934.insert(id, threaded_animation_graph);935}936937AssetEvent::Removed { id } => {938threaded_animation_graphs.0.remove(&id);939}940AssetEvent::Unused { .. } => {}941}942}943}944945impl ThreadedAnimationGraph {946/// Removes all the data in this [`ThreadedAnimationGraph`], keeping the947/// memory around for later reuse.948fn clear(&mut self) {949self.threaded_graph.clear();950self.sorted_edge_ranges.clear();951self.sorted_edges.clear();952}953954/// Prepares the [`ThreadedAnimationGraph`] for recursion.955fn init(&mut self, animation_graph: &AnimationGraph) {956let node_count = animation_graph.graph.node_count();957let edge_count = animation_graph.graph.edge_count();958959self.threaded_graph.reserve(node_count);960self.sorted_edges.reserve(edge_count);961962self.sorted_edge_ranges.clear();963self.sorted_edge_ranges964.extend(iter::repeat_n(0..0, node_count));965966self.computed_masks.clear();967self.computed_masks.extend(iter::repeat_n(0, node_count));968}969970/// Recursively constructs the [`ThreadedAnimationGraph`] for the subtree971/// rooted at the given node.972///973/// `mask` specifies the computed mask of the parent node. (It could be974/// fetched from the [`Self::computed_masks`] field, but we pass it975/// explicitly as a micro-optimization.)976fn build_from(977&mut self,978graph: &AnimationDiGraph,979node_index: AnimationNodeIndex,980mut mask: u64,981) {982// Accumulate the mask.983mask |= graph.node_weight(node_index).unwrap().mask;984self.computed_masks[node_index.index()] = mask;985986// Gather up the indices of our children, and sort them.987let mut kids: SmallVec<[AnimationNodeIndex; 8]> = graph988.neighbors_directed(node_index, Direction::Outgoing)989.collect();990kids.sort_unstable();991992// Write in the list of kids.993self.sorted_edge_ranges[node_index.index()] =994(self.sorted_edges.len() as u32)..((self.sorted_edges.len() + kids.len()) as u32);995self.sorted_edges.extend_from_slice(&kids);996997// Recurse. (This is a postorder traversal.)998for kid in kids.into_iter().rev() {999self.build_from(graph, kid, mask);1000}10011002// Finally, push our index.1003self.threaded_graph.push(node_index);1004}1005}100610071008