Animation info

This commit is contained in:
Spencer Killen 2024-01-02 10:01:05 -07:00
parent 9677c18b57
commit 56de6fefc3
Signed by: sjkillen
GPG Key ID: 3AF3117BA6FBB75B
2 changed files with 98 additions and 41 deletions

View File

@ -9,17 +9,24 @@ use std::{
pub trait PointCache { pub trait PointCache {
type Frame: Frame; type Frame: Frame;
fn read( fn read_frames(
infile: File, infile: File,
) -> Result< ) -> Result<
Box<dyn Iterator<Item = Result<<Self as PointCache>::Frame, Box<dyn Error>>>>, Box<dyn Iterator<Item = Result<<Self as PointCache>::Frame, Box<dyn Error>>>>,
Box<dyn Error>, Box<dyn Error>,
>; >;
fn read_animation_info(infile: File) -> Result<AnimationInfo, Box<dyn Error>>;
fn map_to<F: FnMut(<Self as PointCache>::Frame) -> Result<MDDFrame, Box<dyn Error>>>( fn map_to<F: FnMut(<Self as PointCache>::Frame) -> Result<MDDFrame, Box<dyn Error>>>(
infile: File, infile: File,
outfile: File, outfile: File,
op: F, op: F,
) -> Result<(), Box<dyn Error>>; ) -> Result<(), Box<dyn Error>>;
fn read(infile: File) -> Result<Animation<Self>, Box<dyn Error>> {
Ok(Animation {
frames: Self::read_frames(infile.try_clone()?)?,
info: Self::read_animation_info(infile)?,
})
}
} }
pub trait Frame { pub trait Frame {
@ -38,6 +45,30 @@ pub struct Point {
pub z: f32, 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<PC: PointCache + ?Sized> {
pub frames: Box<dyn Iterator<Item = Result<<PC>::Frame, Box<dyn Error>>>>,
pub info: AnimationInfo,
}
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
pub struct MDDFrame { pub struct MDDFrame {
pub frame_idx: usize, pub frame_idx: usize,
@ -92,15 +123,18 @@ impl Error for ExaustedPointsUnexpectedly {}
impl PointCache for MDDSeekableFile { impl PointCache for MDDSeekableFile {
type Frame = MDDFrame; type Frame = MDDFrame;
fn read( fn read_frames(
mut infile: File, mut infile: File,
) -> Result<Box<dyn Iterator<Item = Result<MDDFrame, Box<dyn Error>>>>, Box<dyn Error>> { ) -> Result<
Box<dyn Iterator<Item = Result<<Self as PointCache>::Frame, Box<dyn Error>>>>,
Box<dyn Error>,
> {
let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?; let (total_frames, total_points) = MDDSeekableFile::read_header(&mut infile)?;
let header_size: u64 = (size_of::<i32>() * 2).try_into()?; let header_size: u64 = (size_of::<i32>() * 2).try_into()?;
let f32_size: u64 = size_of::<f32>().try_into()?; let f32_size: u64 = size_of::<f32>().try_into()?;
let mut buff = [0u8; size_of::<f32>()]; let mut buff = [0u8; size_of::<f32>()];
let data = Box::new( let frames = Box::new(
(0..((total_frames * total_points) as u64)).map::<Result<_, Box<dyn Error>>, _>( (0..((total_frames * total_points) as u64)).map::<Result<_, Box<dyn Error>>, _>(
move |count| { move |count| {
let frame_idx: usize = (count / (total_points as u64)).try_into()?; 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<F>(mut infile: File, mut outfile: File, mut op: F) -> Result<(), Box<dyn Error>> fn map_to<F>(mut infile: File, mut outfile: File, mut op: F) -> Result<(), Box<dyn Error>>
@ -145,7 +179,7 @@ impl PointCache for MDDSeekableFile {
let f32_size: u64 = size_of::<f32>().try_into()?; let f32_size: u64 = size_of::<f32>().try_into()?;
let mut count = 0; let mut count = 0;
for frame in Self::read(infile)? { for frame in Self::read_frames(infile)? {
let frame = op(frame?)?; let frame = op(frame?)?;
let frame_idx: u64 = count / (total_points as u64); let frame_idx: u64 = count / (total_points as u64);
let time_offset: u64 = f32_size * frame_idx; let time_offset: u64 = f32_size * frame_idx;
@ -165,6 +199,31 @@ impl PointCache for MDDSeekableFile {
Ok(()) Ok(())
} }
fn read_animation_info(mut infile: File) -> Result<AnimationInfo, Box<dyn Error>> {
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 { pub fn add(left: usize, right: usize) -> usize {
@ -190,9 +249,25 @@ mod tests {
})?; })?;
println!("\nReading back output.mdd"); println!("\nReading back output.mdd");
let infile = File::open("output.mdd")?; let infile = File::open("output.mdd")?;
for frame in MDDSeekableFile::read(infile)? { for frame in MDDSeekableFile::read_frames(infile)? {
println!("{:?}", frame); println!("{:?}", frame);
} }
Ok(()) Ok(())
} }
#[test]
fn animation_info() -> Result<(), Box<dyn Error>> {
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(())
}
} }

View File

@ -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. // 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 // Openexr library is too complex to go off documentation alone and no projects on github using library. Seek out test cases
// Reference for outputting an image
// TODO make images less than 16k // https://github.com/vfx-rs/openexr-rs/blob/25826b4f89bc768b565ba150d6f9c76876ad6bc3/src/core/output_file.rs#L275
use openexr::core::channel_list::Channel; use openexr::core::channel_list::Channel;
use openexr::core::frame_buffer::{FrameBuffer, Slice}; use openexr::core::frame_buffer::{FrameBuffer, Slice};
use openexr::core::header::Header; use openexr::core::header::Header;
use openexr::core::output_file::OutputFile; use openexr::core::output_file::OutputFile;
use openexr::core::{PixelType, Compression}; use openexr::core::{Compression, PixelType};
use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache}; use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache};
use std::{error::Error, fs::File}; 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; pub const MAX_IMAGE_DIM: usize = 16384;
// TODO lower this once you figure out what the fuck
pub const MOVEMENT_EPSILON: f32 = 0.000000001; pub const MOVEMENT_EPSILON: f32 = 0.000000001;
fn non_color_channel() -> Channel { fn non_color_channel() -> Channel {
@ -24,11 +21,12 @@ fn non_color_channel() -> Channel {
type_: PixelType::Float.into(), type_: PixelType::Float.into(),
x_sampling: 1, x_sampling: 1,
y_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, 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 // Currently only using R channel, can squeeze more into 16k texture if use more channels
fn write_mask_image( fn write_mask_image(
filename: &str, filename: &str,
@ -111,15 +109,8 @@ fn write_point_image(
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
enum PointState { enum PointState {
Unseen, Unseen,
OneValue { OneValue { point_idx: usize, point: Point },
point_idx: usize, Varying { new_idx: usize },
point: Point,
},
Varying {
point: Point,
new_idx: usize,
count: usize,
},
} }
use PointState::*; use PointState::*;
impl PointState { impl PointState {
@ -135,6 +126,7 @@ impl PointState {
} }
} }
fn main() { fn main() {
let basename = std::env::args() let basename = std::env::args()
.nth(1) .nth(1)
@ -147,7 +139,7 @@ fn main() {
let (total_frames, total_points) = MDDSeekableFile::read_header(&mut mdd_file).unwrap(); let (total_frames, total_points) = MDDSeekableFile::read_header(&mut mdd_file).unwrap();
let mut mask_last: Vec<PointState> = vec![Unseen; total_points as usize]; let mut mask_last: Vec<PointState> = 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; let mut total_varying = 0;
for frame in pc { for frame in pc {
let MDDFrame { let MDDFrame {
@ -162,26 +154,12 @@ fn main() {
assert_eq!(old_point_idx, point_idx); assert_eq!(old_point_idx, point_idx);
if PointState::point_changed(&point, &old_point) { if PointState::point_changed(&point, &old_point) {
mask_last[point_idx] = Varying { mask_last[point_idx] = Varying {
point,
new_idx: total_varying, new_idx: total_varying,
count: 1,
}; };
total_varying += 1; total_varying += 1;
} }
} }
Varying { 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,
};
}
}
} }
} }
@ -196,7 +174,7 @@ fn main() {
let mut pixels: Vec<Point> = Vec::with_capacity(total_varying * (total_frames as usize)); let mut pixels: Vec<Point> = 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 pc: Vec<_> = pc.collect();
let max_point_idx = pc let max_point_idx = pc
.iter() .iter()
@ -235,5 +213,9 @@ fn main() {
) )
.unwrap(); .unwrap();
println!("Total {} varying points of {} total points", total_varying, total_points); println!(
"Total {} varying points of {} total points",
total_varying, total_points
);
println!()
} }