Skip to content
This repository has been archived by the owner on Apr 18, 2022. It is now read-only.

Commit

Permalink
feat: Add rate control to animation system
Browse files Browse the repository at this point in the history
  • Loading branch information
Rhuagh committed Feb 10, 2018
1 parent 2f1af6e commit d51bd2d
Show file tree
Hide file tree
Showing 8 changed files with 129 additions and 88 deletions.
1 change: 1 addition & 0 deletions amethyst_animation/Cargo.toml
Expand Up @@ -22,6 +22,7 @@ amethyst_core = { path = "../amethyst_core/", version = "0.1.0" }
amethyst_renderer = { path = "../amethyst_renderer/", version = "0.6.1" }
fnv = "1"
hibitset = "0.3.2"
log = "0.4"
minterpolate = { version = "0.2", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
shred = "0.5"
Expand Down
5 changes: 4 additions & 1 deletion amethyst_animation/src/lib.rs
Expand Up @@ -3,6 +3,8 @@ extern crate amethyst_core;
extern crate amethyst_renderer;
extern crate fnv;
extern crate hibitset;
#[macro_use]
extern crate log;
extern crate minterpolate;
#[macro_use]
extern crate serde;
Expand All @@ -17,7 +19,8 @@ pub use self::skinning::{Joint, Skin, VertexSkinningSystem};
pub use self::systems::{AnimationControlSystem, AnimationProcessor, SamplerInterpolationSystem,
SamplerProcessor};
pub use self::transform::TransformChannel;
pub use self::util::{pause_animation, play_animation, toggle_animation, SamplerPrimitive};
pub use self::util::{pause_animation, play_animation, set_animation_rate, toggle_animation,
SamplerPrimitive};
pub use minterpolate::{InterpolationFunction, InterpolationPrimitive};

mod skinning;
Expand Down
64 changes: 62 additions & 2 deletions amethyst_animation/src/resources.rs
Expand Up @@ -194,8 +194,8 @@ where
pub end: EndControl,
/// What the transform should return to after end
pub after: T::Primitive,
// Control the rate of animation, default is 1.0
// pub rate_multiplier: f32, //TODO
/// Control the rate of animation, default is 1.0
pub rate_multiplier: f32,
}

/// Sampler control set, containing a set of sampler controllers for a single component.
Expand All @@ -211,6 +211,62 @@ where
pub samplers: FnvHashMap<T::Channel, SamplerControl<T>>,
}

impl<T> SamplerControlSet<T>
where
T: AnimationSampling,
{
/// Set channel control
pub fn set_channel(&mut self, channel: T::Channel, control: SamplerControl<T>) {
self.samplers.insert(channel, control);
}

/// Abort control set
pub fn abort(&mut self) {
for sampler in self.samplers
.values_mut()
.filter(|t| t.state != ControlState::Done)
{
sampler.state = ControlState::Abort;
}
}

/// Pause control set
pub fn pause(&mut self) {
for sampler in self.samplers.values_mut() {
sampler.state = match sampler.state {
ControlState::Running(dur) => ControlState::Paused(dur),
_ => ControlState::Paused(Duration::from_secs(0)),
}
}
}

/// Unpause control set
pub fn unpause(&mut self) {
for sampler in self.samplers.values_mut() {
if let ControlState::Paused(dur) = sampler.state {
sampler.state = ControlState::Running(dur);
}
}
}

/// Update rate multiplier
pub fn set_rate_multiplier(&mut self, rate_multiplier: f32)
where
T: AnimationSampling,
{
for sampler in self.samplers.values_mut() {
sampler.rate_multiplier = rate_multiplier;
}
}

/// Check if a control set can be terminated
pub fn check_termination(&self) -> bool {
self.samplers
.values()
.all(|t| t.state == ControlState::Done || t.state == ControlState::Requested)
}
}

impl<T> Component for SamplerControlSet<T>
where
T: AnimationSampling,
Expand Down Expand Up @@ -243,6 +299,8 @@ where
pub state: ControlState,
/// Animation command
pub command: AnimationCommand,
/// Control the rate of animation, default is 1.0
pub rate_multiplier: f32,
m: marker::PhantomData<T>,
}

