From fb47b09925c626b8ad7f818875f9b6571c9c47fc Mon Sep 17 00:00:00 2001 From: Quaternions Date: Wed, 21 Feb 2024 01:32:03 -0800 Subject: [PATCH] wip --- src/physics.rs | 188 ++++++++++++++++++++++++++----------------------- 1 file changed, 99 insertions(+), 89 deletions(-) diff --git a/src/physics.rs b/src/physics.rs index 9ae6c4f..18e61b3 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -138,7 +138,7 @@ impl JumpDirection{ } } } -enum WalkTarget{ +enum TransientAcceleration{ Reached, Reachable{ acceleration:Planar64Vec3, @@ -149,47 +149,53 @@ enum WalkTarget{ acceleration:Planar64Vec3, } } -struct WalkState{ +struct ContactMoveState{ jump_direction:JumpDirection, contact:ContactCollision, - target:WalkTarget, + target:TransientAcceleration, } -impl WalkTarget{ +impl TransientAcceleration{ fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{ if target_diff==Planar64Vec3::ZERO{ - WalkTarget::Reached + TransientAcceleration::Reached }else{ //normal friction acceleration is clippedAcceleration.dot(normal)*friction - WalkTarget::Reachable{ + TransientAcceleration::Reachable{ acceleration:target_diff.with_length(accel), time:time+Time::from(target_diff.length()/accel) } } } - fn acceleration(&self)->Planar64Vec3{ - match self{ - WalkTarget::Reached=>Planar64Vec3::ZERO, - &WalkTarget::Reachable{acceleration,time:_}=>acceleration, - &WalkTarget::Unreachable{acceleration}=>acceleration, - } - } -} -impl WalkState{ - fn ground(body:&Body,walk_settings:&gameplay_style::WalkSettings,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ + fn ground(walk_settings:&gameplay_style::WalkSettings,body:&Body,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ let target_diff=velocity-body.velocity; //precalculate accel let accel=walk_settings.accel(target_diff,gravity); + Self::with_target_diff(target_diff,accel,body.time) + } + fn ladder(ladder_settings:&gameplay_style::LadderSettings,body:&Body,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ + let target_diff=velocity-body.velocity; + let accel=ladder_settings.accel(target_diff,gravity); + Self::with_target_diff(target_diff,accel,body.time) + } + fn acceleration(&self)->Planar64Vec3{ + match self{ + TransientAcceleration::Reached=>Planar64Vec3::ZERO, + &TransientAcceleration::Reachable{acceleration,time:_}=>acceleration, + &TransientAcceleration::Unreachable{acceleration}=>acceleration, + } + } +} +impl ContactMoveState{ + fn ground(walk_settings:&gameplay_style::WalkSettings,body:&Body,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ Self{ - target:WalkTarget::with_target_diff(target_diff,accel,body.time), + target:TransientAcceleration::ground(walk_settings,body,gravity,velocity,contact,normal), contact, jump_direction:JumpDirection::Exactly(Planar64Vec3::Y), } } - fn ladder(body:&Body,ladder_settings:&gameplay_style::LadderSettings,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ - let target_diff=velocity-body.velocity; - let accel=ladder_settings.accel(target_diff,gravity); + fn ladder(ladder_settings:&gameplay_style::LadderSettings,body:&Body,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:Planar64Vec3)->Self{ Self{//,style,velocity,normal,style.ladder_speed,style.ladder_accel - target:WalkTarget::with_target_diff(target_diff,accel,body.time), + target:TransientAcceleration::ladder(ladder_settings,body,gravity,velocity,contact,normal), contact, jump_direction:JumpDirection::FromContactNormal, } @@ -455,17 +461,17 @@ impl StyleHelper for StyleModifiers{ enum MoveState{ Air, - Walk(WalkState), + Walk(ContactMoveState), Water, - Ladder(WalkState), + Ladder(ContactMoveState), Fly, } impl MoveState{ - fn apply_to_body(&self,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&mut Body){ + fn apply_to_body(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ match self{ MoveState::Air=>{ //calculate base acceleration - let mut a=touching.base_acceleration(models, style, camera, input_state); + let mut a=touching.base_acceleration(models,style,camera,input_state); //clip according to contacts touching.constrain_acceleration(models,hitbox_mesh,&mut a); //something @@ -483,34 +489,30 @@ impl MoveState{ }, } } - /// Returns whether the MoveState was changed - fn apply_input(&mut self,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&mut Body){ + /// changes the move state and then applies it to body + fn apply_input(&mut self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ match self{ MoveState::Fly=>(), - MoveState::Air|MoveState::Water=>s.touching.base_acceleration(models,&s.style,&s.camera,&s.input_state), - MoveState::Walk(WalkState{target: state,contact,jump_direction:_})=>{ - let n=contact_normal(models,hitbox_mesh,contact); - let gravity=s.touching.base_acceleration(models,&s.style,&s.camera,&s.input_state); - let a; - let mut v=s.style.get_walk_target_velocity(&s.camera,&s.input_state,&n); - s.touching.constrain_velocity(models,hitbox_mesh,&mut v); - let normal_accel=-n.dot(gravity)/n.length(); - (*state,a)=WalkTarget::with_target_velocity(&s.body,&s.style,v,&n,s.style.walk_speed,normal_accel); - a + MoveState::Air|MoveState::Water=>(), + MoveState::Walk(ContactMoveState{target,contact,jump_direction:_})=>{ + if let Some(walk_settings)=&style.walk{ + *target=TransientAcceleration::ground(); + }else{ + panic!("ContactMoveState exists in style which does not allow walking!"); + } }, - MoveState::Ladder(WalkState{target: state,contact,jump_direction:_})=>{ - let n=contact_normal(models,hitbox_mesh,contact); - //let gravity=s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time); - let a; - let mut v=s.style.get_ladder_target_velocity(&s.camera,&s.input_state,&n); - s.touching.constrain_velocity(models,hitbox_mesh,&mut v); - (*state,a)=WalkTarget::with_target_velocity(&s.body,&s.style,v,&n,s.style.ladder_speed,s.style.ladder_accel); - a + MoveState::Ladder(ContactMoveState{target,contact,jump_direction:_})=>{ + if let Some(ladder_settings)=&style.ladder{ + *target=TransientAcceleration::ladder(); + }else{ + panic!("ContactMoveState exists in style which does not allow walking!"); + } }, } - self.apply_to_body(touching,models,hitbox_mesh,body); + //precalculate gravity??? + self.apply_to_body(body,touching,models,hitbox_mesh,style,camera,input_state); } - fn get_walk_state(&self)->Option<&WalkState>{ + fn get_walk_state(&self)->Option<&ContactMoveState>{ match self{ MoveState::Walk(walk_state) |MoveState::Ladder(walk_state) @@ -521,17 +523,25 @@ impl MoveState{ =>None, } } - fn next_move_instruction(&self)->Option>{ + fn next_move_instruction(&self,strafe:&Option,time:Time)->Option>{ //check if you have a valid walk state and create an instruction match self{ MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.target{ - WalkTarget::Reachable(walk_target)=>Some(TimedInstruction{ - time:walk_target.time, + &TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{ + time, instruction:PhysicsInstruction::ReachWalkTargetVelocity }), - WalkTarget::Reached=>None, + TransientAcceleration::Unreachable{acceleration:_} + |TransientAcceleration::Reached + =>None, } - MoveState::Air=>self.next_strafe_instruction(), + MoveState::Air=>strafe.as_ref().map(|strafe|{ + TimedInstruction{ + time:strafe.next_tick(time), + //only poll the physics if there is a before and after mouse event + instruction:PhysicsInstruction::StrafeTick + } + }), MoveState::Water=>None,//TODO MoveState::Fly=>None, } @@ -723,6 +733,7 @@ impl TouchingState{ } } } + //TODO: delete this or make it a constructor of MoveState fn get_move_state(&self,body:&Body,models:&PhysicsModels,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,camera:&PhysicsCamera,input_state:&InputState,time:Time)->(MoveState,Planar64Vec3){ //check current move conditions and use heuristics to determine //which ladder to climb on, which ground to walk on, etc @@ -740,7 +751,7 @@ impl TouchingState{ let control_dir=style.get_propulsion_control_dir(camera,input_state.controls); let mut target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal); self.constrain_velocity(models,hitbox_mesh,&mut target_velocity); - let (walk_state,mut acceleration)=WalkState::ladder(body,style,gravity,target_velocity,contact.clone(),&normal); + let (walk_state,mut acceleration)=ContactMoveState::ladder(body,style,gravity,target_velocity,contact.clone(),&normal); move_state=MoveState::Ladder(walk_state); self.constrain_acceleration(models,hitbox_mesh,&mut acceleration); a=acceleration; @@ -751,7 +762,7 @@ impl TouchingState{ let control_dir=style.get_y_control_dir(camera,input_state.controls); let mut target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal); self.constrain_velocity(models,hitbox_mesh,&mut target_velocity); - let (walk_state,mut acceleration)=WalkState::ground(body,style,gravity,target_velocity,contact.clone(),&normal); + let (walk_state,mut acceleration)=ContactMoveState::ground(style,gravity,body,target_velocity,contact.clone(),&normal); move_state=MoveState::Walk(walk_state); self.constrain_acceleration(models,hitbox_mesh,&mut acceleration); a=acceleration; @@ -966,15 +977,8 @@ impl PhysicsState { pub const fn get_next_mouse(&self)->&MouseState{ self.input_state.get_next_mouse() } - - fn next_strafe_instruction(&self)->Option>{ - self.style.strafe.as_ref().map(|strafe|{ - TimedInstruction{ - time:strafe.next_tick(self.time), - //only poll the physics if there is a before and after mouse event - instruction:PhysicsInstruction::StrafeTick - } - }) + fn next_move_instruction(&self)->Option>{ + self.move_state.next_move_instruction(&self.style.strafe,self.time) } //state mutated on collision: @@ -1304,7 +1308,8 @@ fn run_teleport_behaviour(wormhole:&Option,models let d=normal.dot(v)*(Planar64::ONE+Planar64::raw(elasticity as i64+1)); v+=normal*(d/normal.dot(normal)); }, - Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=>{ + Some(gameplay_attributes::ContactingBehaviour::Ladder(contacting_ladder))=> + if let Some(ladder_settings)=&state.style.ladder{ if contacting_ladder.sticky{ //kill v //actually you could do this with a booster attribute :thinking: @@ -1312,21 +1317,25 @@ fn run_teleport_behaviour(wormhole:&Option,models } //ladder walkstate let gravity=state.touching.base_acceleration(&data.models,&state.style,&state.camera,&state.input_state); - let mut target_velocity=state.style.get_ladder_target_velocity(&state.camera,&state.input_state,&normal); + let control_dir=state.style.get_y_control_dir(&state.camera,state.input_state.controls); + let mut target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal); state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut target_velocity); - let (walk_state,a)=WalkState::ladder(&state.body,&state.style,gravity,target_velocity,contact.clone(),&normal); + let walk_state=ContactMoveState::ladder(ladder_settings,&state.body,gravity,target_velocity,contact.clone(),normal); state.move_state=MoveState::Ladder(walk_state); - set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a); + state.move_state.apply_to_body(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state); }, Some(gameplay_attributes::ContactingBehaviour::NoJump)=>todo!("nyi"), - None=>if state.style.surf_slope.map_or(true,|s|contact_normal(&data.models,&data.hitbox_mesh,contact).walkable(s,Planar64Vec3::Y)){ - //ground - let gravity=state.touching.base_acceleration(&data.models,&state.style,&state.camera,&state.input_state); - let mut target_velocity=state.style.get_walk_target_velocity(&state.camera,&state.input_state,&normal); - state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut target_velocity); - let (walk_state,a)=WalkState::ground(&state.body,&state.style,gravity,target_velocity,contact.clone(),&normal); - state.move_state=MoveState::Walk(walk_state); - set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a); + None=>if let Some(walk_settings)=&state.style.walk{ + if walk_settings.is_slope_walkable(contact_normal(&data.models,&data.hitbox_mesh,contact),Planar64Vec3::Y){ + //ground + let gravity=state.touching.base_acceleration(&data.models,&state.style,&state.camera,&state.input_state); + let control_dir=state.style.get_y_control_dir(&state.camera,state.input_state.controls); + let mut target_velocity=walk_settings.get_walk_target_velocity(control_dir,normal); + state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut target_velocity); + let walk_state=ContactMoveState::ground(walk_settings,&state.body,gravity,target_velocity,contact.clone(),normal); + state.move_state=MoveState::Walk(walk_state); + state.move_state.apply_to_body(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state); + } }, } //check ground @@ -1375,9 +1384,7 @@ fn run_teleport_behaviour(wormhole:&Option,models if calc_move||Planar64::ZERO{ //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop @@ -1428,16 +1435,19 @@ fn run_teleport_behaviour(wormhole:&Option,models |MoveState::Fly =>println!("ReachWalkTargetVelocity fired for non-walking MoveState"), MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{ - match &mut walk_state.target{ - WalkTarget::Reached=>(), - WalkTarget::Reachable(walk_target)=>{ - //precisely set velocity - let a=Planar64Vec3::ZERO;//ignore gravity for now. - set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a); - let v=walk_target.velocity; - set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,v); - walk_state.target=WalkTarget::Reached; + match &walk_state.target{ + //you are not supposed to reach a walk target which is already reached! + TransientAcceleration::Reached=>unreachable!(), + TransientAcceleration::Reachable{acceleration,time}=>{ + //velocity is already handled by advance_time + //we know that the acceleration is precisely zero because the walk target is known to be reachable + //which means that gravity can be fully cancelled + //ignore moving platforms for now + set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO); + walk_state.target=TransientAcceleration::Reached; }, + //you are not supposed to reach an unreachable walk target! + TransientAcceleration::Unreachable{acceleration:_}=>unreachable!(), } } } @@ -1461,7 +1471,7 @@ fn run_teleport_behaviour(wormhole:&Option,models PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s), PhysicsInputInstruction::SetJump(s)=>{ state.input_state.set_control(Controls::Jump,s); - if let Some(walk_state)=get_walk_state(&state.move_state){ + 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 jumped_velocity=jump_settings.jumped_velocity(&state.style,jump_dir,state.body.velocity); @@ -1492,7 +1502,7 @@ fn run_teleport_behaviour(wormhole:&Option,models PhysicsInputInstruction::Idle=>{b_refresh_walk_target=false;},//literally idle! } if b_refresh_walk_target{ - state.move_state.apply_input(&mut state.body); + state.move_state.apply_input(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state); } }, }