From 1f2ad779f1119d795b666df073df6cbfb91f3598 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Wed, 13 Mar 2024 13:34:06 -0700 Subject: [PATCH] roblox mesh converter + loading --- src/lib.rs | 25 ++++- src/mesh.rs | 210 ++++++++++++++++++++++++++++++++++++++++ src/rbx.rs | 269 +++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 448 insertions(+), 56 deletions(-) create mode 100644 src/mesh.rs diff --git a/src/lib.rs b/src/lib.rs index 412cb55..c04c7d9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,21 @@ use std::io::Read; mod rbx; +mod mesh; mod primitives; +pub mod data{ + pub struct RobloxMeshBytes(Vec); + impl RobloxMeshBytes{ + pub fn new(bytes:Vec)->Self{ + Self(bytes) + } + pub(crate) fn cursor(self)->std::io::Cursor>{ + std::io::Cursor::new(self.0) + } + } +} + pub struct Dom(rbx_dom_weak::WeakDom); #[derive(Debug)] @@ -31,6 +44,14 @@ pub fn read(input:R)->Result{ //ConvertError -pub fn convert)->strafesnet_common::model::RenderConfigId>(dom:&Dom,acquire_render_config_id:F)->rbx::PartialMap1{ - rbx::convert(&dom.0,acquire_render_config_id) +pub fn convert( + 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) } \ No newline at end of file diff --git a/src/mesh.rs b/src/mesh.rs new file mode 100644 index 0000000..3215118 --- /dev/null +++ b/src/mesh.rs @@ -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, + 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,Error> +where + AcquirePosId:FnMut([f32;3])->Result, + AcquireTexId:FnMut([f32;2])->TextureCoordinateId, + AcquireNormalId:FnMut([f32;3])->Result, + 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::>()?) +} +fn ingest_vertices_truncated2< + AcquirePosId, + AcquireTexId, + AcquireNormalId, + AcquireVertexId, +>( + vertices:Vec, + 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,Error> +where + AcquirePosId:FnMut([f32;3])->Result, + AcquireTexId:FnMut([f32;2])->TextureCoordinateId, + AcquireNormalId:FnMut([f32;3])->Result, + 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::>()?) +} + +fn ingest_faces2_lods3( + polygon_groups:&mut Vec, + vertex_id_map:&HashMap, + faces:&Vec, + lods:&Vec +){ + //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{ + //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::>()?))); + }, + 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(), + }) +} \ No newline at end of file diff --git a/src/rbx.rs b/src/rbx.rs index f7626c7..d63c4ae 100644 --- a/src/rbx.rs +++ b/src/rbx.rs @@ -134,8 +134,7 @@ impl ModesBuilder{ 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,wormhole_id_to_out_model:&mut HashMap)->attr::CollisionAttributes{ - let name=object.name.as_str(); +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,wormhole_id_to_out_model:&mut HashMap)->attr::CollisionAttributes{ let mut general=attr::GeneralAttributes::default(); let mut intersecting=attr::IntersectingAttributes::default(); let mut contacting=attr::ContactingAttributes::default(); @@ -404,24 +403,49 @@ enum RobloxBasePartDescription{ Wedge(RobloxWedgeDescription), 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{ mesh:model::MeshId, attributes:attr::CollisionAttributes, color:model::Color4,//transparency is in here transform:Planar64Affine3, } -pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak::WeakDom,mut acquire_render_config_id:F)->PartialMap1{ - let mut modes_builder=ModesBuilder::default(); +struct GetAttributesArgs{ + name:Box, + can_collide:bool, + velocity:Planar64Vec3, +} +pub fn convert( + 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 meshes=Vec::new(); - let mut indexed_model_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(); + let mut deferred_models_deferred_attributes=Vec::new(); + let mut primitive_models_deferred_attributes=Vec::new(); + let mut primitive_meshes=Vec::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 let textureless_render_group=acquire_render_config_id(None); @@ -462,31 +486,35 @@ pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak:: } //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 let shape=match object.class.as_str(){ "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, 1=>primitives::Primitives::Cube, 2=>primitives::Primitives::Cylinder, 3=>primitives::Primitives::Wedge, 4=>primitives::Primitives::CornerWedge, other=>panic!("Funky roblox PartType={};",other), - } + }) }else{ panic!("Part has no Shape!"); }, - "TrussPart"=>primitives::Primitives::Cube, - "WedgePart"=>primitives::Primitives::Wedge, - "CornerWedgePart"=>primitives::Primitives::CornerWedge, + "TrussPart"=>Shape::Primitive(primitives::Primitives::Cube), + "WedgePart"=>Shape::Primitive(primitives::Primitives::Wedge), + "CornerWedgePart"=>Shape::Primitive(primitives::Primitives::CornerWedge), + "MeshPart"=>Shape::MeshPart, _=>{ 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... let mut part_texture_description:RobloxPartDescription=[None,None,None,None,None,None]; temp_objects.clear(); @@ -549,7 +577,7 @@ pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak:: transform:roblox_texture_transform, }); }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)->model::RenderConfigId>(dom:&rbx_dom_weak:: f4,//Cube::Bottom f5,//Cube::Front ]=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::Cube=>RobloxBasePartDescription::Part([f0,f1,f2,f3,f4,f5]), primitives::Primitives::Cylinder=>RobloxBasePartDescription::Cylinder([f0,f1,f2,f3,f4,f5]), @@ -585,13 +613,13 @@ pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak:: ]), }; //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 - indexed_model_id + mesh_id }else{ - let indexed_model_id=model::MeshId::new(meshes.len() as u32); - indexed_model_id_from_description.insert(basepart_texture_description.clone(),indexed_model_id);//borrow checker going crazy - meshes.push(match basepart_texture_description{ + let mesh_id=model::MeshId::new(primitive_meshes.len() as u32); + mesh_id_from_description.insert(basepart_description.clone(),mesh_id);//borrow checker going crazy + let mesh=match basepart_description{ RobloxBasePartDescription::Sphere(part_texture_description) |RobloxBasePartDescription::Cylinder(part_texture_description) |RobloxBasePartDescription::Part(part_texture_description)=>{ @@ -652,34 +680,166 @@ pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak:: } primitives::generate_partial_unit_cornerwedge(cornerwedge_face_description) }, - }); - indexed_model_id + }; + primitive_meshes.push(mesh); + mesh_id }; - let attributes=get_attributes( - &object, - *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, + primitive_models_deferred_attributes:Vec, + deferred_models_deferred_attributes:Vec, +} +impl PartialMap1{ + pub fn add_meshpart_meshes_and_calculate_attributes( + mut self, + meshpart_meshes:impl IntoIterator, + )->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= + 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= + 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, &mut modes_builder, &mut wormhole_in_model_to_id, &mut wormhole_id_to_out_model, - ); - models1.push(ModelOwnedAttributes{ - mesh:indexed_model_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), - attributes, - }); + ), + color:model_deferred_attributes.color, + transform:model_deferred_attributes.transform, } - } - } - let models=models1.into_iter().enumerate().map(|(model_id,mut model1)|{ + }).collect(); + let models=models_owned_attributes.into_iter().enumerate().map(|(model_id,mut model_owned_attributes)|{ + //TODO: TAB let model_id=model::ModelId::new(model_id as u32); //update attributes with wormhole id //TODO: errors/prints 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){ - match &mut model1.attributes{ + match &mut model_owned_attributes.attributes{ attr::CollisionAttributes::Contact{contacting:_,general} |attr::CollisionAttributes::Intersect{intersecting:_,general} =>general.wormhole=Some(attr::Wormhole{destination_model:wormhole_out_model_id}), @@ -689,36 +849,37 @@ pub fn convert)->model::RenderConfigId>(dom:&rbx_dom_weak:: } //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 }else{ let attributes_id=attr::CollisionAttributesId::new(unique_attributes.len() as u32); - attributes_id_from_attributes.insert(model1.attributes.clone(),attributes_id); - unique_attributes.push(model1.attributes); + attributes_id_from_attributes.insert(model_owned_attributes.attributes.clone(),attributes_id); + unique_attributes.push(model_owned_attributes.attributes); attributes_id }; model::Model{ - mesh:model1.mesh, - transform:model1.transform, - color:model1.color, + mesh:model_owned_attributes.mesh, + transform:model_owned_attributes.transform, + color:model_owned_attributes.color, attributes:attributes_id, } }).collect(); - PartialMap1{ - meshes, + PartialMap2{ + meshes:self.primitive_meshes, models, modes:modes_builder.build(), attributes:unique_attributes, } + } } -pub struct PartialMap1{ +pub struct PartialMap2{ meshes:Vec, models:Vec, modes:gameplay_modes::Modes, attributes:Vec, } -impl PartialMap1{ +impl PartialMap2{ pub fn add_render_configs_and_textures( self, render_configs:impl IntoIterator,