148 lines
5.7 KiB
Rust
148 lines
5.7 KiB
Rust
use std::collections::HashMap;
|
|
|
|
use rbx_mesh::mesh::{Vertex2,Vertex2Truncated};
|
|
use strafesnet_common::aabb::Aabb;
|
|
use strafesnet_common::integer::vec3;
|
|
use strafesnet_common::model::{self,ColorId,IndexedVertex,PolygonGroup,PolygonList,RenderConfigId,VertexId};
|
|
|
|
use crate::loader::MeshWithSize;
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error{
|
|
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(
|
|
vertices:Vec<Vertex2>,
|
|
mb:&mut model::MeshBuilder,
|
|
)->HashMap<rbx_mesh::mesh::VertexId2,VertexId>{
|
|
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
|
|
//while also doing the inserting unique entries into lists simultaneously
|
|
// vertex positions that fail to convert are DROPPED
|
|
vertices.into_iter().enumerate().filter_map(|(vertex_id,vertex)|Some((
|
|
rbx_mesh::mesh::VertexId2(vertex_id as u32),
|
|
{
|
|
let vertex=IndexedVertex{
|
|
pos:mb.acquire_pos_id(vec3::try_from_f32_array(vertex.pos).ok()?),
|
|
tex:mb.acquire_tex_id(glam::Vec2::from_array(vertex.tex)),
|
|
normal:mb.acquire_normal_id(vec3::try_from_f32_array(vertex.norm).ok()?),
|
|
color:mb.acquire_color_id(glam::Vec4::from_array(vertex.color.map(|f|f as f32/255.0f32))),
|
|
};
|
|
mb.acquire_vertex_id(vertex)
|
|
}
|
|
))).collect()
|
|
}
|
|
fn ingest_vertices_truncated2(
|
|
vertices:Vec<Vertex2Truncated>,
|
|
mb:&mut model::MeshBuilder,
|
|
static_color_id:ColorId,//pick one color and fill everything with it
|
|
)->HashMap<rbx_mesh::mesh::VertexId2,VertexId>{
|
|
//this monster is collecting a map of old_vertices_index -> unique_vertices_index
|
|
//while also doing the inserting unique entries into lists simultaneously
|
|
// vertex positions that fail to convert are DROPPED
|
|
vertices.into_iter().enumerate().filter_map(|(vertex_id,vertex)|Some((
|
|
rbx_mesh::mesh::VertexId2(vertex_id as u32),
|
|
{
|
|
let vertex=IndexedVertex{
|
|
pos:mb.acquire_pos_id(vec3::try_from_f32_array(vertex.pos).ok()?),
|
|
tex:mb.acquire_tex_id(glam::Vec2::from_array(vertex.tex)),
|
|
normal:mb.acquire_normal_id(vec3::try_from_f32_array(vertex.norm).ok()?),
|
|
color:static_color_id,
|
|
};
|
|
mb.acquire_vertex_id(vertex)
|
|
}
|
|
))).collect()
|
|
}
|
|
|
|
fn ingest_faces2_lods3(
|
|
polygon_groups:&mut Vec<PolygonGroup>,
|
|
vertex_id_map:&HashMap<rbx_mesh::mesh::VertexId2,VertexId>,
|
|
faces:&[rbx_mesh::mesh::Face2],
|
|
lods:&[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().filter_map(|rbx_mesh::mesh::Face2(v0,v1,v2)|
|
|
Some(vec![*vertex_id_map.get(&v0)?,*vertex_id_map.get(&v1)?,*vertex_id_map.get(&v2)?])
|
|
).collect()))
|
|
))
|
|
}
|
|
|
|
pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithSize,Error>{
|
|
//generate that mesh boi
|
|
let mut polygon_groups=Vec::new();
|
|
let mut mb=model::MeshBuilder::new();
|
|
match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)?{
|
|
rbx_mesh::mesh::Mesh::V1(mesh)=>{
|
|
let color_id=mb.acquire_color_id(glam::Vec4::ONE);
|
|
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.vertices.chunks_exact(3).filter_map(|trip|{
|
|
let mut ingest_vertex1=|vertex:&rbx_mesh::mesh::Vertex1|{
|
|
let vertex=IndexedVertex{
|
|
pos:mb.acquire_pos_id(vec3::try_from_f32_array(vertex.pos).ok()?),
|
|
tex:mb.acquire_tex_id(glam::vec2(vertex.tex[0],vertex.tex[1])),
|
|
normal:mb.acquire_normal_id(vec3::try_from_f32_array(vertex.norm).ok()?),
|
|
color:color_id,
|
|
};
|
|
Some(mb.acquire_vertex_id(vertex))
|
|
};
|
|
Some(vec![ingest_vertex1(&trip[0])?,ingest_vertex1(&trip[1])?,ingest_vertex1(&trip[2])?])
|
|
}).collect())));
|
|
},
|
|
rbx_mesh::mesh::Mesh::V2(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=mb.acquire_color_id(glam::Vec4::ONE);
|
|
ingest_vertices_truncated2(mesh.vertices_truncated,&mut mb,color_id)
|
|
},
|
|
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut mb),
|
|
};
|
|
//one big happy group for all the faces
|
|
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.faces.into_iter().filter_map(|face|
|
|
Some(vec![*vertex_id_map.get(&face.0)?,*vertex_id_map.get(&face.1)?,*vertex_id_map.get(&face.2)?])
|
|
).collect())));
|
|
},
|
|
rbx_mesh::mesh::Mesh::V3(mesh)=>{
|
|
let vertex_id_map=match mesh.header.sizeof_vertex{
|
|
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
|
|
let color_id=mb.acquire_color_id(glam::Vec4::ONE);
|
|
ingest_vertices_truncated2(mesh.vertices_truncated,&mut mb,color_id)
|
|
},
|
|
rbx_mesh::mesh::SizeOfVertex2::Full=>ingest_vertices2(mesh.vertices,&mut mb),
|
|
};
|
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
|
},
|
|
rbx_mesh::mesh::Mesh::V4(mesh)=>{
|
|
let vertex_id_map=ingest_vertices2(mesh.vertices,&mut mb);
|
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
|
},
|
|
rbx_mesh::mesh::Mesh::V5(mesh)=>{
|
|
let vertex_id_map=ingest_vertices2(mesh.vertices,&mut mb);
|
|
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
|
|
},
|
|
}
|
|
let mesh=mb.build(
|
|
polygon_groups,
|
|
//these should probably be moved to the model...
|
|
//but what if models want to use the same texture
|
|
vec![model::IndexedGraphicsGroup{
|
|
render:RenderConfigId::new(0),
|
|
//the lowest lod is highest quality
|
|
groups:vec![model::PolygonGroupId::new(0)]
|
|
}],
|
|
//disable physics
|
|
Vec::new(),
|
|
);
|
|
let mut aabb=Aabb::default();
|
|
for &point in &mesh.unique_pos{
|
|
aabb.grow(point);
|
|
}
|
|
Ok(MeshWithSize{mesh,size:aabb.size()})
|
|
}
|