193 lines
5.0 KiB
Rust
193 lines
5.0 KiB
Rust
use std::io::Read;
|
|
use rbx_dom_weak::ustr;
|
|
use rbxassetid::{RobloxAssetId,RobloxAssetIdParseErr};
|
|
use strafesnet_common::model::Mesh;
|
|
use strafesnet_deferred_loader::{loader::Loader,texture::Texture};
|
|
|
|
use crate::data::RobloxMeshBytes;
|
|
use crate::rbx::RobloxPartDescription;
|
|
|
|
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;
|
|
impl TextureLoader{
|
|
pub fn new()->Self{
|
|
Self
|
|
}
|
|
}
|
|
impl Loader for TextureLoader{
|
|
type Error=TextureError;
|
|
type Index<'a>=&'a str;
|
|
type Resource=Texture;
|
|
fn load<'a>(&mut self,index:Self::Index<'a>)->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],
|
|
size_float_bits:[u32;3],
|
|
part_texture_description:RobloxPartDescription,
|
|
},
|
|
}
|
|
#[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],
|
|
size:&rbx_dom_weak::types::Vector3,
|
|
part_texture_description:RobloxPartDescription,
|
|
)->MeshIndex<'a>{
|
|
MeshIndex{
|
|
mesh_type:MeshType::Union{
|
|
mesh_data,
|
|
physics_data,
|
|
size_float_bits:[size.x.to_bits(),size.y.to_bits(),size.z.to_bits()],
|
|
part_texture_description,
|
|
},
|
|
content,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct MeshLoader;
|
|
impl MeshLoader{
|
|
pub fn new()->Self{
|
|
Self
|
|
}
|
|
}
|
|
impl Loader for MeshLoader{
|
|
type Error=MeshError;
|
|
type Index<'a>=MeshIndex<'a>;
|
|
type Resource=Mesh;
|
|
fn load<'a>(&mut self,index:Self::Index<'a>)->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,size_float_bits,part_texture_description}=>{
|
|
// decode asset
|
|
let size=glam::Vec3::from_array(size_float_bits.map(f32::from_bits));
|
|
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(&ustr("PhysicsData")){
|
|
physics_data=data.as_ref();
|
|
}
|
|
}
|
|
if mesh_data.is_empty(){
|
|
if let Some(rbx_dom_weak::types::Variant::BinaryString(data))=instance.properties.get(&ustr("MeshData")){
|
|
mesh_data=data.as_ref();
|
|
}
|
|
}
|
|
crate::union::convert(physics_data,mesh_data,size,part_texture_description)?
|
|
}else{
|
|
crate::union::convert(physics_data,mesh_data,size,part_texture_description)?
|
|
}
|
|
},
|
|
};
|
|
Ok(mesh)
|
|
}
|
|
}
|