From 38efed0d040bce0a19e320287060594952a7214e Mon Sep 17 00:00:00 2001 From: Quaternions Date: Tue, 13 Feb 2024 18:02:32 -0800 Subject: [PATCH] data structure rewrite + refactor loaders --- Cargo.lock | 13 +++- Cargo.toml | 2 +- src/bsp.rs | 221 +++++++++++++++++++++++++++-------------------------- src/lib.rs | 37 ++++++++- 4 files changed, 162 insertions(+), 111 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2f400f7..26020d0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -200,6 +200,16 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +[[package]] +name = "id" +version = "0.1.0" +source = "git+https://git.itzana.me/Quaternions/id?rev=1f710976cc786c8853dab73d6e1cee53158deeb0#1f710976cc786c8853dab73d6e1cee53158deeb0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "indexmap" version = "2.2.1" @@ -344,9 +354,10 @@ dependencies = [ [[package]] name = "strafesnet_common" version = "0.1.0" -source = "git+https://git.itzana.me/StrafesNET/common?rev=434ca29aef7e3015c9ca1ed45de8fef42e33fdfb#434ca29aef7e3015c9ca1ed45de8fef42e33fdfb" +source = "git+https://git.itzana.me/StrafesNET/common?rev=47cdea0c8a5d10a2440ca6270a975d560aa3642d#47cdea0c8a5d10a2440ca6270a975d560aa3642d" dependencies = [ "glam", + "id", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 0346953..127ff5e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" [dependencies] glam = "0.25.0" -strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "434ca29aef7e3015c9ca1ed45de8fef42e33fdfb" } +strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "47cdea0c8a5d10a2440ca6270a975d560aa3642d" } vbsp = "0.5.0" vmdl = "0.1.1" diff --git a/src/bsp.rs b/src/bsp.rs index 1781c48..5a6006c 100644 --- a/src/bsp.rs +++ b/src/bsp.rs @@ -1,108 +1,126 @@ -use strafesnet_common::{model,integer}; +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 generate_indexed_models(input:&mut R)->Result{ - let mut s=Vec::new(); +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(); - match input.read_to_end(&mut s){ - Ok(_)=>(), - Err(e)=>println!("load_bsp::generate_indexed_models read_to_end failed: {:?}",e), + 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()); } - match vbsp::Bsp::read(s.as_slice()){ - Ok(bsp)=>{ - let mut spawn_point=integer::Planar64Vec3::ZERO; + //TODO: make the main map one single mesh with a bunch of different physics groups and graphics groups - let vertices: Vec<_> = bsp - .vertices - .iter() - .map(|vertex|<[f32;3]>::from(vertex.position)) - .collect(); + //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); - let mut name_from_texture_id=Vec::new(); - let mut texture_id_from_name=std::collections::HashMap::new(); + //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()); - let mut models=bsp.models().map(|world_model|{ - //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 groups=world_model.faces() - .filter(|face| face.is_visible())//TODO: look at this - .map(|face|{ - 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); + //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)); - //texture - let texture_id=if let Some(&texture_id)=texture_id_from_name.get(face_texture_data.name()){ - texture_id - }else{ - let texture_id=name_from_texture_id.len() as u32; - texture_id_from_name.insert(face_texture_data.name().to_string(),texture_id); - name_from_texture_id.push(face_texture_data.name().to_string()); - texture_id - }; + //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); - //normal - let normal=face.normal(); - let normal_idx=spam_normal.len() as u32; - spam_normal.push(valve_transform(<[f32;3]>::from(normal))); - let mut vertices:Vec=face.vertex_indexes().map(|vertex_index|{ - let pos=glam::Vec3A::from_array(vertices[vertex_index as usize]); - let pos_idx=spam_pos.len(); - spam_pos.push(valve_transform(vertices[vertex_index as usize])); - - //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 i=spam_vertices.len() as u32; - spam_vertices.push(model::IndexedVertex{ - pos: pos_idx as u32, - tex: tex_idx as u32, - normal: normal_idx, - color: 0, - }); - i - }).collect(); - vertices.reverse(); - model::IndexedGroup{ - texture:Some(texture_id), - polys:vec![model::IndexedPolygon{vertices}], - } - }).collect(); - model::IndexedModel{ - unique_pos:spam_pos, - unique_tex:spam_tex, - unique_normal:spam_normal, - unique_color:vec![glam::Vec4::ONE], - unique_vertices:spam_vertices, - groups, - instances:vec![model::ModelInstance{ - attributes:model::CollisionAttributes::Decoration, - transform:integer::Planar64Affine3::new( - integer::Planar64Mat3::default(), - valve_transform(<[f32;3]>::from(world_model.origin)) - ), - ..Default::default() - }], - } + 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(); - - //dedupe prop models - let mut model_dedupe=std::collections::HashSet::new(); - for prop in bsp.static_props(){ - model_dedupe.insert(prop.model()); + 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(); @@ -137,16 +155,16 @@ pub fn generate_indexed_models(input:&mut R)->Res 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:i as u32, - tex:i as u32, - normal:i as u32, - color:0, + 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::IndexedModel{ + prop_models.push(model::Mesh{ unique_pos:spam_pos, unique_normal:spam_normal, unique_tex:spam_tex, @@ -221,17 +239,4 @@ pub fn generate_indexed_models(input:&mut R)->Res //actually add the prop models prop_models.append(&mut models); - - Ok(model::IndexedModelInstances{ - textures:name_from_texture_id, - models:prop_models, - spawn_point, - modes:Vec::new(), - }) - }, - Err(e)=>{ - println!("rotten {:?}",e); - Err(e) - }, - } -} +*/ diff --git a/src/lib.rs b/src/lib.rs index e06ee2d..f683f49 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,36 @@ -pub mod bsp; \ No newline at end of file +mod bsp; + +pub struct Bsp(vbsp::Bsp); + +#[derive(Debug)] +pub enum ReadError{ + Bsp(vbsp::BspError), + Io(std::io::Error), +} +impl std::fmt::Display for ReadError{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for ReadError{} + +pub fn read(mut input:R)->Result{ + let mut s=Vec::new(); + + //TODO: mmap + input.read_to_end(&mut s).map_err(ReadError::Io)?; + + vbsp::Bsp::read(s.as_slice()).map(Bsp).map_err(ReadError::Bsp) +} + +pub fn convert( + bsp:&Bsp, + acquire_texture_id:AcquireTextureId, + acquire_mesh_id:AcquireMeshId +)->strafesnet_common::map::CompleteMap +where + AcquireTextureId:FnMut(&str)->strafesnet_common::model::TextureId, + AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId, +{ + bsp::convert(&bsp.0,acquire_texture_id,acquire_mesh_id) +} \ No newline at end of file