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

Commit

Permalink
feat: Add stepping and exact input value control to animation system
Browse files Browse the repository at this point in the history
  • Loading branch information
Rhuagh committed Feb 12, 2018
1 parent 1920143 commit 6fc5ca1
Show file tree
Hide file tree
Showing 5 changed files with 201 additions and 20 deletions.
6 changes: 3 additions & 3 deletions amethyst_animation/src/lib.rs
Expand Up @@ -14,13 +14,13 @@ extern crate specs;
pub use self::bundle::{AnimationBundle, SamplingBundle, VertexSkinningBundle};
pub use self::resources::{Animation, AnimationCommand, AnimationControl, AnimationHierarchy,
AnimationSampling, AnimationSet, ControlState, EndControl, Sampler,
SamplerControl, SamplerControlSet};
SamplerControl, SamplerControlSet, StepDirection};
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, set_animation_rate, toggle_animation,
SamplerPrimitive};
pub use self::util::{pause_animation, play_animation, set_animation_input, set_animation_rate,
step_animation, toggle_animation, SamplerPrimitive};
pub use minterpolate::{InterpolationFunction, InterpolationPrimitive};

mod skinning;
Expand Down
68 changes: 66 additions & 2 deletions amethyst_animation/src/resources.rs
Expand Up @@ -2,9 +2,10 @@ use std::hash::Hash;
use std::marker;
use std::time::Duration;

use amethyst_assets::{Asset, Handle, Result};
use amethyst_assets::{Asset, AssetStorage, Handle, Result};
use amethyst_core::timing::{duration_to_secs, secs_to_duration};
use fnv::FnvHashMap;
use minterpolate::{InterpolationFunction, InterpolationPrimitive};
use minterpolate::{get_input_index, InterpolationFunction, InterpolationPrimitive};
use specs::{Component, DenseVecStorage, Entity, VecStorage};

/// Master trait used to define animation sampling on a component
Expand Down Expand Up @@ -257,12 +258,62 @@ where
.for_each(|sampler| sampler.rate_multiplier = rate_multiplier);
}

/// Forcible set the input value (point of interpolation)
pub fn set_input(&mut self, input: f32)
where
T: AnimationSampling,
{
let dur = secs_to_duration(input);
self.samplers.values_mut().for_each(|sampler| {
if let ControlState::Running(_) = sampler.state {
sampler.state = ControlState::Running(dur);
}
});
}

/// 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)
}

/// Step animation
pub fn step(
&mut self,
samplers: &AssetStorage<Sampler<T::Primitive>>,
direction: &StepDirection,
) {
self.samplers
.values_mut()
.filter(|t| t.state != ControlState::Done)
.map(|c| (samplers.get(&c.sampler).unwrap(), c))
.for_each(|(s, c)| {
set_step_state(c, s, direction);
});
}
}

fn set_step_state<T>(
control: &mut SamplerControl<T>,
sampler: &Sampler<T::Primitive>,
direction: &StepDirection,
) where
T: AnimationSampling,
{
if let ControlState::Running(dur) = control.state {
let dur_s = duration_to_secs(dur);
let new_index = match (get_input_index(dur_s, &sampler.input), direction) {
(Some(index), &StepDirection::Forward) if index >= sampler.input.len() - 1 => {
sampler.input.len() - 1
}
(Some(index), &StepDirection::Forward) => index + 1,
(Some(0), &StepDirection::Backward) => 0,
(Some(index), &StepDirection::Backward) => index - 1,
(None, _) => 0,
};
control.state = ControlState::Running(secs_to_duration(sampler.input[new_index]));
}
}

impl<T> Component for SamplerControlSet<T>
Expand All @@ -272,11 +323,24 @@ where
type Storage = DenseVecStorage<Self>;
}

/// Used when doing animation stepping (i.e only move forward/backward to discrete input values)
#[derive(Clone, Debug)]
pub enum StepDirection {
/// Take a step forward
Forward,
/// Take a step backward
Backward,
}

/// Animation command
#[derive(Clone, Debug)]
pub enum AnimationCommand {
/// Start the animation, or unpause if it's paused
Start,
/// Step the animation forward/backward (move to the next/previous input value in sequence)
Step(StepDirection),
/// Forcible set current interpolation point for the animation, value in seconds
SetInputValue(f32),
/// Pause the animation
Pause,
/// Abort the animation, will cause the control object to be removed from the world
Expand Down
48 changes: 47 additions & 1 deletion amethyst_animation/src/systems/control.rs
Expand Up @@ -7,7 +7,8 @@ use minterpolate::InterpolationPrimitive;
use specs::{Component, Entities, Entity, Fetch, Join, ReadStorage, System, WriteStorage};

use resources::{Animation, AnimationCommand, AnimationControl, AnimationHierarchy,
AnimationSampling, ControlState, Sampler, SamplerControl, SamplerControlSet};
AnimationSampling, ControlState, Sampler, SamplerControl, SamplerControlSet,
StepDirection};

/// System for setting up animations, should run before `SamplerInterpolationSystem`.
///
Expand Down Expand Up @@ -69,6 +70,12 @@ where
}) {
control.state = state;
}
if let AnimationCommand::Step(_) = control.command {
control.command = AnimationCommand::Start;
}
if let AnimationCommand::SetInputValue(_) = control.command {
control.command = AnimationCommand::Start;
}
}

for entity in remove {
Expand Down Expand Up @@ -180,6 +187,16 @@ where
Some(ControlState::Running(Duration::from_secs(0)))
}

(&ControlState::Running(..), &AnimationCommand::Step(ref dir)) => {
step_animation(hierarchy, samplers, sampler_storage, dir);
None
}

(&ControlState::Running(..), &AnimationCommand::SetInputValue(value)) => {
set_animation_input(hierarchy, samplers, value);
None
}

