Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
dc35865eca | |||
d51415af1b | |||
4439f1e8fe | |||
bd70712bae | |||
d4315c7df4 | |||
446aeec299 | |||
2be1ad0c65 | |||
453e439849 | |||
926e57790e | |||
012d2d1837 | |||
93c462aa81 | |||
52172e94fd | |||
720ab43f95 | |||
2c729adf64 | |||
3467bc77b0 | |||
2cf5ff5059 | |||
b550778a60 | |||
69599b23be | |||
2d9ad990c2 |
18
Cargo.lock
generated
18
Cargo.lock
generated
@ -1955,9 +1955,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rbx_mesh"
|
name = "rbx_mesh"
|
||||||
version = "0.1.2"
|
version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "864ead0e98afce28c960f653d6203483834890d07f87b60e2f01415530a2fe9d"
|
checksum = "1205fdae1f9a8bfd5d8fbe6036066673d530ee392f7840d6f8a24e763559c7fd"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"binrw",
|
"binrw",
|
||||||
"lazy-regex",
|
"lazy-regex",
|
||||||
@ -2015,6 +2015,13 @@ dependencies = [
|
|||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rbxassetid"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.4.1"
|
version = "0.4.1"
|
||||||
@ -2322,6 +2329,7 @@ version = "0.2.2"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"glam",
|
"glam",
|
||||||
"strafesnet_common",
|
"strafesnet_common",
|
||||||
|
"strafesnet_deferred_loader",
|
||||||
"vbsp",
|
"vbsp",
|
||||||
"vmdl",
|
"vmdl",
|
||||||
]
|
]
|
||||||
@ -2341,11 +2349,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strafesnet_deferred_loader"
|
name = "strafesnet_deferred_loader"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strafesnet_common",
|
"strafesnet_common",
|
||||||
"url",
|
|
||||||
"vbsp",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@ -2384,8 +2390,10 @@ dependencies = [
|
|||||||
"rbx_mesh",
|
"rbx_mesh",
|
||||||
"rbx_reflection_database",
|
"rbx_reflection_database",
|
||||||
"rbx_xml",
|
"rbx_xml",
|
||||||
|
"rbxassetid",
|
||||||
"roblox_emulator",
|
"roblox_emulator",
|
||||||
"strafesnet_common",
|
"strafesnet_common",
|
||||||
|
"strafesnet_deferred_loader",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -12,6 +12,7 @@ members = [
|
|||||||
"lib/linear_ops",
|
"lib/linear_ops",
|
||||||
"lib/ratio_ops",
|
"lib/ratio_ops",
|
||||||
"lib/rbx_loader",
|
"lib/rbx_loader",
|
||||||
|
"lib/rbxassetid",
|
||||||
"lib/roblox_emulator",
|
"lib/roblox_emulator",
|
||||||
"lib/snf",
|
"lib/snf",
|
||||||
"strafe-client",
|
"strafe-client",
|
||||||
|
@ -12,5 +12,6 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
glam = "0.29.0"
|
glam = "0.29.0"
|
||||||
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
||||||
|
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader" }
|
||||||
vbsp = "0.6.0"
|
vbsp = "0.6.0"
|
||||||
vmdl = "0.2.0"
|
vmdl = "0.2.0"
|
||||||
|
@ -1,18 +1,18 @@
|
|||||||
use strafesnet_common::{map,model,integer,gameplay_attributes};
|
use std::borrow::Cow;
|
||||||
|
|
||||||
const VALVE_SCALE:f32=1.0/16.0;
|
use strafesnet_common::{map,model,integer,gameplay_attributes};
|
||||||
fn valve_transform([x,y,z]:[f32;3])->integer::Planar64Vec3{
|
use strafesnet_deferred_loader::deferred_loader::{MeshDeferredLoader,RenderConfigDeferredLoader};
|
||||||
integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
|
use strafesnet_deferred_loader::mesh::Meshes;
|
||||||
}
|
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
|
||||||
pub fn convert_bsp<AcquireRenderConfigId,AcquireMeshId>(
|
|
||||||
bsp:&vbsp::Bsp,
|
use crate::valve_transform;
|
||||||
mut acquire_render_config_id:AcquireRenderConfigId,
|
|
||||||
mut acquire_mesh_id:AcquireMeshId
|
pub fn convert<'a>(
|
||||||
)->PartialMap1
|
bsp:&'a crate::Bsp,
|
||||||
where
|
render_config_deferred_loader:&mut RenderConfigDeferredLoader<Cow<'a,str>>,
|
||||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
mesh_deferred_loader:&mut MeshDeferredLoader<&'a str>,
|
||||||
AcquireMeshId:FnMut(&str)->model::MeshId,
|
)->PartialMap1{
|
||||||
{
|
let bsp=bsp.as_ref();
|
||||||
//figure out real attributes later
|
//figure out real attributes later
|
||||||
let mut unique_attributes=Vec::new();
|
let mut unique_attributes=Vec::new();
|
||||||
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
unique_attributes.push(gameplay_attributes::CollisionAttributes::Decoration);
|
||||||
@ -22,7 +22,7 @@ where
|
|||||||
//declare all prop models to Loader
|
//declare all prop models to Loader
|
||||||
let prop_models=bsp.static_props().map(|prop|{
|
let prop_models=bsp.static_props().map(|prop|{
|
||||||
//get or create mesh_id
|
//get or create mesh_id
|
||||||
let mesh_id=acquire_mesh_id(prop.model());
|
let mesh_id=mesh_deferred_loader.acquire_mesh_id(prop.model());
|
||||||
//not the most failsafe code but this is just for the map tool lmao
|
//not the most failsafe code but this is just for the map tool lmao
|
||||||
if prop_mesh_count==mesh_id.get(){
|
if prop_mesh_count==mesh_id.get(){
|
||||||
prop_mesh_count+=1;
|
prop_mesh_count+=1;
|
||||||
@ -65,7 +65,7 @@ where
|
|||||||
|
|
||||||
//this automatically figures out what the texture is trying to do and creates
|
//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
|
//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()));
|
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(face_texture_data.name().into()));
|
||||||
|
|
||||||
//normal
|
//normal
|
||||||
let normal=face.normal();
|
let normal=face.normal();
|
||||||
@ -176,26 +176,13 @@ pub struct PartialMap1{
|
|||||||
modes:strafesnet_common::gameplay_modes::Modes,
|
modes:strafesnet_common::gameplay_modes::Modes,
|
||||||
}
|
}
|
||||||
impl PartialMap1{
|
impl PartialMap1{
|
||||||
pub fn add_prop_meshes<AcquireRenderConfigId>(
|
pub fn add_prop_meshes<'a>(
|
||||||
self,
|
self,
|
||||||
prop_meshes:impl IntoIterator<Item=(model::MeshId,crate::data::ModelData)>,
|
prop_meshes:Meshes,
|
||||||
mut acquire_render_config_id:AcquireRenderConfigId,
|
)->PartialMap2{
|
||||||
)->PartialMap2
|
|
||||||
where
|
|
||||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
|
||||||
{
|
|
||||||
PartialMap2{
|
PartialMap2{
|
||||||
attributes:self.attributes,
|
attributes:self.attributes,
|
||||||
prop_meshes:prop_meshes.into_iter().filter_map(|(mesh_id,model_data)|
|
prop_meshes:prop_meshes.consume().collect(),
|
||||||
//this will generate new render ids and texture ids
|
|
||||||
match convert_mesh(model_data,&mut acquire_render_config_id){
|
|
||||||
Ok(mesh)=>Some((mesh_id,mesh)),
|
|
||||||
Err(e)=>{
|
|
||||||
println!("error converting mesh: {e}");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
).collect(),
|
|
||||||
prop_models:self.prop_models,
|
prop_models:self.prop_models,
|
||||||
world_meshes:self.world_meshes,
|
world_meshes:self.world_meshes,
|
||||||
world_models:self.world_models,
|
world_models:self.world_models,
|
||||||
@ -214,8 +201,7 @@ pub struct PartialMap2{
|
|||||||
impl PartialMap2{
|
impl PartialMap2{
|
||||||
pub fn add_render_configs_and_textures(
|
pub fn add_render_configs_and_textures(
|
||||||
mut self,
|
mut self,
|
||||||
render_configs:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
|
render_configs:RenderConfigs,
|
||||||
textures:impl IntoIterator<Item=(model::TextureId,Vec<u8>)>,
|
|
||||||
)->map::CompleteMap{
|
)->map::CompleteMap{
|
||||||
//merge mesh and model lists, flatten and remap all ids
|
//merge mesh and model lists, flatten and remap all ids
|
||||||
let mesh_id_offset=self.world_meshes.len();
|
let mesh_id_offset=self.world_meshes.len();
|
||||||
@ -234,10 +220,11 @@ impl PartialMap2{
|
|||||||
})
|
})
|
||||||
));
|
));
|
||||||
//let mut models=Vec::new();
|
//let mut models=Vec::new();
|
||||||
|
let (textures,render_configs)=render_configs.consume();
|
||||||
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>)
|
let (textures,texture_id_map):(Vec<Vec<u8>>,std::collections::HashMap<model::TextureId,model::TextureId>)
|
||||||
=textures.into_iter()
|
=textures.into_iter()
|
||||||
//.filter_map(f) cull unused textures
|
//.filter_map(f) cull unused textures
|
||||||
.enumerate().map(|(new_texture_id,(old_texture_id,texture))|{
|
.enumerate().map(|(new_texture_id,(old_texture_id,Texture::ImageDDS(texture)))|{
|
||||||
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
||||||
}).unzip();
|
}).unzip();
|
||||||
let render_configs=render_configs.into_iter().map(|(_render_config_id,mut render_config)|{
|
let render_configs=render_configs.into_iter().map(|(_render_config_id,mut render_config)|{
|
||||||
@ -257,77 +244,3 @@ impl PartialMap2{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
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(vertex.position.into()));
|
|
||||||
spam_normal.push(valve_transform(vertex.normal.into()));
|
|
||||||
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 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 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,
|
|
||||||
polygon_groups,
|
|
||||||
graphics_groups,
|
|
||||||
physics_groups,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
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())?,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,7 +1,23 @@
|
|||||||
mod bsp;
|
mod bsp;
|
||||||
pub mod data;
|
mod mesh;
|
||||||
|
pub mod loader;
|
||||||
|
|
||||||
pub use data::Bsp;
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const VALVE_SCALE:f32=1.0/16.0;
|
||||||
|
pub(crate) fn valve_transform([x,y,z]:[f32;3])->strafesnet_common::integer::Planar64Vec3{
|
||||||
|
strafesnet_common::integer::vec3::try_from_f32_array([x*VALVE_SCALE,z*VALVE_SCALE,-y*VALVE_SCALE]).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadError{
|
pub enum ReadError{
|
||||||
@ -24,14 +40,4 @@ pub fn read<R:std::io::Read>(mut input:R)->Result<Bsp,ReadError>{
|
|||||||
vbsp::Bsp::read(s.as_slice()).map(Bsp::new).map_err(ReadError::Bsp)
|
vbsp::Bsp::read(s.as_slice()).map(Bsp::new).map_err(ReadError::Bsp)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
pub use bsp::convert;
|
||||||
bsp:&Bsp,
|
|
||||||
acquire_render_config_id:AcquireRenderConfigId,
|
|
||||||
acquire_mesh_id:AcquireMeshId
|
|
||||||
)->bsp::PartialMap1
|
|
||||||
where
|
|
||||||
AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
|
|
||||||
AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
|
|
||||||
{
|
|
||||||
bsp::convert_bsp(bsp.as_ref(),acquire_render_config_id,acquire_mesh_id)
|
|
||||||
}
|
|
||||||
|
112
lib/bsp_loader/src/loader.rs
Normal file
112
lib/bsp_loader/src/loader.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use std::{borrow::Cow, io::Read};
|
||||||
|
|
||||||
|
use strafesnet_common::model::Mesh;
|
||||||
|
use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
|
||||||
|
|
||||||
|
use crate::{mesh::ModelData, Bsp};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TextureError{
|
||||||
|
Io(std::io::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for TextureError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for TextureError{}
|
||||||
|
impl From<std::io::Error> for TextureError{
|
||||||
|
fn from(value:std::io::Error)->Self{
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextureLoader<'a>(std::marker::PhantomData<&'a ()>);
|
||||||
|
impl TextureLoader<'_>{
|
||||||
|
pub fn new()->Self{
|
||||||
|
Self(std::marker::PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Loader for TextureLoader<'a>{
|
||||||
|
type Error=TextureError;
|
||||||
|
type Index=Cow<'a,str>;
|
||||||
|
type Resource=Texture;
|
||||||
|
fn load(&mut self,index:Self::Index)->Result<Self::Resource,Self::Error>{
|
||||||
|
let file_name=format!("textures/{}.dds",index);
|
||||||
|
let mut file=std::fs::File::open(file_name)?;
|
||||||
|
let mut data=Vec::new();
|
||||||
|
file.read_to_end(&mut data)?;
|
||||||
|
Ok(Texture::ImageDDS(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MeshError{
|
||||||
|
Io(std::io::Error),
|
||||||
|
VMDL(vmdl::ModelError),
|
||||||
|
VBSP(vbsp::BspError),
|
||||||
|
MissingMdl,
|
||||||
|
MissingVtx,
|
||||||
|
MissingVvd,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for MeshError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for MeshError{}
|
||||||
|
impl From<std::io::Error> for MeshError{
|
||||||
|
fn from(value:std::io::Error)->Self{
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<vmdl::ModelError> for MeshError{
|
||||||
|
fn from(value:vmdl::ModelError)->Self{
|
||||||
|
Self::VMDL(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<vbsp::BspError> for MeshError{
|
||||||
|
fn from(value:vbsp::BspError)->Self{
|
||||||
|
Self::VBSP(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MeshLoader<'a,'b>{
|
||||||
|
bsp:&'a Bsp,
|
||||||
|
deferred_loader:&'b mut strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader<Cow<'a,str>>,
|
||||||
|
}
|
||||||
|
impl MeshLoader<'_,'_>{
|
||||||
|
pub fn new<'a,'b>(
|
||||||
|
bsp:&'a Bsp,
|
||||||
|
deferred_loader:&'b mut strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader<Cow<'a,str>>,
|
||||||
|
)->MeshLoader<'a,'b>{
|
||||||
|
MeshLoader{
|
||||||
|
bsp,
|
||||||
|
deferred_loader,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Loader for MeshLoader<'a,'_>{
|
||||||
|
type Error=MeshError;
|
||||||
|
type Index=&'a str;
|
||||||
|
type Resource=Mesh;
|
||||||
|
fn load(&mut self,index:Self::Index)->Result<Self::Resource,Self::Error>{
|
||||||
|
let mdl_path_lower=index.to_lowercase();
|
||||||
|
//.mdl, .vvd, .dx90.vtx
|
||||||
|
let path=std::path::PathBuf::from(mdl_path_lower.as_str());
|
||||||
|
let mut vvd_path=path.clone();
|
||||||
|
let mut vtx_path=path;
|
||||||
|
vvd_path.set_extension("vvd");
|
||||||
|
vtx_path.set_extension("dx90.vtx");
|
||||||
|
// TODO: search more packs, possibly using an index of multiple packs
|
||||||
|
let bsp=self.bsp.as_ref();
|
||||||
|
let mdl=bsp.pack.get(mdl_path_lower.as_str())?.ok_or(MeshError::MissingMdl)?;
|
||||||
|
let vtx=bsp.pack.get(vvd_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVtx)?;
|
||||||
|
let vvd=bsp.pack.get(vtx_path.as_os_str().to_str().unwrap())?.ok_or(MeshError::MissingVvd)?;
|
||||||
|
let model=ModelData{mdl,vtx,vvd};
|
||||||
|
let mesh=model.convert_mesh(&mut self.deferred_loader)?;
|
||||||
|
Ok(mesh)
|
||||||
|
}
|
||||||
|
}
|
89
lib/bsp_loader/src/mesh.rs
Normal file
89
lib/bsp_loader/src/mesh.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use std::borrow::Cow;
|
||||||
|
|
||||||
|
use strafesnet_common::model;
|
||||||
|
use strafesnet_deferred_loader::deferred_loader::RenderConfigDeferredLoader;
|
||||||
|
|
||||||
|
use crate::valve_transform;
|
||||||
|
|
||||||
|
pub struct ModelData{
|
||||||
|
pub mdl:Vec<u8>,
|
||||||
|
pub vtx:Vec<u8>,
|
||||||
|
pub vvd:Vec<u8>,
|
||||||
|
}
|
||||||
|
impl ModelData{
|
||||||
|
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())?,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
pub fn convert_mesh<'a>(self,deferred_loader:&mut RenderConfigDeferredLoader<Cow<'a,str>>)->Result<model::Mesh,vmdl::ModelError>{
|
||||||
|
let model=self.read_model()?;
|
||||||
|
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(vertex.position.into()));
|
||||||
|
spam_normal.push(valve_transform(vertex.normal.into()));
|
||||||
|
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 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 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);
|
||||||
|
let index=path.as_os_str().to_str().map(|s|Cow::Owned(s.to_owned()));
|
||||||
|
deferred_loader.acquire_render_config_id(index)
|
||||||
|
}else{
|
||||||
|
deferred_loader.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,
|
||||||
|
polygon_groups,
|
||||||
|
graphics_groups,
|
||||||
|
physics_groups,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "strafesnet_deferred_loader"
|
name = "strafesnet_deferred_loader"
|
||||||
version = "0.4.1"
|
version = "0.5.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
repository = "https://git.itzana.me/StrafesNET/strafe-project"
|
||||||
license = "MIT OR Apache-2.0"
|
license = "MIT OR Apache-2.0"
|
||||||
@ -9,13 +9,5 @@ authors = ["Rhys Lloyd <krakow20@gmail.com>"]
|
|||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[features]
|
|
||||||
default = ["legacy"]
|
|
||||||
legacy = ["dep:url","dep:vbsp"]
|
|
||||||
roblox = []
|
|
||||||
source = ["dep:vbsp"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
||||||
url = { version = "2.5.2", optional = true }
|
|
||||||
vbsp = { version = "0.6.0", optional = true }
|
|
||||||
|
109
lib/deferred_loader/src/deferred_loader.rs
Normal file
109
lib/deferred_loader/src/deferred_loader.rs
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
use crate::loader::Loader;
|
||||||
|
use crate::mesh::Meshes;
|
||||||
|
use crate::texture::{RenderConfigs,Texture};
|
||||||
|
use strafesnet_common::model::{Mesh,MeshId,RenderConfig,RenderConfigId,TextureId};
|
||||||
|
|
||||||
|
pub enum LoadFailureMode{
|
||||||
|
DefaultToNone,
|
||||||
|
Fatal,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct RenderConfigDeferredLoader<H>{
|
||||||
|
texture_count:u32,
|
||||||
|
render_configs:Vec<RenderConfig>,
|
||||||
|
render_config_id_from_asset_id:HashMap<Option<H>,RenderConfigId>,
|
||||||
|
}
|
||||||
|
impl<H> RenderConfigDeferredLoader<H>{
|
||||||
|
pub fn new()->Self{
|
||||||
|
Self{
|
||||||
|
texture_count:0,
|
||||||
|
render_configs:Vec::new(),
|
||||||
|
render_config_id_from_asset_id:HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:core::hash::Hash+Eq> RenderConfigDeferredLoader<H>{
|
||||||
|
pub fn acquire_render_config_id(&mut self,index:Option<H>)->RenderConfigId{
|
||||||
|
let some_texture=index.is_some();
|
||||||
|
*self.render_config_id_from_asset_id.entry(index).or_insert_with(||{
|
||||||
|
//create the render config.
|
||||||
|
let render_config=if some_texture{
|
||||||
|
let render_config=RenderConfig::texture(TextureId::new(self.texture_count));
|
||||||
|
self.texture_count+=1;
|
||||||
|
render_config
|
||||||
|
}else{
|
||||||
|
RenderConfig::default()
|
||||||
|
};
|
||||||
|
let render_id=RenderConfigId::new(self.render_configs.len() as u32);
|
||||||
|
self.render_configs.push(render_config);
|
||||||
|
render_id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn into_render_configs<L:Loader<Index=H,Resource=Texture>>(mut self,loader:&mut L,failure_mode:LoadFailureMode)->Result<RenderConfigs,L::Error>{
|
||||||
|
let mut sorted_textures=vec![None;self.texture_count as usize];
|
||||||
|
for (index_option,render_config_id) in self.render_config_id_from_asset_id{
|
||||||
|
let render_config=&mut self.render_configs[render_config_id.get() as usize];
|
||||||
|
if let (Some(index),Some(texture_id))=(index_option,render_config.texture){
|
||||||
|
let resource_result=loader.load(index);
|
||||||
|
let texture=match failure_mode{
|
||||||
|
// if texture fails to load, use no texture
|
||||||
|
LoadFailureMode::DefaultToNone=>match resource_result{
|
||||||
|
Ok(texture)=>Some(texture),
|
||||||
|
Err(e)=>{
|
||||||
|
render_config.texture=None;
|
||||||
|
println!("Error loading resource: {e}");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// loading failure is fatal
|
||||||
|
LoadFailureMode::Fatal=>Some(resource_result?)
|
||||||
|
};
|
||||||
|
sorted_textures[texture_id.get() as usize]=texture;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(RenderConfigs::new(
|
||||||
|
sorted_textures,
|
||||||
|
self.render_configs,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MeshDeferredLoader<H>{
|
||||||
|
mesh_id_from_asset_id:HashMap<H,MeshId>,
|
||||||
|
}
|
||||||
|
impl<H> MeshDeferredLoader<H>{
|
||||||
|
pub fn new()->Self{
|
||||||
|
Self{
|
||||||
|
mesh_id_from_asset_id:HashMap::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<H:core::hash::Hash+Eq> MeshDeferredLoader<H>{
|
||||||
|
pub fn acquire_mesh_id(&mut self,index:H)->MeshId{
|
||||||
|
let mesh_id=MeshId::new(self.mesh_id_from_asset_id.len() as u32);
|
||||||
|
*self.mesh_id_from_asset_id.entry(index).or_insert(mesh_id)
|
||||||
|
}
|
||||||
|
pub fn into_meshes<L:Loader<Index=H,Resource=Mesh>>(self,loader:&mut L,failure_mode:LoadFailureMode)->Result<Meshes,L::Error>{
|
||||||
|
let mut mesh_list=vec![None;self.mesh_id_from_asset_id.len()];
|
||||||
|
for (index,mesh_id) in self.mesh_id_from_asset_id{
|
||||||
|
let resource_result=loader.load(index);
|
||||||
|
let mesh=match failure_mode{
|
||||||
|
// if mesh fails to load, use no mesh
|
||||||
|
LoadFailureMode::DefaultToNone=>match resource_result{
|
||||||
|
Ok(mesh)=>Some(mesh),
|
||||||
|
Err(e)=>{
|
||||||
|
println!("Error loading resource: {e}");
|
||||||
|
None
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// loading failure is fatal
|
||||||
|
LoadFailureMode::Fatal=>Some(resource_result?)
|
||||||
|
};
|
||||||
|
mesh_list[mesh_id.get() as usize]=mesh;
|
||||||
|
}
|
||||||
|
Ok(Meshes::new(mesh_list))
|
||||||
|
}
|
||||||
|
}
|
@ -1,34 +1,5 @@
|
|||||||
#[cfg(feature="legacy")]
|
|
||||||
mod roblox_legacy;
|
|
||||||
#[cfg(feature="legacy")]
|
|
||||||
mod source_legacy;
|
|
||||||
#[cfg(feature="roblox")]
|
|
||||||
mod roblox;
|
|
||||||
#[cfg(feature="source")]
|
|
||||||
mod source;
|
|
||||||
|
|
||||||
#[cfg(any(feature="roblox",feature="legacy"))]
|
|
||||||
pub mod rbxassetid;
|
|
||||||
|
|
||||||
|
pub mod mesh;
|
||||||
|
pub mod loader;
|
||||||
pub mod texture;
|
pub mod texture;
|
||||||
#[cfg(any(feature="source",feature="legacy"))]
|
pub mod deferred_loader;
|
||||||
pub mod valve_mesh;
|
|
||||||
#[cfg(any(feature="roblox",feature="legacy"))]
|
|
||||||
pub mod roblox_mesh;
|
|
||||||
|
|
||||||
#[cfg(feature="legacy")]
|
|
||||||
pub fn roblox_legacy()->roblox_legacy::Loader{
|
|
||||||
roblox_legacy::Loader::new()
|
|
||||||
}
|
|
||||||
#[cfg(feature="legacy")]
|
|
||||||
pub fn source_legacy()->source_legacy::Loader{
|
|
||||||
source_legacy::Loader::new()
|
|
||||||
}
|
|
||||||
#[cfg(feature="roblox")]
|
|
||||||
pub fn roblox()->roblox::Loader{
|
|
||||||
roblox::Loader::new()
|
|
||||||
}
|
|
||||||
#[cfg(feature="source")]
|
|
||||||
pub fn source()->source::Loader{
|
|
||||||
source::Loader::new()
|
|
||||||
}
|
|
||||||
|
8
lib/deferred_loader/src/loader.rs
Normal file
8
lib/deferred_loader/src/loader.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
pub trait Loader{
|
||||||
|
type Error:Error;
|
||||||
|
type Index;
|
||||||
|
type Resource;
|
||||||
|
fn load(&mut self,index:Self::Index)->Result<Self::Resource,Self::Error>;
|
||||||
|
}
|
17
lib/deferred_loader/src/mesh.rs
Normal file
17
lib/deferred_loader/src/mesh.rs
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
use strafesnet_common::model::{Mesh,MeshId};
|
||||||
|
|
||||||
|
pub struct Meshes{
|
||||||
|
meshes:Vec<Option<Mesh>>,
|
||||||
|
}
|
||||||
|
impl Meshes{
|
||||||
|
pub(crate) const fn new(meshes:Vec<Option<Mesh>>)->Self{
|
||||||
|
Self{
|
||||||
|
meshes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn consume(self)->impl Iterator<Item=(MeshId,Mesh)>{
|
||||||
|
self.meshes.into_iter().enumerate().filter_map(|(mesh_id,maybe_mesh)|
|
||||||
|
maybe_mesh.map(|mesh|(MeshId::new(mesh_id as u32),mesh))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
@ -1,48 +0,0 @@
|
|||||||
#[derive(Hash,Eq,PartialEq)]
|
|
||||||
pub struct RobloxAssetId(pub u64);
|
|
||||||
#[derive(Debug)]
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub struct StringWithError{
|
|
||||||
string:String,
|
|
||||||
error:RobloxAssetIdParseErr,
|
|
||||||
}
|
|
||||||
impl std::fmt::Display for StringWithError{
|
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
|
||||||
write!(f,"{self:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
impl std::error::Error for StringWithError{}
|
|
||||||
impl StringWithError{
|
|
||||||
const fn new(
|
|
||||||
string:String,
|
|
||||||
error:RobloxAssetIdParseErr,
|
|
||||||
)->Self{
|
|
||||||
Self{string,error}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum RobloxAssetIdParseErr{
|
|
||||||
Url(url::ParseError),
|
|
||||||
UnknownScheme,
|
|
||||||
ParseInt(std::num::ParseIntError),
|
|
||||||
MissingAssetId,
|
|
||||||
}
|
|
||||||
impl std::str::FromStr for RobloxAssetId{
|
|
||||||
type Err=StringWithError;
|
|
||||||
fn from_str(s:&str)->Result<Self,Self::Err>{
|
|
||||||
let url=url::Url::parse(s).map_err(|e|StringWithError::new(s.to_owned(),RobloxAssetIdParseErr::Url(e)))?;
|
|
||||||
let parsed_asset_id=match url.scheme(){
|
|
||||||
"rbxassetid"=>url.domain().ok_or_else(||StringWithError::new(s.to_owned(),RobloxAssetIdParseErr::MissingAssetId))?.parse(),
|
|
||||||
"http"|"https"=>{
|
|
||||||
let (_,asset_id)=url.query_pairs()
|
|
||||||
.find(|(id,_)|match id.as_ref(){
|
|
||||||
"ID"|"id"|"Id"|"iD"=>true,
|
|
||||||
_=>false,
|
|
||||||
}).ok_or_else(||StringWithError::new(s.to_owned(),RobloxAssetIdParseErr::MissingAssetId))?;
|
|
||||||
asset_id.parse()
|
|
||||||
},
|
|
||||||
_=>Err(StringWithError::new(s.to_owned(),RobloxAssetIdParseErr::UnknownScheme))?,
|
|
||||||
};
|
|
||||||
Ok(Self(parsed_asset_id.map_err(|e|StringWithError::new(s.to_owned(),RobloxAssetIdParseErr::ParseInt(e)))?))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
use std::io::Read;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use crate::roblox_mesh;
|
|
||||||
use crate::texture::{RenderConfigs,Texture};
|
|
||||||
use strafesnet_common::model::{MeshId,RenderConfig,RenderConfigId,TextureId};
|
|
||||||
use crate::rbxassetid::RobloxAssetId;
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct RenderConfigLoader{
|
|
||||||
texture_count:u32,
|
|
||||||
render_configs:Vec<RenderConfig>,
|
|
||||||
render_config_id_from_asset_id:HashMap<Option<RobloxAssetId>,RenderConfigId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RenderConfigLoader{
|
|
||||||
pub fn acquire_render_config_id(&mut self,name:Option<&str>)->RenderConfigId{
|
|
||||||
let render_id=RenderConfigId::new(self.render_config_id_from_asset_id.len() as u32);
|
|
||||||
let index=name.and_then(|name|{
|
|
||||||
match name.parse::<RobloxAssetId>(){
|
|
||||||
Ok(asset_id)=>Some(asset_id),
|
|
||||||
Err(e)=>{
|
|
||||||
println!("Failed to parse AssetId: {e}");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
|
||||||
});
|
|
||||||
*self.render_config_id_from_asset_id.entry(index).or_insert_with(||{
|
|
||||||
//create the render config.
|
|
||||||
let render_config=if name.is_some(){
|
|
||||||
let render_config=RenderConfig::texture(TextureId::new(self.texture_count));
|
|
||||||
self.texture_count+=1;
|
|
||||||
render_config
|
|
||||||
}else{
|
|
||||||
RenderConfig::default()
|
|
||||||
};
|
|
||||||
self.render_configs.push(render_config);
|
|
||||||
render_id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Default)]
|
|
||||||
pub struct MeshLoader{
|
|
||||||
mesh_id_from_asset_id:HashMap<Option<RobloxAssetId>,MeshId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MeshLoader{
|
|
||||||
pub fn acquire_mesh_id(&mut self,name:&str)->MeshId{
|
|
||||||
let mesh_id=MeshId::new(self.mesh_id_from_asset_id.len() as u32);
|
|
||||||
let index=match name.parse::<RobloxAssetId>(){
|
|
||||||
Ok(asset_id)=>Some(asset_id),
|
|
||||||
Err(e)=>{
|
|
||||||
println!("Failed to parse AssetId: {e}");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
};
|
|
||||||
*self.mesh_id_from_asset_id.entry(index).or_insert(mesh_id)
|
|
||||||
}
|
|
||||||
pub fn load_meshes(&mut self)->Result<roblox_mesh::Meshes,std::io::Error>{
|
|
||||||
let mut mesh_data=vec![None;self.mesh_id_from_asset_id.len()];
|
|
||||||
for (asset_id_option,mesh_id) in &self.mesh_id_from_asset_id{
|
|
||||||
if let Some(asset_id)=asset_id_option{
|
|
||||||
if let Ok(mut file)=std::fs::File::open(format!("meshes/{}",asset_id.0)){
|
|
||||||
//TODO: parallel
|
|
||||||
let mut data=Vec::<u8>::new();
|
|
||||||
file.read_to_end(&mut data)?;
|
|
||||||
mesh_data[mesh_id.get() as usize]=Some(roblox_mesh::RobloxMeshData::new(data));
|
|
||||||
}else{
|
|
||||||
println!("[roblox_legacy] no mesh name={}",asset_id.0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(roblox_mesh::Meshes::new(mesh_data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Loader{
|
|
||||||
render_config_loader:RenderConfigLoader,
|
|
||||||
mesh_loader:MeshLoader,
|
|
||||||
}
|
|
||||||
impl Loader{
|
|
||||||
pub fn new()->Self{
|
|
||||||
Self{
|
|
||||||
render_config_loader:RenderConfigLoader::default(),
|
|
||||||
mesh_loader:MeshLoader::default(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_inner_mut(&mut self)->(&mut RenderConfigLoader,&mut MeshLoader){
|
|
||||||
(&mut self.render_config_loader,&mut self.mesh_loader)
|
|
||||||
}
|
|
||||||
pub fn into_render_configs(mut self)->Result<RenderConfigs,std::io::Error>{
|
|
||||||
let mut sorted_textures=vec![None;self.render_config_loader.texture_count as usize];
|
|
||||||
for (asset_id_option,render_config_id) in self.render_config_loader.render_config_id_from_asset_id{
|
|
||||||
let render_config=self.render_config_loader.render_configs.get_mut(render_config_id.get() as usize).unwrap();
|
|
||||||
if let (Some(asset_id),Some(texture_id))=(asset_id_option,render_config.texture){
|
|
||||||
if let Ok(mut file)=std::fs::File::open(format!("textures/{}.dds",asset_id.0)){
|
|
||||||
//TODO: parallel
|
|
||||||
let mut data=Vec::<u8>::new();
|
|
||||||
file.read_to_end(&mut data)?;
|
|
||||||
sorted_textures[texture_id.get() as usize]=Some(Texture::ImageDDS(data));
|
|
||||||
}else{
|
|
||||||
//texture failed to load
|
|
||||||
render_config.texture=None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(RenderConfigs::new(
|
|
||||||
sorted_textures,
|
|
||||||
self.render_config_loader.render_configs,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
use strafesnet_common::model::MeshId;
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RobloxMeshData(Vec<u8>);
|
|
||||||
impl RobloxMeshData{
|
|
||||||
pub(crate) fn new(data:Vec<u8>)->Self{
|
|
||||||
Self(data)
|
|
||||||
}
|
|
||||||
pub fn get(self)->Vec<u8>{
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct Meshes{
|
|
||||||
meshes:Vec<Option<RobloxMeshData>>,
|
|
||||||
}
|
|
||||||
impl Meshes{
|
|
||||||
pub(crate) const fn new(meshes:Vec<Option<RobloxMeshData>>)->Self{
|
|
||||||
Self{
|
|
||||||
meshes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_texture(&self,texture_id:MeshId)->Option<&RobloxMeshData>{
|
|
||||||
self.meshes.get(texture_id.get() as usize)?.as_ref()
|
|
||||||
}
|
|
||||||
pub fn into_iter(self)->impl Iterator<Item=(MeshId,RobloxMeshData)>{
|
|
||||||
self.meshes.into_iter().enumerate().filter_map(|(mesh_id,maybe_mesh)|
|
|
||||||
maybe_mesh.map(|mesh|(MeshId::new(mesh_id as u32),mesh))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,102 +0,0 @@
|
|||||||
use std::io::Read;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use crate::valve_mesh;
|
|
||||||
use crate::texture::{Texture,RenderConfigs};
|
|
||||||
use strafesnet_common::model::{MeshId,TextureId,RenderConfig,RenderConfigId};
|
|
||||||
|
|
||||||
pub struct RenderConfigLoader{
|
|
||||||
texture_count:u32,
|
|
||||||
render_configs:Vec<RenderConfig>,
|
|
||||||
texture_paths:HashMap<Option<Box<str>>,RenderConfigId>,
|
|
||||||
}
|
|
||||||
impl RenderConfigLoader{
|
|
||||||
pub fn acquire_render_config_id(&mut self,name:Option<&str>)->RenderConfigId{
|
|
||||||
let render_id=RenderConfigId::new(self.texture_paths.len() as u32);
|
|
||||||
*self.texture_paths.entry(name.map(Into::into)).or_insert_with(||{
|
|
||||||
//create the render config.
|
|
||||||
let render_config=if name.is_some(){
|
|
||||||
let render_config=RenderConfig::texture(TextureId::new(self.texture_count));
|
|
||||||
self.texture_count+=1;
|
|
||||||
render_config
|
|
||||||
}else{
|
|
||||||
RenderConfig::default()
|
|
||||||
};
|
|
||||||
self.render_configs.push(render_config);
|
|
||||||
render_id
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub struct MeshLoader{
|
|
||||||
mesh_paths:HashMap<Box<str>,MeshId>,
|
|
||||||
}
|
|
||||||
impl MeshLoader{
|
|
||||||
pub fn acquire_mesh_id(&mut self,name:&str)->MeshId{
|
|
||||||
let mesh_id=MeshId::new(self.mesh_paths.len() as u32);
|
|
||||||
*self.mesh_paths.entry(name.into()).or_insert(mesh_id)
|
|
||||||
}
|
|
||||||
//load_meshes should look like load_textures
|
|
||||||
pub fn load_meshes(&mut self,bsp:&vbsp::Bsp)->valve_mesh::Meshes{
|
|
||||||
let mut mesh_data=vec![None;self.mesh_paths.len()];
|
|
||||||
for (mesh_path,mesh_id) in &self.mesh_paths{
|
|
||||||
let mesh_path_lower=mesh_path.to_lowercase();
|
|
||||||
//.mdl, .vvd, .dx90.vtx
|
|
||||||
let path=std::path::PathBuf::from(mesh_path_lower.as_str());
|
|
||||||
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(mesh_path_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)))=>{
|
|
||||||
mesh_data[mesh_id.get() as usize]=Some(valve_mesh::ModelData{
|
|
||||||
mdl:valve_mesh::MdlData::new(mdl_file),
|
|
||||||
vtx:valve_mesh::VtxData::new(vtx_file),
|
|
||||||
vvd:valve_mesh::VvdData::new(vvd_file),
|
|
||||||
});
|
|
||||||
},
|
|
||||||
_=>println!("no model name={}",mesh_path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
valve_mesh::Meshes::new(mesh_data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Loader{
|
|
||||||
render_config_loader:RenderConfigLoader,
|
|
||||||
mesh_loader:MeshLoader,
|
|
||||||
}
|
|
||||||
impl Loader{
|
|
||||||
pub fn new()->Self{
|
|
||||||
Self{
|
|
||||||
render_config_loader:RenderConfigLoader{
|
|
||||||
texture_count:0,
|
|
||||||
texture_paths:HashMap::new(),
|
|
||||||
render_configs:Vec::new(),
|
|
||||||
},
|
|
||||||
mesh_loader:MeshLoader{mesh_paths:HashMap::new()},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_inner_mut(&mut self)->(&mut RenderConfigLoader,&mut MeshLoader){
|
|
||||||
(&mut self.render_config_loader,&mut self.mesh_loader)
|
|
||||||
}
|
|
||||||
pub fn into_render_configs(mut self)->Result<RenderConfigs,std::io::Error>{
|
|
||||||
let mut sorted_textures=vec![None;self.render_config_loader.texture_count as usize];
|
|
||||||
for (texture_path,render_config_id) in self.render_config_loader.texture_paths{
|
|
||||||
let render_config=self.render_config_loader.render_configs.get_mut(render_config_id.get() as usize).unwrap();
|
|
||||||
if let (Some(texture_path),Some(texture_id))=(texture_path,render_config.texture){
|
|
||||||
if let Ok(mut file)=std::fs::File::open(format!("textures/{}.dds",texture_path)){
|
|
||||||
//TODO: parallel
|
|
||||||
let mut data=Vec::<u8>::new();
|
|
||||||
file.read_to_end(&mut data)?;
|
|
||||||
sorted_textures[texture_id.get() as usize]=Some(Texture::ImageDDS(data));
|
|
||||||
}else{
|
|
||||||
//texture failed to load
|
|
||||||
render_config.texture=None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(RenderConfigs::new(
|
|
||||||
sorted_textures,
|
|
||||||
self.render_config_loader.render_configs,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
use strafesnet_common::model::MeshId;
|
|
||||||
|
|
||||||
//duplicate this code for now
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct MdlData(Vec<u8>);
|
|
||||||
impl MdlData{
|
|
||||||
pub const fn new(value:Vec<u8>)->Self{
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
pub fn get(self)->Vec<u8>{
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct VtxData(Vec<u8>);
|
|
||||||
impl VtxData{
|
|
||||||
pub const fn new(value:Vec<u8>)->Self{
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
pub fn get(self)->Vec<u8>{
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct VvdData(Vec<u8>);
|
|
||||||
impl VvdData{
|
|
||||||
pub const fn new(value:Vec<u8>)->Self{
|
|
||||||
Self(value)
|
|
||||||
}
|
|
||||||
pub fn get(self)->Vec<u8>{
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ModelData{
|
|
||||||
pub mdl:MdlData,
|
|
||||||
pub vtx:VtxData,
|
|
||||||
pub vvd:VvdData,
|
|
||||||
}
|
|
||||||
|
|
||||||
//meshes is more prone to failure
|
|
||||||
pub struct Meshes{
|
|
||||||
meshes:Vec<Option<ModelData>>,
|
|
||||||
}
|
|
||||||
impl Meshes{
|
|
||||||
pub(crate) const fn new(meshes:Vec<Option<ModelData>>)->Self{
|
|
||||||
Self{
|
|
||||||
meshes,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn get_texture(&self,texture_id:MeshId)->Option<&ModelData>{
|
|
||||||
self.meshes.get(texture_id.get() as usize)?.as_ref()
|
|
||||||
}
|
|
||||||
pub fn into_iter(self)->impl Iterator<Item=(MeshId,ModelData)>{
|
|
||||||
self.meshes.into_iter().enumerate().filter_map(|(mesh_id,maybe_mesh)|
|
|
||||||
maybe_mesh.map(|mesh|(MeshId::new(mesh_id as u32),mesh))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,8 +15,10 @@ glam = "0.29.0"
|
|||||||
lazy-regex = "3.1.0"
|
lazy-regex = "3.1.0"
|
||||||
rbx_binary = { version = "0.7.4", registry = "strafesnet" }
|
rbx_binary = { version = "0.7.4", registry = "strafesnet" }
|
||||||
rbx_dom_weak = { version = "2.7.0", registry = "strafesnet" }
|
rbx_dom_weak = { version = "2.7.0", registry = "strafesnet" }
|
||||||
rbx_mesh = "0.1.2"
|
rbx_mesh = "0.2.0"
|
||||||
rbx_reflection_database = { version = "0.2.10", registry = "strafesnet" }
|
rbx_reflection_database = { version = "0.2.10", registry = "strafesnet" }
|
||||||
rbx_xml = { version = "0.13.3", registry = "strafesnet" }
|
rbx_xml = { version = "0.13.3", registry = "strafesnet" }
|
||||||
|
rbxassetid = { version = "0.1.0", path = "../rbxassetid" }
|
||||||
roblox_emulator = { path = "../roblox_emulator", registry = "strafesnet" }
|
roblox_emulator = { path = "../roblox_emulator", registry = "strafesnet" }
|
||||||
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
strafesnet_common = { path = "../common", registry = "strafesnet" }
|
||||||
|
strafesnet_deferred_loader = { version = "0.5.0", path = "../deferred_loader" }
|
||||||
|
6
lib/rbx_loader/src/directories.rs
Normal file
6
lib/rbx_loader/src/directories.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// TODO: make a directories structure like strafe client
|
||||||
|
struct Directories{
|
||||||
|
textures:PathBuf,
|
||||||
|
meshes:PathBuf,
|
||||||
|
unions:PathBuf,
|
||||||
|
}
|
@ -3,6 +3,8 @@ use rbx_dom_weak::WeakDom;
|
|||||||
|
|
||||||
mod rbx;
|
mod rbx;
|
||||||
mod mesh;
|
mod mesh;
|
||||||
|
mod union;
|
||||||
|
pub mod loader;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
|
||||||
pub mod data{
|
pub mod data{
|
||||||
@ -94,14 +96,4 @@ pub fn read<R:Read>(input:R)->Result<Model,ReadError>{
|
|||||||
|
|
||||||
//ConvertError
|
//ConvertError
|
||||||
|
|
||||||
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
pub use rbx::convert;
|
||||||
dom:impl AsRef<WeakDom>,
|
|
||||||
acquire_render_config_id:AcquireRenderConfigId,
|
|
||||||
acquire_mesh_id:AcquireMeshId
|
|
||||||
)->rbx::PartialMap1
|
|
||||||
where
|
|
||||||
AcquireRenderConfigId:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId,
|
|
||||||
AcquireMeshId:FnMut(&str)->strafesnet_common::model::MeshId,
|
|
||||||
{
|
|
||||||
rbx::convert(&dom.as_ref(),acquire_render_config_id,acquire_mesh_id)
|
|
||||||
}
|
|
||||||
|
180
lib/rbx_loader/src/loader.rs
Normal file
180
lib/rbx_loader/src/loader.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
use std::io::Read;
|
||||||
|
use rbxassetid::{RobloxAssetId,RobloxAssetIdParseErr};
|
||||||
|
use strafesnet_common::model::Mesh;
|
||||||
|
use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
|
||||||
|
|
||||||
|
use crate::data::RobloxMeshBytes;
|
||||||
|
|
||||||
|
fn read_entire_file(path:impl AsRef<std::path::Path>)->Result<Vec<u8>,std::io::Error>{
|
||||||
|
let mut file=std::fs::File::open(path)?;
|
||||||
|
let mut data=Vec::new();
|
||||||
|
file.read_to_end(&mut data)?;
|
||||||
|
Ok(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum TextureError{
|
||||||
|
Io(std::io::Error),
|
||||||
|
RobloxAssetIdParse(RobloxAssetIdParseErr),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for TextureError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for TextureError{}
|
||||||
|
impl From<std::io::Error> for TextureError{
|
||||||
|
fn from(value:std::io::Error)->Self{
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<RobloxAssetIdParseErr> for TextureError{
|
||||||
|
fn from(value:RobloxAssetIdParseErr)->Self{
|
||||||
|
Self::RobloxAssetIdParse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TextureLoader<'a>(std::marker::PhantomData<&'a ()>);
|
||||||
|
impl TextureLoader<'_>{
|
||||||
|
pub fn new()->Self{
|
||||||
|
Self(std::marker::PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Loader for TextureLoader<'a>{
|
||||||
|
type Error=TextureError;
|
||||||
|
type Index=&'a str;
|
||||||
|
type Resource=Texture;
|
||||||
|
fn load(&mut self,index:Self::Index)->Result<Self::Resource,Self::Error>{
|
||||||
|
let RobloxAssetId(asset_id)=index.parse()?;
|
||||||
|
let file_name=format!("textures/{}.dds",asset_id);
|
||||||
|
let data=read_entire_file(file_name)?;
|
||||||
|
Ok(Texture::ImageDDS(data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum MeshError{
|
||||||
|
Io(std::io::Error),
|
||||||
|
RobloxAssetIdParse(RobloxAssetIdParseErr),
|
||||||
|
Mesh(crate::mesh::Error),
|
||||||
|
Union(crate::union::Error),
|
||||||
|
DecodeBinary(rbx_binary::DecodeError),
|
||||||
|
OneChildPolicy,
|
||||||
|
MissingInstance,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for MeshError{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for MeshError{}
|
||||||
|
impl From<std::io::Error> for MeshError{
|
||||||
|
fn from(value:std::io::Error)->Self{
|
||||||
|
Self::Io(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<RobloxAssetIdParseErr> for MeshError{
|
||||||
|
fn from(value:RobloxAssetIdParseErr)->Self{
|
||||||
|
Self::RobloxAssetIdParse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<crate::mesh::Error> for MeshError{
|
||||||
|
fn from(value:crate::mesh::Error)->Self{
|
||||||
|
Self::Mesh(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<crate::union::Error> for MeshError{
|
||||||
|
fn from(value:crate::union::Error)->Self{
|
||||||
|
Self::Union(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<rbx_binary::DecodeError> for MeshError{
|
||||||
|
fn from(value:rbx_binary::DecodeError)->Self{
|
||||||
|
Self::DecodeBinary(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash,Eq,PartialEq)]
|
||||||
|
pub enum MeshType<'a>{
|
||||||
|
FileMesh,
|
||||||
|
Union{
|
||||||
|
mesh_data:&'a [u8],
|
||||||
|
physics_data:&'a [u8],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
#[derive(Hash,Eq,PartialEq)]
|
||||||
|
pub struct MeshIndex<'a>{
|
||||||
|
mesh_type:MeshType<'a>,
|
||||||
|
content:&'a str,
|
||||||
|
}
|
||||||
|
impl MeshIndex<'_>{
|
||||||
|
pub fn file_mesh(content:&str)->MeshIndex{
|
||||||
|
MeshIndex{
|
||||||
|
mesh_type:MeshType::FileMesh,
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn union<'a>(
|
||||||
|
content:&'a str,
|
||||||
|
mesh_data:&'a [u8],
|
||||||
|
physics_data:&'a [u8],
|
||||||
|
)->MeshIndex<'a>{
|
||||||
|
MeshIndex{
|
||||||
|
mesh_type:MeshType::Union{mesh_data,physics_data},
|
||||||
|
content,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MeshLoader<'a>(std::marker::PhantomData<&'a ()>);
|
||||||
|
impl MeshLoader<'_>{
|
||||||
|
pub fn new()->Self{
|
||||||
|
Self(std::marker::PhantomData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a> Loader for MeshLoader<'a>{
|
||||||
|
type Error=MeshError;
|
||||||
|
type Index=MeshIndex<'a>;
|
||||||
|
type Resource=Mesh;
|
||||||
|
fn load(&mut self,index:Self::Index)->Result<Self::Resource,Self::Error>{
|
||||||
|
let mesh=match index.mesh_type{
|
||||||
|
MeshType::FileMesh=>{
|
||||||
|
let RobloxAssetId(asset_id)=index.content.parse()?;
|
||||||
|
let file_name=format!("meshes/{}",asset_id);
|
||||||
|
let data=read_entire_file(file_name)?;
|
||||||
|
crate::mesh::convert(RobloxMeshBytes::new(data))?
|
||||||
|
},
|
||||||
|
MeshType::Union{mut physics_data,mut mesh_data}=>{
|
||||||
|
// decode asset
|
||||||
|
if !index.content.is_empty()&&(physics_data.is_empty()||mesh_data.is_empty()){
|
||||||
|
let RobloxAssetId(asset_id)=index.content.parse()?;
|
||||||
|
let file_name=format!("unions/{}",asset_id);
|
||||||
|
let data=read_entire_file(file_name)?;
|
||||||
|
let dom=rbx_binary::from_reader(std::io::Cursor::new(data))?;
|
||||||
|
let &[referent]=dom.root().children()else{
|
||||||
|
return Err(MeshError::OneChildPolicy);
|
||||||
|
};
|
||||||
|
let Some(instance)=dom.get_by_ref(referent)else{
|
||||||
|
return Err(MeshError::MissingInstance);
|
||||||
|
};
|
||||||
|
if physics_data.is_empty(){
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get("PhysicsData"){
|
||||||
|
physics_data=data.as_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if mesh_data.is_empty(){
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get("MeshData"){
|
||||||
|
mesh_data=data.as_ref();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
crate::union::convert(physics_data,mesh_data)?
|
||||||
|
}else{
|
||||||
|
crate::union::convert(physics_data,mesh_data)?
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
Ok(mesh)
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use rbx_mesh::mesh::{Vertex2, Vertex2Truncated};
|
use rbx_mesh::mesh::{Vertex2,Vertex2Truncated};
|
||||||
use strafesnet_common::{integer::vec3,model::{self, ColorId, IndexedVertex, NormalId, PolygonGroup, PolygonList, PositionId, TextureCoordinateId, VertexId}};
|
use strafesnet_common::{integer::vec3,model::{self,ColorId,IndexedVertex,NormalId,PolygonGroup,PolygonList,PositionId,RenderConfigId,TextureCoordinateId,VertexId}};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -205,7 +205,13 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<model::Me
|
|||||||
unique_vertices,
|
unique_vertices,
|
||||||
polygon_groups,
|
polygon_groups,
|
||||||
//these should probably be moved to the model...
|
//these should probably be moved to the model...
|
||||||
graphics_groups:Vec::new(),
|
//but what if models want to use the same texture
|
||||||
|
graphics_groups:vec![model::IndexedGraphicsGroup{
|
||||||
|
render:RenderConfigId::new(0),
|
||||||
|
//the lowest lod is highest quality
|
||||||
|
groups:vec![model::PolygonGroupId::new(0)]
|
||||||
|
}],
|
||||||
|
//disable physics
|
||||||
physics_groups:Vec::new(),
|
physics_groups:Vec::new(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use crate::loader::MeshIndex;
|
||||||
use crate::primitives;
|
use crate::primitives;
|
||||||
use strafesnet_common::map;
|
use strafesnet_common::map;
|
||||||
use strafesnet_common::model;
|
use strafesnet_common::model;
|
||||||
@ -8,6 +9,9 @@ use strafesnet_common::gameplay_attributes as attr;
|
|||||||
use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
|
use strafesnet_common::integer::{self,vec3,Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
|
||||||
use strafesnet_common::model::RenderConfigId;
|
use strafesnet_common::model::RenderConfigId;
|
||||||
use strafesnet_common::updatable::Updatable;
|
use strafesnet_common::updatable::Updatable;
|
||||||
|
use strafesnet_deferred_loader::deferred_loader::{RenderConfigDeferredLoader,MeshDeferredLoader};
|
||||||
|
use strafesnet_deferred_loader::mesh::Meshes;
|
||||||
|
use strafesnet_deferred_loader::texture::{RenderConfigs,Texture};
|
||||||
|
|
||||||
fn class_is_a(class: &str, superclass: &str) -> bool {
|
fn class_is_a(class: &str, superclass: &str) -> bool {
|
||||||
if class==superclass {
|
if class==superclass {
|
||||||
@ -403,52 +407,130 @@ enum RobloxBasePartDescription{
|
|||||||
Wedge(RobloxWedgeDescription),
|
Wedge(RobloxWedgeDescription),
|
||||||
CornerWedge(RobloxCornerWedgeDescription),
|
CornerWedge(RobloxCornerWedgeDescription),
|
||||||
}
|
}
|
||||||
|
fn get_texture_description<'a>(
|
||||||
|
temp_objects:&mut Vec<rbx_dom_weak::types::Ref>,
|
||||||
|
render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>,
|
||||||
|
dom:&'a rbx_dom_weak::WeakDom,
|
||||||
|
object:&rbx_dom_weak::Instance,
|
||||||
|
size:&rbx_dom_weak::types::Vector3,
|
||||||
|
)->RobloxPartDescription{
|
||||||
|
//use the biggest one and cut it down later...
|
||||||
|
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
||||||
|
temp_objects.clear();
|
||||||
|
recursive_collect_superclass(temp_objects,&dom,object,"Decal");
|
||||||
|
for &mut decal_ref in temp_objects{
|
||||||
|
if let Some(decal)=dom.get_by_ref(decal_ref){
|
||||||
|
if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::Content(content)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Enum(normalid)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
|
||||||
|
) = (
|
||||||
|
decal.properties.get("Texture"),
|
||||||
|
decal.properties.get("Face"),
|
||||||
|
decal.properties.get("Color3"),
|
||||||
|
decal.properties.get("Transparency"),
|
||||||
|
) {
|
||||||
|
let render_id=render_config_deferred_loader.acquire_render_config_id(Some(content.as_ref()));
|
||||||
|
let normal_id=normalid.to_u32();
|
||||||
|
if normal_id<6{
|
||||||
|
let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{
|
||||||
|
//generate tranform
|
||||||
|
if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(ox)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(oy)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(sx)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Float32(sy)),
|
||||||
|
) = (
|
||||||
|
decal.properties.get("OffsetStudsU"),
|
||||||
|
decal.properties.get("OffsetStudsV"),
|
||||||
|
decal.properties.get("StudsPerTileU"),
|
||||||
|
decal.properties.get("StudsPerTileV"),
|
||||||
|
)
|
||||||
|
{
|
||||||
|
let (size_u,size_v)=match normal_id{
|
||||||
|
0=>(size.z,size.y),//right
|
||||||
|
1=>(size.x,size.z),//top
|
||||||
|
2=>(size.x,size.y),//back
|
||||||
|
3=>(size.z,size.y),//left
|
||||||
|
4=>(size.x,size.z),//bottom
|
||||||
|
5=>(size.x,size.y),//front
|
||||||
|
_=>unreachable!(),
|
||||||
|
};
|
||||||
|
(
|
||||||
|
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
|
||||||
|
RobloxTextureTransform{
|
||||||
|
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
||||||
|
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
||||||
|
};
|
||||||
|
part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{
|
||||||
|
render:render_id,
|
||||||
|
color:roblox_texture_color,
|
||||||
|
transform:roblox_texture_transform,
|
||||||
|
});
|
||||||
|
}else{
|
||||||
|
println!("NormalId={} is invalid",normal_id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
part_texture_description
|
||||||
|
}
|
||||||
enum Shape{
|
enum Shape{
|
||||||
Primitive(primitives::Primitives),
|
Primitive(primitives::Primitives),
|
||||||
MeshPart,
|
MeshPart,
|
||||||
|
PhysicsData,
|
||||||
}
|
}
|
||||||
enum MeshAvailability{
|
enum MeshAvailability{
|
||||||
Immediate,
|
Immediate,
|
||||||
Deferred(RenderConfigId),
|
DeferredMesh(RenderConfigId),
|
||||||
|
DeferredUnion(RobloxPartDescription),
|
||||||
}
|
}
|
||||||
struct DeferredModelDeferredAttributes{
|
struct DeferredModelDeferredAttributes<'a>{
|
||||||
render:RenderConfigId,
|
render:RenderConfigId,
|
||||||
model:ModelDeferredAttributes,
|
model:ModelDeferredAttributes<'a>,
|
||||||
}
|
}
|
||||||
struct ModelDeferredAttributes{
|
struct ModelDeferredAttributes<'a>{
|
||||||
mesh:model::MeshId,
|
mesh:model::MeshId,
|
||||||
deferred_attributes:GetAttributesArgs,
|
deferred_attributes:GetAttributesArgs<'a>,
|
||||||
color:model::Color4,//transparency is in here
|
color:model::Color4,//transparency is in here
|
||||||
transform:Planar64Affine3,
|
transform:Planar64Affine3,
|
||||||
}
|
}
|
||||||
|
struct DeferredUnionDeferredAttributes<'a>{
|
||||||
|
render:RobloxPartDescription,
|
||||||
|
model:ModelDeferredAttributes<'a>,
|
||||||
|
}
|
||||||
struct ModelOwnedAttributes{
|
struct ModelOwnedAttributes{
|
||||||
mesh:model::MeshId,
|
mesh:model::MeshId,
|
||||||
attributes:attr::CollisionAttributes,
|
attributes:attr::CollisionAttributes,
|
||||||
color:model::Color4,//transparency is in here
|
color:model::Color4,//transparency is in here
|
||||||
transform:Planar64Affine3,
|
transform:Planar64Affine3,
|
||||||
}
|
}
|
||||||
struct GetAttributesArgs{
|
struct GetAttributesArgs<'a>{
|
||||||
name:Box<str>,
|
name:&'a str,
|
||||||
can_collide:bool,
|
can_collide:bool,
|
||||||
velocity:Planar64Vec3,
|
velocity:Planar64Vec3,
|
||||||
}
|
}
|
||||||
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
pub fn convert<'a>(
|
||||||
dom:&rbx_dom_weak::WeakDom,
|
dom:&'a rbx_dom_weak::WeakDom,
|
||||||
mut acquire_render_config_id:AcquireRenderConfigId,
|
render_config_deferred_loader:&mut RenderConfigDeferredLoader<&'a str>,
|
||||||
mut acquire_mesh_id:AcquireMeshId,
|
mesh_deferred_loader:&mut MeshDeferredLoader<MeshIndex<'a>>,
|
||||||
)->PartialMap1
|
)->PartialMap1<'a>{
|
||||||
where
|
|
||||||
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
|
||||||
AcquireMeshId:FnMut(&str)->model::MeshId,
|
|
||||||
{
|
|
||||||
|
|
||||||
let mut deferred_models_deferred_attributes=Vec::new();
|
let mut deferred_models_deferred_attributes=Vec::new();
|
||||||
|
let mut deferred_unions_deferred_attributes=Vec::new();
|
||||||
let mut primitive_models_deferred_attributes=Vec::new();
|
let mut primitive_models_deferred_attributes=Vec::new();
|
||||||
let mut primitive_meshes=Vec::new();
|
let mut primitive_meshes=Vec::new();
|
||||||
let mut mesh_id_from_description=HashMap::new();
|
let mut mesh_id_from_description=HashMap::new();
|
||||||
|
|
||||||
//just going to leave it like this for now instead of reworking the data structures for this whole thing
|
//just going to leave it like this for now instead of reworking the data structures for this whole thing
|
||||||
let textureless_render_group=acquire_render_config_id(None);
|
let textureless_render_group=render_config_deferred_loader.acquire_render_config_id(None);
|
||||||
|
|
||||||
let mut object_refs=Vec::new();
|
let mut object_refs=Vec::new();
|
||||||
let mut temp_objects=Vec::new();
|
let mut temp_objects=Vec::new();
|
||||||
@ -471,7 +553,7 @@ where
|
|||||||
object.properties.get("CanCollide"),
|
object.properties.get("CanCollide"),
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
let model_transform=planar64_affine3_from_roblox(cf,size);
|
let mut model_transform=planar64_affine3_from_roblox(cf,size);
|
||||||
|
|
||||||
if model_transform.matrix3.det().is_zero(){
|
if model_transform.matrix3.det().is_zero(){
|
||||||
let mut parent_ref=object.parent();
|
let mut parent_ref=object.parent();
|
||||||
@ -503,6 +585,7 @@ where
|
|||||||
"WedgePart"=>Shape::Primitive(primitives::Primitives::Wedge),
|
"WedgePart"=>Shape::Primitive(primitives::Primitives::Wedge),
|
||||||
"CornerWedgePart"=>Shape::Primitive(primitives::Primitives::CornerWedge),
|
"CornerWedgePart"=>Shape::Primitive(primitives::Primitives::CornerWedge),
|
||||||
"MeshPart"=>Shape::MeshPart,
|
"MeshPart"=>Shape::MeshPart,
|
||||||
|
"UnionOperation"=>Shape::PhysicsData,
|
||||||
_=>{
|
_=>{
|
||||||
println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class);
|
println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class);
|
||||||
Shape::Primitive(primitives::Primitives::Cube)
|
Shape::Primitive(primitives::Primitives::Cube)
|
||||||
@ -511,74 +594,8 @@ where
|
|||||||
|
|
||||||
let (availability,mesh_id)=match shape{
|
let (availability,mesh_id)=match shape{
|
||||||
Shape::Primitive(primitive_shape)=>{
|
Shape::Primitive(primitive_shape)=>{
|
||||||
//TODO: TAB TAB
|
//TODO: TAB TAB
|
||||||
//use the biggest one and cut it down later...
|
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);
|
||||||
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
|
||||||
temp_objects.clear();
|
|
||||||
recursive_collect_superclass(&mut temp_objects, &dom, object,"Decal");
|
|
||||||
for &decal_ref in &temp_objects{
|
|
||||||
if let Some(decal)=dom.get_by_ref(decal_ref){
|
|
||||||
if let (
|
|
||||||
Some(rbx_dom_weak::types::Variant::Content(content)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Enum(normalid)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Color3(decal_color3)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(decal_transparency)),
|
|
||||||
) = (
|
|
||||||
decal.properties.get("Texture"),
|
|
||||||
decal.properties.get("Face"),
|
|
||||||
decal.properties.get("Color3"),
|
|
||||||
decal.properties.get("Transparency"),
|
|
||||||
) {
|
|
||||||
let render_id=acquire_render_config_id(Some(content.as_ref()));
|
|
||||||
let normal_id=normalid.to_u32();
|
|
||||||
if normal_id<6{
|
|
||||||
let (roblox_texture_color,roblox_texture_transform)=if decal.class=="Texture"{
|
|
||||||
//generate tranform
|
|
||||||
if let (
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(ox)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(oy)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(sx)),
|
|
||||||
Some(rbx_dom_weak::types::Variant::Float32(sy)),
|
|
||||||
) = (
|
|
||||||
decal.properties.get("OffsetStudsU"),
|
|
||||||
decal.properties.get("OffsetStudsV"),
|
|
||||||
decal.properties.get("StudsPerTileU"),
|
|
||||||
decal.properties.get("StudsPerTileV"),
|
|
||||||
)
|
|
||||||
{
|
|
||||||
let (size_u,size_v)=match normal_id{
|
|
||||||
0=>(size.z,size.y),//right
|
|
||||||
1=>(size.x,size.z),//top
|
|
||||||
2=>(size.x,size.y),//back
|
|
||||||
3=>(size.z,size.y),//left
|
|
||||||
4=>(size.x,size.z),//bottom
|
|
||||||
5=>(size.x,size.y),//front
|
|
||||||
_=>unreachable!(),
|
|
||||||
};
|
|
||||||
(
|
|
||||||
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
|
|
||||||
RobloxTextureTransform{
|
|
||||||
offset_u:*ox/(*sx),offset_v:*oy/(*sy),
|
|
||||||
scale_u:size_u/(*sx),scale_v:size_v/(*sy),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}else{
|
|
||||||
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
|
||||||
}
|
|
||||||
}else{
|
|
||||||
(glam::Vec4::ONE,RobloxTextureTransform::default())
|
|
||||||
};
|
|
||||||
part_texture_description[normal_id as usize]=Some(RobloxFaceTextureDescription{
|
|
||||||
render:render_id,
|
|
||||||
color:roblox_texture_color,
|
|
||||||
transform:roblox_texture_transform,
|
|
||||||
});
|
|
||||||
}else{
|
|
||||||
println!("NormalId={} unsupported for shape={:?}",normal_id,primitive_shape);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//obscure rust syntax "slice pattern"
|
//obscure rust syntax "slice pattern"
|
||||||
let [
|
let [
|
||||||
f0,//Cube::Right
|
f0,//Cube::Right
|
||||||
@ -691,29 +708,54 @@ where
|
|||||||
object.properties.get("TextureID"),
|
object.properties.get("TextureID"),
|
||||||
){
|
){
|
||||||
(
|
(
|
||||||
MeshAvailability::Deferred(acquire_render_config_id(Some(texture_asset_id.as_ref()))),
|
MeshAvailability::DeferredMesh(render_config_deferred_loader.acquire_render_config_id(Some(texture_asset_id.as_ref()))),
|
||||||
acquire_mesh_id(mesh_asset_id.as_ref()),
|
mesh_deferred_loader.acquire_mesh_id(MeshIndex::file_mesh(mesh_asset_id.as_ref())),
|
||||||
)
|
)
|
||||||
}else{
|
}else{
|
||||||
panic!("Mesh has no Mesh or Texture");
|
panic!("Mesh has no Mesh or Texture");
|
||||||
},
|
},
|
||||||
|
Shape::PhysicsData=>{
|
||||||
|
//The union mesh is sized already
|
||||||
|
model_transform=planar64_affine3_from_roblox(cf,&rbx_dom_weak::types::Vector3{x:2.0,y:2.0,z:2.0});
|
||||||
|
|
||||||
|
let mut content="";
|
||||||
|
let mut mesh_data:&[u8]=&[];
|
||||||
|
let mut physics_data:&[u8]=&[];
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::Content(asset_id))=object.properties.get("AssetId"){
|
||||||
|
content=asset_id.as_ref();
|
||||||
|
}
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get("MeshData"){
|
||||||
|
mesh_data=data.as_ref();
|
||||||
|
}
|
||||||
|
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=object.properties.get("PhysicsData"){
|
||||||
|
physics_data=data.as_ref();
|
||||||
|
}
|
||||||
|
let part_texture_description=get_texture_description(&mut temp_objects,render_config_deferred_loader,dom,object,size);
|
||||||
|
let mesh_index=MeshIndex::union(content,mesh_data,physics_data);
|
||||||
|
let mesh_id=mesh_deferred_loader.acquire_mesh_id(mesh_index);
|
||||||
|
(MeshAvailability::DeferredUnion(part_texture_description),mesh_id)
|
||||||
|
},
|
||||||
};
|
};
|
||||||
let model_deferred_attributes=ModelDeferredAttributes{
|
let model_deferred_attributes=ModelDeferredAttributes{
|
||||||
mesh:mesh_id,
|
mesh:mesh_id,
|
||||||
transform:model_transform,
|
transform:model_transform,
|
||||||
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
|
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
|
||||||
deferred_attributes:GetAttributesArgs{
|
deferred_attributes:GetAttributesArgs{
|
||||||
name:object.name.as_str().into(),
|
name:object.name.as_str(),
|
||||||
can_collide:*can_collide,
|
can_collide:*can_collide,
|
||||||
velocity:vec3::try_from_f32_array([velocity.x,velocity.y,velocity.z]).unwrap(),
|
velocity:vec3::try_from_f32_array([velocity.x,velocity.y,velocity.z]).unwrap(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
match availability{
|
match availability{
|
||||||
MeshAvailability::Immediate=>primitive_models_deferred_attributes.push(model_deferred_attributes),
|
MeshAvailability::Immediate=>primitive_models_deferred_attributes.push(model_deferred_attributes),
|
||||||
MeshAvailability::Deferred(render)=>deferred_models_deferred_attributes.push(DeferredModelDeferredAttributes{
|
MeshAvailability::DeferredMesh(render)=>deferred_models_deferred_attributes.push(DeferredModelDeferredAttributes{
|
||||||
render,
|
render,
|
||||||
model:model_deferred_attributes
|
model:model_deferred_attributes
|
||||||
}),
|
}),
|
||||||
|
MeshAvailability::DeferredUnion(part_texture_description)=>deferred_unions_deferred_attributes.push(DeferredUnionDeferredAttributes{
|
||||||
|
render:part_texture_description,
|
||||||
|
model:model_deferred_attributes,
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -722,21 +764,23 @@ where
|
|||||||
primitive_meshes,
|
primitive_meshes,
|
||||||
primitive_models_deferred_attributes,
|
primitive_models_deferred_attributes,
|
||||||
deferred_models_deferred_attributes,
|
deferred_models_deferred_attributes,
|
||||||
|
deferred_unions_deferred_attributes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
struct MeshWithAabb{
|
struct MeshWithAabb{
|
||||||
mesh:model::Mesh,
|
mesh:model::Mesh,
|
||||||
aabb:strafesnet_common::aabb::Aabb,
|
aabb:strafesnet_common::aabb::Aabb,
|
||||||
}
|
}
|
||||||
pub struct PartialMap1{
|
pub struct PartialMap1<'a>{
|
||||||
primitive_meshes:Vec<model::Mesh>,
|
primitive_meshes:Vec<model::Mesh>,
|
||||||
primitive_models_deferred_attributes:Vec<ModelDeferredAttributes>,
|
primitive_models_deferred_attributes:Vec<ModelDeferredAttributes<'a>>,
|
||||||
deferred_models_deferred_attributes:Vec<DeferredModelDeferredAttributes>,
|
deferred_models_deferred_attributes:Vec<DeferredModelDeferredAttributes<'a>>,
|
||||||
|
deferred_unions_deferred_attributes:Vec<DeferredUnionDeferredAttributes<'a>>,
|
||||||
}
|
}
|
||||||
impl PartialMap1{
|
impl PartialMap1<'_>{
|
||||||
pub fn add_meshpart_meshes_and_calculate_attributes(
|
pub fn add_meshpart_meshes_and_calculate_attributes(
|
||||||
mut self,
|
mut self,
|
||||||
meshpart_meshes:impl IntoIterator<Item=(model::MeshId,crate::data::RobloxMeshBytes)>,
|
meshpart_meshes:Meshes,
|
||||||
)->PartialMap2{
|
)->PartialMap2{
|
||||||
//calculate attributes
|
//calculate attributes
|
||||||
let mut modes_builder=ModesBuilder::default();
|
let mut modes_builder=ModesBuilder::default();
|
||||||
@ -749,24 +793,16 @@ impl PartialMap1{
|
|||||||
//decode roblox meshes
|
//decode roblox meshes
|
||||||
//generate mesh_id_map based on meshes that failed to load
|
//generate mesh_id_map based on meshes that failed to load
|
||||||
let loaded_meshes:HashMap<model::MeshId,MeshWithAabb>=
|
let loaded_meshes:HashMap<model::MeshId,MeshWithAabb>=
|
||||||
meshpart_meshes.into_iter().flat_map(|(old_mesh_id,roblox_mesh_bytes)|
|
meshpart_meshes.consume().map(|(old_mesh_id,mesh)|{
|
||||||
match crate::mesh::convert(roblox_mesh_bytes){
|
let mut aabb=strafesnet_common::aabb::Aabb::default();
|
||||||
Ok(mesh)=>{
|
for &pos in &mesh.unique_pos{
|
||||||
let mut aabb=strafesnet_common::aabb::Aabb::default();
|
aabb.grow(pos);
|
||||||
for &pos in &mesh.unique_pos{
|
|
||||||
aabb.grow(pos);
|
|
||||||
}
|
|
||||||
Some((old_mesh_id,MeshWithAabb{
|
|
||||||
mesh,
|
|
||||||
aabb,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
Err(e)=>{
|
|
||||||
println!("Error converting mesh: {e:?}");
|
|
||||||
None
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
).collect();
|
(old_mesh_id,MeshWithAabb{
|
||||||
|
mesh,
|
||||||
|
aabb,
|
||||||
|
})
|
||||||
|
}).collect();
|
||||||
|
|
||||||
let mut mesh_id_from_render_config_id=HashMap::new();
|
let mut mesh_id_from_render_config_id=HashMap::new();
|
||||||
//ignore meshes that fail to load completely for now
|
//ignore meshes that fail to load completely for now
|
||||||
@ -776,12 +812,10 @@ impl PartialMap1{
|
|||||||
.entry(render).or_insert_with(||{
|
.entry(render).or_insert_with(||{
|
||||||
let mesh_id=model::MeshId::new(self.primitive_meshes.len() as u32);
|
let mesh_id=model::MeshId::new(self.primitive_meshes.len() as u32);
|
||||||
let mut mesh_clone=mesh_with_aabb.mesh.clone();
|
let mut mesh_clone=mesh_with_aabb.mesh.clone();
|
||||||
//add a render group lool
|
//set the render group lool
|
||||||
mesh_clone.graphics_groups.push(model::IndexedGraphicsGroup{
|
if let Some(graphics_group)=mesh_clone.graphics_groups.first_mut(){
|
||||||
render,
|
graphics_group.render=render;
|
||||||
//the lowest lod is highest quality
|
}
|
||||||
groups:vec![model::PolygonGroupId::new(0)]
|
|
||||||
});
|
|
||||||
self.primitive_meshes.push(mesh_clone);
|
self.primitive_meshes.push(mesh_clone);
|
||||||
mesh_id
|
mesh_id
|
||||||
}),
|
}),
|
||||||
@ -879,11 +913,11 @@ pub struct PartialMap2{
|
|||||||
impl PartialMap2{
|
impl PartialMap2{
|
||||||
pub fn add_render_configs_and_textures(
|
pub fn add_render_configs_and_textures(
|
||||||
self,
|
self,
|
||||||
render_configs:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
|
render_configs:RenderConfigs,
|
||||||
textures:impl IntoIterator<Item=(model::TextureId,Vec<u8>)>,
|
|
||||||
)->map::CompleteMap{
|
)->map::CompleteMap{
|
||||||
|
let (textures,render_configs)=render_configs.consume();
|
||||||
let (textures,texture_id_map):(Vec<Vec<u8>>,HashMap<model::TextureId,model::TextureId>)
|
let (textures,texture_id_map):(Vec<Vec<u8>>,HashMap<model::TextureId,model::TextureId>)
|
||||||
=textures.into_iter().enumerate().map(|(new_texture_id,(old_texture_id,texture))|{
|
=textures.into_iter().enumerate().map(|(new_texture_id,(old_texture_id,Texture::ImageDDS(texture)))|{
|
||||||
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
|
||||||
}).unzip();
|
}).unzip();
|
||||||
let render_configs=render_configs.into_iter().map(|(_render_config_id,mut render_config)|{
|
let render_configs=render_configs.into_iter().map(|(_render_config_id,mut render_config)|{
|
||||||
|
143
lib/rbx_loader/src/union.rs
Normal file
143
lib/rbx_loader/src/union.rs
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use strafesnet_common::model::{self, ColorId, IndexedVertex, NormalId, PolygonGroup, PolygonGroupId, PolygonList, PositionId, RenderConfigId, TextureCoordinateId, VertexId};
|
||||||
|
use strafesnet_common::integer::vec3;
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
Block,
|
||||||
|
NotSupposedToHappen,
|
||||||
|
MissingVertexId(u32),
|
||||||
|
Planar64Vec3(strafesnet_common::integer::Planar64TryFromFloatError),
|
||||||
|
RobloxPhysicsData(rbx_mesh::physics_data::Error),
|
||||||
|
RobloxMeshData(rbx_mesh::mesh_data::Error),
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for Error{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for Error{}
|
||||||
|
pub fn convert(roblox_physics_data:&[u8],roblox_mesh_data:&[u8])->Result<model::Mesh,Error>{
|
||||||
|
match (roblox_physics_data,roblox_mesh_data){
|
||||||
|
(b"",b"")=>return Err(Error::Block),
|
||||||
|
(b"",_)
|
||||||
|
|(_,b"")=>return Err(Error::NotSupposedToHappen),
|
||||||
|
_=>(),
|
||||||
|
}
|
||||||
|
|
||||||
|
// graphical
|
||||||
|
let mesh_data=rbx_mesh::read_mesh_data_versioned(
|
||||||
|
std::io::Cursor::new(roblox_mesh_data)
|
||||||
|
).map_err(Error::RobloxMeshData)?;
|
||||||
|
let graphics_mesh=match mesh_data{
|
||||||
|
rbx_mesh::mesh_data::CSGPHS::CSGK(_)=>return Err(Error::NotSupposedToHappen),
|
||||||
|
rbx_mesh::mesh_data::CSGPHS::CSGPHS2(mesh_data2)=>mesh_data2.mesh,
|
||||||
|
rbx_mesh::mesh_data::CSGPHS::CSGPHS4(mesh_data4)=>mesh_data4.mesh,
|
||||||
|
};
|
||||||
|
|
||||||
|
// physical
|
||||||
|
let physics_data=rbx_mesh::read_physics_data(
|
||||||
|
std::io::Cursor::new(roblox_physics_data)
|
||||||
|
).map_err(Error::RobloxPhysicsData)?;
|
||||||
|
let physics_convex_meshes=match physics_data{
|
||||||
|
rbx_mesh::physics_data::PhysicsData::CSGK(_)
|
||||||
|
// have not seen this format in practice
|
||||||
|
|rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::Block)
|
||||||
|
=>return Err(Error::NotSupposedToHappen),
|
||||||
|
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::Meshes(meshes))
|
||||||
|
=>meshes.meshes,
|
||||||
|
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::PhysicsInfoMesh(pim))
|
||||||
|
=>vec![pim.mesh],
|
||||||
|
};
|
||||||
|
let mut unique_pos=Vec::new();
|
||||||
|
let mut pos_id_from=HashMap::new();
|
||||||
|
let mut unique_tex=Vec::new();
|
||||||
|
let mut tex_id_from=HashMap::new();
|
||||||
|
let mut unique_normal=Vec::new();
|
||||||
|
let mut normal_id_from=HashMap::new();
|
||||||
|
let mut unique_color=Vec::new();
|
||||||
|
let mut color_id_from=HashMap::new();
|
||||||
|
let mut unique_vertices=Vec::new();
|
||||||
|
let mut vertex_id_from=HashMap::new();
|
||||||
|
let mut acquire_pos_id=|pos|{
|
||||||
|
let p=vec3::try_from_f32_array(pos).map_err(Error::Planar64Vec3)?;
|
||||||
|
Ok(*pos_id_from.entry(p).or_insert_with(||{
|
||||||
|
let pos_id=PositionId::new(unique_pos.len() as u32);
|
||||||
|
unique_pos.push(p);
|
||||||
|
pos_id
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
let mut acquire_tex_id=|tex|{
|
||||||
|
let h=bytemuck::cast::<[f32;2],[u32;2]>(tex);
|
||||||
|
*tex_id_from.entry(h).or_insert_with(||{
|
||||||
|
let tex_id=TextureCoordinateId::new(unique_tex.len() as u32);
|
||||||
|
unique_tex.push(glam::Vec2::from_array(tex));
|
||||||
|
tex_id
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let mut acquire_normal_id=|normal|{
|
||||||
|
let n=vec3::try_from_f32_array(normal).map_err(Error::Planar64Vec3)?;
|
||||||
|
Ok(*normal_id_from.entry(n).or_insert_with(||{
|
||||||
|
let normal_id=NormalId::new(unique_normal.len() as u32);
|
||||||
|
unique_normal.push(n);
|
||||||
|
normal_id
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
let mut acquire_color_id=|color|{
|
||||||
|
let h=bytemuck::cast::<[f32;4],[u32;4]>(color);
|
||||||
|
*color_id_from.entry(h).or_insert_with(||{
|
||||||
|
let color_id=ColorId::new(unique_color.len() as u32);
|
||||||
|
unique_color.push(glam::Vec4::from_array(color));
|
||||||
|
color_id
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let mut acquire_vertex_id=|vertex:IndexedVertex|{
|
||||||
|
*vertex_id_from.entry(vertex.clone()).or_insert_with(||{
|
||||||
|
let vertex_id=VertexId::new(unique_vertices.len() as u32);
|
||||||
|
unique_vertices.push(vertex);
|
||||||
|
vertex_id
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let color=acquire_color_id([1.0f32;4]);
|
||||||
|
let tex=acquire_tex_id([0.0f32;2]);
|
||||||
|
let polygon_groups:Vec<PolygonGroup>=physics_convex_meshes.into_iter().map(|mesh|{
|
||||||
|
Ok(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().map(|[vertex_id0,vertex_id1,vertex_id2]|{
|
||||||
|
let v0=mesh.vertices.get(vertex_id0.0 as usize).ok_or(Error::MissingVertexId(vertex_id0.0))?;
|
||||||
|
let v1=mesh.vertices.get(vertex_id1.0 as usize).ok_or(Error::MissingVertexId(vertex_id1.0))?;
|
||||||
|
let v2=mesh.vertices.get(vertex_id2.0 as usize).ok_or(Error::MissingVertexId(vertex_id2.0))?;
|
||||||
|
let vertex_norm=(glam::Vec3::from_slice(v1)-glam::Vec3::from_slice(v0))
|
||||||
|
.cross(glam::Vec3::from_slice(v2)-glam::Vec3::from_slice(v0)).to_array();
|
||||||
|
let mut ingest_vertex_id=|&vertex_pos:&[f32;3]|Ok(acquire_vertex_id(IndexedVertex{
|
||||||
|
pos:acquire_pos_id(vertex_pos)?,
|
||||||
|
tex,
|
||||||
|
normal:acquire_normal_id(vertex_norm)?,
|
||||||
|
color,
|
||||||
|
}));
|
||||||
|
Ok(vec![
|
||||||
|
ingest_vertex_id(v0)?,
|
||||||
|
ingest_vertex_id(v1)?,
|
||||||
|
ingest_vertex_id(v2)?,
|
||||||
|
])
|
||||||
|
}).collect::<Result<_,_>>()?)))
|
||||||
|
}).collect::<Result<_,_>>()?;
|
||||||
|
let graphics_groups=vec![model::IndexedGraphicsGroup{
|
||||||
|
render:RenderConfigId::new(0),
|
||||||
|
groups:(0..polygon_groups.len()).map(|id|PolygonGroupId::new(id as u32)).collect()
|
||||||
|
}];
|
||||||
|
let physics_groups=(0..polygon_groups.len()).map(|id|model::IndexedPhysicsGroup{
|
||||||
|
groups:vec![PolygonGroupId::new(id as u32)]
|
||||||
|
}).collect();
|
||||||
|
Ok(model::Mesh{
|
||||||
|
unique_pos,
|
||||||
|
unique_normal,
|
||||||
|
unique_tex,
|
||||||
|
unique_color,
|
||||||
|
unique_vertices,
|
||||||
|
polygon_groups,
|
||||||
|
graphics_groups,
|
||||||
|
physics_groups,
|
||||||
|
})
|
||||||
|
}
|
7
lib/rbxassetid/Cargo.toml
Normal file
7
lib/rbxassetid/Cargo.toml
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "rbxassetid"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
url = "2.5.4"
|
35
lib/rbxassetid/src/lib.rs
Normal file
35
lib/rbxassetid/src/lib.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#[derive(Clone,Copy,Debug,Hash,Eq,PartialEq)]
|
||||||
|
pub struct RobloxAssetId(pub u64);
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum RobloxAssetIdParseErr{
|
||||||
|
Url(url::ParseError),
|
||||||
|
UnknownScheme,
|
||||||
|
ParseInt(std::num::ParseIntError),
|
||||||
|
MissingAssetId,
|
||||||
|
MissingIDQueryParam,
|
||||||
|
}
|
||||||
|
impl std::fmt::Display for RobloxAssetIdParseErr{
|
||||||
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
|
write!(f,"{self:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl std::error::Error for RobloxAssetIdParseErr{}
|
||||||
|
impl std::str::FromStr for RobloxAssetId{
|
||||||
|
type Err=RobloxAssetIdParseErr;
|
||||||
|
fn from_str(s:&str)->Result<Self,Self::Err>{
|
||||||
|
let url=url::Url::parse(s).map_err(RobloxAssetIdParseErr::Url)?;
|
||||||
|
let parsed_asset_id=match url.scheme(){
|
||||||
|
"rbxassetid"=>url.domain().ok_or(RobloxAssetIdParseErr::MissingAssetId)?.parse(),
|
||||||
|
"http"|"https"=>{
|
||||||
|
let (_,asset_id)=url.query_pairs()
|
||||||
|
.find(|(id,_)|match id.as_ref(){
|
||||||
|
"ID"|"id"|"Id"|"iD"=>true,
|
||||||
|
_=>false,
|
||||||
|
}).ok_or(RobloxAssetIdParseErr::MissingIDQueryParam)?;
|
||||||
|
asset_id.parse()
|
||||||
|
},
|
||||||
|
_=>Err(RobloxAssetIdParseErr::UnknownScheme)?,
|
||||||
|
};
|
||||||
|
Ok(Self(parsed_asset_id.map_err(RobloxAssetIdParseErr::ParseInt)?))
|
||||||
|
}
|
||||||
|
}
|
@ -21,7 +21,7 @@ parking_lot = "0.12.1"
|
|||||||
pollster = "0.4.0"
|
pollster = "0.4.0"
|
||||||
strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true }
|
strafesnet_bsp_loader = { path = "../lib/bsp_loader", registry = "strafesnet", optional = true }
|
||||||
strafesnet_common = { path = "../lib/common", registry = "strafesnet" }
|
strafesnet_common = { path = "../lib/common", registry = "strafesnet" }
|
||||||
strafesnet_deferred_loader = { path = "../lib/deferred_loader", features = ["legacy"], registry = "strafesnet", optional = true }
|
strafesnet_deferred_loader = { path = "../lib/deferred_loader", registry = "strafesnet", optional = true }
|
||||||
strafesnet_graphics = { path = "../engine/graphics", registry = "strafesnet" }
|
strafesnet_graphics = { path = "../engine/graphics", registry = "strafesnet" }
|
||||||
strafesnet_physics = { path = "../engine/physics", registry = "strafesnet" }
|
strafesnet_physics = { path = "../engine/physics", registry = "strafesnet" }
|
||||||
strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet", optional = true }
|
strafesnet_rbx_loader = { path = "../lib/rbx_loader", registry = "strafesnet", optional = true }
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
|
#[cfg(any(feature="roblox",feature="source"))]
|
||||||
|
use strafesnet_deferred_loader::deferred_loader::{LoadFailureMode,MeshDeferredLoader,RenderConfigDeferredLoader};
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ReadError{
|
pub enum ReadError{
|
||||||
@ -65,7 +68,14 @@ pub fn read<R:Read+std::io::Seek>(input:R)->Result<ReadFormat,ReadError>{
|
|||||||
pub enum LoadError{
|
pub enum LoadError{
|
||||||
ReadError(ReadError),
|
ReadError(ReadError),
|
||||||
File(std::io::Error),
|
File(std::io::Error),
|
||||||
Io(std::io::Error),
|
#[cfg(feature="roblox")]
|
||||||
|
LoadRobloxMesh(strafesnet_rbx_loader::loader::MeshError),
|
||||||
|
#[cfg(feature="roblox")]
|
||||||
|
LoadRobloxTexture(strafesnet_rbx_loader::loader::TextureError),
|
||||||
|
#[cfg(feature="source")]
|
||||||
|
LoadSourceMesh(strafesnet_bsp_loader::loader::MeshError),
|
||||||
|
#[cfg(feature="source")]
|
||||||
|
LoadSourceTexture(strafesnet_bsp_loader::loader::TextureError),
|
||||||
}
|
}
|
||||||
impl std::fmt::Display for LoadError{
|
impl std::fmt::Display for LoadError{
|
||||||
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
|
||||||
@ -94,73 +104,47 @@ pub fn load<P:AsRef<std::path::Path>>(path:P)->Result<LoadFormat,LoadError>{
|
|||||||
let mut place=model.into_place();
|
let mut place=model.into_place();
|
||||||
place.run_scripts();
|
place.run_scripts();
|
||||||
|
|
||||||
let mut loader=strafesnet_deferred_loader::roblox_legacy();
|
let mut texture_deferred_loader=RenderConfigDeferredLoader::new();
|
||||||
|
let mut mesh_deferred_loader=MeshDeferredLoader::new();
|
||||||
let (texture_loader,mesh_loader)=loader.get_inner_mut();
|
|
||||||
|
|
||||||
let map_step1=strafesnet_rbx_loader::convert(
|
let map_step1=strafesnet_rbx_loader::convert(
|
||||||
&place,
|
place.as_ref(),
|
||||||
|name|texture_loader.acquire_render_config_id(name),
|
&mut texture_deferred_loader,
|
||||||
|name|mesh_loader.acquire_mesh_id(name),
|
&mut mesh_deferred_loader,
|
||||||
);
|
);
|
||||||
|
|
||||||
let meshpart_meshes=mesh_loader.load_meshes().map_err(LoadError::Io)?;
|
let mut mesh_loader=strafesnet_rbx_loader::loader::MeshLoader::new();
|
||||||
|
let meshpart_meshes=mesh_deferred_loader.into_meshes(&mut mesh_loader,LoadFailureMode::DefaultToNone).map_err(LoadError::LoadRobloxMesh)?;
|
||||||
|
|
||||||
let map_step2=map_step1.add_meshpart_meshes_and_calculate_attributes(
|
let map_step2=map_step1.add_meshpart_meshes_and_calculate_attributes(meshpart_meshes);
|
||||||
meshpart_meshes.into_iter().map(|(mesh_id,loader_model)|
|
|
||||||
(mesh_id,strafesnet_rbx_loader::data::RobloxMeshBytes::new(loader_model.get()))
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
|
let mut texture_loader=strafesnet_rbx_loader::loader::TextureLoader::new();
|
||||||
|
let render_configs=texture_deferred_loader.into_render_configs(&mut texture_loader,LoadFailureMode::DefaultToNone).map_err(LoadError::LoadRobloxTexture)?;
|
||||||
|
|
||||||
let map=map_step2.add_render_configs_and_textures(
|
let map=map_step2.add_render_configs_and_textures(render_configs);
|
||||||
render_configs.into_iter(),
|
|
||||||
textures.into_iter().map(|(texture_id,texture)|
|
|
||||||
(texture_id,match texture{
|
|
||||||
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(LoadFormat::Map(map))
|
Ok(LoadFormat::Map(map))
|
||||||
},
|
},
|
||||||
#[cfg(feature="source")]
|
#[cfg(feature="source")]
|
||||||
ReadFormat::Source(bsp)=>{
|
ReadFormat::Source(bsp)=>{
|
||||||
let mut loader=strafesnet_deferred_loader::source_legacy();
|
let mut texture_deferred_loader=RenderConfigDeferredLoader::new();
|
||||||
|
let mut mesh_deferred_loader=MeshDeferredLoader::new();
|
||||||
let (texture_loader,mesh_loader)=loader.get_inner_mut();
|
|
||||||
|
|
||||||
let map_step1=strafesnet_bsp_loader::convert(
|
let map_step1=strafesnet_bsp_loader::convert(
|
||||||
&bsp,
|
&bsp,
|
||||||
|name|texture_loader.acquire_render_config_id(name),
|
&mut texture_deferred_loader,
|
||||||
|name|mesh_loader.acquire_mesh_id(name),
|
&mut mesh_deferred_loader,
|
||||||
);
|
);
|
||||||
|
|
||||||
let prop_meshes=mesh_loader.load_meshes(bsp.as_ref());
|
let mut mesh_loader=strafesnet_bsp_loader::loader::MeshLoader::new(&bsp,&mut texture_deferred_loader);
|
||||||
|
let prop_meshes=mesh_deferred_loader.into_meshes(&mut mesh_loader,LoadFailureMode::DefaultToNone).map_err(LoadError::LoadSourceMesh)?;
|
||||||
|
|
||||||
let map_step2=map_step1.add_prop_meshes(
|
let map_step2=map_step1.add_prop_meshes(prop_meshes);
|
||||||
//the type conflagulator 9000
|
|
||||||
prop_meshes.into_iter().map(|(mesh_id,loader_model)|
|
|
||||||
(mesh_id,strafesnet_bsp_loader::data::ModelData{
|
|
||||||
mdl:strafesnet_bsp_loader::data::MdlData::new(loader_model.mdl.get()),
|
|
||||||
vtx:strafesnet_bsp_loader::data::VtxData::new(loader_model.vtx.get()),
|
|
||||||
vvd:strafesnet_bsp_loader::data::VvdData::new(loader_model.vvd.get()),
|
|
||||||
})
|
|
||||||
),
|
|
||||||
|name|texture_loader.acquire_render_config_id(name),
|
|
||||||
);
|
|
||||||
|
|
||||||
let (textures,render_configs)=loader.into_render_configs().map_err(LoadError::Io)?.consume();
|
let mut texture_loader=strafesnet_bsp_loader::loader::TextureLoader::new();
|
||||||
|
let render_configs=texture_deferred_loader.into_render_configs(&mut texture_loader,LoadFailureMode::DefaultToNone).map_err(LoadError::LoadSourceTexture)?;
|
||||||
|
|
||||||
let map=map_step2.add_render_configs_and_textures(
|
let map=map_step2.add_render_configs_and_textures(render_configs);
|
||||||
render_configs.into_iter(),
|
|
||||||
textures.into_iter().map(|(texture_id,texture)|
|
|
||||||
(texture_id,match texture{
|
|
||||||
strafesnet_deferred_loader::texture::Texture::ImageDDS(data)=>data,
|
|
||||||
})
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(LoadFormat::Map(map))
|
Ok(LoadFormat::Map(map))
|
||||||
},
|
},
|
||||||
|
1
tools/meshes
Symbolic link
1
tools/meshes
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/run/media/quat/Files/Documents/map-files/verify-scripts/meshes
|
1
tools/textures
Symbolic link
1
tools/textures
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
/run/media/quat/Files/Documents/map-files/verify-scripts/textures
|
Loading…
x
Reference in New Issue
Block a user