roblox mesh converter + loading
This commit is contained in:
parent
775d1972dc
commit
1f2ad779f1
25
src/lib.rs
25
src/lib.rs
@ -1,8 +1,21 @@
|
|||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
|
||||||
mod rbx;
|
mod rbx;
|
||||||
|
mod mesh;
|
||||||
mod primitives;
|
mod primitives;
|
||||||
|
|
||||||
|
pub mod data{
|
||||||
|
pub struct RobloxMeshBytes(Vec<u8>);
|
||||||
|
impl RobloxMeshBytes{
|
||||||
|
pub fn new(bytes:Vec<u8>)->Self{
|
||||||
|
Self(bytes)
|
||||||
|
}
|
||||||
|
pub(crate) fn cursor(self)->std::io::Cursor<Vec<u8>>{
|
||||||
|
std::io::Cursor::new(self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Dom(rbx_dom_weak::WeakDom);
|
pub struct Dom(rbx_dom_weak::WeakDom);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -31,6 +44,14 @@ pub fn read<R:Read>(input:R)->Result<Dom,ReadError>{
|
|||||||
|
|
||||||
//ConvertError
|
//ConvertError
|
||||||
|
|
||||||
pub fn convert<F:FnMut(Option<&str>)->strafesnet_common::model::RenderConfigId>(dom:&Dom,acquire_render_config_id:F)->rbx::PartialMap1{
|
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
||||||
rbx::convert(&dom.0,acquire_render_config_id)
|
dom:&Dom,
|
||||||
|
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.0,acquire_render_config_id,acquire_mesh_id)
|
||||||
}
|
}
|
210
src/mesh.rs
Normal file
210
src/mesh.rs
Normal file
@ -0,0 +1,210 @@
|
|||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
use rbx_mesh::mesh::{Vertex2, Vertex2Truncated};
|
||||||
|
use strafesnet_common::{integer::Planar64Vec3,model::{self, ColorId, IndexedVertex, NormalId, PolygonGroup, PolygonList, PositionId, TextureCoordinateId, VertexId}};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum Error{
|
||||||
|
Planar64Vec3(strafesnet_common::integer::Planar64TryFromFloatError),
|
||||||
|
RbxMesh(rbx_mesh::mesh::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{}
|
||||||
|
|
||||||
|
fn ingest_vertices2<
|
||||||
|
AcquirePosId,
|
||||||
|
AcquireTexId,
|
||||||
|
AcquireNormalId,
|
||||||
|
AcquireColorId,
|
||||||
|
AcquireVertexId,
|
||||||
|
>(
|
||||||
|
vertices:Vec<Vertex2>,
|
||||||
|
acquire_pos_id:&mut AcquirePosId,
|
||||||
|
acquire_tex_id:&mut AcquireTexId,
|
||||||
|
acquire_normal_id:&mut AcquireNormalId,
|
||||||
|
acquire_color_id:&mut AcquireColorId,
|
||||||
|
acquire_vertex_id:&mut AcquireVertexId,
|
||||||
|
)->Result<HashMap<rbx_mesh::mesh::VertexId2,VertexId>,Error>
|
||||||
|
where
|
||||||
|
AcquirePosId:FnMut([f32;3])->Result<PositionId,Error>,
|
||||||
|
AcquireTexId:FnMut([f32;2])->TextureCoordinateId,
|
||||||
|
AcquireNormalId:FnMut([f32;3])->Result<NormalId,Error>,
|
||||||
|
AcquireColorId:FnMut([f32;4])->ColorId,
|
||||||
|
AcquireVertexId:FnMut(IndexedVertex)->VertexId,
|
||||||
|
{
|
||||||
|
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
|
||||||
|
//while also doing the inserting unique entries into lists simultaneously
|
||||||
|
Ok(vertices.into_iter().enumerate().map(|(vertex_id,vertex)|Ok((
|
||||||
|
rbx_mesh::mesh::VertexId2(vertex_id as u32),
|
||||||
|
acquire_vertex_id(IndexedVertex{
|
||||||
|
pos:acquire_pos_id(vertex.pos)?,
|
||||||
|
tex:acquire_tex_id(vertex.tex),
|
||||||
|
normal:acquire_normal_id(vertex.norm)?,
|
||||||
|
color:acquire_color_id(vertex.color.map(|f|f as f32/255.0f32))
|
||||||
|
}),
|
||||||
|
))).collect::<Result<_,_>>()?)
|
||||||
|
}
|
||||||
|
fn ingest_vertices_truncated2<
|
||||||
|
AcquirePosId,
|
||||||
|
AcquireTexId,
|
||||||
|
AcquireNormalId,
|
||||||
|
AcquireVertexId,
|
||||||
|
>(
|
||||||
|
vertices:Vec<Vertex2Truncated>,
|
||||||
|
acquire_pos_id:&mut AcquirePosId,
|
||||||
|
acquire_tex_id:&mut AcquireTexId,
|
||||||
|
acquire_normal_id:&mut AcquireNormalId,
|
||||||
|
static_color_id:ColorId,//pick one color and fill everything with it
|
||||||
|
acquire_vertex_id:&mut AcquireVertexId,
|
||||||
|
)->Result<HashMap<rbx_mesh::mesh::VertexId2,VertexId>,Error>
|
||||||
|
where
|
||||||
|
AcquirePosId:FnMut([f32;3])->Result<PositionId,Error>,
|
||||||
|
AcquireTexId:FnMut([f32;2])->TextureCoordinateId,
|
||||||
|
AcquireNormalId:FnMut([f32;3])->Result<NormalId,Error>,
|
||||||
|
AcquireVertexId:FnMut(IndexedVertex)->VertexId,
|
||||||
|
{
|
||||||
|
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
|
||||||
|
//while also doing the inserting unique entries into lists simultaneously
|
||||||
|
Ok(vertices.into_iter().enumerate().map(|(vertex_id,vertex)|Ok((
|
||||||
|
rbx_mesh::mesh::VertexId2(vertex_id as u32),
|
||||||
|
acquire_vertex_id(IndexedVertex{
|
||||||
|
pos:acquire_pos_id(vertex.pos)?,
|
||||||
|
tex:acquire_tex_id(vertex.tex),
|
||||||
|
normal:acquire_normal_id(vertex.norm)?,
|
||||||
|
color:static_color_id
|
||||||
|
}),
|
||||||
|
))).collect::<Result<_,_>>()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ingest_faces2_lods3(
|
||||||
|
polygon_groups:&mut Vec<PolygonGroup>,
|
||||||
|
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
|
||||||
|
faces:&Vec<rbx_mesh::mesh::Face2>,
|
||||||
|
lods:&Vec<rbx_mesh::mesh::Lod3>
|
||||||
|
){
|
||||||
|
//faces have to be split into polygon groups based on lod
|
||||||
|
polygon_groups.extend(lods.windows(2).map(|lod_pair|
|
||||||
|
PolygonGroup::PolygonList(PolygonList::new(faces[lod_pair[0].0 as usize..lod_pair[1].0 as usize].iter().map(|face|
|
||||||
|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
|
||||||
|
).collect()))
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<model::Mesh,Error>{
|
||||||
|
//generate that mesh boi
|
||||||
|
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 polygon_groups=Vec::new();
|
||||||
|
let mut acquire_pos_id=|pos|{
|
||||||
|
let p=Planar64Vec3::try_from(pos).map_err(Error::Planar64Vec3)?;
|
||||||
|
Ok(PositionId::new(*pos_id_from.entry(p).or_insert_with(||{
|
||||||
|
let pos_id=unique_pos.len();
|
||||||
|
unique_pos.push(p);
|
||||||
|
pos_id
|
||||||
|
}) as u32))
|
||||||
|
};
|
||||||
|
let mut acquire_tex_id=|tex|{
|
||||||
|
let h=bytemuck::cast::<[f32;2],[u32;2]>(tex);
|
||||||
|
TextureCoordinateId::new(*tex_id_from.entry(h).or_insert_with(||{
|
||||||
|
let tex_id=unique_tex.len();
|
||||||
|
unique_tex.push(glam::Vec2::from_array(tex));
|
||||||
|
tex_id
|
||||||
|
}) as u32)
|
||||||
|
};
|
||||||
|
let mut acquire_normal_id=|normal|{
|
||||||
|
let n=Planar64Vec3::try_from(normal).map_err(Error::Planar64Vec3)?;
|
||||||
|
Ok(NormalId::new(*normal_id_from.entry(n).or_insert_with(||{
|
||||||
|
let normal_id=unique_normal.len();
|
||||||
|
unique_normal.push(n);
|
||||||
|
normal_id
|
||||||
|
}) as u32))
|
||||||
|
};
|
||||||
|
let mut acquire_color_id=|color|{
|
||||||
|
let h=bytemuck::cast::<[f32;4],[u32;4]>(color);
|
||||||
|
ColorId::new(*color_id_from.entry(h).or_insert_with(||{
|
||||||
|
let color_id=unique_color.len();
|
||||||
|
unique_color.push(glam::Vec4::from_array(color));
|
||||||
|
color_id
|
||||||
|
}) as u32)
|
||||||
|
};
|
||||||
|
let mut acquire_vertex_id=|vertex:IndexedVertex|{
|
||||||
|
VertexId::new(*vertex_id_from.entry(vertex.clone()).or_insert_with(||{
|
||||||
|
let vertex_id=unique_vertices.len();
|
||||||
|
unique_vertices.push(vertex);
|
||||||
|
vertex_id
|
||||||
|
}) as u32)
|
||||||
|
};
|
||||||
|
match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)?{
|
||||||
|
rbx_mesh::mesh::VersionedMesh::Version1(mesh)=>{
|
||||||
|
let color_id=acquire_color_id([1.0f32;4]);
|
||||||
|
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.vertices.chunks_exact(3).map(|trip|{
|
||||||
|
let mut ingest_vertex1=|vertex:&rbx_mesh::mesh::Vertex1|Ok(acquire_vertex_id(IndexedVertex{
|
||||||
|
pos:acquire_pos_id(vertex.pos)?,
|
||||||
|
tex:acquire_tex_id([vertex.tex[0],vertex.tex[1]]),
|
||||||
|
normal:acquire_normal_id(vertex.norm)?,
|
||||||
|
color:color_id,
|
||||||
|
}));
|
||||||
|
Ok(vec![ingest_vertex1(&trip[0])?,ingest_vertex1(&trip[1])?,ingest_vertex1(&trip[2])?])
|
||||||
|
}).collect::<Result<_,_>>()?)));
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::VersionedMesh::Version2(mesh)=>{
|
||||||
|
let vertex_id_map=match mesh.header.sizeof_vertex{
|
||||||
|
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
|
||||||
|
//pick white and make all the vertices white
|
||||||
|
let color_id=acquire_color_id([1.0f32;4]);
|
||||||
|
ingest_vertices_truncated2(mesh.vertices_truncated,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,color_id,&mut acquire_vertex_id)
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id),
|
||||||
|
}?;
|
||||||
|
//one big happy group for all the faces
|
||||||
|
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().map(|face|
|
||||||
|
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
|
||||||
|
).collect())));
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::VersionedMesh::Version3(mesh)=>{
|
||||||
|
let vertex_id_map=match mesh.header.sizeof_vertex{
|
||||||
|
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
|
||||||
|
let color_id=acquire_color_id([1.0f32;4]);
|
||||||
|
ingest_vertices_truncated2(mesh.vertices_truncated,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,color_id,&mut acquire_vertex_id)
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id),
|
||||||
|
}?;
|
||||||
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::VersionedMesh::Version4(mesh)=>{
|
||||||
|
let vertex_id_map=ingest_vertices2(
|
||||||
|
mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id
|
||||||
|
)?;
|
||||||
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
||||||
|
},
|
||||||
|
rbx_mesh::mesh::VersionedMesh::Version5(mesh)=>{
|
||||||
|
let vertex_id_map=ingest_vertices2(
|
||||||
|
mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id
|
||||||
|
)?;
|
||||||
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
Ok(model::Mesh{
|
||||||
|
unique_pos,
|
||||||
|
unique_normal,
|
||||||
|
unique_tex,
|
||||||
|
unique_color,
|
||||||
|
unique_vertices,
|
||||||
|
polygon_groups,
|
||||||
|
//these should probably be moved to the model...
|
||||||
|
graphics_groups:Vec::new(),
|
||||||
|
physics_groups:Vec::new(),
|
||||||
|
})
|
||||||
|
}
|
269
src/rbx.rs
269
src/rbx.rs
@ -134,8 +134,7 @@ impl ModesBuilder{
|
|||||||
self.stage_updates.push((mode_id,stage_id,stage_update));
|
self.stage_updates.push((mode_id,stage_id,stage_update));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn get_attributes(object:&rbx_dom_weak::Instance,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{
|
fn get_attributes(name:&str,can_collide:bool,velocity:Planar64Vec3,model_id:model::ModelId,modes_builder:&mut ModesBuilder,wormhole_in_model_to_id:&mut HashMap<model::ModelId,u32>,wormhole_id_to_out_model:&mut HashMap<u32,model::ModelId>)->attr::CollisionAttributes{
|
||||||
let name=object.name.as_str();
|
|
||||||
let mut general=attr::GeneralAttributes::default();
|
let mut general=attr::GeneralAttributes::default();
|
||||||
let mut intersecting=attr::IntersectingAttributes::default();
|
let mut intersecting=attr::IntersectingAttributes::default();
|
||||||
let mut contacting=attr::ContactingAttributes::default();
|
let mut contacting=attr::ContactingAttributes::default();
|
||||||
@ -404,24 +403,49 @@ enum RobloxBasePartDescription{
|
|||||||
Wedge(RobloxWedgeDescription),
|
Wedge(RobloxWedgeDescription),
|
||||||
CornerWedge(RobloxCornerWedgeDescription),
|
CornerWedge(RobloxCornerWedgeDescription),
|
||||||
}
|
}
|
||||||
|
enum Shape{
|
||||||
|
Primitive(primitives::Primitives),
|
||||||
|
MeshPart,
|
||||||
|
}
|
||||||
|
enum MeshAvailability{
|
||||||
|
Immediate,
|
||||||
|
Deferred(RenderConfigId),
|
||||||
|
}
|
||||||
|
struct DeferredModelDeferredAttributes{
|
||||||
|
render:RenderConfigId,
|
||||||
|
model:ModelDeferredAttributes,
|
||||||
|
}
|
||||||
|
struct ModelDeferredAttributes{
|
||||||
|
mesh:model::MeshId,
|
||||||
|
deferred_attributes:GetAttributesArgs,
|
||||||
|
color:model::Color4,//transparency is in here
|
||||||
|
transform:Planar64Affine3,
|
||||||
|
}
|
||||||
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,
|
||||||
}
|
}
|
||||||
pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::WeakDom,mut acquire_render_config_id:F)->PartialMap1{
|
struct GetAttributesArgs{
|
||||||
let mut modes_builder=ModesBuilder::default();
|
name:Box<str>,
|
||||||
|
can_collide:bool,
|
||||||
|
velocity:Planar64Vec3,
|
||||||
|
}
|
||||||
|
pub fn convert<AcquireRenderConfigId,AcquireMeshId>(
|
||||||
|
dom:&rbx_dom_weak::WeakDom,
|
||||||
|
mut acquire_render_config_id:AcquireRenderConfigId,
|
||||||
|
mut acquire_mesh_id:AcquireMeshId,
|
||||||
|
)->PartialMap1
|
||||||
|
where
|
||||||
|
AcquireRenderConfigId:FnMut(Option<&str>)->model::RenderConfigId,
|
||||||
|
AcquireMeshId:FnMut(&str)->model::MeshId,
|
||||||
|
{
|
||||||
|
|
||||||
let mut models1=Vec::new();
|
let mut deferred_models_deferred_attributes=Vec::new();
|
||||||
let mut meshes=Vec::new();
|
let mut primitive_models_deferred_attributes=Vec::new();
|
||||||
let mut indexed_model_id_from_description=HashMap::new();
|
let mut primitive_meshes=Vec::new();
|
||||||
|
let mut mesh_id_from_description=HashMap::new();
|
||||||
let mut unique_attributes=Vec::new();
|
|
||||||
let mut attributes_id_from_attributes=HashMap::new();
|
|
||||||
|
|
||||||
let mut wormhole_in_model_to_id=HashMap::new();
|
|
||||||
let mut wormhole_id_to_out_model=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=acquire_render_config_id(None);
|
||||||
@ -462,31 +486,35 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
}
|
}
|
||||||
|
|
||||||
//at this point a new model is going to be generated for sure.
|
//at this point a new model is going to be generated for sure.
|
||||||
let model_id=model::ModelId::new(models1.len() as u32);
|
let model_id=model::ModelId::new(primitive_models_deferred_attributes.len() as u32);
|
||||||
|
|
||||||
//TODO: also detect "CylinderMesh" etc here
|
//TODO: also detect "CylinderMesh" etc here
|
||||||
let shape=match object.class.as_str(){
|
let shape=match object.class.as_str(){
|
||||||
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
|
"Part"=>if let Some(rbx_dom_weak::types::Variant::Enum(shape))=object.properties.get("Shape"){
|
||||||
match shape.to_u32(){
|
Shape::Primitive(match shape.to_u32(){
|
||||||
0=>primitives::Primitives::Sphere,
|
0=>primitives::Primitives::Sphere,
|
||||||
1=>primitives::Primitives::Cube,
|
1=>primitives::Primitives::Cube,
|
||||||
2=>primitives::Primitives::Cylinder,
|
2=>primitives::Primitives::Cylinder,
|
||||||
3=>primitives::Primitives::Wedge,
|
3=>primitives::Primitives::Wedge,
|
||||||
4=>primitives::Primitives::CornerWedge,
|
4=>primitives::Primitives::CornerWedge,
|
||||||
other=>panic!("Funky roblox PartType={};",other),
|
other=>panic!("Funky roblox PartType={};",other),
|
||||||
}
|
})
|
||||||
}else{
|
}else{
|
||||||
panic!("Part has no Shape!");
|
panic!("Part has no Shape!");
|
||||||
},
|
},
|
||||||
"TrussPart"=>primitives::Primitives::Cube,
|
"TrussPart"=>Shape::Primitive(primitives::Primitives::Cube),
|
||||||
"WedgePart"=>primitives::Primitives::Wedge,
|
"WedgePart"=>Shape::Primitive(primitives::Primitives::Wedge),
|
||||||
"CornerWedgePart"=>primitives::Primitives::CornerWedge,
|
"CornerWedgePart"=>Shape::Primitive(primitives::Primitives::CornerWedge),
|
||||||
|
"MeshPart"=>Shape::MeshPart,
|
||||||
_=>{
|
_=>{
|
||||||
println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class);
|
println!("Unsupported BasePart ClassName={}; defaulting to cube",object.class);
|
||||||
primitives::Primitives::Cube
|
Shape::Primitive(primitives::Primitives::Cube)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let (availability,mesh_id)=match shape{
|
||||||
|
Shape::Primitive(primitive_shape)=>{
|
||||||
|
//TODO: TAB TAB
|
||||||
//use the biggest one and cut it down later...
|
//use the biggest one and cut it down later...
|
||||||
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None];
|
||||||
temp_objects.clear();
|
temp_objects.clear();
|
||||||
@ -549,7 +577,7 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
transform:roblox_texture_transform,
|
transform:roblox_texture_transform,
|
||||||
});
|
});
|
||||||
}else{
|
}else{
|
||||||
println!("NormalId={} unsupported for shape={:?}",normal_id,shape);
|
println!("NormalId={} unsupported for shape={:?}",normal_id,primitive_shape);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -563,7 +591,7 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
f4,//Cube::Bottom
|
f4,//Cube::Bottom
|
||||||
f5,//Cube::Front
|
f5,//Cube::Front
|
||||||
]=part_texture_description;
|
]=part_texture_description;
|
||||||
let basepart_texture_description=match shape{
|
let basepart_description=match primitive_shape{
|
||||||
primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere([f0,f1,f2,f3,f4,f5]),
|
primitives::Primitives::Sphere=>RobloxBasePartDescription::Sphere([f0,f1,f2,f3,f4,f5]),
|
||||||
primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]),
|
primitives::Primitives::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]),
|
||||||
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder([f0,f1,f2,f3,f4,f5]),
|
primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder([f0,f1,f2,f3,f4,f5]),
|
||||||
@ -585,13 +613,13 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
]),
|
]),
|
||||||
};
|
};
|
||||||
//make new model if unit cube has not been created before
|
//make new model if unit cube has not been created before
|
||||||
let indexed_model_id=if let Some(&indexed_model_id)=indexed_model_id_from_description.get(&basepart_texture_description){
|
let mesh_id=if let Some(&mesh_id)=mesh_id_from_description.get(&basepart_description){
|
||||||
//push to existing texture model
|
//push to existing texture model
|
||||||
indexed_model_id
|
mesh_id
|
||||||
}else{
|
}else{
|
||||||
let indexed_model_id=model::MeshId::new(meshes.len() as u32);
|
let mesh_id=model::MeshId::new(primitive_meshes.len() as u32);
|
||||||
indexed_model_id_from_description.insert(basepart_texture_description.clone(),indexed_model_id);//borrow checker going crazy
|
mesh_id_from_description.insert(basepart_description.clone(),mesh_id);//borrow checker going crazy
|
||||||
meshes.push(match basepart_texture_description{
|
let mesh=match basepart_description{
|
||||||
RobloxBasePartDescription::Sphere(part_texture_description)
|
RobloxBasePartDescription::Sphere(part_texture_description)
|
||||||
|RobloxBasePartDescription::Cylinder(part_texture_description)
|
|RobloxBasePartDescription::Cylinder(part_texture_description)
|
||||||
|RobloxBasePartDescription::Part(part_texture_description)=>{
|
|RobloxBasePartDescription::Part(part_texture_description)=>{
|
||||||
@ -652,34 +680,166 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
}
|
}
|
||||||
primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description)
|
primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description)
|
||||||
},
|
},
|
||||||
});
|
|
||||||
indexed_model_id
|
|
||||||
};
|
};
|
||||||
let attributes=get_attributes(
|
primitive_meshes.push(mesh);
|
||||||
&object,
|
mesh_id
|
||||||
*can_collide,
|
};
|
||||||
Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),
|
(MeshAvailability::Immediate,mesh_id)
|
||||||
|
},
|
||||||
|
Shape::MeshPart=>if let (
|
||||||
|
Some(rbx_dom_weak::types::Variant::Content(mesh_asset_id)),
|
||||||
|
Some(rbx_dom_weak::types::Variant::Content(texture_asset_id)),
|
||||||
|
)=(
|
||||||
|
object.properties.get("MeshId"),
|
||||||
|
object.properties.get("TextureID"),
|
||||||
|
){
|
||||||
|
(
|
||||||
|
MeshAvailability::Deferred(acquire_render_config_id(Some(texture_asset_id.as_ref()))),
|
||||||
|
acquire_mesh_id(mesh_asset_id.as_ref()),
|
||||||
|
)
|
||||||
|
}else{
|
||||||
|
panic!("Mesh has no Mesh or Texture");
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let model_deferred_attributes=ModelDeferredAttributes{
|
||||||
|
mesh:mesh_id,
|
||||||
|
transform:model_transform,
|
||||||
|
color:glam::vec4(color3.r as f32/255f32, color3.g as f32/255f32, color3.b as f32/255f32, 1.0-*transparency),
|
||||||
|
deferred_attributes:GetAttributesArgs{
|
||||||
|
name:object.name.as_str().into(),
|
||||||
|
can_collide:*can_collide,
|
||||||
|
velocity:Planar64Vec3::try_from([velocity.x,velocity.y,velocity.z]).unwrap(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match availability{
|
||||||
|
MeshAvailability::Immediate=>primitive_models_deferred_attributes.push(model_deferred_attributes),
|
||||||
|
MeshAvailability::Deferred(render)=>deferred_models_deferred_attributes.push(DeferredModelDeferredAttributes{
|
||||||
|
render,
|
||||||
|
model:model_deferred_attributes
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PartialMap1{
|
||||||
|
primitive_meshes,
|
||||||
|
primitive_models_deferred_attributes,
|
||||||
|
deferred_models_deferred_attributes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct MeshWithAabb{
|
||||||
|
mesh:model::Mesh,
|
||||||
|
aabb:strafesnet_common::aabb::Aabb,
|
||||||
|
}
|
||||||
|
pub struct PartialMap1{
|
||||||
|
primitive_meshes:Vec<model::Mesh>,
|
||||||
|
primitive_models_deferred_attributes:Vec<ModelDeferredAttributes>,
|
||||||
|
deferred_models_deferred_attributes:Vec<DeferredModelDeferredAttributes>,
|
||||||
|
}
|
||||||
|
impl PartialMap1{
|
||||||
|
pub fn add_meshpart_meshes_and_calculate_attributes(
|
||||||
|
mut self,
|
||||||
|
meshpart_meshes:impl IntoIterator<Item=(model::MeshId,crate::data::RobloxMeshBytes)>,
|
||||||
|
)->PartialMap2{
|
||||||
|
//calculate attributes
|
||||||
|
let mut modes_builder=ModesBuilder::default();
|
||||||
|
let mut unique_attributes=Vec::new();
|
||||||
|
let mut attributes_id_from_attributes=HashMap::new();
|
||||||
|
|
||||||
|
let mut wormhole_in_model_to_id=HashMap::new();
|
||||||
|
let mut wormhole_id_to_out_model=HashMap::new();
|
||||||
|
|
||||||
|
//decode roblox meshes
|
||||||
|
//generate mesh_id_map based on meshes that failed to load
|
||||||
|
let loaded_meshes:HashMap<model::MeshId,MeshWithAabb>=
|
||||||
|
meshpart_meshes.into_iter().flat_map(|(old_mesh_id,roblox_mesh_bytes)|
|
||||||
|
match crate::mesh::convert(roblox_mesh_bytes){
|
||||||
|
Ok(mesh)=>{
|
||||||
|
let mut aabb=strafesnet_common::aabb::Aabb::default();
|
||||||
|
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();
|
||||||
|
|
||||||
|
let mut mesh_id_from_render_config_id=HashMap::new();
|
||||||
|
//ignore meshes that fail to load completely for now
|
||||||
|
let mut acquire_mesh_id_from_render_config_id=|old_mesh_id,render|{
|
||||||
|
loaded_meshes.get(&old_mesh_id).map(|mesh_with_aabb|(
|
||||||
|
*mesh_id_from_render_config_id.entry(old_mesh_id).or_insert_with(||HashMap::new())
|
||||||
|
.entry(render).or_insert_with(||{
|
||||||
|
let mesh_id=model::MeshId::new(self.primitive_meshes.len() as u32);
|
||||||
|
let mut mesh_clone=mesh_with_aabb.mesh.clone();
|
||||||
|
//add a render group lool
|
||||||
|
mesh_clone.graphics_groups.push(model::IndexedGraphicsGroup{
|
||||||
|
render,
|
||||||
|
//the lowest lod is highest quality
|
||||||
|
groups:vec![model::PolygonGroupId::new(0)]
|
||||||
|
});
|
||||||
|
self.primitive_meshes.push(mesh_clone);
|
||||||
|
mesh_id
|
||||||
|
}),
|
||||||
|
&mesh_with_aabb.aabb,
|
||||||
|
))
|
||||||
|
};
|
||||||
|
//now that the meshes are loaded, these models can be generated
|
||||||
|
let models_owned_attributes:Vec<ModelOwnedAttributes>=
|
||||||
|
self.deferred_models_deferred_attributes.into_iter().flat_map(|deferred_model_deferred_attributes|{
|
||||||
|
//meshes need to be cloned from loaded_meshes with a new id when they are used with a new render_id
|
||||||
|
//insert into primitive_meshes
|
||||||
|
let (mesh,aabb)=acquire_mesh_id_from_render_config_id(
|
||||||
|
deferred_model_deferred_attributes.model.mesh,
|
||||||
|
deferred_model_deferred_attributes.render
|
||||||
|
)?;
|
||||||
|
let size=aabb.size();
|
||||||
|
Some(ModelDeferredAttributes{
|
||||||
|
mesh,
|
||||||
|
deferred_attributes:deferred_model_deferred_attributes.model.deferred_attributes,
|
||||||
|
color:deferred_model_deferred_attributes.model.color,
|
||||||
|
transform:Planar64Affine3::new(
|
||||||
|
Planar64Mat3::from_cols(
|
||||||
|
deferred_model_deferred_attributes.model.transform.matrix3.x_axis*2/size.x(),
|
||||||
|
deferred_model_deferred_attributes.model.transform.matrix3.y_axis*2/size.y(),
|
||||||
|
deferred_model_deferred_attributes.model.transform.matrix3.z_axis*2/size.z()
|
||||||
|
),
|
||||||
|
deferred_model_deferred_attributes.model.transform.translation
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}).chain(self.primitive_models_deferred_attributes.into_iter())
|
||||||
|
.enumerate().map(|(model_id,model_deferred_attributes)|{
|
||||||
|
let model_id=model::ModelId::new(model_id as u32);
|
||||||
|
ModelOwnedAttributes{
|
||||||
|
mesh:model_deferred_attributes.mesh,
|
||||||
|
attributes:get_attributes(
|
||||||
|
&model_deferred_attributes.deferred_attributes.name,
|
||||||
|
model_deferred_attributes.deferred_attributes.can_collide,
|
||||||
|
model_deferred_attributes.deferred_attributes.velocity,
|
||||||
model_id,
|
model_id,
|
||||||
&mut modes_builder,
|
&mut modes_builder,
|
||||||
&mut wormhole_in_model_to_id,
|
&mut wormhole_in_model_to_id,
|
||||||
&mut wormhole_id_to_out_model,
|
&mut wormhole_id_to_out_model,
|
||||||
);
|
),
|
||||||
models1.push(ModelOwnedAttributes{
|
color:model_deferred_attributes.color,
|
||||||
mesh:indexed_model_id,
|
transform:model_deferred_attributes.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),
|
|
||||||
attributes,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}).collect();
|
||||||
}
|
let models=models_owned_attributes.into_iter().enumerate().map(|(model_id,mut model_owned_attributes)|{
|
||||||
let models=models1.into_iter().enumerate().map(|(model_id,mut model1)|{
|
//TODO: TAB
|
||||||
let model_id=model::ModelId::new(model_id as u32);
|
let model_id=model::ModelId::new(model_id as u32);
|
||||||
//update attributes with wormhole id
|
//update attributes with wormhole id
|
||||||
//TODO: errors/prints
|
//TODO: errors/prints
|
||||||
if let Some(wormhole_id)=wormhole_in_model_to_id.get(&model_id){
|
if let Some(wormhole_id)=wormhole_in_model_to_id.get(&model_id){
|
||||||
if let Some(&wormhole_out_model_id)=wormhole_id_to_out_model.get(wormhole_id){
|
if let Some(&wormhole_out_model_id)=wormhole_id_to_out_model.get(wormhole_id){
|
||||||
match &mut model1.attributes{
|
match &mut model_owned_attributes.attributes{
|
||||||
attr::CollisionAttributes::Contact{contacting:_,general}
|
attr::CollisionAttributes::Contact{contacting:_,general}
|
||||||
|attr::CollisionAttributes::Intersect{intersecting:_,general}
|
|attr::CollisionAttributes::Intersect{intersecting:_,general}
|
||||||
=>general.wormhole=Some(attr::Wormhole{destination_model:wormhole_out_model_id}),
|
=>general.wormhole=Some(attr::Wormhole{destination_model:wormhole_out_model_id}),
|
||||||
@ -689,36 +849,37 @@ pub fn convert<F:FnMut(Option<&str>)->model::RenderConfigId>(dom:&rbx_dom_weak::
|
|||||||
}
|
}
|
||||||
|
|
||||||
//index the attributes
|
//index the attributes
|
||||||
let attributes_id=if let Some(&attributes_id)=attributes_id_from_attributes.get(&model1.attributes){
|
let attributes_id=if let Some(&attributes_id)=attributes_id_from_attributes.get(&model_owned_attributes.attributes){
|
||||||
attributes_id
|
attributes_id
|
||||||
}else{
|
}else{
|
||||||
let attributes_id=attr::CollisionAttributesId::new(unique_attributes.len() as u32);
|
let attributes_id=attr::CollisionAttributesId::new(unique_attributes.len() as u32);
|
||||||
attributes_id_from_attributes.insert(model1.attributes.clone(),attributes_id);
|
attributes_id_from_attributes.insert(model_owned_attributes.attributes.clone(),attributes_id);
|
||||||
unique_attributes.push(model1.attributes);
|
unique_attributes.push(model_owned_attributes.attributes);
|
||||||
attributes_id
|
attributes_id
|
||||||
};
|
};
|
||||||
model::Model{
|
model::Model{
|
||||||
mesh:model1.mesh,
|
mesh:model_owned_attributes.mesh,
|
||||||
transform:model1.transform,
|
transform:model_owned_attributes.transform,
|
||||||
color:model1.color,
|
color:model_owned_attributes.color,
|
||||||
attributes:attributes_id,
|
attributes:attributes_id,
|
||||||
}
|
}
|
||||||
}).collect();
|
}).collect();
|
||||||
PartialMap1{
|
PartialMap2{
|
||||||
meshes,
|
meshes:self.primitive_meshes,
|
||||||
models,
|
models,
|
||||||
modes:modes_builder.build(),
|
modes:modes_builder.build(),
|
||||||
attributes:unique_attributes,
|
attributes:unique_attributes,
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PartialMap1{
|
pub struct PartialMap2{
|
||||||
meshes:Vec<model::Mesh>,
|
meshes:Vec<model::Mesh>,
|
||||||
models:Vec<model::Model>,
|
models:Vec<model::Model>,
|
||||||
modes:gameplay_modes::Modes,
|
modes:gameplay_modes::Modes,
|
||||||
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
attributes:Vec<strafesnet_common::gameplay_attributes::CollisionAttributes>,
|
||||||
}
|
}
|
||||||
impl PartialMap1{
|
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:impl IntoIterator<Item=(model::RenderConfigId,model::RenderConfig)>,
|
||||||
|
Loading…
Reference in New Issue
Block a user