diff --git a/Cargo.lock b/Cargo.lock index adc83f2..1f07c5a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,13 +2,216 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aho-corasick" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +dependencies = [ + "memchr", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "cppmm-build" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28ab7d173fadeec5208c3ef3eb0aef0d3f419f5fa1450f01d2aa81ce8916e2ce" +dependencies = [ + "cmake", + "quick-xml", + "regex", +] + +[[package]] +name = "half" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7" + +[[package]] +name = "imath-traits" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4659ab80c4ff995c16bd3973b6bb2e3ae7be0528b5f66cf860625b51855711ad" +dependencies = [ + "half", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "openexr" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "062f65fe94f0ee4e7aeb44d6c2b366fa90611ddb632e69c753ba3076731d606e" +dependencies = [ + "bitflags", + "cfg-if", + "half", + "imath-traits", + "openexr-sys", + "paste", + "thiserror", +] + +[[package]] +name = "openexr-sys" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71b018f0a60798b0dbabeb2214ea3f8b68c3ac5e0aadd3036dd90a7c3f72c003" +dependencies = [ + "cppmm-build", +] + +[[package]] +name = "paste" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" + [[package]] name = "pointcache" version = "0.1.0" +[[package]] +name = "proc-macro2" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39278fbbf5fb4f646ce651690877f89d1c5811a3d4acb27700c1cb3cdb78fd3b" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quick-xml" +version = "0.22.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8533f14c8382aaad0d592c812ac3b826162128b65662331e1127b45c3d18536b" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" + +[[package]] +name = "syn" +version = "2.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tools" version = "0.1.0" dependencies = [ + "half", + "imath-traits", + "openexr", "pointcache", ] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" diff --git a/src/lib.rs b/src/lib.rs index 8f999c2..031568f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,7 +8,7 @@ use std::{ }; pub trait PointCache { - type Frame; + type Frame: Frame; fn read( infile: File, ) -> Result< @@ -22,14 +22,22 @@ pub trait PointCache { ) -> Result<(), Box>; } +pub trait Frame { + fn point_idx(&self) -> usize; + fn point(&self) -> Point; + fn with_point(self, point: Point) -> Self; +} + pub struct MDDSeekableFile; -#[derive(Debug)] +#[repr(C)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] pub struct Point { pub x: f32, pub y: f32, pub z: f32, } + #[derive(Debug)] pub struct MDDFrame { pub frame_idx: usize, @@ -38,8 +46,20 @@ pub struct MDDFrame { pub point: Point, } +impl Frame for MDDFrame { + fn point_idx(&self) -> usize { + self.point_idx + } + fn point(&self) -> Point { + self.point + } + fn with_point(self, point: Point) -> Self { + MDDFrame { point, ..self } + } +} + impl MDDSeekableFile { - fn read_header(infile: &mut File) -> Result<(i32, i32), Box> { + pub fn read_header(infile: &mut File) -> Result<(i32, i32), Box> { infile.seek(SeekFrom::Start(0))?; let mut buff = [0u8; size_of::()]; infile.read_exact(&mut buff)?; diff --git a/tools/Cargo.toml b/tools/Cargo.toml index ac95a68..705aa52 100644 --- a/tools/Cargo.toml +++ b/tools/Cargo.toml @@ -5,3 +5,6 @@ edition = "2021" [dependencies] pointcache = { path = ".." } +openexr = "0.11.0" +half = "1.8.2" +imath-traits = "0.4.0" diff --git a/tools/output.exr b/tools/output.exr new file mode 100644 index 0000000..e69de29 diff --git a/tools/src/main.rs b/tools/src/main.rs index 28335d7..29f4dd1 100644 --- a/tools/src/main.rs +++ b/tools/src/main.rs @@ -1,29 +1,174 @@ -use std::fs::File; +// Openexr library is too complex to go off documentation alone and no projects on github using library. Seek out test cases -use pointcache::{MDDSeekableFile, PointCache, MDDFrame, Point}; +use imath_traits::Zero; +use openexr::core::channel_list::{CHANNEL_FLOAT, CHANNEL_UINT}; +use openexr::core::frame_buffer::{FrameBuffer, Slice}; +use openexr::core::header::Header; +use openexr::core::output_file::OutputFile; +use openexr::core::PixelType; +use openexr::rgba::{Rgba, RgbaChannels, RgbaOutputFile}; +use openexr::tiled::TiledOutputFile; +use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache}; +use std::default; +use std::{error::Error, fs::File}; +// https://github.com/vfx-rs/openexr-rs/blob/25826b4f89bc768b565ba150d6f9c76876ad6bc3/src/core/output_file.rs#L275 + +// Godot doesn't seem to support Uint images, using f32 instead +fn write_mask_image( + filename: &str, + pixels: &Vec, + vertex_total: usize, +) -> Result<(), Box> { + let width = vertex_total; + let height: usize = 1; + let mut header = Header::from_dimensions(width as i32, height as i32); + + header.channels_mut().insert("R", &CHANNEL_FLOAT); + + let mut frame_buffer = FrameBuffer::new(); + + frame_buffer.insert( + "R", + &Slice::builder( + PixelType::Float, + &pixels[0] as *const _ as *const u8, + width as i64, + height as i64, + ) + .build()?, + )?; + + let mut file = OutputFile::new(filename, &header, 1)?; + file.set_frame_buffer(&frame_buffer)?; + unsafe { file.write_pixels(height as i32)? }; + + Ok(()) +} + +fn write_point_image( + filename: &str, + pixels: &Vec, + width: usize, + height: usize, +) -> Result<(), Box> { + let mut header = Header::from_dimensions(width as i32, height as i32); + + header.channels_mut().insert("R", &CHANNEL_FLOAT); + header.channels_mut().insert("G", &CHANNEL_FLOAT); + header.channels_mut().insert("B", &CHANNEL_FLOAT); + + let mut frame_buffer = FrameBuffer::new(); + + [ + ("R", &pixels[0].x), + ("G", &pixels[0].y), + ("B", &pixels[0].z), + ] + .map(|(name, spot)| { + frame_buffer.insert( + name, + &Slice::builder( + PixelType::Float, + spot as *const _ as *const u8, + width as i64, + height as i64, + ) + .x_stride(std::mem::size_of::()) + .build()?, + ) + }) + .into_iter() + .collect::>()?; + + let mut file = OutputFile::new(filename, &header, 1)?; + file.set_frame_buffer(&frame_buffer)?; + unsafe { file.write_pixels(height as i32)? }; + + Ok(()) +} + +#[derive(Debug, Clone, Copy)] +enum PointState { + Unseen, + OneValue(Point), + Varying(usize), +} +use PointState::*; +impl PointState { + fn mask_value(&self) -> f32 { + match *self { + Unseen => panic!(), + OneValue(_) => -1.0, + Varying(idx) => idx as f32, + } + } +} fn main() { - let file_a = std::env::args().nth(1).expect("Need three files"); - let file_b = std::env::args().nth(1).expect("Need three files"); - let file_c = std::env::args().nth(1).expect("Need three files"); + let mdd_filename = std::env::args() + .nth(1) + .expect("expected first argument to be a file path to an .mdd file"); + let path = std::env::args() + .nth(2) + .expect("expected second argument to be an output directory"); - let file_a = File::open(file_a).unwrap(); - let file_b = File::open(file_b).unwrap(); - let file_c = File::create(file_c).unwrap(); + let mut mdd_file = File::open(mdd_filename).expect("Mdd filename is not valid"); - let mut file_b = MDDSeekableFile::read(file_b).unwrap(); + let (total_frames, total_points) = MDDSeekableFile::read_header(&mut mdd_file).unwrap(); - MDDSeekableFile::map_to(file_a, file_c, |frame| { - let frame_b = file_b.next().unwrap()?; - Ok(MDDFrame { - point: Point { - x: frame_b.point.x - frame.point.x, - y: frame_b.point.y - frame.point.y, - z: frame_b.point.z - frame.point.z, - }, - ..frame - }) - }).unwrap(); + let mut mask_last: Vec = vec![Unseen; total_points as usize]; + + let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap(); + let mut total_varying = 0; + for frame in pc { + let MDDFrame { + point, point_idx, .. + } = frame.unwrap(); + match mask_last[point_idx] { + Unseen => mask_last[point_idx] = OneValue(point), + OneValue(old_point) => { + if point != old_point { + mask_last[point_idx] = Varying(total_varying); + total_varying += 1; + } + } + Varying(_) => {} + } + } + + let mask: Vec = mask_last.iter().map(PointState::mask_value).collect(); + + // write_mask_image( + // "/home/rat/cow/godot/vertex_animation/mask.exr", + // &pixels.collect(), + // width as usize, + // ) + // .unwrap(); + + let mut pixels: Vec = Vec::with_capacity(total_varying * (total_frames as usize)); + + let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap(); + + pixels.extend(pc.filter_map(|frame| { + let MDDFrame { + point_idx, point, .. + } = frame.unwrap(); + match mask_last[point_idx] { + Unseen => panic!(), + OneValue(_) => None, + Varying(_) => Some(point), + } + })); + + assert!(pixels.len() == total_varying * (total_frames as usize)); + + write_point_image( + "/home/rat/cow/godot/vertex_animation/test.exr", + &pixels, + total_varying, + total_frames as usize, + ) + .unwrap(); } diff --git a/tools/test.mdd b/tools/test.mdd new file mode 100644 index 0000000..b99ed7a Binary files /dev/null and b/tools/test.mdd differ diff --git a/tools/write_deep1.exr b/tools/write_deep1.exr new file mode 100644 index 0000000..e633744 Binary files /dev/null and b/tools/write_deep1.exr differ