bevy_animation/graph.rs
1//! The animation graph, which allows animations to be blended together.
2
3use core::{
4 fmt::Write,
5 iter,
6 ops::{Index, IndexMut, Range},
7};
8use std::io;
9
10use bevy_asset::{
11 io::Reader, Asset, AssetEvent, AssetId, AssetLoader, AssetPath, Assets, Handle, LoadContext,
12};
13use bevy_derive::{Deref, DerefMut};
14use bevy_ecs::{
15 component::Component,
16 message::MessageReader,
17 reflect::ReflectComponent,
18 resource::Resource,
19 system::{Res, ResMut},
20};
21use bevy_platform::collections::HashMap;
22use bevy_reflect::{prelude::ReflectDefault, Reflect, TypePath};
23use derive_more::derive::From;
24use petgraph::{
25 graph::{DiGraph, NodeIndex},
26 Direction,
27};
28use ron::de::SpannedError;
29use serde::{Deserialize, Serialize};
30use smallvec::SmallVec;
31use thiserror::Error;
32
33use crate::{AnimationClip, AnimationTargetId};
34
35/// A graph structure that describes how animation clips are to be blended
36/// together.
37///
38/// Applications frequently want to be able to play multiple animations at once
39/// and to fine-tune the influence that animations have on a skinned mesh. Bevy
40/// uses an *animation graph* to store this information. Animation graphs are a
41/// directed acyclic graph (DAG) that describes how animations are to be
42/// weighted and combined together. Every frame, Bevy evaluates the graph from
43/// the root and blends the animations together in a bottom-up fashion to
44/// produce the final pose.
45///
46/// There are three types of nodes: *blend nodes*, *add nodes*, and *clip
47/// nodes*, all of which can have an associated weight. Blend nodes and add
48/// nodes have no associated animation clip and combine the animations of their
49/// children according to those children's weights. Clip nodes specify an
50/// animation clip to play. When a graph is created, it starts with only a
51/// single blend node, the root node.
52///
53/// For example, consider the following graph:
54///
55/// ```text
56/// ┌────────────┐
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 weight
76/// 1.0, the Run and Walk animations will be equally blended together, then
77/// 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 of
81/// animation targets that the node and its descendants affect. Each bit in the
82/// mask corresponds to a *mask group*, which is a set of animation targets
83/// (bones). An animation target can belong to any number of mask groups within
84/// the context of an animation graph.
85///
86/// When the appropriate bit is set in a node's mask, neither the node nor its
87/// 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 that
89/// group. If an animation target belongs to multiple mask groups, masking any
90/// 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 groups
92/// are unmasked.)
93///
94/// A common use of masks is to allow characters to hold objects. For this, the
95/// 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 hand
97/// that the object is held in for the character's animation set, then positions
98/// the hand's digits as necessary to grasp the object. The character's
99/// animations will continue to play but will not affect the hand, which will
100/// 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 more
106/// information.
107///
108/// [RON]: https://github.com/ron-rs/ron
109///
110/// [RFC 51]: https://github.com/bevyengine/rfcs/blob/main/rfcs/51-animation-composition.md
111#[derive(Asset, Reflect, Clone, Debug)]
112#[reflect(Debug, Clone)]
113pub struct AnimationGraph {
114 /// The `petgraph` data structure that defines the animation graph.
115 pub graph: AnimationDiGraph,
116
117 /// The index of the root node in the animation graph.
118 pub root: NodeIndex,
119
120 /// 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 N
123 /// indicates that the animation target doesn't belong to mask group N, and
124 /// a 1 in position N indicates that the animation target does belong to
125 /// mask group N.
126 ///
127 /// Animation targets not in this collection are treated as though they
128 /// don't belong to any mask groups.
129 pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,
130}
131
132/// 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>);
136
137impl From<AnimationGraphHandle> for AssetId<AnimationGraph> {
138 fn from(handle: AnimationGraphHandle) -> Self {
139 handle.id()
140 }
141}
142
143impl From<&AnimationGraphHandle> for AssetId<AnimationGraph> {
144 fn from(handle: &AnimationGraphHandle) -> Self {
145 handle.id()
146 }
147}
148
149/// A type alias for the `petgraph` data structure that defines the animation
150/// graph.
151pub type AnimationDiGraph = DiGraph<AnimationGraphNode, (), u32>;
152
153/// 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::AnimationPlayer
158pub type AnimationNodeIndex = NodeIndex<u32>;
159
160/// An individual node within an animation graph.
161///
162/// The [`AnimationGraphNode::node_type`] field specifies the type of node: one
163/// of a *clip node*, a *blend node*, or an *add node*. Clip nodes, the leaves
164/// of the graph, contain animation clips to play. Blend and add nodes describe
165/// 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 clip
172 /// associated with the node.
173 pub node_type: AnimationNodeType,
174
175 /// A bitfield specifying the mask groups that this node and its descendants
176 /// will not affect.
177 ///
178 /// A 0 in bit N indicates that this node and its descendants *can* animate
179 /// animation targets in mask group N, while a 1 in bit N indicates that
180 /// this node and its descendants *cannot* animate mask group N.
181 pub mask: AnimationMask,
182
183 /// 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 determine
187 /// the total animation that is accumulated at that node. The parent node's
188 /// weight is used only to determine the contribution of that total animation
189 /// in *further* blending.
190 ///
191 /// In other words, it is as if the blend node is replaced by a single clip
192 /// node consisting of the blended animation with the weight specified at the
193 /// 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::Blend
199 /// [Add]: AnimationNodeType::Add
200 /// [active animation weight]: crate::ActiveAnimation::weight
201 pub weight: f32,
202}
203
204/// 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 clip
207/// 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.
214 Clip(Handle<AnimationClip>),
215
216 /// 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]
220 Blend,
221
222 /// An *additive blend node*, which combines the animations of its children
223 /// additively.
224 ///
225 /// The weights of all the children of this node are *not* normalized to
226 /// 1.0. Rather, each child is multiplied by its respective weight and
227 /// added in sequence.
228 ///
229 /// Add nodes are primarily useful for superimposing an animation for a
230 /// portion of a rig on top of the main animation. For example, an add node
231 /// could superimpose a weapon attack animation for a character's limb on
232 /// top of a running animation to produce an animation of a character
233 /// attacking while running.
234 Add,
235}
236
237/// An [`AssetLoader`] that can load [`AnimationGraph`]s as assets.
238///
239/// The canonical extension for [`AnimationGraph`]s is `.animgraph.ron`. Plain
240/// `.animgraph` is supported as well.
241#[derive(Default, TypePath)]
242pub struct AnimationGraphAssetLoader;
243
244/// 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)]
249 Io(#[from] io::Error),
250 /// An error occurred in RON serialization.
251 #[error(transparent)]
252 Ron(#[from] ron::Error),
253 /// An error occurred converting the graph to its serialization form.
254 #[error(transparent)]
255 ConvertToSerialized(#[from] NonPathHandleError),
256}
257
258/// 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)]
263 Io(#[from] io::Error),
264 /// An error occurred in RON deserialization.
265 #[error(transparent)]
266 Ron(#[from] ron::Error),
267 /// An error occurred in RON deserialization, and the location of the error
268 /// is supplied.
269 #[error(transparent)]
270 SpannedRon(#[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, \
274 which is no longer supported. Consider manually deserializing the SerializedAnimationGraph \
275 type and determine how to migrate any SerializedAnimationClip::AssetId animation clips"
276 )]
277 GraphContainsLegacyAssetId,
278}
279
280/// Acceleration structures for animation graphs that allows Bevy to evaluate
281/// 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(
287 pub(crate) HashMap<AssetId<AnimationGraph>, ThreadedAnimationGraph>,
288);
289
290/// An acceleration structure for an animation graph that allows Bevy to
291/// evaluate it quickly.
292///
293/// This is kept up to date as the associated [`AnimationGraph`] instance is
294/// 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 in
300 /// descending order. This is because the
301 /// [`AnimationCurveEvaluator`](`crate::animation_curves::AnimationCurveEvaluator`) uses a stack for
302 /// evaluation. Consider this graph:
303 ///
304 /// ```text
305 /// ┌─────┐
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 ensures
333 /// that, at each level, the order of blending proceeds in ascending order
334 /// by node index, as we guarantee. To illustrate this, consider the way
335 /// 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 | | |
352 pub threaded_graph: Vec<AnimationNodeIndex>,
353
354 /// A mapping from each parent node index to the range within
355 /// [`Self::sorted_edges`].
356 ///
357 /// This allows for quick lookup of the children of each node, sorted in
358 /// ascending order of node index, without having to sort the result of the
359 /// `petgraph` traversal functions every frame.
360 pub sorted_edge_ranges: Vec<Range<u32>>,
361
362 /// A list of the children of each node, sorted in ascending order.
363 pub sorted_edges: Vec<AnimationNodeIndex>,
364
365 /// A mapping from node index to a bitfield specifying the mask groups that
366 /// this node masks *out* (i.e. doesn't animate).
367 ///
368 /// A 1 in bit position N indicates that this node doesn't animate any
369 /// targets of mask group N.
370 pub computed_masks: Vec<u64>,
371}
372
373/// 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 the
377/// [`bevy_asset::AssetServer`] assigns IDs in unpredictable ways. That fact
378/// motivates this type, which replaces the `Handle<AnimationClip>` with an
379/// asset path. Loading an animation graph via the [`bevy_asset::AssetServer`]
380/// actually loads a serialized instance of this type, as does serializing an
381/// [`AnimationGraph`] through `serde`.
382#[derive(Serialize, Deserialize)]
383pub struct SerializedAnimationGraph {
384 /// Corresponds to the `graph` field on [`AnimationGraph`].
385 pub graph: DiGraph<SerializedAnimationGraphNode, (), u32>,
386 /// Corresponds to the `root` field on [`AnimationGraph`].
387 pub root: NodeIndex,
388 /// Corresponds to the `mask_groups` field on [`AnimationGraph`].
389 pub mask_groups: HashMap<AnimationTargetId, AnimationMask>,
390}
391
392/// 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`].
398 pub node_type: SerializedAnimationNodeType,
399 /// Corresponds to the `mask` field on [`AnimationGraphNode`].
400 pub mask: AnimationMask,
401 /// Corresponds to the `weight` field on [`AnimationGraphNode`].
402 pub weight: f32,
403}
404
405/// A version of [`AnimationNodeType`] suitable for serializing as part of a
406/// [`SerializedAnimationGraphNode`] asset.
407#[derive(Serialize, Deserialize)]
408pub enum SerializedAnimationNodeType {
409 /// Corresponds to [`AnimationNodeType::Clip`].
410 Clip(AssetPath<'static>),
411 /// Corresponds to [`AnimationNodeType::Blend`].
412 Blend,
413 /// Corresponds to [`AnimationNodeType::Add`].
414 Add,
415}
416
417/// The type of an animation mask bitfield.
418///
419/// Bit N corresponds to mask group N.
420///
421/// Because this is a 64-bit value, there is currently a limitation of 64 mask
422/// groups per animation graph.
423pub type AnimationMask = u64;
424
425impl AnimationGraph {
426 /// Creates a new animation graph with a root node and no other nodes.
427 pub fn new() -> Self {
428 let mut graph = DiGraph::default();
429 let root = graph.add_node(AnimationGraphNode::default());
430 Self {
431 graph,
432 root,
433 mask_groups: HashMap::default(),
434 }
435 }
436
437 /// A convenience function for creating an [`AnimationGraph`] from a single
438 /// [`AnimationClip`].
439 ///
440 /// The clip will be a direct child of the root with weight 1.0. Both the
441 /// graph and the index of the added node are returned as a tuple.
442 pub fn from_clip(clip: Handle<AnimationClip>) -> (Self, AnimationNodeIndex) {
443 let mut graph = Self::new();
444 let node_index = graph.add_clip(clip, 1.0, graph.root);
445 (graph, node_index)
446 }
447
448 /// A convenience method to create an [`AnimationGraph`]s with an iterator
449 /// of clips.
450 ///
451 /// All of the animation clips will be direct children of the root with
452 /// weight 1.0.
453 ///
454 /// Returns the graph and indices of the new nodes.
455 pub fn from_clips<'a, I>(clips: I) -> (Self, Vec<AnimationNodeIndex>)
456 where
457 I: IntoIterator<Item = Handle<AnimationClip>>,
458 <I as IntoIterator>::IntoIter: 'a,
459 {
460 let mut graph = Self::new();
461 let indices = graph.add_clips(clips, 1.0, graph.root).collect();
462 (graph, indices)
463 }
464
465 /// Adds an [`AnimationClip`] to the animation graph with the given weight
466 /// and returns its index.
467 ///
468 /// The animation clip will be the child of the given parent. The resulting
469 /// node will have no mask.
470 pub fn add_clip(
471 &mut self,
472 clip: Handle<AnimationClip>,
473 weight: f32,
474 parent: AnimationNodeIndex,
475 ) -> AnimationNodeIndex {
476 let node_index = self.graph.add_node(AnimationGraphNode {
477 node_type: AnimationNodeType::Clip(clip),
478 mask: 0,
479 weight,
480 });
481 self.graph.add_edge(parent, node_index, ());
482 node_index
483 }
484
485 /// Adds an [`AnimationClip`] to the animation graph with the given weight
486 /// and mask, and returns its index.
487 ///
488 /// The animation clip will be the child of the given parent.
489 pub fn add_clip_with_mask(
490 &mut self,
491 clip: Handle<AnimationClip>,
492 mask: AnimationMask,
493 weight: f32,
494 parent: AnimationNodeIndex,
495 ) -> AnimationNodeIndex {
496 let node_index = self.graph.add_node(AnimationGraphNode {
497 node_type: AnimationNodeType::Clip(clip),
498 mask,
499 weight,
500 });
501 self.graph.add_edge(parent, node_index, ());
502 node_index
503 }
504
505 /// A convenience method to add multiple [`AnimationClip`]s to the animation
506 /// graph.
507 ///
508 /// All of the animation clips will have the same weight and will be
509 /// parented to the same node.
510 ///
511 /// Returns the indices of the new nodes.
512 pub fn add_clips<'a, I>(
513 &'a mut self,
514 clips: I,
515 weight: f32,
516 parent: AnimationNodeIndex,
517 ) -> impl Iterator<Item = AnimationNodeIndex> + 'a
518 where
519 I: IntoIterator<Item = Handle<AnimationClip>>,
520 <I as IntoIterator>::IntoIter: 'a,
521 {
522 clips
523 .into_iter()
524 .map(move |clip| self.add_clip(clip, weight, parent))
525 }
526
527 /// Adds a blend node to the animation graph with the given weight and
528 /// returns its index.
529 ///
530 /// The blend node will be placed under the supplied `parent` node. During
531 /// animation evaluation, the descendants of this blend node will have their
532 /// weights multiplied by the weight of the blend. The blend node will have
533 /// no mask.
534 pub fn add_blend(&mut self, weight: f32, parent: AnimationNodeIndex) -> AnimationNodeIndex {
535 let node_index = self.graph.add_node(AnimationGraphNode {
536 node_type: AnimationNodeType::Blend,
537 mask: 0,
538 weight,
539 });
540 self.graph.add_edge(parent, node_index, ());
541 node_index
542 }
543
544 /// Adds a blend node to the animation graph with the given weight and
545 /// returns its index.
546 ///
547 /// The blend node will be placed under the supplied `parent` node. During
548 /// animation evaluation, the descendants of this blend node will have their
549 /// weights multiplied by the weight of the blend. Neither this node nor its
550 /// descendants will affect animation targets that belong to mask groups not
551 /// in the given `mask`.
552 pub fn add_blend_with_mask(
553 &mut self,
554 mask: AnimationMask,
555 weight: f32,
556 parent: AnimationNodeIndex,
557 ) -> AnimationNodeIndex {
558 let node_index = self.graph.add_node(AnimationGraphNode {
559 node_type: AnimationNodeType::Blend,
560 mask,
561 weight,
562 });
563 self.graph.add_edge(parent, node_index, ());
564 node_index
565 }
566
567 /// Adds a blend node to the animation graph with the given weight and
568 /// returns its index.
569 ///
570 /// The blend node will be placed under the supplied `parent` node. During
571 /// animation evaluation, the descendants of this blend node will have their
572 /// weights multiplied by the weight of the blend. The blend node will have
573 /// no mask.
574 pub fn add_additive_blend(
575 &mut self,
576 weight: f32,
577 parent: AnimationNodeIndex,
578 ) -> AnimationNodeIndex {
579 let node_index = self.graph.add_node(AnimationGraphNode {
580 node_type: AnimationNodeType::Add,
581 mask: 0,
582 weight,
583 });
584 self.graph.add_edge(parent, node_index, ());
585 node_index
586 }
587
588 /// Adds a blend node to the animation graph with the given weight and
589 /// returns its index.
590 ///
591 /// The blend node will be placed under the supplied `parent` node. During
592 /// animation evaluation, the descendants of this blend node will have their
593 /// weights multiplied by the weight of the blend. Neither this node nor its
594 /// descendants will affect animation targets that belong to mask groups not
595 /// in the given `mask`.
596 pub fn add_additive_blend_with_mask(
597 &mut self,
598 mask: AnimationMask,
599 weight: f32,
600 parent: AnimationNodeIndex,
601 ) -> AnimationNodeIndex {
602 let node_index = self.graph.add_node(AnimationGraphNode {
603 node_type: AnimationNodeType::Add,
604 mask,
605 weight,
606 });
607 self.graph.add_edge(parent, node_index, ());
608 node_index
609 }
610
611 /// Adds an edge from the edge `from` to `to`, making `to` a child of
612 /// `from`.
613 ///
614 /// The behavior is unspecified if adding this produces a cycle in the
615 /// graph.
616 pub fn add_edge(&mut self, from: NodeIndex, to: NodeIndex) {
617 self.graph.add_edge(from, to, ());
618 }
619
620 /// Removes an edge between `from` and `to` if it exists.
621 ///
622 /// Returns true if the edge was successfully removed or false if no such
623 /// edge existed.
624 pub fn remove_edge(&mut self, from: NodeIndex, to: NodeIndex) -> bool {
625 self.graph
626 .find_edge(from, to)
627 .map(|edge| self.graph.remove_edge(edge))
628 .is_some()
629 }
630
631 /// Returns the [`AnimationGraphNode`] associated with the given index.
632 ///
633 /// If no node with the given index exists, returns `None`.
634 pub fn get(&self, animation: AnimationNodeIndex) -> Option<&AnimationGraphNode> {
635 self.graph.node_weight(animation)
636 }
637
638 /// Returns a mutable reference to the [`AnimationGraphNode`] associated
639 /// with the given index.
640 ///
641 /// If no node with the given index exists, returns `None`.
642 pub fn get_mut(&mut self, animation: AnimationNodeIndex) -> Option<&mut AnimationGraphNode> {
643 self.graph.node_weight_mut(animation)
644 }
645
646 /// Returns an iterator over the [`AnimationGraphNode`]s in this graph.
647 pub fn nodes(&self) -> impl Iterator<Item = AnimationNodeIndex> {
648 self.graph.node_indices()
649 }
650
651 /// Serializes the animation graph to the given [`Write`]r in RON format.
652 ///
653 /// If writing to a file, it can later be loaded with the
654 /// [`AnimationGraphAssetLoader`] to reconstruct the graph.
655 pub fn save<W>(&self, writer: &mut W) -> Result<(), AnimationGraphSaveError>
656 where
657 W: Write,
658 {
659 let mut ron_serializer = ron::ser::Serializer::new(writer, None)?;
660 let serialized_graph: SerializedAnimationGraph = self.clone().try_into()?;
661 Ok(serialized_graph.serialize(&mut ron_serializer)?)
662 }
663
664 /// Adds an animation target (bone) to the mask group with the given ID.
665 ///
666 /// Calling this method multiple times with the same animation target but
667 /// different mask groups will result in that target being added to all of
668 /// the specified groups.
669 pub fn add_target_to_mask_group(&mut self, target: AnimationTargetId, mask_group: u32) {
670 *self.mask_groups.entry(target).or_default() |= 1 << mask_group;
671 }
672}
673
674impl AnimationGraphNode {
675 /// Masks out the mask groups specified by the given `mask` bitfield.
676 ///
677 /// A 1 in bit position N causes this function to mask out mask group N, and
678 /// thus neither this node nor its descendants will animate any animation
679 /// targets that belong to group N.
680 pub fn add_mask(&mut self, mask: AnimationMask) -> &mut Self {
681 self.mask |= mask;
682 self
683 }
684
685 /// Unmasks the mask groups specified by the given `mask` bitfield.
686 ///
687 /// A 1 in bit position N causes this function to unmask mask group N, and
688 /// thus this node and its descendants will be allowed to animate animation
689 /// targets that belong to group N, unless another mask masks those targets
690 /// out.
691 pub fn remove_mask(&mut self, mask: AnimationMask) -> &mut Self {
692 self.mask &= !mask;
693 self
694 }
695
696 /// Masks out the single mask group specified by `group`.
697 ///
698 /// After calling this function, neither this node nor its descendants will
699 /// animate any animation targets that belong to the given `group`.
700 pub fn add_mask_group(&mut self, group: u32) -> &mut Self {
701 self.add_mask(1 << group)
702 }
703
704 /// Unmasks the single mask group specified by `group`.
705 ///
706 /// After calling this function, this node and its descendants will be
707 /// allowed to animate animation targets that belong to the given `group`,
708 /// unless another mask masks those targets out.
709 pub fn remove_mask_group(&mut self, group: u32) -> &mut Self {
710 self.remove_mask(1 << group)
711 }
712}
713
714impl Index<AnimationNodeIndex> for AnimationGraph {
715 type Output = AnimationGraphNode;
716
717 fn index(&self, index: AnimationNodeIndex) -> &Self::Output {
718 &self.graph[index]
719 }
720}
721
722impl IndexMut<AnimationNodeIndex> for AnimationGraph {
723 fn index_mut(&mut self, index: AnimationNodeIndex) -> &mut Self::Output {
724 &mut self.graph[index]
725 }
726}
727
728impl Default for AnimationGraphNode {
729 fn default() -> Self {
730 Self {
731 node_type: Default::default(),
732 mask: 0,
733 weight: 1.0,
734 }
735 }
736}
737
738impl Default for AnimationGraph {
739 fn default() -> Self {
740 Self::new()
741 }
742}
743
744impl AssetLoader for AnimationGraphAssetLoader {
745 type Asset = AnimationGraph;
746
747 type Settings = ();
748
749 type Error = AnimationGraphLoadError;
750
751 async fn load(
752 &self,
753 reader: &mut dyn Reader,
754 _: &Self::Settings,
755 load_context: &mut LoadContext<'_>,
756 ) -> Result<Self::Asset, Self::Error> {
757 let mut bytes = Vec::new();
758 reader.read_to_end(&mut bytes).await?;
759
760 // Deserialize a `SerializedAnimationGraph` directly, so that we can
761 // get the list of the animation clips it refers to and load them.
762 let mut deserializer = ron::de::Deserializer::from_bytes(&bytes)?;
763 let serialized_animation_graph = SerializedAnimationGraph::deserialize(&mut deserializer)
764 .map_err(|err| deserializer.span_error(err))?;
765
766 // Load all `AssetPath`s to convert from a `SerializedAnimationGraph` to a real
767 // `AnimationGraph`. This is effectively a `DiGraph::map`, but this allows us to return
768 // errors.
769 let mut animation_graph = DiGraph::with_capacity(
770 serialized_animation_graph.graph.node_count(),
771 serialized_animation_graph.graph.edge_count(),
772 );
773
774 for serialized_node in serialized_animation_graph.graph.node_weights() {
775 animation_graph.add_node(AnimationGraphNode {
776 node_type: match serialized_node.node_type {
777 SerializedAnimationNodeType::Clip(ref path) => {
778 AnimationNodeType::Clip(load_context.load(path.clone()))
779 }
780 SerializedAnimationNodeType::Blend => AnimationNodeType::Blend,
781 SerializedAnimationNodeType::Add => AnimationNodeType::Add,
782 },
783 mask: serialized_node.mask,
784 weight: serialized_node.weight,
785 });
786 }
787 for edge in serialized_animation_graph.graph.raw_edges() {
788 animation_graph.add_edge(edge.source(), edge.target(), ());
789 }
790 Ok(AnimationGraph {
791 graph: animation_graph,
792 root: serialized_animation_graph.root,
793 mask_groups: serialized_animation_graph.mask_groups,
794 })
795 }
796
797 fn extensions(&self) -> &[&str] {
798 &["animgraph", "animgraph.ron"]
799 }
800}
801
802impl TryFrom<AnimationGraph> for SerializedAnimationGraph {
803 type Error = NonPathHandleError;
804
805 fn try_from(animation_graph: AnimationGraph) -> Result<Self, NonPathHandleError> {
806 // Convert all the `Handle<AnimationClip>` to AssetPath, so that
807 // `AnimationGraphAssetLoader` can load them. This is effectively just doing a
808 // `DiGraph::map`, except we need to return an error if any handles aren't associated to a
809 // path.
810 let mut serialized_graph = DiGraph::with_capacity(
811 animation_graph.graph.node_count(),
812 animation_graph.graph.edge_count(),
813 );
814 for node in animation_graph.graph.node_weights() {
815 serialized_graph.add_node(SerializedAnimationGraphNode {
816 weight: node.weight,
817 mask: node.mask,
818 node_type: match node.node_type {
819 AnimationNodeType::Clip(ref clip) => match clip.path() {
820 Some(path) => SerializedAnimationNodeType::Clip(path.clone()),
821 None => return Err(NonPathHandleError),
822 },
823 AnimationNodeType::Blend => SerializedAnimationNodeType::Blend,
824 AnimationNodeType::Add => SerializedAnimationNodeType::Add,
825 },
826 });
827 }
828 for edge in animation_graph.graph.raw_edges() {
829 serialized_graph.add_edge(edge.source(), edge.target(), ());
830 }
831 Ok(Self {
832 graph: serialized_graph,
833 root: animation_graph.root,
834 mask_groups: animation_graph.mask_groups,
835 })
836 }
837}
838
839/// Error for when only path [`Handle`]s are supported.
840#[derive(Error, Debug)]
841#[error("AnimationGraph contains a handle to an AnimationClip that does not correspond to an asset path")]
842pub struct NonPathHandleError;
843
844/// A system that creates, updates, and removes [`ThreadedAnimationGraph`]
845/// structures for every changed [`AnimationGraph`].
846///
847/// The [`ThreadedAnimationGraph`] contains acceleration structures that allow
848/// for quick evaluation of that graph's animations.
849pub(crate) fn thread_animation_graphs(
850 mut threaded_animation_graphs: ResMut<ThreadedAnimationGraphs>,
851 animation_graphs: Res<Assets<AnimationGraph>>,
852 mut animation_graph_asset_events: MessageReader<AssetEvent<AnimationGraph>>,
853) {
854 for animation_graph_asset_event in animation_graph_asset_events.read() {
855 match *animation_graph_asset_event {
856 AssetEvent::Added { id }
857 | AssetEvent::Modified { id }
858 | AssetEvent::LoadedWithDependencies { id } => {
859 // Fetch the animation graph.
860 let Some(animation_graph) = animation_graphs.get(id) else {
861 continue;
862 };
863
864 // Reuse the allocation if possible.
865 let mut threaded_animation_graph =
866 threaded_animation_graphs.0.remove(&id).unwrap_or_default();
867 threaded_animation_graph.clear();
868
869 // Recursively thread the graph in postorder.
870 threaded_animation_graph.init(animation_graph);
871 threaded_animation_graph.build_from(
872 &animation_graph.graph,
873 animation_graph.root,
874 0,
875 );
876
877 // Write in the threaded graph.
878 threaded_animation_graphs
879 .0
880 .insert(id, threaded_animation_graph);
881 }
882
883 AssetEvent::Removed { id } => {
884 threaded_animation_graphs.0.remove(&id);
885 }
886 AssetEvent::Unused { .. } => {}
887 }
888 }
889}
890
891impl ThreadedAnimationGraph {
892 /// Removes all the data in this [`ThreadedAnimationGraph`], keeping the
893 /// memory around for later reuse.
894 fn clear(&mut self) {
895 self.threaded_graph.clear();
896 self.sorted_edge_ranges.clear();
897 self.sorted_edges.clear();
898 }
899
900 /// Prepares the [`ThreadedAnimationGraph`] for recursion.
901 fn init(&mut self, animation_graph: &AnimationGraph) {
902 let node_count = animation_graph.graph.node_count();
903 let edge_count = animation_graph.graph.edge_count();
904
905 self.threaded_graph.reserve(node_count);
906 self.sorted_edges.reserve(edge_count);
907
908 self.sorted_edge_ranges.clear();
909 self.sorted_edge_ranges
910 .extend(iter::repeat_n(0..0, node_count));
911
912 self.computed_masks.clear();
913 self.computed_masks.extend(iter::repeat_n(0, node_count));
914 }
915
916 /// Recursively constructs the [`ThreadedAnimationGraph`] for the subtree
917 /// rooted at the given node.
918 ///
919 /// `mask` specifies the computed mask of the parent node. (It could be
920 /// fetched from the [`Self::computed_masks`] field, but we pass it
921 /// explicitly as a micro-optimization.)
922 fn build_from(
923 &mut self,
924 graph: &AnimationDiGraph,
925 node_index: AnimationNodeIndex,
926 mut mask: u64,
927 ) {
928 // Accumulate the mask.
929 mask |= graph.node_weight(node_index).unwrap().mask;
930 self.computed_masks[node_index.index()] = mask;
931
932 // Gather up the indices of our children, and sort them.
933 let mut kids: SmallVec<[AnimationNodeIndex; 8]> = graph
934 .neighbors_directed(node_index, Direction::Outgoing)
935 .collect();
936 kids.sort_unstable();
937
938 // Write in the list of kids.
939 self.sorted_edge_ranges[node_index.index()] =
940 (self.sorted_edges.len() as u32)..((self.sorted_edges.len() + kids.len()) as u32);
941 self.sorted_edges.extend_from_slice(&kids);
942
943 // Recurse. (This is a postorder traversal.)
944 for kid in kids.into_iter().rev() {
945 self.build_from(graph, kid, mask);
946 }
947
948 // Finally, push our index.
949 self.threaded_graph.push(node_index);
950 }
951}