Compare commits
4 Commits
9677c18b57
...
5d3b7051a4
Author | SHA1 | Date |
---|---|---|
Spencer Killen | 5d3b7051a4 | |
Spencer Killen | cc792cfb1b | |
Spencer Killen | ccf290f070 | |
Spencer Killen | 56de6fefc3 |
92
src/lib.rs
92
src/lib.rs
|
@ -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,32 @@ 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,
|
||||||
|
// MDD file format supports varying FPS , but Blender's exporter does not
|
||||||
|
// FPS is only correct if it's not varying, see individual frame times for more granularity
|
||||||
|
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 +125,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 +167,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 +181,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 +201,32 @@ 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;
|
||||||
|
// TODO: sometimes the time difference between frames is 0, not sure why
|
||||||
|
// Need to rely on later frames to avoid divide by zero
|
||||||
|
for next_frame in frames {
|
||||||
|
let next_frame = next_frame?;
|
||||||
|
if fps < 0.0 && (next_frame.time - prev_frame.time) > 0.0 {
|
||||||
|
fps = 1.0 / (next_frame.time - prev_frame.time);
|
||||||
|
}
|
||||||
|
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 +252,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(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,87 +10,41 @@
|
||||||
@tool
|
@tool
|
||||||
extends SceneTree
|
extends SceneTree
|
||||||
|
|
||||||
var bundle_name := "bundle.res"
|
var bundle_name := "vertex_animations.scn"
|
||||||
|
var VertexAnimation = preload("VertexAnimation.gd")
|
||||||
|
var VertexAnimations = preload("VertexAnimations.gd")
|
||||||
|
|
||||||
|
|
||||||
func _init():
|
func _init():
|
||||||
var script := GDScript.new()
|
|
||||||
var basenames := OS.get_cmdline_user_args()
|
var basenames := OS.get_cmdline_user_args()
|
||||||
script.source_code = make_all_headers(basenames, true)
|
|
||||||
script.reload()
|
|
||||||
var res: Resource = script.new()
|
|
||||||
|
|
||||||
res.basenames = basenames
|
var animations = VertexAnimations.new()
|
||||||
|
animations.name = "VertexAnimations"
|
||||||
for basename in basenames:
|
for basename in basenames:
|
||||||
add_images(res, basename)
|
animations.animations.append(add_animation(basename))
|
||||||
|
|
||||||
|
|
||||||
var shader := ShaderInclude.new()
|
|
||||||
for basename in basenames:
|
|
||||||
shader.code += make_shader(basename)
|
|
||||||
|
|
||||||
script.source_code = make_all_headers(basenames, false)
|
|
||||||
|
|
||||||
|
|
||||||
# Cannot use built-in scripts
|
|
||||||
# See: https://github.com/godotengine/godot/issues/85876
|
|
||||||
script.resource_path = "res://bundle/bundle.gd"
|
|
||||||
ResourceSaver.save(script, "res://bundle/bundle.gd")
|
|
||||||
res.shader = shader
|
|
||||||
res.shader.resource_name = "shader"
|
|
||||||
shader.resource_path = "res://bundle/bundle.gdshaderinc"
|
|
||||||
ResourceSaver.save(shader, "res://bundle/bundle.gdshaderinc", ResourceSaver.FLAG_CHANGE_PATH)
|
|
||||||
ResourceSaver.save(res, "res://bundle/" + bundle_name, ResourceSaver.FLAG_CHANGE_PATH)
|
|
||||||
|
|
||||||
|
var packed = PackedScene.new()
|
||||||
|
packed.pack(animations)
|
||||||
|
|
||||||
|
ResourceSaver.save(packed, "res://" + bundle_name, ResourceSaver.FLAG_CHANGE_PATH)
|
||||||
|
|
||||||
|
# Was never added to the scene tree, must manually free
|
||||||
|
animations.free()
|
||||||
quit()
|
quit()
|
||||||
|
|
||||||
|
|
||||||
func make_all_shaders(basenames):
|
func add_animation(basename: String):
|
||||||
for basename in basenames:
|
var animation_info: Dictionary = JSON.parse_string(FileAccess.open(basename + ".json", FileAccess.READ).get_as_text())
|
||||||
pass
|
var animation = VertexAnimation.new()
|
||||||
|
animation.name = basename
|
||||||
func make_shader(basename: String):
|
animation.start_frame = animation_info["frame_start"]
|
||||||
var code = "\nuniform sampler2D image_" + basename + ": repeat_disable;\n"
|
animation.end_frame = animation_info["frame_end"]
|
||||||
code += "uniform sampler2D image_mask_" + basename + ": repeat_disable;\n"
|
animation.fps = animation_info["fps"]
|
||||||
code += "uniform bool enabled_" + basename + " = false;\n"
|
|
||||||
code += "uniform int start_time_" + basename + ";\n"
|
|
||||||
return code
|
|
||||||
|
|
||||||
func make_all_headers(basenames, include_setters):
|
|
||||||
var header := "extends Resource\n"
|
|
||||||
header += "@export var basenames: PackedStringArray\n"
|
|
||||||
header += "@export var shader: ShaderInclude\n"
|
|
||||||
for basename in basenames:
|
|
||||||
header += make_header(basename)
|
|
||||||
if include_setters:
|
|
||||||
header += make_setters(basename)
|
|
||||||
return header + attach_code(basenames)
|
|
||||||
|
|
||||||
func attach_code(basenames):
|
|
||||||
var code := "\nfunc attach(mat: ShaderMaterial):\n"
|
|
||||||
for basename in basenames:
|
|
||||||
code += " mat.set_shader_parameter('image_"+basename+"', image_"+basename+")\n"
|
|
||||||
code += " mat.set_shader_parameter('image_mask_"+basename+"', image_mask_"+basename+")\n"
|
|
||||||
return code
|
|
||||||
|
|
||||||
func make_header(basename: String):
|
|
||||||
var header := "@export var image_" + basename + ": Texture2D\n"
|
|
||||||
header += "@export var image_mask_" + basename + ": Texture2D\n"
|
|
||||||
return header
|
|
||||||
|
|
||||||
func make_setters(basename: String):
|
|
||||||
var body := """
|
|
||||||
func set_image_"""+basename+"""(image):\n image_""" + basename + """ = image
|
|
||||||
func set_image_mask_"""+basename+"""(image):\n image_mask_""" + basename + """ = image
|
|
||||||
"""
|
|
||||||
return body
|
|
||||||
|
|
||||||
|
|
||||||
func add_images(res, basename: String):
|
|
||||||
var img := Image.new()
|
var img := Image.new()
|
||||||
img.load(basename + ".exr")
|
img.load(basename + ".exr")
|
||||||
var tex := ImageTexture.create_from_image(img)
|
animation.data = ImageTexture.create_from_image(img)
|
||||||
res.call("set_image_"+basename, tex)
|
|
||||||
img = Image.new()
|
img = Image.new()
|
||||||
img.load(basename + "_mask.exr")
|
img.load(basename + "_mask.exr")
|
||||||
tex = ImageTexture.create_from_image(img)
|
animation.mask = ImageTexture.create_from_image(img)
|
||||||
res.call("set_image_mask_"+basename, tex)
|
|
||||||
|
return animation
|
||||||
|
|
|
@ -2,21 +2,19 @@
|
||||||
// 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::{AnimationInfo, MDDFrame, MDDSeekableFile, Point, PointCache};
|
||||||
|
use std::io::Write;
|
||||||
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 +22,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,
|
||||||
|
@ -108,18 +107,22 @@ fn write_point_image(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Not used because blender always starts frame frame zero so this info is useless
|
||||||
|
fn _write_animation_info(filename: &str, info: AnimationInfo) -> Result<(), Box<dyn Error>> {
|
||||||
|
let data = format!(
|
||||||
|
"{{ \"total_frames\": {}, \"total_points\": {}, \"start_frame\": {}, \"end_frame\": {}, \"fps\": {} }}\n",
|
||||||
|
info.total_frames, info.total_points, info.start_frame, info.end_frame, info.fps
|
||||||
|
);
|
||||||
|
let mut file = File::create(filename)?;
|
||||||
|
file.write_all(data.as_bytes())?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[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 +138,7 @@ impl PointState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let basename = std::env::args()
|
let basename = std::env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
|
@ -142,12 +146,12 @@ fn main() {
|
||||||
|
|
||||||
let mdd_filename = basename.clone() + ".mdd";
|
let mdd_filename = basename.clone() + ".mdd";
|
||||||
|
|
||||||
let mut mdd_file = File::open(mdd_filename).expect("Mdd filename is not valid");
|
let mdd_file = File::open(mdd_filename).expect("Mdd filename is not valid");
|
||||||
|
|
||||||
let (total_frames, total_points) = MDDSeekableFile::read_header(&mut mdd_file).unwrap();
|
let info = MDDSeekableFile::read_animation_info(mdd_file.try_clone().unwrap()).unwrap();
|
||||||
|
|
||||||
let mut mask_last: Vec<PointState> = vec![Unseen; total_points as usize];
|
let mut mask_last: Vec<PointState> = vec![Unseen; info.total_points];
|
||||||
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,41 +166,22 @@ 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,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mask: Vec<f32> = mask_last.iter().map(PointState::mask_value).collect();
|
let mask: Vec<f32> = mask_last.iter().map(PointState::mask_value).collect();
|
||||||
|
|
||||||
write_mask_image(
|
write_mask_image(&(basename.clone() + "_mask.exr"), &mask, info.total_points).unwrap();
|
||||||
&(basename.clone() + "_mask.exr"),
|
|
||||||
&mask,
|
|
||||||
total_points as usize,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut pixels: Vec<Point> = Vec::with_capacity(total_varying * (total_frames as usize));
|
let mut pixels: Vec<Point> = Vec::with_capacity(total_varying * (info.total_frames));
|
||||||
|
|
||||||
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()
|
||||||
|
@ -205,7 +190,7 @@ fn main() {
|
||||||
.cloned()
|
.cloned()
|
||||||
.max()
|
.max()
|
||||||
.unwrap();
|
.unwrap();
|
||||||
assert_eq!(max_point_idx + 1, total_points as usize);
|
assert_eq!(max_point_idx + 1, info.total_points);
|
||||||
|
|
||||||
let pc = pc.into_iter();
|
let pc = pc.into_iter();
|
||||||
|
|
||||||
|
@ -220,7 +205,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
assert!(pixels.len() == total_varying * (total_frames as usize));
|
assert!(pixels.len() == total_varying * (info.total_frames));
|
||||||
|
|
||||||
if total_varying == 0 {
|
if total_varying == 0 {
|
||||||
eprintln!("No varying frames, not outputting .exr image");
|
eprintln!("No varying frames, not outputting .exr image");
|
||||||
|
@ -228,12 +213,15 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
write_point_image(
|
write_point_image(
|
||||||
&(basename + ".exr"),
|
&(basename.clone() + ".exr"),
|
||||||
&pixels,
|
&pixels,
|
||||||
total_varying,
|
total_varying,
|
||||||
total_frames as usize,
|
info.total_frames,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
println!("Total {} varying points of {} total points", total_varying, total_points);
|
println!(
|
||||||
|
"Total {} varying points of {} total points",
|
||||||
|
total_varying, info.total_points
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue