From 2ee6e8b6256b56f03e16444a2eb271f60c64c272 Mon Sep 17 00:00:00 2001 From: Matt McKegg Date: Wed, 7 Sep 2022 16:04:43 +1200 Subject: [PATCH] add support for parsing rOBJ and rCAM chunks --- .gitignore | 3 +- Cargo.toml | 2 + src/dot_vox_data.rs | 12 +- src/lib.rs | 49 ++++++++ src/parser.rs | 46 ++++++- src/resources/default_render_cameras.json | 112 ++++++++++++++++++ src/resources/default_render_objects.json | 101 ++++++++++++++++ .../placeholder-with-render-objects.vox | Bin 0 -> 23136 bytes 8 files changed, 319 insertions(+), 6 deletions(-) create mode 100644 src/resources/default_render_cameras.json create mode 100644 src/resources/default_render_objects.json create mode 100644 src/resources/placeholder-with-render-objects.vox diff --git a/.gitignore b/.gitignore index 887f9b1..bff2fac 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ target Cargo.lock .vscode .idea/ -*.iml \ No newline at end of file +*.iml +.DS_Store diff --git a/Cargo.toml b/Cargo.toml index caa13d0..469ca21 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,3 +20,5 @@ nom = { version = "^7", default-features = false, features = ["alloc"] } [dev-dependencies] avow = "0.2.0" env_logger = "^0.9" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" diff --git a/src/dot_vox_data.rs b/src/dot_vox_data.rs index 23fe48c..651794b 100644 --- a/src/dot_vox_data.rs +++ b/src/dot_vox_data.rs @@ -1,5 +1,9 @@ -use crate::{Layer, Material, Model, SceneNode}; -use std::io::{self, Write}; +use crate::{parser::RenderCamera, Layer, Material, Model, SceneNode}; +use std::{ + collections::HashMap, + io::{self, Write}, +}; +pub type Dict = HashMap; /// Container for .vox file data #[derive(Debug, PartialEq, Eq)] @@ -12,6 +16,10 @@ pub struct DotVoxData { pub palette: Vec, /// A Vec containing all the Materials set pub materials: Vec, + /// A Vec containing a collection Render Object dicts + pub render_objects: Vec, + // A Vec containing a collection of Render Cameras + pub render_cameras: Vec, /// Scene. The first node in this list is always the root node. pub scenes: Vec, /// Layers. Used by scene transform nodes. diff --git a/src/lib.rs b/src/lib.rs index fbab7d9..ccefb9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -87,6 +87,8 @@ use std::io::Read; /// } /// }) /// .collect(), +/// render_objects: vec![], +/// render_cameras: vec![], /// scenes: placeholder::SCENES.to_vec(), /// layers: placeholder::LAYERS.to_vec(), /// }); @@ -153,6 +155,8 @@ pub fn load(filename: &str) -> Result { /// } /// }) /// .collect(), +/// render_objects: vec![], +/// render_cameras: vec![], /// scenes: placeholder::SCENES.to_vec(), /// layers: placeholder::LAYERS.to_vec(), /// }); @@ -218,6 +222,8 @@ pub mod placeholder { mod tests { use std::collections::HashMap; + use crate::parser::RenderCamera; + use super::*; use avow::vec; @@ -239,6 +245,27 @@ mod tests { .collect(); } + lazy_static! { + static ref DEFAULT_RENDER_OBJECTS: Vec = serde_json::from_slice::>( + include_bytes!("resources/default_render_objects.json") + ) + .unwrap(); + } + + lazy_static! { + static ref DEFAULT_RENDER_CAMERAS: Vec = + serde_json::from_slice::>(include_bytes!( + "resources/default_render_cameras.json" + )) + .unwrap() + .iter() + .map(|(id, properties)| RenderCamera { + id: *id, + properties: properties.to_owned() + }) + .collect(); + } + fn placeholder( palette: Vec, materials: Vec, @@ -278,6 +305,8 @@ mod tests { }], palette, materials, + render_objects: vec![], + render_cameras: vec![], scenes, layers, } @@ -378,6 +407,26 @@ mod tests { ); } + #[test] + fn can_parse_vox_file_with_render_objects() { + let bytes = include_bytes!("resources/placeholder-with-render-objects.vox").to_vec(); + let result = super::parse_vox_file(&bytes); + assert!(result.is_ok()); + let (_, voxel_data) = result.unwrap(); + + vec::are_eq(voxel_data.render_objects, DEFAULT_RENDER_OBJECTS.to_vec()); + } + + #[test] + fn can_parse_vox_file_with_render_cameras() { + let bytes = include_bytes!("resources/placeholder-with-render-objects.vox").to_vec(); + let result = super::parse_vox_file(&bytes); + assert!(result.is_ok()); + let (_, voxel_data) = result.unwrap(); + + vec::are_eq(voxel_data.render_cameras, DEFAULT_RENDER_CAMERAS.to_vec()); + } + fn write_and_load(data: DotVoxData) { let mut buffer = Vec::new(); let write_result = data.write_vox(&mut buffer); diff --git a/src/parser.rs b/src/parser.rs index 6ee9491..fd4ade5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -24,6 +24,8 @@ pub enum Chunk { Pack(Model), Palette(Vec), Material(Material), + RenderObjects(Dict), + RenderCamera(RenderCamera), TransformNode(SceneTransform), GroupNode(SceneGroup), ShapeNode(SceneShape), @@ -41,6 +43,15 @@ pub struct Material { pub properties: Dict, } +/// A material used to render this model. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct RenderCamera { + /// The Cameras's ID. + pub id: u32, + /// Properties of the camera, mapped by property name. + pub properties: Dict, +} + // TODO: maybe material schemas? impl Material { /// The '_type' field, if present @@ -56,9 +67,10 @@ impl Material { pub fn weight(&self) -> Option { let w = self.get_f32("_weight"); - if let Some(w) = w && (w < 0.0 || w > 1.0) - { - debug!("_weight observed outside of range of [0..1]: {}", w); + if let Some(w) = w { + if w < 0.0 || w > 1.0 { + debug!("_weight observed outside of range of [0..1]: {}", w); + } } w @@ -183,6 +195,8 @@ fn map_chunk_to_data(version: u32, main: Chunk) -> DotVoxData { let mut models: Vec = vec![]; let mut palette_holder: Vec = DEFAULT_PALETTE.to_vec(); let mut materials: Vec = vec![]; + let mut render_objects: Vec = vec![]; + let mut render_cameras: Vec = vec![]; let mut scene: Vec = vec![]; let mut layers: Vec = Vec::new(); @@ -197,6 +211,8 @@ fn map_chunk_to_data(version: u32, main: Chunk) -> DotVoxData { Chunk::Pack(model) => models.push(model), Chunk::Palette(palette) => palette_holder = palette, Chunk::Material(material) => materials.push(material), + Chunk::RenderObjects(dict) => render_objects.push(dict), + Chunk::RenderCamera(camera) => render_cameras.push(camera), Chunk::TransformNode(scene_transform) => { scene.push(SceneNode::Transform { attributes: scene_transform.header.attributes, @@ -238,6 +254,8 @@ fn map_chunk_to_data(version: u32, main: Chunk) -> DotVoxData { models, palette: palette_holder, materials, + render_objects, + render_cameras, scenes: scene, layers, } @@ -247,6 +265,8 @@ fn map_chunk_to_data(version: u32, main: Chunk) -> DotVoxData { models: vec![], palette: vec![], materials: vec![], + render_objects: vec![], + render_cameras: vec![], scenes: vec![], layers: vec![], }, @@ -270,6 +290,8 @@ fn build_chunk(id: &str, chunk_content: &[u8], children_size: u32, child_content "PACK" => build_pack_chunk(chunk_content), "RGBA" => build_palette_chunk(chunk_content), "MATL" => build_material_chunk(chunk_content), + "rOBJ" => build_render_objects_chunk(chunk_content), + "rCAM" => build_render_camera_chunk(chunk_content), "nTRN" => build_scene_transform_chunk(chunk_content), "nGRP" => build_scene_group_chunk(chunk_content), "nSHP" => build_scene_shape_chunk(chunk_content), @@ -305,6 +327,19 @@ fn build_material_chunk(chunk_content: &[u8]) -> Chunk { } Chunk::Invalid(chunk_content.to_vec()) } +fn build_render_camera_chunk(chunk_content: &[u8]) -> Chunk { + if let Ok((_, camera)) = parse_render_camera(chunk_content) { + return Chunk::RenderCamera(camera); + } + Chunk::Invalid(chunk_content.to_vec()) +} + +fn build_render_objects_chunk(chunk_content: &[u8]) -> Chunk { + if let Ok((_, dict)) = parse_dict(chunk_content) { + return Chunk::RenderObjects(dict); + } + Chunk::Invalid(chunk_content.to_vec()) +} fn build_palette_chunk(chunk_content: &[u8]) -> Chunk { if let Ok((_, palette)) = palette::extract_palette(chunk_content) { @@ -374,6 +409,11 @@ pub fn parse_material(i: &[u8]) -> IResult<&[u8], Material> { Ok((i, Material { id, properties })) } +pub fn parse_render_camera(i: &[u8]) -> IResult<&[u8], RenderCamera> { + let (i, (id, properties)) = pair(le_u32, parse_dict)(i)?; + Ok((i, RenderCamera { id, properties })) +} + pub(crate) fn parse_dict(i: &[u8]) -> IResult<&[u8], Dict> { let (i, n) = le_u32(i)?; diff --git a/src/resources/default_render_cameras.json b/src/resources/default_render_cameras.json new file mode 100644 index 0000000..dafc88c --- /dev/null +++ b/src/resources/default_render_cameras.json @@ -0,0 +1,112 @@ +[ + [ + 0, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 1, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 2, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 3, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 4, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 5, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 6, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 7, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 8, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ], + [ + 9, + { + "_angle": "0 0 0", + "_focus": "0 0 0", + "_fov": "45", + "_frustum": "0.414214", + "_mode": "pers", + "_radius": "0" + } + ] +] \ No newline at end of file diff --git a/src/resources/default_render_objects.json b/src/resources/default_render_objects.json new file mode 100644 index 0000000..35f6585 --- /dev/null +++ b/src/resources/default_render_objects.json @@ -0,0 +1,101 @@ +[ + { + "_type": "_bounce", + "_diffuse": "2", + "_energy": "3", + "_scatter": "5", + "_specular": "5" + }, + { + "_type": "_env", + "_mode": "0" + }, + { + "_type": "_inf", + "_angle": "50 50", + "_area": "0.07", + "_disk": "0", + "_i": "0.7", + "_k": "255 255 255" + }, + { + "_type": "_uni", + "_i": "0.7", + "_k": "255 255 255" + }, + { + "_type": "_ibl", + "_i": "1", + "_path": "HDR_041_Path_Env.hdr", + "_rot": "0" + }, + { + "_type": "_atm", + "_mie_d": "0.4", + "_mie_g": "0.85", + "_mie_k": "255 255 255", + "_o3_d": "0", + "_o3_k": "105 255 110", + "_ray_d": "0.4", + "_ray_k": "45 104 255" + }, + { + "_type": "_fog_uni", + "_d": "0", + "_g": "0", + "_k": "255 255 255" + }, + { + "_type": "_lens", + "_aperture": "0.25", + "_blade_n": "0", + "_blade_r": "0", + "_fov": "45", + "_proj": "0" + }, + { + "_type": "_film", + "_aces": "1", + "_expo": "1", + "_gam": "2.2", + "_vig": "0" + }, + { + "_type": "_bloom", + "_aspect": "0", + "_mix": "0.5", + "_scale": "0", + "_threshold": "1" + }, + { + "_type": "_ground", + "_color": "80 80 80", + "_hor": "0.1" + }, + { + "_type": "_bg", + "_color": "0 0 0" + }, + { + "_type": "_edge", + "_color": "0 0 0", + "_width": "0.2" + }, + { + "_type": "_grid", + "_color": "0 0 0", + "_display": "0", + "_spacing": "1", + "_width": "0.02" + }, + { + "_type": "_setting", + "_bg_a": "0", + "_bg_c": "0", + "_cell": "1", + "_edge": "0", + "_grid": "0", + "_ground": "1", + "_scale": "1 1 1" + } +] \ No newline at end of file diff --git a/src/resources/placeholder-with-render-objects.vox b/src/resources/placeholder-with-render-objects.vox new file mode 100644 index 0000000000000000000000000000000000000000..cf14a1086d45fe48c8e60d6a415100c2c799a6f5 GIT binary patch literal 23136 zcmc(n2XrG>6^5TvHjQQoy$J^h)r>S!yaWPx<7Ib~ZFtvVcS#t=(mYvNNh3y*y-N#( zW=Nq2h8jAU-iztI484V7O6U+w??wFgj-+)vhr{7NXyj|n+`03;Z{B+|E8BbKf;|T^ zR~ch=jqlivH?#AQG5dBL+Il_Nz;fJ*IB@XLj?*=3%n9lHwL?Q@(~udu&I}D*Q{O+i z`!?PEy|*E(bh?YI%TnJqx%Wots_EtAUD^`H-Mjl_TbAPyf{=Bo>(zR#6S3uFoJ_X9 zZ~NY!nv~1ZL(BTk@q?4Ml9lcv`{ZD+?$-j@aHN5iRgzp+m0epUitDO!YpcX)U6s4G zO1gAiReo)iSg)%ptgYI#QPo*%t4`XeYD}sow{02kjj?QZIte01ScIr!L?o5qTY`Lo ze1d#}yp+Yr$5@GxkCBg&mqRV`7I}*ei@Zf%a4zyL@-Ff&5-#%i>KNn=@&JZ1ti z2zkW_i*OOhV@HB-349XxB=AY#qk8a3;FG{7flmS-RfbOjp9DS$d=mKRX}~9ePXeC= zJ_&r})Drk4@JZm4z$bwZ?9?R2_ZU7gd}8=$7r-ZmK#ay>fMY ztKbvECx%Z9pBO&ceej9l6T>HlPYfUF(ilE5d}8>-@QLB0-K;$$Hu7>2$fJKOd@Ot{ zd@Ot{d~`VAW8q^VVbP!kT!#n;Srk}oSom1@=&-@Z!pFkL!pFi#hSI{v!pFkL!pFi# zhgU~QdPZ#Ig*<#*__*+K;p4)`g^x}H__*+K;p4)`g^x}R__*+K;p0NYMWZ@h0CRDm ziy{{Z7e4qxAP*lGJ}!J*_#lmt86%@4JtH=F!NJvr{VWjv7Qq?*3s|5Bvp@$} zpo1(B{`}7A?1!*G7qUPXu|N-Hfjk!IVJwi(0!_0(MHZ;U0tGCPWr4~p&sX-Ivp{cPf!@dh zy@>^SGYj+<7U-=k(A!v`x3fU+V1eGr0=^ z3-l!x=*uk7S6HC0vOr&BfxgZHeS-zMh6Va23-m1(=-VvNcUYh$7U;Vy(DzuN@3TNZ zV1ce>fquvW{fGtnF$?q)7U-ue(9c+)pR+)}V1a(g0$s-f{fY%zW`Taq0{w;s`Yj9e zI~M5oEYKfVpg*!ee`10D%mV#|1^O!s^fwmh?<~+iSfGEhK>uQaI{z!^d;oMAN}pJF zA~wAL_OV6B$w8$P@ZA}83elb|=Uu4G^eISv}xiHV!P@` zk|~_5nWEon1!+PL59&cwJ}L#Sv~vblWD$dYJ0ju*^;u2S!czM9Iw`M6EtXA))#a7C z)w8G^k6q5_m~I?C4I8;aA=3>x%=7EzY9I#}oJ_${iXR1j_w2_sQK~c#i=!irj$uU> zA+K(N})m+wN%Xq9=y`;?*!)h4G$!~Tt z`lU_J^qz92pfeS-=E4=KH(fpkk<*Z;S8C6l-92Pg+j^owshpmuw%k8RIdhd#tNXMy z?WK#l6sgjKrI&Q29935M_bEDx&4ypB)O8@T$FS+hJ2@@EHQ%WEdTUbMOS=up&VEBF zENHa=ThVPgq=jpS`MRY(I+AisHiMTQTnwsJDac0Wj_+!BZRAZ3 z&wR#bL5vQZJmaWo{x>%%~rc6>&|FCo6lwQI^zAy zWu<@^4OBp`;DHLr{2i!(49-9WWbzGEKpG#YfLuod6_AT{paSww=>J_{_n!S*=h3h% O8H8L3lEd4;mVX1Jpi?^l literal 0 HcmV?d00001