new loading api
This commit is contained in:
parent
b8283d921f
commit
45b2af405b
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -354,7 +354,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "strafesnet_common"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.itzana.me/StrafesNET/common?rev=47cdea0c8a5d10a2440ca6270a975d560aa3642d#47cdea0c8a5d10a2440ca6270a975d560aa3642d"
|
||||
source = "git+https://git.itzana.me/StrafesNET/common?rev=093a54c527134ef7020a22a0f5778df8cba60228#093a54c527134ef7020a22a0f5778df8cba60228"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"id",
|
||||
|
@ -7,6 +7,6 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
glam = "0.25.0"
|
||||
strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "47cdea0c8a5d10a2440ca6270a975d560aa3642d" }
|
||||
strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "093a54c527134ef7020a22a0f5778df8cba60228" }
|
||||
vbsp = "0.5.0"
|
||||
vmdl = "0.1.1"
|
||||
|
292
src/bsp.rs
292
src/bsp.rs
@ -4,31 +4,55 @@ 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<AcquireTextureId,AcquireMeshId>(
|
||||
pub fn convert_bsp<AcquireRenderConfigId,AcquireMeshId>(
|
||||
bsp:&vbsp::Bsp,
|
||||
mut acquire_texture_id:AcquireTextureId,
|
||||
acquire_render_config_id:&mut AcquireRenderConfigId,
|
||||
mut acquire_mesh_id:AcquireMeshId
|
||||
)->strafesnet_common::map::CompleteMap
|
||||
)->PartialMap1
|
||||
where
|
||||
AcquireTextureId:FnMut(&str)->model::TextureId,
|
||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
||||
AcquireMeshId:FnMut(&str)->model::MeshId,
|
||||
{
|
||||
let mut unique_render_configs=Vec::new();
|
||||
|
||||
//figure out real attributes later
|
||||
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);
|
||||
|
||||
let mut prop_mesh_count=0;
|
||||
//declare all prop models to Loader
|
||||
for prop in bsp.static_props(){
|
||||
acquire_mesh_id(prop.model());
|
||||
let prop_models=bsp.static_props().map(|prop|{
|
||||
//get or create mesh_id
|
||||
let mesh_id=acquire_mesh_id(prop.model());
|
||||
//not the most failsafe code but this is just for the map tool lmao
|
||||
if prop_mesh_count==mesh_id.get(){
|
||||
prop_mesh_count+=1;
|
||||
};
|
||||
let placement=prop.as_prop_placement();
|
||||
model::Model{
|
||||
mesh:mesh_id,
|
||||
attributes:TEMP_TOUCH_ME_ATTRIBUTE,
|
||||
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)),
|
||||
),
|
||||
color:glam::Vec4::ONE,
|
||||
}
|
||||
}).collect();
|
||||
|
||||
//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<model::Mesh>,Vec<model::Model>)=bsp.models().enumerate().map(|(mesh_id,world_model)|{
|
||||
let (world_meshes,world_models):(Vec<model::Mesh>,Vec<model::Model>)=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();
|
||||
@ -45,14 +69,9 @@ where
|
||||
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());
|
||||
//this automatically figures out what the texture is trying to do and creates
|
||||
//a render config for it, and then returns the id to that render config
|
||||
let render_id=acquire_render_config_id(Some(face_texture_data.name()));
|
||||
|
||||
//normal
|
||||
let normal=face.normal();
|
||||
@ -111,36 +130,114 @@ where
|
||||
)
|
||||
}).unzip();
|
||||
|
||||
map::CompleteMap{
|
||||
PartialMap1{
|
||||
attributes:unique_attributes,
|
||||
meshes,
|
||||
render_configs:unique_render_configs,
|
||||
models,
|
||||
world_meshes,
|
||||
prop_models,
|
||||
world_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);
|
||||
//partially constructed map types
|
||||
pub struct PartialMap1{
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
prop_models:Vec<model::Model>,
|
||||
world_meshes:Vec<model::Mesh>,
|
||||
world_models:Vec<model::Model>,
|
||||
modes:strafesnet_common::gameplay_modes::Modes,
|
||||
}
|
||||
impl PartialMap1{
|
||||
pub fn add_prop_meshes<AcquireRenderConfigId>(
|
||||
self,
|
||||
prop_meshes:impl IntoIterator<Item=(model::MeshId,crate::data::ModelData)>,
|
||||
acquire_render_config_id:&mut AcquireRenderConfigId,
|
||||
)->PartialMap2
|
||||
where
|
||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
||||
{
|
||||
PartialMap2{
|
||||
attributes:self.attributes,
|
||||
prop_meshes:prop_meshes.into_iter().filter_map(|(mesh_id,model_data)|
|
||||
//this will generate new render ids and texture ids
|
||||
match convert_mesh(model_data,acquire_render_config_id){
|
||||
Ok(mesh)=>Some((mesh_id,mesh)),
|
||||
Err(e)=>{
|
||||
println!("error converting mesh: {e}");
|
||||
None
|
||||
}
|
||||
}
|
||||
).collect(),
|
||||
prop_models:self.prop_models,
|
||||
world_meshes:self.world_meshes,
|
||||
world_models:self.world_models,
|
||||
modes:self.modes,
|
||||
}
|
||||
}
|
||||
}
|
||||
pub struct PartialMap2{
|
||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||
prop_meshes:Vec<(model::MeshId,model::Mesh)>,
|
||||
prop_models:Vec<model::Model>,
|
||||
world_meshes:Vec<model::Mesh>,
|
||||
world_models:Vec<model::Model>,
|
||||
modes:strafesnet_common::gameplay_modes::Modes,
|
||||
}
|
||||
impl PartialMap2{
|
||||
pub fn add_render_configs_and_textures(
|
||||
mut self,
|
||||
render_configs:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
|
||||
textures:impl IntoIterator<Item=(model::TextureId,Vec<u8>)>,
|
||||
)->map::CompleteMap{
|
||||
//merge mesh and model lists, flatten and remap all ids
|
||||
let mesh_id_offset=self.world_meshes.len();
|
||||
println!("prop_meshes.len()={}",self.prop_meshes.len());
|
||||
let (mut prop_meshes,prop_mesh_id_map):(Vec<model::Mesh>,std::collections::HashMap<model::MeshId,model::MeshId>)
|
||||
=self.prop_meshes.into_iter().enumerate().map(|(new_mesh_id,(old_mesh_id,mesh))|{
|
||||
(mesh,(old_mesh_id,model::MeshId::new((mesh_id_offset+new_mesh_id) as u32)))
|
||||
}).unzip();
|
||||
self.world_meshes.append(&mut prop_meshes);
|
||||
//there is no modes or runtime behaviour with references to the model ids currently
|
||||
//so just relentlessly cull them if the mesh is missing
|
||||
self.world_models.extend(self.prop_models.into_iter().filter_map(|mut model|
|
||||
prop_mesh_id_map.get(&model.mesh).map(|&new_mesh_id|{
|
||||
model.mesh=new_mesh_id;
|
||||
model
|
||||
})
|
||||
));
|
||||
//let mut models=Vec::new();
|
||||
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>)
|
||||
=textures.into_iter()
|
||||
//.filter_map(f) cull unused textures
|
||||
.enumerate().map(|(new_texture_id,(old_texture_id,texture))|{
|
||||
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
||||
}).unzip();
|
||||
let render_configs=render_configs.into_iter().map(|(render_config_id,mut render_config)|{
|
||||
//this may generate duplicate no-texture render configs but idc
|
||||
render_config.texture=render_config.texture.and_then(|texture_id|
|
||||
texture_id_map.get(&texture_id).copied()
|
||||
);
|
||||
render_config
|
||||
}).collect();
|
||||
map::CompleteMap{
|
||||
modes:self.modes,
|
||||
attributes:self.attributes,
|
||||
meshes:self.world_meshes,
|
||||
models:self.world_models,
|
||||
textures,
|
||||
render_configs,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_mesh<AcquireRenderConfigId>(
|
||||
model_data:crate::data::ModelData,
|
||||
acquire_render_config_id:&mut AcquireRenderConfigId,
|
||||
)->Result<model::Mesh,vmdl::ModelError>
|
||||
where
|
||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
||||
{
|
||||
let model=model_data.read_model()?;
|
||||
let texture_paths=model.texture_directories();
|
||||
if texture_paths.len()!=1{
|
||||
println!("WARNING: multiple texture paths");
|
||||
@ -162,82 +259,47 @@ where
|
||||
color:model::ColorId::new(0),
|
||||
});
|
||||
}
|
||||
let mut graphics_groups=Vec::new();
|
||||
let mut physics_groups=Vec::new();
|
||||
let polygon_groups=model.meshes().enumerate().map(|(polygon_group_id,mesh)|{
|
||||
let polygon_group_id=model::PolygonGroupId::new(polygon_group_id as u32);
|
||||
|
||||
let model_id=prop_models.len();
|
||||
model_map.insert(model_name,model_id);
|
||||
prop_models.push(model::Mesh{
|
||||
let render_id=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);
|
||||
acquire_render_config_id(path.as_os_str().to_str())
|
||||
}else{
|
||||
acquire_render_config_id(None)
|
||||
};
|
||||
|
||||
graphics_groups.push(model::IndexedGraphicsGroup{
|
||||
render:render_id,
|
||||
groups:vec![polygon_group_id],
|
||||
});
|
||||
physics_groups.push(model::IndexedPhysicsGroup{
|
||||
groups:vec![polygon_group_id],
|
||||
});
|
||||
model::PolygonGroup::PolygonList(model::PolygonList::new(
|
||||
//looking at the code, it would seem that the strips are pre-deindexed into triangle lists when calling this function
|
||||
mesh.vertex_strip_indices().flat_map(|mut strip|
|
||||
std::iter::from_fn(move||{
|
||||
match (strip.next(),strip.next(),strip.next()){
|
||||
(Some(v1),Some(v2),Some(v3))=>Some([v1,v2,v3].map(|vertex_id|model::VertexId::new(vertex_id as u32)).to_vec()),
|
||||
//ignore extra vertices, not sure what to do in this case, failing the whole conversion could be appropriate
|
||||
_=>None,
|
||||
}
|
||||
})
|
||||
).collect()
|
||||
))
|
||||
}).collect();
|
||||
Ok(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::<Vec<usize>>().chunks(3).map(|tri|{
|
||||
model::IndexedPolygon{vertices:vec![tri[0] as u32,tri[1] as u32,tri[2] as u32]}
|
||||
}).collect::<Vec<model::IndexedPolygon>>()
|
||||
}).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);
|
||||
*/
|
||||
polygon_groups,
|
||||
graphics_groups,
|
||||
physics_groups,
|
||||
})
|
||||
}
|
||||
|
60
src/data.rs
Normal file
60
src/data.rs
Normal file
@ -0,0 +1,60 @@
|
||||
pub struct Bsp(vbsp::Bsp);
|
||||
impl Bsp{
|
||||
pub const fn new(value:vbsp::Bsp)->Self{
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl AsRef<vbsp::Bsp> for Bsp{
|
||||
fn as_ref(&self)->&vbsp::Bsp{
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MdlData(Vec<u8>);
|
||||
impl MdlData{
|
||||
pub const fn new(value:Vec<u8>)->Self{
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl AsRef<[u8]> for MdlData{
|
||||
fn as_ref(&self)->&[u8]{
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
pub struct VtxData(Vec<u8>);
|
||||
impl VtxData{
|
||||
pub const fn new(value:Vec<u8>)->Self{
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl AsRef<[u8]> for VtxData{
|
||||
fn as_ref(&self)->&[u8]{
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
pub struct VvdData(Vec<u8>);
|
||||
impl VvdData{
|
||||
pub const fn new(value:Vec<u8>)->Self{
|
||||
Self(value)
|
||||
}
|
||||
}
|
||||
impl AsRef<[u8]> for VvdData{
|
||||
fn as_ref(&self)->&[u8]{
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ModelData{
|
||||
pub mdl:MdlData,
|
||||
pub vtx:VtxData,
|
||||
pub vvd:VvdData,
|
||||
}
|
||||
impl ModelData{
|
||||
pub fn read_model(&self)->Result<vmdl::Model,vmdl::ModelError>{
|
||||
Ok(vmdl::Model::from_parts(
|
||||
vmdl::mdl::Mdl::read(self.mdl.as_ref())?,
|
||||
vmdl::vtx::Vtx::read(self.vtx.as_ref())?,
|
||||
vmdl::vvd::Vvd::read(self.vvd.as_ref())?,
|
||||
))
|
||||
}
|
||||
}
|
15
src/lib.rs
15
src/lib.rs
@ -1,6 +1,7 @@
|
||||
mod bsp;
|
||||
pub mod data;
|
||||
|
||||
pub struct Bsp(vbsp::Bsp);
|
||||
pub use data::Bsp;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ReadError{
|
||||
@ -20,17 +21,17 @@ pub fn read<R:std::io::Read>(mut input:R)->Result<Bsp,ReadError>{
|
||||
//TODO: mmap
|
||||
input.read_to_end(&mut s).map_err(ReadError::Io)?;
|
||||
|
||||
vbsp::Bsp::read(s.as_slice()).map(Bsp).map_err(ReadError::Bsp)
|
||||
vbsp::Bsp::read(s.as_slice()).map(Bsp::new).map_err(ReadError::Bsp)
|
||||
}
|
||||
|
||||
pub fn convert<AcquireTextureId,AcquireMeshId>(
|
||||
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
||||
bsp:&Bsp,
|
||||
acquire_texture_id:AcquireTextureId,
|
||||
acquire_render_config_id:&mut AcquireRenderConfigId,
|
||||
acquire_mesh_id:AcquireMeshId
|
||||
)->strafesnet_common::map::CompleteMap
|
||||
)->bsp::PartialMap1
|
||||
where
|
||||
AcquireTextureId:FnMut(&str)->strafesnet_common::model::TextureId,
|
||||
AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
|
||||
AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
|
||||
{
|
||||
bsp::convert(&bsp.0,acquire_texture_id,acquire_mesh_id)
|
||||
bsp::convert_bsp(bsp.as_ref(),acquire_render_config_id,acquire_mesh_id)
|
||||
}
|
Loading…
Reference in New Issue
Block a user