use std::{marker, time::Duration};
use itertools::Itertools;
use minterpolate::InterpolationPrimitive;
use amethyst_assets::AssetStorage;
use amethyst_core::{
duration_to_nanos, duration_to_secs,
ecs::prelude::{Component, Join, Read, System, WriteStorage},
nanos_to_duration, secs_to_duration, Time,
};
use crate::resources::{
AnimationSampling, ApplyData, BlendMethod, ControlState, EndControl, Sampler, SamplerControl,
SamplerControlSet,
};
#[cfg(feature = "profiler")]
use thread_profiler::profile_scope;
#[derive(Default, Debug)]
pub struct SamplerInterpolationSystem<T>
where
T: AnimationSampling,
{
m: marker::PhantomData<T>,
inner: Vec<(f32, T::Channel, T::Primitive)>,
channels: Vec<T::Channel>,
}
impl<T> SamplerInterpolationSystem<T>
where
T: AnimationSampling,
{
pub fn new() -> Self {
Self {
m: marker::PhantomData,
inner: Vec::default(),
channels: Vec::default(),
}
}
}
impl<'a, T> System<'a> for SamplerInterpolationSystem<T>
where
T: AnimationSampling + Component,
{
type SystemData = (
Read<'a, Time>,
Read<'a, AssetStorage<Sampler<T::Primitive>>>,
WriteStorage<'a, SamplerControlSet<T>>,
WriteStorage<'a, T>,
<T as ApplyData<'a>>::ApplyData,
);
fn run(&mut self, (time, samplers, mut control_sets, mut comps, apply_data): Self::SystemData) {
#[cfg(feature = "profiler")]
profile_scope!("sampler_interpolation_system");
for (control_set, comp) in (&mut control_sets, &mut comps).join() {
self.inner.clear();
for control in control_set.samplers.iter_mut() {
if let Some(ref sampler) = samplers.get(&control.sampler) {
process_sampler(control, sampler, &time, &mut self.inner);
}
}
if !self.inner.is_empty() {
self.channels.clear();
self.channels
.extend(self.inner.iter().map(|o| &o.1).unique().cloned());
for channel in &self.channels {
match comp.blend_method(channel) {
None => {
if let Some(p) = self
.inner
.iter()
.filter(|p| p.1 == *channel)
.map(|p| p.2.clone())
.last()
{
comp.apply_sample(channel, &p, &apply_data);
}
}
Some(BlendMethod::Linear) => {
if let Some(p) = linear_blend::<T>(channel, &self.inner) {
comp.apply_sample(channel, &p, &apply_data);
}
}
}
}
}
}
}
}
fn process_sampler<T>(
control: &mut SamplerControl<T>,
sampler: &Sampler<T::Primitive>,
time: &Time,
output: &mut Vec<(f32, T::Channel, T::Primitive)>,
) where
T: AnimationSampling,
{
use crate::resources::ControlState::*;
let (new_state, new_end) = update_duration_and_check(&control, sampler, time);
if let Some(end) = new_end {
control.end = end;
}
match new_state {
Running(duration) | Paused(duration) => {
output.push((
control.blend_weight,
control.channel.clone(),
sampler.function.interpolate(
duration_to_secs(duration),
&sampler.input,
&sampler.output,
false,
),
));
}
Done => {
if let EndControl::Normal = control.end {
output.push((
control.blend_weight,
control.channel.clone(),
control.after.clone(),
));
}
if let EndControl::Stay = control.end {
let last_frame = sampler.input.last().cloned().unwrap_or(0.);
output.push((
control.blend_weight,
control.channel.clone(),
sampler.function.interpolate(
last_frame,
&sampler.input,
&sampler.output,
false,
),
));
}
}
_ => {}
}
control.state = new_state;
}
fn update_duration_and_check<T>(
control: &SamplerControl<T>,
sampler: &Sampler<T::Primitive>,
time: &Time,
) -> (ControlState, Option<EndControl>)
where
T: AnimationSampling,
{
use crate::resources::ControlState::*;
match control.state {
Requested => (Running(Duration::from_secs(0)), None),
Deferred(dur) => (Running(dur), None),
Abort => (Done, None),
Running(duration) => {
let current_dur =
duration + secs_to_duration(time.delta_seconds() * control.rate_multiplier);
let last_frame = sampler
.input
.last()
.cloned()
.map(secs_to_duration)
.unwrap_or(Duration::from_secs(0));
if current_dur > last_frame {
match control.end {
EndControl::Loop(Some(i)) if i <= 1 => (Done, Some(EndControl::Normal)),
EndControl::Loop(None) => {
(Running(next_duration(last_frame, current_dur).0), None)
}
EndControl::Loop(Some(i)) => {
let (next_dur, loops_removed) = next_duration(last_frame, current_dur);
let remaining_loops = i - loops_removed;
if remaining_loops <= 1 {
(Done, Some(EndControl::Normal))
} else {
(
Running(next_dur),
Some(EndControl::Loop(Some(remaining_loops))),
)
}
}
_ => (Done, None),
}
} else {
(Running(current_dur), None)
}
}
ref state => (state.clone(), None),
}
}
fn next_duration(last_frame: Duration, duration: Duration) -> (Duration, u32) {
let animation_duration = duration_to_nanos(last_frame);
let current_duration = duration_to_nanos(duration);
let remain_duration = current_duration % animation_duration;
let loops = current_duration / animation_duration;
(nanos_to_duration(remain_duration), loops as u32)
}
fn linear_blend<T>(
channel: &T::Channel,
output: &[(f32, T::Channel, T::Primitive)],
) -> Option<T::Primitive>
where
T: AnimationSampling,
{
let total_blend_weight = output.iter().filter(|o| o.1 == *channel).map(|o| o.0).sum();
if total_blend_weight == 0. {
None
} else {
Some(
output
.iter()
.filter(|o| o.1 == *channel)
.map(|o| single_blend::<T>(total_blend_weight, o))
.fold(T::default_primitive(channel), |acc, p| acc.add(&p)),
)
}
}
fn single_blend<T>(
total: f32,
&(ref weight, _, ref primitive): &(f32, T::Channel, T::Primitive),
) -> T::Primitive
where
T: AnimationSampling,
{
primitive.mul(*weight / total)
}