use strafesnet_common::{map,model,integer,gameplay_attributes}; const VALVE_SCALE:f32=1.0/16.0; fn valve_transform(v:[f32;3])->integer::Planar64Vec3{ integer::Planar64Vec3::try_from([v[0]*VALVE_SCALE,v[2]*VALVE_SCALE,-v[1]*VALVE_SCALE]).unwrap() } pub fn convert( bsp:&vbsp::Bsp, mut acquire_texture_id:AcquireTextureId, mut acquire_mesh_id:AcquireMeshId )->strafesnet_common::map::CompleteMap where AcquireTextureId:FnMut(&str)->model::TextureId, AcquireMeshId:FnMut(&str)->model::MeshId, { let mut unique_render_configs=Vec::new(); let mut unique_attributes=Vec::new(); unique_attributes.push(gameplay_attributes::CollisionAttributes::contact_default()); const TEMP_TOUCH_ME_ATTRIBUTE:gameplay_attributes::CollisionAttributesId=gameplay_attributes::CollisionAttributesId::new(0); //declare all prop models to Loader for prop in bsp.static_props(){ acquire_mesh_id(prop.model()); } //TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups //the generated MeshIds in here will collide with the Loader Mesh Ids //but I can't think of a good workaround other than just remapping one later. let (meshes,models):(Vec,Vec)=bsp.models().enumerate().map(|(mesh_id,world_model)|{ let mesh_id=model::MeshId::new(mesh_id as u32); //non-deduplicated let mut spam_pos=Vec::new(); let mut spam_tex=Vec::new(); let mut spam_normal=Vec::new(); let mut spam_vertices=Vec::new(); let mut graphics_groups=Vec::new(); let mut physics_group=model::IndexedPhysicsGroup::default(); let polygon_groups=world_model.faces().enumerate().map(|(polygon_group_id,face)|{ let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32); let face_texture=face.texture(); let face_texture_data=face_texture.texture_data(); let (texture_u,texture_v)=(glam::Vec3A::from_slice(&face_texture.texture_transforms_u[0..3]),glam::Vec3A::from_slice(&face_texture.texture_transforms_v[0..3])); let texture_offset=glam::vec2(face_texture.texture_transforms_u[3],face_texture.texture_transforms_v[3]); let texture_size=glam::vec2(face_texture_data.width as f32,face_texture_data.height as f32); //texture let texture_id=acquire_texture_id(face_texture_data.name()); //this is equivalent to a get_or_create pattern because there is a singular no-texture RenderId //so RenderId==TextureId //not the most failsafe code but this is just for the map tool lmao if unique_render_configs.len()==texture_id.get() as usize{ unique_render_configs.push(model::RenderConfig::texture(texture_id)); }; let render_id=model::RenderConfigId::new(texture_id.get()); //normal let normal=face.normal(); let normal_idx=spam_normal.len() as u32; spam_normal.push(valve_transform(<[f32;3]>::from(normal))); let mut indices:Vec=face.vertex_positions().map(|vertex_position|{ let vertex_xyz=<[f32;3]>::from(vertex_position); let pos=glam::Vec3A::from_array(vertex_xyz); let pos_idx=spam_pos.len(); spam_pos.push(valve_transform(vertex_xyz)); //calculate texture coordinates let tex=(glam::vec2(pos.dot(texture_u),pos.dot(texture_v))+texture_offset)/texture_size; let tex_idx=spam_tex.len() as u32; spam_tex.push(tex); let vertex_id=model::VertexId::new(spam_vertices.len() as u32); spam_vertices.push(model::IndexedVertex{ pos:model::PositionId::new(pos_idx as u32), tex:model::TextureCoordinateId::new(tex_idx as u32), normal:model::NormalId::new(normal_idx), color:model::ColorId::new(0), }); vertex_id }).collect(); if face.is_visible(){ graphics_groups.push(model::IndexedGraphicsGroup{ render:render_id, groups:vec![polygon_group_id], }) } physics_group.groups.push(polygon_group_id); model::PolygonGroup::PolygonList(model::PolygonList::new(vec![indices])) }).collect(); ( model::Mesh{ unique_pos:spam_pos, unique_tex:spam_tex, unique_normal:spam_normal, unique_color:vec![glam::Vec4::ONE], unique_vertices:spam_vertices, polygon_groups, graphics_groups, physics_groups:vec![physics_group], }, model::Model{ mesh:mesh_id, attributes:TEMP_TOUCH_ME_ATTRIBUTE, transform:integer::Planar64Affine3::new( integer::Planar64Mat3::default(), valve_transform(<[f32;3]>::from(world_model.origin)) ), color:glam::Vec4::ONE, }, ) }).unzip(); map::CompleteMap{ attributes:unique_attributes, meshes, render_configs:unique_render_configs, models, modes:strafesnet_common::gameplay_modes::Modes::new(Vec::new()), } } /* //call self.acquire_texture_id for each texture in the mesh //generate unique meshes let mut model_map=std::collections::HashMap::with_capacity(model_dedupe.len()); let mut prop_models=Vec::new(); for model_name in model_dedupe{ let model_name_lower=model_name.to_lowercase(); //.mdl, .vvd, .dx90.vtx let mut path=std::path::PathBuf::from(model_name_lower.as_str()); let file_name=std::path::PathBuf::from(path.file_stem().unwrap()); path.pop(); path.push(file_name); let mut vvd_path=path.clone(); let mut vtx_path=path.clone(); vvd_path.set_extension("vvd"); vtx_path.set_extension("dx90.vtx"); match (bsp.pack.get(model_name_lower.as_str()),bsp.pack.get(vvd_path.as_os_str().to_str().unwrap()),bsp.pack.get(vtx_path.as_os_str().to_str().unwrap())){ (Ok(Some(mdl_file)),Ok(Some(vvd_file)),Ok(Some(vtx_file)))=>{ match (vmdl::mdl::Mdl::read(mdl_file.as_ref()),vmdl::vvd::Vvd::read(vvd_file.as_ref()),vmdl::vtx::Vtx::read(vtx_file.as_ref())){ (Ok(mdl),Ok(vvd),Ok(vtx))=>{ let model=vmdl::Model::from_parts(mdl,vtx,vvd); let texture_paths=model.texture_directories(); if texture_paths.len()!=1{ println!("WARNING: multiple texture paths"); } let skin=model.skin_tables().nth(0).unwrap(); let mut spam_pos=Vec::with_capacity(model.vertices().len()); let mut spam_normal=Vec::with_capacity(model.vertices().len()); let mut spam_tex=Vec::with_capacity(model.vertices().len()); let mut spam_vertices=Vec::with_capacity(model.vertices().len()); for (i,vertex) in model.vertices().iter().enumerate(){ spam_pos.push(valve_transform(<[f32;3]>::from(vertex.position))); spam_normal.push(valve_transform(<[f32;3]>::from(vertex.normal))); spam_tex.push(glam::Vec2::from_array(vertex.texture_coordinates)); spam_vertices.push(model::IndexedVertex{ pos:model::PositionId::new(i as u32), tex:model::TextureCoordinateId::new(i as u32), normal:model::NormalId::new(i as u32), color:model::ColorId::new(0), }); } let model_id=prop_models.len(); model_map.insert(model_name,model_id); prop_models.push(model::Mesh{ unique_pos:spam_pos, unique_normal:spam_normal, unique_tex:spam_tex, unique_color:vec![glam::Vec4::ONE], unique_vertices:spam_vertices, groups:model.meshes().map(|mesh|{ let texture=if let (Some(texture_path),Some(texture_name))=(texture_paths.get(0),skin.texture(mesh.material_index())){ let mut path=std::path::PathBuf::from(texture_path.as_str()); path.push(texture_name); let texture_location=path.as_os_str().to_str().unwrap(); let texture_id=if let Some(&texture_id)=texture_id_from_name.get(texture_location){ texture_id }else{ println!("texture! {}",texture_location); let texture_id=name_from_texture_id.len() as u32; texture_id_from_name.insert(texture_location.to_string(),texture_id); name_from_texture_id.push(texture_location.to_string()); texture_id }; Some(texture_id) }else{ None }; model::IndexedGroup{ texture, polys:{ //looking at the code, it would seem that the strips are pre-deindexed into triangle lists when calling this function mesh.vertex_strip_indices().map(|strip|{ strip.collect::>().chunks(3).map(|tri|{ model::IndexedPolygon{vertices:vec![tri[0] as u32,tri[1] as u32,tri[2] as u32]} }).collect::>() }).flatten().collect() }, } }).collect(), instances:Vec::new(), }); }, _=>println!("model_name={} error",model_name), } }, _=>println!("no model name={}",model_name), } } //generate model instances for prop in bsp.static_props(){ let placement=prop.as_prop_placement(); if let Some(&model_index)=model_map.get(placement.model){ prop_models[model_index].instances.push(model::ModelInstance{ transform:integer::Planar64Affine3::new( integer::Planar64Mat3::try_from( glam::Mat3A::from_diagonal(glam::Vec3::splat(placement.scale)) //TODO: figure this out *glam::Mat3A::from_quat(glam::Quat::from_xyzw( placement.rotation.v.x,//b placement.rotation.v.y,//c placement.rotation.v.z,//d placement.rotation.s,//a )) ).unwrap(), valve_transform(<[f32;3]>::from(placement.origin)), ), attributes:model::CollisionAttributes::Decoration, ..Default::default() }); }else{ //println!("model not found {}",placement.model); } } //actually add the prop models prop_models.append(&mut models); */