Fix infinite loop with intersects when allowing 0s collisions #21

Closed
aidan9382 wants to merge 8 commits from aidan9382/strafe-project:fix-intersect-loop into bug3-real
3 changed files with 42 additions and 23 deletions

View File

@@ -752,7 +752,22 @@ impl MinkowskiMesh<'_>{
infinity_body.velocity=dir;
infinity_body.acceleration=vec3::ZERO;
//crawl in from negative infinity along a tangent line to get the closest fev
infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,start_time).miss()
match infinity_fev.crawl(self,&infinity_body,Bound::Unbounded,start_time){
// This is the expected case.
// We expect to never hit the mesh while setting up for the real crawl
// since the algorithm breaks down on the inside of the mesh.
crate::face_crawler::CrawlResult::Miss(fev)=>Some(fev),
// An exact hit is allowed, it has not crossed the boundary.
crate::face_crawler::CrawlResult::Hit(face,ratio)=>match start_time{
Bound::Included(_)=>ratio.num.is_zero().then(||FEV::Face(face)),
// You are looking for collision events within a range that does not include the start_time.
// The boundary is crossed at exactly start_time, so the range is not met.
// Therefore, the correct return value is None.
Bound::Excluded(_)=>unimplemented!(),
// To infinity and beyond!
Bound::Unbounded=>None,
},
}
})
}
pub fn predict_collision_in(&self,relative_body:&Body,range:impl RangeBounds<Time>)->Option<(MinkowskiFace,GigaTime)>{

View File

@@ -28,6 +28,7 @@ pub enum InternalInstruction{
CollisionStart(Collision,model_physics::GigaTime),
CollisionEnd(Collision,model_physics::GigaTime),
StrafeTick,
// TODO: add GigaTime to ReachWalkTargetVelocity
ReachWalkTargetVelocity,
// Water,
}
@@ -802,7 +803,7 @@ impl TouchingState{
//detect face slide off
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,start_time..collector.time(),contact.face_id).map(|(_face,time)|{
collector.collect(minkowski.predict_collision_face_out(&relative_body,start_time..=collector.time(),contact.face_id).map(|(_face,time)|{
TimedInstruction{
time:relative_body.time+time.into(),
instruction:InternalInstruction::CollisionEnd(
@@ -816,7 +817,7 @@ impl TouchingState{
//detect model collision in reverse
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,start_time..collector.time()).map(|(_face,time)|{
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(
@@ -1150,7 +1151,7 @@ impl<'a> PhysicsContext<'a>{
//JUST POLLING!!! NO MUTATION
let mut collector=instruction::InstructionCollector::new(time_limit);
collector.collect(state.next_move_instruction());
// collector.collect(state.next_move_instruction());
//check for collision ends
state.touching.predict_collision_end(&mut collector,&data.models,&data.hitbox_mesh,&state.body,state.time);
@@ -1165,17 +1166,19 @@ impl<'a> PhysicsContext<'a>{
//no checks are needed because of the time limits.
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
collector.collect(minkowski.predict_collision_in(relative_body,state.time..=collector.time())
.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<time).then_some((time,face,dt))
}).map(|(time,face,dt)|
// prevent active intersects from repeatedly getting picked up
let collision=Collision::new(convex_mesh_id,face);
match collision{
Collision::Contact(contact)=>(!state.touching.contacts.contains(&contact)).then_some((collision,dt)),
Collision::Intersect(intersect)=>(!state.touching.intersects.contains(&intersect)).then_some((collision,dt)),
}
}).map(|(collision,dt)|
TimedInstruction{
time,
time:relative_body.time+dt.into(),
instruction:InternalInstruction::CollisionStart(
Collision::new(convex_mesh_id,face),
collision,
dt
)
}
@@ -1693,17 +1696,14 @@ fn collision_end_intersect(
}
fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:TimedInstruction<InternalInstruction,Time>){
state.time=ins.time;
let (should_advance_body,goober_time)=match ins.instruction{
match ins.instruction{
// collisions advance the body precisely
InternalInstruction::CollisionStart(_,dt)
|InternalInstruction::CollisionEnd(_,dt)=>(true,Some(dt)),
InternalInstruction::StrafeTick
|InternalInstruction::ReachWalkTargetVelocity=>(true,None),
};
if should_advance_body{
match goober_time{
Some(dt)=>state.body.advance_time_ratio_dt(dt),
None=>state.body.advance_time(state.time),
}
|InternalInstruction::CollisionEnd(_,dt)=>state.body.advance_time_ratio_dt(dt),
// this advances imprecisely
InternalInstruction::ReachWalkTargetVelocity=>state.body.advance_time(state.time),
// strafe tick decides for itself whether to advance the body.
InternalInstruction::StrafeTick=>(),
}
match ins.instruction{
InternalInstruction::CollisionStart(collision,_)=>{
@@ -1750,6 +1750,8 @@ fn atomic_internal_instruction(state:&mut PhysicsState,data:&PhysicsData,ins:Tim
let masked_controls=strafe_settings.mask(controls);
let control_dir=state.style.get_control_dir(masked_controls);
if control_dir!=vec3::ZERO{
// manually advance time
state.body.advance_time(state.time);
let camera_mat=state.camera.simulate_move_rotation_y(state.input_state.lerp_delta(state.time).x);
if let Some(ticked_velocity)=strafe_settings.tick_velocity(state.body.velocity,(camera_mat*control_dir).with_length(Planar64::ONE).divide().wrap_1()){
//this is wrong but will work ig

View File

@@ -106,7 +106,9 @@ fn bug_3(){
.filter(|ins|!matches!(ins.instruction,InternalInstruction::StrafeTick));
// touch side of part at 0,0,0
assert_eq!(phys_iter.next().unwrap().time,Time::from_secs(1));
// touch top of part at 5,-5,0
// EXPECTED: touch top of part at 5,-5,0
// OBSERVED: CollisionEnd part at 0,0,0; clip through part at 5,-5,0
assert_eq!(phys_iter.next().unwrap().time,Time::from_secs(2));
assert_eq!(phys_iter.next().unwrap().time,Time::from_secs(2));
assert!(phys_iter.next().is_none());
let body=physics.body();