Expand All @@ -255,12 +313,14 @@ where
end: EndControl,
state: ControlState,
command: AnimationCommand,
rate_multiplier: f32,
) -> Self {
AnimationControl {
animation,
end,
state,
command,
rate_multiplier,
m: marker::PhantomData,
}
}
Expand Down
103 changes: 25 additions & 78 deletions amethyst_animation/src/systems/control.rs
Expand Up @@ -132,7 +132,7 @@ where
None => if only_one_index(&animation.nodes) {
&h_fallback
} else {
eprintln!(
error!(
"Animation control which target multiple nodes without a hierarchy detected, dropping"
);
remove.push(*entity);
Expand Down Expand Up @@ -189,6 +189,8 @@ where
samplers.remove(*node_entity);
}
remove.push(*entity);
} else {
update_animation_rate(hierarchy, samplers, control.rate_multiplier);
}
None
}
Expand Down Expand Up @@ -244,10 +246,11 @@ where
state: ControlState::Requested,
sampler: sampler_handle.clone(),
end: control.end.clone(),
after: get_after(channel, component),
after: component.current_sample(channel),
rate_multiplier: control.rate_multiplier,
};
let add = if let Some(ref mut set) = samplers.get_mut(*node_entity) {
add_to_set(channel, set, sampler_control);
set.set_channel(channel.clone(), sampler_control);
None
} else {
Some(sampler_control)
Expand All @@ -256,54 +259,49 @@ where
let mut set = SamplerControlSet {
samplers: FnvHashMap::default(),
};
add_to_set(channel, &mut set, sampler_control);
set.set_channel(channel.clone(), sampler_control);
samplers.insert(*node_entity, set);
}
}
true
}

