Compare commits

..

6 Commits

Author SHA1 Message Date
54e82ee1a6 print advance_time 2025-05-22 16:11:40 -07:00
dcda00f18d do not strafe tick 2025-05-22 16:11:40 -07:00
5108d58847 print instruction query and execute 2025-05-22 16:11:40 -07:00
84111d752c mark collision end and culling 2025-05-22 16:11:40 -07:00
e6284ae4dc debug 2025-05-22 16:11:40 -07:00
99f16706c6 fix bug 3, break everything else 2025-05-22 16:11:36 -07:00
9 changed files with 305 additions and 358 deletions

569
Cargo.lock generated

File diff suppressed because it is too large Load Diff

@ -61,6 +61,7 @@ impl<T> Body<T>
self.velocity+(self.acceleration*dt).map(|elem|elem.divide().clamp_1()) self.velocity+(self.acceleration*dt).map(|elem|elem.divide().clamp_1())
} }
pub fn advance_time(&mut self,time:Time<T>){ pub fn advance_time(&mut self,time:Time<T>){
println!("advance_time");
self.position=self.extrapolated_position(time); self.position=self.extrapolated_position(time);
self.velocity=self.extrapolated_velocity(time); self.velocity=self.extrapolated_velocity(time);
self.time=time; self.time=time;
@ -102,6 +103,7 @@ impl<T> Body<T>
self.acceleration.map(|elem|(dt*elem).divide().clamp())+self.velocity self.acceleration.map(|elem|(dt*elem).divide().clamp())+self.velocity
} }
pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){ pub fn advance_time_ratio_dt(&mut self,dt:crate::model::GigaTime){
println!("advance_time_ratio_dt");
self.position=self.extrapolated_position_ratio_dt(dt); self.position=self.extrapolated_position_ratio_dt(dt);
self.velocity=self.extrapolated_velocity_ratio_dt(dt); self.velocity=self.extrapolated_velocity_ratio_dt(dt);
self.time+=dt.into(); self.time+=dt.into();

@ -4,6 +4,7 @@ use crate::physics::{Time,Body};
use core::ops::Bound; use core::ops::Bound;
#[derive(Debug)]
enum Transition<M:MeshQuery>{ enum Transition<M:MeshQuery>{
Miss, Miss,
Next(FEV<M>,GigaTime), Next(FEV<M>,GigaTime),
@ -76,6 +77,8 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
M::Face:Copy, M::Face:Copy,
M::Edge:Copy, M::Edge:Copy,
M::Vert:Copy, M::Vert:Copy,
M:std::fmt::Debug,
F:std::fmt::Display,
F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>, F:core::ops::Mul<Fixed<1,32>,Output=Fixed<4,128>>,
<F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum, <F as core::ops::Mul<Fixed<1,32>>>::Output:core::iter::Sum,
M::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>, M::Offset:core::ops::Sub<<F as std::ops::Mul<Fixed<1,32>>>::Output>,
@ -90,10 +93,15 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
//n=face.normal d=face.dot //n=face.normal d=face.dot
//n.a t^2+n.v t+n.p-d==0 //n.a t^2+n.v t+n.p-d==0
let (n,d)=mesh.face_nd(face_id); let (n,d)=mesh.face_nd(face_id);
println!("Face n={} d={}",n,d);
//TODO: use higher precision d value? //TODO: use higher precision d value?
//use the mesh transform translation instead of baking it into the d value. //use the mesh transform translation instead of baking it into the d value.
for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){ for dt in Fixed::<4,128>::zeroes2((n.dot(body.position)-d)*2,n.dot(body.velocity)*2,n.dot(body.acceleration)){
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let low=low(&lower_bound,&dt);
let upp=upp(&dt,&upper_bound);
let into=n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative();
println!("dt={} low={low} upp={upp} into={into}",dt.divide());
if low&&upp&&into{
upper_bound=Bound::Included(dt); upper_bound=Bound::Included(dt);
best_transition=Transition::Hit(face_id,dt); best_transition=Transition::Hit(face_id,dt);
break; break;
@ -119,19 +127,24 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
}, },
&FEV::Edge(edge_id)=>{ &FEV::Edge(edge_id)=>{
//test each face collision time, ignoring roots with zero or conflicting derivative //test each face collision time, ignoring roots with zero or conflicting derivative
let edge_n=mesh.edge_n(edge_id);
let edge_verts=mesh.edge_verts(edge_id); let edge_verts=mesh.edge_verts(edge_id);
let &[ev0,ev1]=edge_verts.as_ref(); let &[ev0,ev1]=edge_verts.as_ref();
let (v0,v1)=(mesh.vert(ev0),mesh.vert(ev1)); let delta_pos=body.position*2-(mesh.vert(ev0)+mesh.vert(ev1));
let edge_n=v1-v0;
let delta_pos=body.position*2-(v0+v1);
for (i,&edge_face_id) in mesh.edge_faces(edge_id).as_ref().iter().enumerate(){ for (i,&edge_face_id) in mesh.edge_faces(edge_id).as_ref().iter().enumerate(){
let face_n=mesh.face_nd(edge_face_id).0; let face_n=mesh.face_nd(edge_face_id).0;
//edge_n gets parity from the order of edge_faces //edge_n gets parity from the order of edge_faces
let n=face_n.cross(edge_n)*((i as i64)*2-1); let n=face_n.cross(edge_n)*((i as i64)*2-1);
let d=n.dot(delta_pos).wrap_4();
println!("Edge Face={:?} boundary_n={} boundary_d={}",edge_face_id,n,d>>1);
//WARNING yada yada d *2 //WARNING yada yada d *2
//wrap for speed //wrap for speed
for dt in Fixed::<4,128>::zeroes2(n.dot(delta_pos).wrap_4(),n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){ for dt in Fixed::<4,128>::zeroes2(d,n.dot(body.velocity).wrap_4()*2,n.dot(body.acceleration).wrap_4()){
if low(&lower_bound,&dt)&&upp(&dt,&upper_bound)&&n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative(){ let low=low(&lower_bound,&dt);
let upp=upp(&dt,&upper_bound);
let into=n.dot(body.extrapolated_velocity_ratio_dt(dt)).is_negative();
println!("dt={} low={low} upp={upp} into={into}",dt.divide());
if low&&upp&&into{
upper_bound=Bound::Included(dt); upper_bound=Bound::Included(dt);
best_transition=Transition::Next(FEV::Face(edge_face_id),dt); best_transition=Transition::Next(FEV::Face(edge_face_id),dt);
break; break;
@ -175,8 +188,11 @@ impl<F:Copy,M:MeshQuery<Normal=Vector3<F>,Offset=Fixed<4,128>>> FEV<M>
pub fn crawl(mut self,mesh:&M,relative_body:&Body,lower_bound:Bound<&Time>,upper_bound:Bound<&Time>)->CrawlResult<M>{ pub fn crawl(mut self,mesh:&M,relative_body:&Body,lower_bound:Bound<&Time>,upper_bound:Bound<&Time>)->CrawlResult<M>{
let mut lower_bound=lower_bound.map(|&t|into_giga_time(t,relative_body.time)); let mut lower_bound=lower_bound.map(|&t|into_giga_time(t,relative_body.time));
let upper_bound=upper_bound.map(|&t|into_giga_time(t,relative_body.time)); let upper_bound=upper_bound.map(|&t|into_giga_time(t,relative_body.time));
println!("crawl begin={self:?}");
for _ in 0..20{ for _ in 0..20{
match self.next_transition(mesh,relative_body,lower_bound,upper_bound){ let transition=self.next_transition(mesh,relative_body,lower_bound,upper_bound);
println!("transition={transition:?}");
match transition{
Transition::Miss=>return CrawlResult::Miss(self), Transition::Miss=>return CrawlResult::Miss(self),
Transition::Next(next_fev,next_time)=>(self,lower_bound)=(next_fev,Bound::Included(next_time)), Transition::Next(next_fev,next_time)=>(self,lower_bound)=(next_fev,Bound::Included(next_time)),
Transition::Hit(face,time)=>return CrawlResult::Hit(face,time), Transition::Hit(face,time)=>return CrawlResult::Hit(face,time),

@ -76,9 +76,9 @@ struct Face{
#[derive(Debug)] #[derive(Debug)]
struct Vert(Planar64Vec3); struct Vert(Planar64Vec3);
pub trait MeshQuery{ pub trait MeshQuery{
type Face:Copy; type Face:Copy+std::fmt::Debug;
type Edge:Copy+DirectedEdge; type Edge:Copy+DirectedEdge+std::fmt::Debug;
type Vert:Copy; type Vert:Copy+std::fmt::Debug;
// Vertex must be Planar64Vec3 because it represents an actual position // Vertex must be Planar64Vec3 because it represents an actual position
type Normal; type Normal;
type Offset; type Offset;
@ -759,7 +759,9 @@ impl MinkowskiMesh<'_>{
}) })
} }
pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{ pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{
println!("@@@BEGIN SETUP@@@");
self.closest_fev_not_inside(*relative_body,range.start_bound()).and_then(|fev|{ self.closest_fev_not_inside(*relative_body,range.start_bound()).and_then(|fev|{
println!("@@@BEGIN REAL CRAWL@@@");
//continue forwards along the body parabola //continue forwards along the body parabola
fev.crawl(self,relative_body,range.start_bound(),range.end_bound()).hit() fev.crawl(self,relative_body,range.start_bound(),range.end_bound()).hit()
}) })

@ -28,6 +28,7 @@ pub enum InternalInstruction{
CollisionStart(Collision,model_physics::GigaTime), CollisionStart(Collision,model_physics::GigaTime),
CollisionEnd(Collision,model_physics::GigaTime), CollisionEnd(Collision,model_physics::GigaTime),
StrafeTick, StrafeTick,
// TODO: add GigaTime to ReachWalkTargetVelocity
ReachWalkTargetVelocity, ReachWalkTargetVelocity,
// Water, // Water,
} }
@ -1130,10 +1131,11 @@ impl PhysicsData{
//this is the one who asks //this is the one who asks
fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<InternalInstruction,Time>>{ fn next_instruction_internal(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option<TimedInstruction<InternalInstruction,Time>>{
println!("==== next_instruction_internal ====");
//JUST POLLING!!! NO MUTATION //JUST POLLING!!! NO MUTATION
let mut collector=instruction::InstructionCollector::new(time_limit); let mut collector=instruction::InstructionCollector::new(time_limit);
collector.collect(state.next_move_instruction()); // collector.collect(state.next_move_instruction());
//check for collision ends //check for collision ends
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time); state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time);
@ -1145,6 +1147,7 @@ impl PhysicsData{
//let relative_body=state.body.relative_to(&Body::ZERO); //let relative_body=state.body.relative_to(&Body::ZERO);
let relative_body=&state.body; let relative_body=&state.body;
data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{ data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{
println!("Sampling object id={:?}",convex_mesh_id.model_id);
//no checks are needed because of the time limits. //no checks are needed because of the time limits.
let model_mesh=data.models.mesh(convex_mesh_id); let model_mesh=data.models.mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,data.hitbox_mesh.transformed_mesh());
@ -1260,6 +1263,7 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM
let n=contact_normal(models,hitbox_mesh,contact); let n=contact_normal(models,hitbox_mesh,contact);
let r=n.dot(v).is_positive(); let r=n.dot(v).is_positive();
if r{ if r{
println!("culled {:?}",contact.model_id);
culled=true; culled=true;
} }
!r !r
@ -1627,6 +1631,7 @@ fn collision_end_contact(
_attr:&gameplay_attributes::ContactAttributes, _attr:&gameplay_attributes::ContactAttributes,
contact:ContactCollision, contact:ContactCollision,
){ ){
println!("collision_end {:?}",contact.model_id);
touching.remove(&Collision::Contact(contact));//remove contact before calling contact_constrain_acceleration touching.remove(&Collision::Contact(contact));//remove contact before calling contact_constrain_acceleration
//check ground //check ground
//TODO do better //TODO do better
@ -1674,18 +1679,16 @@ fn collision_end_intersect(
} }
} }
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,Time>){ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,Time>){
println!("\n==== atomic_internal_instruction ====");
state.time=ins.time; state.time=ins.time;
let (should_advance_body,goober_time)=match ins.instruction{ match ins.instruction{
// collisions advance the body precisely
InternalInstruction::CollisionStart(_,dt) InternalInstruction::CollisionStart(_,dt)
|InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)), |InternalInstruction::CollisionEnd(_,dt)=>state.body.advance_time_ratio_dt(dt),
InternalInstruction::StrafeTick // this advances imprecisely
|InternalInstruction::ReachWalkTargetVelocity=>(true,None), InternalInstruction::ReachWalkTargetVelocity=>state.body.advance_time(state.time),
}; // strafe tick decides for itself whether to advance the body.
if should_advance_body{ InternalInstruction::StrafeTick=>(),
match goober_time{
Some(dt)=>state.body.advance_time_ratio_dt(dt),
None=>state.body.advance_time(state.time),
}
} }
match ins.instruction{ match ins.instruction{
InternalInstruction::CollisionStart(collision,_)=>{ InternalInstruction::CollisionStart(collision,_)=>{
@ -1732,6 +1735,8 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
let masked_controls=strafe_settings.mask(controls); let masked_controls=strafe_settings.mask(controls);
let control_dir=state.style.get_control_dir(masked_controls); let control_dir=state.style.get_control_dir(masked_controls);
if control_dir!=vec3::ZERO{ if control_dir!=vec3::ZERO{
// manually advance time
state.body.advance_time(state.time);
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x); let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){ if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
//this is wrong but will work ig //this is wrong but will work ig

@ -15,7 +15,7 @@ glam = "0.30.0"
lazy-regex = "3.1.0" lazy-regex = "3.1.0"
rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" } rbx_binary = { version = "1.1.0-sn4", registry = "strafesnet" }
rbx_dom_weak = { version = "3.1.0-sn4", registry = "strafesnet", features = ["instance-userdata"] } rbx_dom_weak = { version = "3.1.0-sn4", registry = "strafesnet", features = ["instance-userdata"] }
rbx_mesh = "0.4.0" rbx_mesh = "0.3.1"
rbx_reflection = "5.0.0" rbx_reflection = "5.0.0"
rbx_reflection_database = "1.0.0" rbx_reflection_database = "1.0.0"
rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" } rbx_xml = { version = "1.1.0-sn4", registry = "strafesnet" }

@ -154,7 +154,7 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
}) as u32) }) as u32)
}; };
match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)?{ match rbx_mesh::read_versioned(roblox_mesh_bytes.cursor()).map_err(Error::RbxMesh)?{
rbx_mesh::mesh::Mesh::V1(mesh)=>{ rbx_mesh::mesh::VersionedMesh::Version1(mesh)=>{
let color_id=acquire_color_id([1.0f32;4]); let color_id=acquire_color_id([1.0f32;4]);
polygon_groups.push(PolygonGroup::PolygonList(PolygonList::new(mesh.vertices.chunks_exact(3).map(|trip|{ 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{ let mut ingest_vertex1=|vertex:&rbx_mesh::mesh::Vertex1|Ok(acquire_vertex_id(IndexedVertex{
@ -166,7 +166,7 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
Ok(vec![ingest_vertex1(&trip[0])?,ingest_vertex1(&trip[1])?,ingest_vertex1(&trip[2])?]) Ok(vec![ingest_vertex1(&trip[0])?,ingest_vertex1(&trip[1])?,ingest_vertex1(&trip[2])?])
}).collect::<Result<_,_>>()?))); }).collect::<Result<_,_>>()?)));
}, },
rbx_mesh::mesh::Mesh::V2(mesh)=>{ rbx_mesh::mesh::VersionedMesh::Version2(mesh)=>{
let vertex_id_map=match mesh.header.sizeof_vertex{ let vertex_id_map=match mesh.header.sizeof_vertex{
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{ rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
//pick white and make all the vertices white //pick white and make all the vertices white
@ -180,7 +180,7 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]] vec![vertex_id_map[&face.0],vertex_id_map[&face.1],vertex_id_map[&face.2]]
).collect()))); ).collect())));
}, },
rbx_mesh::mesh::Mesh::V3(mesh)=>{ rbx_mesh::mesh::VersionedMesh::Version3(mesh)=>{
let vertex_id_map=match mesh.header.sizeof_vertex{ let vertex_id_map=match mesh.header.sizeof_vertex{
rbx_mesh::mesh::SizeOfVertex2::Truncated=>{ rbx_mesh::mesh::SizeOfVertex2::Truncated=>{
let color_id=acquire_color_id([1.0f32;4]); let color_id=acquire_color_id([1.0f32;4]);
@ -190,13 +190,13 @@ pub fn convert(roblox_mesh_bytes:crate::data::RobloxMeshBytes)->Result<MeshWithS
}?; }?;
ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods); ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
}, },
rbx_mesh::mesh::Mesh::V4(mesh)=>{ rbx_mesh::mesh::VersionedMesh::Version4(mesh)=>{
let vertex_id_map=ingest_vertices2( 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 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); ingest_faces2_lods3(&mut polygon_groups,&vertex_id_map,&mesh.faces,&mesh.lods);
}, },
rbx_mesh::mesh::Mesh::V5(mesh)=>{ rbx_mesh::mesh::VersionedMesh::Version5(mesh)=>{
let vertex_id_map=ingest_vertices2( 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 mesh.vertices,&mut acquire_pos_id,&mut acquire_tex_id,&mut acquire_normal_id,&mut acquire_color_id,&mut acquire_vertex_id
)?; )?;

@ -81,8 +81,8 @@ pub fn convert(
).map_err(Error::RobloxMeshData)?; ).map_err(Error::RobloxMeshData)?;
let graphics_mesh=match mesh_data{ let graphics_mesh=match mesh_data{
rbx_mesh::mesh_data::MeshData::CSGK(_)=>return Err(Error::Block), rbx_mesh::mesh_data::MeshData::CSGK(_)=>return Err(Error::Block),
rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::V2(mesh_data2))=>mesh_data2.mesh, rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL2(mesh_data2))=>mesh_data2.mesh,
rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::V4(mesh_data4))=>mesh_data4.mesh, rbx_mesh::mesh_data::MeshData::CSGMDL(rbx_mesh::mesh_data::CSGMDL::CSGMDL4(mesh_data4))=>mesh_data4.mesh,
}; };
//autoscale to size, idk what roblox is doing with the graphics mesh size //autoscale to size, idk what roblox is doing with the graphics mesh size
let mut pos_min=glam::Vec3::MAX; let mut pos_min=glam::Vec3::MAX;
@ -153,13 +153,10 @@ pub fn convert(
// have not seen this format in practice // have not seen this format in practice
|rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::Block) |rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::Block)
=>return Err(Error::Block), =>return Err(Error::Block),
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::V3(meshes)) rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::Meshes(meshes))
|rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::V5(meshes))
=>meshes.meshes,
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::V6(meshes))
=>vec![meshes.mesh],
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::V7(meshes))
=>meshes.meshes, =>meshes.meshes,
rbx_mesh::physics_data::PhysicsData::CSGPHS(rbx_mesh::physics_data::CSGPHS::PhysicsInfoMesh(pim))
=>vec![pim.mesh],
}; };
let physics_convex_meshes_it=physics_convex_meshes.into_iter().map(|mesh|{ let physics_convex_meshes_it=physics_convex_meshes.into_iter().map(|mesh|{
// this can be factored out of the loop but I am lazy // this can be factored out of the loop but I am lazy

@ -6,6 +6,6 @@ edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
binrw = "0.15.0" binrw = "0.14.0"
id = { version = "0.1.0", registry = "strafesnet" } id = { version = "0.1.0", registry = "strafesnet" }
strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" } strafesnet_common = { version = "0.7.0", path = "../common", registry = "strafesnet" }