258 lines
8.4 KiB
Rust
258 lines
8.4 KiB
Rust
|
use super::transforms::{LinearInterpolation, Rotation, Scale, Transform, Translation};
|
||
|
use anyhow::Result;
|
||
|
|
||
|
use utilities::prelude::cgmath::{Matrix4, Quaternion, Vector3, VectorSpace, Zero};
|
||
|
|
||
|
use gltf;
|
||
|
|
||
|
use std::cmp::Ordering;
|
||
|
use std::time::Duration;
|
||
|
|
||
|
#[derive(Clone, Debug)]
|
||
|
pub struct Channel {
|
||
|
translations: Vec<Translation>,
|
||
|
rotations: Vec<Rotation>,
|
||
|
scales: Vec<Scale>,
|
||
|
}
|
||
|
|
||
|
impl Channel {
|
||
|
pub fn new(
|
||
|
gltf_channel: &gltf::animation::Channel<'_>,
|
||
|
buffers: &Vec<gltf::buffer::Data>,
|
||
|
outputs: Vec<Transform>,
|
||
|
) -> Result<Channel> {
|
||
|
let reader = gltf_channel.reader(|buffer| Some(&buffers[buffer.index()]));
|
||
|
|
||
|
let mut translations = Vec::new();
|
||
|
let mut rotations = Vec::new();
|
||
|
let mut scales = Vec::new();
|
||
|
|
||
|
match reader.read_inputs() {
|
||
|
Some(inputs) => {
|
||
|
for (i, time) in inputs.enumerate() {
|
||
|
match outputs[i] {
|
||
|
Transform::Translation(translation) => translations
|
||
|
.push(Translation::new(Duration::from_secs_f32(time), translation)),
|
||
|
Transform::Rotation(rotation) => {
|
||
|
rotations.push(Rotation::new(Duration::from_secs_f32(time), rotation))
|
||
|
}
|
||
|
Transform::Scale(scale) => {
|
||
|
scales.push(Scale::new(Duration::from_secs_f32(time), scale))
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
None => return Err(anyhow::Error::msg("Gltf: Missing Input from Channel")),
|
||
|
};
|
||
|
|
||
|
Ok(Channel {
|
||
|
translations,
|
||
|
rotations,
|
||
|
scales,
|
||
|
})
|
||
|
}
|
||
|
|
||
|
pub fn update(
|
||
|
&mut self,
|
||
|
gltf_channel: &gltf::animation::Channel<'_>,
|
||
|
buffers: &Vec<gltf::buffer::Data>,
|
||
|
outputs: Vec<Transform>,
|
||
|
) -> Result<()> {
|
||
|
let reader = gltf_channel.reader(|buffer| Some(&buffers[buffer.index()]));
|
||
|
|
||
|
match reader.read_inputs() {
|
||
|
Some(inputs) => {
|
||
|
for (time, transform) in inputs.zip(outputs.into_iter()) {
|
||
|
let time = Duration::from_secs_f32(time);
|
||
|
|
||
|
match transform {
|
||
|
Transform::Translation(translation) => {
|
||
|
// check that time is unique
|
||
|
if cfg!(debug_assertions) {
|
||
|
for translation in self.translations.iter() {
|
||
|
assert!(translation.time() != time);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.translations.push(Translation::new(time, translation));
|
||
|
}
|
||
|
Transform::Rotation(rotation) => {
|
||
|
// check that time is unique
|
||
|
if cfg!(debug_assertions) {
|
||
|
for rotation in self.rotations.iter() {
|
||
|
assert!(rotation.time() != time);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.rotations.push(Rotation::new(time, rotation));
|
||
|
}
|
||
|
Transform::Scale(scale) => {
|
||
|
// check that time is unique
|
||
|
if cfg!(debug_assertions) {
|
||
|
for scale in self.scales.iter() {
|
||
|
assert!(scale.time() != time);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
self.scales.push(Scale::new(time, scale));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
None => return Err(anyhow::Error::msg("Gltf: Missing Input from Channel")),
|
||
|
};
|
||
|
|
||
|
Ok(())
|
||
|
}
|
||
|
|
||
|
pub fn compress(&mut self) {
|
||
|
self.translations = Self::compress_transform(&self.translations);
|
||
|
self.rotations = Self::compress_transform(&self.rotations);
|
||
|
self.scales = Self::compress_transform(&self.scales);
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn compress_transform<T: LinearInterpolation<Inner = K> + Clone, K: PartialEq>(
|
||
|
transforms: &Vec<T>,
|
||
|
) -> Vec<T> {
|
||
|
let mut current: Option<&T> = None;
|
||
|
let mut result: Vec<T> = Vec::new();
|
||
|
|
||
|
for transform in transforms {
|
||
|
match current {
|
||
|
Some(current_transform) => {
|
||
|
if transform.almost_equal(current_transform) {
|
||
|
current = Some(transform);
|
||
|
} else {
|
||
|
result.push(transform.clone());
|
||
|
current = None;
|
||
|
}
|
||
|
}
|
||
|
None => {
|
||
|
result.push(transform.clone());
|
||
|
current = Some(transform);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if result.is_empty() {
|
||
|
if let Some(transform) = current {
|
||
|
result.push(transform.clone());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
result
|
||
|
}
|
||
|
|
||
|
pub fn sort_by_time(&mut self) {
|
||
|
self.translations
|
||
|
.sort_by(|a, b| a.time().partial_cmp(&b.time()).unwrap_or(Ordering::Equal));
|
||
|
|
||
|
self.rotations
|
||
|
.sort_by(|a, b| a.time().partial_cmp(&b.time()).unwrap_or(Ordering::Equal));
|
||
|
|
||
|
self.scales
|
||
|
.sort_by(|a, b| a.time().partial_cmp(&b.time()).unwrap_or(Ordering::Equal));
|
||
|
}
|
||
|
|
||
|
pub fn duration(&self) -> Duration {
|
||
|
let translation_duration = match self.translations.last() {
|
||
|
Some(translation) => translation.time(),
|
||
|
None => Duration::from_secs(0),
|
||
|
};
|
||
|
|
||
|
let rotation_duration = match self.rotations.last() {
|
||
|
Some(rotation) => rotation.time(),
|
||
|
None => Duration::from_secs(0),
|
||
|
};
|
||
|
|
||
|
let scale_duration = match self.scales.last() {
|
||
|
Some(scale) => scale.time(),
|
||
|
None => Duration::from_secs(0),
|
||
|
};
|
||
|
|
||
|
use std::cmp::max;
|
||
|
|
||
|
max(max(translation_duration, rotation_duration), scale_duration)
|
||
|
}
|
||
|
|
||
|
pub fn transform(&self, time: Duration) -> InterpolatedTransforms {
|
||
|
InterpolatedTransforms {
|
||
|
translation: Self::interpolate(&self.translations, time),
|
||
|
rotation: Self::interpolate(&self.rotations, time),
|
||
|
scale: Self::interpolate(&self.scales, time),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[inline]
|
||
|
fn interpolate<K: Zero>(
|
||
|
transforms: &Vec<impl LinearInterpolation<Inner = K>>,
|
||
|
now: Duration,
|
||
|
) -> K {
|
||
|
if transforms.is_empty() {
|
||
|
return K::zero();
|
||
|
} else if transforms.len() == 1 {
|
||
|
return transforms[0].to_inner();
|
||
|
}
|
||
|
|
||
|
match transforms
|
||
|
.iter()
|
||
|
.enumerate()
|
||
|
.find_map(|(index, transform)| {
|
||
|
if transform.time() >= now {
|
||
|
Some((index, transform))
|
||
|
} else {
|
||
|
None
|
||
|
}
|
||
|
}) {
|
||
|
Some((index, transform)) => {
|
||
|
if index == 0 {
|
||
|
transform.to_inner()
|
||
|
} else {
|
||
|
let previous_transform = &transforms[index - 1];
|
||
|
|
||
|
transform
|
||
|
.linear_interpolation(previous_transform, now)
|
||
|
.to_inner()
|
||
|
}
|
||
|
}
|
||
|
None => K::zero(),
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#[derive(Clone, Debug)]
|
||
|
pub struct InterpolatedTransforms {
|
||
|
pub(crate) translation: Vector3<f32>,
|
||
|
pub(crate) rotation: Quaternion<f32>,
|
||
|
pub(crate) scale: Vector3<f32>,
|
||
|
}
|
||
|
|
||
|
impl InterpolatedTransforms {
|
||
|
pub fn interpolate(self, other: InterpolatedTransforms, factor: f32) -> InterpolatedTransforms {
|
||
|
InterpolatedTransforms {
|
||
|
translation: self.translation.lerp(other.translation, factor),
|
||
|
rotation: self.rotation.lerp(other.rotation, factor),
|
||
|
scale: self.scale.lerp(other.scale, factor),
|
||
|
}
|
||
|
}
|
||
|
|
||
|
pub fn collapse(self) -> Matrix4<f32> {
|
||
|
Self::convert_translation(self.translation)
|
||
|
* Self::convert_rotation(self.rotation)
|
||
|
* Self::convert_scale(self.scale)
|
||
|
}
|
||
|
|
||
|
fn convert_translation(translation: Vector3<f32>) -> Matrix4<f32> {
|
||
|
Matrix4::from_translation(translation)
|
||
|
}
|
||
|
|
||
|
fn convert_rotation(rotation: Quaternion<f32>) -> Matrix4<f32> {
|
||
|
Matrix4::from(rotation)
|
||
|
}
|
||
|
|
||
|
fn convert_scale(scale: Vector3<f32>) -> Matrix4<f32> {
|
||
|
Matrix4::from_nonuniform_scale(scale.x, scale.y, scale.z)
|
||
|
}
|
||
|
}
|