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

feat: Add stepping and exact input value control to animation system #569

Merged
merged 1 commit into from Feb 13, 2018
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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