Vendor dependencies for 0.3.0 release

This commit is contained in:
2025-09-27 10:29:08 -05:00
parent 0c8d39d483
commit 82ab7f317b
26803 changed files with 16134934 additions and 0 deletions

272
vendor/bevy_animation/src/animatable.rs vendored Normal file
View File

@@ -0,0 +1,272 @@
//! Traits and type for interpolating between values.
use crate::util;
use bevy_color::{Laba, LinearRgba, Oklaba, Srgba, Xyza};
use bevy_math::*;
use bevy_reflect::Reflect;
use bevy_transform::prelude::Transform;
/// An individual input for [`Animatable::blend`].
pub struct BlendInput<T> {
/// The individual item's weight. This may not be bound to the range `[0.0, 1.0]`.
pub weight: f32,
/// The input value to be blended.
pub value: T,
/// Whether or not to additively blend this input into the final result.
pub additive: bool,
}
/// An animatable value type.
pub trait Animatable: Reflect + Sized + Send + Sync + 'static {
/// Interpolates between `a` and `b` with an interpolation factor of `time`.
///
/// The `time` parameter here may not be clamped to the range `[0.0, 1.0]`.
fn interpolate(a: &Self, b: &Self, time: f32) -> Self;
/// Blends one or more values together.
///
/// Implementors should return a default value when no inputs are provided here.
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self;
}
macro_rules! impl_float_animatable {
($ty: ty, $base: ty) => {
impl Animatable for $ty {
#[inline]
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
let t = <$base>::from(t);
(*a) * (1.0 - t) + (*b) * t
}
#[inline]
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut value = Default::default();
for input in inputs {
if input.additive {
value += <$base>::from(input.weight) * input.value;
} else {
value = Self::interpolate(&value, &input.value, input.weight);
}
}
value
}
}
};
}
macro_rules! impl_color_animatable {
($ty: ident) => {
impl Animatable for $ty {
#[inline]
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
let value = *a * (1. - t) + *b * t;
value
}
#[inline]
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut value = Default::default();
for input in inputs {
if input.additive {
value += input.weight * input.value;
} else {
value = Self::interpolate(&value, &input.value, input.weight);
}
}
value
}
}
};
}
impl_float_animatable!(f32, f32);
impl_float_animatable!(Vec2, f32);
impl_float_animatable!(Vec3A, f32);
impl_float_animatable!(Vec4, f32);
impl_float_animatable!(f64, f64);
impl_float_animatable!(DVec2, f64);
impl_float_animatable!(DVec3, f64);
impl_float_animatable!(DVec4, f64);
impl_color_animatable!(LinearRgba);
impl_color_animatable!(Laba);
impl_color_animatable!(Oklaba);
impl_color_animatable!(Srgba);
impl_color_animatable!(Xyza);
// Vec3 is special cased to use Vec3A internally for blending
impl Animatable for Vec3 {
#[inline]
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
(*a) * (1.0 - t) + (*b) * t
}
#[inline]
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut value = Vec3A::ZERO;
for input in inputs {
if input.additive {
value += input.weight * Vec3A::from(input.value);
} else {
value = Vec3A::interpolate(&value, &Vec3A::from(input.value), input.weight);
}
}
Self::from(value)
}
}
impl Animatable for bool {
#[inline]
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
util::step_unclamped(*a, *b, t)
}
#[inline]
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
inputs
.max_by_key(|x| FloatOrd(x.weight))
.is_some_and(|input| input.value)
}
}
impl Animatable for Transform {
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
Self {
translation: Vec3::interpolate(&a.translation, &b.translation, t),
rotation: Quat::interpolate(&a.rotation, &b.rotation, t),
scale: Vec3::interpolate(&a.scale, &b.scale, t),
}
}
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut translation = Vec3A::ZERO;
let mut scale = Vec3A::ZERO;
let mut rotation = Quat::IDENTITY;
for input in inputs {
if input.additive {
translation += input.weight * Vec3A::from(input.value.translation);
scale += input.weight * Vec3A::from(input.value.scale);
rotation =
Quat::slerp(Quat::IDENTITY, input.value.rotation, input.weight) * rotation;
} else {
translation = Vec3A::interpolate(
&translation,
&Vec3A::from(input.value.translation),
input.weight,
);
scale = Vec3A::interpolate(&scale, &Vec3A::from(input.value.scale), input.weight);
rotation = Quat::interpolate(&rotation, &input.value.rotation, input.weight);
}
}
Self {
translation: Vec3::from(translation),
rotation,
scale: Vec3::from(scale),
}
}
}
impl Animatable for Quat {
/// Performs a slerp to smoothly interpolate between quaternions.
#[inline]
fn interpolate(a: &Self, b: &Self, t: f32) -> Self {
// We want to smoothly interpolate between the two quaternions by default,
// rather than using a quicker but less correct linear interpolation.
a.slerp(*b, t)
}
#[inline]
fn blend(inputs: impl Iterator<Item = BlendInput<Self>>) -> Self {
let mut value = Self::IDENTITY;
for BlendInput {
weight,
value: incoming_value,
additive,
} in inputs
{
if additive {
value = Self::slerp(Self::IDENTITY, incoming_value, weight) * value;
} else {
value = Self::interpolate(&value, &incoming_value, weight);
}
}
value
}
}
/// Evaluates a cubic Bézier curve at a value `t`, given two endpoints and the
/// derivatives at those endpoints.
///
/// The derivatives are linearly scaled by `duration`.
pub fn interpolate_with_cubic_bezier<T>(p0: &T, d0: &T, d3: &T, p3: &T, t: f32, duration: f32) -> T
where
T: Animatable + Clone,
{
// We're given two endpoints, along with the derivatives at those endpoints,
// and have to evaluate the cubic Bézier curve at time t using only
// (additive) blending and linear interpolation.
//
// Evaluating a Bézier curve via repeated linear interpolation when the
// control points are known is straightforward via [de Casteljau
// subdivision]. So the only remaining problem is to get the two off-curve
// control points. The [derivative of the cubic Bézier curve] is:
//
// B(t) = 3(1 - t)²(P₁ - P₀) + 6(1 - t)t(P₂ - P₁) + 3t²(P₃ - P₂)
//
// Setting t = 0 and t = 1 and solving gives us:
//
// P₁ = P₀ + B(0) / 3
// P₂ = P₃ - B(1) / 3
//
// These P₁ and P₂ formulas can be expressed as additive blends.
//
// So, to sum up, first we calculate the off-curve control points via
// additive blending, and then we use repeated linear interpolation to
// evaluate the curve.
//
// [de Casteljau subdivision]: https://en.wikipedia.org/wiki/De_Casteljau%27s_algorithm
// [derivative of the cubic Bézier curve]: https://en.wikipedia.org/wiki/B%C3%A9zier_curve#Cubic_B%C3%A9zier_curves
// Compute control points from derivatives.
let p1 = T::blend(
[
BlendInput {
weight: duration / 3.0,
value: (*d0).clone(),
additive: true,
},
BlendInput {
weight: 1.0,
value: (*p0).clone(),
additive: true,
},
]
.into_iter(),
);
let p2 = T::blend(
[
BlendInput {
weight: duration / -3.0,
value: (*d3).clone(),
additive: true,
},
BlendInput {
weight: 1.0,
value: (*p3).clone(),
additive: true,
},
]
.into_iter(),
);
// Use de Casteljau subdivision to evaluate.
let p0p1 = T::interpolate(p0, &p1, t);
let p1p2 = T::interpolate(&p1, &p2, t);
let p2p3 = T::interpolate(&p2, p3, t);
let p0p1p2 = T::interpolate(&p0p1, &p1p2, t);
let p1p2p3 = T::interpolate(&p1p2, &p2p3, t);
T::interpolate(&p0p1p2, &p1p2p3, t)
}

