13 Commits

Author SHA1 Message Date
e58a476d39 multiple wormhole 2024-02-15 00:49:15 -08:00
ece055db6f allow replace existing stage 2024-02-15 00:47:09 -08:00
261e42c33d unused import 2024-02-15 00:46:43 -08:00
e0739fa792 use utf8 patched version 2024-02-15 00:43:55 -08:00
34017ea72c v0.2.0 new loading function signatures 2024-02-14 23:40:25 -08:00
bcaa28cf6a implement new loading api 2024-02-14 23:38:29 -08:00
0b630576d4 wrapped types + implement error trait 2024-02-13 21:28:06 -08:00
1c33646765 pass as ref 2024-02-13 21:28:06 -08:00
3b038687b6 rust master 2024-02-13 06:58:39 -08:00
048e408390 prefer this code 2024-02-13 05:43:37 -08:00
ed0dcdd051 not todo 2024-02-13 05:40:48 -08:00
0f3bd4420e use unreachable instead of panic 2024-02-13 05:38:35 -08:00
93bc4dc0fb data structure rewrite, implement texture_loader 2024-02-13 03:12:07 -08:00
5 changed files with 96 additions and 71 deletions

22
Cargo.lock generated

@ -272,8 +272,7 @@ dependencies = [
[[package]]
name = "rbx_binary"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6314dd6bf5c21d0598cdb53cf5d241aa643ba41da8b8abf7402b4a35096f03f6"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"log",
"lz4",
@ -287,8 +286,7 @@ dependencies = [
[[package]]
name = "rbx_dom_weak"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b67b56bac99849c2e3c57547b036927f71c57cf7f4d900d04e3e4ee774ec316"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"rbx_types",
"serde",
@ -297,8 +295,7 @@ dependencies = [
[[package]]
name = "rbx_reflection"
version = "4.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0d41509c991b53a7276a746a795eae2b9204f398164920f61976995b47fe1722"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"rbx_types",
"serde",
@ -308,8 +305,7 @@ dependencies = [
[[package]]
name = "rbx_reflection_database"
version = "0.2.10+roblox-607"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12e20c06fa41f7aadc79005c8354f592b2c2f4d0c61e1080ed5718dafc30aea0"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"lazy_static",
"rbx_reflection",
@ -320,8 +316,7 @@ dependencies = [
[[package]]
name = "rbx_types"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ca23bfd469d067d81ef14f65fe09aeddc25abcf576a889d1a7664fe021cf18c"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"base64",
"bitflags",
@ -335,8 +330,7 @@ dependencies = [
[[package]]
name = "rbx_xml"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8c03f95500961c32340791d1fabd4587f6873bdbff077ecca6ae32db7960dea"
source = "git+https://github.com/krakow10/rbx-dom?rev=841643178680c9f3143422778f9c52f949b222b9#841643178680c9f3143422778f9c52f949b222b9"
dependencies = [
"base64",
"log",
@ -420,7 +414,7 @@ dependencies = [
[[package]]
name = "strafesnet_common"
version = "0.1.0"
source = "git+https://git.itzana.me/StrafesNET/common?rev=fccb13bc6080ea6db94ef9175affd4aef1d9249d#fccb13bc6080ea6db94ef9175affd4aef1d9249d"
source = "git+https://git.itzana.me/StrafesNET/common?rev=093a54c527134ef7020a22a0f5778df8cba60228#093a54c527134ef7020a22a0f5778df8cba60228"
dependencies = [
"glam",
"id",
@ -428,7 +422,7 @@ dependencies = [
[[package]]
name = "strafesnet_rbx_loader"
version = "0.1.0"
version = "0.2.0"
dependencies = [
"glam",
"lazy-regex",

@ -1,6 +1,6 @@
[package]
name = "strafesnet_rbx_loader"
version = "0.1.0"
version = "0.2.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
@ -8,8 +8,8 @@ edition = "2021"
[dependencies]
glam = "0.25.0"
lazy-regex = "3.1.0"
rbx_binary = "0.7.4"
rbx_dom_weak = "2.7.0"
rbx_reflection_database = "0.2.10"
rbx_xml = "0.13.3"
strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "fccb13bc6080ea6db94ef9175affd4aef1d9249d" }
rbx_binary = { git = "https://github.com/krakow10/rbx-dom", rev = "841643178680c9f3143422778f9c52f949b222b9" }
rbx_dom_weak = { git = "https://github.com/krakow10/rbx-dom", rev = "841643178680c9f3143422778f9c52f949b222b9" }
rbx_reflection_database = { git = "https://github.com/krakow10/rbx-dom", rev = "841643178680c9f3143422778f9c52f949b222b9" }
rbx_xml = { git = "https://github.com/krakow10/rbx-dom", rev = "841643178680c9f3143422778f9c52f949b222b9" }
strafesnet_common = { git = "https://git.itzana.me/StrafesNET/common", rev = "093a54c527134ef7020a22a0f5778df8cba60228" }

@ -3,24 +3,34 @@ use std::io::Read;
mod rbx;
mod primitives;
pub struct Dom(rbx_dom_weak::WeakDom);
#[derive(Debug)]
pub enum Error{
pub enum ReadError{
RbxBinary(rbx_binary::DecodeError),
RbxXml(rbx_xml::DecodeError),
Io(std::io::Error),
UnknownFileFormat,
}
impl std::fmt::Display for ReadError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for ReadError{}
fn load_dom<R:Read>(input:R)->Result<rbx_dom_weak::WeakDom,Error>{
pub fn read<R:Read>(input:R)->Result<Dom,ReadError>{
let mut buf=std::io::BufReader::new(input);
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(Error::Io)?;
let peek=std::io::BufRead::fill_buf(&mut buf).map_err(ReadError::Io)?;
match &peek[0..8]{
b"<roblox!"=>return rbx_binary::from_reader(buf).map_err(Error::RbxBinary),
b"<roblox "=>return rbx_xml::from_reader_default(buf).map_err(Error::RbxXml),
_=>Err(Error::UnknownFileFormat),
b"<roblox!"=>rbx_binary::from_reader(buf).map(Dom).map_err(ReadError::RbxBinary),
b"<roblox "=>rbx_xml::from_reader_default(buf).map(Dom).map_err(ReadError::RbxXml),
_=>Err(ReadError::UnknownFileFormat),
}
}
pub fn read<R:Read,F:FnMut(&str)->Option<strafesnet_common::model::TextureId>>(input:R,acquire_id:F)->Result<strafesnet_common::map::CompleteMap,Error>{
Ok(rbx::convert(load_dom(input)?,acquire_id))
//ConvertError
pub fn convert<F:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId>(dom:&Dom,acquire_render_config_id:F)->rbx::PartialMap1{
rbx::convert(&dom.0,acquire_render_config_id)
}

@ -207,8 +207,6 @@ impl FaceDescription{
}
}
}
//TODO: it's probably better to use a shared vertex buffer between all primitives and use indexed rendering instead of generating a unique vertex buffer for each primitive.
//implementation: put all roblox primitives into one model.groups <- this won't work but I forget why
pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
let mut generated_pos=Vec::new();
let mut generated_tex=Vec::new();
@ -217,7 +215,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
let mut generated_vertices=Vec::new();
let mut polygon_groups=Vec::new();
let mut graphics_groups=Vec::new();
let mut physics_groups=vec![IndexedPhysicsGroup::default()];
let mut physics_group=IndexedPhysicsGroup::default();
let mut transforms=Vec::new();
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id,face_description) in face_descriptions.pairs(){
@ -228,9 +226,9 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
//create new transform_index
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(face_description.transform.transform_point2(tex));
}
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
@ -273,7 +271,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
render:face_description.render,
groups:vec![group_id],
});
physics_groups[0].groups.push(group_id);
physics_group.groups.push(group_id);
}
Mesh{
unique_pos:generated_pos,
@ -283,7 +281,7 @@ pub fn generate_partial_unit_cube(face_descriptions:CubeFaceDescription)->Mesh{
unique_vertices:generated_vertices,
polygon_groups,
graphics_groups,
physics_groups,
physics_groups:vec![physics_group],
}
}
//don't think too hard about the copy paste because this is all going into the map tool eventually...
@ -330,7 +328,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
let mut generated_vertices=Vec::new();
let mut polygon_groups=Vec::new();
let mut graphics_groups=Vec::new();
let mut physics_groups=vec![IndexedPhysicsGroup::default()];
let mut physics_group=IndexedPhysicsGroup::default();
let mut transforms=Vec::new();
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id,face_description) in face_descriptions.pairs(){
@ -341,9 +339,9 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
//create new transform_index
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(face_description.transform.transform_point2(tex));
}
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
@ -386,7 +384,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
render:face_description.render,
groups:vec![group_id],
});
physics_groups[0].groups.push(group_id);
physics_group.groups.push(group_id);
}
Mesh{
unique_pos:generated_pos,
@ -396,7 +394,7 @@ pub fn generate_partial_unit_wedge(face_descriptions:WedgeFaceDescription)->Mesh
unique_vertices:generated_vertices,
polygon_groups,
graphics_groups,
physics_groups,
physics_groups:vec![physics_group],
}
}
@ -441,7 +439,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
let mut generated_vertices=Vec::new();
let mut polygon_groups=Vec::new();
let mut graphics_groups=Vec::new();
let mut physics_groups=vec![IndexedPhysicsGroup::default()];
let mut physics_group=IndexedPhysicsGroup::default();
let mut transforms=Vec::new();
//note that on a cube every vertex is guaranteed to be unique, so there's no need to hash them against existing vertices.
for (face_id,face_description) in face_descriptions.pairs(){
@ -452,9 +450,9 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
//create new transform_index
let transform_index=transforms.len();
transforms.push(face_description.transform);
for tex in CUBE_DEFAULT_TEXTURE_COORDS{
generated_tex.push(face_description.transform.transform_point2(tex));
}
generated_tex.extend(CUBE_DEFAULT_TEXTURE_COORDS.map(|tex|
face_description.transform.transform_point2(tex)
));
transform_index
} as u32;
let color_index=if let Some(color_index)=generated_color.iter().position(|&color|color==face_description.color){
@ -497,7 +495,7 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
render:face_description.render,
groups:vec![group_id],
});
physics_groups[0].groups.push(group_id);
physics_group.groups.push(group_id);
}
Mesh{
unique_pos:generated_pos,
@ -507,6 +505,6 @@ pub fn generate_partial_unit_cornerwedge(face_descriptions:CornerWedgeFaceDescri
unique_vertices:generated_vertices,
polygon_groups,
graphics_groups,
physics_groups,
physics_groups:vec![physics_group],
}
}

