use crate::{asset::Animation, AnimationAsset};
use glam::{Quat, Vec3};
use std::borrow::Cow;
use std::collections::HashMap;
mod read;
mod write;
#[derive(Clone, Copy, Debug, Default)]
pub struct UncompressedFrame {
pub translation_id: u16,
pub scale_id: u16,
pub rotation_id: u16,
}
#[derive(Clone, Debug)]
pub struct Uncompressed {
pub(crate) duration: f32,
pub(crate) fps: f32,
pub(crate) frame_count: usize,
pub(crate) vector_palette: Vec<Vec3>,
pub(crate) quat_palette: Vec<Quat>,
pub(crate) joint_frames: HashMap<u32, Vec<UncompressedFrame>>,
}
impl Uncompressed {
pub fn new(
fps: f32,
vector_palette: Vec<Vec3>,
quat_palette: Vec<Quat>,
joint_frames: HashMap<u32, Vec<UncompressedFrame>>,
) -> Self {
let frame_count = joint_frames.values().next().map(|f| f.len()).unwrap_or(0);
let duration = if fps > 0.0 {
frame_count as f32 / fps
} else {
0.0
};
Self {
duration,
fps,
frame_count,
vector_palette,
quat_palette,
joint_frames,
}
}
pub fn duration(&self) -> f32 {
self.duration
}
pub fn fps(&self) -> f32 {
self.fps
}
pub fn frame_count(&self) -> usize {
self.frame_count
}
pub fn vector_palette(&self) -> &[Vec3] {
&self.vector_palette
}
pub fn quat_palette(&self) -> &[Quat] {
&self.quat_palette
}
pub fn joint_frames(&self) -> &HashMap<u32, Vec<UncompressedFrame>> {
&self.joint_frames
}
pub fn joint_hashes(&self) -> impl Iterator<Item = &u32> {
self.joint_frames.keys()
}
pub fn get_joint_frames(&self, joint_hash: u32) -> Option<&[UncompressedFrame]> {
self.joint_frames.get(&joint_hash).map(|v| v.as_slice())
}
pub fn evaluate_frame(&self, joint_hash: u32, frame_id: usize) -> Option<(Quat, Vec3, Vec3)> {
let frames = self.joint_frames.get(&joint_hash)?;
let frame = frames.get(frame_id)?;
let rotation = self.quat_palette.get(frame.rotation_id as usize)?;
let translation = self.vector_palette.get(frame.translation_id as usize)?;
let scale = self.vector_palette.get(frame.scale_id as usize)?;
Some((*rotation, *translation, *scale))
}
pub fn joint_count(&self) -> usize {
self.joint_frames.len()
}
pub fn joints(&self) -> Cow<'_, [u32]> {
Cow::Owned(self.joint_frames.keys().copied().collect())
}
pub fn evaluate(&self, time: f32) -> HashMap<u32, (Quat, Vec3, Vec3)> {
if self.frame_count == 0 {
return HashMap::new();
}
let time = time.clamp(0.0, self.duration);
let frame_pos = time * self.fps;
let frame_a = (frame_pos.floor() as usize).min(self.frame_count - 1);
let frame_b = (frame_a + 1).min(self.frame_count - 1);
let t = frame_pos.fract();
self.joint_frames
.iter()
.filter_map(|(&hash, frames)| {
let transform = self.sample_joint(frames, frame_a, frame_b, t)?;
Some((hash, transform))
})
.collect()
}
fn sample_joint(
&self,
frames: &[UncompressedFrame],
frame_a: usize,
frame_b: usize,
t: f32,
) -> Option<(Quat, Vec3, Vec3)> {
let fa = frames.get(frame_a)?;
if frame_a == frame_b || t < f32::EPSILON {
return Some((
*self.quat_palette.get(fa.rotation_id as usize)?,
*self.vector_palette.get(fa.translation_id as usize)?,
*self.vector_palette.get(fa.scale_id as usize)?,
));
}
let fb = frames.get(frame_b)?;
let ra = self.quat_palette.get(fa.rotation_id as usize)?;
let rb = self.quat_palette.get(fb.rotation_id as usize)?;
let ta = self.vector_palette.get(fa.translation_id as usize)?;
let tb = self.vector_palette.get(fb.translation_id as usize)?;
let sa = self.vector_palette.get(fa.scale_id as usize)?;
let sb = self.vector_palette.get(fb.scale_id as usize)?;
Some((ra.slerp(*rb, t), ta.lerp(*tb, t), sa.lerp(*sb, t)))
}
}
impl Animation for Uncompressed {
fn duration(&self) -> f32 {
self.duration
}
fn fps(&self) -> f32 {
self.fps
}
fn joint_count(&self) -> usize {
self.joint_frames.len()
}
fn joints(&self) -> Cow<'_, [u32]> {
Uncompressed::joints(self)
}
fn evaluate(&self, time: f32) -> HashMap<u32, (Quat, Vec3, Vec3)> {
Uncompressed::evaluate(self, time)
}
}
impl From<Uncompressed> for AnimationAsset {
fn from(val: Uncompressed) -> Self {
AnimationAsset::Uncompressed(val)
}
}