Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions assets/animation_graphs/Fox.animgraph.ron
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,27 @@
graph: (
nodes: [
(
clip: None,
node_type: Blend,
mask: 0,
weight: 1.0,
),
(
clip: None,
node_type: Blend,
mask: 0,
weight: 0.5,
weight: 1.0,
),
(
clip: Some(AssetPath("models/animated/Fox.glb#Animation0")),
node_type: Clip(AssetPath("models/animated/Fox.glb#Animation0")),
mask: 0,
weight: 1.0,
),
(
clip: Some(AssetPath("models/animated/Fox.glb#Animation1")),
node_type: Clip(AssetPath("models/animated/Fox.glb#Animation1")),
mask: 0,
weight: 1.0,
),
(
clip: Some(AssetPath("models/animated/Fox.glb#Animation2")),
node_type: Clip(AssetPath("models/animated/Fox.glb#Animation2")),
mask: 0,
weight: 1.0,
),
Expand Down
111 changes: 94 additions & 17 deletions crates/bevy_animation/src/animation_curves.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ use bevy_render::mesh::morph::MorphWeights;
use bevy_transform::prelude::Transform;

use crate::{
graph::AnimationNodeIndex, prelude::Animatable, AnimationEntityMut, AnimationEvaluationError,
graph::AnimationNodeIndex,
prelude::{Animatable, BlendInput},
AnimationEntityMut, AnimationEvaluationError,
};

/// A value on a component that Bevy can animate.
Expand Down Expand Up @@ -297,7 +299,11 @@ where
P: AnimatableProperty,
{
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.blend(graph_node)
self.evaluator.combine(graph_node, /*additive=*/ false)
}

fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, /*additive=*/ true)
}

fn push_blend_register(
Expand Down Expand Up @@ -393,7 +399,11 @@ where

impl AnimationCurveEvaluator for TranslationCurveEvaluator {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.blend(graph_node)
self.evaluator.combine(graph_node, /*additive=*/ false)
}

fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, /*additive=*/ true)
}

fn push_blend_register(
Expand Down Expand Up @@ -487,7 +497,11 @@ where

impl AnimationCurveEvaluator for RotationCurveEvaluator {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.blend(graph_node)
self.evaluator.combine(graph_node, /*additive=*/ false)
}

fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, /*additive=*/ true)
}

fn push_blend_register(
Expand Down Expand Up @@ -581,7 +595,11 @@ where

impl AnimationCurveEvaluator for ScaleCurveEvaluator {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.blend(graph_node)
self.evaluator.combine(graph_node, /*additive=*/ false)
}

fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.evaluator.combine(graph_node, /*additive=*/ true)
}

fn push_blend_register(
Expand Down Expand Up @@ -708,8 +726,12 @@ where
}
}

impl AnimationCurveEvaluator for WeightsCurveEvaluator {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
impl WeightsCurveEvaluator {
fn combine(
&mut self,
graph_node: AnimationNodeIndex,
additive: bool,
) -> Result<(), AnimationEvaluationError> {
let Some(&(_, top_graph_node)) = self.stack_blend_weights_and_graph_nodes.last() else {
return Ok(());
};
Expand All @@ -736,13 +758,27 @@ impl AnimationCurveEvaluator for WeightsCurveEvaluator {
.iter_mut()
.zip(stack_iter)
{
*dest = f32::interpolate(dest, &src, weight_to_blend / *current_weight);
if additive {
*dest += src * weight_to_blend;
} else {
*dest = f32::interpolate(dest, &src, weight_to_blend / *current_weight);
}
}
}
}

Ok(())
}
}

impl AnimationCurveEvaluator for WeightsCurveEvaluator {
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.combine(graph_node, /*additive=*/ false)
}

fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
self.combine(graph_node, /*additive=*/ true)
}

fn push_blend_register(
&mut self,
Expand Down Expand Up @@ -826,7 +862,11 @@ impl<A> BasicAnimationCurveEvaluator<A>
where
A: Animatable,
{
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError> {
fn combine(
&mut self,
graph_node: AnimationNodeIndex,
additive: bool,
) -> Result<(), AnimationEvaluationError> {
let Some(top) = self.stack.last() else {
return Ok(());
};
Expand All @@ -840,15 +880,36 @@ where
graph_node: _,
} = self.stack.pop().unwrap();

match self.blend_register {
match self.blend_register.take() {
None => self.blend_register = Some((value_to_blend, weight_to_blend)),
Some((ref mut current_value, ref mut current_weight)) => {
*current_weight += weight_to_blend;
*current_value = A::interpolate(
current_value,
&value_to_blend,
weight_to_blend / *current_weight,
);
Some((mut current_value, mut current_weight)) => {
current_weight += weight_to_blend;

if additive {
current_value = A::blend(
[
BlendInput {
weight: 1.0,
value: current_value,
additive: true,
},
BlendInput {
weight: weight_to_blend,
value: value_to_blend,
additive: true,
},
]
.into_iter(),
);
} else {
current_value = A::interpolate(
&current_value,
&value_to_blend,
weight_to_blend / current_weight,
);
}

self.blend_register = Some((current_value, current_weight));
}
}

Expand Down Expand Up @@ -967,6 +1028,22 @@ pub trait AnimationCurveEvaluator: Reflect {
/// 4. Return success.
fn blend(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;

/// Additively blends the top element of the stack with the blend register.
///
/// The semantics of this method are as follows:
///
/// 1. Pop the top element of the stack. Call its value vₘ and its weight
/// wₘ. If the stack was empty, return success.
///
/// 2. If the blend register is empty, set the blend register value to vₘ
/// and the blend register weight to wₘ; then, return success.
///
/// 3. If the blend register is nonempty, call its current value vₙ.
/// Then, set the value of the blend register to vₙ + vₘwₘ.
///
/// 4. Return success.
fn add(&mut self, graph_node: AnimationNodeIndex) -> Result<(), AnimationEvaluationError>;

/// Pushes the current value of the blend register onto the stack.
///
/// If the blend register is empty, this method does nothing successfully.
Expand Down
Loading