File diff suppressed because it is too large Load Diff

435
vendor/bevy_animation/src/gltf_curves.rs vendored Normal file
View File

@@ -0,0 +1,435 @@
//! Concrete curve structures used to load glTF curves into the animation system.
use bevy_math::{
curve::{cores::*, iterable::IterableCurve, *},
vec4, Quat, Vec4, VectorSpace,
};
use bevy_reflect::Reflect;
use either::Either;
use thiserror::Error;
/// A keyframe-defined curve that "interpolates" by stepping at `t = 1.0` to the next keyframe.
#[derive(Debug, Clone, Reflect)]
pub struct SteppedKeyframeCurve<T> {
core: UnevenCore<T>,
}
impl<T> Curve<T> for SteppedKeyframeCurve<T>
where
T: Clone,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> T {
self.core
.sample_with(t, |x, y, t| if t >= 1.0 { y.clone() } else { x.clone() })
}
#[inline]
fn sample_unchecked(&self, t: f32) -> T {
self.sample_clamped(t)
}
}
impl<T> SteppedKeyframeCurve<T> {
/// Create a new [`SteppedKeyframeCurve`]. If the curve could not be constructed from the
/// given data, an error is returned.
#[inline]
pub fn new(timed_samples: impl IntoIterator<Item = (f32, T)>) -> Result<Self, UnevenCoreError> {
Ok(Self {
core: UnevenCore::new(timed_samples)?,
})
}
}
/// A keyframe-defined curve that uses cubic spline interpolation, backed by a contiguous buffer.
#[derive(Debug, Clone, Reflect)]
pub struct CubicKeyframeCurve<T> {
// Note: the sample width here should be 3.
core: ChunkedUnevenCore<T>,
}
impl<V> Curve<V> for CubicKeyframeCurve<V>
where
V: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> V {
match self.core.sample_interp_timed(t) {
// In all the cases where only one frame matters, defer to the position within it.
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => v[1],
InterpolationDatum::Between((t0, u), (t1, v), s) => {
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
}
}
}
#[inline]
fn sample_unchecked(&self, t: f32) -> V {
self.sample_clamped(t)
}
}
impl<T> CubicKeyframeCurve<T> {
/// Create a new [`CubicKeyframeCurve`] from keyframe `times` and their associated `values`.
/// Because 3 values are needed to perform cubic interpolation, `values` must have triple the
/// length of `times` — each consecutive triple `a_k, v_k, b_k` associated to time `t_k`
/// consists of:
/// - The in-tangent `a_k` for the sample at time `t_k`
/// - The actual value `v_k` for the sample at time `t_k`
/// - The out-tangent `b_k` for the sample at time `t_k`
///
/// For example, for a curve built from two keyframes, the inputs would have the following form:
/// - `times`: `[t_0, t_1]`
/// - `values`: `[a_0, v_0, b_0, a_1, v_1, b_1]`
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, ChunkedUnevenCoreError> {
Ok(Self {
core: ChunkedUnevenCore::new(times, values, 3)?,
})
}
}
// NOTE: We can probably delete `CubicRotationCurve` once we improve the `Reflect` implementations
// for the `Curve` API adaptors; this is basically a `CubicKeyframeCurve` composed with `map`.
/// A keyframe-defined curve that uses cubic spline interpolation, special-cased for quaternions
/// since it uses `Vec4` internally.
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub struct CubicRotationCurve {
// Note: The sample width here should be 3.
core: ChunkedUnevenCore<Vec4>,
}
impl Curve<Quat> for CubicRotationCurve {
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_clamped(&self, t: f32) -> Quat {
let vec = match self.core.sample_interp_timed(t) {
// In all the cases where only one frame matters, defer to the position within it.
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => v[1],
InterpolationDatum::Between((t0, u), (t1, v), s) => {
cubic_spline_interpolation(u[1], u[2], v[0], v[1], s, t1 - t0)
}
};
Quat::from_vec4(vec.normalize())
}
#[inline]
fn sample_unchecked(&self, t: f32) -> Quat {
self.sample_clamped(t)
}
}
impl CubicRotationCurve {
/// Create a new [`CubicRotationCurve`] from keyframe `times` and their associated `values`.
/// Because 3 values are needed to perform cubic interpolation, `values` must have triple the
/// length of `times` — each consecutive triple `a_k, v_k, b_k` associated to time `t_k`
/// consists of:
/// - The in-tangent `a_k` for the sample at time `t_k`
/// - The actual value `v_k` for the sample at time `t_k`
/// - The out-tangent `b_k` for the sample at time `t_k`
///
/// For example, for a curve built from two keyframes, the inputs would have the following form:
/// - `times`: `[t_0, t_1]`
/// - `values`: `[a_0, v_0, b_0, a_1, v_1, b_1]`
///
/// To sample quaternions from this curve, the resulting interpolated `Vec4` output is normalized
/// and interpreted as a quaternion.
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = Vec4>,
) -> Result<Self, ChunkedUnevenCoreError> {
Ok(Self {
core: ChunkedUnevenCore::new(times, values, 3)?,
})
}
}
/// A keyframe-defined curve that uses linear interpolation over many samples at once, backed
/// by a contiguous buffer.
#[derive(Debug, Clone, Reflect)]
pub struct WideLinearKeyframeCurve<T> {
// Here the sample width is the number of things to simultaneously interpolate.
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideLinearKeyframeCurve<T>
where
T: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) {
InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v)
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().copied()),
InterpolationDatum::Between(u, v, s) => {
let interpolated = u.iter().zip(v.iter()).map(move |(x, y)| x.lerp(*y, s));
Either::Right(interpolated)
}
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
impl<T> WideLinearKeyframeCurve<T> {
/// Create a new [`WideLinearKeyframeCurve`]. An error will be returned if:
/// - `values` has length zero.
/// - `times` has less than `2` unique valid entries.
/// - The length of `values` is not divisible by that of `times` (once sorted, filtered,
/// and deduplicated).
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
/// A keyframe-defined curve that uses stepped "interpolation" over many samples at once, backed
/// by a contiguous buffer.
#[derive(Debug, Clone, Reflect)]
pub struct WideSteppedKeyframeCurve<T> {
// Here the sample width is the number of things to simultaneously interpolate.
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideSteppedKeyframeCurve<T>
where
T: Clone,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
#[inline]
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp(t) {
InterpolationDatum::Exact(v)
| InterpolationDatum::LeftTail(v)
| InterpolationDatum::RightTail(v) => Either::Left(v.iter().cloned()),
InterpolationDatum::Between(u, v, s) => {
let interpolated =
u.iter()
.zip(v.iter())
.map(move |(x, y)| if s >= 1.0 { y.clone() } else { x.clone() });
Either::Right(interpolated)
}
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
impl<T> WideSteppedKeyframeCurve<T> {
/// Create a new [`WideSteppedKeyframeCurve`]. An error will be returned if:
/// - `values` has length zero.
/// - `times` has less than `2` unique valid entries.
/// - The length of `values` is not divisible by that of `times` (once sorted, filtered,
/// and deduplicated).
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
/// A keyframe-defined curve that uses cubic interpolation over many samples at once, backed by a
/// contiguous buffer.
#[derive(Debug, Clone, Reflect)]
pub struct WideCubicKeyframeCurve<T> {
core: ChunkedUnevenCore<T>,
}
impl<T> IterableCurve<T> for WideCubicKeyframeCurve<T>
where
T: VectorSpace,
{
#[inline]
fn domain(&self) -> Interval {
self.core.domain()
}
fn sample_iter_clamped(&self, t: f32) -> impl Iterator<Item = T> {
match self.core.sample_interp_timed(t) {
InterpolationDatum::Exact((_, v))
| InterpolationDatum::LeftTail((_, v))
| InterpolationDatum::RightTail((_, v)) => {
// Pick out the part of this that actually represents the position (instead of tangents),
// which is the middle third.
let width = self.core.width();
Either::Left(v[width..(width * 2)].iter().copied())
}
InterpolationDatum::Between((t0, u), (t1, v), s) => Either::Right(
cubic_spline_interpolate_slices(self.core.width() / 3, u, v, s, t1 - t0),
),
}
}
#[inline]
fn sample_iter_unchecked(&self, t: f32) -> impl Iterator<Item = T> {
self.sample_iter_clamped(t)
}
}
/// An error indicating that a multisampling keyframe curve could not be constructed.
#[derive(Debug, Error)]
#[error("unable to construct a curve using this data")]
pub enum WideKeyframeCurveError {
/// The number of given values was not divisible by a multiple of the number of keyframes.
#[error("number of values ({values_given}) is not divisible by {divisor}")]
LengthMismatch {
/// The number of values given.
values_given: usize,
/// The number that `values_given` was supposed to be divisible by.
divisor: usize,
},
/// An error was returned by the internal core constructor.
#[error(transparent)]
CoreError(#[from] ChunkedUnevenCoreError),
}
impl<T> WideCubicKeyframeCurve<T> {
/// Create a new [`WideCubicKeyframeCurve`].
///
/// An error will be returned if:
/// - `values` has length zero.
/// - `times` has less than `2` unique valid entries.
/// - The length of `values` is not divisible by three times that of `times` (once sorted,
/// filtered, and deduplicated).
#[inline]
pub fn new(
times: impl IntoIterator<Item = f32>,
values: impl IntoIterator<Item = T>,
) -> Result<Self, WideKeyframeCurveError> {
let times: Vec<f32> = times.into_iter().collect();
let values: Vec<T> = values.into_iter().collect();
let divisor = times.len() * 3;
if values.len() % divisor != 0 {
return Err(WideKeyframeCurveError::LengthMismatch {
values_given: values.len(),
divisor,
});
}
Ok(Self {
core: ChunkedUnevenCore::new_width_inferred(times, values)?,
})
}
}
/// A curve specifying the [`MorphWeights`] for a mesh in animation. The variants are broken
/// down by interpolation mode (with the exception of `Constant`, which never interpolates).
///
/// This type is, itself, a `Curve<Vec<f32>>`; however, in order to avoid allocation, it is
/// recommended to use its implementation of the [`IterableCurve`] trait, which allows iterating
/// directly over information derived from the curve without allocating.
///
/// [`MorphWeights`]: bevy_mesh::morph::MorphWeights
#[derive(Debug, Clone, Reflect)]
#[reflect(Clone)]
pub enum WeightsCurve {
/// A curve which takes a constant value over its domain. Notably, this is how animations with
/// only a single keyframe are interpreted.
Constant(ConstantCurve<Vec<f32>>),
/// A curve which interpolates weights linearly between keyframes.
Linear(WideLinearKeyframeCurve<f32>),
/// A curve which interpolates weights between keyframes in steps.
Step(WideSteppedKeyframeCurve<f32>),
/// A curve which interpolates between keyframes by using auxiliary tangent data to join
/// adjacent keyframes with a cubic Hermite spline, which is then sampled.
CubicSpline(WideCubicKeyframeCurve<f32>),
}
//---------//
// HELPERS //
//---------//
/// Helper function for cubic spline interpolation.
fn cubic_spline_interpolation<T>(
value_start: T,
tangent_out_start: T,
tangent_in_end: T,
value_end: T,
lerp: f32,
step_duration: f32,
) -> T
where
T: VectorSpace,
{
let coeffs = (vec4(2.0, 1.0, -2.0, 1.0) * lerp + vec4(-3.0, -2.0, 3.0, -1.0)) * lerp;
value_start * (coeffs.x * lerp + 1.0)
+ tangent_out_start * step_duration * lerp * (coeffs.y + 1.0)
+ value_end * lerp * coeffs.z
+ tangent_in_end * step_duration * lerp * coeffs.w
}
fn cubic_spline_interpolate_slices<'a, T: VectorSpace>(
width: usize,
first: &'a [T],
second: &'a [T],
s: f32,
step_between: f32,
) -> impl Iterator<Item = T> + 'a {
(0..width).map(move |idx| {
cubic_spline_interpolation(
first[idx + width],
first[idx + (width * 2)],
second[idx + width],
second[idx],
s,
step_between,
)
})
}

930
vendor/bevy_animation/src/graph.rs vendored Normal file
View File

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

1678
vendor/bevy_animation/src/lib.rs vendored Normal file

File diff suppressed because it is too large Load Diff

160
vendor/bevy_animation/src/transition.rs vendored Normal file
View File

@@ -0,0 +1,160 @@
//! Animation transitions.
//!
//! Please note that this is an unstable temporary API. It may be replaced by a
//! state machine in the future.
use bevy_ecs::{
component::Component,
reflect::ReflectComponent,
system::{Query, Res},
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_time::Time;
use core::time::Duration;
use crate::{graph::AnimationNodeIndex, ActiveAnimation, AnimationPlayer};
/// Manages fade-out of animation blend factors, allowing for smooth transitions
/// between animations.
///
/// To use this component, place it on the same entity as the
/// [`AnimationPlayer`] and [`AnimationGraphHandle`](crate::AnimationGraphHandle). It'll take
/// responsibility for adjusting the weight on the [`ActiveAnimation`] in order
/// to fade out animations smoothly.
///
/// When using an [`AnimationTransitions`] component, you should play all
/// animations through the [`AnimationTransitions::play`] method, rather than by
/// directly manipulating the [`AnimationPlayer`]. Playing animations through
/// the [`AnimationPlayer`] directly will cause the [`AnimationTransitions`]
/// component to get confused about which animation is the "main" animation, and
/// transitions will usually be incorrect as a result.
#[derive(Component, Default, Reflect)]
#[reflect(Component, Default, Clone)]
pub struct AnimationTransitions {
main_animation: Option<AnimationNodeIndex>,
transitions: Vec<AnimationTransition>,
}
// This is needed since `#[derive(Clone)]` does not generate optimized `clone_from`.
impl Clone for AnimationTransitions {
fn clone(&self) -> Self {
Self {
main_animation: self.main_animation,
transitions: self.transitions.clone(),
}
}
fn clone_from(&mut self, source: &Self) {
self.main_animation = source.main_animation;
self.transitions.clone_from(&source.transitions);
}
}
/// An animation that is being faded out as part of a transition
#[derive(Debug, Clone, Copy, Reflect)]
#[reflect(Clone)]
pub struct AnimationTransition {
/// The current weight. Starts at 1.0 and goes to 0.0 during the fade-out.
current_weight: f32,
/// How much to decrease `current_weight` per second
weight_decline_per_sec: f32,
/// The animation that is being faded out
animation: AnimationNodeIndex,
}
impl AnimationTransitions {
/// Creates a new [`AnimationTransitions`] component, ready to be added to
/// an entity with an [`AnimationPlayer`].
pub fn new() -> AnimationTransitions {
AnimationTransitions::default()
}
/// Plays a new animation on the given [`AnimationPlayer`], fading out any
/// existing animations that were already playing over the
/// `transition_duration`.
///
/// Pass [`Duration::ZERO`] to instantly switch to a new animation, avoiding
/// any transition.
pub fn play<'p>(
&mut self,
player: &'p mut AnimationPlayer,
new_animation: AnimationNodeIndex,
transition_duration: Duration,
) -> &'p mut ActiveAnimation {
if let Some(old_animation_index) = self.main_animation.replace(new_animation) {
if let Some(old_animation) = player.animation_mut(old_animation_index) {
if !old_animation.is_paused() {
self.transitions.push(AnimationTransition {
current_weight: old_animation.weight,
weight_decline_per_sec: 1.0 / transition_duration.as_secs_f32(),
animation: old_animation_index,
});
}
}
}
// If already transitioning away from this animation, cancel the transition.
// Otherwise the transition ending would incorrectly stop the new animation.
self.transitions
.retain(|transition| transition.animation != new_animation);
player.start(new_animation)
}
/// Obtain the currently playing main animation.
pub fn get_main_animation(&self) -> Option<AnimationNodeIndex> {
self.main_animation
}
}
/// A system that alters the weight of currently-playing transitions based on
/// the current time and decline amount.
pub fn advance_transitions(
mut query: Query<(&mut AnimationTransitions, &mut AnimationPlayer)>,
time: Res<Time>,
) {
// We use a "greedy layer" system here. The top layer (most recent
// transition) gets as much as weight as it wants, and the remaining amount
// is divided between all the other layers, eventually culminating in the
// currently-playing animation receiving whatever's left. This results in a
// nicely normalized weight.
for (mut animation_transitions, mut player) in query.iter_mut() {
let mut remaining_weight = 1.0;
for transition in &mut animation_transitions.transitions.iter_mut().rev() {
// Decrease weight.
transition.current_weight = (transition.current_weight
- transition.weight_decline_per_sec * time.delta_secs())
.max(0.0);
// Update weight.
let Some(ref mut animation) = player.animation_mut(transition.animation) else {
continue;
};
animation.weight = transition.current_weight * remaining_weight;
remaining_weight -= animation.weight;
}
if let Some(main_animation_index) = animation_transitions.main_animation {
if let Some(ref mut animation) = player.animation_mut(main_animation_index) {
animation.weight = remaining_weight;
}
}
}
}
/// A system that removed transitions that have completed from the
/// [`AnimationTransitions`] object.
pub fn expire_completed_transitions(
mut query: Query<(&mut AnimationTransitions, &mut AnimationPlayer)>,
) {
for (mut animation_transitions, mut player) in query.iter_mut() {
animation_transitions.transitions.retain(|transition| {
let expire = transition.current_weight <= 0.0;
if expire {
player.stop(transition.animation);
}
!expire
});
}
}

10
vendor/bevy_animation/src/util.rs vendored Normal file
View File

@@ -0,0 +1,10 @@
/// Steps between two different discrete values of any type.
/// Returns `a` if `t < 1.0`, otherwise returns `b`.
#[inline]
pub(crate) fn step_unclamped<T>(a: T, b: T, t: f32) -> T {
if t < 1.0 {
a
} else {
b
}
}