implement map file format
This commit is contained in:
parent
b6935b4f5f
commit
3434cd394a
278
src/map.rs
278
src/map.rs
@ -1,11 +1,21 @@
|
|||||||
//use strafesnet_common::model;
|
use std::io::Read;
|
||||||
//use strafesnet_common::gameplay_modes;
|
use std::collections::HashMap;
|
||||||
use binrw::{BinReaderExt, binrw};
|
|
||||||
|
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{
|
pub enum Error{
|
||||||
InvalidHeader,
|
InvalidHeader(binrw::Error),
|
||||||
InvalidBvhNodeId(BvhNodeId),
|
InvalidBlockId(BlockId),
|
||||||
InvalidRegion(binrw::Error),
|
InvalidMeshId(model::MeshId),
|
||||||
|
InvalidTextureId(model::TextureId),
|
||||||
|
InvalidData(binrw::Error),
|
||||||
|
IO(std::io::Error),
|
||||||
File(crate::file::Error),
|
File(crate::file::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -14,10 +24,13 @@ pub enum Error{
|
|||||||
BLOCK_MAP_HEADER:
|
BLOCK_MAP_HEADER:
|
||||||
DefaultStyleInfo style_info
|
DefaultStyleInfo style_info
|
||||||
//bvh goes here
|
//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
|
//node 0 parent node is implied to be None
|
||||||
for node_id in 1..num_nodes{
|
for node_id in 1..num_nodes{
|
||||||
u64 parent_node
|
u32 parent_node
|
||||||
}
|
}
|
||||||
|
|
||||||
//NOTE: alternate realities are not necessary.
|
//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
|
//ideally spacial blocks are sorted from distance to start zone
|
||||||
//texture blocks are inserted before the first spacial block they are used in
|
//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{
|
for spacial_block_id in 0..num_spacial_blocks{
|
||||||
u64 node_id
|
u32 node_id
|
||||||
u64 block_id //data block
|
u32 block_id //data block
|
||||||
Aabb block_extents
|
Aabb extents
|
||||||
}
|
}
|
||||||
//if the map file references external resources, num_resources = 0
|
//the order of these lists uniquely generates the incremental Ids
|
||||||
u64 num_resources
|
//MeshId, TextureId etc. based on resource type
|
||||||
for resource_id in 0..num_resources{
|
//the first 8 bits of resource_uuid describe the type (mesh, texture, etc)
|
||||||
u64 block_id
|
//if the map file references only external resources, num_resource_blocks = 0
|
||||||
u128 resource_id
|
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:
|
BLOCK_MAP_RESOURCE:
|
||||||
@ -52,71 +69,218 @@ Resource resource_type
|
|||||||
BLOCK_MAP_REGION:
|
BLOCK_MAP_REGION:
|
||||||
u64 num_models
|
u64 num_models
|
||||||
for model_id in 0..num_models{
|
for model_id in 0..num_models{
|
||||||
u128 model_resource_uuid
|
|
||||||
ModelInstance mode_instance
|
ModelInstance mode_instance
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
//if you hash the resource itself and set the first 8 bits to this, that's the resource uuid
|
||||||
//error hiding mock code
|
#[binrw]
|
||||||
mod gameplay_modes{
|
#[brw(little,repr=u8)]
|
||||||
pub struct Modes{}
|
enum ResourceType{
|
||||||
|
Mesh,
|
||||||
|
Texture,
|
||||||
|
//Shader,
|
||||||
|
//Sound,
|
||||||
|
//Video,
|
||||||
|
//Animation,
|
||||||
}
|
}
|
||||||
mod model{
|
const RESOURCE_TYPE_VARIANT_COUNT:u8=2;
|
||||||
pub struct Mesh{}
|
#[binrw]
|
||||||
#[super::binrw]
|
#[brw(little)]
|
||||||
#[brw(little)]
|
struct ResourceId(u128);
|
||||||
pub struct Model{}
|
impl ResourceId{
|
||||||
}
|
fn resource_type(&self)->Option<ResourceType>{
|
||||||
mod image{
|
let discriminant=self.0 as u8;
|
||||||
pub struct Image{}
|
//TODO: use this when it is stabilized https://github.com/rust-lang/rust/issues/73662
|
||||||
|
//if (discriminant as usize)<std::mem::variant_count::<ResourceType>(){
|
||||||
|
match discriminant<RESOURCE_TYPE_VARIANT_COUNT{
|
||||||
|
true=>Some(unsafe{std::mem::transmute::<u8,ResourceType>(discriminant)}),
|
||||||
|
false=>None,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//serious code
|
struct ResourceMap<T>{
|
||||||
|
meshes:HashMap<strafesnet_common::model::MeshId,T>,
|
||||||
struct ModelUuid(u128);
|
textures:HashMap<strafesnet_common::model::TextureId,T>,
|
||||||
struct ImageUuid(u128);
|
}
|
||||||
|
impl<T> Default for ResourceMap<T>{
|
||||||
|
fn default()->Self{
|
||||||
|
Self{
|
||||||
|
meshes:HashMap::new(),
|
||||||
|
textures:HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
#[derive(Clone,Copy,id::Id)]
|
struct SpacialBlockHeader{
|
||||||
pub struct BvhNodeId(u32);
|
id:BlockId,
|
||||||
struct BvhNode{
|
extents:newtypes::aabb::Aabb,
|
||||||
//aabb
|
|
||||||
//child
|
|
||||||
}
|
}
|
||||||
|
#[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<u32>,
|
||||||
|
#[br(count=num_spacial_blocks)]
|
||||||
|
spacial_blocks:Vec<SpacialBlockHeader>,
|
||||||
|
#[br(count=num_resource_blocks)]
|
||||||
|
resource_blocks:Vec<ResourceBlockHeader>,
|
||||||
|
#[br(count=num_resources_external)]
|
||||||
|
external_resources:Vec<ResourceExternalHeader>,
|
||||||
|
#[br(count=num_modes)]
|
||||||
|
modes:Vec<newtypes::gameplay_modes::Mode>,
|
||||||
|
#[br(count=num_attributes)]
|
||||||
|
attributes:Vec<newtypes::gameplay_attributes::CollisionAttributes>,
|
||||||
|
#[br(count=num_render_configs)]
|
||||||
|
render_configs:Vec<newtypes::model::RenderConfig>,
|
||||||
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
#[brw(little)]
|
#[brw(little)]
|
||||||
struct Region{
|
struct Region{
|
||||||
//consider including a bvh in the region instead of needing to rebalance the physics bvh on the fly
|
//consider including a bvh in the region instead of needing to rebalance the physics bvh on the fly
|
||||||
model_count:u32,
|
model_count:u32,
|
||||||
#[br(count=model_count)]
|
#[br(count=model_count)]
|
||||||
models:Vec<model::Model>,
|
models:Vec<newtypes::model::Model>,
|
||||||
|
}
|
||||||
|
|
||||||
|
//code deduplication reused in into_complete_map
|
||||||
|
fn read_region<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<model::Model>,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<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<model::Mesh,Error>{
|
||||||
|
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<R:BinReaderExt>(file:&mut crate::file::File<R>,block_id:BlockId)->Result<Vec<u8>,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<R:BinReaderExt>{
|
pub struct StreamableMap<R:BinReaderExt>{
|
||||||
file:crate::file::File<R>,
|
file:crate::file::File<R>,
|
||||||
//this includes every platform... move the unconstrained datas to their appropriate data block?
|
//this includes every platform... move the unconstrained datas to their appropriate data block?
|
||||||
modes:gameplay_modes::Modes,
|
modes:gameplay_modes::Modes,
|
||||||
bvh:BvhNode,
|
//this is every possible attribute... need some sort of streaming system
|
||||||
node_id_to_block_id:Vec<crate::file::BlockId>,
|
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||||
|
//this is every possible render configuration... shaders and such... need streaming
|
||||||
|
render_configs:Vec<strafesnet_common::model::RenderConfig>,
|
||||||
|
//this makes sense to keep in memory for streaming, a map of which blocks occupy what space
|
||||||
|
bvh:BvhNode<BlockId>,
|
||||||
|
//something something resources hashmaps
|
||||||
|
resource_blocks:ResourceMap<BlockId>,
|
||||||
|
//resource_external:ResourceMap<ResourceId>,
|
||||||
}
|
}
|
||||||
impl<R:BinReaderExt> StreamableMap<R>{
|
impl<R:BinReaderExt> StreamableMap<R>{
|
||||||
pub(crate) fn new(file:crate::file::File<R>)->Result<Self,Error>{
|
pub(crate) fn new(mut file:crate::file::File<R>)->Result<Self,Error>{
|
||||||
Err(Error::InvalidHeader)
|
//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<Vec<model::Model>,Error>{
|
pub fn get_intersecting_region_block_ids(&self,aabb:&Aabb)->Vec<BlockId>{
|
||||||
//load region from disk
|
let mut block_ids=Vec::new();
|
||||||
//parse the models and determine what resources need to be loaded
|
self.bvh.the_tester(aabb,&mut |block_id|block_ids.push(block_id));
|
||||||
//load resources into self.resources
|
block_ids
|
||||||
//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 load_resource(&mut self,resource_id:ResourceId)->Resource{
|
pub fn load_region(&mut self,block_id:BlockId)->Result<Vec<model::Model>,Error>{
|
||||||
// //
|
read_region(&mut self.file,block_id)
|
||||||
// }
|
}
|
||||||
}
|
pub fn load_mesh(&mut self,mesh_id:model::MeshId)->Result<model::Mesh,Error>{
|
||||||
|
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<Vec<u8>,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<strafesnet_common::map::CompleteMap,Error>{
|
||||||
|
//load all meshes
|
||||||
|
let meshes=self.resource_blocks.meshes.into_values().map(|block_id|
|
||||||
|
read_mesh(&mut self.file,block_id)
|
||||||
|
).collect::<Result<Vec<_>,_>>()?;
|
||||||
|
//load all textures
|
||||||
|
let textures=self.resource_blocks.textures.into_values().map(|block_id|
|
||||||
|
read_texture(&mut self.file,block_id)
|
||||||
|
).collect::<Result<Vec<_>,_>>()?;
|
||||||
|
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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user