use super::{
evaluate::{
compress_time, decompress_vector3, HotFrameEvaluator, JointHotFrame, JumpFrame,
JumpFrameU16, JumpFrameU32, QuaternionHotFrame, VectorHotFrame,
},
frame::TransformType,
read::AnimationFlags,
Compressed,
};
use crate::quantized;
use glam::{Quat, Vec3};
use std::collections::HashMap;
use std::mem::size_of;
pub struct CompressedEvaluator<'a> {
animation: &'a Compressed,
state: HotFrameEvaluator,
}
impl<'a> CompressedEvaluator<'a> {
pub fn new(animation: &'a Compressed) -> Self {
Self {
state: HotFrameEvaluator::new(animation.joint_count()),
animation,
}
}
pub fn reset(&mut self) {
self.state.reset();
}
pub fn evaluate(&mut self, time: f32) -> HashMap<u32, (Quat, Vec3, Vec3)> {
let time = time.clamp(0.0, self.animation.duration);
let parametrized = self
.animation
.flags
.contains(AnimationFlags::UseKeyframeParametrization);
self.update_hot_frames(time);
let compressed_time = compress_time(time, self.animation.duration);
self.animation
.joints
.iter()
.zip(self.state.hot_frames.iter())
.map(|(&hash, hot_frame)| (hash, hot_frame.sample(compressed_time, parametrized)))
.collect()
}
fn update_hot_frames(&mut self, time: f32) {
let needs_reinit = self.state.last_evaluation_time < 0.0
|| self.state.last_evaluation_time > time
|| (self.animation.jump_cache_count > 0
&& (time - self.state.last_evaluation_time)
> self.animation.duration / self.animation.jump_cache_count as f32);
if needs_reinit {
self.initialize_from_jump_cache(time);
}
let compressed_time = compress_time(time, self.animation.duration);
self.advance_cursor(compressed_time);
self.state.last_evaluation_time = time;
}
fn initialize_from_jump_cache(&mut self, time: f32) {
if self.animation.jump_cache_count == 0 || self.animation.duration <= 0.0 {
return;
}
let jump_cache_id = ((self.animation.jump_cache_count as f32
* (time / self.animation.duration)) as usize)
.min(self.animation.jump_cache_count - 1);
self.state.cursor = 0;
if self.animation.frames.len() < 0x10001 {
self.init_from_cache::<JumpFrameU16>(jump_cache_id);
} else {
self.init_from_cache::<JumpFrameU32>(jump_cache_id);
}
self.state.cursor += 1;
}
fn init_from_cache<J: JumpFrame>(&mut self, jump_cache_id: usize) {
let cache_start = jump_cache_id * size_of::<J>() * self.animation.joints.len();
for joint_id in 0..self.animation.joints.len() {
let offset = cache_start + joint_id * size_of::<J>();
let Some(bytes) = self
.animation
.jump_caches
.get(offset..offset + size_of::<J>())
else {
continue;
};
let jump_frame: J = bytemuck::pod_read_unaligned(bytes);
self.init_joint_hot_frame(joint_id, &jump_frame);
}
}
fn init_joint_hot_frame<J: JumpFrame>(&mut self, joint_id: usize, jump_frame: &J) {
let mut hot_frame = JointHotFrame::default();
for (i, &frame_idx) in jump_frame.rotation_keys().iter().enumerate() {
self.state.cursor = self.state.cursor.max(frame_idx);
if let Some(frame) = self.animation.frames.get(frame_idx) {
hot_frame.rotation[i] = QuaternionHotFrame {
time: frame.time(),
value: quantized::decompress_quat_u16(&frame.value()),
};
}
}
for (i, &frame_idx) in jump_frame.translation_keys().iter().enumerate() {
self.state.cursor = self.state.cursor.max(frame_idx);
if let Some(frame) = self.animation.frames.get(frame_idx) {
hot_frame.translation[i] = VectorHotFrame {
time: frame.time(),
value: decompress_vector3(
&frame.value(),
self.animation.translation_min,
self.animation.translation_max,
),
};
}
}
for (i, &frame_idx) in jump_frame.scale_keys().iter().enumerate() {
self.state.cursor = self.state.cursor.max(frame_idx);
if let Some(frame) = self.animation.frames.get(frame_idx) {
hot_frame.scale[i] = VectorHotFrame {
time: frame.time(),
value: decompress_vector3(
&frame.value(),
self.animation.scale_min,
self.animation.scale_max,
),
};
}
}
for i in 1..4 {
if hot_frame.rotation[i].value.dot(hot_frame.rotation[0].value) < 0.0 {
hot_frame.rotation[i].value = -hot_frame.rotation[i].value;
}
}
self.state.hot_frames[joint_id] = hot_frame;
}
fn advance_cursor(&mut self, compressed_time: u16) {
while self.state.cursor < self.animation.frames.len() {
let frame = &self.animation.frames[self.state.cursor];
let joint_id = frame.joint_id() as usize;
let transform_type = frame.transform_type();
let hot_frame = &self.state.hot_frames[joint_id];
let needs_update = match transform_type {
TransformType::Rotation => compressed_time >= hot_frame.rotation[2].time,
TransformType::Translation => compressed_time >= hot_frame.translation[2].time,
TransformType::Scale => compressed_time >= hot_frame.scale[2].time,
};
if !needs_update {
break;
}
match transform_type {
TransformType::Rotation => {
self.fetch_rotation_frame(joint_id, frame.time(), &frame.value())
}
TransformType::Translation => {
self.fetch_translation_frame(joint_id, frame.time(), &frame.value())
}
TransformType::Scale => {
self.fetch_scale_frame(joint_id, frame.time(), &frame.value())
}
}
self.state.cursor += 1;
}
}
fn fetch_rotation_frame(&mut self, joint_id: usize, time: u16, value: &[u16; 3]) {
let hot_frame = &mut self.state.hot_frames[joint_id];
hot_frame.rotation[0] = hot_frame.rotation[1];
hot_frame.rotation[1] = hot_frame.rotation[2];
hot_frame.rotation[2] = hot_frame.rotation[3];
hot_frame.rotation[3] = QuaternionHotFrame {
time,
value: quantized::decompress_quat_u16(value),
};
for i in 1..4 {
if hot_frame.rotation[i].value.dot(hot_frame.rotation[0].value) < 0.0 {
hot_frame.rotation[i].value = -hot_frame.rotation[i].value;
}
}
}
fn fetch_translation_frame(&mut self, joint_id: usize, time: u16, value: &[u16; 3]) {
let hot_frame = &mut self.state.hot_frames[joint_id];
hot_frame.translation[0] = hot_frame.translation[1];
hot_frame.translation[1] = hot_frame.translation[2];
hot_frame.translation[2] = hot_frame.translation[3];
hot_frame.translation[3] = VectorHotFrame {
time,
value: decompress_vector3(
value,
self.animation.translation_min,
self.animation.translation_max,
),
};
}
fn fetch_scale_frame(&mut self, joint_id: usize, time: u16, value: &[u16; 3]) {
let hot_frame = &mut self.state.hot_frames[joint_id];
hot_frame.scale[0] = hot_frame.scale[1];
hot_frame.scale[1] = hot_frame.scale[2];
hot_frame.scale[2] = hot_frame.scale[3];
hot_frame.scale[3] = VectorHotFrame {
time,
value: decompress_vector3(value, self.animation.scale_min, self.animation.scale_max),
};
}
}