@ -6,7 +6,6 @@ use strafesnet_common::gameplay_modes;
use strafesnet_common::gameplay_style;
use strafesnet_common::gameplay_attributes as attr;
use strafesnet_common::integer::{Planar64,Planar64Vec3,Planar64Mat3,Planar64Affine3};
use strafesnet_common::model::RenderConfig;
use strafesnet_common::model::RenderConfigId;
use strafesnet_common::updatable::Updatable;
@ -126,7 +125,7 @@ impl ModesBuilder{
assert!(self.modes.insert(mode_id,mode).is_none(),"Cannot replace existing mode");
}
fn insert_stage(&mut self,mode_id:gameplay_modes::ModeId,stage_id:gameplay_modes::StageId,stage:gameplay_modes::Stage){
assert!(self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage).is_none(),"Cannot replace existing stage");
self.stages.entry(mode_id).or_insert(HashMap::new()).insert(stage_id,stage);//assert!(.is_none(),"Cannot replace existing stage");
}
fn push_mode_update(&mut self,mode_id:gameplay_modes::ModeId,mode_update:gameplay_modes::ModeUpdate){
self.mode_updates.push((mode_id,mode_update));
@ -221,7 +220,7 @@ fn get_attributes(object:&rbx_dom_weak::Instance,can_collide:bool,velocity:Plana
//the PhysicsModelId has to exist for it to be teleported to!
force_intersecting=true;
//this object is not special in strafe client, but the roblox mapping needs to be converted to model id
assert!(wormhole_id_to_out_model.insert(captures[2].parse::<u32>().unwrap(),model_id).is_none(),"Cannot have multiple WormholeOut with same id");
wormhole_id_to_out_model.insert(captures[2].parse::<u32>().unwrap(),model_id);//assert!(.is_none(),"Cannot have multiple WormholeOut with same id");
},
_=>(),
}
@ -405,7 +404,7 @@ struct ModelOwnedAttributes{
color:model::Color4,//transparency is in here
transform:Planar64Affine3,
}
pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDom,mut acquire_id:F)->map::CompleteMap{
pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::WeakDom,mut acquire_render_config_id:F)->PartialMap1{
let mut modes_builder=ModesBuilder::default();
let mut models1=Vec::new();
@ -418,10 +417,8 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
let mut wormhole_in_model_to_id=HashMap::new();
let mut wormhole_id_to_out_model=HashMap::new();
//TODO: some sort of thing like RobloxResources that describes where to get each resource
//this would be another dependency built for downloading resources to keep this one clean
let mut unique_render_groups=vec![RenderConfig::default()];
let textureless_render_group=RenderConfigId::new(0);
//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 mut object_refs=Vec::new();
let mut temp_objects=Vec::new();
@ -503,14 +500,7 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
decal.properties.get("Color3"),
decal.properties.get("Transparency"),
) {
if let Some(texture_id)=acquire_id(content.as_ref()){
//this is equivalent to a get_or_create pattern because there is a singular no-texture RenderId
//so RenderId==TextureId+1
//not the most failsafe code but this is just for the map tool lmao
if unique_render_groups.len()==texture_id.get() as usize+1{
unique_render_groups.push(RenderConfig::texture(texture_id));
};
let render_id=RenderConfigId::new(texture_id.get()+1);
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"{
@ -534,7 +524,7 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
3=>(size.z,size.y),//left
4=>(size.x,size.z),//bottom
5=>(size.x,size.y),//front
_=>panic!("unreachable"),
_=>unreachable!(),
};
(
glam::vec4(decal_color3.r,decal_color3.g,decal_color3.b,1.0-*decal_transparency),
@ -557,7 +547,6 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
}else{
println!("NormalId={} unsupported for shape={:?}",normal_id,shape);
}
}
}
}
}
@ -612,7 +601,7 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
3=>primitives::CubeFace::Left,
4=>primitives::CubeFace::Bottom,
5=>primitives::CubeFace::Front,
_=>panic!("unreachable"),
_=>unreachable!(),
},
match roblox_face_description{
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
@ -631,7 +620,7 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
2=>primitives::WedgeFace::Back,
3=>primitives::WedgeFace::Left,
4=>primitives::WedgeFace::Bottom,
_=>panic!("unreachable"),
_=>unreachable!(),
},
match roblox_face_description{
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
@ -650,7 +639,7 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
2=>primitives::CornerWedgeFace::TopLeft,
3=>primitives::CornerWedgeFace::Bottom,
4=>primitives::CornerWedgeFace::Front,
_=>panic!("unreachable"),
_=>unreachable!(),
},
match roblox_face_description{
Some(roblox_texture_transform)=>roblox_texture_transform.to_face_description(),
@ -711,11 +700,45 @@ pub fn convert<F:FnMut(&str)->Option<model::TextureId>>(dom:rbx_dom_weak::WeakDo
attributes:attributes_id,
}
}).collect();
map::CompleteMap{
render_configs:unique_render_groups,//asset_id_from_texture_id.iter().map(|t|t.to_string()).collect(),
PartialMap1{
meshes,
models,
modes:modes_builder.build(),
attributes:unique_attributes,
}
}
pub struct PartialMap1{
meshes:Vec<model::Mesh>,
models:Vec<model::Model>,
modes:gameplay_modes::Modes,
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
}
impl PartialMap1{
pub fn add_render_configs_and_textures(
self,
render_configs:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
textures:impl IntoIterator<Item=(model::TextureId,Vec<u8>)>,
)->map::CompleteMap{
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))|{
(texture,(old_texture_id,model::TextureId::new(new_texture_id as u32)))
}).unzip();
let render_configs=render_configs.into_iter().map(|(render_config_id,mut render_config)|{
//this may generate duplicate no-texture render configs but idc
render_config.texture=render_config.texture.and_then(|texture_id|
texture_id_map.get(&texture_id).copied()
);
render_config
}).collect();
map::CompleteMap{
modes:self.modes,
attributes:self.attributes,
meshes:self.meshes,
models:self.models,
//the roblox legacy texture thing always works
textures,
render_configs,
}
}
}