From 3434cd394a14fbea8d6ac99dac70244933c0239a Mon Sep 17 00:00:00 2001 From: Quaternions Date: Thu, 25 Jul 2024 10:48:07 -0700 Subject: [PATCH] implement map file format --- src/map.rs | 278 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 221 insertions(+), 57 deletions(-) diff --git a/src/map.rs b/src/map.rs index 89b39fc..ffea77b 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,11 +1,21 @@ -//use strafesnet_common::model; -//use strafesnet_common::gameplay_modes; -use binrw::{BinReaderExt, binrw}; +use std::io::Read; +use std::collections::HashMap; + +use crate::newtypes; +use crate::file::BlockId; +use binrw::{binrw,BinReaderExt}; +use strafesnet_common::model; +use strafesnet_common::aabb::Aabb; +use strafesnet_common::bvh::BvhNode; +use strafesnet_common::gameplay_modes; pub enum Error{ - InvalidHeader, - InvalidBvhNodeId(BvhNodeId), - InvalidRegion(binrw::Error), + InvalidHeader(binrw::Error), + InvalidBlockId(BlockId), + InvalidMeshId(model::MeshId), + InvalidTextureId(model::TextureId), + InvalidData(binrw::Error), + IO(std::io::Error), File(crate::file::Error), } @@ -14,10 +24,13 @@ pub enum Error{ BLOCK_MAP_HEADER: DefaultStyleInfo style_info //bvh goes here -u64 num_nodes +u32 num_nodes +u32 num_spacial_blocks +u32 num_resource_blocks +u32 num_resources_external //node 0 parent node is implied to be None for node_id in 1..num_nodes{ - u64 parent_node + u32 parent_node } //NOTE: alternate realities are not necessary. @@ -26,17 +39,21 @@ for node_id in 1..num_nodes{ //ideally spacial blocks are sorted from distance to start zone //texture blocks are inserted before the first spacial block they are used in -u64 num_spacial_blocks for spacial_block_id in 0..num_spacial_blocks{ - u64 node_id - u64 block_id //data block - Aabb block_extents + u32 node_id + u32 block_id //data block + Aabb extents } -//if the map file references external resources, num_resources = 0 -u64 num_resources -for resource_id in 0..num_resources{ - u64 block_id - u128 resource_id +//the order of these lists uniquely generates the incremental Ids +//MeshId, TextureId etc. based on resource type +//the first 8 bits of resource_uuid describe the type (mesh, texture, etc) +//if the map file references only external resources, num_resource_blocks = 0 +for resource_idx in 0..num_resource_blocks{ + Resource resource_type + u32 block_id +} +for resource_idx in 0..num_resources_external{ + u128 resource_uuid } BLOCK_MAP_RESOURCE: @@ -52,71 +69,218 @@ Resource resource_type BLOCK_MAP_REGION: u64 num_models for model_id in 0..num_models{ - u128 model_resource_uuid ModelInstance mode_instance } */ - -//error hiding mock code -mod gameplay_modes{ - pub struct Modes{} +//if you hash the resource itself and set the first 8 bits to this, that's the resource uuid +#[binrw] +#[brw(little,repr=u8)] +enum ResourceType{ + Mesh, + Texture, + //Shader, + //Sound, + //Video, + //Animation, } -mod model{ - pub struct Mesh{} - #[super::binrw] - #[brw(little)] - pub struct Model{} -} -mod image{ - pub struct Image{} +const RESOURCE_TYPE_VARIANT_COUNT:u8=2; +#[binrw] +#[brw(little)] +struct ResourceId(u128); +impl ResourceId{ + fn resource_type(&self)->Option{ + let discriminant=self.0 as u8; + //TODO: use this when it is stabilized https://github.com/rust-lang/rust/issues/73662 + //if (discriminant as usize)(){ + match discriminantSome(unsafe{std::mem::transmute::(discriminant)}), + false=>None, + } + } } -//serious code - -struct ModelUuid(u128); -struct ImageUuid(u128); +struct ResourceMap{ + meshes:HashMap, + textures:HashMap, +} +impl Default for ResourceMap{ + fn default()->Self{ + Self{ + meshes:HashMap::new(), + textures:HashMap::new(), + } + } +} #[binrw] #[brw(little)] -#[derive(Clone,Copy,id::Id)] -pub struct BvhNodeId(u32); -struct BvhNode{ - //aabb - //child +struct SpacialBlockHeader{ + id:BlockId, + extents:newtypes::aabb::Aabb, } +#[binrw] +#[brw(little)] +struct ResourceBlockHeader{ + resource:ResourceType, + id:BlockId, +} +#[binrw] +#[brw(little)] +struct ResourceExternalHeader{ + resource_uuid:ResourceId, +} + +#[binrw] +#[brw(little)] +struct MapHeader{ + num_nodes:u32, + num_spacial_blocks:u32, + num_resource_blocks:u32, + num_resources_external:u32, + num_modes:u32, + num_attributes:u32, + num_render_configs:u32, + #[br(count=num_nodes)] + nodes:Vec, + #[br(count=num_spacial_blocks)] + spacial_blocks:Vec, + #[br(count=num_resource_blocks)] + resource_blocks:Vec, + #[br(count=num_resources_external)] + external_resources:Vec, + #[br(count=num_modes)] + modes:Vec, + #[br(count=num_attributes)] + attributes:Vec, + #[br(count=num_render_configs)] + render_configs:Vec, +} + #[binrw] #[brw(little)] struct Region{ //consider including a bvh in the region instead of needing to rebalance the physics bvh on the fly model_count:u32, #[br(count=model_count)] - models:Vec, + models:Vec, +} + +//code deduplication reused in into_complete_map +fn read_region(file:&mut crate::file::File,block_id:BlockId)->Result,Error>{ + //load region from disk + //parse the models and determine what resources need to be loaded + //load resources into self.resources + //return Region + let mut block=file.block_reader(block_id).map_err(Error::File)?; + let region:Region=block.read_le().map_err(Error::InvalidData)?; + Ok(region.models.into_iter().map(Into::into).collect()) +} +fn read_mesh(file:&mut crate::file::File,block_id:BlockId)->Result{ + let mut block=file.block_reader(block_id).map_err(Error::File)?; + let mesh:newtypes::model::Mesh=block.read_le().map_err(Error::InvalidData)?; + Ok(mesh.into()) +} +fn read_texture(file:&mut crate::file::File,block_id:BlockId)->Result,Error>{ + let mut block=file.block_reader(block_id).map_err(Error::File)?; + let mut texture=Vec::new(); + block.read_to_end(&mut texture).map_err(Error::IO)?; + Ok(texture) } pub struct StreamableMap{ file:crate::file::File, //this includes every platform... move the unconstrained datas to their appropriate data block? modes:gameplay_modes::Modes, - bvh:BvhNode, - node_id_to_block_id:Vec, + //this is every possible attribute... need some sort of streaming system + attributes:Vec, + //this is every possible render configuration... shaders and such... need streaming + render_configs:Vec, + //this makes sense to keep in memory for streaming, a map of which blocks occupy what space + bvh:BvhNode, + //something something resources hashmaps + resource_blocks:ResourceMap, + //resource_external:ResourceMap, } impl StreamableMap{ - pub(crate) fn new(file:crate::file::File)->Result{ - Err(Error::InvalidHeader) + pub(crate) fn new(mut file:crate::file::File)->Result{ + //assume the file seek is in the right place to start reading a map header + let header:MapHeader=file.as_mut().read_le().map_err(Error::InvalidHeader)?; + let modes=header.modes.into_iter().map(Into::into).collect(); + let attributes=header.attributes.into_iter().map(Into::into).collect(); + let render_configs=header.render_configs.into_iter().map(Into::into).collect(); + let bvh=header.spacial_blocks.into_iter().map(|spacial_block| + (spacial_block.id,spacial_block.extents.into()) + ).collect(); + //generate mesh ids and texture ids from resource list order + let mut resource_blocks=ResourceMap::default(); + for resource_block_header in header.resource_blocks{ + match resource_block_header.resource{ + ResourceType::Mesh=>{ + resource_blocks.meshes.insert( + //generate the id from the current length + model::MeshId::new(resource_blocks.meshes.len() as u32), + resource_block_header.id + ); + }, + ResourceType::Texture=>{ + resource_blocks.textures.insert( + model::TextureId::new(resource_blocks.textures.len() as u32), + resource_block_header.id + ); + }, + } + } + Ok(Self{ + file, + modes:strafesnet_common::gameplay_modes::Modes::new(modes), + attributes, + render_configs, + bvh:strafesnet_common::bvh::generate_bvh(bvh), + resource_blocks, + //resource_external:Default::default(), + }) } - pub fn load_node(&mut self,node_id:BvhNodeId)->Result,Error>{ - //load region from disk - //parse the models and determine what resources need to be loaded - //load resources into self.resources - //return Region - let block_id=*self.node_id_to_block_id.get(node_id.get() as usize).ok_or(Error::InvalidBvhNodeId(node_id))?; - let mut block=self.file.block_reader(block_id).map_err(Error::File)?; - let region:Region=block.read_le().map_err(Error::InvalidRegion)?; - Ok(region.models) + pub fn get_intersecting_region_block_ids(&self,aabb:&Aabb)->Vec{ + let mut block_ids=Vec::new(); + self.bvh.the_tester(aabb,&mut |block_id|block_ids.push(block_id)); + block_ids } - // pub fn load_resource(&mut self,resource_id:ResourceId)->Resource{ - // // - // } -} \ No newline at end of file + pub fn load_region(&mut self,block_id:BlockId)->Result,Error>{ + read_region(&mut self.file,block_id) + } + pub fn load_mesh(&mut self,mesh_id:model::MeshId)->Result{ + let block_id=*self.resource_blocks.meshes.get(&mesh_id).ok_or(Error::InvalidMeshId(mesh_id))?; + read_mesh(&mut self.file,block_id) + } + pub fn load_texture(&mut self,texture_id:model::TextureId)->Result,Error>{ + let block_id=*self.resource_blocks.textures.get(&texture_id).ok_or(Error::InvalidTextureId(texture_id))?; + read_texture(&mut self.file,block_id) + } + pub fn into_complete_map(mut self)->Result{ + //load all meshes + let meshes=self.resource_blocks.meshes.into_values().map(|block_id| + read_mesh(&mut self.file,block_id) + ).collect::,_>>()?; + //load all textures + let textures=self.resource_blocks.textures.into_values().map(|block_id| + read_texture(&mut self.file,block_id) + ).collect::,_>>()?; + let mut block_ids=Vec::new(); + self.bvh.into_visitor(&mut |block_id|block_ids.push(block_id)); + //load all regions + let mut models=Vec::new(); + for block_id in block_ids{ + models.append(&mut read_region(&mut self.file,block_id)?); + } + Ok(strafesnet_common::map::CompleteMap{ + modes:self.modes, + attributes:self.attributes, + meshes, + models, + textures, + render_configs:self.render_configs, + }) + } +}