diff --git a/src/lib.rs b/src/lib.rs index f2c7874..5a61333 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,17 +9,24 @@ use std::{ pub trait PointCache { type Frame: Frame; - fn read( + fn read_frames( infile: File, ) -> Result< Box::Frame, Box>>>, Box, >; + fn read_animation_info(infile: File) -> Result>; fn map_to::Frame) -> Result>>( infile: File, outfile: File, op: F, ) -> Result<(), Box>; + fn read(infile: File) -> Result, Box> { + Ok(Animation { + frames: Self::read_frames(infile.try_clone()?)?, + info: Self::read_animation_info(infile)?, + }) + } } pub trait Frame { @@ -38,6 +45,30 @@ pub struct Point { pub z: f32, } +#[derive(Debug, PartialEq)] +pub struct AnimationInfo { + pub total_frames: usize, + pub total_points: usize, + pub start_frame: usize, + pub end_frame: usize, + pub fps: f32, +} + +#[derive(Debug)] +pub struct EmptyAnimationError; + +impl Display for EmptyAnimationError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "EmptyAnimationError") + } +} +impl Error for EmptyAnimationError {} + +pub struct Animation { + pub frames: Box::Frame, Box>>>, + pub info: AnimationInfo, +} + #[derive(Debug, Clone, PartialEq)] pub struct MDDFrame { pub frame_idx: usize, @@ -92,15 +123,18 @@ impl Error for ExaustedPointsUnexpectedly {} impl PointCache for MDDSeekableFile { type Frame = MDDFrame; - fn read( + fn read_frames( mut infile: File, - ) -> Result>>>, Box> { + ) -> Result< + Box::Frame, Box>>>, + Box, + > { let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?; let header_size: u64 = (size_of::() * 2).try_into()?; let f32_size: u64 = size_of::().try_into()?; let mut buff = [0u8; size_of::()]; - let data = Box::new( + let frames = Box::new( (0..((total_frames * total_points) as u64)).map::>, _>( move |count| { let frame_idx: usize = (count / (total_points as u64)).try_into()?; @@ -131,7 +165,7 @@ impl PointCache for MDDSeekableFile { }, ), ); - Ok(data) + Ok(frames) } fn map_to(mut infile: File, mut outfile: File, mut op: F) -> Result<(), Box> @@ -145,7 +179,7 @@ impl PointCache for MDDSeekableFile { let f32_size: u64 = size_of::().try_into()?; let mut count = 0; - for frame in Self::read(infile)? { + for frame in Self::read_frames(infile)? { let frame = op(frame?)?; let frame_idx: u64 = count / (total_points as u64); let time_offset: u64 = f32_size * frame_idx; @@ -165,6 +199,31 @@ impl PointCache for MDDSeekableFile { Ok(()) } + + fn read_animation_info(mut infile: File) -> Result> { + let (total_frames, total_points) = Self::read_header(&mut infile)?; + let mut frames = Self::read_frames(infile)?; + let mut fps: f32 = -1.0; + let mut prev_frame = frames.next().ok_or(EmptyAnimationError)??; + let start_frame = prev_frame.frame_idx; + if let Some(second_frame) = frames.next() { + let second_frame = second_frame?; + fps = 1.0 / (second_frame.time - prev_frame.time); + prev_frame = second_frame; + } + for next_frame in frames { + prev_frame = next_frame?; + } + let end_frame = prev_frame.frame_idx; + + Ok(AnimationInfo { + total_frames: total_frames as usize, + total_points: total_points as usize, + start_frame, + end_frame, + fps, + }) + } } pub fn add(left: usize, right: usize) -> usize { @@ -190,9 +249,25 @@ mod tests { })?; println!("\nReading back output.mdd"); let infile = File::open("output.mdd")?; - for frame in MDDSeekableFile::read(infile)? { + for frame in MDDSeekableFile::read_frames(infile)? { println!("{:?}", frame); } Ok(()) } + #[test] + fn animation_info() -> Result<(), Box> { + let infile = File::open("test_cube.mdd")?; + let animation = MDDSeekableFile::read(infile)?; + assert_eq!( + animation.info, + AnimationInfo { + total_frames: 4, + total_points: 1, + start_frame: 0, + end_frame: 3, + fps: 24.0 + } + ); + Ok(()) + } } diff --git a/tools/src/main.rs b/tools/src/main.rs index 23f359f..e97e2f6 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -2,21 +2,18 @@ // First image contains index of every active vertex in the bigger image file, vertices that don't move have a value of -1 in the mask image. // Openexr library is too complex to go off documentation alone and no projects on github using library. Seek out test cases - -// TODO make images less than 16k +// Reference for outputting an image +// https://github.com/vfx-rs/openexr-rs/blob/25826b4f89bc768b565ba150d6f9c76876ad6bc3/src/core/output_file.rs#L275 use openexr::core::channel_list::Channel; use openexr::core::frame_buffer::{FrameBuffer, Slice}; use openexr::core::header::Header; use openexr::core::output_file::OutputFile; -use openexr::core::{PixelType, Compression}; +use openexr::core::{Compression, PixelType}; use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache}; use std::{error::Error, fs::File}; -// https://github.com/vfx-rs/openexr-rs/blob/25826b4f89bc768b565ba150d6f9c76876ad6bc3/src/core/output_file.rs#L275 - pub const MAX_IMAGE_DIM: usize = 16384; -// TODO lower this once you figure out what the fuck pub const MOVEMENT_EPSILON: f32 = 0.000000001; fn non_color_channel() -> Channel { @@ -24,11 +21,12 @@ fn non_color_channel() -> Channel { type_: PixelType::Float.into(), x_sampling: 1, y_sampling: 1, + // The docs say this is only used by a particular compression algorithm, but I don't think it hurts to have it here p_linear: false, } } -// Godot doesn't seem to support Uint images, using f32 instead +// Using f32 instead of Uint to store integers because Godot does not support Uint // Currently only using R channel, can squeeze more into 16k texture if use more channels fn write_mask_image( filename: &str, @@ -111,15 +109,8 @@ fn write_point_image( #[derive(Debug, Clone, Copy)] enum PointState { Unseen, - OneValue { - point_idx: usize, - point: Point, - }, - Varying { - point: Point, - new_idx: usize, - count: usize, - }, + OneValue { point_idx: usize, point: Point }, + Varying { new_idx: usize }, } use PointState::*; impl PointState { @@ -135,6 +126,7 @@ impl PointState { } } + fn main() { let basename = std::env::args() .nth(1) @@ -147,7 +139,7 @@ fn main() { let (total_frames, total_points) = MDDSeekableFile::read_header(&mut mdd_file).unwrap(); let mut mask_last: Vec = vec![Unseen; total_points as usize]; - let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap(); + let pc = MDDSeekableFile::read_frames(mdd_file.try_clone().unwrap()).unwrap(); let mut total_varying = 0; for frame in pc { let MDDFrame { @@ -162,26 +154,12 @@ fn main() { assert_eq!(old_point_idx, point_idx); if PointState::point_changed(&point, &old_point) { mask_last[point_idx] = Varying { - point, new_idx: total_varying, - count: 1, }; total_varying += 1; } } - Varying { - point: old_point, - new_idx, - count, - } => { - if PointState::point_changed(&point, &old_point) { - mask_last[point_idx] = Varying { - point, - new_idx, - count: count + 1, - }; - } - } + Varying { .. } => {} } } @@ -196,7 +174,7 @@ fn main() { let mut pixels: Vec = Vec::with_capacity(total_varying * (total_frames as usize)); - let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap(); + let pc = MDDSeekableFile::read_frames(mdd_file.try_clone().unwrap()).unwrap(); let pc: Vec<_> = pc.collect(); let max_point_idx = pc .iter() @@ -235,5 +213,9 @@ fn main() { ) .unwrap(); - println!("Total {} varying points of {} total points", total_varying, total_points); + println!( + "Total {} varying points of {} total points", + total_varying, total_points + ); + println!() }