// check for finished/aborted animations, wait for samplers to signal done,
// then remove control objects
(&ControlState::Running(..), _) => {
Expand Down Expand Up @@ -292,6 +309,35 @@ fn unpause_animation<T>(
}
}

fn step_animation<T>(
hierarchy: &AnimationHierarchy<T>,
controls: &mut WriteStorage<SamplerControlSet<T>>,
sampler_storage: &AssetStorage<Sampler<T::Primitive>>,
direction: &StepDirection,
) where
T: AnimationSampling,
{
for (_, node_entity) in &hierarchy.nodes {
if let Some(ref mut s) = controls.get_mut(*node_entity) {
s.step(sampler_storage, direction);
}
}
}

fn set_animation_input<T>(
hierarchy: &AnimationHierarchy<T>,
controls: &mut WriteStorage<SamplerControlSet<T>>,
input: f32,
) where
T: AnimationSampling,
{
for (_, node_entity) in &hierarchy.nodes {
if let Some(ref mut s) = controls.get_mut(*node_entity) {
s.set_input(input);
}
}
}

fn update_animation_rate<T>(
hierarchy: &AnimationHierarchy<T>,
samplers: &mut WriteStorage<SamplerControlSet<T>>,
Expand Down
48 changes: 47 additions & 1 deletion amethyst_animation/src/util.rs
Expand Up @@ -5,7 +5,7 @@ use minterpolate::InterpolationPrimitive;
use specs::{Entity, WriteStorage};

use resources::{Animation, AnimationCommand, AnimationControl, AnimationSampling, ControlState,
EndControl};
EndControl, StepDirection};

/// Play a given animation on the given entity.
///
Expand Down Expand Up @@ -125,6 +125,52 @@ pub fn set_animation_rate<T>(
}
}

/// Step animation.
///
/// ## Parameters:
///
/// - `controls`: animation control storage in the world.
/// - `animation`: handle to the animation
/// - `entity`: entity the animation is running on.
/// - `direction`: direction to step the animation
pub fn step_animation<T>(
controls: &mut WriteStorage<AnimationControl<T>>,
animation: &Handle<Animation<T>>,
entity: Entity,
direction: StepDirection,
) where
T: AnimationSampling,
{
if let Some(ref mut control) = controls.get_mut(entity) {
if control.animation == *animation && control.state.is_running() {
control.command = AnimationCommand::Step(direction);
}
}
}

/// Forcibly set animation input value (i.e. the point of interpolation)
///
/// ## Parameters:
///
/// - `controls`: animation control storage in the world.
/// - `animation`: handle to the animation
/// - `entity`: entity the animation is running on.
/// - `input`: input value to set
pub fn set_animation_input<T>(
controls: &mut WriteStorage<AnimationControl<T>>,
animation: &Handle<Animation<T>>,
entity: Entity,
input: f32,
) where
T: AnimationSampling,
{
if let Some(ref mut control) = controls.get_mut(entity) {
if control.animation == *animation && control.state.is_running() {
control.command = AnimationCommand::SetInputValue(input);
}
}
}

/// Sampler primitive
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub enum SamplerPrimitive<S>
Expand Down
51 changes: 38 additions & 13 deletions examples/animation/main.rs
Expand Up @@ -9,11 +9,11 @@ use amethyst::core::{GlobalTransform, Parent, Transform, TransformBundle};
use amethyst::core::cgmath::{Deg, InnerSpace, Vector3};
use amethyst::ecs::{Entity, World};
use amethyst::prelude::*;
use amethyst::renderer::{AmbientColor, Camera, DisplayConfig, DrawShaded, Event, KeyboardInput,
Light, Mesh, Pipeline, PointLight, PosNormTex, Projection, RenderBundle,
Rgba, Stage, VirtualKeyCode, WindowEvent};
use amethyst_animation::{play_animation, Animation, AnimationBundle, EndControl,
InterpolationFunction, Sampler, TransformChannel};
use amethyst::renderer::{AmbientColor, Camera, DisplayConfig, DrawShaded, ElementState, Event,
KeyboardInput, Light, Mesh, Pipeline, PointLight, PosNormTex, Projection,
RenderBundle, Rgba, Stage, VirtualKeyCode, WindowEvent};
use amethyst_animation::{play_animation, step_animation, Animation, AnimationBundle, EndControl,
InterpolationFunction, Sampler, StepDirection, TransformChannel};
use genmesh::{MapToVertices, Triangulate, Vertices};
use genmesh::generators::SphereUV;

Expand Down Expand Up @@ -54,18 +54,43 @@ impl State for Example {
WindowEvent::KeyboardInput {
input:
KeyboardInput {
virtual_keycode: Some(VirtualKeyCode::Space),
virtual_keycode,
state: ElementState::Released,
..
},
..
} => {
play_animation(
&mut world.write(),
self.animation.as_ref().unwrap(),
self.sphere.unwrap().clone(),
EndControl::Loop(None),
1.0,
);
match virtual_keycode {
Some(VirtualKeyCode::Space) => {
play_animation(
&mut world.write(),
self.animation.as_ref().unwrap(),
self.sphere.unwrap().clone(),
EndControl::Loop(None),
0.0,
);
}

Some(VirtualKeyCode::Left) => {
step_animation(
&mut world.write(),
self.animation.as_ref().unwrap(),
self.sphere.unwrap().clone(),
StepDirection::Backward,
);
}

Some(VirtualKeyCode::Right) => {
step_animation(
&mut world.write(),
self.animation.as_ref().unwrap(),
self.sphere.unwrap().clone(),
StepDirection::Forward,
);
}

_ => {}
}

Trans::None
}
Expand Down

0 comments on commit 6fc5ca1

Please sign in to comment.