fn add_to_set<T>(
channel: &T::Channel,
control_set: &mut SamplerControlSet<T>,
control: SamplerControl<T>,
fn pause_animation<T>(
hierarchy: &AnimationHierarchy<T>,
samplers: &mut WriteStorage<SamplerControlSet<T>>,
) where
T: AnimationSampling,
{
control_set.samplers.insert(channel.clone(), control);
}

fn get_after<T>(channel: &T::Channel, component: &T) -> T::Primitive
where
T: AnimationSampling,
{
component.current_sample(channel)
for (_, node_entity) in &hierarchy.nodes {
if let Some(ref mut s) = samplers.get_mut(*node_entity) {
s.pause();
}
}
}

fn pause_animation<T>(
fn unpause_animation<T>(
hierarchy: &AnimationHierarchy<T>,
samplers: &mut WriteStorage<SamplerControlSet<T>>,
) where
T: AnimationSampling,
{
for (_, node_entity) in &hierarchy.nodes {
match samplers.get_mut(*node_entity) {
Some(ref mut s) => do_control_set_pause(s),
_ => (),
if let Some(ref mut s) = samplers.get_mut(*node_entity) {
s.unpause();
}
}
}

fn unpause_animation<T>(
fn update_animation_rate<T>(
hierarchy: &AnimationHierarchy<T>,
samplers: &mut WriteStorage<SamplerControlSet<T>>,
rate_multiplier: f32,
) where
T: AnimationSampling,
{
for (_, node_entity) in &hierarchy.nodes {
match samplers.get_mut(*node_entity) {
Some(ref mut s) => do_control_set_unpause(s),
_ => (),
if let Some(ref mut s) = samplers.get_mut(*node_entity) {
s.set_rate_multiplier(rate_multiplier);
}
}
}
Expand All @@ -327,9 +325,8 @@ where
} else {
// Request termination of samplers
for (_, node_entity) in &hierarchy.nodes {
match samplers.get_mut(*node_entity) {
Some(ref mut s) => do_control_set_termination(s),
_ => (),
if let Some(ref mut s) = samplers.get_mut(*node_entity) {
s.abort();
}
}
false
Expand All @@ -348,55 +345,5 @@ where
.nodes
.iter()
.flat_map(|(_, node_entity)| samplers.get(*node_entity))
.all(check_control_set_termination)
}

/// Request termination of `SamplerControlSet`
fn do_control_set_termination<T>(control_set: &mut SamplerControlSet<T>)
where
T: AnimationSampling,
{
for sampler in control_set
.samplers
.values_mut()
.filter(|t| t.state != ControlState::Done)
{
sampler.state = ControlState::Abort;
}
}

/// Pause a `SamplerControlSet`
fn do_control_set_pause<T>(control_set: &mut SamplerControlSet<T>)
where
T: AnimationSampling,
{
for sampler in control_set.samplers.values_mut() {
sampler.state = match sampler.state {
ControlState::Running(dur) => ControlState::Paused(dur),
_ => ControlState::Paused(Duration::from_secs(0)),
}
}
}

/// Unpause a `SamplerControlSet`
fn do_control_set_unpause<T>(control_set: &mut SamplerControlSet<T>)
where
T: AnimationSampling,
{
for sampler in control_set.samplers.values_mut() {
if let ControlState::Paused(dur) = sampler.state {
sampler.state = ControlState::Running(dur);
}
}
}

/// Check if a control set can be terminated
fn check_control_set_termination<T>(control_set: &SamplerControlSet<T>) -> bool
where
T: AnimationSampling,
{
control_set
.samplers
.values()
.all(|t| t.state == ControlState::Done || t.state == ControlState::Requested)
.all(SamplerControlSet::check_termination)
}
11 changes: 5 additions & 6 deletions amethyst_animation/src/systems/sampling.rs
Expand Up @@ -72,7 +72,7 @@ fn process_sampler<T>(

// If a new end condition has been computed, update in control state
if let Some(end) = new_end {
control.end = end.clone();
control.end = end;
}

// Do sampling
Expand Down Expand Up @@ -121,7 +121,8 @@ where
// sampling is running, update duration and check end condition
Running(duration) => {
let zero = Duration::from_secs(0);
let current_dur = duration + time.delta_time();
let current_dur =
duration + secs_to_duration(time.delta_seconds() * control.rate_multiplier);
let last_frame = sampler
.input
.last()
Expand Down Expand Up @@ -176,10 +177,8 @@ fn do_end_control<T>(control: &SamplerControl<T>, component: &mut T)
where
T: AnimationSampling,
{
match control.end {
EndControl::Normal => component.apply_sample(&control.channel, &control.after),
// looping is handled during duration update
_ => {}
if let EndControl::Normal = control.end {
component.apply_sample(&control.channel, &control.after);
}
}

Expand Down
31 changes: 30 additions & 1 deletion amethyst_animation/src/util.rs
Expand Up @@ -17,11 +17,13 @@ use resources::{Animation, AnimationCommand, AnimationControl, AnimationSampling
/// matches the `Animation`, or only refer to a single node, else the animation will
/// not be run.
/// - `end`: action to perform when the animation has reached its end.
/// - `rate_multiplier`: animation rate to set
pub fn play_animation<T>(
controls: &mut WriteStorage<AnimationControl<T>>,
animation: &Handle<Animation<T>>,
entity: Entity,
end: EndControl,
rate_multiplier: f32,
) where
T: AnimationSampling,
{
Expand All @@ -39,6 +41,7 @@ pub fn play_animation<T>(
end,
ControlState::Requested,
AnimationCommand::Start,
rate_multiplier,
),
);
}
Expand Down Expand Up @@ -77,11 +80,13 @@ pub fn pause_animation<T>(
/// matches the `Animation`, or only refer to a single node, else the animation will
/// not be run.
/// - `end`: action to perform when the animation has reached its end.
/// - `rate_multiplier`: animation rate to set
pub fn toggle_animation<T>(
controls: &mut WriteStorage<AnimationControl<T>>,
animation: &Handle<Animation<T>>,
entity: Entity,
end: EndControl,
rate_multiplier: f32,
) where
T: AnimationSampling,
{
Expand All @@ -92,7 +97,31 @@ pub fn toggle_animation<T>(
{
pause_animation(controls, animation, entity);
} else {
play_animation(controls, animation, entity, end);
play_animation(controls, animation, entity, end, rate_multiplier);
}
}

/// Set animation rate
///
/// ## Parameters:
///
/// - `controls`: animation control storage in the world.
/// - `animation`: handle to the animation
/// - `entity`: entity the animation is running on.
/// - `rate_multiplier`: animation rate to set
pub fn set_animation_rate<T>(
controls: &mut WriteStorage<AnimationControl<T>>,
animation: &Handle<Animation<T>>,
entity: Entity,
rate_multiplier: f32,
) where
T: AnimationSampling,
{
match controls.get_mut(entity) {
Some(ref mut control) if control.animation == *animation => {
control.rate_multiplier = rate_multiplier;
}
_ => {}
}
}

Expand Down

0 comments on commit d51bd2d

Please sign in to comment.