engine/gltf-loader/src/channel.rs

258 lines
8.4 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
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)
}
}