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