diff --git a/engine/physics/src/physics.rs b/engine/physics/src/physics.rs index e8aa849e..585f15a5 100644 --- a/engine/physics/src/physics.rs +++ b/engine/physics/src/physics.rs @@ -87,7 +87,7 @@ enum JumpDirection{ impl JumpDirection{ fn direction(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ match self{ - JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,contact), + JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id), &JumpDirection::Exactly(dir)=>dir, } } @@ -163,7 +163,7 @@ impl ContactMoveState{ } } fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){ - let normal=contact_normal(models,hitbox_mesh,contact); + let normal=contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id); let gravity=touching.base_acceleration(models,style,camera,input_state); let control_dir=style.get_y_control_dir(camera,input_state.controls); let target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal); @@ -171,7 +171,7 @@ fn ground_things(walk_settings:&gameplay_style::WalkSettings,contact:&ContactCol (gravity,target_velocity_clipped) } fn ladder_things(ladder_settings:&gameplay_style::LadderSettings,contact:&ContactCollision,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->(Planar64Vec3,Planar64Vec3){ - let normal=contact_normal(models,hitbox_mesh,contact); + let normal=contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id); let gravity=touching.base_acceleration(models,style,camera,input_state); let control_dir=style.get_y_control_dir(camera,input_state.controls); let target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal); @@ -614,7 +614,7 @@ impl MoveState{ // TODO: unduplicate this code match self.get_walk_state(){ // did you stop touching the thing you were walking on? - Some(walk_state)=>if !touching.contacts.contains(&walk_state.contact){ + Some(walk_state)=>if !touching.contacts.contains_key(&walk_state.contact.convex_mesh_id){ self.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); }else{ // stopped touching something else while walking @@ -731,8 +731,8 @@ struct IntersectModel{ #[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] pub struct ContactCollision{ - face_id:model_physics::MinkowskiFace, convex_mesh_id:ConvexMeshId, + face_id:model_physics::MinkowskiFace, } #[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] pub struct IntersectCollision{ @@ -753,24 +753,30 @@ impl Collision{ } #[derive(Clone,Debug,Default)] struct TouchingState{ - contacts:HashSet, - intersects:HashSet, + contacts:HashMap,model_physics::MinkowskiFace>, + intersects:HashSet>, } impl TouchingState{ fn clear(&mut self){ self.contacts.clear(); self.intersects.clear(); } - fn insert(&mut self,collision:Collision)->bool{ - match collision{ - Collision::Contact(collision)=>self.contacts.insert(collision), - Collision::Intersect(collision)=>self.intersects.insert(collision), - } + fn insert_contact(&mut self,contact:ContactCollision)->Option{ + self.contacts.insert(contact.convex_mesh_id,contact.face_id) } - fn remove(&mut self,collision:&Collision)->bool{ - match collision{ - Collision::Contact(collision)=>self.contacts.remove(collision), - Collision::Intersect(collision)=>self.intersects.remove(collision), + fn insert_intersect(&mut self,intersect:IntersectCollision)->bool{ + self.intersects.insert(intersect.convex_mesh_id) + } + fn remove_contact(&mut self,convex_mesh_id:&ConvexMeshId)->Option{ + self.contacts.remove(convex_mesh_id) + } + fn remove_intersect(&mut self,convex_mesh_id:&ConvexMeshId)->bool{ + self.intersects.remove(convex_mesh_id) + } + fn contains(&self,convex_mesh_id:&ConvexMeshId)->bool{ + match convex_mesh_id.model_id{ + PhysicsModelId::Contact(contact_model_id)=>self.contacts.contains_key(&convex_mesh_id.map(contact_model_id)), + PhysicsModelId::Intersect(intersect_model_id)=>self.intersects.contains(&convex_mesh_id.map(intersect_model_id)), } } fn base_acceleration(&self,models:&PhysicsModels,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->Planar64Vec3{ @@ -779,13 +785,13 @@ impl TouchingState{ a+=rocket_settings.acceleration(style.get_propulsion_control_dir(camera,input_state.controls)); } //add accelerators - for contact in &self.contacts{ - if let Some(accelerator)=&models.contact_attr(contact.convex_mesh_id.model_id).general.accelerator{ + for contact in self.contacts.keys(){ + if let Some(accelerator)=&models.contact_attr(contact.model_id).general.accelerator{ a+=accelerator.acceleration; } } for intersect in &self.intersects{ - if let Some(accelerator)=&models.intersect_attr(intersect.convex_mesh_id.model_id).general.accelerator{ + if let Some(accelerator)=&models.intersect_attr(intersect.model_id).general.accelerator{ a+=accelerator.acceleration; } } @@ -793,8 +799,8 @@ impl TouchingState{ a } fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:Planar64Vec3)->Planar64Vec3{ - let contacts:Vec<_>=self.contacts.iter().map(|contact|{ - let n=contact_normal(models,hitbox_mesh,contact); + let contacts:Vec<_>=self.contacts.iter().map(|(convex_mesh_id,face_id)|{ + let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id); crate::push_solve::Contact{ position:vec3::ZERO, velocity:n, @@ -804,8 +810,8 @@ impl TouchingState{ crate::push_solve::push_solve(&contacts,velocity) } fn constrain_acceleration(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,acceleration:Planar64Vec3)->Planar64Vec3{ - let contacts:Vec<_>=self.contacts.iter().map(|contact|{ - let n=contact_normal(models,hitbox_mesh,contact); + let contacts:Vec<_>=self.contacts.iter().map(|(convex_mesh_id,face_id)|{ + let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id); crate::push_solve::Contact{ position:vec3::ZERO, velocity:n, @@ -817,29 +823,29 @@ impl TouchingState{ fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,start_time:Time){ // let relative_body=body.relative_to(&Body::ZERO); let relative_body=body; - for contact in &self.contacts{ + for (convex_mesh_id,face_id) in &self.contacts{ //detect face slide off - let model_mesh=models.contact_mesh(&contact.convex_mesh_id); + let model_mesh=models.contact_mesh(convex_mesh_id); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); - collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),contact.face_id).map(|(_face,time)|{ + collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..collector.time(),*face_id).map(|(_face,time)|{ TimedInstruction{ time:relative_body.time+time.into(), instruction:InternalInstruction::CollisionEnd( - Collision::Contact(*contact), + Collision::Contact(ContactCollision{face_id:*face_id,convex_mesh_id:*convex_mesh_id}), time ), } })); } - for intersect in &self.intersects{ + for convex_mesh_id in &self.intersects{ //detect model collision in reverse - let model_mesh=models.intersect_mesh(&intersect.convex_mesh_id); + let model_mesh=models.intersect_mesh(convex_mesh_id); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); collector.collect(minkowski.predict_collision_out(&relative_body,start_time..collector.time()).map(|(_face,time)|{ TimedInstruction{ time:relative_body.time+time.into(), instruction:InternalInstruction::CollisionEnd( - Collision::Intersect(*intersect), + Collision::Intersect(IntersectCollision{convex_mesh_id:*convex_mesh_id}), time ), } @@ -1193,21 +1199,19 @@ impl<'a> PhysicsContext<'a>{ //relative to moving platforms //let relative_body=state.body.relative_to(&Body::ZERO); let relative_body=&state.body; - data.bvh.sample_aabb(&aabb,&mut |&convex_mesh_id|{ + data.bvh.sample_aabb(&aabb,&mut |convex_mesh_id|{ + if state.touching.contains(convex_mesh_id){ + return; + } //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()); collector.collect(minkowski.predict_collision_in(relative_body,state.time..collector.time()) - //temp (?) code to avoid collision loops - .and_then(|(face,dt)|{ - // this must be rounded to avoid the infinite loop when hitting the start zone - let time=relative_body.time+dt.into(); - (state.time PhysicsContext<'a>{ } -fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ - let model_mesh=models.contact_mesh(&contact.convex_mesh_id); +fn contact_normal( + models:&PhysicsModels, + hitbox_mesh:&HitboxMesh, + convex_mesh_id:&ConvexMeshId, + face_id:model_physics::MinkowskiFace, +)->Planar64Vec3{ + let model_mesh=models.contact_mesh(convex_mesh_id); let minkowski=model_physics::MinkowskiMesh::minkowski_sum(model_mesh,hitbox_mesh.transformed_mesh()); // TODO: normalize to i64::MAX>>1 // wrap for speed - minkowski.face_nd(contact.face_id).0.wrap_1() + minkowski.face_nd(face_id).0.wrap_1() } fn recalculate_touching( @@ -1244,11 +1253,11 @@ fn recalculate_touching( //collision_end all existing contacts //I would have preferred while let Some(contact)=contacts.pop() //but there is no such method - while let Some(&contact)=touching.contacts.iter().next(){ - collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(contact.convex_mesh_id.model_id),contact) + while let Some((convex_mesh_id,_face_id))=touching.contacts.iter().next(){ + collision_end_contact(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,models.contact_attr(convex_mesh_id.model_id),*convex_mesh_id) } - while let Some(&intersect)=touching.intersects.iter().next(){ - collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(intersect.convex_mesh_id.model_id),intersect,time); + while let Some(convex_mesh_id)=touching.intersects.iter().next(){ + collision_end_intersect(move_state,body,touching,models,hitbox_mesh,style,camera,input_state,mode,run,models.intersect_attr(convex_mesh_id.model_id),*convex_mesh_id,time); } //find all models in the teleport region let mut aabb=aabb::Aabb::default(); @@ -1304,8 +1313,8 @@ fn set_position( fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,v:Planar64Vec3)->bool{ //This is not correct but is better than what I have let mut culled=false; - touching.contacts.retain(|contact|{ - let n=contact_normal(models,hitbox_mesh,contact); + touching.contacts.retain(|convex_mesh_id,face_id|{ + let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id); let r=n.dot(v).is_positive(); if r{ culled=true; @@ -1322,8 +1331,8 @@ fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hit fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,a:Planar64Vec3)->bool{ //This is not correct but is better than what I have let mut culled=false; - touching.contacts.retain(|contact|{ - let n=contact_normal(models,hitbox_mesh,contact); + touching.contacts.retain(|convex_mesh_id,face_id|{ + let n=contact_normal(models,hitbox_mesh,convex_mesh_id,*face_id); let r=n.dot(a).is_positive(); if r{ culled=true; @@ -1536,7 +1545,7 @@ fn collision_start_contact( ){ let incident_velocity=body.velocity; //add to touching - touching.insert(Collision::Contact(contact)); + touching.insert_contact(contact); //clip v set_velocity(body,touching,models,hitbox_mesh,incident_velocity); let mut allow_jump=true; @@ -1564,7 +1573,7 @@ fn collision_start_contact( }, Some(gameplay_attributes::ContactingBehaviour::NoJump)=>allow_jump=false, None=>if let Some(walk_settings)=&style.walk{ - if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact),vec3::Y){ + if walk_settings.is_slope_walkable(contact_normal(models,hitbox_mesh,&contact.convex_mesh_id,contact.face_id),vec3::Y){ allow_run_teleport_behaviour=true; //ground let (gravity,target_velocity)=ground_things(walk_settings,&contact,touching,models,hitbox_mesh,style,camera,input_state); @@ -1638,7 +1647,7 @@ fn collision_start_intersect( time:Time, ){ //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop - touching.insert(Collision::Intersect(intersect)); + touching.insert_intersect(intersect); //insta booster! if let Some(booster)=&attr.general.booster{ move_state.cull_velocity(booster.boost(body.velocity),body,touching,models,hitbox_mesh,style,camera,input_state); @@ -1674,15 +1683,17 @@ fn collision_end_contact( camera:&PhysicsCamera, input_state:&InputState, _attr:&gameplay_attributes::ContactAttributes, - contact:ContactCollision, + convex_mesh_id:ConvexMeshId, ){ - touching.remove(&Collision::Contact(contact));//remove contact before calling contact_constrain_acceleration + touching.remove_contact(&convex_mesh_id);//remove contact before calling contact_constrain_acceleration //check ground //TODO do better //this is inner code from move_state.cull_velocity match move_state.get_walk_state(){ // did you stop touching the thing you were walking on? - Some(walk_state)=>if walk_state.contact==contact{ + // This does not check the face! Is that a bad thing? It should be + // impossible to stop touching a different face than you started touching... + Some(walk_state)=>if walk_state.contact.convex_mesh_id==convex_mesh_id{ move_state.set_move_state(MoveState::Air,body,touching,models,hitbox_mesh,style,camera,input_state); }else{ // stopped touching something else while walking @@ -1704,13 +1715,13 @@ fn collision_end_intersect( mode:Option<&gameplay_modes::Mode>, run:&mut run::Run, _attr:&gameplay_attributes::IntersectAttributes, - intersect:IntersectCollision, + convex_mesh_id:ConvexMeshId, time:Time, ){ - touching.remove(&Collision::Intersect(intersect)); + touching.remove_intersect(&convex_mesh_id); move_state.apply_enum_and_body(body,touching,models,hitbox_mesh,style,camera,input_state); if let Some(mode)=mode{ - let zone=mode.get_zone(intersect.convex_mesh_id.model_id.into()); + let zone=mode.get_zone(convex_mesh_id.model_id.into()); match zone{ Some(gameplay_modes::Zone::Start)=>{ match run.start(time){ @@ -1762,14 +1773,14 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim Collision::Contact(contact)=>collision_end_contact( &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, data.models.contact_attr(contact.convex_mesh_id.model_id), - contact + contact.convex_mesh_id ), Collision::Intersect(intersect)=>collision_end_intersect( &mut state.move_state,&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state, data.modes.get_mode(state.mode_state.get_mode_id()), &mut state.run, data.models.intersect_attr(intersect.convex_mesh_id.model_id), - intersect, + intersect.convex_mesh_id, state.time ), },