//! The animation graph, which allows animations to be blended together. use core::iter; use core::ops::{Index, IndexMut, Range}; use std::io::{self, Write}; use bevy_asset::{ io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext, }; use bevy_derive::{Deref, DerefMut}; use bevy_ecs::{ component::Component, event::EventReader, reflect::ReflectComponent, system::{Res, ResMut, Resource}, }; use bevy_reflect::{prelude::ReflectDefault, Reflect, ReflectSerialize}; use bevy_utils::HashMap; use derive_more::derive::{Display, Error, From}; use petgraph::{ graph::{DiGraph, NodeIndex}, Direction, }; use ron::de::SpannedError; use serde::{Deserialize, Serialize}; use smallvec::SmallVec; use crate::{AnimationClip, AnimationTargetId}; /// A graph structure that describes how animation clips are to be blended /// together. /// /// Applications frequently want to be able to play multiple animations at once /// and to fine-tune the influence that animations have on a skinned mesh. Bevy /// uses an *animation graph* to store this information. Animation graphs are a /// directed acyclic graph (DAG) that describes how animations are to be /// weighted and combined together. Every frame, Bevy evaluates the graph from /// the root and blends the animations together in a bottom-up fashion to /// produce the final pose. /// /// There are three types of nodes: *blend nodes*, *add nodes*, and *clip /// nodes*, all of which can have an associated weight. Blend nodes and add /// nodes have no associated animation clip and combine the animations of their /// children according to those children's weights. Clip nodes specify an /// animation clip to play. When a graph is created, it starts with only a /// single blend node, the root node. /// /// For example, consider the following graph: /// /// ```text /// ┌────────────┐ /// │ │ /// │ Idle ├─────────────────────┐ /// │ │ │ /// └────────────┘ │ /// │ /// ┌────────────┐ │ ┌────────────┐ /// │ │ │ │ │ /// │ Run ├──┐ ├──┤ Root │ /// │ │ │ ┌────────────┐ │ │ │ /// └────────────┘ │ │ Blend │ │ └────────────┘ /// ├──┤ ├──┘ /// ┌────────────┐ │ │ 0.5 │ /// │ │ │ └────────────┘ /// │ Walk ├──┘ /// │ │ /// └────────────┘ /// ``` /// /// In this case, assuming that Idle, Run, and Walk are all playing with weight /// 1.0, the Run and Walk animations will be equally blended together, then /// their weights will be halved and finally blended with the Idle animation. /// Thus the weight of Run and Walk are effectively half of the weight of Idle. /// /// Nodes can optionally have a *mask*, a bitfield that restricts the set of /// animation targets that the node and its descendants affect. Each bit in the /// mask corresponds to a *mask group*, which is a set of animation targets /// (bones). An animation target can belong to any number of mask groups within /// the context of an animation graph. /// /// When the appropriate bit is set in a node's mask, neither the node nor its /// descendants will animate any animation targets belonging to that mask group. /// That is, setting a mask bit to 1 *disables* the animation targets in that /// group. If an animation target belongs to multiple mask groups, masking any /// one of the mask groups that it belongs to will mask that animation target. /// (Thus an animation target will only be animated if *all* of its mask groups /// are unmasked.) /// /// A common use of masks is to allow characters to hold objects. For this, the /// typical workflow is to assign each character's hand to a mask group. Then, /// when the character picks up an object, the application masks out the hand /// that the object is held in for the character's animation set, then positions /// the hand's digits as necessary to grasp the object. The character's /// animations will continue to play but will not affect the hand, which will /// continue to be depicted as holding the object. /// /// Animation graphs are assets and can be serialized to and loaded from [RON] /// files. Canonically, such files have an `.animgraph.ron` extension. /// /// The animation graph implements [RFC 51]. See that document for more /// information. /// /// [RON]: https://github.com/ron-rs/ron /// /// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md #[derive(Asset, Reflect, Clone, Debug, Serialize)] #[reflect(Serialize, Debug)] #[serde(into = "SerializedAnimationGraph")] pub struct AnimationGraph { /// The `petgraph` data structure that defines the animation graph. pub graph: AnimationDiGraph, /// The index of the root node in the animation graph. pub root: NodeIndex, /// The mask groups that each animation target (bone) belongs to. /// /// Each value in this map is a bitfield, in which 0 in bit position N /// indicates that the animation target doesn't belong to mask group N, and /// a 1 in position N indicates that the animation target does belong to /// mask group N. /// /// Animation targets not in this collection are treated as though they /// don't belong to any mask groups. pub mask_groups: HashMap, } /// A [`Handle`] to the [`AnimationGraph`] to be used by the [`AnimationPlayer`](crate::AnimationPlayer) on the same entity. #[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)] #[reflect(Component, Default)] pub struct AnimationGraphHandle(pub Handle); impl From for AssetId { fn from(handle: AnimationGraphHandle) -> Self { handle.id() } } impl From<&AnimationGraphHandle> for AssetId { fn from(handle: &AnimationGraphHandle) -> Self { handle.id() } } /// A type alias for the `petgraph` data structure that defines the animation /// graph. pub type AnimationDiGraph = DiGraph; /// The index of either an animation or blend node in the animation graph. /// /// These indices are the way that [animation players] identify each animation. /// /// [animation players]: crate::AnimationPlayer pub type AnimationNodeIndex = NodeIndex; /// An individual node within an animation graph. /// /// The [`AnimationGraphNode::node_type`] field specifies the type of node: one /// of a *clip node*, a *blend node*, or an *add node*. Clip nodes, the leaves /// of the graph, contain animation clips to play. Blend and add nodes describe /// how to combine their children to produce a final animation. #[derive(Clone, Reflect, Debug)] pub struct AnimationGraphNode { /// Animation node data specific to the type of node (clip, blend, or add). /// /// In the case of clip nodes, this contains the actual animation clip /// associated with the node. pub node_type: AnimationNodeType, /// A bitfield specifying the mask groups that this node and its descendants /// will not affect. /// /// A 0 in bit N indicates that this node and its descendants *can* animate /// animation targets in mask group N, while a 1 in bit N indicates that /// this node and its descendants *cannot* animate mask group N. pub mask: AnimationMask, /// The weight of this node, which signifies its contribution in blending. /// /// Note that this does not propagate down the graph hierarchy; rather, /// each [Blend] and [Add] node uses the weights of its children to determine /// the total animation that is accumulated at that node. The parent node's /// weight is used only to determine the contribution of that total animation /// in *further* blending. /// /// In other words, it is as if the blend node is replaced by a single clip /// node consisting of the blended animation with the weight specified at the /// blend node. /// /// For animation clips, this weight is also multiplied by the [active animation weight] /// before being applied. /// /// [Blend]: AnimationNodeType::Blend /// [Add]: AnimationNodeType::Add /// [active animation weight]: crate::ActiveAnimation::weight pub weight: f32, } /// Animation node data specific to the type of node (clip, blend, or add). /// /// In the case of clip nodes, this contains the actual animation clip /// associated with the node. #[derive(Clone, Default, Reflect, Debug)] pub enum AnimationNodeType { /// A *clip node*, which plays an animation clip. /// /// These are always the leaves of the graph. Clip(Handle), /// A *blend node*, which blends its children according to their weights. /// /// The weights of all the children of this node are normalized to 1.0. #[default] Blend, /// An *additive blend node*, which combines the animations of its children /// additively. /// /// The weights of all the children of this node are *not* normalized to /// 1.0. Rather, the first child is used as a base, ignoring its weight, /// while the others are multiplied by their respective weights and then /// added in sequence to the base. /// /// Add nodes are primarily useful for superimposing an animation for a /// portion of a rig on top of the main animation. For example, an add node /// could superimpose a weapon attack animation for a character's limb on /// top of a running animation to produce an animation of a character /// attacking while running. Add, } /// An [`AssetLoader`] that can load [`AnimationGraph`]s as assets. /// /// The canonical extension for [`AnimationGraph`]s is `.animgraph.ron`. Plain /// `.animgraph` is supported as well. #[derive(Default)] pub struct AnimationGraphAssetLoader; /// Various errors that can occur when serializing or deserializing animation /// graphs to and from RON, respectively. #[derive(Error, Display, Debug, From)] pub enum AnimationGraphLoadError { /// An I/O error occurred. #[display("I/O")] Io(io::Error), /// An error occurred in RON serialization or deserialization. #[display("RON serialization")] Ron(ron::Error), /// An error occurred in RON deserialization, and the location of the error /// is supplied. #[display("RON serialization")] SpannedRon(SpannedError), } /// Acceleration structures for animation graphs that allows Bevy to evaluate /// them quickly. /// /// These are kept up to date as [`AnimationGraph`] instances are added, /// modified, and removed. #[derive(Default, Reflect, Resource)] pub struct ThreadedAnimationGraphs( pub(crate) HashMap, ThreadedAnimationGraph>, ); /// An acceleration structure for an animation graph that allows Bevy to /// evaluate it quickly. /// /// This is kept up to date as the associated [`AnimationGraph`] instance is /// added, modified, or removed. #[derive(Default, Reflect)] pub struct ThreadedAnimationGraph { /// A cached postorder traversal of the graph. /// /// The node indices here are stored in postorder. Siblings are stored in /// descending order. This is because the /// [`crate::animation_curves::AnimationCurveEvaluator`] uses a stack for /// evaluation. Consider this graph: /// /// ```text /// ┌─────┐ /// │ │ /// │ 1 │ /// │ │ /// └──┬──┘ /// │ /// ┌───────┼───────┐ /// │ │ │ /// ▼ ▼ ▼ /// ┌─────┐ ┌─────┐ ┌─────┐ /// │ │ │ │ │ │ /// │ 2 │ │ 3 │ │ 4 │ /// │ │ │ │ │ │ /// └──┬──┘ └─────┘ └─────┘ /// │ /// ┌───┴───┐ /// │ │ /// ▼ ▼ /// ┌─────┐ ┌─────┐ /// │ │ │ │ /// │ 5 │ │ 6 │ /// │ │ │ │ /// └─────┘ └─────┘ /// ``` /// /// The postorder traversal in this case will be (4, 3, 6, 5, 2, 1). /// /// The fact that the children of each node are sorted in reverse ensures /// that, at each level, the order of blending proceeds in ascending order /// by node index, as we guarantee. To illustrate this, consider the way /// the graph above is evaluated. (Interpolation is represented with the ⊕ /// symbol.) /// /// | Step | Node | Operation | Stack (after operation) | Blend Register | /// | ---- | ---- | ---------- | ----------------------- | -------------- | /// | 1 | 4 | Push | 4 | | /// | 2 | 3 | Push | 4 3 | | /// | 3 | 6 | Push | 4 3 6 | | /// | 4 | 5 | Push | 4 3 6 5 | | /// | 5 | 2 | Blend 5 | 4 3 6 | 5 | /// | 6 | 2 | Blend 6 | 4 3 | 5 ⊕ 6 | /// | 7 | 2 | Push Blend | 4 3 2 | | /// | 8 | 1 | Blend 2 | 4 3 | 2 | /// | 9 | 1 | Blend 3 | 4 | 2 ⊕ 3 | /// | 10 | 1 | Blend 4 | | 2 ⊕ 3 ⊕ 4 | /// | 11 | 1 | Push Blend | 1 | | /// | 12 | | Commit | | | pub threaded_graph: Vec, /// A mapping from each parent node index to the range within /// [`Self::sorted_edges`]. /// /// This allows for quick lookup of the children of each node, sorted in /// ascending order of node index, without having to sort the result of the /// `petgraph` traversal functions every frame. pub sorted_edge_ranges: Vec>, /// A list of the children of each node, sorted in ascending order. pub sorted_edges: Vec, /// A mapping from node index to a bitfield specifying the mask groups that /// this node masks *out* (i.e. doesn't animate). /// /// A 1 in bit position N indicates that this node doesn't animate any /// targets of mask group N. pub computed_masks: Vec, } /// A version of [`AnimationGraph`] suitable for serializing as an asset. /// /// Animation nodes can refer to external animation clips, and the [`AssetId`] /// is typically not sufficient to identify the clips, since the /// [`bevy_asset::AssetServer`] assigns IDs in unpredictable ways. That fact /// motivates this type, which replaces the `Handle` with an /// asset path. Loading an animation graph via the [`bevy_asset::AssetServer`] /// actually loads a serialized instance of this type, as does serializing an /// [`AnimationGraph`] through `serde`. #[derive(Serialize, Deserialize)] pub struct SerializedAnimationGraph { /// Corresponds to the `graph` field on [`AnimationGraph`]. pub graph: DiGraph, /// Corresponds to the `root` field on [`AnimationGraph`]. pub root: NodeIndex, /// Corresponds to the `mask_groups` field on [`AnimationGraph`]. pub mask_groups: HashMap, } /// A version of [`AnimationGraphNode`] suitable for serializing as an asset. /// /// See the comments in [`SerializedAnimationGraph`] for more information. #[derive(Serialize, Deserialize)] pub struct SerializedAnimationGraphNode { /// Corresponds to the `node_type` field on [`AnimationGraphNode`]. pub node_type: SerializedAnimationNodeType, /// Corresponds to the `mask` field on [`AnimationGraphNode`]. pub mask: AnimationMask, /// Corresponds to the `weight` field on [`AnimationGraphNode`]. pub weight: f32, } /// A version of [`AnimationNodeType`] suitable for serializing as part of a /// [`SerializedAnimationGraphNode`] asset. #[derive(Serialize, Deserialize)] pub enum SerializedAnimationNodeType { /// Corresponds to [`AnimationNodeType::Clip`]. Clip(SerializedAnimationClip), /// Corresponds to [`AnimationNodeType::Blend`]. Blend, /// Corresponds to [`AnimationNodeType::Add`]. Add, } /// A version of `Handle` suitable for serializing as an asset. /// /// This replaces any handle that has a path with an [`AssetPath`]. Failing /// that, the asset ID is serialized directly. #[derive(Serialize, Deserialize)] pub enum SerializedAnimationClip { /// Records an asset path. AssetPath(AssetPath<'static>), /// The fallback that records an asset ID. /// /// Because asset IDs can change, this should not be relied upon. Prefer to /// use asset paths where possible. AssetId(AssetId), } /// The type of an animation mask bitfield. /// /// Bit N corresponds to mask group N. /// /// Because this is a 64-bit value, there is currently a limitation of 64 mask /// groups per animation graph. pub type AnimationMask = u64; impl AnimationGraph { /// Creates a new animation graph with a root node and no other nodes. pub fn new() -> Self { let mut graph = DiGraph::default(); let root = graph.add_node(AnimationGraphNode::default()); Self { graph, root, mask_groups: HashMap::new(), } } /// A convenience function for creating an [`AnimationGraph`] from a single /// [`AnimationClip`]. /// /// The clip will be a direct child of the root with weight 1.0. Both the /// graph and the index of the added node are returned as a tuple. pub fn from_clip(clip: Handle) -> (Self, AnimationNodeIndex) { let mut graph = Self::new(); let node_index = graph.add_clip(clip, 1.0, graph.root); (graph, node_index) } /// A convenience method to create an [`AnimationGraph`]s with an iterator /// of clips. /// /// All of the animation clips will be direct children of the root with /// weight 1.0. /// /// Returns the graph and indices of the new nodes. pub fn from_clips<'a, I>(clips: I) -> (Self, Vec) where I: IntoIterator>, ::IntoIter: 'a, { let mut graph = Self::new(); let indices = graph.add_clips(clips, 1.0, graph.root).collect(); (graph, indices) } /// Adds an [`AnimationClip`] to the animation graph with the given weight /// and returns its index. /// /// The animation clip will be the child of the given parent. The resulting /// node will have no mask. pub fn add_clip( &mut self, clip: Handle, weight: f32, parent: AnimationNodeIndex, ) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Clip(clip), mask: 0, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// Adds an [`AnimationClip`] to the animation graph with the given weight /// and mask, and returns its index. /// /// The animation clip will be the child of the given parent. pub fn add_clip_with_mask( &mut self, clip: Handle, mask: AnimationMask, weight: f32, parent: AnimationNodeIndex, ) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Clip(clip), mask, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// A convenience method to add multiple [`AnimationClip`]s to the animation /// graph. /// /// All of the animation clips will have the same weight and will be /// parented to the same node. /// /// Returns the indices of the new nodes. pub fn add_clips<'a, I>( &'a mut self, clips: I, weight: f32, parent: AnimationNodeIndex, ) -> impl Iterator + 'a where I: IntoIterator>, ::IntoIter: 'a, { clips .into_iter() .map(move |clip| self.add_clip(clip, weight, parent)) } /// Adds a blend node to the animation graph with the given weight and /// returns its index. /// /// The blend node will be placed under the supplied `parent` node. During /// animation evaluation, the descendants of this blend node will have their /// weights multiplied by the weight of the blend. The blend node will have /// no mask. pub fn add_blend(&mut self, weight: f32, parent: AnimationNodeIndex) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Blend, mask: 0, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// Adds a blend node to the animation graph with the given weight and /// returns its index. /// /// The blend node will be placed under the supplied `parent` node. During /// animation evaluation, the descendants of this blend node will have their /// weights multiplied by the weight of the blend. Neither this node nor its /// descendants will affect animation targets that belong to mask groups not /// in the given `mask`. pub fn add_blend_with_mask( &mut self, mask: AnimationMask, weight: f32, parent: AnimationNodeIndex, ) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Blend, mask, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// Adds a blend node to the animation graph with the given weight and /// returns its index. /// /// The blend node will be placed under the supplied `parent` node. During /// animation evaluation, the descendants of this blend node will have their /// weights multiplied by the weight of the blend. The blend node will have /// no mask. pub fn add_additive_blend( &mut self, weight: f32, parent: AnimationNodeIndex, ) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Add, mask: 0, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// Adds a blend node to the animation graph with the given weight and /// returns its index. /// /// The blend node will be placed under the supplied `parent` node. During /// animation evaluation, the descendants of this blend node will have their /// weights multiplied by the weight of the blend. Neither this node nor its /// descendants will affect animation targets that belong to mask groups not /// in the given `mask`. pub fn add_additive_blend_with_mask( &mut self, mask: AnimationMask, weight: f32, parent: AnimationNodeIndex, ) -> AnimationNodeIndex { let node_index = self.graph.add_node(AnimationGraphNode { node_type: AnimationNodeType::Add, mask, weight, }); self.graph.add_edge(parent, node_index, ()); node_index } /// Adds an edge from the edge `from` to `to`, making `to` a child of /// `from`. /// /// The behavior is unspecified if adding this produces a cycle in the /// graph. pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex) { self.graph.add_edge(from, to, ()); } /// Removes an edge between `from` and `to` if it exists. /// /// Returns true if the edge was successfully removed or false if no such /// edge existed. pub fn remove_edge(&mut self, from: NodeIndex, to: NodeIndex) -> bool { self.graph .find_edge(from, to) .map(|edge| self.graph.remove_edge(edge)) .is_some() } /// Returns the [`AnimationGraphNode`] associated with the given index. /// /// If no node with the given index exists, returns `None`. pub fn get(&self, animation: AnimationNodeIndex) -> Option<&AnimationGraphNode> { self.graph.node_weight(animation) } /// Returns a mutable reference to the [`AnimationGraphNode`] associated /// with the given index. /// /// If no node with the given index exists, returns `None`. pub fn get_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut AnimationGraphNode> { self.graph.node_weight_mut(animation) } /// Returns an iterator over the [`AnimationGraphNode`]s in this graph. pub fn nodes(&self) -> impl Iterator { self.graph.node_indices() } /// Serializes the animation graph to the given [`Write`]r in RON format. /// /// If writing to a file, it can later be loaded with the /// [`AnimationGraphAssetLoader`] to reconstruct the graph. pub fn save(&self, writer: &mut W) -> Result<(), AnimationGraphLoadError> where W: Write, { let mut ron_serializer = ron::ser::Serializer::new(writer, None)?; Ok(self.serialize(&mut ron_serializer)?) } /// Adds an animation target (bone) to the mask group with the given ID. /// /// Calling this method multiple times with the same animation target but /// different mask groups will result in that target being added to all of /// the specified groups. pub fn add_target_to_mask_group(&mut self, target: AnimationTargetId, mask_group: u32) { *self.mask_groups.entry(target).or_default() |= 1 << mask_group; } } impl AnimationGraphNode { /// Masks out the mask groups specified by the given `mask` bitfield. /// /// A 1 in bit position N causes this function to mask out mask group N, and /// thus neither this node nor its descendants will animate any animation /// targets that belong to group N. pub fn add_mask(&mut self, mask: AnimationMask) -> &mut Self { self.mask |= mask; self } /// Unmasks the mask groups specified by the given `mask` bitfield. /// /// A 1 in bit position N causes this function to unmask mask group N, and /// thus this node and its descendants will be allowed to animate animation /// targets that belong to group N, unless another mask masks those targets /// out. pub fn remove_mask(&mut self, mask: AnimationMask) -> &mut Self { self.mask &= !mask; self } /// Masks out the single mask group specified by `group`. /// /// After calling this function, neither this node nor its descendants will /// animate any animation targets that belong to the given `group`. pub fn add_mask_group(&mut self, group: u32) -> &mut Self { self.add_mask(1 << group) } /// Unmasks the single mask group specified by `group`. /// /// After calling this function, this node and its descendants will be /// allowed to animate animation targets that belong to the given `group`, /// unless another mask masks those targets out. pub fn remove_mask_group(&mut self, group: u32) -> &mut Self { self.remove_mask(1 << group) } } impl Index for AnimationGraph { type Output = AnimationGraphNode; fn index(&self, index: AnimationNodeIndex) -> &Self::Output { &self.graph[index] } } impl IndexMut for AnimationGraph { fn index_mut(&mut self, index: AnimationNodeIndex) -> &mut Self::Output { &mut self.graph[index] } } impl Default for AnimationGraphNode { fn default() -> Self { Self { node_type: Default::default(), mask: 0, weight: 1.0, } } } impl Default for AnimationGraph { fn default() -> Self { Self::new() } } impl AssetLoader for AnimationGraphAssetLoader { type Asset = AnimationGraph; type Settings = (); type Error = AnimationGraphLoadError; async fn load( &self, reader: &mut dyn Reader, _: &Self::Settings, load_context: &mut LoadContext<'_>, ) -> Result { let mut bytes = Vec::new(); reader.read_to_end(&mut bytes).await?; // Deserialize a `SerializedAnimationGraph` directly, so that we can // get the list of the animation clips it refers to and load them. let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?; let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer) .map_err(|err| deserializer.span_error(err))?; // Load all `AssetPath`s to convert from a // `SerializedAnimationGraph` to a real `AnimationGraph`. Ok(AnimationGraph { graph: serialized_animation_graph.graph.map( |_, serialized_node| AnimationGraphNode { node_type: match serialized_node.node_type { SerializedAnimationNodeType::Clip(ref clip) => match clip { SerializedAnimationClip::AssetId(asset_id) => { AnimationNodeType::Clip(Handle::Weak(*asset_id)) } SerializedAnimationClip::AssetPath(asset_path) => { AnimationNodeType::Clip(load_context.load(asset_path)) } }, SerializedAnimationNodeType::Blend => AnimationNodeType::Blend, SerializedAnimationNodeType::Add => AnimationNodeType::Add, }, mask: serialized_node.mask, weight: serialized_node.weight, }, |_, _| (), ), root: serialized_animation_graph.root, mask_groups: serialized_animation_graph.mask_groups, }) } fn extensions(&self) -> &[&str] { &["animgraph", "animgraph.ron"] } } impl From for SerializedAnimationGraph { fn from(animation_graph: AnimationGraph) -> Self { // If any of the animation clips have paths, then serialize them as // `SerializedAnimationClip::AssetPath` so that the // `AnimationGraphAssetLoader` can load them. Self { graph: animation_graph.graph.map( |_, node| SerializedAnimationGraphNode { weight: node.weight, mask: node.mask, node_type: match node.node_type { AnimationNodeType::Clip(ref clip) => match clip.path() { Some(path) => SerializedAnimationNodeType::Clip( SerializedAnimationClip::AssetPath(path.clone()), ), None => SerializedAnimationNodeType::Clip( SerializedAnimationClip::AssetId(clip.id()), ), }, AnimationNodeType::Blend => SerializedAnimationNodeType::Blend, AnimationNodeType::Add => SerializedAnimationNodeType::Add, }, }, |_, _| (), ), root: animation_graph.root, mask_groups: animation_graph.mask_groups, } } } /// A system that creates, updates, and removes [`ThreadedAnimationGraph`] /// structures for every changed [`AnimationGraph`]. /// /// The [`ThreadedAnimationGraph`] contains acceleration structures that allow /// for quick evaluation of that graph's animations. pub(crate) fn thread_animation_graphs( mut threaded_animation_graphs: ResMut, animation_graphs: Res>, mut animation_graph_asset_events: EventReader>, ) { for animation_graph_asset_event in animation_graph_asset_events.read() { match *animation_graph_asset_event { AssetEvent::Added { id } | AssetEvent::Modified { id } | AssetEvent::LoadedWithDependencies { id } => { // Fetch the animation graph. let Some(animation_graph) = animation_graphs.get(id) else { continue; }; // Reuse the allocation if possible. let mut threaded_animation_graph = threaded_animation_graphs.0.remove(&id).unwrap_or_default(); threaded_animation_graph.clear(); // Recursively thread the graph in postorder. threaded_animation_graph.init(animation_graph); threaded_animation_graph.build_from( &animation_graph.graph, animation_graph.root, 0, ); // Write in the threaded graph. threaded_animation_graphs .0 .insert(id, threaded_animation_graph); } AssetEvent::Removed { id } => { threaded_animation_graphs.0.remove(&id); } AssetEvent::Unused { .. } => {} } } } impl ThreadedAnimationGraph { /// Removes all the data in this [`ThreadedAnimationGraph`], keeping the /// memory around for later reuse. fn clear(&mut self) { self.threaded_graph.clear(); self.sorted_edge_ranges.clear(); self.sorted_edges.clear(); } /// Prepares the [`ThreadedAnimationGraph`] for recursion. fn init(&mut self, animation_graph: &AnimationGraph) { let node_count = animation_graph.graph.node_count(); let edge_count = animation_graph.graph.edge_count(); self.threaded_graph.reserve(node_count); self.sorted_edges.reserve(edge_count); self.sorted_edge_ranges.clear(); self.sorted_edge_ranges .extend(iter::repeat(0..0).take(node_count)); self.computed_masks.clear(); self.computed_masks.extend(iter::repeat(0).take(node_count)); } /// Recursively constructs the [`ThreadedAnimationGraph`] for the subtree /// rooted at the given node. /// /// `mask` specifies the computed mask of the parent node. (It could be /// fetched from the [`Self::computed_masks`] field, but we pass it /// explicitly as a micro-optimization.) fn build_from( &mut self, graph: &AnimationDiGraph, node_index: AnimationNodeIndex, mut mask: u64, ) { // Accumulate the mask. mask |= graph.node_weight(node_index).unwrap().mask; self.computed_masks[node_index.index()] = mask; // Gather up the indices of our children, and sort them. let mut kids: SmallVec<[AnimationNodeIndex; 8]> = graph .neighbors_directed(node_index, Direction::Outgoing) .collect(); kids.sort_unstable(); // Write in the list of kids. self.sorted_edge_ranges[node_index.index()] = (self.sorted_edges.len() as u32)..((self.sorted_edges.len() + kids.len()) as u32); self.sorted_edges.extend_from_slice(&kids); // Recurse. (This is a postorder traversal.) for kid in kids.into_iter().rev() { self.build_from(graph, kid, mask); } // Finally, push our index. self.threaded_graph.push(node_index); } }