use cgmath;
use froggy;
use mint;
use object::{Base, Object};
use std::hash::{Hash, Hasher};
use std::sync::mpsc;
pub type Target = Base;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Interpolation {
Discrete,
Linear,
Cubic,
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum LoopMode {
Once,
Repeat {
limit: Option<u32>,
},
PingPong {
limit: Option<u32>,
},
}
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub enum Binding {
Position,
Orientation,
Scale,
Weights,
}
enum FrameRef {
Unstarted,
InProgress(usize),
Ended,
}
#[derive(Clone, Debug)]
pub enum Values {
Euler(Vec<mint::EulerAngles<f32, mint::IntraXYZ>>),
Quaternion(Vec<mint::Quaternion<f32>>),
Scalar(Vec<f32>),
Vector3(Vec<mint::Vector3<f32>>),
}
enum Operation {
Enable,
Disable,
Pause,
Play,
SetLoopMode(LoopMode),
}
type Message = (froggy::WeakPointer<ActionData>, Operation);
#[derive(Clone, Debug)]
pub struct Action {
tx: mpsc::Sender<Message>,
pointer: froggy::Pointer<ActionData>,
}
impl PartialEq for Action {
fn eq(
&self,
other: &Action,
) -> bool {
self.pointer == other.pointer
}
}
impl Eq for Action {}
impl Hash for Action {
fn hash<H: Hasher>(
&self,
state: &mut H,
) {
self.pointer.hash(state);
}
}
struct ActionData {
pub clip: Clip,
pub enabled: bool,
pub loop_mode: LoopMode,
pub paused: bool,
pub local_time: f32,
pub local_time_scale: f32,
}
#[derive(Clone, Debug)]
pub struct Clip {
pub name: Option<String>,
pub tracks: Vec<(Track, Target)>,
}
#[derive(Clone, Debug)]
pub struct Track {
pub binding: Binding,
pub times: Vec<f32>,
pub values: Values,
pub interpolation: Interpolation,
}
pub struct Mixer {
actions: froggy::Storage<ActionData>,
rx: mpsc::Receiver<Message>,
tx: mpsc::Sender<Message>,
}
impl Action {
fn send(
&mut self,
operation: Operation,
) -> &mut Self {
let message = (self.pointer.downgrade(), operation);
let _ = self.tx.send(message);
self
}
pub fn enable(&mut self) -> &mut Self {
self.send(Operation::Enable)
}
pub fn disable(&mut self) -> &mut Self {
self.send(Operation::Disable)
}
pub fn pause(&mut self) -> &mut Self {
self.send(Operation::Pause)
}
pub fn play(&mut self) -> &mut Self {
self.send(Operation::Play)
}
pub fn set_loop_mode(
&mut self,
loop_mode: LoopMode,
) -> &mut Self {
self.send(Operation::SetLoopMode(loop_mode))
}
}
impl Mixer {
fn process_messages(&mut self) {
while let Ok((weak_ptr, operation)) = self.rx.try_recv() {
let action = match weak_ptr.upgrade() {
Ok(ptr) => &mut self.actions[&ptr],
Err(_) => continue,
};
match operation {
Operation::Enable => action.enabled = true,
Operation::Disable => action.enabled = false,
Operation::Pause => action.paused = true,
Operation::Play => {
action.paused = false;
action.enabled = true;
}
Operation::SetLoopMode(loop_mode) => action.loop_mode = loop_mode,
}
}
}
fn update_actions(
&mut self,
delta_time: f32,
) {
for action in self.actions.iter_mut() {
action.update(delta_time);
}
}
pub fn new() -> Self {
let actions = froggy::Storage::new();
let (tx, rx) = mpsc::channel();
Mixer { actions, rx, tx }
}
pub fn action(
&mut self,
clip: Clip,
) -> Action {
let action_data = ActionData::new(clip);
let pointer = self.actions.create(action_data);
let tx = self.tx.clone();
Action { tx, pointer }
}
pub fn update(
&mut self,
delta_time: f32,
) {
self.process_messages();
self.update_actions(delta_time);
}
}
impl ActionData {
fn new(clip: Clip) -> Self {
ActionData {
clip: clip,
enabled: true,
loop_mode: LoopMode::Repeat { limit: None },
paused: false,
local_time: 0.0,
local_time_scale: 1.0,
}
}
fn update(
&mut self,
delta_time: f32,
) {
if self.paused || !self.enabled {
return;
}
self.local_time += delta_time * self.local_time_scale;
let mut finish_count = 0;
for &(ref track, ref target) in self.clip.tracks.iter() {
let frame_index = match track.frame_at_time(self.local_time) {
FrameRef::Unstarted => continue,
FrameRef::Ended => {
finish_count += 1;
continue;
}
FrameRef::InProgress(i) => i,
};
let frame_start_time = track.times[frame_index];
let frame_end_time = track.times[frame_index + 1];
let frame_delta_time = frame_end_time - frame_start_time;
let s = (self.local_time - frame_start_time) / frame_delta_time;
match (track.binding, &track.values) {
(Binding::Orientation, &Values::Euler(ref values)) => {
let frame_start_value = {
let euler = values[frame_index];
cgmath::Quaternion::from(cgmath::Euler::new(
cgmath::Rad(euler.a),
cgmath::Rad(euler.b),
cgmath::Rad(euler.c),
))
};
let frame_end_value = {
let euler = values[frame_index + 1];
cgmath::Quaternion::from(cgmath::Euler::new(
cgmath::Rad(euler.a),
cgmath::Rad(euler.b),
cgmath::Rad(euler.c),
))
};
let update = frame_start_value.slerp(frame_end_value, s);
target.set_orientation(update);
}
(Binding::Orientation, &Values::Quaternion(ref values)) => {
let frame_start_value: cgmath::Quaternion<f32> = values[frame_index].into();
let frame_end_value: cgmath::Quaternion<f32> = values[frame_index + 1].into();
let update = frame_start_value.slerp(frame_end_value, s);
target.set_orientation(update);
}
(Binding::Position, &Values::Vector3(ref values)) => {
use cgmath::{EuclideanSpace, InnerSpace};
let frame_start_value: cgmath::Vector3<f32> = values[frame_index].into();
let frame_end_value: cgmath::Vector3<f32> = values[frame_index + 1].into();
let update = frame_start_value.lerp(frame_end_value, s);
target.set_position(cgmath::Point3::from_vec(update));
}
(Binding::Scale, &Values::Scalar(ref values)) => {
let frame_start_value = values[frame_index];
let frame_end_value = values[frame_index + 1];
let update = frame_start_value * (1.0 - s) + frame_end_value * s;
target.set_scale(update);
}
(Binding::Weights, &Values::Scalar(ref values)) => {
let update = values
.chunks(track.times.len())
.map(|chunk| {
let start_value = chunk[frame_index];
let end_value = chunk[frame_index + 1];
start_value * (1.0 - s) + end_value * s
})
.collect();
target.set_weights(update);
}
_ => panic!("Unsupported (binding, value) pair"),
}
}
if finish_count == self.clip.tracks.len() {
match self.loop_mode {
LoopMode::Once => self.enabled = false,
LoopMode::Repeat { limit: None } => self.local_time = 0.0,
LoopMode::Repeat { limit: Some(0) } => self.enabled = false,
LoopMode::Repeat { limit: Some(n) } => {
self.local_time = 0.0;
self.loop_mode = LoopMode::Repeat { limit: Some(n - 1) };
}
LoopMode::PingPong { .. } => {
unimplemented!()
}
}
}
}
}
impl Track {
fn frame_at_time(
&self,
t: f32,
) -> FrameRef {
if t < self.times[0] {
return FrameRef::Unstarted;
}
if t > *self.times.last().unwrap() {
return FrameRef::Ended;
}
let mut i = 0;
while t > self.times[i + 1] {
i += 1;
}
FrameRef::InProgress(i)
}
}