megatype-ify

This commit is contained in:
Quaternions 2024-02-06 20:29:36 -08:00
parent 5b38b2ca33
commit 50eeeb003a
2 changed files with 269 additions and 209 deletions

View File

@ -17,26 +17,31 @@ pub trait DirectedEdge{
} }
} }
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct VertId(u32); pub struct MeshVertId(u32);
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct EdgeId(u32); pub struct MeshFaceId(u32);
/// DirectedEdgeId refers to an EdgeId when undirected.
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct DirectedEdgeId(u32);
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub struct FaceId(u32);
impl UndirectedEdge for EdgeId{ #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
type DirectedEdge=DirectedEdgeId; pub struct SubmeshVertId(u32);
fn as_directed(&self,parity:bool)->DirectedEdgeId{ #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
DirectedEdgeId(self.0|((parity as u32)<<(u32::BITS-1))) pub struct SubmeshEdgeId(u32);
/// DirectedEdgeId refers to an EdgeId when undirected.
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct SubmeshDirectedEdgeId(u32);
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct SubmeshFaceId(u32);
impl UndirectedEdge for SubmeshEdgeId{
type DirectedEdge=SubmeshDirectedEdgeId;
fn as_directed(&self,parity:bool)->SubmeshDirectedEdgeId{
SubmeshDirectedEdgeId(self.0|((parity as u32)<<(u32::BITS-1)))
} }
} }
impl DirectedEdge for DirectedEdgeId{ impl DirectedEdge for SubmeshDirectedEdgeId{
type UndirectedEdge=EdgeId; type UndirectedEdge=SubmeshEdgeId;
fn as_undirected(&self)->EdgeId{ fn as_undirected(&self)->SubmeshEdgeId{
EdgeId(self.0&!(1<<(u32::BITS-1))) SubmeshEdgeId(self.0&!(1<<(u32::BITS-1)))
} }
fn parity(&self)->bool{ fn parity(&self)->bool{
self.0&(1<<(u32::BITS-1))!=0 self.0&(1<<(u32::BITS-1))!=0
@ -74,16 +79,16 @@ pub trait MeshQuery<FACE:Clone,EDGE:Clone+DirectedEdge,VERT:Clone>{
fn vert_faces(&self,vert_id:VERT)->Cow<Vec<FACE>>; fn vert_faces(&self,vert_id:VERT)->Cow<Vec<FACE>>;
} }
struct FaceRefs{ struct FaceRefs{
edges:Vec<DirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
//verts:Vec<VertId>, //verts:Vec<VertId>,
} }
struct EdgeRefs{ struct EdgeRefs{
faces:[FaceId;2],//left, right faces:[SubmeshFaceId;2],//left, right
verts:[VertId;2],//bottom, top verts:[SubmeshVertId;2],//bottom, top
} }
struct VertRefs{ struct VertRefs{
faces:Vec<FaceId>, faces:Vec<SubmeshFaceId>,
edges:Vec<DirectedEdgeId>, edges:Vec<SubmeshDirectedEdgeId>,
} }
struct PhysicsMeshData{ struct PhysicsMeshData{
//this contains all real and virtual faces used in both the complete mesh and convex submeshes //this contains all real and virtual faces used in both the complete mesh and convex submeshes
@ -91,20 +96,24 @@ struct PhysicsMeshData{
//all remaining faces are virtual to operate internal logic of the face crawler //all remaining faces are virtual to operate internal logic of the face crawler
//and cannot be part of a physics collision //and cannot be part of a physics collision
//virtual faces are only used in convex submeshes. //virtual faces are only used in convex submeshes.
faces:Vec<Face>, faces:Vec<Face>,//MeshFaceId indexes this list
verts:Vec<Vert>, verts:Vec<Vert>,//MeshVertId indexes this list
} }
struct PhysicsMeshTopology{ struct PhysicsMeshTopology{
//mapping of local ids to PhysicsMeshData ids //mapping of local ids to PhysicsMeshData ids
faces:Vec<FaceId>, faces:Vec<MeshFaceId>,//SubmeshFaceId indexes this list
verts:Vec<VertId>, verts:Vec<MeshVertId>,//SubmeshVertId indexes this list
//all ids here are local to this object //all ids here are local to this object
face_topology:Vec<FaceRefs>, face_topology:Vec<FaceRefs>,
edge_topology:Vec<EdgeRefs>, edge_topology:Vec<EdgeRefs>,
vert_topology:Vec<VertRefs>, vert_topology:Vec<VertRefs>,
} }
#[derive(id::Id)]
pub struct PhysicsMeshId(u32);
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
pub struct PhysicsSubmeshId(u32);
pub struct PhysicsMesh{ pub struct PhysicsMesh{
mesh_data:PhysicsMeshData, data:PhysicsMeshData,
//index 0 is the complete mesh. //index 0 is the complete mesh.
//index 1-2+ is convex submeshes. //index 1-2+ is convex submeshes.
//Most objects in roblox maps are already convex, so the list length is 1 //Most objects in roblox maps are already convex, so the list length is 1
@ -115,7 +124,7 @@ pub struct PhysicsMesh{
impl PhysicsMesh{ impl PhysicsMesh{
pub fn unit_cube()->Self{ pub fn unit_cube()->Self{
//go go gadget debug print mesh //go go gadget debug print mesh
let mesh_data=PhysicsMeshData{ let data=PhysicsMeshData{
faces:vec![ faces:vec![
Face{normal:Planar64Vec3::raw( 4294967296, 0, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 4294967296, 0, 0),dot:Planar64::raw(4294967296)},
Face{normal:Planar64Vec3::raw( 0, 4294967296, 0),dot:Planar64::raw(4294967296)}, Face{normal:Planar64Vec3::raw( 0, 4294967296, 0),dot:Planar64::raw(4294967296)},
@ -136,43 +145,43 @@ impl PhysicsMesh{
] ]
}; };
let mesh_topology=PhysicsMeshTopology{ let mesh_topology=PhysicsMeshTopology{
faces:(0..mesh_data.faces.len()).map(FaceId).collect(), faces:(0..data.faces.len() as u32).map(MeshFaceId::new).collect(),
verts:(0..mesh_data.verts.len()).map(VertId).collect(), verts:(0..data.verts.len() as u32).map(MeshVertId::new).collect(),
face_topology:vec![ face_topology:vec![
FaceRefs{edges:vec![DirectedEdgeId(9223372036854775808),DirectedEdgeId(9223372036854775809),DirectedEdgeId(9223372036854775810),DirectedEdgeId(3)]}, FaceRefs{edges:vec![SubmeshDirectedEdgeId(9223372036854775808),SubmeshDirectedEdgeId(9223372036854775809),SubmeshDirectedEdgeId(9223372036854775810),SubmeshDirectedEdgeId(3)]},
FaceRefs{edges:vec![DirectedEdgeId(9223372036854775812),DirectedEdgeId(9223372036854775813),DirectedEdgeId(6),DirectedEdgeId(1)]}, FaceRefs{edges:vec![SubmeshDirectedEdgeId(9223372036854775812),SubmeshDirectedEdgeId(9223372036854775813),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId(1)]},
FaceRefs{edges:vec![DirectedEdgeId(7),DirectedEdgeId(2),DirectedEdgeId(9223372036854775814),DirectedEdgeId(9223372036854775816)]}, FaceRefs{edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId(9223372036854775814),SubmeshDirectedEdgeId(9223372036854775816)]},
FaceRefs{edges:vec![DirectedEdgeId(8),DirectedEdgeId(5),DirectedEdgeId(9223372036854775817),DirectedEdgeId(10)]}, FaceRefs{edges:vec![SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId(9223372036854775817),SubmeshDirectedEdgeId(10)]},
FaceRefs{edges:vec![DirectedEdgeId(9223372036854775815),DirectedEdgeId(9223372036854775818),DirectedEdgeId(11),DirectedEdgeId(9223372036854775811)]}, FaceRefs{edges:vec![SubmeshDirectedEdgeId(9223372036854775815),SubmeshDirectedEdgeId(9223372036854775818),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId(9223372036854775811)]},
FaceRefs{edges:vec![DirectedEdgeId(4),DirectedEdgeId(0),DirectedEdgeId(9223372036854775819),DirectedEdgeId(9)]} FaceRefs{edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId(9223372036854775819),SubmeshDirectedEdgeId(9)]}
], ],
edge_topology:vec![ edge_topology:vec![
EdgeRefs{faces:[FaceId(0),FaceId(5)],verts:[VertId(0),VertId(1)]}, EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(5)],verts:[SubmeshVertId(0),SubmeshVertId(1)]},
EdgeRefs{faces:[FaceId(0),FaceId(1)],verts:[VertId(1),VertId(2)]}, EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(1)],verts:[SubmeshVertId(1),SubmeshVertId(2)]},
EdgeRefs{faces:[FaceId(0),FaceId(2)],verts:[VertId(2),VertId(3)]}, EdgeRefs{faces:[SubmeshFaceId(0),SubmeshFaceId(2)],verts:[SubmeshVertId(2),SubmeshVertId(3)]},
EdgeRefs{faces:[FaceId(4),FaceId(0)],verts:[VertId(0),VertId(3)]}, EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(0)],verts:[SubmeshVertId(0),SubmeshVertId(3)]},
EdgeRefs{faces:[FaceId(1),FaceId(5)],verts:[VertId(1),VertId(4)]}, EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(5)],verts:[SubmeshVertId(1),SubmeshVertId(4)]},
EdgeRefs{faces:[FaceId(1),FaceId(3)],verts:[VertId(4),VertId(5)]}, EdgeRefs{faces:[SubmeshFaceId(1),SubmeshFaceId(3)],verts:[SubmeshVertId(4),SubmeshVertId(5)]},
EdgeRefs{faces:[FaceId(2),FaceId(1)],verts:[VertId(2),VertId(5)]}, EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(1)],verts:[SubmeshVertId(2),SubmeshVertId(5)]},
EdgeRefs{faces:[FaceId(4),FaceId(2)],verts:[VertId(3),VertId(6)]}, EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(2)],verts:[SubmeshVertId(3),SubmeshVertId(6)]},
EdgeRefs{faces:[FaceId(2),FaceId(3)],verts:[VertId(5),VertId(6)]}, EdgeRefs{faces:[SubmeshFaceId(2),SubmeshFaceId(3)],verts:[SubmeshVertId(5),SubmeshVertId(6)]},
EdgeRefs{faces:[FaceId(3),FaceId(5)],verts:[VertId(4),VertId(7)]}, EdgeRefs{faces:[SubmeshFaceId(3),SubmeshFaceId(5)],verts:[SubmeshVertId(4),SubmeshVertId(7)]},
EdgeRefs{faces:[FaceId(4),FaceId(3)],verts:[VertId(6),VertId(7)]}, EdgeRefs{faces:[SubmeshFaceId(4),SubmeshFaceId(3)],verts:[SubmeshVertId(6),SubmeshVertId(7)]},
EdgeRefs{faces:[FaceId(5),FaceId(4)],verts:[VertId(0),VertId(7)]} EdgeRefs{faces:[SubmeshFaceId(5),SubmeshFaceId(4)],verts:[SubmeshVertId(0),SubmeshVertId(7)]}
], ],
vert_topology:vec![ vert_topology:vec![
VertRefs{faces:vec![FaceId(0),FaceId(4),FaceId(5)],edges:vec![DirectedEdgeId(9223372036854775811),DirectedEdgeId(9223372036854775819),DirectedEdgeId(9223372036854775808)]}, VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(4),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId(9223372036854775811),SubmeshDirectedEdgeId(9223372036854775819),SubmeshDirectedEdgeId(9223372036854775808)]},
VertRefs{faces:vec![FaceId(0),FaceId(5),FaceId(1)],edges:vec![DirectedEdgeId(9223372036854775812),DirectedEdgeId(0),DirectedEdgeId(9223372036854775809)]}, VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(9223372036854775812),SubmeshDirectedEdgeId(0),SubmeshDirectedEdgeId(9223372036854775809)]},
VertRefs{faces:vec![FaceId(0),FaceId(2),FaceId(1)],edges:vec![DirectedEdgeId(1),DirectedEdgeId(9223372036854775810),DirectedEdgeId(9223372036854775814)]}, VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(1),SubmeshDirectedEdgeId(9223372036854775810),SubmeshDirectedEdgeId(9223372036854775814)]},
VertRefs{faces:vec![FaceId(0),FaceId(2),FaceId(4)],edges:vec![DirectedEdgeId(2),DirectedEdgeId(3),DirectedEdgeId(9223372036854775815)]}, VertRefs{faces:vec![SubmeshFaceId(0),SubmeshFaceId(2),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(2),SubmeshDirectedEdgeId(3),SubmeshDirectedEdgeId(9223372036854775815)]},
VertRefs{faces:vec![FaceId(3),FaceId(5),FaceId(1)],edges:vec![DirectedEdgeId(4),DirectedEdgeId(9223372036854775817),DirectedEdgeId(9223372036854775813)]}, VertRefs{faces:vec![SubmeshFaceId(3),SubmeshFaceId(5),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(4),SubmeshDirectedEdgeId(9223372036854775817),SubmeshDirectedEdgeId(9223372036854775813)]},
VertRefs{faces:vec![FaceId(2),FaceId(3),FaceId(1)],edges:vec![DirectedEdgeId(5),DirectedEdgeId(6),DirectedEdgeId(9223372036854775816)]}, VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(1)],edges:vec![SubmeshDirectedEdgeId(5),SubmeshDirectedEdgeId(6),SubmeshDirectedEdgeId(9223372036854775816)]},
VertRefs{faces:vec![FaceId(2),FaceId(3),FaceId(4)],edges:vec![DirectedEdgeId(7),DirectedEdgeId(8),DirectedEdgeId(9223372036854775818)]}, VertRefs{faces:vec![SubmeshFaceId(2),SubmeshFaceId(3),SubmeshFaceId(4)],edges:vec![SubmeshDirectedEdgeId(7),SubmeshDirectedEdgeId(8),SubmeshDirectedEdgeId(9223372036854775818)]},
VertRefs{faces:vec![FaceId(4),FaceId(3),FaceId(5)],edges:vec![DirectedEdgeId(10),DirectedEdgeId(11),DirectedEdgeId(9)]} VertRefs{faces:vec![SubmeshFaceId(4),SubmeshFaceId(3),SubmeshFaceId(5)],edges:vec![SubmeshDirectedEdgeId(10),SubmeshDirectedEdgeId(11),SubmeshDirectedEdgeId(9)]}
] ]
}; };
Self{ Self{
mesh_data, data,
submeshes:vec![mesh_topology], submeshes:vec![mesh_topology],
} }
} }
@ -180,12 +189,12 @@ impl PhysicsMesh{
Self::unit_cube() Self::unit_cube()
} }
pub fn mesh_data(&self)->&PhysicsMeshData{ pub fn mesh_data(&self)->&PhysicsMeshData{
&self.mesh_data &self.data
} }
pub fn complete_mesh(&self)->&PhysicsMeshTopology{ pub fn complete_mesh(&self)->&PhysicsMeshTopology{
&self.submeshes[0] &self.submeshes[0]
} }
pub fn convex_submeshes(&self)->&[PhysicsMeshTopology]{ pub fn submeshes(&self)->&[PhysicsMeshTopology]{
if self.submeshes.len()==1{ if self.submeshes.len()==1{
//the complete mesh is already a convex mesh //the complete mesh is already a convex mesh
&self.submeshes[0..0] &self.submeshes[0..0]
@ -193,18 +202,30 @@ impl PhysicsMesh{
&self.submeshes[1..] &self.submeshes[1..]
} }
} }
pub fn submesh_view(&self,submesh_id:PhysicsSubmeshId)->PhysicsMeshView{
PhysicsMeshView{
data:&self.data,
topology:&self.submeshes()[submesh_id.get() as usize],
}
}
pub fn submesh_views(&self)->impl Iterator<Item=PhysicsMeshView>{
self.submeshes().iter().map(|topology|PhysicsMeshView{
data:&self.data,
topology,
})
}
} }
//mesh builder code //mesh builder code
#[derive(Default,Clone)] #[derive(Default,Clone)]
struct VertRefGuy{ struct VertRefGuy{
edges:std::collections::HashSet<DirectedEdgeId>, edges:std::collections::HashSet<SubmeshDirectedEdgeId>,
faces:std::collections::HashSet<FaceId>, faces:std::collections::HashSet<SubmeshFaceId>,
} }
#[derive(Clone,Hash,Eq,PartialEq)] #[derive(Clone,Hash,Eq,PartialEq)]
struct EdgeRefVerts([VertId;2]); struct EdgeRefVerts([SubmeshVertId;2]);
impl EdgeRefVerts{ impl EdgeRefVerts{
fn new(v0:VertId,v1:VertId)->(Self,bool){ fn new(v0:SubmeshVertId,v1:SubmeshVertId)->(Self,bool){
(if v0.0<v1.0{ (if v0.0<v1.0{
Self([v0,v1]) Self([v0,v1])
}else{ }else{
@ -212,23 +233,23 @@ impl EdgeRefVerts{
},v0.0<v1.0) },v0.0<v1.0)
} }
} }
struct EdgeRefFaces([FaceId;2]); struct EdgeRefFaces([SubmeshFaceId;2]);
impl EdgeRefFaces{ impl EdgeRefFaces{
fn new()->Self{ fn new()->Self{
Self([FaceId(0);2]) Self([SubmeshFaceId(0);2])
} }
fn push(&mut self,i:usize,face_id:FaceId){ fn push(&mut self,i:usize,face_id:SubmeshFaceId){
self.0[i]=face_id; self.0[i]=face_id;
} }
} }
struct FaceRefEdges(Vec<DirectedEdgeId>); struct FaceRefEdges(Vec<SubmeshDirectedEdgeId>);
#[derive(Default)] #[derive(Default)]
struct EdgePool{ struct EdgePool{
edge_guys:Vec<(EdgeRefVerts,EdgeRefFaces)>, edge_guys:Vec<(EdgeRefVerts,EdgeRefFaces)>,
edge_id_from_guy:std::collections::HashMap<EdgeRefVerts,usize>, edge_id_from_guy:std::collections::HashMap<EdgeRefVerts,usize>,
} }
impl EdgePool{ impl EdgePool{
fn push(&mut self,edge_ref_verts:EdgeRefVerts)->(&mut EdgeRefFaces,EdgeId){ fn push(&mut self,edge_ref_verts:EdgeRefVerts)->(&mut EdgeRefFaces,SubmeshEdgeId){
let edge_id=if let Some(&edge_id)=self.edge_id_from_guy.get(&edge_ref_verts){ let edge_id=if let Some(&edge_id)=self.edge_id_from_guy.get(&edge_ref_verts){
edge_id edge_id
}else{ }else{
@ -237,7 +258,7 @@ impl EdgePool{
self.edge_id_from_guy.insert(edge_ref_verts,edge_id); self.edge_id_from_guy.insert(edge_ref_verts,edge_id);
edge_id edge_id
}; };
(&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id)}.1,EdgeId(edge_id)) (&mut unsafe{self.edge_guys.get_unchecked_mut(edge_id)}.1,SubmeshEdgeId::new(edge_id as u32))
} }
} }
impl From<&model::IndexedModel> for PhysicsMesh{ impl From<&model::IndexedModel> for PhysicsMesh{
@ -250,7 +271,7 @@ impl From<&model::IndexedModel> for PhysicsMesh{
let mut faces=Vec::new(); let mut faces=Vec::new();
let mut face_ref_guys=Vec::new(); let mut face_ref_guys=Vec::new();
for group in &indexed_model.polygon_groups{for poly_vertices in group.polys(){ for group in &indexed_model.polygon_groups{for poly_vertices in group.polys(){
let face_id=FaceId(face_i); let face_id=SubmeshFaceId::new(face_i);
//one face per poly //one face per poly
let mut normal=Planar64Vec3::ZERO; let mut normal=Planar64Vec3::ZERO;
let len=poly_vertices.len(); let len=poly_vertices.len();
@ -266,7 +287,7 @@ impl From<&model::IndexedModel> for PhysicsMesh{
(v0.x()-v1.x())*(v0.y()+v1.y()), (v0.x()-v1.x())*(v0.y()+v1.y()),
); );
//get/create edge and push face into it //get/create edge and push face into it
let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(VertId(vert0_id),VertId(vert1_id)); let (edge_ref_verts,is_sorted)=EdgeRefVerts::new(SubmeshVertId::new(vert0_id as u32),SubmeshVertId::new(vert1_id as u32));
let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts); let (edge_ref_faces,edge_id)=edge_pool.push(edge_ref_verts);
//polygon vertices as assumed to be listed clockwise //polygon vertices as assumed to be listed clockwise
//populate the edge face on the left or right depending on how the edge vertices got sorted //populate the edge face on the left or right depending on how the edge vertices got sorted
@ -311,108 +332,129 @@ impl From<&model::IndexedModel> for PhysicsMesh{
} }
} }
struct PhysicsMeshView<'a>{
data:&'a PhysicsMeshData,
topology:&'a PhysicsMeshTopology,
}
impl PhysicsMeshView<'_>{ impl PhysicsMeshView<'_>{
pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{ pub fn verts<'a>(&'a self)->impl Iterator<Item=Planar64Vec3>+'a{
self.mesh_data.verts.iter().map(|Vert(pos)|*pos) self.data.verts.iter().map(|Vert(pos)|*pos)
} }
} }
impl MeshQuery<FaceId,DirectedEdgeId,VertId> for PhysicsMesh{ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for PhysicsMeshView<'_>{
fn face_nd(&self,face_id:FaceId)->(Planar64Vec3,Planar64){ fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
(self.faces[face_id.0].normal,self.faces[face_id.0].dot) let face_idx=self.topology.faces[face_id.get() as usize].get() as usize;
(self.data.faces[face_idx].normal,self.data.faces[face_idx].dot)
} }
//ideally I never calculate the vertex position, but I have to for the graphical meshes... //ideally I never calculate the vertex position, but I have to for the graphical meshes...
fn vert(&self,vert_id:VertId)->Planar64Vec3{ fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
self.verts[vert_id.0].0 let vert_idx=self.topology.verts[vert_id.get() as usize].get() as usize;
self.data.verts[vert_idx].0
} }
fn face_edges(&self,face_id:FaceId)->Cow<Vec<DirectedEdgeId>>{ fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
Cow::Borrowed(&self.face_topology[face_id.0].edges) Cow::Borrowed(&self.topology.face_topology[face_id.get() as usize].edges)
} }
fn edge_faces(&self,edge_id:EdgeId)->Cow<[FaceId;2]>{ fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{
Cow::Borrowed(&self.edge_topology[edge_id.0].faces) Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].faces)
} }
fn edge_verts(&self,edge_id:EdgeId)->Cow<[VertId;2]>{ fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{
Cow::Borrowed(&self.edge_topology[edge_id.0].verts) Cow::Borrowed(&self.topology.edge_topology[edge_id.get() as usize].verts)
} }
fn vert_edges(&self,vert_id:VertId)->Cow<Vec<DirectedEdgeId>>{ fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshDirectedEdgeId>>{
Cow::Borrowed(&self.vert_topology[vert_id.0].edges) Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].edges)
} }
fn vert_faces(&self,vert_id:VertId)->Cow<Vec<FaceId>>{ fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshFaceId>>{
Cow::Borrowed(&self.vert_topology[vert_id.0].faces) Cow::Borrowed(&self.topology.vert_topology[vert_id.get() as usize].faces)
} }
} }
struct PhysicsMeshView<'a>{ pub struct PhysicsMeshTransform<'a>{
mesh_data:&'a PhysicsMeshData, vertex:&'a integer::Planar64Affine3,
topology:&'a PhysicsMeshTopology, normal:&'a integer::Planar64Mat3,
det:Planar64,
}
impl PhysicsMeshTransform<'_>{
pub fn new<'a>(
vertex:&'a integer::Planar64Affine3,
normal:&'a integer::Planar64Mat3,
det:Planar64
)->PhysicsMeshTransform<'a>{
PhysicsMeshTransform{
vertex,
normal,
det,
}
} }
struct PhysicsMeshTransform<'a>{
transform:&'a integer::Planar64Affine3,
normal_transform:&'a integer::Planar64Mat3,
transform_det:Planar64,
} }
pub struct TransformedMesh<'a>{ pub struct TransformedMesh<'a>{
mesh:PhysicsMeshView<'a>, view:PhysicsMeshView<'a>,
transform:PhysicsMeshTransform<'a>, transform:PhysicsMeshTransform<'a>,
} }
impl TransformedMesh<'_>{ impl TransformedMesh<'_>{
pub fn new<'a>( pub fn new<'a>(
mesh_data:&'a PhysicsMeshData, mesh_data:&'a PhysicsMeshData,
topology:&'a PhysicsMeshTopology, topology:&'a PhysicsMeshTopology,
transform:&'a integer::Planar64Affine3, vertex:&'a integer::Planar64Affine3,
normal_transform:&'a integer::Planar64Mat3, normal:&'a integer::Planar64Mat3,
transform_det:Planar64, det:Planar64,
)->TransformedMesh<'a>{ )->TransformedMesh<'a>{
TransformedMesh{ TransformedMesh{
mesh_data, view:PhysicsMeshView{
data: mesh_data,
topology, topology,
transform, },
normal_transform, transform:PhysicsMeshTransform{
transform_det, vertex,
normal,
det,
} }
} }
fn farthest_vert(&self,dir:Planar64Vec3)->VertId{ }
fn farthest_vert(&self,dir:Planar64Vec3)->SubmeshVertId{
let mut best_dot=Planar64::MIN; let mut best_dot=Planar64::MIN;
let mut best_vert=VertId(0); let mut best_vert=SubmeshVertId(0);
for (i,vert) in self.mesh.verts.iter().enumerate(){ //this happens to be well-defined. there are no virtual virtices
let p=self.transform.transform_point3(vert.0); for (i,vert_id) in self.view.topology.verts.iter().enumerate(){
let vert=self.view.data.verts[vert_id.get() as usize];
let p=self.transform.vertex.transform_point3(vert.0);
let d=dir.dot(p); let d=dir.dot(p);
if best_dot<d{ if best_dot<d{
best_dot=d; best_dot=d;
best_vert=VertId(i); best_vert=SubmeshVertId::new(i as u32);
} }
} }
best_vert best_vert
} }
} }
impl MeshQuery<FaceId,DirectedEdgeId,VertId> for TransformedMesh<'_>{ impl MeshQuery<SubmeshFaceId,SubmeshDirectedEdgeId,SubmeshVertId> for TransformedMesh<'_>{
fn face_nd(&self,face_id:FaceId)->(Planar64Vec3,Planar64){ fn face_nd(&self,face_id:SubmeshFaceId)->(Planar64Vec3,Planar64){
let (n,d)=self.mesh.face_nd(face_id); let (n,d)=self.view.face_nd(face_id);
let transformed_n=*self.normal_transform*n; let transformed_n=*self.transform.normal*n;
let transformed_d=d+transformed_n.dot(self.transform.translation)/self.transform_det; let transformed_d=d+transformed_n.dot(self.transform.vertex.translation)/self.transform.det;
(transformed_n/self.transform_det,transformed_d) (transformed_n/self.transform.det,transformed_d)
} }
fn vert(&self,vert_id:VertId)->Planar64Vec3{ fn vert(&self,vert_id:SubmeshVertId)->Planar64Vec3{
self.transform.transform_point3(self.mesh.vert(vert_id)) self.transform.vertex.transform_point3(self.view.vert(vert_id))
} }
#[inline] #[inline]
fn face_edges(&self,face_id:FaceId)->Cow<Vec<DirectedEdgeId>>{ fn face_edges(&self,face_id:SubmeshFaceId)->Cow<Vec<SubmeshDirectedEdgeId>>{
self.mesh.face_edges(face_id) self.view.face_edges(face_id)
} }
#[inline] #[inline]
fn edge_faces(&self,edge_id:EdgeId)->Cow<[FaceId;2]>{ fn edge_faces(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshFaceId;2]>{
self.mesh.edge_faces(edge_id) self.view.edge_faces(edge_id)
} }
#[inline] #[inline]
fn edge_verts(&self,edge_id:EdgeId)->Cow<[VertId;2]>{ fn edge_verts(&self,edge_id:SubmeshEdgeId)->Cow<[SubmeshVertId;2]>{
self.mesh.edge_verts(edge_id) self.view.edge_verts(edge_id)
} }
#[inline] #[inline]
fn vert_edges(&self,vert_id:VertId)->Cow<Vec<DirectedEdgeId>>{ fn vert_edges(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshDirectedEdgeId>>{
self.mesh.vert_edges(vert_id) self.view.vert_edges(vert_id)
} }
#[inline] #[inline]
fn vert_faces(&self,vert_id:VertId)->Cow<Vec<FaceId>>{ fn vert_faces(&self,vert_id:SubmeshVertId)->Cow<Vec<SubmeshFaceId>>{
self.mesh.vert_faces(vert_id) self.view.vert_faces(vert_id)
} }
} }
@ -422,12 +464,12 @@ impl MeshQuery<FaceId,DirectedEdgeId,VertId> for TransformedMesh<'_>{
//(vertex,face) //(vertex,face)
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
pub enum MinkowskiVert{ pub enum MinkowskiVert{
VertVert(VertId,VertId), VertVert(SubmeshVertId,SubmeshVertId),
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
pub enum MinkowskiEdge{ pub enum MinkowskiEdge{
VertEdge(VertId,EdgeId), VertEdge(SubmeshVertId,SubmeshEdgeId),
EdgeVert(EdgeId,VertId), EdgeVert(SubmeshEdgeId,SubmeshVertId),
//EdgeEdge when edges are parallel //EdgeEdge when edges are parallel
} }
impl UndirectedEdge for MinkowskiEdge{ impl UndirectedEdge for MinkowskiEdge{
@ -441,8 +483,8 @@ impl UndirectedEdge for MinkowskiEdge{
} }
#[derive(Clone,Copy)] #[derive(Clone,Copy)]
pub enum MinkowskiDirectedEdge{ pub enum MinkowskiDirectedEdge{
VertEdge(VertId,DirectedEdgeId), VertEdge(SubmeshVertId,SubmeshDirectedEdgeId),
EdgeVert(DirectedEdgeId,VertId), EdgeVert(SubmeshDirectedEdgeId,SubmeshVertId),
//EdgeEdge when edges are parallel //EdgeEdge when edges are parallel
} }
impl DirectedEdge for MinkowskiDirectedEdge{ impl DirectedEdge for MinkowskiDirectedEdge{
@ -462,9 +504,9 @@ impl DirectedEdge for MinkowskiDirectedEdge{
} }
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
pub enum MinkowskiFace{ pub enum MinkowskiFace{
VertFace(VertId,FaceId), VertFace(SubmeshVertId,SubmeshFaceId),
EdgeEdge(EdgeId,EdgeId,bool), EdgeEdge(SubmeshEdgeId,SubmeshEdgeId,bool),
FaceVert(FaceId,VertId), FaceVert(SubmeshFaceId,SubmeshVertId),
//EdgeFace //EdgeFace
//FaceEdge //FaceEdge
//FaceFace //FaceFace

View File

@ -1,7 +1,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use crate::model_physics::{self,PhysicsMesh,TransformedMesh,MeshQuery}; use crate::model_physics::{self,PhysicsMesh,TransformedMesh,MeshQuery,PhysicsMeshId,PhysicsSubmeshId};
use strafesnet_common::bvh; use strafesnet_common::bvh;
use strafesnet_common::map; use strafesnet_common::map;
use strafesnet_common::aabb; use strafesnet_common::aabb;
@ -169,11 +169,10 @@ impl PhysicsModels{
} }
//TODO: "statically" verify the maps don't refer to any nonexistant data, if they do delete the references. //TODO: "statically" verify the maps don't refer to any nonexistant data, if they do delete the references.
//then I can make these getter functions unchecked. //then I can make these getter functions unchecked.
fn mesh(&self,model_id:PhysicsModelId)->TransformedMesh{ fn mesh(&self,convex_mesh_id:ConvexMeshId)->TransformedMesh{
let idx=model_id.get() as usize; let model_idx=convex_mesh_id.model_id.get() as usize;
let convex_mesh_id=self.models[idx].convex_mesh_id;
TransformedMesh::new( TransformedMesh::new(
&self.meshes[convex_mesh_id.mesh_id.get() as usize].groups[convex_mesh_id.group_id.get() as usize], &self.meshes[model_idx].submesh_view(convex_mesh_id.submesh_id),
&self.models[idx].transform, &self.models[idx].transform,
&self.models[idx].normal_transform, &self.models[idx].normal_transform,
self.models[idx].transform_det, self.models[idx].transform_det,
@ -285,23 +284,19 @@ struct WorldState{}
struct HitboxMesh{ struct HitboxMesh{
halfsize:Planar64Vec3, halfsize:Planar64Vec3,
mesh:PhysicsMesh, mesh:PhysicsMesh,
transform:integer::Planar64Affine3, transform:PhysicsModelTransform,
normal_transform:Planar64Mat3,
transform_det:Planar64,
} }
impl HitboxMesh{ impl HitboxMesh{
fn new(mesh:PhysicsMesh,transform:integer::Planar64Affine3)->Self{ fn new(mesh:PhysicsMesh,vertex_transform:integer::Planar64Affine3)->Self{
//calculate extents //calculate extents
let mut aabb=aabb::Aabb::default(); let mut aabb=aabb::Aabb::default();
for vert in mesh.verts(){ for vert in mesh.complete_mesh_view().verts(){
aabb.grow(transform.transform_point3(vert)); aabb.grow(vertex_transform.transform_point3(vert));
} }
Self{ Self{
halfsize:aabb.size()/2, halfsize:aabb.size()/2,
mesh, mesh,
transform, transform:PhysicsModelTransform::new(vertex_transform)
normal_transform:transform.matrix3.inverse_times_det().transpose(),
transform_det:transform.matrix3.determinant(),
} }
} }
#[inline] #[inline]
@ -482,15 +477,11 @@ impl TryFrom<&gameplay_attributes::CollisionAttributes> for PhysicsCollisionAttr
#[derive(id::Id)] #[derive(id::Id)]
struct PhysicsAttributesId(u32); struct PhysicsAttributesId(u32);
//id assigned to deindexed IndexedPhysicsGroup
#[derive(id::Id)]
struct PhysicsMeshId(u32);
#[derive(id::Id)]
struct PhysicsGroupId(u32);
//unique physics meshes indexed by this //unique physics meshes indexed by this
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
struct ConvexMeshId{ struct ConvexMeshId{
model_id:PhysicsModelId,// 1:1 with IndexedModelId model_id:PhysicsModelId,
group_id:PhysicsGroupId,// group in model submesh_id:PhysicsSubmeshId,
} }
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)] #[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
struct PhysicsModelId(u32); struct PhysicsModelId(u32);
@ -504,37 +495,50 @@ impl From<ModelId> for PhysicsModelId{
Self::new(value.get()) Self::new(value.get())
} }
} }
pub struct PhysicsModelTransform{
vertex:integer::Planar64Affine3,
normal:integer::Planar64Mat3,
det:Planar64,
}
impl PhysicsModelTransform{
pub const fn new(vertex_transform:integer::Planar64Affine3)->Self{
Self{
normal:vertex_transform.matrix3.inverse_times_det().transpose(),
det:vertex_transform.matrix3.determinant(),
vertex:vertex_transform,
}
}
}
pub struct PhysicsModel{ pub struct PhysicsModel{
//A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es //A model is a thing that has a hitbox. can be represented by a list of TreyMesh-es
//in this iteration, all it needs is extents. //in this iteration, all it needs is extents.
mesh_id:PhysicsMeshId, mesh_id:PhysicsMeshId,
//put these up on the Model (data normalization) //put these up on the Model (data normalization)
attr_id:PhysicsAttributesId, attr_id:PhysicsAttributesId,
transform:integer::Planar64Affine3, transform:PhysicsModelTransform,
normal_transform:integer::Planar64Mat3,
transform_det:Planar64,
} }
impl PhysicsModel{ impl PhysicsModel{
pub fn new(mesh_id:PhysicsMeshId,attr_id:PhysicsAttributesId,transform:integer::Planar64Affine3)->Self{ pub const fn new(mesh_id:PhysicsMeshId,attr_id:PhysicsAttributesId,transform:PhysicsModelTransform)->Self{
Self{ Self{
mesh_id, mesh_id,
attr_id, attr_id,
transform, transform,
normal_transform:transform.matrix3.inverse_times_det().transpose(),
transform_det:transform.matrix3.determinant(),
} }
} }
const fn transform(&self)->&PhysicsModelTransform{
&self.transform
}
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct ContactCollision{ struct ContactCollision{
face_id:model_physics::MinkowskiFace, face_id:model_physics::MinkowskiFace,
model_id:PhysicsModelId,//using id to avoid lifetimes convex_mesh_id:ConvexMeshId,
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
struct IntersectCollision{ struct IntersectCollision{
model_id:PhysicsModelId, convex_mesh_id:ConvexMeshId,
} }
#[derive(Debug,Clone,Eq,Hash,PartialEq)] #[derive(Debug,Clone,Eq,Hash,PartialEq)]
enum Collision{ enum Collision{
@ -542,16 +546,16 @@ enum Collision{
Intersect(IntersectCollision), Intersect(IntersectCollision),
} }
impl Collision{ impl Collision{
fn model_id(&self)->PhysicsModelId{ fn convex_mesh_id(&self)->ConvexMeshId{
match self{ match self{
&Collision::Contact(ContactCollision{model_id,face_id:_}) &Collision::Contact(ContactCollision{convex_mesh_id,face_id:_})
|&Collision::Intersect(IntersectCollision{model_id})=>model_id, |&Collision::Intersect(IntersectCollision{convex_mesh_id})=>convex_mesh_id,
} }
} }
fn face_id(&self)->Option<model_physics::MinkowskiFace>{ fn face_id(&self)->Option<model_physics::MinkowskiFace>{
match self{ match self{
&Collision::Contact(ContactCollision{model_id:_,face_id})=>Some(face_id), &Collision::Contact(ContactCollision{convex_mesh_id:_,face_id})=>Some(face_id),
&Collision::Intersect(IntersectCollision{model_id:_})=>None, &Collision::Intersect(IntersectCollision{convex_mesh_id:_})=>None,
} }
} }
} }
@ -584,7 +588,7 @@ impl TouchingState{
} }
//add accelerators //add accelerators
for contact in &self.contacts{ for contact in &self.contacts{
match models.attr(contact.model_id){ match models.attr(contact.convex_mesh_id.model_id){
PhysicsCollisionAttributes::Contact{contacting,general}=>{ PhysicsCollisionAttributes::Contact{contacting,general}=>{
match &general.accelerator{ match &general.accelerator{
Some(accelerator)=>a+=accelerator.acceleration, Some(accelerator)=>a+=accelerator.acceleration,
@ -595,7 +599,7 @@ impl TouchingState{
} }
} }
for intersect in &self.intersects{ for intersect in &self.intersects{
match models.attr(intersect.model_id){ match models.attr(intersect.convex_mesh_id.model_id){
PhysicsCollisionAttributes::Intersect{intersecting,general}=>{ PhysicsCollisionAttributes::Intersect{intersecting,general}=>{
match &general.accelerator{ match &general.accelerator{
Some(accelerator)=>a+=accelerator.acceleration, Some(accelerator)=>a+=accelerator.acceleration,
@ -636,7 +640,7 @@ impl TouchingState{
let mut move_state=MoveState::Air; let mut move_state=MoveState::Air;
let mut a=gravity; let mut a=gravity;
for contact in &self.contacts{ for contact in &self.contacts{
match models.attr(contact.model_id){ match models.attr(contact.convex_mesh_id.model_id){
PhysicsCollisionAttributes::Contact{contacting,general}=>{ PhysicsCollisionAttributes::Contact{contacting,general}=>{
let normal=contact_normal(models,hitbox_mesh,contact); let normal=contact_normal(models,hitbox_mesh,contact);
match &contacting.contact_behaviour{ match &contacting.contact_behaviour{
@ -674,26 +678,26 @@ impl TouchingState{
let relative_body=VirtualBody::relative(&Body::default(),body).body(time); let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
for contact in &self.contacts{ for contact in &self.contacts{
//detect face slide off //detect face slide off
let model_mesh=models.mesh(contact.model_id); let model_mesh=models.mesh(contact.convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(face,time)|{ collector.collect(minkowski.predict_collision_face_out(&relative_body,collector.time(),contact.face_id).map(|(face,time)|{
TimedInstruction{ TimedInstruction{
time, time,
instruction:PhysicsInstruction::CollisionEnd( instruction:PhysicsInstruction::CollisionEnd(
Collision::Contact(ContactCollision{model_id:contact.model_id,face_id:contact.face_id}) Collision::Contact(ContactCollision{convex_mesh_id:contact.convex_mesh_id,face_id:contact.face_id})
), ),
} }
})); }));
} }
for intersect in &self.intersects{ for intersect in &self.intersects{
//detect model collision in reverse //detect model collision in reverse
let model_mesh=models.mesh(intersect.model_id); let model_mesh=models.mesh(intersect.convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(face,time)|{ collector.collect(minkowski.predict_collision_out(&relative_body,collector.time()).map(|(face,time)|{
TimedInstruction{ TimedInstruction{
time, time,
instruction:PhysicsInstruction::CollisionEnd( instruction:PhysicsInstruction::CollisionEnd(
Collision::Intersect(IntersectCollision{model_id:intersect.model_id}) Collision::Intersect(IntersectCollision{convex_mesh_id:intersect.convex_mesh_id})
), ),
} }
})); }));
@ -809,7 +813,7 @@ pub struct PhysicsState{
//random collection of contextual data that doesn't belong in PhysicsState //random collection of contextual data that doesn't belong in PhysicsState
pub struct PhysicsData{ pub struct PhysicsData{
//permanent map data //permanent map data
bvh:bvh::BvhNode<PhysicsModelId>, bvh:bvh::BvhNode<ConvexMeshId>,
modes:gameplay_modes::Modes, modes:gameplay_modes::Modes,
//transient map/environment data (open world may load/unload) //transient map/environment data (open world may load/unload)
models:PhysicsModels, models:PhysicsModels,
@ -939,33 +943,47 @@ impl PhysicsContext{
}); });
} }
pub fn generate_models(&mut self,map:&map::Map){ pub fn generate_models(&mut self,map:map::Map){
let mut starts=Vec::new(); let mut starts=Vec::new();
let mut spawns=Vec::new(); let mut spawns=Vec::new();
let mut attr_hash=HashMap::new(); let mut attr_hash=HashMap::new();
for model in &map.models{ for (model_id,model) in map.models{
let mesh_id=self.models.meshes.len(); let mesh_id=self.data.models.meshes.len();
let mut make_mesh=false; let mut make_mesh=false;
for model_instance in &model.instances{ for model_instance in &model.instances{
if let Ok(physics_attributes)=PhysicsCollisionAttributes::try_from(&model_instance.attributes){ if let Ok(physics_attributes)=PhysicsCollisionAttributes::try_from(&model_instance.attributes){
let attr_id=if let Some(&attr_id)=attr_hash.get(&physics_attributes){ let attr_id=if let Some(&attr_id)=attr_hash.get(&physics_attributes){
attr_id attr_id
}else{ }else{
let attr_id=self.models.push_attr(physics_attributes.clone()); let attr_id=self.data.models.push_attr(physics_attributes.clone());
attr_hash.insert(physics_attributes,attr_id); attr_hash.insert(physics_attributes,attr_id);
attr_id attr_id
}; };
let model_physics=PhysicsModel::new(mesh_id,attr_id,model_instance.transform); let model_physics=PhysicsModel::new(mesh_id,attr_id,model_instance.transform);
make_mesh=true; make_mesh=true;
self.models.push_model(model_physics); self.data.models.push_model(model_physics);
} }
} }
if make_mesh{ if make_mesh{
self.models.push_mesh(PhysicsMesh::from(model)); self.data.models.push_mesh(PhysicsMesh::from(model));
} }
} }
self.bvh=bvh::generate_bvh(self.models.aabb_list(),|i|PhysicsModelId::new(i as u32)); let convex_mesh_aabb_list=self.data.models.models.iter()
println!("Physics Objects: {}",self.models.models.len()); .enumerate().flat_map(|(model_id,model)|{
self.data.models.meshes[model.mesh_id.get() as usize].submesh_views()
.enumerate().map(|(submesh_id,view)|{
let mut aabb=aabb::Aabb::default();
for v in view.verts(){
aabb.grow(v)
}
(ConvexMeshId{
model_id:PhysicsModelId::new(model_id as u32),
submesh_id:PhysicsSubmeshId::new(submesh_id as u32),
},aabb)
})
}).collect();
self.data.bvh=bvh::generate_bvh_node(convex_mesh_aabb_list);
println!("Physics Objects: {}",self.data.models.models.len());
} }
//tickless gaming //tickless gaming
@ -1020,17 +1038,17 @@ impl PhysicsContext{
//common body //common body
let relative_body=VirtualBody::relative(&Body::default(),&state.body).body(state.time); let relative_body=VirtualBody::relative(&Body::default(),&state.body).body(state.time);
let hitbox_mesh=data.hitbox_mesh.transformed_mesh(); let hitbox_mesh=data.hitbox_mesh.transformed_mesh();
data.bvh.the_tester(&aabb,&mut |id|{ data.bvh.the_tester(&aabb,&mut |convex_mesh_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(id); let model_mesh=data.models.mesh(convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh);
collector.collect(minkowski.predict_collision_in(&relative_body,collector.time()) collector.collect(minkowski.predict_collision_in(&relative_body,collector.time())
//temp (?) code to avoid collision loops //temp (?) code to avoid collision loops
.map_or(None,|(face,time)|if time==state.time{None}else{Some((face,time))}) .map_or(None,|(face,time)|if time==state.time{None}else{Some((face,time))})
.map(|(face,time)|{ .map(|(face,time)|{
TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match data.models.attr(id){ TimedInstruction{time,instruction:PhysicsInstruction::CollisionStart(match data.models.attr(convex_mesh_id.model_id){
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{model_id:id,face_id:face}), PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{convex_mesh_id,face_id:face}),
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{model_id:id}), PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{convex_mesh_id}),
})} })}
})); }));
}); });
@ -1053,8 +1071,8 @@ fn jumped_velocity(models:&PhysicsModels,style:&StyleModifiers,hitbox_mesh:&Hitb
} }
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
let model_mesh=models.mesh(contact.model_id); let model_mesh=models.mesh(contact.convex_mesh_id);
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&model_mesh,&hitbox_mesh.transformed_mesh()); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
minkowski.face_nd(contact.face_id).0 minkowski.face_nd(contact.face_id).0
} }
@ -1116,15 +1134,15 @@ fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,sty
} }
fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,mode:&gameplay_modes::Mode,models:&PhysicsModels,stage_id:gameplay_modes::StageId)->Option<MoveState>{ fn teleport_to_spawn(body:&mut Body,touching:&mut TouchingState,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,mode:&gameplay_modes::Mode,models:&PhysicsModels,stage_id:gameplay_modes::StageId)->Option<MoveState>{
let model=models.model(mode.get_spawn_model_id(stage_id)?.into()); let model=models.model(mode.get_spawn_model_id(stage_id)?.into());
let point=model.transform.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16); let point=model.transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
Some(teleport(body,touching,models,style,hitbox_mesh,point)) Some(teleport(body,touching,models,style,hitbox_mesh,point))
} }
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,model_id:PhysicsModelId)->Option<MoveState>{ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&mut ModeState,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{
//TODO: jump count and checkpoints are always reset on teleport. //TODO: jump count and checkpoints are always reset on teleport.
//Map makers are expected to use tools to prevent //Map makers are expected to use tools to prevent
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity //multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
if let Some(stage_element)=mode.get_element(model_id.into()){ if let Some(stage_element)=mode.get_element(convex_mesh_id.model_id.into()){
let stage=mode.get_stage(stage_element.stage_id)?; let stage=mode.get_stage(stage_element.stage_id)?;
if stage_element.force||game.stage_id<stage_element.stage_id{ if stage_element.force||game.stage_id<stage_element.stage_id{
//TODO: check if all checkpoints are complete up to destination stage id, otherwise set to checkpoint completion stage it //TODO: check if all checkpoints are complete up to destination stage id, otherwise set to checkpoint completion stage it
@ -1151,23 +1169,23 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
}, },
} }
if let Some(next_checkpoint)=stage.ordered_checkpoints.get(&game.next_ordered_checkpoint_id){ if let Some(next_checkpoint)=stage.ordered_checkpoints.get(&game.next_ordered_checkpoint_id){
if model_id==next_checkpoint{ if convex_mesh_id==next_checkpoint{
//if you hit the next number in a sequence of ordered checkpoints //if you hit the next number in a sequence of ordered checkpoints
//increment the current checkpoint id //increment the current checkpoint id
game.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(game.next_ordered_checkpoint_id.get()+1); game.next_ordered_checkpoint_id=gameplay_modes::CheckpointId::new(game.next_ordered_checkpoint_id.get()+1);
} }
} }
if stage.unordered_checkpoints.contains(model_id.into()){ if stage.unordered_checkpoints.contains(convex_mesh_id.model_id.into()){
//count model id in accumulated unordered checkpoints //count model id in accumulated unordered checkpoints
game.unordered_checkpoints.insert(model_id.into()); game.unordered_checkpoints.insert(convex_mesh_id.model_id.into());
} }
} }
match wormhole{ match wormhole{
&Some(gameplay_attributes::Wormhole{destination_model})=>{ &Some(gameplay_attributes::Wormhole{destination_model})=>{
let origin_model=models.model(model_id); let origin_model=models.model(convex_mesh_id.model_id);
let destination_model=models.model(destination_model.into()); let destination_model=models.model(destination_model.into());
//ignore the transform for now //ignore the transform for now
Some(teleport(body,touching,models,style,hitbox_mesh,body.position-origin_model.transform.translation+destination_model.transform.translation)) Some(teleport(body,touching,models,style,hitbox_mesh,body.position-origin_model.transform.vertex.translation+destination_model.transform.vertex.translation))
} }
None=>None, None=>None,
} }
@ -1192,8 +1210,8 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
} }
match ins.instruction{ match ins.instruction{
PhysicsInstruction::CollisionStart(c)=>{ PhysicsInstruction::CollisionStart(c)=>{
let model_id=c.model_id(); let convex_mesh_id=c.convex_mesh_id();
match (data.models.attr(model_id),&c){ match (data.models.attr(convex_mesh_id.model_id),&c){
(PhysicsCollisionAttributes::Contact{contacting,general},Collision::Contact(contact))=>{ (PhysicsCollisionAttributes::Contact{contacting,general},Collision::Contact(contact))=>{
let mut v=state.body.velocity; let mut v=state.body.velocity;
let normal=contact_normal(&data.models,&data.hitbox_mesh,contact); let normal=contact_normal(&data.models,&data.hitbox_mesh,contact);
@ -1232,7 +1250,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
//check ground //check ground
state.touching.insert(c); state.touching.insert(c);
//I love making functions with 10 arguments to dodge the borrow checker //I love making functions with 10 arguments to dodge the borrow checker
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,model_id); run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
//flatten v //flatten v
state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v); state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v);
match &general.booster{ match &general.booster{
@ -1276,13 +1294,13 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
(PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{ (PhysicsCollisionAttributes::Intersect{intersecting: _,general},Collision::Intersect(intersect))=>{
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
state.touching.insert(c); state.touching.insert(c);
run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,model_id); run_teleport_behaviour(&general.wormhole,&mut state.mode_state,&data.models,&data.modes.get_mode(state.mode_state.mode_id).unwrap(),&state.style,&data.hitbox_mesh,&mut state.touching,&mut state.body,convex_mesh_id);
}, },
_=>panic!("invalid pair"), _=>panic!("invalid pair"),
} }
}, },
PhysicsInstruction::CollisionEnd(c) => { PhysicsInstruction::CollisionEnd(c) => {
match data.models.attr(c.model_id()){ match data.models.attr(c.convex_mesh_id().model_id){
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>{ PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>{
state.touching.remove(&c);//remove contact before calling contact_constrain_acceleration state.touching.remove(&c);//remove contact before calling contact_constrain_acceleration
//check ground //check ground
@ -1365,7 +1383,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,game:&
let spawn_point={ let spawn_point={
let mode=data.modes.get_mode(state.mode_state.mode_id).unwrap(); let mode=data.modes.get_mode(state.mode_state.mode_id).unwrap();
let stage=mode.get_stage(gameplay_modes::StageId::FIRST).unwrap(); let stage=mode.get_stage(gameplay_modes::StageId::FIRST).unwrap();
data.models.model(stage.spawn().into()).transform.translation data.models.model(stage.spawn().into()).transform.vertex.translation
}; };
set_position(&mut state.body,&mut state.touching,spawn_point); set_position(&mut state.body,&mut state.touching,spawn_point);
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO); set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
@ -1409,7 +1427,7 @@ fn test_collision_rotated(relative_body:Body,expected_collision_time:Option<Time
let h1=HitboxMesh::roblox(); let h1=HitboxMesh::roblox();
let hitbox_mesh=h1.transformed_mesh(); let hitbox_mesh=h1.transformed_mesh();
let platform_mesh=h0.transformed_mesh(); let platform_mesh=h0.transformed_mesh();
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(&platform_mesh,&hitbox_mesh); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(platform_mesh,hitbox_mesh);
let collision=minkowski.predict_collision_in(&relative_body,Time::MAX); let collision=minkowski.predict_collision_in(&relative_body,Time::MAX);
assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision"); assert_eq!(collision.map(|tup|tup.1),expected_collision_time,"Incorrect time of collision");
} }