engine/gltf-loader/src/transforms.rs

176 lines
4.4 KiB
Rust
Raw Normal View History

2024-08-23 11:22:09 +00:00
use utilities::prelude::cgmath::{InnerSpace, Quaternion, Vector3};
use std::time::Duration;
#[derive(Clone, Debug)]
pub struct Translation {
time: Duration,
translation: Vector3<f32>,
}
#[derive(Clone, Debug)]
pub struct Rotation {
time: Duration,
rotation: Quaternion<f32>,
}
#[derive(Clone, Debug)]
pub struct Scale {
time: Duration,
scale: Vector3<f32>,
}
pub(crate) trait LinearInterpolation {
type Inner;
fn new(time: Duration, value: Self::Inner) -> Self;
fn time(&self) -> Duration;
fn linear_interpolation(&self, other: &Self, now: Duration) -> Self;
fn to_inner(&self) -> Self::Inner;
fn almost_equal(&self, other: &Self) -> bool;
}
impl LinearInterpolation for Translation {
type Inner = Vector3<f32>;
fn new(time: Duration, translation: Vector3<f32>) -> Self {
Translation { time, translation }
}
fn time(&self) -> Duration {
self.time
}
fn linear_interpolation(&self, other: &Self, now: Duration) -> Self {
debug_assert!(self.time > other.time);
let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32())
/ (self.time.as_secs_f32() - other.time.as_secs_f32());
let translation =
other.translation + interpolation_value * (self.translation - other.translation);
Translation {
time: now,
translation,
}
}
fn to_inner(&self) -> Vector3<f32> {
self.translation
}
fn almost_equal(&self, other: &Self) -> bool {
almost_equal_f32(self.translation.x, other.translation.x)
&& almost_equal_f32(self.translation.y, other.translation.y)
&& almost_equal_f32(self.translation.z, other.translation.z)
}
}
impl LinearInterpolation for Rotation {
type Inner = Quaternion<f32>;
fn new(time: Duration, rotation: Quaternion<f32>) -> Self {
Rotation { time, rotation }
}
fn time(&self) -> Duration {
self.time
}
fn linear_interpolation(&self, other: &Self, now: Duration) -> Self {
debug_assert!(self.time > other.time);
let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32())
/ (self.time.as_secs_f32() - other.time.as_secs_f32());
let norm_me = self.rotation.normalize();
let norm_other = other.rotation.normalize();
let rotation = slerp(norm_other, norm_me, interpolation_value);
Rotation {
time: now,
rotation,
}
}
fn to_inner(&self) -> Quaternion<f32> {
self.rotation
}
fn almost_equal(&self, other: &Self) -> bool {
almost_equal_f32(self.rotation.s, other.rotation.s)
&& almost_equal_f32(self.rotation.v.x, other.rotation.v.x)
&& almost_equal_f32(self.rotation.v.y, other.rotation.v.y)
&& almost_equal_f32(self.rotation.v.z, other.rotation.v.z)
}
}
#[inline]
fn slerp(p: Quaternion<f32>, q: Quaternion<f32>, t: f32) -> Quaternion<f32> {
let dot = p.dot(q);
if dot < 0.0 {
return slerp(-1.0 * p, q, t);
}
let angle = dot.acos();
let sin = angle.sin();
if almost_equal_f32(sin, 0.0) {
return p;
}
let first = p * ((1.0 - t) * angle).sin();
let second = q * (t * angle).sin();
(first + second) / sin
}
impl LinearInterpolation for Scale {
type Inner = Vector3<f32>;
fn new(time: Duration, scale: Vector3<f32>) -> Self {
Scale { time, scale }
}
fn time(&self) -> Duration {
self.time
}
fn linear_interpolation(&self, other: &Self, now: Duration) -> Self {
debug_assert!(self.time > other.time);
let interpolation_value = (now.as_secs_f32() - other.time.as_secs_f32())
/ (self.time.as_secs_f32() - other.time.as_secs_f32());
let scale = other.scale + interpolation_value * (self.scale - other.scale);
Scale { time: now, scale }
}
fn to_inner(&self) -> Vector3<f32> {
self.scale
}
fn almost_equal(&self, other: &Self) -> bool {
almost_equal_f32(self.scale.x, other.scale.x)
&& almost_equal_f32(self.scale.y, other.scale.y)
&& almost_equal_f32(self.scale.z, other.scale.z)
}
}
#[inline]
fn almost_equal_f32(f1: f32, f2: f32) -> bool {
(f1 - f2).abs() < 0.000001
}
#[derive(Clone, Debug)]
pub enum Transform {
Translation(Vector3<f32>),
Rotation(Quaternion<f32>),
Scale(Vector3<f32>),
}