physics: refactor models and attributes with type safety
make invalid states unrepresentable!!!
This commit is contained in:
parent
5e45753756
commit
d3f84c2dbd
20
Cargo.lock
generated
20
Cargo.lock
generated
@ -1900,9 +1900,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_bsp_loader"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "b6b3e1324034abfd648e339580989f8f2c30ac2009296229349d88b8fcb4eedd"
|
||||
checksum = "35ee2c534efa039ad17ca41893ba1d75fafff014076353ac676c73fc808b9e44"
|
||||
dependencies = [
|
||||
"glam",
|
||||
"strafesnet_common",
|
||||
@ -1912,9 +1912,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_common"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "1077d45a0b064964906a57de765a5a2bfe47b41f2f807d13b18c70765e76d3dd"
|
||||
checksum = "ea4126f6fbf9aecf89c9e319290f0221d177dcaa8659b4b9e3d82acc37829f12"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bitflags 2.6.0",
|
||||
@ -1924,9 +1924,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_deferred_loader"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "9d5ad437524fb201fd5be68f76c53dd831e81ccad4655e19e3d1ca201863b566"
|
||||
checksum = "596aba6d2747818781336ad95a1ee496e37f70052fd625a299fc7a555a6938d4"
|
||||
dependencies = [
|
||||
"lazy-regex",
|
||||
"strafesnet_common",
|
||||
@ -1935,9 +1935,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_rbx_loader"
|
||||
version = "0.3.3"
|
||||
version = "0.3.4"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "3a910867e1f5ab2d9cc9c178973aee7fa029547e27465e47fea2eb99b860bb81"
|
||||
checksum = "6cd7fb0eca01ccd382067924e5fad15844f55a6bcc7c14c0e57a171298263a3e"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"glam",
|
||||
@ -1952,9 +1952,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "strafesnet_snf"
|
||||
version = "0.1.2"
|
||||
version = "0.1.3"
|
||||
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
|
||||
checksum = "d12fc351b2af5fd7a8183175d55ac43e21eb8fd1f76cc70cd4713c0d1a556c96"
|
||||
checksum = "a9ae481152d0389be29967e1d5f0377498df8ff9638175d56cd8e2c2e6982bfa"
|
||||
dependencies = [
|
||||
"binrw 0.14.0",
|
||||
"id",
|
||||
|
@ -23,7 +23,7 @@ id = { version = "0.1.0", registry = "strafesnet" }
|
||||
parking_lot = "0.12.1"
|
||||
pollster = "0.3.0"
|
||||
strafesnet_bsp_loader = { version = "0.1.3", registry = "strafesnet", optional = true }
|
||||
strafesnet_common = { version = "0.3.0", registry = "strafesnet" }
|
||||
strafesnet_common = { version = "0.4.0", registry = "strafesnet" }
|
||||
strafesnet_deferred_loader = { version = "0.3.1", features = ["legacy"], registry = "strafesnet", optional = true }
|
||||
strafesnet_rbx_loader = { version = "0.3.2", registry = "strafesnet", optional = true }
|
||||
strafesnet_snf = { version = "0.1.2", registry = "strafesnet", optional = true }
|
||||
|
426
src/physics.rs
426
src/physics.rs
@ -189,33 +189,69 @@ fn ladder_things(ladder_settings:&gameplay_style::LadderSettings,contact:&Contac
|
||||
#[derive(Default)]
|
||||
struct PhysicsModels{
|
||||
meshes:HashMap<PhysicsMeshId,PhysicsMesh>,
|
||||
models:HashMap<PhysicsModelId,PhysicsModel>,
|
||||
//separate models into Contacting and Intersecting?
|
||||
//wrap model id with ContactingModelId and IntersectingModelId
|
||||
//attributes can be split into contacting and intersecting (this also saves a bit of memory)
|
||||
//can go even further and deduplicate General attributes separately, reconstructing it when queried
|
||||
attributes:HashMap<PhysicsAttributesId,PhysicsCollisionAttributes>,
|
||||
contact_models:HashMap<ContactModelId,ContactModel>,
|
||||
intersect_models:HashMap<IntersectModelId,IntersectModel>,
|
||||
contact_attributes:HashMap<ContactAttributesId,gameplay_attributes::ContactAttributes>,
|
||||
intersect_attributes:HashMap<IntersectAttributesId,gameplay_attributes::IntersectAttributes>,
|
||||
}
|
||||
impl PhysicsModels{
|
||||
fn clear(&mut self){
|
||||
self.meshes.clear();
|
||||
self.models.clear();
|
||||
self.attributes.clear();
|
||||
self.contact_models.clear();
|
||||
self.intersect_models.clear();
|
||||
self.contact_attributes.clear();
|
||||
self.intersect_attributes.clear();
|
||||
}
|
||||
//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.
|
||||
fn mesh(&self,convex_mesh_id:ConvexMeshId)->TransformedMesh{
|
||||
let model=&self.models[&convex_mesh_id.model_id];
|
||||
let (mesh_id,transform)=match convex_mesh_id.model_id{
|
||||
PhysicsModelId::Contact(model_id)=>{
|
||||
let model=&self.contact_models[&model_id];
|
||||
(&model.mesh_id,&model.transform)
|
||||
},
|
||||
PhysicsModelId::Intersect(model_id)=>{
|
||||
let model=&self.intersect_models[&model_id];
|
||||
(&model.mesh_id,&model.transform)
|
||||
},
|
||||
};
|
||||
TransformedMesh::new(
|
||||
self.meshes[&model.mesh_id].submesh_view(convex_mesh_id.submesh_id),
|
||||
self.meshes[mesh_id].submesh_view(convex_mesh_id.submesh_id),
|
||||
transform
|
||||
)
|
||||
}
|
||||
//it's a bit weird to have three functions, but it's always going to be one of these
|
||||
fn contact_mesh(&self,contact:&ContactCollision)->TransformedMesh{
|
||||
let model=&self.contact_models[&contact.model_id];
|
||||
TransformedMesh::new(
|
||||
self.meshes[&model.mesh_id].submesh_view(contact.submesh_id),
|
||||
&model.transform
|
||||
)
|
||||
}
|
||||
fn model(&self,model_id:PhysicsModelId)->&PhysicsModel{
|
||||
&self.models[&model_id]
|
||||
fn intersect_mesh(&self,intersect:&IntersectCollision)->TransformedMesh{
|
||||
let model=&self.intersect_models[&intersect.model_id];
|
||||
TransformedMesh::new(
|
||||
self.meshes[&model.mesh_id].submesh_view(intersect.submesh_id),
|
||||
&model.transform
|
||||
)
|
||||
}
|
||||
fn attr(&self,model_id:PhysicsModelId)->&PhysicsCollisionAttributes{
|
||||
&self.attributes[&self.models[&model_id].attr_id]
|
||||
fn get_model_transform(&self,model_id:ModelId)->Option<&PhysicsMeshTransform>{
|
||||
//ModelId can possibly be a decoration
|
||||
self.contact_models.get(&ContactModelId::new(model_id.get())).map_or_else(
|
||||
||self.intersect_models.get(&IntersectModelId::new(model_id.get()))
|
||||
.map(|model|&model.transform),
|
||||
|model|Some(&model.transform)
|
||||
)
|
||||
}
|
||||
fn contact_model(&self,model_id:ContactModelId)->&ContactModel{
|
||||
&self.contact_models[&model_id]
|
||||
}
|
||||
fn intersect_model(&self,model_id:IntersectModelId)->&IntersectModel{
|
||||
&self.intersect_models[&model_id]
|
||||
}
|
||||
fn contact_attr(&self,model_id:ContactModelId)->&gameplay_attributes::ContactAttributes{
|
||||
&self.contact_attributes[&self.contact_models[&model_id].attr_id]
|
||||
}
|
||||
fn intersect_attr(&self,model_id:IntersectModelId)->&gameplay_attributes::IntersectAttributes{
|
||||
&self.intersect_attributes[&self.intersect_models[&model_id].attr_id]
|
||||
}
|
||||
}
|
||||
|
||||
@ -560,14 +596,8 @@ impl PhysicsOutputState{
|
||||
|
||||
#[derive(Clone,Hash,Eq,PartialEq)]
|
||||
enum PhysicsCollisionAttributes{
|
||||
Contact{//track whether you are contacting the object
|
||||
contacting:gameplay_attributes::ContactingAttributes,
|
||||
general:gameplay_attributes::GeneralAttributes,
|
||||
},
|
||||
Intersect{//track whether you are intersecting the object
|
||||
intersecting:gameplay_attributes::IntersectingAttributes,
|
||||
general:gameplay_attributes::GeneralAttributes,
|
||||
},
|
||||
Contact(gameplay_attributes::ContactAttributes),
|
||||
Intersect(gameplay_attributes::IntersectAttributes),
|
||||
}
|
||||
struct NonPhysicsError;
|
||||
impl TryFrom<&gameplay_attributes::CollisionAttributes> for PhysicsCollisionAttributes{
|
||||
@ -575,85 +605,100 @@ impl TryFrom<&gameplay_attributes::CollisionAttributes> for PhysicsCollisionAttr
|
||||
fn try_from(value:&gameplay_attributes::CollisionAttributes)->Result<Self,Self::Error>{
|
||||
match value{
|
||||
gameplay_attributes::CollisionAttributes::Decoration=>Err(NonPhysicsError),
|
||||
gameplay_attributes::CollisionAttributes::Contact{contacting,general}=>Ok(Self::Contact{contacting:contacting.clone(),general:general.clone()}),
|
||||
gameplay_attributes::CollisionAttributes::Intersect{intersecting,general}=>Ok(Self::Intersect{intersecting:intersecting.clone(),general:general.clone()}),
|
||||
gameplay_attributes::CollisionAttributes::Contact(attr)=>Ok(Self::Contact(attr.clone())),
|
||||
gameplay_attributes::CollisionAttributes::Intersect(attr)=>Ok(Self::Intersect(attr.clone())),
|
||||
}
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
struct PhysicsAttributesId(u32);
|
||||
impl Into<CollisionAttributesId> for PhysicsAttributesId{
|
||||
struct ContactAttributesId(u32);
|
||||
impl Into<CollisionAttributesId> for ContactAttributesId{
|
||||
fn into(self)->CollisionAttributesId{
|
||||
CollisionAttributesId::new(self.0)
|
||||
}
|
||||
}
|
||||
impl From<CollisionAttributesId> for PhysicsAttributesId{
|
||||
impl From<CollisionAttributesId> for ContactAttributesId{
|
||||
fn from(value:CollisionAttributesId)->Self{
|
||||
Self::new(value.get())
|
||||
}
|
||||
}
|
||||
#[derive(Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
struct IntersectAttributesId(u32);
|
||||
impl Into<CollisionAttributesId> for IntersectAttributesId{
|
||||
fn into(self)->CollisionAttributesId{
|
||||
CollisionAttributesId::new(self.0)
|
||||
}
|
||||
}
|
||||
impl From<CollisionAttributesId> for IntersectAttributesId{
|
||||
fn from(value:CollisionAttributesId)->Self{
|
||||
Self::new(value.get())
|
||||
}
|
||||
}
|
||||
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
struct ContactModelId(u32);
|
||||
impl Into<ModelId> for ContactModelId{
|
||||
fn into(self)->ModelId{
|
||||
ModelId::new(self.get())
|
||||
}
|
||||
}
|
||||
#[derive(Debug,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
struct IntersectModelId(u32);
|
||||
impl Into<ModelId> for IntersectModelId{
|
||||
fn into(self)->ModelId{
|
||||
ModelId::new(self.get())
|
||||
}
|
||||
}
|
||||
#[derive(Debug,Clone,Copy,Hash,Eq,PartialEq)]
|
||||
enum PhysicsModelId{
|
||||
Contact(ContactModelId),
|
||||
Intersect(IntersectModelId),
|
||||
}
|
||||
impl Into<ModelId> for PhysicsModelId{
|
||||
fn into(self)->ModelId{
|
||||
ModelId::new(match self{
|
||||
PhysicsModelId::Contact(model_id)=>model_id.get(),
|
||||
PhysicsModelId::Intersect(model_id)=>model_id.get(),
|
||||
})
|
||||
}
|
||||
}
|
||||
//unique physics meshes indexed by this
|
||||
#[derive(Debug,Default,Clone,Copy,Eq,Hash,PartialEq)]
|
||||
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
|
||||
struct ConvexMeshId{
|
||||
model_id:PhysicsModelId,
|
||||
submesh_id:PhysicsSubmeshId,
|
||||
}
|
||||
#[derive(Debug,Default,Clone,Copy,Hash,id::Id,Eq,PartialEq)]
|
||||
struct PhysicsModelId(u32);
|
||||
impl Into<ModelId> for PhysicsModelId{
|
||||
fn into(self)->ModelId{
|
||||
ModelId::new(self.0)
|
||||
}
|
||||
}
|
||||
impl From<ModelId> for PhysicsModelId{
|
||||
fn from(value:ModelId)->Self{
|
||||
Self::new(value.get())
|
||||
}
|
||||
}
|
||||
pub struct PhysicsModel{
|
||||
//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.
|
||||
struct ContactModel{
|
||||
mesh_id:PhysicsMeshId,
|
||||
//put these up on the Model (data normalization)
|
||||
attr_id:PhysicsAttributesId,
|
||||
attr_id:ContactAttributesId,
|
||||
transform:PhysicsMeshTransform,
|
||||
}
|
||||
struct IntersectModel{
|
||||
mesh_id:PhysicsMeshId,
|
||||
attr_id:IntersectAttributesId,
|
||||
transform:PhysicsMeshTransform,
|
||||
}
|
||||
|
||||
impl PhysicsModel{
|
||||
const fn new(mesh_id:PhysicsMeshId,attr_id:PhysicsAttributesId,transform:PhysicsMeshTransform)->Self{
|
||||
Self{
|
||||
mesh_id,
|
||||
attr_id,
|
||||
transform,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
|
||||
pub struct ContactCollision{
|
||||
struct ContactCollision{
|
||||
face_id:model_physics::MinkowskiFace,
|
||||
convex_mesh_id:ConvexMeshId,
|
||||
model_id:ContactModelId,
|
||||
submesh_id:PhysicsSubmeshId,
|
||||
}
|
||||
#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)]
|
||||
pub struct IntersectCollision{
|
||||
convex_mesh_id:ConvexMeshId,
|
||||
struct IntersectCollision{
|
||||
model_id:IntersectModelId,
|
||||
submesh_id:PhysicsSubmeshId,
|
||||
}
|
||||
#[derive(Debug,Clone,Eq,Hash,PartialEq)]
|
||||
pub enum Collision{
|
||||
enum Collision{
|
||||
Contact(ContactCollision),
|
||||
Intersect(IntersectCollision),
|
||||
}
|
||||
impl Collision{
|
||||
const fn convex_mesh_id(&self)->ConvexMeshId{
|
||||
match self{
|
||||
&Collision::Contact(ContactCollision{convex_mesh_id,face_id:_})
|
||||
|&Collision::Intersect(IntersectCollision{convex_mesh_id})=>convex_mesh_id,
|
||||
}
|
||||
}
|
||||
const fn face_id(&self)->Option<model_physics::MinkowskiFace>{
|
||||
match self{
|
||||
&Collision::Contact(ContactCollision{convex_mesh_id:_,face_id})=>Some(face_id),
|
||||
&Collision::Intersect(IntersectCollision{convex_mesh_id:_})=>None,
|
||||
const fn new(convex_mesh_id:ConvexMeshId,face_id:model_physics::MinkowskiFace)->Self{
|
||||
match convex_mesh_id.model_id{
|
||||
PhysicsModelId::Contact(model_id)=>Collision::Contact(ContactCollision{model_id,submesh_id:convex_mesh_id.submesh_id,face_id}),
|
||||
PhysicsModelId::Intersect(model_id)=>Collision::Intersect(IntersectCollision{model_id,submesh_id:convex_mesh_id.submesh_id}),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -686,25 +731,13 @@ impl TouchingState{
|
||||
}
|
||||
//add accelerators
|
||||
for contact in &self.contacts{
|
||||
match models.attr(contact.convex_mesh_id.model_id){
|
||||
PhysicsCollisionAttributes::Contact{contacting:_,general}=>{
|
||||
match &general.accelerator{
|
||||
Some(accelerator)=>a+=accelerator.acceleration,
|
||||
None=>(),
|
||||
}
|
||||
},
|
||||
_=>panic!("impossible touching state"),
|
||||
if let Some(accelerator)=&models.contact_attr(contact.model_id).general.accelerator{
|
||||
a+=accelerator.acceleration;
|
||||
}
|
||||
}
|
||||
for intersect in &self.intersects{
|
||||
match models.attr(intersect.convex_mesh_id.model_id){
|
||||
PhysicsCollisionAttributes::Intersect{intersecting:_,general}=>{
|
||||
match &general.accelerator{
|
||||
Some(accelerator)=>a+=accelerator.acceleration,
|
||||
None=>(),
|
||||
}
|
||||
},
|
||||
_=>panic!("impossible touching state"),
|
||||
if let Some(accelerator)=&models.intersect_attr(intersect.model_id).general.accelerator{
|
||||
a+=accelerator.acceleration;
|
||||
}
|
||||
}
|
||||
//TODO: add water
|
||||
@ -734,26 +767,26 @@ impl TouchingState{
|
||||
let relative_body=VirtualBody::relative(&Body::default(),body).body(time);
|
||||
for contact in &self.contacts{
|
||||
//detect face slide off
|
||||
let model_mesh=models.mesh(contact.convex_mesh_id);
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
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)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInternalInstruction::CollisionEnd(
|
||||
Collision::Contact(ContactCollision{convex_mesh_id:contact.convex_mesh_id,face_id:contact.face_id})
|
||||
Collision::Contact(*contact)
|
||||
),
|
||||
}
|
||||
}));
|
||||
}
|
||||
for intersect in &self.intersects{
|
||||
//detect model collision in reverse
|
||||
let model_mesh=models.mesh(intersect.convex_mesh_id);
|
||||
let model_mesh=models.intersect_mesh(intersect);
|
||||
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)|{
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInternalInstruction::CollisionEnd(
|
||||
Collision::Intersect(IntersectCollision{convex_mesh_id:intersect.convex_mesh_id})
|
||||
Collision::Intersect(*intersect)
|
||||
),
|
||||
}
|
||||
}));
|
||||
@ -1020,15 +1053,27 @@ impl PhysicsContext{
|
||||
/// use with caution, this is the only non-instruction way to mess with physics
|
||||
pub fn generate_models(&mut self,map:&map::CompleteMap){
|
||||
self.state.clear();
|
||||
self.data.modes=map.modes.clone();
|
||||
for mode in &mut self.data.modes.modes{
|
||||
let mut modes=map.modes.clone();
|
||||
for mode in &mut modes.modes{
|
||||
mode.denormalize_data();
|
||||
}
|
||||
let mut used_attributes=Vec::new();
|
||||
let mut used_contact_attributes=Vec::new();
|
||||
let mut used_intersect_attributes=Vec::new();
|
||||
|
||||
//temporary type for type safety lol
|
||||
#[derive(Clone,Copy,Hash,Eq,PartialEq)]
|
||||
enum PhysicsAttributesId{
|
||||
Contact(ContactAttributesId),
|
||||
Intersect(IntersectAttributesId),
|
||||
}
|
||||
|
||||
let mut contact_models=HashMap::new();
|
||||
let mut intersect_models=HashMap::new();
|
||||
|
||||
let mut physics_attr_id_from_model_attr_id=HashMap::<CollisionAttributesId,PhysicsAttributesId>::new();
|
||||
let mut used_meshes=Vec::new();
|
||||
let mut physics_mesh_id_from_model_mesh_id=HashMap::<MeshId,PhysicsMeshId>::new();
|
||||
self.data.models.models=map.models.iter().enumerate().filter_map(|(model_id,model)|{
|
||||
for (model_id,model) in map.models.iter().enumerate(){
|
||||
//TODO: use .entry().or_insert_with(||{
|
||||
let attr_id=if let Some(&attr_id)=physics_attr_id_from_model_attr_id.get(&model.attributes){
|
||||
attr_id
|
||||
@ -1036,14 +1081,24 @@ impl PhysicsContext{
|
||||
//check if it's real
|
||||
match map.attributes.get(model.attributes.get() as usize).and_then(|m_attr|{
|
||||
PhysicsCollisionAttributes::try_from(m_attr).map_or(None,|p_attr|{
|
||||
let attr_id=PhysicsAttributesId::new(used_attributes.len() as u32);
|
||||
used_attributes.push(p_attr);
|
||||
let attr_id=match p_attr{
|
||||
PhysicsCollisionAttributes::Contact(attr)=>{
|
||||
let attr_id=ContactAttributesId::new(used_contact_attributes.len() as u32);
|
||||
used_contact_attributes.push(attr);
|
||||
PhysicsAttributesId::Contact(attr_id)
|
||||
},
|
||||
PhysicsCollisionAttributes::Intersect(attr)=>{
|
||||
let attr_id=IntersectAttributesId::new(used_intersect_attributes.len() as u32);
|
||||
used_intersect_attributes.push(attr);
|
||||
PhysicsAttributesId::Intersect(attr_id)
|
||||
},
|
||||
};
|
||||
physics_attr_id_from_model_attr_id.insert(model.attributes,attr_id);
|
||||
Some(attr_id)
|
||||
})
|
||||
}){
|
||||
Some(attr_id)=>attr_id,
|
||||
None=>return None,
|
||||
None=>continue,
|
||||
}
|
||||
};
|
||||
let mesh_id=if let Some(&mesh_id)=physics_mesh_id_from_model_mesh_id.get(&model.mesh){
|
||||
@ -1064,19 +1119,44 @@ impl PhysicsContext{
|
||||
}
|
||||
}){
|
||||
Some(mesh_id)=>mesh_id,
|
||||
None=>return None,
|
||||
None=>continue,
|
||||
}
|
||||
};
|
||||
Some((PhysicsModelId::new(model_id as u32),PhysicsModel::new(mesh_id,attr_id,PhysicsMeshTransform::new(model.transform))))
|
||||
}).collect();
|
||||
self.data.models.attributes=used_attributes.into_iter().enumerate().map(|(attr_id,attr)|(PhysicsAttributesId::new(attr_id as u32),attr)).collect();
|
||||
self.data.models.meshes=used_meshes.into_iter().enumerate().map(|(mesh_id,mesh)|(PhysicsMeshId::new(mesh_id as u32),mesh)).collect();
|
||||
let convex_mesh_aabb_list=self.data.models.models.iter()
|
||||
.flat_map(|(&model_id,model)|{
|
||||
self.data.models.meshes[&model.mesh_id].submesh_views()
|
||||
let transform=PhysicsMeshTransform::new(model.transform);
|
||||
match attr_id{
|
||||
PhysicsAttributesId::Contact(attr_id)=>{
|
||||
contact_models.insert(ContactModelId::new(model_id as u32),ContactModel{
|
||||
mesh_id,
|
||||
attr_id,
|
||||
transform,
|
||||
});
|
||||
},
|
||||
PhysicsAttributesId::Intersect(attr_id)=>{
|
||||
intersect_models.insert(IntersectModelId::new(model_id as u32),IntersectModel{
|
||||
mesh_id,
|
||||
attr_id,
|
||||
transform,
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
let meshes:HashMap<PhysicsMeshId,PhysicsMesh>=used_meshes.into_iter()
|
||||
.enumerate()
|
||||
.map(|(mesh_id,mesh)|
|
||||
(PhysicsMeshId::new(mesh_id as u32),mesh)
|
||||
).collect();
|
||||
let convex_mesh_aabb_list=
|
||||
//map the two lists into a single type so they can be processed with one closure
|
||||
contact_models.iter().map(|(&model_id,model)|
|
||||
(PhysicsModelId::Contact(model_id),&model.mesh_id,&model.transform)
|
||||
).chain(intersect_models.iter().map(|(&model_id,model)|
|
||||
(PhysicsModelId::Intersect(model_id),&model.mesh_id,&model.transform)
|
||||
))
|
||||
.flat_map(|(model_id,mesh_id,transform)|{
|
||||
meshes[mesh_id].submesh_views()
|
||||
.enumerate().map(move|(submesh_id,view)|{
|
||||
let mut aabb=aabb::Aabb::default();
|
||||
let transformed_mesh=TransformedMesh::new(view,&model.transform);
|
||||
let transformed_mesh=TransformedMesh::new(view,transform);
|
||||
for v in transformed_mesh.verts(){
|
||||
aabb.grow(v);
|
||||
}
|
||||
@ -1086,8 +1166,28 @@ impl PhysicsContext{
|
||||
},aabb)
|
||||
})
|
||||
}).collect();
|
||||
self.data.bvh=bvh::generate_bvh(convex_mesh_aabb_list);
|
||||
println!("Physics Objects: {}",self.data.models.models.len());
|
||||
let bvh=bvh::generate_bvh(convex_mesh_aabb_list);
|
||||
let model_count=contact_models.len()+intersect_models.len();
|
||||
let models=PhysicsModels{
|
||||
meshes,
|
||||
contact_models,
|
||||
intersect_models,
|
||||
contact_attributes:used_contact_attributes.into_iter()
|
||||
.enumerate()
|
||||
.map(|(attr_id,attr)|
|
||||
(ContactAttributesId::new(attr_id as u32),attr)
|
||||
).collect(),
|
||||
intersect_attributes:used_intersect_attributes.into_iter()
|
||||
.enumerate()
|
||||
.map(|(attr_id,attr)|
|
||||
(IntersectAttributesId::new(attr_id as u32),attr)
|
||||
).collect(),
|
||||
};
|
||||
self.data.bvh=bvh;
|
||||
self.data.models=models;
|
||||
self.data.modes=modes;
|
||||
//hitbox_mesh is unchanged
|
||||
println!("Physics Objects: {}",model_count);
|
||||
}
|
||||
|
||||
//tickless gaming
|
||||
@ -1134,19 +1234,22 @@ impl PhysicsContext{
|
||||
collector.collect(minkowski.predict_collision_in(relative_body,collector.time())
|
||||
//temp (?) code to avoid collision loops
|
||||
.map_or(None,|(face,time)|if time<=state.time{None}else{Some((face,time))})
|
||||
.map(|(face,time)|{
|
||||
TimedInstruction{time,instruction:PhysicsInternalInstruction::CollisionStart(match data.models.attr(convex_mesh_id.model_id){
|
||||
PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>Collision::Contact(ContactCollision{convex_mesh_id,face_id:face}),
|
||||
PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>Collision::Intersect(IntersectCollision{convex_mesh_id}),
|
||||
})}
|
||||
}));
|
||||
.map(|(face,time)|
|
||||
TimedInstruction{
|
||||
time,
|
||||
instruction:PhysicsInternalInstruction::CollisionStart(
|
||||
Collision::new(convex_mesh_id,face)
|
||||
)
|
||||
}
|
||||
)
|
||||
);
|
||||
});
|
||||
collector.instruction()
|
||||
}
|
||||
|
||||
|
||||
fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{
|
||||
let model_mesh=models.mesh(contact.convex_mesh_id);
|
||||
let model_mesh=models.contact_mesh(contact);
|
||||
let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh());
|
||||
minkowski.face_nd(contact.face_id).0
|
||||
}
|
||||
@ -1206,16 +1309,26 @@ fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,sty
|
||||
MoveState::Air
|
||||
}
|
||||
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 point=model.transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
|
||||
let transform=models.get_model_transform(mode.get_spawn_model_id(stage_id)?)?;
|
||||
let point=transform.vertex.transform_point3(Planar64Vec3::Y)+Planar64Vec3::Y*(style.hitbox.halfsize.y()+Planar64::ONE/16);
|
||||
Some(teleport(body,touching,models,style,hitbox_mesh,point))
|
||||
}
|
||||
|
||||
fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,models:&PhysicsModels,mode:&gameplay_modes::Mode,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,mode_state:&mut ModeState,touching:&mut TouchingState,body:&mut Body,convex_mesh_id:ConvexMeshId)->Option<MoveState>{
|
||||
fn run_teleport_behaviour(
|
||||
wormhole:&Option<gameplay_attributes::Wormhole>,
|
||||
models:&PhysicsModels,
|
||||
mode:&gameplay_modes::Mode,
|
||||
style:&StyleModifiers,
|
||||
hitbox_mesh:&HitboxMesh,
|
||||
mode_state:&mut ModeState,
|
||||
touching:&mut TouchingState,
|
||||
body:&mut Body,
|
||||
model_id:ModelId,
|
||||
)->Option<MoveState>{
|
||||
//TODO: jump count and checkpoints are always reset on teleport.
|
||||
//Map makers are expected to use tools to prevent
|
||||
//multi-boosting on JumpLimit boosters such as spawning into a SetVelocity
|
||||
if let Some(stage_element)=mode.get_element(convex_mesh_id.model_id.into()){
|
||||
if let Some(stage_element)=mode.get_element(model_id){
|
||||
if let Some(stage)=mode.get_stage(stage_element.stage_id()){
|
||||
if mode_state.get_stage_id()<stage_element.stage_id(){
|
||||
//checkpoint check
|
||||
@ -1253,19 +1366,19 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,models
|
||||
gameplay_modes::StageElementBehaviour::Checkpoint=>{
|
||||
//each of these checks if the model is actually a valid respective checkpoint object
|
||||
//accumulate sequential ordered checkpoints
|
||||
mode_state.accumulate_ordered_checkpoint(&stage,convex_mesh_id.model_id.into());
|
||||
mode_state.accumulate_ordered_checkpoint(&stage,model_id);
|
||||
//insert model id in accumulated unordered checkpoints
|
||||
mode_state.accumulate_unordered_checkpoint(&stage,convex_mesh_id.model_id.into());
|
||||
mode_state.accumulate_unordered_checkpoint(&stage,model_id);
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
match wormhole{
|
||||
&Some(gameplay_attributes::Wormhole{destination_model})=>{
|
||||
let origin_model=models.model(convex_mesh_id.model_id);
|
||||
let destination_model=models.model(destination_model.into());
|
||||
let origin=models.get_model_transform(model_id)?;
|
||||
let destination=models.get_model_transform(destination_model)?;
|
||||
//ignore the transform for now
|
||||
Some(teleport(body,touching,models,style,hitbox_mesh,body.position-origin_model.transform.vertex.translation+destination_model.transform.vertex.translation))
|
||||
Some(teleport(body,touching,models,style,hitbox_mesh,body.position-origin.vertex.translation+destination.vertex.translation))
|
||||
}
|
||||
None=>None,
|
||||
}
|
||||
@ -1274,8 +1387,7 @@ fn run_teleport_behaviour(wormhole:&Option<gameplay_attributes::Wormhole>,models
|
||||
fn collision_start_contact(
|
||||
state:&mut PhysicsState,
|
||||
data:&PhysicsData,
|
||||
contacting:&gameplay_attributes::ContactingAttributes,
|
||||
general:&gameplay_attributes::GeneralAttributes,
|
||||
attr:&gameplay_attributes::ContactAttributes,
|
||||
contact:ContactCollision,
|
||||
){
|
||||
let incident_velocity=state.body.velocity;
|
||||
@ -1283,7 +1395,7 @@ fn collision_start_contact(
|
||||
state.touching.insert(Collision::Contact(contact));
|
||||
//clip v
|
||||
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,incident_velocity);
|
||||
match &contacting.contact_behaviour{
|
||||
match &attr.contacting.contact_behaviour{
|
||||
Some(gameplay_attributes::ContactingBehaviour::Surf)=>println!("I'm surfing!"),
|
||||
Some(gameplay_attributes::ContactingBehaviour::Cling)=>println!("Unimplemented!"),
|
||||
&Some(gameplay_attributes::ContactingBehaviour::Elastic(elasticity))=>{
|
||||
@ -1315,16 +1427,16 @@ fn collision_start_contact(
|
||||
}
|
||||
//I love making functions with 10 arguments to dodge the borrow checker
|
||||
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){
|
||||
run_teleport_behaviour(&general.wormhole,&data.models,mode,&state.style,&data.hitbox_mesh,&mut state.mode_state,&mut state.touching,&mut state.body,contact.convex_mesh_id);
|
||||
run_teleport_behaviour(&attr.general.wormhole,&data.models,mode,&state.style,&data.hitbox_mesh,&mut state.mode_state,&mut state.touching,&mut state.body,contact.model_id.into());
|
||||
}
|
||||
if state.style.get_control(Controls::Jump,state.input_state.controls){
|
||||
if let (Some(jump_settings),Some(walk_state))=(&state.style.jump,state.move_state.get_walk_state()){
|
||||
let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact);
|
||||
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,general.booster.as_ref());
|
||||
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,attr.general.booster.as_ref());
|
||||
state.cull_velocity(data,jumped_velocity);
|
||||
}
|
||||
}
|
||||
match &general.trajectory{
|
||||
match &attr.general.trajectory{
|
||||
Some(trajectory)=>{
|
||||
match trajectory{
|
||||
gameplay_attributes::SetTrajectory::AirTime(_)=>todo!(),
|
||||
@ -1347,18 +1459,17 @@ fn collision_start_contact(
|
||||
fn collision_start_intersect(
|
||||
state:&mut PhysicsState,
|
||||
data:&PhysicsData,
|
||||
intersecting:&gameplay_attributes::IntersectingAttributes,
|
||||
general:&gameplay_attributes::GeneralAttributes,
|
||||
attr:&gameplay_attributes::IntersectAttributes,
|
||||
intersect:IntersectCollision,
|
||||
){
|
||||
//I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop
|
||||
state.touching.insert(Collision::Intersect(intersect));
|
||||
//insta booster!
|
||||
if let Some(booster)=&general.booster{
|
||||
if let Some(booster)=&attr.general.booster{
|
||||
state.cull_velocity(data,booster.boost(state.body.velocity));
|
||||
}
|
||||
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){
|
||||
let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into());
|
||||
let zone=mode.get_zone(intersect.model_id.into());
|
||||
match zone{
|
||||
Some(gameplay_modes::Zone::Start)=>{
|
||||
println!("@@@@ Starting new run!");
|
||||
@ -1373,15 +1484,14 @@ fn collision_start_intersect(
|
||||
Some(gameplay_modes::Zone::Anticheat)=>state.run.flag(run::FlagReason::Anticheat),
|
||||
None=>(),
|
||||
}
|
||||
run_teleport_behaviour(&general.wormhole,&data.models,mode,&state.style,&data.hitbox_mesh,&mut state.mode_state,&mut state.touching,&mut state.body,intersect.convex_mesh_id);
|
||||
run_teleport_behaviour(&attr.general.wormhole,&data.models,mode,&state.style,&data.hitbox_mesh,&mut state.mode_state,&mut state.touching,&mut state.body,intersect.model_id.into());
|
||||
}
|
||||
}
|
||||
|
||||
fn collision_end_contact(
|
||||
state:&mut PhysicsState,
|
||||
data:&PhysicsData,
|
||||
_contacting:&gameplay_attributes::ContactingAttributes,
|
||||
_general:&gameplay_attributes::GeneralAttributes,
|
||||
_attr:&gameplay_attributes::ContactAttributes,
|
||||
contact:ContactCollision,
|
||||
){
|
||||
state.touching.remove(&Collision::Contact(contact));//remove contact before calling contact_constrain_acceleration
|
||||
@ -1399,13 +1509,12 @@ fn collision_end_contact(
|
||||
fn collision_end_intersect(
|
||||
state:&mut PhysicsState,
|
||||
data:&PhysicsData,
|
||||
_intersecting:&gameplay_attributes::IntersectingAttributes,
|
||||
_general:&gameplay_attributes::GeneralAttributes,
|
||||
_attr:&gameplay_attributes::IntersectAttributes,
|
||||
intersect:IntersectCollision,
|
||||
){
|
||||
state.touching.remove(&Collision::Intersect(intersect));
|
||||
if let Some(mode)=data.modes.get_mode(state.mode_state.get_mode_id()){
|
||||
let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into());
|
||||
let zone=mode.get_zone(intersect.model_id.into());
|
||||
match zone{
|
||||
Some(gameplay_modes::Zone::Start)=>{
|
||||
match state.run.start(state.time){
|
||||
@ -1429,23 +1538,13 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
|
||||
state.body.advance_time(state.time);
|
||||
}
|
||||
match ins.instruction{
|
||||
PhysicsInternalInstruction::CollisionStart(collision)=>{
|
||||
match (data.models.attr(collision.convex_mesh_id().model_id),&collision){
|
||||
(PhysicsCollisionAttributes::Contact{contacting,general},&Collision::Contact(contact))=>
|
||||
collision_start_contact(state,data,contacting,general,contact),
|
||||
(PhysicsCollisionAttributes::Intersect{intersecting,general},&Collision::Intersect(intersect))=>
|
||||
collision_start_intersect(state,data,intersecting,general,intersect),
|
||||
_=>panic!("invalid pair"),
|
||||
}
|
||||
PhysicsInternalInstruction::CollisionStart(collision)=>match collision{
|
||||
Collision::Contact(contact)=>collision_start_contact(state,data,data.models.contact_attr(contact.model_id),contact),
|
||||
Collision::Intersect(intersect)=>collision_start_intersect(state,data,data.models.intersect_attr(intersect.model_id),intersect),
|
||||
},
|
||||
PhysicsInternalInstruction::CollisionEnd(collision)=>{
|
||||
match (data.models.attr(collision.convex_mesh_id().model_id),&collision){
|
||||
(PhysicsCollisionAttributes::Contact{contacting,general},&Collision::Contact(contact))=>
|
||||
collision_end_contact(state,data,contacting,general,contact),
|
||||
(PhysicsCollisionAttributes::Intersect{intersecting,general},&Collision::Intersect(intersect))=>
|
||||
collision_end_intersect(state,data,intersecting,general,intersect),
|
||||
_=>panic!("invalid pair"),
|
||||
}
|
||||
PhysicsInternalInstruction::CollisionEnd(collision)=>match collision{
|
||||
Collision::Contact(contact)=>collision_end_contact(state,data,data.models.contact_attr(contact.model_id),contact),
|
||||
Collision::Intersect(intersect)=>collision_end_intersect(state,data,data.models.intersect_attr(intersect.model_id),intersect),
|
||||
},
|
||||
PhysicsInternalInstruction::StrafeTick=>{
|
||||
//TODO make this less huge
|
||||
@ -1550,10 +1649,7 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
if let Some(walk_state)=state.move_state.get_walk_state(){
|
||||
if let Some(jump_settings)=&state.style.jump{
|
||||
let jump_dir=walk_state.jump_direction.direction(&data.models,&data.hitbox_mesh,&walk_state.contact);
|
||||
let booster_option=match data.models.attr(walk_state.contact.convex_mesh_id.model_id){
|
||||
PhysicsCollisionAttributes::Contact{contacting:_,general}=>general.booster.as_ref(),
|
||||
PhysicsCollisionAttributes::Intersect{..}=>None,
|
||||
};
|
||||
let booster_option=data.models.contact_attr(walk_state.contact.model_id).general.booster.as_ref();
|
||||
let jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity,booster_option);
|
||||
state.cull_velocity(&data,jumped_velocity);
|
||||
}
|
||||
@ -1571,10 +1667,12 @@ fn atomic_input_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedI
|
||||
},
|
||||
PhysicsInputInstruction::Restart=>{
|
||||
//teleport to start zone
|
||||
let spawn_point=data.modes.get_mode(state.mode_state.get_mode_id()).map(|mode|
|
||||
let spawn_point=data.modes.get_mode(state.mode_state.get_mode_id()).and_then(|mode|
|
||||
//TODO: spawn at the bottom of the start zone plus the hitbox size
|
||||
//TODO: set camera andles to face the same way as the start zone
|
||||
data.models.model(mode.get_start().into()).transform.vertex.translation
|
||||
data.models.get_model_transform(mode.get_start().into()).map(|transform|
|
||||
transform.vertex.translation
|
||||
)
|
||||
).unwrap_or(Planar64Vec3::ZERO);
|
||||
set_position(&mut state.body,&mut state.touching,spawn_point);
|
||||
set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);
|
||||
|
Loading…
Reference in New Issue
Block a user