use crate::{
animation::{
machine::{EvaluatePose, Parameter, ParameterContainer, PoseNode, PoseWeight},
AnimationContainer, AnimationPose,
},
core::{
pool::{Handle, Pool},
visitor::{Visit, VisitResult, Visitor},
},
};
use std::cell::{Cell, Ref, RefCell};
#[derive(Default)]
pub struct BlendPose {
weight: PoseWeight,
pose_source: Handle<PoseNode>,
}
impl BlendPose {
pub fn new(weight: PoseWeight, pose_source: Handle<PoseNode>) -> Self {
Self {
weight,
pose_source,
}
}
pub fn with_constant_weight(weight: f32, pose_source: Handle<PoseNode>) -> Self {
Self {
weight: PoseWeight::Constant(weight),
pose_source,
}
}
pub fn with_param_weight(param_id: &str, pose_source: Handle<PoseNode>) -> Self {
Self {
weight: PoseWeight::Parameter(param_id.to_owned()),
pose_source,
}
}
}
impl Visit for BlendPose {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.weight.visit("Weight", visitor)?;
self.pose_source.visit("PoseSource", visitor)?;
visitor.leave_region()
}
}
#[derive(Default)]
pub struct BlendAnimations {
pose_sources: Vec<BlendPose>,
output_pose: RefCell<AnimationPose>,
}
impl BlendAnimations {
pub fn new(poses: Vec<BlendPose>) -> Self {
Self {
pose_sources: poses,
output_pose: Default::default(),
}
}
}
impl Visit for BlendAnimations {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.pose_sources.visit("PoseSources", visitor)?;
visitor.leave_region()
}
}
impl EvaluatePose for BlendAnimations {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
params: &ParameterContainer,
animations: &AnimationContainer,
dt: f32,
) -> Ref<AnimationPose> {
self.output_pose.borrow_mut().reset();
for blend_pose in self.pose_sources.iter() {
let weight = match blend_pose.weight {
PoseWeight::Constant(value) => value,
PoseWeight::Parameter(ref param_id) => {
if let Some(Parameter::Weight(weight)) = params.get(param_id) {
*weight
} else {
0.0
}
}
};
let pose_source =
nodes[blend_pose.pose_source].eval_pose(nodes, params, animations, dt);
self.output_pose
.borrow_mut()
.blend_with(&pose_source, weight);
}
self.output_pose.borrow()
}
}
#[derive(Default)]
pub struct IndexedBlendInput {
pub blend_time: f32,
pub pose_source: Handle<PoseNode>,
}
impl Visit for IndexedBlendInput {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.blend_time.visit("BlendTime", visitor)?;
self.pose_source.visit("PoseSource", visitor)?;
visitor.leave_region()
}
}
#[derive(Default)]
pub struct BlendAnimationsByIndex {
index_parameter: String,
inputs: Vec<IndexedBlendInput>,
output_pose: RefCell<AnimationPose>,
prev_index: Cell<Option<u32>>,
blend_time: Cell<f32>,
}
impl BlendAnimationsByIndex {
pub fn new(index_parameter: String, inputs: Vec<IndexedBlendInput>) -> Self {
Self {
index_parameter,
inputs,
output_pose: RefCell::new(Default::default()),
prev_index: Cell::new(None),
blend_time: Cell::new(0.0),
}
}
}
impl Visit for BlendAnimationsByIndex {
fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
visitor.enter_region(name)?;
self.index_parameter.visit("IndexParameter", visitor)?;
self.inputs.visit("Inputs", visitor)?;
self.prev_index.visit("PrevIndex", visitor)?;
self.blend_time.visit("BlendTime", visitor)?;
visitor.leave_region()
}
}
impl EvaluatePose for BlendAnimationsByIndex {
fn eval_pose(
&self,
nodes: &Pool<PoseNode>,
params: &ParameterContainer,
animations: &AnimationContainer,
dt: f32,
) -> Ref<AnimationPose> {
self.output_pose.borrow_mut().reset();
if let Some(&Parameter::Index(current_index)) = params.get(&self.index_parameter) {
let mut applied = false;
if let Some(prev_index) = self.prev_index.get() {
if prev_index != current_index {
let prev_input = &self.inputs[prev_index as usize];
let current_input = &self.inputs[current_index as usize];
self.blend_time
.set((self.blend_time.get() + dt).min(current_input.blend_time));
let interpolator = self.blend_time.get() / current_input.blend_time;
self.output_pose.borrow_mut().blend_with(
&nodes[prev_input.pose_source].eval_pose(nodes, params, animations, dt),
1.0 - interpolator,
);
self.output_pose.borrow_mut().blend_with(
&nodes[current_input.pose_source].eval_pose(nodes, params, animations, dt),
interpolator,
);
if interpolator >= 1.0 {
self.prev_index.set(Some(current_index));
self.blend_time.set(0.0);
}
applied = true;
}
} else {
self.prev_index.set(Some(current_index));
}
if !applied {
self.blend_time.set(0.0);
nodes[self.inputs[current_index as usize].pose_source]
.eval_pose(nodes, params, animations, dt)
.clone_into(&mut *self.output_pose.borrow_mut());
}
}
self.output_pose.borrow()
}
}