From 69d3d9b4167fed4daa2800c196fbbf252a9784cf Mon Sep 17 00:00:00 2001 From: PikminGuts92 Date: Fri, 22 Sep 2023 21:26:09 -0400 Subject: [PATCH] Move custom gltf code to dedicated lib --- Cargo.toml | 2 + core/grim/Cargo.toml | 7 +- core/grim/src/model/export.rs | 267 +-------------------------------- core/grim_gltf/Cargo.toml | 11 ++ core/grim_gltf/src/lib.rs | 270 ++++++++++++++++++++++++++++++++++ 5 files changed, 288 insertions(+), 269 deletions(-) create mode 100644 core/grim_gltf/Cargo.toml create mode 100644 core/grim_gltf/src/lib.rs diff --git a/Cargo.toml b/Cargo.toml index 2d29387..8101186 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,6 +19,8 @@ edition = "2021" [workspace.dependencies] clap = { version = "4.3.12", features = ["derive"] } +gltf = { version = "=1.2.0", features = [ "extras", "import", "names" ] } +gltf-json = { version = "=1.2.0", features = [ "names" ] } grim = { path = "core/grim" } itertools = "0.11.0" lazy_static = "1.4.0" diff --git a/core/grim/Cargo.toml b/core/grim/Cargo.toml index 85257f0..d3066f3 100644 --- a/core/grim/Cargo.toml +++ b/core/grim/Cargo.toml @@ -10,8 +10,9 @@ bitstream-io = { version = "1.7.0", optional = true } ffmpeg-next = { version = "6.0.0", optional = true } flate2 = "1.0.26" fon = { version = "0.6.0", optional = true } -gltf = { version = "1.2.0", optional = true, features = [ "extras", "import", "names" ] } -gltf-json = { version = "1.2.0", optional = true, features = [ "names" ] } +gltf = { workspace = true, optional = true } +gltf-json = { workspace = true, optional = true } +grim_gltf = { path = "../grim_gltf", optional = true } grim_macros = { path = "../grim_macros" } grim_traits = { path = "../grim_traits" } half = { version = "2.3.1", default-features = false } @@ -38,7 +39,7 @@ rstest = "0.18.1" audio = [ "bitstream-io", "fon", "wav" ] audio_experimental = [ "ffmpeg-next" ] midi = [ "midly" ] -model = [ "gltf", "gltf-json", "nalgebra", "serde" ] +model = [ "gltf", "gltf-json", "grim_gltf", "nalgebra", "serde" ] python = [ "pyo3" ] [[bench]] diff --git a/core/grim/src/model/export.rs b/core/grim/src/model/export.rs index a59806b..5dca4a1 100644 --- a/core/grim/src/model/export.rs +++ b/core/grim/src/model/export.rs @@ -5,6 +5,7 @@ use crate::{Platform, SystemInfo}; use grim_traits::scene::Group; use itertools::*; use gltf_json as json; +use grim_gltf::*; use nalgebra as na; use serde::ser::Serialize; use std::collections::{BTreeMap, HashMap, HashSet}; @@ -2186,10 +2187,6 @@ impl GltfExporter { } } -fn align_to_multiple_of_four(n: usize) -> usize { - (n + 3) & !3 -} - fn decompose_trs(mat: na::Matrix4) -> (na::Vector3, na::UnitQuaternion, na::Vector3) { // Decompose matrix to T*R*S let translate = mat.column(3).xyz(); @@ -2234,268 +2231,6 @@ fn decompose_trs_with_milo_coords(mut mat: na::Matrix4) -> (na::Vector3)>, - accessors: Vec, -} - -impl AccessorBuilder { - fn new() -> AccessorBuilder { - AccessorBuilder { - working_data: Default::default(), - accessors: Vec::new() - } - } - - fn calc_stride(&self) -> usize { - N * T::size() - } - - fn update_buffer_view(&mut self, mut data: Vec) -> (usize, usize) { - let stride = self.calc_stride::(); - let data_size = data.len(); - let next_idx = self.working_data.len(); - - // Upsert buffer data - let (idx, buff) = self.working_data - .entry(stride) - .and_modify(|(_, b)| b.append(&mut data)) - .or_insert_with(|| (next_idx, data)); - - // Return index of updated buffer view + insert offset - (*idx, buff.len() - data_size) - } - - pub fn add_scalar, T: ComponentValue, U: IntoIterator>(&mut self, name: S, data: U) -> Option { - // Map to iter of single-item arrays (definitely hacky) - self.add_array(name, data.into_iter().map(|d| [d])) - } - - pub fn add_array, T: ComponentValue, U: IntoIterator, V: Into<[T; N]>>(&mut self, name: S, data: U) -> Option { - let comp_type = T::get_component_type(); - - let acc_type = match N { - 1 => json::accessor::Type::Scalar, - 2 => json::accessor::Type::Vec2, - 3 => json::accessor::Type::Vec3, - 4 => json::accessor::Type::Vec4, - 9 => json::accessor::Type::Mat3, - 16 => json::accessor::Type::Mat4, - _ => unimplemented!() - }; - - // Write to stream and find min/max values - let mut data_stream = Vec::new(); - let (count, min, max) = data - .into_iter() - .fold((0usize, [T::max(); N], [T::min(); N]), |(count, mut min, mut max), item| { - let mut i = 0; - for v in item.into() { - // Encode + append each value to master buffer - data_stream.append(&mut v.encode()); - - // Calc min + max values - min[i] = min[i].get_min(v); - max[i] = max[i].get_max(v); - - i += 1; - } - - (count + 1, min, max) - }); - - if count == 0 { - // If count is 0, don't bother adding - return None; - } - - // Update buffer views - let (buff_idx, buff_off) = self.update_buffer_view::(data_stream); - - let acc_index = self.accessors.len(); - - let (min_value, max_value) = Self::get_min_max_values( - &acc_type, - min, - max - ).unwrap(); - - // Create accessor - let accessor = json::Accessor { - buffer_view: Some(json::Index::new(buff_idx as u32)), - byte_offset: buff_off as u32, - count: count as u32, - component_type: json::validation::Checked::Valid(json::accessor::GenericComponentType(comp_type)), - extensions: None, - extras: None, - type_: json::validation::Checked::Valid(acc_type), - min: Some(min_value), - max: Some(max_value), - name: match name.into() { - s if !s.is_empty() => Some(s), - _ => None - }, - normalized: false, - sparse: None - }; - - self.accessors.push(accessor); - Some(acc_index) - } - - fn generate_buffer_views(&mut self) -> (Vec, Vec) { - // Get view info and sort by assigned index - let view_data = self.working_data - .drain() - .map(|(k, (idx, data))| (idx, k, data)) // (idx, stride, data) - .sorted_by(|(a, ..), (b, ..)| a.cmp(b)); - - let mut views = Vec::new(); - let mut all_data = Vec::new(); - - for (_idx, stride, mut data) in view_data { - // Pad buffer view if required - let padded_size = align_to_multiple_of_four(data.len()); - if padded_size > data.len() { - let diff_size = padded_size - data.len(); - data.append(&mut vec![0u8; diff_size]); - } - - let data_size = data.len(); - let data_offset = all_data.len(); - - // Move data from view to full buffer - all_data.append(&mut data); - - views.push(json::buffer::View { - name: None, - byte_length: data_size as u32, - byte_offset: Some(data_offset as u32), - byte_stride: match stride { - 64 => None, // Hacky way to disable writing stride for inverse bind transforms - s if s % 4 == 0 => Some(stride as u32), - _ => None // Don't encode if not multiple - }, - buffer: json::Index::new(0), - target: None, - extensions: None, - extras: None - }); - } - - (views, all_data) - } - - fn generate>(mut self, name: T) -> (Vec, Vec, json::Buffer, Vec) { - // Generate buffer views + final buffer blob - let (views, buffer_data) = self.generate_buffer_views(); - - // Create buffer json - let buffer = json::Buffer { - name: None, - byte_length: buffer_data.len() as u32, - uri: match name.into() { - s if !s.is_empty() => Some(s), - _ => None - }, - extensions: None, - extras: None - }; - - // Return everything - (self.accessors, - views, - buffer, - buffer_data) - } - - fn get_min_max_values(acc_type: &json::accessor::Type, min: [T; N], max: [T; N]) -> Option<(json::Value, json::Value)> { - let result = match acc_type { - json::accessor::Type::Scalar => ( - json::serialize::to_value([min.iter().fold(T::max(), |acc, m| acc.get_min(*m))]), - json::serialize::to_value([max.iter().fold(T::min(), |acc, m| acc.get_max(*m))]), - ), - _ => ( - json::serialize::to_value(min.to_vec()), - json::serialize::to_value(max.to_vec()), - ), - }; - - match result { - (Ok(min), Ok(max)) => Some((min, max)), - _ => None - } - } -} - -trait ComponentValue : Copy + Serialize { - fn min() -> Self; - fn max() -> Self; - - fn get_min(self, other: Self) -> Self; - fn get_max(self, other: Self) -> Self; - - fn encode(self) -> Vec; - fn get_component_type() -> json::accessor::ComponentType; - - fn size() -> usize { - std::mem::size_of::() - } -} - -impl ComponentValue for u16 { - fn min() -> Self { - u16::MIN - } - - fn max() -> Self { - u16::MAX - } - - fn get_min(self, other: Self) -> Self { - std::cmp::min(self, other) - } - - fn get_max(self, other: Self) -> Self { - std::cmp::max(self, other) - } - - fn encode(self) -> Vec { - self.to_le_bytes().to_vec() - } - - fn get_component_type() -> json::accessor::ComponentType { - json::accessor::ComponentType::U16 - } -} - -impl ComponentValue for f32 { - fn min() -> Self { - f32::MIN - } - - fn max() -> Self { - f32::MAX - } - - fn get_min(self, other: Self) -> Self { - f32::min(self, other) - } - - fn get_max(self, other: Self) -> Self { - f32::max(self, other) - } - - fn encode(self) -> Vec { - self.to_le_bytes().to_vec() - } - - fn get_component_type() -> json::accessor::ComponentType { - json::accessor::ComponentType::F32 - } -} - #[cfg(test)] mod tests { use rstest::*; diff --git a/core/grim_gltf/Cargo.toml b/core/grim_gltf/Cargo.toml new file mode 100644 index 0000000..ea6837e --- /dev/null +++ b/core/grim_gltf/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "grim_gltf" +version.workspace = true +authors.workspace = true +edition.workspace = true + +[dependencies] +gltf = { workspace = true } +gltf-json = { workspace = true } +itertools = { workspace = true } +serde = { workspace = true } \ No newline at end of file diff --git a/core/grim_gltf/src/lib.rs b/core/grim_gltf/src/lib.rs new file mode 100644 index 0000000..9a9484b --- /dev/null +++ b/core/grim_gltf/src/lib.rs @@ -0,0 +1,270 @@ +use gltf_json as json; +use itertools::*; +use serde::ser::Serialize; +use std::collections::{HashMap}; + +pub struct AccessorBuilder { + // Key = stride, Value = (idx, data) + working_data: HashMap)>, + accessors: Vec, +} + +impl AccessorBuilder { + pub fn new() -> AccessorBuilder { + AccessorBuilder { + working_data: Default::default(), + accessors: Vec::new() + } + } + + fn calc_stride(&self) -> usize { + N * T::size() + } + + fn update_buffer_view(&mut self, mut data: Vec) -> (usize, usize) { + let stride = self.calc_stride::(); + let data_size = data.len(); + let next_idx = self.working_data.len(); + + // Upsert buffer data + let (idx, buff) = self.working_data + .entry(stride) + .and_modify(|(_, b)| b.append(&mut data)) + .or_insert_with(|| (next_idx, data)); + + // Return index of updated buffer view + insert offset + (*idx, buff.len() - data_size) + } + + pub fn add_scalar, T: ComponentValue, U: IntoIterator>(&mut self, name: S, data: U) -> Option { + // Map to iter of single-item arrays (definitely hacky) + self.add_array(name, data.into_iter().map(|d| [d])) + } + + pub fn add_array, T: ComponentValue, U: IntoIterator, V: Into<[T; N]>>(&mut self, name: S, data: U) -> Option { + let comp_type = T::get_component_type(); + + let acc_type = match N { + 1 => json::accessor::Type::Scalar, + 2 => json::accessor::Type::Vec2, + 3 => json::accessor::Type::Vec3, + 4 => json::accessor::Type::Vec4, + 9 => json::accessor::Type::Mat3, + 16 => json::accessor::Type::Mat4, + _ => unimplemented!() + }; + + // Write to stream and find min/max values + let mut data_stream = Vec::new(); + let (count, min, max) = data + .into_iter() + .fold((0usize, [T::max(); N], [T::min(); N]), |(count, mut min, mut max), item| { + let mut i = 0; + for v in item.into() { + // Encode + append each value to master buffer + data_stream.append(&mut v.encode()); + + // Calc min + max values + min[i] = min[i].get_min(v); + max[i] = max[i].get_max(v); + + i += 1; + } + + (count + 1, min, max) + }); + + if count == 0 { + // If count is 0, don't bother adding + return None; + } + + // Update buffer views + let (buff_idx, buff_off) = self.update_buffer_view::(data_stream); + + let acc_index = self.accessors.len(); + + let (min_value, max_value) = Self::get_min_max_values( + &acc_type, + min, + max + ).unwrap(); + + // Create accessor + let accessor = json::Accessor { + buffer_view: Some(json::Index::new(buff_idx as u32)), + byte_offset: buff_off as u32, + count: count as u32, + component_type: json::validation::Checked::Valid(json::accessor::GenericComponentType(comp_type)), + extensions: None, + extras: None, + type_: json::validation::Checked::Valid(acc_type), + min: Some(min_value), + max: Some(max_value), + name: match name.into() { + s if !s.is_empty() => Some(s), + _ => None + }, + normalized: false, + sparse: None + }; + + self.accessors.push(accessor); + Some(acc_index) + } + + fn generate_buffer_views(&mut self) -> (Vec, Vec) { + // Get view info and sort by assigned index + let view_data = self.working_data + .drain() + .map(|(k, (idx, data))| (idx, k, data)) // (idx, stride, data) + .sorted_by(|(a, ..), (b, ..)| a.cmp(b)); + + let mut views = Vec::new(); + let mut all_data = Vec::new(); + + for (_idx, stride, mut data) in view_data { + // Pad buffer view if required + let padded_size = align_to_multiple_of_four(data.len()); + if padded_size > data.len() { + let diff_size = padded_size - data.len(); + data.append(&mut vec![0u8; diff_size]); + } + + let data_size = data.len(); + let data_offset = all_data.len(); + + // Move data from view to full buffer + all_data.append(&mut data); + + views.push(json::buffer::View { + name: None, + byte_length: data_size as u32, + byte_offset: Some(data_offset as u32), + byte_stride: match stride { + 64 => None, // Hacky way to disable writing stride for inverse bind transforms + s if s % 4 == 0 => Some(stride as u32), + _ => None // Don't encode if not multiple + }, + buffer: json::Index::new(0), + target: None, + extensions: None, + extras: None + }); + } + + (views, all_data) + } + + pub fn generate>(mut self, name: T) -> (Vec, Vec, json::Buffer, Vec) { + // Generate buffer views + final buffer blob + let (views, buffer_data) = self.generate_buffer_views(); + + // Create buffer json + let buffer = json::Buffer { + name: None, + byte_length: buffer_data.len() as u32, + uri: match name.into() { + s if !s.is_empty() => Some(s), + _ => None + }, + extensions: None, + extras: None + }; + + // Return everything + (self.accessors, + views, + buffer, + buffer_data) + } + + fn get_min_max_values(acc_type: &json::accessor::Type, min: [T; N], max: [T; N]) -> Option<(json::Value, json::Value)> { + let result = match acc_type { + json::accessor::Type::Scalar => ( + json::serialize::to_value([min.iter().fold(T::max(), |acc, m| acc.get_min(*m))]), + json::serialize::to_value([max.iter().fold(T::min(), |acc, m| acc.get_max(*m))]), + ), + _ => ( + json::serialize::to_value(min.to_vec()), + json::serialize::to_value(max.to_vec()), + ), + }; + + match result { + (Ok(min), Ok(max)) => Some((min, max)), + _ => None + } + } +} + +pub trait ComponentValue : Copy + Serialize { + fn min() -> Self; + fn max() -> Self; + + fn get_min(self, other: Self) -> Self; + fn get_max(self, other: Self) -> Self; + + fn encode(self) -> Vec; + fn get_component_type() -> json::accessor::ComponentType; + + fn size() -> usize { + std::mem::size_of::() + } +} + +impl ComponentValue for u16 { + fn min() -> Self { + u16::MIN + } + + fn max() -> Self { + u16::MAX + } + + fn get_min(self, other: Self) -> Self { + std::cmp::min(self, other) + } + + fn get_max(self, other: Self) -> Self { + std::cmp::max(self, other) + } + + fn encode(self) -> Vec { + self.to_le_bytes().to_vec() + } + + fn get_component_type() -> json::accessor::ComponentType { + json::accessor::ComponentType::U16 + } +} + +impl ComponentValue for f32 { + fn min() -> Self { + f32::MIN + } + + fn max() -> Self { + f32::MAX + } + + fn get_min(self, other: Self) -> Self { + f32::min(self, other) + } + + fn get_max(self, other: Self) -> Self { + f32::max(self, other) + } + + fn encode(self) -> Vec { + self.to_le_bytes().to_vec() + } + + fn get_component_type() -> json::accessor::ComponentType { + json::accessor::ComponentType::F32 + } +} + +fn align_to_multiple_of_four(n: usize) -> usize { + (n + 3) & !3 +} \ No newline at end of file