diff --git a/lib/rbx_loader/src/loader.rs b/lib/rbx_loader/src/loader.rs index 9cfa8ab5d..ae41ec1ce 100644 --- a/lib/rbx_loader/src/loader.rs +++ b/lib/rbx_loader/src/loader.rs @@ -4,7 +4,7 @@ use strafesnet_common::model::Mesh; use strafesnet_deferred_loader::{loader::Loader,texture::Texture}; use crate::data::RobloxMeshBytes; -use crate::rbx::RobloxFaceTextureDescription; +use crate::rbx::RobloxPartDescription; fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{ let mut file=std::fs::File::open(path)?; @@ -104,7 +104,7 @@ pub enum MeshType<'a>{ mesh_data:&'a [u8], physics_data:&'a [u8], size_float_bits:[u32;3], - part_texture_description:[Option<RobloxFaceTextureDescription>;6], + part_texture_description:RobloxPartDescription, }, } #[derive(Hash,Eq,PartialEq)] @@ -124,7 +124,7 @@ impl MeshIndex<'_>{ mesh_data:&'a [u8], physics_data:&'a [u8], size:&rbx_dom_weak::types::Vector3, - part_texture_description:crate::rbx::RobloxPartDescription, + part_texture_description:RobloxPartDescription, )->MeshIndex<'a>{ MeshIndex{ mesh_type:MeshType::Union{ diff --git a/lib/rbx_loader/src/primitives.rs b/lib/rbx_loader/src/primitives.rs index 00d6d153d..c2d3319d8 100644 --- a/lib/rbx_loader/src/primitives.rs +++ b/lib/rbx_loader/src/primitives.rs @@ -1,3 +1,4 @@ +use crate::rbx::{RobloxPartDescription,RobloxWedgeDescription,RobloxCornerWedgeDescription}; use strafesnet_common::model::{Color4,TextureCoordinate,Mesh,IndexedGraphicsGroup,IndexedPhysicsGroup,IndexedVertex,PolygonGroupId,PolygonGroup,PolygonList,PositionId,TextureCoordinateId,NormalId,ColorId,VertexId,RenderConfigId}; use strafesnet_common::integer::{vec3,Planar64Vec3}; @@ -9,7 +10,22 @@ pub enum Primitives{ Wedge, CornerWedge, } -#[derive(Hash,PartialEq,Eq)] +#[derive(Debug)] +pub struct PrimitivesError; +impl TryFrom<u32> for Primitives{ + type Error=PrimitivesError; + fn try_from(value:u32)->Result<Self,Self::Error>{ + match value{ + 0=>Ok(Primitives::Sphere), + 1=>Ok(Primitives::Cube), + 2=>Ok(Primitives::Cylinder), + 3=>Ok(Primitives::Wedge), + 4=>Ok(Primitives::CornerWedge), + _=>Err(PrimitivesError), + } + } +} +#[derive(Clone,Copy,Hash,PartialEq,Eq)] pub enum CubeFace{ Right, Top, @@ -18,6 +34,22 @@ pub enum CubeFace{ Bottom, Front, } +#[derive(Debug)] +pub struct CubeFaceError; +impl TryFrom<u32> for CubeFace{ + type Error=CubeFaceError; + fn try_from(value:u32)->Result<Self,Self::Error>{ + match value{ + 0=>Ok(CubeFace::Right), + 1=>Ok(CubeFace::Top), + 2=>Ok(CubeFace::Back), + 3=>Ok(CubeFace::Left), + 4=>Ok(CubeFace::Bottom), + 5=>Ok(CubeFace::Front), + _=>Err(CubeFaceError), + } + } +} const CUBE_DEFAULT_TEXTURE_COORDS:[TextureCoordinate;4]=[ TextureCoordinate::new(0.0,0.0), TextureCoordinate::new(1.0,0.0), @@ -43,50 +75,34 @@ const CUBE_DEFAULT_NORMALS:[Planar64Vec3;6]=[ vec3::int( 0, 0,-1),//CubeFace::Front ]; -#[derive(Hash,PartialEq,Eq)] -pub enum WedgeFace{ - Right, - TopFront, - Back, - Left, - Bottom, -} -#[derive(Hash,PartialEq,Eq)] -pub enum CornerWedgeFace{ - Right, - TopBack, - TopLeft, - Bottom, - Front, -} -#[derive(Default)] -pub struct CubeFaceDescription([Option<FaceDescription>;6]); +pub struct CubeFaceDescription([FaceDescription;Self::FACES]); impl CubeFaceDescription{ - pub fn insert(&mut self,index:CubeFace,value:FaceDescription){ - self.0[index as usize]=Some(value); - } - pub fn pairs(self)->impl Iterator<Item=(usize,FaceDescription)>{ - self.0.into_iter().enumerate().filter_map(|(i,v)|v.map(|u|(i,u))) + pub const FACES:usize=6; + pub fn new(RobloxPartDescription(part_description):RobloxPartDescription,textureless_render_id:RenderConfigId)->Self{ + Self(part_description.map(|face_description|match face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>FaceDescription::new_with_render_id(textureless_render_id), + })) } } -#[derive(Default)] -pub struct WedgeFaceDescription([Option<FaceDescription>;5]); +pub struct WedgeFaceDescription([FaceDescription;Self::FACES]); impl WedgeFaceDescription{ - pub fn insert(&mut self,index:WedgeFace,value:FaceDescription){ - self.0[index as usize]=Some(value); - } - pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{ - self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u))) + pub const FACES:usize=5; + pub fn new(RobloxWedgeDescription(part_description):RobloxWedgeDescription,textureless_render_id:RenderConfigId)->Self{ + Self(part_description.map(|face_description|match face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>FaceDescription::new_with_render_id(textureless_render_id), + })) } } -#[derive(Default)] -pub struct CornerWedgeFaceDescription([Option<FaceDescription>;5]); +pub struct CornerWedgeFaceDescription([FaceDescription;Self::FACES]); impl CornerWedgeFaceDescription{ - pub fn insert(&mut self,index:CornerWedgeFace,value:FaceDescription){ - self.0[index as usize]=Some(value); - } - pub fn pairs(self)->std::iter::FilterMap<std::iter::Enumerate<std::array::IntoIter<Option<FaceDescription>,5>>,impl FnMut((usize,Option<FaceDescription>))->Option<(usize,FaceDescription)>>{ - self.0.into_iter().enumerate().filter_map(|v|v.1.map(|u|(v.0,u))) + pub const FACES:usize=5; + pub fn new(RobloxCornerWedgeDescription(part_description):RobloxCornerWedgeDescription,textureless_render_id:RenderConfigId)->Self{ + Self(part_description.map(|face_description|match face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>FaceDescription::new_with_render_id(textureless_render_id), + })) } } @@ -97,7 +113,7 @@ pub struct FaceDescription{ pub color:Color4, } impl FaceDescription{ - pub fn new_with_render_id(render:RenderConfigId)->Self { + pub fn new_with_render_id(render:RenderConfigId)->Self{ Self{ render, transform:glam::Affine2::IDENTITY, @@ -105,7 +121,7 @@ impl FaceDescription{ } } } -pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{ +pub fn generate_partial_unit_cube(CubeFaceDescription(face_descriptions):CubeFaceDescription)->Mesh{ const CUBE_DEFAULT_POLYS:[[[u32;2];4];6]=[ // right (1, 0, 0) [ @@ -160,7 +176,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{ let mut physics_group=IndexedPhysicsGroup::default(); let mut transforms=Vec::new(); //note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices. - for (face_id,face_description) in face_descriptions.pairs(){ + for (face_id,face_description) in face_descriptions.into_iter().enumerate(){ //assume that scanning short lists is faster than hashing. let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){ transform_index @@ -227,7 +243,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{ } } //don't think too hard about the copy paste because this is all going into the map tool eventually... -pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh{ +pub fn generate_partial_unit_wedge(WedgeFaceDescription(face_descriptions):WedgeFaceDescription)->Mesh{ const WEDGE_DEFAULT_POLYS:[&[[u32;2]];5]=[ // right (1, 0, 0) &[ @@ -280,7 +296,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh let mut physics_group=IndexedPhysicsGroup::default(); let mut transforms=Vec::new(); //note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices. - for (face_id,face_description) in face_descriptions.pairs(){ + for (face_id,face_description) in face_descriptions.into_iter().enumerate(){ //assume that scanning short lists is faster than hashing. let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){ transform_index @@ -347,7 +363,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh } } -pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescription)->Mesh{ +pub fn generate_partial_unit_cornerwedge(CornerWedgeFaceDescription(face_descriptions):CornerWedgeFaceDescription)->Mesh{ const CORNERWEDGE_DEFAULT_POLYS:[&[[u32;2]];5]=[ // right (1, 0, 0) &[ @@ -398,7 +414,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri let mut physics_group=IndexedPhysicsGroup::default(); let mut transforms=Vec::new(); //note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices. - for (face_id,face_description) in face_descriptions.pairs(){ + for (face_id,face_description) in face_descriptions.into_iter().enumerate(){ //assume that scanning short lists is faster than hashing. let transform_index=if let Some(transform_index)=transforms.iter().position(|&transform|transform==face_description.transform){ transform_index diff --git a/lib/rbx_loader/src/rbx.rs b/lib/rbx_loader/src/rbx.rs index cb081aecb..8f2a14b06 100644 --- a/lib/rbx_loader/src/rbx.rs +++ b/lib/rbx_loader/src/rbx.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use crate::loader::MeshIndex; -use crate::primitives; +use crate::primitives::{self,CubeFace,CubeFaceDescription,WedgeFaceDescription,CornerWedgeFaceDescription,FaceDescription,Primitives}; use strafesnet_common::aabb::Aabb; use strafesnet_common::map; use strafesnet_common::model; @@ -347,17 +347,40 @@ impl RobloxFaceTextureDescription{ transform:self.transform.to_bits(), } } - pub fn to_face_description(&self)->primitives::FaceDescription{ - primitives::FaceDescription{ + pub fn to_face_description(&self)->FaceDescription{ + FaceDescription{ render:self.render, transform:self.transform.affine(), color:self.color, } } } -pub type RobloxPartDescription=[Option<RobloxFaceTextureDescription>;6]; -type RobloxWedgeDescription=[Option<RobloxFaceTextureDescription>;5]; -type RobloxCornerWedgeDescription=[Option<RobloxFaceTextureDescription>;5]; +macro_rules! impl_description_index{ + ($description:ident,$index:ty)=>{ + impl core::ops::Index<$index> for $description{ + type Output=Option<RobloxFaceTextureDescription>; + fn index(&self,index:$index)->&Self::Output{ + &self.0[index as usize] + } + } + impl core::ops::IndexMut<$index> for $description{ + fn index_mut(&mut self,index:$index)->&mut Self::Output{ + &mut self.0[index as usize] + } + } + }; +} + +#[derive(Clone,Default,Eq,Hash,PartialEq)] +pub struct RobloxPartDescription(pub(crate)[Option<RobloxFaceTextureDescription>;6]); +impl_description_index!(RobloxPartDescription,CubeFace); + +#[derive(Clone,Default,Eq,Hash,PartialEq)] +pub struct RobloxWedgeDescription(pub(crate)[Option<RobloxFaceTextureDescription>;5]); + +#[derive(Clone,Default,Eq,Hash,PartialEq)] +pub struct RobloxCornerWedgeDescription(pub(crate)[Option<RobloxFaceTextureDescription>;5]); + #[derive(Clone,Eq,Hash,PartialEq)] enum RobloxBasePartDescription{ Sphere(RobloxPartDescription), @@ -374,7 +397,7 @@ fn get_texture_description<'a>( size:&rbx_dom_weak::types::Vector3, )->RobloxPartDescription{ //use the biggest one and cut it down later... - let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None]; + let mut part_texture_description=RobloxPartDescription::default(); temp_objects.clear(); recursive_collect_superclass(temp_objects,&dom,object,"Decal"); for &mut decal_ref in temp_objects{ @@ -391,8 +414,7 @@ fn get_texture_description<'a>( decal.properties.get("Transparency"), ) { let render_id=render_config_deferred_loader.acquire_render_config_id(Some(content.as_ref())); - let normal_id=normalid.to_u32(); - if normal_id<6{ + if let Ok(cube_face)=normalid.to_u32().try_into(){ let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{ //generate tranform if let ( @@ -407,14 +429,13 @@ fn get_texture_description<'a>( decal.properties.get("StudsPerTileV"), ) { - let (size_u,size_v)=match normal_id{ - 0=>(size.z,size.y),//right - 1=>(size.x,size.z),//top - 2=>(size.x,size.y),//back - 3=>(size.z,size.y),//left - 4=>(size.x,size.z),//bottom - 5=>(size.x,size.y),//front - _=>unreachable!(), + let (size_u,size_v)=match cube_face{ + CubeFace::Right=>(size.z,size.y),//right + CubeFace::Top=>(size.x,size.z),//top + CubeFace::Back=>(size.x,size.y),//back + CubeFace::Left=>(size.z,size.y),//left + CubeFace::Bottom=>(size.x,size.z),//bottom + CubeFace::Front=>(size.x,size.y),//front }; ( glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency), @@ -433,13 +454,13 @@ fn get_texture_description<'a>( }else{ (glam::Vec4::ONE,RobloxTextureTransform::identity()) }; - part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{ + part_texture_description[cube_face]=Some(RobloxFaceTextureDescription{ render:render_id, color:roblox_texture_color, transform:roblox_texture_transform, }); }else{ - println!("NormalId={} is invalid",normal_id); + println!("NormalId is invalid"); } } } @@ -447,7 +468,7 @@ fn get_texture_description<'a>( part_texture_description } enum Shape{ - Primitive(primitives::Primitives), + Primitive(Primitives), MeshPart, PhysicsData, } @@ -533,25 +554,18 @@ pub fn convert<'a>( //TODO: also detect "CylinderMesh" etc here let shape=match object.class.as_str(){ "Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){ - Shape::Primitive(match shape.to_u32(){ - 0=>primitives::Primitives::Sphere, - 1=>primitives::Primitives::Cube, - 2=>primitives::Primitives::Cylinder, - 3=>primitives::Primitives::Wedge, - 4=>primitives::Primitives::CornerWedge, - other=>panic!("Funky roblox PartType={};",other), - }) + Shape::Primitive(shape.to_u32().try_into().expect("Funky roblox PartType")) }else{ panic!("Part has no Shape!"); }, - "TrussPart"=>Shape::Primitive(primitives::Primitives::Cube), - "WedgePart"=>Shape::Primitive(primitives::Primitives::Wedge), - "CornerWedgePart"=>Shape::Primitive(primitives::Primitives::CornerWedge), + "TrussPart"=>Shape::Primitive(Primitives::Cube), + "WedgePart"=>Shape::Primitive(Primitives::Wedge), + "CornerWedgePart"=>Shape::Primitive(Primitives::CornerWedge), "MeshPart"=>Shape::MeshPart, "UnionOperation"=>Shape::PhysicsData, _=>{ println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class); - Shape::Primitive(primitives::Primitives::Cube) + Shape::Primitive(Primitives::Cube) } }; @@ -560,34 +574,34 @@ pub fn convert<'a>( //TODO: TAB TAB let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size); //obscure rust syntax "slice pattern" - let [ + let RobloxPartDescription([ f0,//Cube::Right f1,//Cube::Top f2,//Cube::Back f3,//Cube::Left f4,//Cube::Bottom f5,//Cube::Front - ]=part_texture_description; + ])=part_texture_description; let basepart_description=match primitive_shape{ - primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere([f0,f1,f2,f3,f4,f5]), - primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]), - primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder([f0,f1,f2,f3,f4,f5]), + Primitives::Sphere=>RobloxBasePartDescription::Sphere(RobloxPartDescription([f0,f1,f2,f3,f4,f5])), + Primitives::Cube=>RobloxBasePartDescription::Part(RobloxPartDescription([f0,f1,f2,f3,f4,f5])), + Primitives::Cylinder=>RobloxBasePartDescription::Cylinder(RobloxPartDescription([f0,f1,f2,f3,f4,f5])), //use front face texture first and use top face texture as a fallback - primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([ + Primitives::Wedge=>RobloxBasePartDescription::Wedge(RobloxWedgeDescription([ f0,//Cube::Right->Wedge::Right f5.or(f1),//Cube::Front|Cube::Top->Wedge::TopFront f2,//Cube::Back->Wedge::Back f3,//Cube::Left->Wedge::Left f4,//Cube::Bottom->Wedge::Bottom - ]), + ])), //TODO: fix Left+Back texture coordinates to match roblox when not overwridden by Top - primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([ + Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge(RobloxCornerWedgeDescription([ f0,//Cube::Right->CornerWedge::Right f2.or(f1.clone()),//Cube::Back|Cube::Top->CornerWedge::TopBack f3.or(f1),//Cube::Left|Cube::Top->CornerWedge::TopLeft f4,//Cube::Bottom->CornerWedge::Bottom f5,//Cube::Front->CornerWedge::Front - ]), + ])), }; //make new model if unit cube has not been created before let mesh_id=if let Some(&mesh_id)=mesh_id_from_description.get(&basepart_description){ @@ -599,64 +613,9 @@ pub fn convert<'a>( let mesh=match basepart_description{ RobloxBasePartDescription::Sphere(part_texture_description) |RobloxBasePartDescription::Cylinder(part_texture_description) - |RobloxBasePartDescription::Part(part_texture_description)=>{ - let mut cube_face_description=primitives::CubeFaceDescription::default(); - for (face_id,roblox_face_description) in part_texture_description.iter().enumerate(){ - cube_face_description.insert( - match face_id{ - 0=>primitives::CubeFace::Right, - 1=>primitives::CubeFace::Top, - 2=>primitives::CubeFace::Back, - 3=>primitives::CubeFace::Left, - 4=>primitives::CubeFace::Bottom, - 5=>primitives::CubeFace::Front, - _=>unreachable!(), - }, - match roblox_face_description{ - Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), - None=>primitives::FaceDescription::new_with_render_id(textureless_render_group), - }); - } - primitives::generate_partial_unit_cube(cube_face_description) - }, - RobloxBasePartDescription::Wedge(wedge_texture_description)=>{ - let mut wedge_face_description=primitives::WedgeFaceDescription::default(); - for (face_id,roblox_face_description) in wedge_texture_description.iter().enumerate(){ - wedge_face_description.insert( - match face_id{ - 0=>primitives::WedgeFace::Right, - 1=>primitives::WedgeFace::TopFront, - 2=>primitives::WedgeFace::Back, - 3=>primitives::WedgeFace::Left, - 4=>primitives::WedgeFace::Bottom, - _=>unreachable!(), - }, - match roblox_face_description{ - Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), - None=>primitives::FaceDescription::new_with_render_id(textureless_render_group), - }); - } - primitives::generate_partial_unit_wedge(wedge_face_description) - }, - RobloxBasePartDescription::CornerWedge(cornerwedge_texture_description)=>{ - let mut cornerwedge_face_description=primitives::CornerWedgeFaceDescription::default(); - for (face_id,roblox_face_description) in cornerwedge_texture_description.iter().enumerate(){ - cornerwedge_face_description.insert( - match face_id{ - 0=>primitives::CornerWedgeFace::Right, - 1=>primitives::CornerWedgeFace::TopBack, - 2=>primitives::CornerWedgeFace::TopLeft, - 3=>primitives::CornerWedgeFace::Bottom, - 4=>primitives::CornerWedgeFace::Front, - _=>unreachable!(), - }, - match roblox_face_description{ - Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), - None=>primitives::FaceDescription::new_with_render_id(textureless_render_group), - }); - } - primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description) - }, + |RobloxBasePartDescription::Part(part_texture_description)=>primitives::generate_partial_unit_cube(CubeFaceDescription::new(part_texture_description,textureless_render_group)), + RobloxBasePartDescription::Wedge(wedge_texture_description)=>primitives::generate_partial_unit_wedge(WedgeFaceDescription::new(wedge_texture_description,textureless_render_group)), + RobloxBasePartDescription::CornerWedge(cornerwedge_texture_description)=>primitives::generate_partial_unit_cornerwedge(CornerWedgeFaceDescription::new(cornerwedge_texture_description,textureless_render_group)), }; primitive_meshes.push(mesh); mesh_id @@ -768,7 +727,7 @@ fn acquire_union_id_from_render_config_id<'a>( let union_id=model::MeshId::new(primitive_meshes.len() as u32); let mut union_clone=union_with_aabb.mesh.clone(); //set the render groups - for (graphics_group,maybe_face_texture_description) in union_clone.graphics_groups.iter_mut().zip(part_texture_description){ + for (graphics_group,maybe_face_texture_description) in union_clone.graphics_groups.iter_mut().zip(part_texture_description.0){ if let Some(face_texture_description)=maybe_face_texture_description{ graphics_group.render=face_texture_description.render; } diff --git a/lib/rbx_loader/src/union.rs b/lib/rbx_loader/src/union.rs index 7c9bb0925..059f45011 100644 --- a/lib/rbx_loader/src/union.rs +++ b/lib/rbx_loader/src/union.rs @@ -56,7 +56,7 @@ pub fn convert( roblox_physics_data:&[u8], roblox_mesh_data:&[u8], size:glam::Vec3, - part_texture_description:crate::rbx::RobloxPartDescription, + crate::rbx::RobloxPartDescription(part_texture_description):crate::rbx::RobloxPartDescription, )->Result<model::Mesh,Error>{ const NORMAL_FACES:usize=6; let mut polygon_groups_normal_id=vec![Vec::new();NORMAL_FACES];