diff --git a/src/load_roblox.rs b/src/load_roblox.rs index fdde353..b74df97 100644 --- a/src/load_roblox.rs +++ b/src/load_roblox.rs @@ -86,12 +86,30 @@ impl std::hash::Hash for RobloxFaceTextureDescription { } } } +impl RobloxFaceTextureDescription{ + fn to_face_description(&self)->primitives::FaceDescription{ + primitives::FaceDescription{ + texture:Some(self.texture), + transform:glam::Affine2::from_translation( + glam::vec2(self.transform.offset_u,self.transform.offset_v) + ) + *glam::Affine2::from_scale( + glam::vec2(self.transform.scale_u,self.transform.scale_v) + ), + color:self.color, + } + } +} type RobloxPartDescription=[Option;6]; -//type RobloxWedgeDescription=[Option;5]; +type RobloxWedgeDescription=[Option;5]; +type RobloxCornerWedgeDescription=[Option;4]; #[derive(Clone,Eq,Hash,PartialEq)] enum RobloxBasePartDescription{ + Sphere, Part(RobloxPartDescription), - //Wedge(RobloxWedgeDescription), + Cylinder, + Wedge(RobloxWedgeDescription), + CornerWedge(RobloxCornerWedgeDescription), } pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(IndexedModelInstances,glam::Vec3), Box>{ //IndexedModelInstances includes textures @@ -139,13 +157,55 @@ pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Inde spawn_point=model_transform.transform_point3(glam::Vec3::Y)+glam::vec3(0.0,2.5,0.0); println!("Found MapStart{:?}",spawn_point); } - if *transparency==1.0||shape.to_u32()!=1 { + if *transparency==1.0 { continue; } + + let shape=match shape.to_u32(){ + 0=>primitives::Primitives::Sphere, + 1=>primitives::Primitives::Cube, + 2=>primitives::Primitives::Cylinder, + 3=>primitives::Primitives::Wedge, + 4=>primitives::Primitives::CornerWedge, + _=>panic!("funky roblox PartType={}",shape.to_u32()), + }; + + //TODO: also detect "CylinderMesh" etc here + let mut face_map=std::collections::HashMap::new(); + match shape{ + primitives::Primitives::Cube => { + face_map.insert(0,0);//Right + face_map.insert(1,1);//Top + face_map.insert(2,2);//Back + face_map.insert(3,3);//Left + face_map.insert(4,4);//Bottom + face_map.insert(5,5);//Front + }, + primitives::Primitives::Wedge => { + face_map.insert(0,0);//Right + face_map.insert(1,1);//Top -> TopFront (some surf maps put surf textures on the Top face) + face_map.insert(2,1);//Front -> TopFront + face_map.insert(3,2);//Back + face_map.insert(4,3);//Left + face_map.insert(5,4);//Bottom + }, + primitives::Primitives::CornerWedge => { + //Right -> None + face_map.insert(1,0);//Top + //Back -> None + face_map.insert(3,1);//Right + face_map.insert(4,2);//Bottom + face_map.insert(5,3);//Front + }, + //do not support textured spheres/cylinders imported from roblox + //this can be added later, there are some maps that use it + primitives::Primitives::Sphere + |primitives::Primitives::Cylinder => (), + } + //use the biggest one and cut it down later... + let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None]; temp_objects.clear(); recursive_collect_superclass(&mut temp_objects, &dom, object,"Decal"); - - let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None]; for &decal_ref in &temp_objects{ if let Some(decal)=dom.get_by_ref(decal_ref){ if let ( @@ -168,8 +228,8 @@ pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Inde asset_id_from_texture_id.push(asset_id.0); texture_id }; - let face=normalid.to_u32(); - if face<6{ + let normal_id=normalid.to_u32(); + if let Some(&face)=face_map.get(&normal_id){ let mut roblox_texture_transform=RobloxTextureTransform::default(); let mut roblox_texture_color=glam::Vec4::ONE; if decal.class=="Texture"{ @@ -186,7 +246,7 @@ pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Inde decal.properties.get("StudsPerTileV"), ) { - let (size_u,size_v)=match face{ + 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 @@ -202,20 +262,28 @@ pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Inde roblox_texture_color=glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency); } } - part_texture_description[face as usize]=Some(RobloxFaceTextureDescription{ + part_texture_description[face]=Some(RobloxFaceTextureDescription{ texture:texture_id, color:roblox_texture_color, transform:roblox_texture_transform, }); }else{ - println!("goofy ahh roblox gave NormalId {}", face); + println!("NormalId={} unsupported for shape={:?}",normal_id,shape); } } } } } - //TODO: generate unit Block, Wedge, etc. based on part shape lists - let basepart_texture_description=RobloxBasePartDescription::Part(part_texture_description); + //obscure rust syntax "slice pattern" + let [f0,f1,f2,f3,f4,f5]=part_texture_description; + let basepart_texture_description=match shape{ + primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere, + primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]), + primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder, + //HAHAHA + primitives::Primitives::Wedge=>RobloxBasePartDescription::Wedge([f0,f1,f2,f3,f4]), + primitives::Primitives::CornerWedge=>RobloxBasePartDescription::CornerWedge([f0,f1,f2,f3]), + }; //make new model if unit cube has not been crated before let model_id=if let Some(&model_id)=model_id_from_description.get(&basepart_texture_description){ //push to existing texture model @@ -223,30 +291,68 @@ pub fn generate_indexed_models_roblox(dom:rbx_dom_weak::WeakDom) -> Result<(Inde }else{ let model_id=indexed_models.len(); model_id_from_description.insert(basepart_texture_description.clone(),model_id);//borrow checker going crazy - match basepart_texture_description{ + indexed_models.push(match basepart_texture_description{ + RobloxBasePartDescription::Sphere=>primitives::unit_sphere(), RobloxBasePartDescription::Part(part_texture_description)=>{ - let unit_cube_faces=part_texture_description.map(|face|{ - match face{ - Some(roblox_texture_transform)=>Some( - primitives::FaceDescription{ - texture:Some(roblox_texture_transform.texture), - transform:glam::Affine2::from_translation( - glam::vec2(roblox_texture_transform.transform.offset_u,roblox_texture_transform.transform.offset_v) - ) - *glam::Affine2::from_scale( - glam::vec2(roblox_texture_transform.transform.scale_u,roblox_texture_transform.transform.scale_v) - ), - color:roblox_texture_transform.color, - } - ), - None=>Some(primitives::FaceDescription::default()), - } - }); - let indexed_model=primitives::generate_partial_unit_cube(unit_cube_faces); - indexed_models.push(indexed_model); - model_id + let mut cube_face_description=primitives::CubeFaceDescription::new(); + 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, + _=>panic!("unreachable"), + }, + match roblox_face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>primitives::FaceDescription::default(), + }); + } + primitives::generate_partial_unit_cube(cube_face_description) }, - } + RobloxBasePartDescription::Cylinder=>primitives::unit_cylinder(), + RobloxBasePartDescription::Wedge(wedge_texture_description)=>{ + let mut wedge_face_description=primitives::WedgeFaceDescription::new(); + 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, + _=>panic!("unreachable"), + }, + match roblox_face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>primitives::FaceDescription::default(), + }); + } + primitives::generate_partial_unit_wedge(wedge_face_description) + }, + RobloxBasePartDescription::CornerWedge(cornerwedge_texture_description)=>{ + let mut cornerwedge_face_description=primitives::CornerWedgeFaceDescription::new(); + for (face_id,roblox_face_description) in cornerwedge_texture_description.iter().enumerate(){ + cornerwedge_face_description.insert( + match face_id{ + 0=>primitives::CornerWedgeFace::Top, + 1=>primitives::CornerWedgeFace::Right, + 2=>primitives::CornerWedgeFace::Bottom, + 3=>primitives::CornerWedgeFace::Front, + _=>panic!("unreachable"), + }, + match roblox_face_description{ + Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(), + None=>primitives::FaceDescription::default(), + }); + } + primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description) + }, + }); + model_id }; indexed_models[model_id].instances.push(ModelInstance { transform:model_transform, diff --git a/src/main.rs b/src/main.rs index ffee735..89052be 100644 --- a/src/main.rs +++ b/src/main.rs @@ -197,6 +197,7 @@ impl GraphicsData { let mut vertices = Vec::new(); let mut index_from_vertex = std::collections::HashMap::new();//:: let mut entities = Vec::new(); + //TODO: combine groups using the same render pattern for group in model.groups { let mut indices = Vec::new(); for poly in group.polys { @@ -355,9 +356,9 @@ impl framework::Example for GraphicsData { ) -> Self { let mut indexed_models = Vec::new(); indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teslacyberv3.0.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref())); - indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/suzanne.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref())); - indexed_models.append(&mut model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teapot.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref())); - indexed_models.push(primitives::the_unit_cube_lol()); + indexed_models.push(primitives::unit_sphere()); + indexed_models.push(primitives::unit_cylinder()); + indexed_models.push(primitives::unit_cube()); println!("models.len = {:?}", indexed_models.len()); indexed_models[0].instances.push(ModelInstance{ transform:glam::Affine3A::from_translation(glam::vec3(10.,0.,-10.)), @@ -382,7 +383,7 @@ impl framework::Example for GraphicsData { }); //teapot indexed_models[2].instances.push(ModelInstance{ - transform:glam::Affine3A::from_translation(glam::vec3(-10.,5.,10.)), + transform:glam::Affine3A::from_scale_rotation_translation(glam::vec3(0.5, 1.0, 0.2),glam::quat(-0.22248298016985793,-0.839457167990537,-0.05603504040830783,-0.49261857546227916),glam::vec3(-10.,7.,10.)), color:glam::Vec4::ONE, }); //ground diff --git a/src/primitives.rs b/src/primitives.rs index b9af225..53be8cb 100644 --- a/src/primitives.rs +++ b/src/primitives.rs @@ -1,5 +1,22 @@ use crate::model::{IndexedModel, IndexedPolygon, IndexedGroup, IndexedVertex}; +#[derive(Debug)] +pub enum Primitives{ + Sphere, + Cube, + Cylinder, + Wedge, + CornerWedge, +} +#[derive(Hash,PartialEq,Eq)] +pub enum CubeFace{ + Right, + Top, + Back, + Left, + Bottom, + Front, +} const CUBE_DEFAULT_TEXTURE_COORDS:[[f32;2];4]=[[0.0,0.0],[1.0,0.0],[1.0,1.0],[0.0,1.0]]; const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[ [-1.,-1., 1.],//0 left bottom back @@ -12,12 +29,12 @@ const CUBE_DEFAULT_VERTICES:[[f32;3];8]=[ [-1.,-1.,-1.],//7 left bottom front ]; const CUBE_DEFAULT_NORMALS:[[f32;3];6]=[ - [ 1., 0., 0.],//AabbFace::Right - [ 0., 1., 0.],//AabbFace::Top - [ 0., 0., 1.],//AabbFace::Back - [-1., 0., 0.],//AabbFace::Left - [ 0.,-1., 0.],//AabbFace::Bottom - [ 0., 0.,-1.],//AabbFace::Front + [ 1., 0., 0.],//CubeFace::Right + [ 0., 1., 0.],//CubeFace::Top + [ 0., 0., 1.],//CubeFace::Back + [-1., 0., 0.],//CubeFace::Left + [ 0.,-1., 0.],//CubeFace::Bottom + [ 0., 0.,-1.],//CubeFace::Front ]; const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[ // right (1, 0, 0) @@ -63,8 +80,93 @@ const CUBE_DEFAULT_POLYS:[[[u32;3];4];6]=[ [7,2,5], ], ]; -pub fn the_unit_cube_lol() -> crate::model::IndexedModel{ - generate_partial_unit_cube([Some(FaceDescription::default());6]) + +#[derive(Hash,PartialEq,Eq)] +pub enum WedgeFace{ + Right, + TopFront, + Back, + Left, + Bottom, +} +const WEDGE_DEFAULT_NORMALS:[[f32;3];5]=[ + [ 1., 0., 0.],//Wedge::Right + [ 0., 1.,-1.],//Wedge::TopFront + [ 0., 0., 1.],//Wedge::Back + [-1., 0., 0.],//Wedge::Left + [ 0.,-1., 0.],//Wedge::Bottom +]; +/* +local cornerWedgeVerticies = { + Vector3.new(-1/2,-1/2,-1/2),7 + Vector3.new(-1/2,-1/2, 1/2),0 + Vector3.new( 1/2,-1/2,-1/2),6 + Vector3.new( 1/2,-1/2, 1/2),1 + Vector3.new( 1/2, 1/2,-1/2),5 +} +*/ +#[derive(Hash,PartialEq,Eq)] +pub enum CornerWedgeFace{ + Top, + Right, + Bottom, + Front, +} +const CORNERWEDGE_DEFAULT_NORMALS:[[f32;3];5]=[ + [ 1., 0., 0.],//Wedge::Right + [ 0., 1., 1.],//Wedge::BackTop + [-1., 1., 0.],//Wedge::LeftTop + [ 0.,-1., 0.],//Wedge::Bottom + [ 0., 0.,-1.],//Wedge::Front +]; +//HashMap fits this use case perfectly but feels like using a sledgehammer to drive a nail +pub fn unit_sphere()->crate::model::IndexedModel{ + let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/suzanne.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0); + for pos in indexed_model.unique_pos.iter_mut(){ + pos[0]=pos[0]*0.5; + pos[1]=pos[1]*0.5; + pos[2]=pos[2]*0.5; + } + indexed_model +} +pub type CubeFaceDescription=std::collections::HashMap::; +pub fn unit_cube()->crate::model::IndexedModel{ + let mut t=CubeFaceDescription::new(); + t.insert(CubeFace::Right,FaceDescription::default()); + t.insert(CubeFace::Top,FaceDescription::default()); + t.insert(CubeFace::Back,FaceDescription::default()); + t.insert(CubeFace::Left,FaceDescription::default()); + t.insert(CubeFace::Bottom,FaceDescription::default()); + t.insert(CubeFace::Front,FaceDescription::default()); + generate_partial_unit_cube(t) +} +pub fn unit_cylinder()->crate::model::IndexedModel{ + let mut indexed_model=crate::model::generate_indexed_model_list_from_obj(obj::ObjData::load_buf(&include_bytes!("../models/teapot.obj")[..]).unwrap(),*glam::Vec4::ONE.as_ref()).remove(0); + for pos in indexed_model.unique_pos.iter_mut(){ + pos[0]=pos[0]*0.1; + pos[1]=pos[1]*0.1; + pos[2]=pos[2]*0.1; + } + indexed_model +} +pub type WedgeFaceDescription=std::collections::HashMap::; +pub fn unit_wedge()->crate::model::IndexedModel{ + let mut t=WedgeFaceDescription::new(); + t.insert(WedgeFace::Right,FaceDescription::default()); + t.insert(WedgeFace::TopFront,FaceDescription::default()); + t.insert(WedgeFace::Back,FaceDescription::default()); + t.insert(WedgeFace::Left,FaceDescription::default()); + t.insert(WedgeFace::Bottom,FaceDescription::default()); + generate_partial_unit_wedge(t) +} +pub type CornerWedgeFaceDescription=std::collections::HashMap::; +pub fn unit_cornerwedge()->crate::model::IndexedModel{ + let mut t=CornerWedgeFaceDescription::new(); + t.insert(CornerWedgeFace::Right,FaceDescription::default()); + t.insert(CornerWedgeFace::Top,FaceDescription::default()); + t.insert(CornerWedgeFace::Bottom,FaceDescription::default()); + t.insert(CornerWedgeFace::Front,FaceDescription::default()); + generate_partial_unit_cornerwedge(t) } #[derive(Copy,Clone)] @@ -74,7 +176,7 @@ pub struct FaceDescription{ pub color:glam::Vec4, } impl std::default::Default for FaceDescription{ - fn default() -> Self { + fn default()->Self { Self{ texture:None, transform:glam::Affine2::IDENTITY, @@ -95,8 +197,8 @@ impl FaceDescription{ } } //TODO: it's probably better to use a shared vertex buffer between all primitives and use indexed rendering instead of generating a unique vertex buffer for each primitive. -//implementation: put all roblox primitives into one model.groups -pub fn generate_partial_unit_cube(face_descriptions:[Option;6]) -> crate::model::IndexedModel{ +//implementation: put all roblox primitives into one model.groups <- this won't work but I forget why +pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->crate::model::IndexedModel{ let mut generated_pos=Vec::<[f32;3]>::new(); let mut generated_tex=Vec::new(); let mut generated_normal=Vec::new(); @@ -105,59 +207,290 @@ pub fn generate_partial_unit_cube(face_descriptions:[Option;6]) let mut groups=Vec::new(); 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 (i,maybe_face_description) in face_descriptions.iter().enumerate(){ - if let Some(face_description)=maybe_face_description{ - //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 - }else{ - //create new transform_index - let transform_index=transforms.len(); - transforms.push(face_description.transform); - for tex in CUBE_DEFAULT_TEXTURE_COORDS{ - generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref()); - } - transform_index - } as u32; - let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){ - color_index - }else{ - //create new color_index - let color_index=generated_color.len(); - generated_color.push(*face_description.color.as_ref()); - color_index - } as u32; - //always push normal - let normal_index=generated_normal.len() as u32; - generated_normal.push(CUBE_DEFAULT_NORMALS[i]); - //push vertices as they are needed - groups.push(IndexedGroup{ - texture:face_description.texture, - polys:vec![IndexedPolygon{ - vertices:CUBE_DEFAULT_POLYS[i].map(|tup|{ - let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize]; - let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){ - pos_index - }else{ - //create new pos_index - let pos_index=generated_pos.len(); - generated_pos.push(pos); - pos_index - } as u32; - //always push vertex - let vertex=IndexedVertex{ - pos:pos_index, - tex:tup[1]+4*transform_index, - normal:normal_index, - color:color_index, - }; - let vert_index=generated_vertices.len(); - generated_vertices.push(vertex); - vert_index as u32 - }).to_vec(), - }], - }); - } + for (face,face_description) in face_descriptions.iter(){ + //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 + }else{ + //create new transform_index + let transform_index=transforms.len(); + transforms.push(face_description.transform); + for tex in CUBE_DEFAULT_TEXTURE_COORDS{ + generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref()); + } + transform_index + } as u32; + let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){ + color_index + }else{ + //create new color_index + let color_index=generated_color.len(); + generated_color.push(*face_description.color.as_ref()); + color_index + } as u32; + let face_id=match face{ + CubeFace::Right => 0, + CubeFace::Top => 1, + CubeFace::Back => 2, + CubeFace::Left => 3, + CubeFace::Bottom => 4, + CubeFace::Front => 5, + }; + //always push normal + let normal_index=generated_normal.len() as u32; + generated_normal.push(CUBE_DEFAULT_NORMALS[face_id]); + //push vertices as they are needed + groups.push(IndexedGroup{ + texture:face_description.texture, + polys:vec![IndexedPolygon{ + vertices:CUBE_DEFAULT_POLYS[face_id].map(|tup|{ + let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize]; + let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){ + pos_index + }else{ + //create new pos_index + let pos_index=generated_pos.len(); + generated_pos.push(pos); + pos_index + } as u32; + //always push vertex + let vertex=IndexedVertex{ + pos:pos_index, + tex:tup[1]+4*transform_index, + normal:normal_index, + color:color_index, + }; + let vert_index=generated_vertices.len(); + generated_vertices.push(vertex); + vert_index as u32 + }).to_vec(), + }], + }); + } + IndexedModel{ + unique_pos:generated_pos, + unique_tex:generated_tex, + unique_normal:generated_normal, + unique_color:generated_color, + unique_vertices:generated_vertices, + groups, + instances:Vec::new(), + } +} +//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)->crate::model::IndexedModel{ + let wedge_default_polys=vec![ + // right (1, 0, 0) + vec![ + [6,2,0],//[vertex,tex,norm] + [2,0,0], + [1,3,0], + ], + // FrontTop (0, 1, -1) + vec![ + [3,1,1], + [2,0,1], + [6,3,1], + [7,2,1], + ], + // back (0, 0, 1) + vec![ + [0,3,2], + [1,2,2], + [2,1,2], + [3,0,2], + ], + // left (-1, 0, 0) + vec![ + [0,2,3], + [3,1,3], + [7,3,3], + ], + // bottom (0,-1, 0) + vec![ + [1,1,4], + [0,0,4], + [7,3,4], + [6,2,4], + ], + ]; + let mut generated_pos=Vec::<[f32;3]>::new(); + let mut generated_tex=Vec::new(); + let mut generated_normal=Vec::new(); + let mut generated_color=Vec::new(); + let mut generated_vertices=Vec::new(); + let mut groups=Vec::new(); + 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,face_description) in face_descriptions.iter(){ + //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 + }else{ + //create new transform_index + let transform_index=transforms.len(); + transforms.push(face_description.transform); + for tex in CUBE_DEFAULT_TEXTURE_COORDS{ + generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref()); + } + transform_index + } as u32; + let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){ + color_index + }else{ + //create new color_index + let color_index=generated_color.len(); + generated_color.push(*face_description.color.as_ref()); + color_index + } as u32; + let face_id=match face{ + WedgeFace::Right => 0, + WedgeFace::TopFront => 1, + WedgeFace::Back => 2, + WedgeFace::Left => 3, + WedgeFace::Bottom => 4, + }; + //always push normal + let normal_index=generated_normal.len() as u32; + generated_normal.push(WEDGE_DEFAULT_NORMALS[face_id]); + //push vertices as they are needed + groups.push(IndexedGroup{ + texture:face_description.texture, + polys:vec![IndexedPolygon{ + vertices:wedge_default_polys[face_id].iter().map(|tup|{ + let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize]; + let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){ + pos_index + }else{ + //create new pos_index + let pos_index=generated_pos.len(); + generated_pos.push(pos); + pos_index + } as u32; + //always push vertex + let vertex=IndexedVertex{ + pos:pos_index, + tex:tup[1]+4*transform_index, + normal:normal_index, + color:color_index, + }; + let vert_index=generated_vertices.len(); + generated_vertices.push(vertex); + vert_index as u32 + }).collect(), + }], + }); + } + IndexedModel{ + unique_pos:generated_pos, + unique_tex:generated_tex, + unique_normal:generated_normal, + unique_color:generated_color, + unique_vertices:generated_vertices, + groups, + instances:Vec::new(), + } +} + +pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescription)->crate::model::IndexedModel{ + let cornerwedge_default_polys=vec![ + // right (1, 0, 0) + vec![ + [6,2,0],//[vertex,tex,norm] + [5,1,0], + [1,3,0], + ], + // BackTop (0, 1, 1) + vec![ + [5,3,1], + [0,1,1], + [1,0,1], + ], + // LeftTop (-1, 1, 0) + vec![ + [5,3,2], + [7,2,2], + [0,1,2], + ], + // bottom (0,-1, 0) + vec![ + [1,1,3], + [0,0,3], + [7,3,3], + [6,2,3], + ], + // front (0, 0,-1) + vec![ + [5,0,4], + [6,3,4], + [7,2,4], + ], + ]; + let mut generated_pos=Vec::<[f32;3]>::new(); + let mut generated_tex=Vec::new(); + let mut generated_normal=Vec::new(); + let mut generated_color=Vec::new(); + let mut generated_vertices=Vec::new(); + let mut groups=Vec::new(); + 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,face_description) in face_descriptions.iter(){ + //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 + }else{ + //create new transform_index + let transform_index=transforms.len(); + transforms.push(face_description.transform); + for tex in CUBE_DEFAULT_TEXTURE_COORDS{ + generated_tex.push(*face_description.transform.transform_point2(glam::Vec2::from_array(tex)).as_ref()); + } + transform_index + } as u32; + let color_index=if let Some(color_index)=generated_color.iter().position(|color|color==face_description.color.as_ref()){ + color_index + }else{ + //create new color_index + let color_index=generated_color.len(); + generated_color.push(*face_description.color.as_ref()); + color_index + } as u32; + let face_id=match face{ + CornerWedgeFace::Right => 0, + CornerWedgeFace::Top => 1, + CornerWedgeFace::Bottom => 2, + CornerWedgeFace::Front => 3, + }; + //always push normal + let normal_index=generated_normal.len() as u32; + generated_normal.push(CORNERWEDGE_DEFAULT_NORMALS[face_id]); + //push vertices as they are needed + groups.push(IndexedGroup{ + texture:face_description.texture, + polys:vec![IndexedPolygon{ + vertices:cornerwedge_default_polys[face_id].iter().map(|tup|{ + let pos=CUBE_DEFAULT_VERTICES[tup[0] as usize]; + let pos_index=if let Some(pos_index)=generated_pos.iter().position(|&p|p==pos){ + pos_index + }else{ + //create new pos_index + let pos_index=generated_pos.len(); + generated_pos.push(pos); + pos_index + } as u32; + //always push vertex + let vertex=IndexedVertex{ + pos:pos_index, + tex:tup[1]+4*transform_index, + normal:normal_index, + color:color_index, + }; + let vert_index=generated_vertices.len(); + generated_vertices.push(vertex); + vert_index as u32 + }).collect(), + }], + }); } IndexedModel{ unique_pos:generated_pos,