This commit is contained in:
parent
e0f00392d2
commit
f06f741a74
|
@ -38,7 +38,7 @@ pub struct Point {
|
||||||
pub z: f32,
|
pub z: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
pub struct MDDFrame {
|
pub struct MDDFrame {
|
||||||
pub frame_idx: usize,
|
pub frame_idx: usize,
|
||||||
pub point_idx: usize,
|
pub point_idx: usize,
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
extends Resource
|
||||||
|
@export var basenames: PackedStringArray
|
||||||
|
@export var shader: ShaderInclude
|
||||||
|
@export var image_cow: Texture2D
|
||||||
|
@export var image_mask_cow: Texture2D
|
||||||
|
|
||||||
|
func attach(mat: ShaderMaterial):
|
||||||
|
mat.set_shader_parameter('image_cow', image_cow)
|
||||||
|
mat.set_shader_parameter('image_mask_cow', image_mask_cow)
|
|
@ -0,0 +1,5 @@
|
||||||
|
|
||||||
|
uniform sampler2D image_cow: repeat_disable;
|
||||||
|
uniform sampler2D image_mask_cow: repeat_disable;
|
||||||
|
uniform bool enabled_cow = false;
|
||||||
|
uniform int start_time_cow;
|
|
@ -0,0 +1,5 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# $@: list of file basenames for .exr files
|
||||||
|
|
||||||
|
mkdir -p bundle
|
||||||
|
./pack_res.gd ++ $@
|
|
@ -0,0 +1,96 @@
|
||||||
|
#!/usr/bin/env -S godot -s --headless
|
||||||
|
# To be called with args "++ basename, basename, ..."
|
||||||
|
|
||||||
|
# Fragile script
|
||||||
|
# Relies on instantiating instead of using set_script
|
||||||
|
# Godot ignores exported properties unless you reload script
|
||||||
|
# Can't call methods directly because it's a parse error
|
||||||
|
# Also can't use .set() because then the properties aren't exported.
|
||||||
|
|
||||||
|
@tool
|
||||||
|
extends SceneTree
|
||||||
|
|
||||||
|
var bundle_name := "bundle.res"
|
||||||
|
|
||||||
|
func _init():
|
||||||
|
var script := GDScript.new()
|
||||||
|
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
|
||||||
|
for basename in basenames:
|
||||||
|
add_images(res, 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)
|
||||||
|
|
||||||
|
quit()
|
||||||
|
|
||||||
|
|
||||||
|
func make_all_shaders(basenames):
|
||||||
|
for basename in basenames:
|
||||||
|
pass
|
||||||
|
|
||||||
|
func make_shader(basename: String):
|
||||||
|
var code = "\nuniform sampler2D image_" + basename + ": repeat_disable;\n"
|
||||||
|
code += "uniform sampler2D image_mask_" + basename + ": repeat_disable;\n"
|
||||||
|
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()
|
||||||
|
img.load(basename + ".exr")
|
||||||
|
var tex := ImageTexture.create_from_image(img)
|
||||||
|
res.call("set_image_"+basename, tex)
|
||||||
|
img = Image.new()
|
||||||
|
img.load(basename + "_mask.exr")
|
||||||
|
tex = ImageTexture.create_from_image(img)
|
||||||
|
res.call("set_image_mask_"+basename, tex)
|
|
@ -1,19 +1,32 @@
|
||||||
|
// Creates two image files for a given [basename].mdd file
|
||||||
|
// 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
|
||||||
|
|
||||||
use imath_traits::Zero;
|
// TODO make images less than 16k
|
||||||
use openexr::core::channel_list::{CHANNEL_FLOAT, CHANNEL_UINT};
|
|
||||||
|
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;
|
use openexr::core::PixelType;
|
||||||
use openexr::rgba::{Rgba, RgbaChannels, RgbaOutputFile};
|
|
||||||
use openexr::tiled::TiledOutputFile;
|
|
||||||
use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache};
|
use pointcache::{MDDFrame, MDDSeekableFile, Point, PointCache};
|
||||||
use std::default;
|
|
||||||
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
|
// https://github.com/vfx-rs/openexr-rs/blob/25826b4f89bc768b565ba150d6f9c76876ad6bc3/src/core/output_file.rs#L275
|
||||||
|
|
||||||
|
pub const MAX_IMAGE_DIM: usize = 16384;
|
||||||
|
pub const MOVEMENT_EPSILON: f32 = 0.0000001;
|
||||||
|
|
||||||
|
fn non_color_channel() -> Channel {
|
||||||
|
Channel {
|
||||||
|
type_: PixelType::Float.into(),
|
||||||
|
x_sampling: 1,
|
||||||
|
y_sampling: 1,
|
||||||
|
p_linear: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Godot doesn't seem to support Uint images, using f32 instead
|
// Godot doesn't seem to support Uint images, using f32 instead
|
||||||
fn write_mask_image(
|
fn write_mask_image(
|
||||||
filename: &str,
|
filename: &str,
|
||||||
|
@ -24,7 +37,7 @@ fn write_mask_image(
|
||||||
let height: usize = 1;
|
let height: usize = 1;
|
||||||
let mut header = Header::from_dimensions(width as i32, height as i32);
|
let mut header = Header::from_dimensions(width as i32, height as i32);
|
||||||
|
|
||||||
header.channels_mut().insert("R", &CHANNEL_FLOAT);
|
header.channels_mut().insert("R", &non_color_channel());
|
||||||
|
|
||||||
let mut frame_buffer = FrameBuffer::new();
|
let mut frame_buffer = FrameBuffer::new();
|
||||||
|
|
||||||
|
@ -54,9 +67,9 @@ fn write_point_image(
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let mut header = Header::from_dimensions(width as i32, height as i32);
|
let mut header = Header::from_dimensions(width as i32, height as i32);
|
||||||
|
|
||||||
header.channels_mut().insert("R", &CHANNEL_FLOAT);
|
header.channels_mut().insert("R", &non_color_channel());
|
||||||
header.channels_mut().insert("G", &CHANNEL_FLOAT);
|
header.channels_mut().insert("G", &non_color_channel());
|
||||||
header.channels_mut().insert("B", &CHANNEL_FLOAT);
|
header.channels_mut().insert("B", &non_color_channel());
|
||||||
|
|
||||||
let mut frame_buffer = FrameBuffer::new();
|
let mut frame_buffer = FrameBuffer::new();
|
||||||
|
|
||||||
|
@ -91,34 +104,42 @@ fn write_point_image(
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
enum PointState {
|
enum PointState {
|
||||||
Unseen,
|
Unseen,
|
||||||
OneValue(Point),
|
OneValue {
|
||||||
Varying(usize),
|
point_idx: usize,
|
||||||
|
point: Point,
|
||||||
|
},
|
||||||
|
Varying {
|
||||||
|
point: Point,
|
||||||
|
new_idx: usize,
|
||||||
|
count: usize,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
use PointState::*;
|
use PointState::*;
|
||||||
impl PointState {
|
impl PointState {
|
||||||
fn mask_value(&self) -> f32 {
|
fn mask_value(&self) -> f32 {
|
||||||
match *self {
|
match *self {
|
||||||
Unseen => panic!(),
|
Unseen => panic!("no point should be unseen"),
|
||||||
OneValue(_) => -1.0,
|
OneValue { .. } => -1.0,
|
||||||
Varying(idx) => idx as f32,
|
Varying { new_idx, .. } => new_idx as f32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fn point_changed(a: &Point, b: &Point) -> bool {
|
||||||
|
((a.x - b.x).abs() + (a.y - b.y).abs() + (a.z - b.z).abs()) > MOVEMENT_EPSILON
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mdd_filename = std::env::args()
|
let basename = std::env::args()
|
||||||
.nth(1)
|
.nth(1)
|
||||||
.expect("expected first argument to be a file path to an .mdd file");
|
.expect("expected first argument to be the basename to an .mdd file in the CWD");
|
||||||
let path = std::env::args()
|
|
||||||
.nth(2)
|
let mdd_filename = basename.clone() + ".mdd";
|
||||||
.expect("expected second argument to be an output directory");
|
|
||||||
|
|
||||||
let mut mdd_file = File::open(mdd_filename).expect("Mdd filename is not valid");
|
let mut 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 (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(mdd_file.try_clone().unwrap()).unwrap();
|
||||||
let mut total_varying = 0;
|
let mut total_varying = 0;
|
||||||
for frame in pc {
|
for frame in pc {
|
||||||
|
@ -126,29 +147,67 @@ fn main() {
|
||||||
point, point_idx, ..
|
point, point_idx, ..
|
||||||
} = frame.unwrap();
|
} = frame.unwrap();
|
||||||
match mask_last[point_idx] {
|
match mask_last[point_idx] {
|
||||||
Unseen => mask_last[point_idx] = OneValue(point),
|
Unseen => mask_last[point_idx] = OneValue { point_idx, point },
|
||||||
OneValue(old_point) => {
|
OneValue {
|
||||||
if point != old_point {
|
point_idx: old_point_idx,
|
||||||
mask_last[point_idx] = Varying(total_varying);
|
point: old_point,
|
||||||
|
} => {
|
||||||
|
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;
|
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(
|
||||||
// "/home/rat/cow/godot/vertex_animation/mask.exr",
|
&(basename.clone() + "_mask.exr"),
|
||||||
// &pixels.collect(),
|
&mask,
|
||||||
// width as usize,
|
total_points as usize,
|
||||||
// )
|
)
|
||||||
// .unwrap();
|
.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 * (total_frames as usize));
|
||||||
|
|
||||||
let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap();
|
let pc = MDDSeekableFile::read(mdd_file.try_clone().unwrap()).unwrap();
|
||||||
|
let pc: Vec<_> = pc.collect();
|
||||||
|
let max_point_idx = pc
|
||||||
|
.iter()
|
||||||
|
.map(|frame| frame.as_ref().unwrap())
|
||||||
|
.map(|MDDFrame { point_idx, .. }| point_idx)
|
||||||
|
.cloned()
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
assert_eq!(max_point_idx + 1, total_points as usize);
|
||||||
|
|
||||||
|
let varying_count = 17;
|
||||||
|
let varied = mask_last.iter().filter(|x| match x {
|
||||||
|
Varying { count, .. } => *count > varying_count,
|
||||||
|
_ => false
|
||||||
|
}).count();
|
||||||
|
println!("There were {} points that varied more than {} times", varied, varying_count);
|
||||||
|
|
||||||
|
let pc = pc.into_iter();
|
||||||
|
|
||||||
pixels.extend(pc.filter_map(|frame| {
|
pixels.extend(pc.filter_map(|frame| {
|
||||||
let MDDFrame {
|
let MDDFrame {
|
||||||
|
@ -156,19 +215,22 @@ fn main() {
|
||||||
} = frame.unwrap();
|
} = frame.unwrap();
|
||||||
match mask_last[point_idx] {
|
match mask_last[point_idx] {
|
||||||
Unseen => panic!(),
|
Unseen => panic!(),
|
||||||
OneValue(_) => None,
|
OneValue { .. } => None,
|
||||||
Varying(_) => Some(point),
|
Varying { .. } => Some(point)
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
assert!(pixels.len() == total_varying * (total_frames as usize));
|
assert!(pixels.len() == total_varying * (total_frames as usize));
|
||||||
|
|
||||||
write_point_image(
|
write_point_image(
|
||||||
"/home/rat/cow/godot/vertex_animation/test.exr",
|
&(basename + ".exr"),
|
||||||
&pixels,
|
&pixels,
|
||||||
total_varying,
|
total_varying,
|
||||||
total_frames as usize,
|
total_frames as usize,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
println!("Total {} varying points", total_varying);
|
||||||
}
|
}
|
||||||
|
|
Binary file not shown.
Loading…
Reference in New Issue