//! 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,15message::MessageReader,16reflect::ReflectComponent,17resource::Resource,18system::{Res, ResMut},19};20use bevy_platform::collections::HashMap;21use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath};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;3132use crate::{AnimationClip, AnimationTargetId};3334/// A graph structure that describes how animation clips are to be blended35/// together.36///37/// Applications frequently want to be able to play multiple animations at once38/// and to fine-tune the influence that animations have on a skinned mesh. Bevy39/// uses an *animation graph* to store this information. Animation graphs are a40/// directed acyclic graph (DAG) that describes how animations are to be41/// weighted and combined together. Every frame, Bevy evaluates the graph from42/// the root and blends the animations together in a bottom-up fashion to43/// produce the final pose.44///45/// There are three types of nodes: *blend nodes*, *add nodes*, and *clip46/// nodes*, all of which can have an associated weight. Blend nodes and add47/// nodes have no associated animation clip and combine the animations of their48/// children according to those children's weights. Clip nodes specify an49/// animation clip to play. When a graph is created, it starts with only a50/// single blend node, the root node.51///52/// For example, consider the following graph:53///54/// ```text55/// ┌────────────┐56/// │ │57/// │ Idle ├─────────────────────┐58/// │ │ │59/// └────────────┘ │60/// │61/// ┌────────────┐ │ ┌────────────┐62/// │ │ │ │ │63/// │ Run ├──┐ ├──┤ Root │64/// │ │ │ ┌────────────┐ │ │ │65/// └────────────┘ │ │ Blend │ │ └────────────┘66/// ├──┤ ├──┘67/// ┌────────────┐ │ │ 0.5 │68/// │ │ │ └────────────┘69/// │ Walk ├──┘70/// │ │71/// └────────────┘72/// ```73///74/// In this case, assuming that Idle, Run, and Walk are all playing with weight75/// 1.0, the Run and Walk animations will be equally blended together, then76/// their weights will be halved and finally blended with the Idle animation.77/// Thus the weight of Run and Walk are effectively half of the weight of Idle.78///79/// Nodes can optionally have a *mask*, a bitfield that restricts the set of80/// animation targets that the node and its descendants affect. Each bit in the81/// mask corresponds to a *mask group*, which is a set of animation targets82/// (bones). An animation target can belong to any number of mask groups within83/// the context of an animation graph.84///85/// When the appropriate bit is set in a node's mask, neither the node nor its86/// descendants will animate any animation targets belonging to that mask group.87/// That is, setting a mask bit to 1 *disables* the animation targets in that88/// group. If an animation target belongs to multiple mask groups, masking any89/// one of the mask groups that it belongs to will mask that animation target.90/// (Thus an animation target will only be animated if *all* of its mask groups91/// are unmasked.)92///93/// A common use of masks is to allow characters to hold objects. For this, the94/// typical workflow is to assign each character's hand to a mask group. Then,95/// when the character picks up an object, the application masks out the hand96/// that the object is held in for the character's animation set, then positions97/// the hand's digits as necessary to grasp the object. The character's98/// animations will continue to play but will not affect the hand, which will99/// continue to be depicted as holding the object.100///101/// Animation graphs are assets and can be serialized to and loaded from [RON]102/// files. Canonically, such files have an `.animgraph.ron` extension.103///104/// The animation graph implements [RFC 51]. See that document for more105/// information.106///107/// [RON]: https://github.com/ron-rs/ron108///109/// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md110#[derive(Asset, Reflect, Clone, Debug)]111#[reflect(Debug, Clone)]112pub struct AnimationGraph {113/// The `petgraph` data structure that defines the animation graph.114pub graph: AnimationDiGraph,115116/// The index of the root node in the animation graph.117pub root: NodeIndex,118119/// The mask groups that each animation target (bone) belongs to.120///121/// Each value in this map is a bitfield, in which 0 in bit position N122/// indicates that the animation target doesn't belong to mask group N, and123/// a 1 in position N indicates that the animation target does belong to124/// mask group N.125///126/// Animation targets not in this collection are treated as though they127/// don't belong to any mask groups.128pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,129}130131/// A [`Handle`] to the [`AnimationGraph`] to be used by the [`AnimationPlayer`](crate::AnimationPlayer) on the same entity.132#[derive(Component, Clone, Debug, Default, Deref, DerefMut, Reflect, PartialEq, Eq, From)]133#[reflect(Component, Default, Clone)]134pub struct AnimationGraphHandle(pub Handle<AnimationGraph>);135136impl From<AnimationGraphHandle> for AssetId<AnimationGraph> {137fn from(handle: AnimationGraphHandle) -> Self {138handle.id()139}140}141142impl From<&AnimationGraphHandle> for AssetId<AnimationGraph> {143fn from(handle: &AnimationGraphHandle) -> Self {144handle.id()145}146}147148/// A type alias for the `petgraph` data structure that defines the animation149/// graph.150pub type AnimationDiGraph = DiGraph<AnimationGraphNode, (), u32>;151152/// The index of either an animation or blend node in the animation graph.153///154/// These indices are the way that [animation players] identify each animation.155///156/// [animation players]: crate::AnimationPlayer157pub type AnimationNodeIndex = NodeIndex<u32>;158159/// An individual node within an animation graph.160///161/// The [`AnimationGraphNode::node_type`] field specifies the type of node: one162/// of a *clip node*, a *blend node*, or an *add node*. Clip nodes, the leaves163/// of the graph, contain animation clips to play. Blend and add nodes describe164/// how to combine their children to produce a final animation.165#[derive(Clone, Reflect, Debug)]166#[reflect(Clone)]167pub struct AnimationGraphNode {168/// Animation node data specific to the type of node (clip, blend, or add).169///170/// In the case of clip nodes, this contains the actual animation clip171/// associated with the node.172pub node_type: AnimationNodeType,173174/// A bitfield specifying the mask groups that this node and its descendants175/// will not affect.176///177/// A 0 in bit N indicates that this node and its descendants *can* animate178/// animation targets in mask group N, while a 1 in bit N indicates that179/// this node and its descendants *cannot* animate mask group N.180pub mask: AnimationMask,181182/// The weight of this node, which signifies its contribution in blending.183///184/// Note that this does not propagate down the graph hierarchy; rather,185/// each [Blend] and [Add] node uses the weights of its children to determine186/// the total animation that is accumulated at that node. The parent node's187/// weight is used only to determine the contribution of that total animation188/// in *further* blending.189///190/// In other words, it is as if the blend node is replaced by a single clip191/// node consisting of the blended animation with the weight specified at the192/// blend node.193///194/// For animation clips, this weight is also multiplied by the [active animation weight]195/// before being applied.196///197/// [Blend]: AnimationNodeType::Blend198/// [Add]: AnimationNodeType::Add199/// [active animation weight]: crate::ActiveAnimation::weight200pub weight: f32,201}202203/// Animation node data specific to the type of node (clip, blend, or add).204///205/// In the case of clip nodes, this contains the actual animation clip206/// associated with the node.207#[derive(Clone, Default, Reflect, Debug)]208#[reflect(Clone)]209pub enum AnimationNodeType {210/// A *clip node*, which plays an animation clip.211///212/// These are always the leaves of the graph.213Clip(Handle<AnimationClip>),214215/// A *blend node*, which blends its children according to their weights.216///217/// The weights of all the children of this node are normalized to 1.0.218#[default]219Blend,220221/// An *additive blend node*, which combines the animations of its children222/// additively.223///224/// The weights of all the children of this node are *not* normalized to225/// 1.0. Rather, each child is multiplied by its respective weight and226/// added in sequence.227///228/// Add nodes are primarily useful for superimposing an animation for a229/// portion of a rig on top of the main animation. For example, an add node230/// could superimpose a weapon attack animation for a character's limb on231/// top of a running animation to produce an animation of a character232/// attacking while running.233Add,234}235236/// An [`AssetLoader`] that can load [`AnimationGraph`]s as assets.237///238/// The canonical extension for [`AnimationGraph`]s is `.animgraph.ron`. Plain239/// `.animgraph` is supported as well.240#[derive(Default, TypePath)]241pub struct AnimationGraphAssetLoader;242243/// Errors that can occur when serializing animation graphs to RON.244#[derive(Error, Debug)]245pub enum AnimationGraphSaveError {246/// An I/O error occurred.247#[error(transparent)]248Io(#[from] io::Error),249/// An error occurred in RON serialization.250#[error(transparent)]251Ron(#[from] ron::Error),252/// An error occurred converting the graph to its serialization form.253#[error(transparent)]254ConvertToSerialized(#[from] NonPathHandleError),255}256257/// Errors that can occur when deserializing animation graphs from RON.258#[derive(Error, Debug)]259pub enum AnimationGraphLoadError {260/// An I/O error occurred.261#[error(transparent)]262Io(#[from] io::Error),263/// An error occurred in RON deserialization.264#[error(transparent)]265Ron(#[from] ron::Error),266/// An error occurred in RON deserialization, and the location of the error267/// is supplied.268#[error(transparent)]269SpannedRon(#[from] SpannedError),270/// The deserialized graph contained legacy data that we no longer support.271#[error(272"The deserialized AnimationGraph contained an AnimationClip referenced by an AssetId, \273which is no longer supported. Consider manually deserializing the SerializedAnimationGraph \274type and determine how to migrate any SerializedAnimationClip::AssetId animation clips"275)]276GraphContainsLegacyAssetId,277}278279/// Acceleration structures for animation graphs that allows Bevy to evaluate280/// them quickly.281///282/// These are kept up to date as [`AnimationGraph`] instances are added,283/// modified, and removed.284#[derive(Default, Reflect, Resource)]285pub struct ThreadedAnimationGraphs(286pub(crate) HashMap<AssetId<AnimationGraph>, ThreadedAnimationGraph>,287);288289/// An acceleration structure for an animation graph that allows Bevy to290/// evaluate it quickly.291///292/// This is kept up to date as the associated [`AnimationGraph`] instance is293/// added, modified, or removed.294#[derive(Default, Reflect)]295pub struct ThreadedAnimationGraph {296/// A cached postorder traversal of the graph.297///298/// The node indices here are stored in postorder. Siblings are stored in299/// descending order. This is because the300/// [`AnimationCurveEvaluator`](`crate::animation_curves::AnimationCurveEvaluator`) uses a stack for301/// evaluation. Consider this graph:302///303/// ```text304/// ┌─────┐305/// │ │306/// │ 1 │307/// │ │308/// └──┬──┘309/// │310/// ┌───────┼───────┐311/// │ │ │312/// ▼ ▼ ▼313/// ┌─────┐ ┌─────┐ ┌─────┐314/// │ │ │ │ │ │315/// │ 2 │ │ 3 │ │ 4 │316/// │ │ │ │ │ │317/// └──┬──┘ └─────┘ └─────┘318/// │319/// ┌───┴───┐320/// │ │321/// ▼ ▼322/// ┌─────┐ ┌─────┐323/// │ │ │ │324/// │ 5 │ │ 6 │325/// │ │ │ │326/// └─────┘ └─────┘327/// ```328///329/// The postorder traversal in this case will be (4, 3, 6, 5, 2, 1).330///331/// The fact that the children of each node are sorted in reverse ensures332/// that, at each level, the order of blending proceeds in ascending order333/// by node index, as we guarantee. To illustrate this, consider the way334/// the graph above is evaluated. (Interpolation is represented with the ⊕335/// symbol.)336///337/// | Step | Node | Operation | Stack (after operation) | Blend Register |338/// | ---- | ---- | ---------- | ----------------------- | -------------- |339/// | 1 | 4 | Push | 4 | |340/// | 2 | 3 | Push | 4 3 | |341/// | 3 | 6 | Push | 4 3 6 | |342/// | 4 | 5 | Push | 4 3 6 5 | |343/// | 5 | 2 | Blend 5 | 4 3 6 | 5 |344/// | 6 | 2 | Blend 6 | 4 3 | 5 ⊕ 6 |345/// | 7 | 2 | Push Blend | 4 3 2 | |346/// | 8 | 1 | Blend 2 | 4 3 | 2 |347/// | 9 | 1 | Blend 3 | 4 | 2 ⊕ 3 |348/// | 10 | 1 | Blend 4 | | 2 ⊕ 3 ⊕ 4 |349/// | 11 | 1 | Push Blend | 1 | |350/// | 12 | | Commit | | |351pub threaded_graph: Vec<AnimationNodeIndex>,352353/// A mapping from each parent node index to the range within354/// [`Self::sorted_edges`].355///356/// This allows for quick lookup of the children of each node, sorted in357/// ascending order of node index, without having to sort the result of the358/// `petgraph` traversal functions every frame.359pub sorted_edge_ranges: Vec<Range<u32>>,360361/// A list of the children of each node, sorted in ascending order.362pub sorted_edges: Vec<AnimationNodeIndex>,363364/// A mapping from node index to a bitfield specifying the mask groups that365/// this node masks *out* (i.e. doesn't animate).366///367/// A 1 in bit position N indicates that this node doesn't animate any368/// targets of mask group N.369pub computed_masks: Vec<u64>,370}371372/// A version of [`AnimationGraph`] suitable for serializing as an asset.373///374/// Animation nodes can refer to external animation clips, and the [`AssetId`]375/// is typically not sufficient to identify the clips, since the376/// [`bevy_asset::AssetServer`] assigns IDs in unpredictable ways. That fact377/// motivates this type, which replaces the `Handle<AnimationClip>` with an378/// asset path. Loading an animation graph via the [`bevy_asset::AssetServer`]379/// actually loads a serialized instance of this type, as does serializing an380/// [`AnimationGraph`] through `serde`.381#[derive(Serialize, Deserialize)]382pub struct SerializedAnimationGraph {383/// Corresponds to the `graph` field on [`AnimationGraph`].384pub graph: DiGraph<SerializedAnimationGraphNode, (), u32>,385/// Corresponds to the `root` field on [`AnimationGraph`].386pub root: NodeIndex,387/// Corresponds to the `mask_groups` field on [`AnimationGraph`].388pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,389}390391/// A version of [`AnimationGraphNode`] suitable for serializing as an asset.392///393/// See the comments in [`SerializedAnimationGraph`] for more information.394#[derive(Serialize, Deserialize)]395pub struct SerializedAnimationGraphNode {396/// Corresponds to the `node_type` field on [`AnimationGraphNode`].397pub node_type: SerializedAnimationNodeType,398/// Corresponds to the `mask` field on [`AnimationGraphNode`].399pub mask: AnimationMask,400/// Corresponds to the `weight` field on [`AnimationGraphNode`].401pub weight: f32,402}403404/// A version of [`AnimationNodeType`] suitable for serializing as part of a405/// [`SerializedAnimationGraphNode`] asset.406#[derive(Serialize, Deserialize)]407pub enum SerializedAnimationNodeType {408/// Corresponds to [`AnimationNodeType::Clip`].409Clip(AssetPath<'static>),410/// Corresponds to [`AnimationNodeType::Blend`].411Blend,412/// Corresponds to [`AnimationNodeType::Add`].413Add,414}415416/// The type of an animation mask bitfield.417///418/// Bit N corresponds to mask group N.419///420/// Because this is a 64-bit value, there is currently a limitation of 64 mask421/// groups per animation graph.422pub type AnimationMask = u64;423424impl AnimationGraph {425/// Creates a new animation graph with a root node and no other nodes.426pub fn new() -> Self {427let mut graph = DiGraph::default();428let root = graph.add_node(AnimationGraphNode::default());429Self {430graph,431root,432mask_groups: HashMap::default(),433}434}435436/// A convenience function for creating an [`AnimationGraph`] from a single437/// [`AnimationClip`].438///439/// The clip will be a direct child of the root with weight 1.0. Both the440/// graph and the index of the added node are returned as a tuple.441pub fn from_clip(clip: Handle<AnimationClip>) -> (Self, AnimationNodeIndex) {442let mut graph = Self::new();443let node_index = graph.add_clip(clip, 1.0, graph.root);444(graph, node_index)445}446447/// A convenience method to create an [`AnimationGraph`]s with an iterator448/// of clips.449///450/// All of the animation clips will be direct children of the root with451/// weight 1.0.452///453/// Returns the graph and indices of the new nodes.454pub fn from_clips<'a, I>(clips: I) -> (Self, Vec<AnimationNodeIndex>)455where456I: IntoIterator<Item = Handle<AnimationClip>>,457<I as IntoIterator>::IntoIter: 'a,458{459let mut graph = Self::new();460let indices = graph.add_clips(clips, 1.0, graph.root).collect();461(graph, indices)462}463464/// Adds an [`AnimationClip`] to the animation graph with the given weight465/// and returns its index.466///467/// The animation clip will be the child of the given parent. The resulting468/// node will have no mask.469pub fn add_clip(470&mut self,471clip: Handle<AnimationClip>,472weight: f32,473parent: AnimationNodeIndex,474) -> AnimationNodeIndex {475let node_index = self.graph.add_node(AnimationGraphNode {476node_type: AnimationNodeType::Clip(clip),477mask: 0,478weight,479});480self.graph.add_edge(parent, node_index, ());481node_index482}483484/// Adds an [`AnimationClip`] to the animation graph with the given weight485/// and mask, and returns its index.486///487/// The animation clip will be the child of the given parent.488pub fn add_clip_with_mask(489&mut self,490clip: Handle<AnimationClip>,491mask: AnimationMask,492weight: f32,493parent: AnimationNodeIndex,494) -> AnimationNodeIndex {495let node_index = self.graph.add_node(AnimationGraphNode {496node_type: AnimationNodeType::Clip(clip),497mask,498weight,499});500self.graph.add_edge(parent, node_index, ());501node_index502}503504/// A convenience method to add multiple [`AnimationClip`]s to the animation505/// graph.506///507/// All of the animation clips will have the same weight and will be508/// parented to the same node.509///510/// Returns the indices of the new nodes.511pub fn add_clips<'a, I>(512&'a mut self,513clips: I,514weight: f32,515parent: AnimationNodeIndex,516) -> impl Iterator<Item = AnimationNodeIndex> + 'a517where518I: IntoIterator<Item = Handle<AnimationClip>>,519<I as IntoIterator>::IntoIter: 'a,520{521clips522.into_iter()523.map(move |clip| self.add_clip(clip, weight, parent))524}525526/// Adds a blend node to the animation graph with the given weight and527/// returns its index.528///529/// The blend node will be placed under the supplied `parent` node. During530/// animation evaluation, the descendants of this blend node will have their531/// weights multiplied by the weight of the blend. The blend node will have532/// no mask.533pub fn add_blend(&mut self, weight: f32, parent: AnimationNodeIndex) -> AnimationNodeIndex {534let node_index = self.graph.add_node(AnimationGraphNode {535node_type: AnimationNodeType::Blend,536mask: 0,537weight,538});539self.graph.add_edge(parent, node_index, ());540node_index541}542543/// Adds a blend node to the animation graph with the given weight and544/// returns its index.545///546/// The blend node will be placed under the supplied `parent` node. During547/// animation evaluation, the descendants of this blend node will have their548/// weights multiplied by the weight of the blend. Neither this node nor its549/// descendants will affect animation targets that belong to mask groups not550/// in the given `mask`.551pub fn add_blend_with_mask(552&mut self,553mask: AnimationMask,554weight: f32,555parent: AnimationNodeIndex,556) -> AnimationNodeIndex {557let node_index = self.graph.add_node(AnimationGraphNode {558node_type: AnimationNodeType::Blend,559mask,560weight,561});562self.graph.add_edge(parent, node_index, ());563node_index564}565566/// Adds a blend node to the animation graph with the given weight and567/// returns its index.568///569/// The blend node will be placed under the supplied `parent` node. During570/// animation evaluation, the descendants of this blend node will have their571/// weights multiplied by the weight of the blend. The blend node will have572/// no mask.573pub fn add_additive_blend(574&mut self,575weight: f32,576parent: AnimationNodeIndex,577) -> AnimationNodeIndex {578let node_index = self.graph.add_node(AnimationGraphNode {579node_type: AnimationNodeType::Add,580mask: 0,581weight,582});583self.graph.add_edge(parent, node_index, ());584node_index585}586587/// Adds a blend node to the animation graph with the given weight and588/// returns its index.589///590/// The blend node will be placed under the supplied `parent` node. During591/// animation evaluation, the descendants of this blend node will have their592/// weights multiplied by the weight of the blend. Neither this node nor its593/// descendants will affect animation targets that belong to mask groups not594/// in the given `mask`.595pub fn add_additive_blend_with_mask(596&mut self,597mask: AnimationMask,598weight: f32,599parent: AnimationNodeIndex,600) -> AnimationNodeIndex {601let node_index = self.graph.add_node(AnimationGraphNode {602node_type: AnimationNodeType::Add,603mask,604weight,605});606self.graph.add_edge(parent, node_index, ());607node_index608}609610/// Adds an edge from the edge `from` to `to`, making `to` a child of611/// `from`.612///613/// The behavior is unspecified if adding this produces a cycle in the614/// graph.615pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex) {616self.graph.add_edge(from, to, ());617}618619/// Removes an edge between `from` and `to` if it exists.620///621/// Returns true if the edge was successfully removed or false if no such622/// edge existed.623pub fn remove_edge(&mut self, from: NodeIndex, to: NodeIndex) -> bool {624self.graph625.find_edge(from, to)626.map(|edge| self.graph.remove_edge(edge))627.is_some()628}629630/// Returns the [`AnimationGraphNode`] associated with the given index.631///632/// If no node with the given index exists, returns `None`.633pub fn get(&self, animation: AnimationNodeIndex) -> Option<&AnimationGraphNode> {634self.graph.node_weight(animation)635}636637/// Returns a mutable reference to the [`AnimationGraphNode`] associated638/// with the given index.639///640/// If no node with the given index exists, returns `None`.641pub fn get_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut AnimationGraphNode> {642self.graph.node_weight_mut(animation)643}644645/// Returns an iterator over the [`AnimationGraphNode`]s in this graph.646pub fn nodes(&self) -> impl Iterator<Item = AnimationNodeIndex> {647self.graph.node_indices()648}649650/// Serializes the animation graph to the given [`Write`]r in RON format.651///652/// If writing to a file, it can later be loaded with the653/// [`AnimationGraphAssetLoader`] to reconstruct the graph.654pub fn save<W>(&self, writer: &mut W) -> Result<(), AnimationGraphSaveError>655where656W: Write,657{658let mut ron_serializer = ron::ser::Serializer::new(writer, None)?;659let serialized_graph: SerializedAnimationGraph = self.clone().try_into()?;660Ok(serialized_graph.serialize(&mut ron_serializer)?)661}662663/// Adds an animation target (bone) to the mask group with the given ID.664///665/// Calling this method multiple times with the same animation target but666/// different mask groups will result in that target being added to all of667/// the specified groups.668pub fn add_target_to_mask_group(&mut self, target: AnimationTargetId, mask_group: u32) {669*self.mask_groups.entry(target).or_default() |= 1 << mask_group;670}671}672673impl AnimationGraphNode {674/// Masks out the mask groups specified by the given `mask` bitfield.675///676/// A 1 in bit position N causes this function to mask out mask group N, and677/// thus neither this node nor its descendants will animate any animation678/// targets that belong to group N.679pub fn add_mask(&mut self, mask: AnimationMask) -> &mut Self {680self.mask |= mask;681self682}683684/// Unmasks the mask groups specified by the given `mask` bitfield.685///686/// A 1 in bit position N causes this function to unmask mask group N, and687/// thus this node and its descendants will be allowed to animate animation688/// targets that belong to group N, unless another mask masks those targets689/// out.690pub fn remove_mask(&mut self, mask: AnimationMask) -> &mut Self {691self.mask &= !mask;692self693}694695/// Masks out the single mask group specified by `group`.696///697/// After calling this function, neither this node nor its descendants will698/// animate any animation targets that belong to the given `group`.699pub fn add_mask_group(&mut self, group: u32) -> &mut Self {700self.add_mask(1 << group)701}702703/// Unmasks the single mask group specified by `group`.704///705/// After calling this function, this node and its descendants will be706/// allowed to animate animation targets that belong to the given `group`,707/// unless another mask masks those targets out.708pub fn remove_mask_group(&mut self, group: u32) -> &mut Self {709self.remove_mask(1 << group)710}711}712713impl Index<AnimationNodeIndex> for AnimationGraph {714type Output = AnimationGraphNode;715716fn index(&self, index: AnimationNodeIndex) -> &Self::Output {717&self.graph[index]718}719}720721impl IndexMut<AnimationNodeIndex> for AnimationGraph {722fn index_mut(&mut self, index: AnimationNodeIndex) -> &mut Self::Output {723&mut self.graph[index]724}725}726727impl Default for AnimationGraphNode {728fn default() -> Self {729Self {730node_type: Default::default(),731mask: 0,732weight: 1.0,733}734}735}736737impl Default for AnimationGraph {738fn default() -> Self {739Self::new()740}741}742743impl AssetLoader for AnimationGraphAssetLoader {744type Asset = AnimationGraph;745746type Settings = ();747748type Error = AnimationGraphLoadError;749750async fn load(751&self,752reader: &mut dyn Reader,753_: &Self::Settings,754load_context: &mut LoadContext<'_>,755) -> Result<Self::Asset, Self::Error> {756let mut bytes = Vec::new();757reader.read_to_end(&mut bytes).await?;758759// Deserialize a `SerializedAnimationGraph` directly, so that we can760// get the list of the animation clips it refers to and load them.761let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;762let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)763.map_err(|err| deserializer.span_error(err))?;764765// Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real766// `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return767// errors.768let mut animation_graph = DiGraph::with_capacity(769serialized_animation_graph.graph.node_count(),770serialized_animation_graph.graph.edge_count(),771);772773for serialized_node in serialized_animation_graph.graph.node_weights() {774animation_graph.add_node(AnimationGraphNode {775node_type: match serialized_node.node_type {776SerializedAnimationNodeType::Clip(ref path) => {777AnimationNodeType::Clip(load_context.load(path.clone()))778}779SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,780SerializedAnimationNodeType::Add => AnimationNodeType::Add,781},782mask: serialized_node.mask,783weight: serialized_node.weight,784});785}786for edge in serialized_animation_graph.graph.raw_edges() {787animation_graph.add_edge(edge.source(), edge.target(), ());788}789Ok(AnimationGraph {790graph: animation_graph,791root: serialized_animation_graph.root,792mask_groups: serialized_animation_graph.mask_groups,793})794}795796fn extensions(&self) -> &[&str] {797&["animgraph", "animgraph.ron"]798}799}800801impl TryFrom<AnimationGraph> for SerializedAnimationGraph {802type Error = NonPathHandleError;803804fn try_from(animation_graph: AnimationGraph) -> Result<Self, NonPathHandleError> {805// Convert all the `Handle<AnimationClip>` to AssetPath, so that806// `AnimationGraphAssetLoader` can load them. This is effectively just doing a807// `DiGraph::map`, except we need to return an error if any handles aren't associated to a808// path.809let mut serialized_graph = DiGraph::with_capacity(810animation_graph.graph.node_count(),811animation_graph.graph.edge_count(),812);813for node in animation_graph.graph.node_weights() {814serialized_graph.add_node(SerializedAnimationGraphNode {815weight: node.weight,816mask: node.mask,817node_type: match node.node_type {818AnimationNodeType::Clip(ref clip) => match clip.path() {819Some(path) => SerializedAnimationNodeType::Clip(path.clone()),820None => return Err(NonPathHandleError),821},822AnimationNodeType::Blend => SerializedAnimationNodeType::Blend,823AnimationNodeType::Add => SerializedAnimationNodeType::Add,824},825});826}827for edge in animation_graph.graph.raw_edges() {828serialized_graph.add_edge(edge.source(), edge.target(), ());829}830Ok(Self {831graph: serialized_graph,832root: animation_graph.root,833mask_groups: animation_graph.mask_groups,834})835}836}837838/// Error for when only path [`Handle`]s are supported.839#[derive(Error, Debug)]840#[error("AnimationGraph contains a handle to an AnimationClip that does not correspond to an asset path")]841pub struct NonPathHandleError;842843/// A system that creates, updates, and removes [`ThreadedAnimationGraph`]844/// structures for every changed [`AnimationGraph`].845///846/// The [`ThreadedAnimationGraph`] contains acceleration structures that allow847/// for quick evaluation of that graph's animations.848pub(crate) fn thread_animation_graphs(849mut threaded_animation_graphs: ResMut<ThreadedAnimationGraphs>,850animation_graphs: Res<Assets<AnimationGraph>>,851mut animation_graph_asset_events: MessageReader<AssetEvent<AnimationGraph>>,852) {853for animation_graph_asset_event in animation_graph_asset_events.read() {854match *animation_graph_asset_event {855AssetEvent::Added { id }856| AssetEvent::Modified { id }857| AssetEvent::LoadedWithDependencies { id } => {858// Fetch the animation graph.859let Some(animation_graph) = animation_graphs.get(id) else {860continue;861};862863// Reuse the allocation if possible.864let mut threaded_animation_graph =865threaded_animation_graphs.0.remove(&id).unwrap_or_default();866threaded_animation_graph.clear();867868// Recursively thread the graph in postorder.869threaded_animation_graph.init(animation_graph);870threaded_animation_graph.build_from(871&animation_graph.graph,872animation_graph.root,8730,874);875876// Write in the threaded graph.877threaded_animation_graphs878.0879.insert(id, threaded_animation_graph);880}881882AssetEvent::Removed { id } => {883threaded_animation_graphs.0.remove(&id);884}885AssetEvent::Unused { .. } => {}886}887}888}889890impl ThreadedAnimationGraph {891/// Removes all the data in this [`ThreadedAnimationGraph`], keeping the892/// memory around for later reuse.893fn clear(&mut self) {894self.threaded_graph.clear();895self.sorted_edge_ranges.clear();896self.sorted_edges.clear();897}898899/// Prepares the [`ThreadedAnimationGraph`] for recursion.900fn init(&mut self, animation_graph: &AnimationGraph) {901let node_count = animation_graph.graph.node_count();902let edge_count = animation_graph.graph.edge_count();903904self.threaded_graph.reserve(node_count);905self.sorted_edges.reserve(edge_count);906907self.sorted_edge_ranges.clear();908self.sorted_edge_ranges909.extend(iter::repeat_n(0..0, node_count));910911self.computed_masks.clear();912self.computed_masks.extend(iter::repeat_n(0, node_count));913}914915/// Recursively constructs the [`ThreadedAnimationGraph`] for the subtree916/// rooted at the given node.917///918/// `mask` specifies the computed mask of the parent node. (It could be919/// fetched from the [`Self::computed_masks`] field, but we pass it920/// explicitly as a micro-optimization.)921fn build_from(922&mut self,923graph: &AnimationDiGraph,924node_index: AnimationNodeIndex,925mut mask: u64,926) {927// Accumulate the mask.928mask |= graph.node_weight(node_index).unwrap().mask;929self.computed_masks[node_index.index()] = mask;930931// Gather up the indices of our children, and sort them.932let mut kids: SmallVec<[AnimationNodeIndex; 8]> = graph933.neighbors_directed(node_index, Direction::Outgoing)934.collect();935kids.sort_unstable();936937// Write in the list of kids.938self.sorted_edge_ranges[node_index.index()] =939(self.sorted_edges.len() as u32)..((self.sorted_edges.len() + kids.len()) as u32);940self.sorted_edges.extend_from_slice(&kids);941942// Recurse. (This is a postorder traversal.)943for kid in kids.into_iter().rev() {944self.build_from(graph, kid, mask);945}946947// Finally, push our index.948self.threaded_graph.push(node_index);949}950}951952953