diff --git a/src/graphics.rs b/src/graphics.rs index b94a84e..d6ec94c 100644 --- a/src/graphics.rs +++ b/src/graphics.rs @@ -798,7 +798,7 @@ impl GraphicsState{ }); let camera=GraphicsCamera::default(); - let camera_uniforms=camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(glam::IVec2::ZERO,integer::Time::ZERO)); + let camera_uniforms=camera.to_uniform_data(crate::physics::PhysicsOutputState::default().extrapolate(crate::physics::MouseState::default())); let camera_buf=device.create_buffer_init(&wgpu::util::BufferInitDescriptor{ label:Some("Camera"), contents:bytemuck::cast_slice(&camera_uniforms), @@ -875,7 +875,7 @@ impl GraphicsState{ let mut encoder=device.create_command_encoder(&wgpu::CommandEncoderDescriptor{label:None}); // update rotation - let camera_uniforms=self.camera.to_uniform_data(physics_output.extrapolate(mouse_pos,predicted_time)); + let camera_uniforms=self.camera.to_uniform_data(physics_output.extrapolate(crate::physics::MouseState{pos:mouse_pos,time:predicted_time})); self.staging_belt .write_buffer( &mut encoder, diff --git a/src/physics.rs b/src/physics.rs index d0a3cad..7739c9f 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -4,10 +4,11 @@ use crate::model_physics::{self,PhysicsMesh,PhysicsMeshTransform,TransformedMesh use strafesnet_common::bvh; use strafesnet_common::map; use strafesnet_common::aabb; -use strafesnet_common::gameplay_modes::{self,StageId}; -use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId}; use strafesnet_common::model::{MeshId,ModelId}; +use strafesnet_common::gameplay_attributes::{self,CollisionAttributesId}; +use strafesnet_common::gameplay_modes::{self,StageId}; use strafesnet_common::gameplay_style::{self,StyleModifiers}; +use strafesnet_common::controls_bitflag::Controls; use strafesnet_common::instruction::{self,InstructionEmitter,InstructionConsumer,TimedInstruction}; use strafesnet_common::integer::{self,Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64Vec2}; use gameplay::ModeState; @@ -26,6 +27,7 @@ pub enum PhysicsInstruction { // ) //InputInstructions conditionally activate RefreshWalkTarget (by doing what SetWalkTargetVelocity used to do and then flagging it) Input(PhysicsInputInstruction), + SetSensitivity(Ratio64Vec2), } #[derive(Debug)] pub enum PhysicsInputInstruction { @@ -44,9 +46,10 @@ pub enum PhysicsInputInstruction { //Idle: there were no input events, but the simulation is safe to advance to this timestep //for interpolation / networking / playback reasons, most playback heads will always want //to be 1 instruction ahead to generate the next state for interpolation. + PracticeFly, } -#[derive(Clone,Hash,Default)] +#[derive(Clone,Copy,Debug,Default,Hash)] pub struct Body{ pub position:Planar64Vec3,//I64 where 2^32 = 1 u pub velocity:Planar64Vec3,//I64 where 2^32 = 1 u/s @@ -91,65 +94,133 @@ impl MouseState { } } +#[derive(Clone,Debug,Default)] +pub struct InputState{ + mouse:MouseState, + next_mouse:MouseState, + controls:strafesnet_common::controls_bitflag::Controls, +} +impl InputState{ + pub const fn get_next_mouse(&self)->&MouseState{ + &self.next_mouse + } + fn set_next_mouse(&mut self,next_mouse:MouseState){ + (self.next_mouse,self.mouse)=(next_mouse,self.next_mouse.clone()); + } + fn replace_mouse(&mut self,mouse:MouseState,next_mouse:MouseState){ + (self.next_mouse,self.mouse)=(next_mouse,mouse); + } + fn set_control(&mut self,control:Controls,state:bool){ + self.controls.set(control,state) + } + fn time_delta(&self)->Time{ + self.next_mouse.time-self.mouse.time + } + fn mouse_delta(&self)->glam::IVec2{ + self.next_mouse.pos-self.mouse.pos + } + fn lerp_delta(&self,time:Time)->glam::IVec2{ + //these are deltas + let dm=self.mouse_delta().as_i64vec2(); + let t=(time-self.mouse.time).nanos(); + let dt=self.time_delta().nanos(); + ((dm*t)/dt).as_ivec2() + } +} +#[derive(Clone,Debug)] enum JumpDirection{ Exactly(Planar64Vec3), FromContactNormal, } -enum WalkEnum{ - Reached, - Transient(WalkTarget), -} -struct WalkTarget{ - velocity:Planar64Vec3, - time:Time, -} -struct WalkState{ - jump_direction:JumpDirection, - contact:ContactCollision, - state:WalkEnum, -} -impl WalkEnum{ - //args going crazy - //(walk_enum,body.acceleration)=with_target_velocity(); - fn with_target_velocity(body:&Body,style:&StyleModifiers,velocity:Planar64Vec3,normal:&Planar64Vec3,speed:Planar64,normal_accel:Planar64)->(WalkEnum,Planar64Vec3){ - let mut target_diff=velocity-body.velocity; - //remove normal component - target_diff-=normal.clone()*(normal.dot(target_diff)/normal.dot(normal.clone())); - if target_diff==Planar64Vec3::ZERO{ - (WalkEnum::Reached,Planar64Vec3::ZERO) - }else{ - //normal friction acceleration is clippedAcceleration.dot(normal)*friction - let diff_len=target_diff.length(); - let friction=if diff_lenPlanar64Vec3{ + match self{ + JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,contact), + &JumpDirection::Exactly(dir)=>dir, } } } -impl WalkState{ - fn ground(body:&Body,style:&StyleModifiers,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:&Planar64Vec3)->(Self,Planar64Vec3){ - let (walk_enum,a)=WalkEnum::with_target_velocity(body,style,velocity,&Planar64Vec3::Y,style.walk_speed,-normal.dot(gravity)); - (Self{ - state:walk_enum, +#[derive(Clone,Debug)] +enum TransientAcceleration{ + Reached, + Reachable{ + acceleration:Planar64Vec3, + time:Time, + }, + //walk target will never be reached + Unreachable{ + acceleration:Planar64Vec3, + } +} +#[derive(Clone,Debug)] +struct ContactMoveState{ + jump_direction:JumpDirection, + contact:ContactCollision, + target:TransientAcceleration, +} +impl TransientAcceleration{ + fn with_target_diff(target_diff:Planar64Vec3,accel:Planar64,time:Time)->Self{ + if target_diff==Planar64Vec3::ZERO{ + TransientAcceleration::Reached + }else{ + //normal friction acceleration is clippedAcceleration.dot(normal)*friction + TransientAcceleration::Reachable{ + acceleration:target_diff.with_length(accel), + time:time+Time::from(target_diff.length()/accel) + } + } + } + fn ground(walk_settings:&gameplay_style::WalkSettings,body:&Body,gravity:Planar64Vec3,target_velocity:Planar64Vec3)->Self{ + let target_diff=target_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,target_velocity:Planar64Vec3)->Self{ + let target_diff=target_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,target_velocity:Planar64Vec3,contact:ContactCollision)->Self{ + Self{ + target:TransientAcceleration::ground(walk_settings,body,gravity,target_velocity), contact, jump_direction:JumpDirection::Exactly(Planar64Vec3::Y), - },a) + } } - fn ladder(body:&Body,style:&StyleModifiers,gravity:Planar64Vec3,velocity:Planar64Vec3,contact:ContactCollision,normal:&Planar64Vec3)->(Self,Planar64Vec3){ - let (walk_enum,a)=WalkEnum::with_target_velocity(body,style,velocity,normal,style.ladder_speed,style.ladder_accel); - (Self{ - state:walk_enum, + fn ladder(ladder_settings:&gameplay_style::LadderSettings,body:&Body,gravity:Planar64Vec3,target_velocity:Planar64Vec3,contact:ContactCollision)->Self{ + Self{//,style,velocity,normal,style.ladder_speed,style.ladder_accel + target:TransientAcceleration::ladder(ladder_settings,body,gravity,target_velocity), contact, jump_direction:JumpDirection::FromContactNormal, - },a) + } } } +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 gravity=touching.base_acceleration(models,style,camera,input_state); + 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); + touching.constrain_velocity(models,hitbox_mesh,&mut target_velocity); + (gravity,target_velocity) +} +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 gravity=touching.base_acceleration(models,style,camera,input_state); + let control_dir=style.get_y_control_dir(camera,input_state.controls); + let mut target_velocity=ladder_settings.get_ladder_target_velocity(control_dir,normal); + touching.constrain_velocity(models,hitbox_mesh,&mut target_velocity); + (gravity,target_velocity) +} #[derive(Default)] struct PhysicsModels{ @@ -184,12 +255,11 @@ impl PhysicsModels{ } } -#[derive(Clone)] +#[derive(Clone,Copy,Debug)] pub struct PhysicsCamera{ //punch: Planar64Vec3, //punch_velocity: Planar64Vec3, sensitivity:Ratio64Vec2,//dots to Angle32 ratios - mouse:MouseState,//last seen absolute mouse pos clamped_mouse_pos:glam::IVec2,//angles are calculated from this cumulative value //angle limits could be an enum + struct that defines whether it's limited and selects clamp or wrap depending // enum AngleLimit{ @@ -203,47 +273,60 @@ pub struct PhysicsCamera{ impl PhysicsCamera{ const ANGLE_PITCH_LOWER_LIMIT:Angle32=Angle32::NEG_FRAC_PI_2; const ANGLE_PITCH_UPPER_LIMIT:Angle32=Angle32::FRAC_PI_2; - pub fn move_mouse(&mut self,mouse_pos:glam::IVec2){ - let mut unclamped_mouse_pos=mouse_pos-self.mouse.pos+self.clamped_mouse_pos; + pub fn move_mouse(&mut self,mouse_delta:glam::IVec2){ + let mut unclamped_mouse_pos=self.clamped_mouse_pos+mouse_delta; unclamped_mouse_pos.y=unclamped_mouse_pos.y.clamp( self.sensitivity.y.rhs_div_int(Self::ANGLE_PITCH_LOWER_LIMIT.get() as i64) as i32, self.sensitivity.y.rhs_div_int(Self::ANGLE_PITCH_UPPER_LIMIT.get() as i64) as i32, ); self.clamped_mouse_pos=unclamped_mouse_pos; } - pub fn simulate_move_angles(&self,mouse_pos:glam::IVec2)->glam::Vec2 { - let a=-self.sensitivity.mul_int((mouse_pos-self.mouse.pos+self.clamped_mouse_pos).as_i64vec2()); + pub fn simulate_move_angles(&self,mouse_delta:glam::IVec2)->glam::Vec2 { + let a=-self.sensitivity.mul_int((self.clamped_mouse_pos+mouse_delta).as_i64vec2()); let ax=Angle32::wrap_from_i64(a.x); let ay=Angle32::clamp_from_i64(a.y) //clamp to actual vertical cam limit .clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT); return glam::vec2(ax.into(),ay.into()); } - fn simulate_move_rotation(&self,mouse_pos:glam::IVec2)->Planar64Mat3{ - let a=-self.sensitivity.mul_int((mouse_pos-self.mouse.pos+self.clamped_mouse_pos).as_i64vec2()); + #[inline] + fn get_rotation(&self,mouse_pos:glam::IVec2)->Planar64Mat3{ + let a=-self.sensitivity.mul_int(mouse_pos.as_i64vec2()); let ax=Angle32::wrap_from_i64(a.x); let ay=Angle32::clamp_from_i64(a.y) //clamp to actual vertical cam limit .clamp(Self::ANGLE_PITCH_LOWER_LIMIT,Self::ANGLE_PITCH_UPPER_LIMIT); Planar64Mat3::from_rotation_yx(ax,ay) } - fn simulate_move_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{ - let ax=-self.sensitivity.x.mul_int((mouse_pos_x-self.mouse.pos.x+self.clamped_mouse_pos.x) as i64); + fn rotation(&self)->Planar64Mat3{ + self.get_rotation(self.clamped_mouse_pos) + } + fn simulate_move_rotation(&self,mouse_delta:glam::IVec2)->Planar64Mat3{ + self.get_rotation(self.clamped_mouse_pos+mouse_delta) + } + fn get_rotation_y(&self,mouse_pos_x:i32)->Planar64Mat3{ + let ax=-self.sensitivity.x.mul_int(mouse_pos_x as i64); Planar64Mat3::from_rotation_y(Angle32::wrap_from_i64(ax)) } + fn rotation_y(&self)->Planar64Mat3{ + self.get_rotation_y(self.clamped_mouse_pos.x) + } + fn simulate_move_rotation_y(&self,mouse_delta_x:i32)->Planar64Mat3{ + self.get_rotation_y(self.clamped_mouse_pos.x+mouse_delta_x) + } } impl std::default::Default for PhysicsCamera{ fn default()->Self{ Self{ sensitivity:Ratio64Vec2::ONE*200_000, - mouse:MouseState::default(),//t=0 does not cause divide by zero because it's immediately replaced clamped_mouse_pos:glam::IVec2::ZERO, } } } mod gameplay{ use super::{gameplay_modes,HashSet,HashMap,ModelId}; + #[derive(Clone,Debug)] pub struct ModeState{ mode_id:gameplay_modes::ModeId, stage_id:gameplay_modes::StageId, @@ -312,7 +395,7 @@ mod gameplay{ } } } - +#[derive(Clone,Debug)] struct WorldState{} struct HitboxMesh{ @@ -342,122 +425,50 @@ impl HitboxMesh{ } trait StyleHelper{ - fn get_control(&self,control:u32,controls:u32)->bool; - fn allow_strafe(&self,controls:u32)->bool; - fn get_control_dir(&self,controls:u32)->Planar64Vec3; - //fn get_jump_time(&self)->Planar64; - //fn get_jump_height(&self)->Planar64; - //fn get_jump_energy(&self)->Planar64; - fn get_jump_deltav(&self)->Planar64; - fn get_walk_target_velocity(&self,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time,normal:&Planar64Vec3)->Planar64Vec3; - fn get_ladder_target_velocity(&self,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time,normal:&Planar64Vec3)->Planar64Vec3; - fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time)->Planar64Vec3; + fn get_control(&self,control:Controls,controls:Controls)->bool; + fn get_control_dir(&self,controls:Controls)->Planar64Vec3; + fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3; + fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3; fn calculate_mesh(&self)->HitboxMesh; } impl StyleHelper for StyleModifiers{ - fn get_control(&self,control:u32,controls:u32)->bool{ - controls&self.controls_mask&control==control + fn get_control(&self,control:Controls,controls:Controls)->bool{ + controls.intersection(self.controls_mask).contains(control) } - fn allow_strafe(&self,controls:u32)->bool{ - //disable strafing according to strafe settings - self.strafe.as_ref().is_some_and(|s|s.mask(controls)) - } - - fn get_control_dir(&self,controls:u32)->Planar64Vec3{ + fn get_control_dir(&self,controls:Controls)->Planar64Vec3{ //don't get fancy just do it let mut control_dir:Planar64Vec3 = Planar64Vec3::ZERO; //Apply mask after held check so you can require non-allowed keys to be held for some reason - let controls=controls&self.controls_mask; - if controls & Self::CONTROL_MOVEFORWARD == Self::CONTROL_MOVEFORWARD { + let controls=controls.intersection(self.controls_mask); + if controls.contains(Controls::MoveForward){ control_dir+=Self::FORWARD_DIR; } - if controls & Self::CONTROL_MOVEBACK == Self::CONTROL_MOVEBACK { + if controls.contains(Controls::MoveBackward){ control_dir-=Self::FORWARD_DIR; } - if controls & Self::CONTROL_MOVELEFT == Self::CONTROL_MOVELEFT { + if controls.contains(Controls::MoveLeft){ control_dir-=Self::RIGHT_DIR; } - if controls & Self::CONTROL_MOVERIGHT == Self::CONTROL_MOVERIGHT { + if controls.contains(Controls::MoveRight){ control_dir+=Self::RIGHT_DIR; } - if controls & Self::CONTROL_MOVEUP == Self::CONTROL_MOVEUP { + if controls.contains(Controls::MoveUp){ control_dir+=Self::UP_DIR; } - if controls & Self::CONTROL_MOVEDOWN == Self::CONTROL_MOVEDOWN { + if controls.contains(Controls::MoveDown){ control_dir-=Self::UP_DIR; } return control_dir } - //fn get_jump_time(&self)->Planar64 - //fn get_jump_height(&self)->Planar64 - //fn get_jump_energy(&self)->Planar64 - fn get_jump_deltav(&self)->Planar64{ - match &self.jump_impulse{ - &gameplay_style::JumpImpulse::FromTime(time)=>self.gravity.length()*(time/2), - &gameplay_style::JumpImpulse::FromHeight(height)=>(self.gravity.length()*height*2).sqrt(), - &gameplay_style::JumpImpulse::FromDeltaV(deltav)=>deltav, - &gameplay_style::JumpImpulse::FromEnergy(energy)=>(energy*2/self.mass).sqrt(), - } + fn get_y_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{ + camera.rotation_y()*self.get_control_dir(controls) } - fn get_walk_target_velocity(&self,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time,normal:&Planar64Vec3)->Planar64Vec3{ - let mut control_dir=self.get_control_dir(controls); - if control_dir==Planar64Vec3::ZERO{ - return control_dir; - } - let camera_mat=camera.simulate_move_rotation_y(camera.mouse.lerp(&next_mouse,time).x); - control_dir=camera_mat*control_dir; - let n=normal.length(); - let m=control_dir.length(); - let d=normal.dot(control_dir)/m; - if dPlanar64Vec3{ - let mut control_dir=self.get_control_dir(controls); - if control_dir==Planar64Vec3::ZERO{ - return control_dir; - } - let camera_mat=camera.simulate_move_rotation(camera.mouse.lerp(&next_mouse,time)); - control_dir=camera_mat*control_dir; - let n=normal.length(); - let m=control_dir.length(); - let mut d=normal.dot(control_dir)/m; - if d< -self.ladder_dot*n{ - control_dir=Planar64Vec3::Y*m; - d=normal.y(); - }else if self.ladder_dot*nPlanar64Vec3{ - let camera_mat=camera.simulate_move_rotation(camera.mouse.lerp(&next_mouse,time)); - camera_mat*self.get_control_dir(controls) + fn get_propulsion_control_dir(&self,camera:&PhysicsCamera,controls:Controls)->Planar64Vec3{ + //don't interpolate this! discrete mouse movement, constant acceleration + camera.rotation()*self.get_control_dir(controls) } fn calculate_mesh(&self)->HitboxMesh{ let mesh=match self.hitbox.mesh{ @@ -468,12 +479,106 @@ impl StyleHelper for StyleModifiers{ HitboxMesh::new(mesh,transform) } } - +#[derive(Clone,Debug)] enum MoveState{ Air, - Walk(WalkState), + Walk(ContactMoveState), + Ladder(ContactMoveState), Water, - Ladder(WalkState), + Fly, +} +impl MoveState{ + //call this after state.move_state is changed + fn apply_enum(&self,body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ + match self{ + MoveState::Fly=>body.acceleration=Planar64Vec3::ZERO, + MoveState::Air=>{ + //calculate base acceleration + let a=touching.base_acceleration(models,style,camera,input_state); + //set_acceleration clips according to contacts + set_acceleration(body,touching,models,hitbox_mesh,a); + }, + _=>(), + } + } + //function to coerce &mut self into &self + 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=>(), + MoveState::Water=>(), + MoveState::Fly=>{ + //set velocity according to current control state + let v=style.get_propulsion_control_dir(camera,input_state.controls)*80; + //set_velocity clips velocity according to current touching state + set_velocity(body,touching,models,hitbox_mesh,v); + }, + MoveState::Walk(walk_state) + |MoveState::Ladder(walk_state) + =>{ + //accelerate towards walk target or do nothing + let a=walk_state.target.acceleration(); + set_acceleration(body,touching,models,hitbox_mesh,a); + }, + } + } + /// changes the move state + fn apply_input(&mut self,body:&Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState){ + match self{ + MoveState::Fly + |MoveState::Air + |MoveState::Water=>(), + MoveState::Walk(ContactMoveState{target,contact,jump_direction:_})=>{ + if let Some(walk_settings)=&style.walk{ + let (gravity,target_velocity)=ground_things(walk_settings,contact,touching,models,hitbox_mesh,style,camera,input_state); + *target=TransientAcceleration::ground(walk_settings,body,gravity,target_velocity); + }else{ + panic!("ContactMoveState exists in style which does not allow walking!"); + } + }, + MoveState::Ladder(ContactMoveState{target,contact,jump_direction:_})=>{ + if let Some(ladder_settings)=&style.ladder{ + let (gravity,target_velocity)=ladder_things(ladder_settings,contact,touching,models,hitbox_mesh,style,camera,input_state); + *target=TransientAcceleration::ladder(ladder_settings,body,gravity,target_velocity); + }else{ + panic!("ContactMoveState exists in style which does not allow walking!"); + } + }, + } + } + fn get_walk_state(&self)->Option<&ContactMoveState>{ + match self{ + MoveState::Walk(walk_state) + |MoveState::Ladder(walk_state) + =>Some(walk_state), + MoveState::Air + |MoveState::Water + |MoveState::Fly + =>None, + } + } + 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{ + &TransientAcceleration::Reachable{acceleration:_,time}=>Some(TimedInstruction{ + time, + instruction:PhysicsInstruction::ReachWalkTargetVelocity + }), + TransientAcceleration::Unreachable{acceleration:_} + |TransientAcceleration::Reached + =>None, + } + 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, + } + } } #[derive(Clone,Default)] @@ -481,10 +586,11 @@ pub struct PhysicsOutputState{ body:Body, camera:PhysicsCamera, camera_offset:Planar64Vec3, + mouse_pos:glam::IVec2, } impl PhysicsOutputState{ - pub fn extrapolate(&self,mouse_pos:glam::IVec2,time:Time)->(glam::Vec3,glam::Vec2){ - ((self.body.extrapolated_position(time)+self.camera_offset).into(),self.camera.simulate_move_angles(mouse_pos)) + pub fn extrapolate(&self,mouse:MouseState)->(glam::Vec3,glam::Vec2){ + ((self.body.extrapolated_position(mouse.time)+self.camera_offset).into(),self.camera.simulate_move_angles(mouse.pos-self.mouse_pos)) } } @@ -559,17 +665,17 @@ impl PhysicsModel{ } } -#[derive(Debug,Clone,Eq,Hash,PartialEq)] -struct ContactCollision{ +#[derive(Debug,Clone,Copy,Eq,Hash,PartialEq)] +pub struct ContactCollision{ face_id:model_physics::MinkowskiFace, convex_mesh_id:ConvexMeshId, } #[derive(Debug,Clone,Eq,Hash,PartialEq)] -struct IntersectCollision{ +pub struct IntersectCollision{ convex_mesh_id:ConvexMeshId, } #[derive(Debug,Clone,Eq,Hash,PartialEq)] -enum Collision{ +pub enum Collision{ Contact(ContactCollision), Intersect(IntersectCollision), } @@ -587,7 +693,7 @@ impl Collision{ } } } -#[derive(Default)] +#[derive(Clone,Debug,Default)] struct TouchingState{ contacts:HashSet::, intersects:HashSet::, @@ -609,15 +715,15 @@ impl TouchingState{ Collision::Intersect(collision)=>self.intersects.remove(collision), } } - fn base_acceleration(&self,models:&PhysicsModels,style:&StyleModifiers,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time)->Planar64Vec3{ + fn base_acceleration(&self,models:&PhysicsModels,style:&StyleModifiers,camera:&PhysicsCamera,input_state:&InputState)->Planar64Vec3{ let mut a=style.gravity; - if let Some(rocket_force)=style.rocket_force{ - a+=style.get_propulsion_control_dir(camera,controls,next_mouse,time)*rocket_force; + if let Some(rocket_settings)=&style.rocket{ + a+=rocket_settings.acceleration(style.get_propulsion_control_dir(camera,input_state.controls)); } //add accelerators for contact in &self.contacts{ match models.attr(contact.convex_mesh_id.model_id){ - PhysicsCollisionAttributes::Contact{contacting,general}=>{ + PhysicsCollisionAttributes::Contact{contacting:_,general}=>{ match &general.accelerator{ Some(accelerator)=>a+=accelerator.acceleration, None=>(), @@ -628,7 +734,7 @@ impl TouchingState{ } for intersect in &self.intersects{ match models.attr(intersect.convex_mesh_id.model_id){ - PhysicsCollisionAttributes::Intersect{intersecting,general}=>{ + PhysicsCollisionAttributes::Intersect{intersecting:_,general}=>{ match &general.accelerator{ Some(accelerator)=>a+=accelerator.acceleration, None=>(), @@ -637,7 +743,7 @@ impl TouchingState{ _=>panic!("impossible touching state"), } } - //add water../? + //TODO: add water a } fn constrain_velocity(&self,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,velocity:&mut Planar64Vec3){ @@ -660,48 +766,6 @@ impl TouchingState{ } } } - fn get_move_state(&self,body:&Body,models:&PhysicsModels,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,camera:&PhysicsCamera,controls:u32,next_mouse:&MouseState,time:Time)->(MoveState,Planar64Vec3){ - //check current move conditions and use heuristics to determine - //which ladder to climb on, which ground to walk on, etc - //collect move state affecting objects from contacts (accelerator,water,ladder,ground) - let gravity=self.base_acceleration(models,style,camera,controls,next_mouse,time); - let mut move_state=MoveState::Air; - let mut a=gravity; - for contact in &self.contacts{ - match models.attr(contact.convex_mesh_id.model_id){ - PhysicsCollisionAttributes::Contact{contacting,general}=>{ - let normal=contact_normal(models,hitbox_mesh,contact); - match &contacting.contact_behaviour{ - Some(gameplay_attributes::ContactingBehaviour::Ladder(_))=>{ - //ladder walkstate - let mut target_velocity=style.get_ladder_target_velocity(camera,controls,next_mouse,time,&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); - move_state=MoveState::Ladder(walk_state); - self.constrain_acceleration(models,hitbox_mesh,&mut acceleration); - a=acceleration; - }, - None=>if style.surf_slope.map_or(true,|s|normal.walkable(s,Planar64Vec3::Y)){ - //check ground - let mut target_velocity=style.get_walk_target_velocity(camera,controls,next_mouse,time,&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); - move_state=MoveState::Walk(walk_state); - self.constrain_acceleration(models,hitbox_mesh,&mut acceleration); - a=acceleration; - }, - _=>(), - } - }, - _=>panic!("impossible touching state"), - } - } - for intersect in &self.intersects{ - //water - } - self.constrain_acceleration(models,hitbox_mesh,&mut a); - (move_state,a) - } fn predict_collision_end(&self,collector:&mut instruction::InstructionCollector,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,body:&Body,time:Time){ let relative_body=VirtualBody::relative(&Body::default(),body).body(time); for contact in &self.contacts{ @@ -825,6 +889,7 @@ impl VirtualBody<'_>{ } } +#[derive(Clone,Debug)] pub struct PhysicsState{ time:Time, body:Body, @@ -833,8 +898,7 @@ pub struct PhysicsState{ //camera must exist in state because wormholes modify the camera, also camera punch camera:PhysicsCamera, //input_state - pub next_mouse:MouseState,//Where is the mouse headed next - controls:u32,//TODO this should be a struct + input_state:InputState, //style style:StyleModifiers,//mode style with custom style updates applied //gameplay_state @@ -861,8 +925,7 @@ impl Default for PhysicsState{ touching:TouchingState::default(), move_state: MoveState::Air, camera:PhysicsCamera::default(), - next_mouse:MouseState::default(), - controls:0, + input_state:InputState::default(), world:WorldState{}, mode_state:ModeState::default(), } @@ -880,39 +943,47 @@ impl Default for PhysicsData{ } impl PhysicsState { - pub fn clear(&mut self){ + fn clear(&mut self){ self.touching.clear(); } - - pub fn output(&self)->PhysicsOutputState{ - PhysicsOutputState{ - body:self.body.clone(), - camera:self.camera.clone(), - camera_offset:self.style.camera_offset.clone(), - } - } - - pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ - self.camera.sensitivity=user_settings.calculate_sensitivity(); - } - - pub fn advance_time(&mut self, time: Time){ + fn advance_time(&mut self, time: Time){ self.body.advance_time(time); self.time=time; } - - fn set_control(&mut self,control:u32,state:bool){ - self.controls=if state{self.controls|control}else{self.controls&!control}; + fn next_move_instruction(&self)->Option>{ + self.move_state.next_move_instruction(&self.style.strafe,self.time) } - - 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 + //lmao idk this is convenient + fn apply_enum_and_input_and_body(&mut self,data:&PhysicsData){ + self.move_state.apply_enum(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + self.move_state.apply_input(&self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + self.move_state.apply_to_body(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + } + fn apply_enum_and_body(&mut self,data:&PhysicsData){ + self.move_state.apply_enum(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + self.move_state.apply_to_body(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + } + fn apply_input_and_body(&mut self,data:&PhysicsData){ + self.move_state.apply_input(&self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + self.move_state.apply_to_body(&mut self.body,&self.touching,&data.models,&data.hitbox_mesh,&self.style,&self.camera,&self.input_state); + } + fn set_move_state(&mut self,data:&PhysicsData,move_state:MoveState){ + self.move_state=move_state; + //this function call reads the above state that was just set + self.apply_enum_and_body(data); + } + fn cull_velocity(&mut self,data:&PhysicsData,velocity:Planar64Vec3){ + //TODO: be more precise about contacts + if set_velocity_cull(&mut self.body,&mut self.touching,&data.models,&data.hitbox_mesh,velocity){ + //TODO do better + match self.move_state.get_walk_state(){ + //did you stop touching the thing you were walking on? + Some(walk_state)=>if !self.touching.contacts.contains(&walk_state.contact){ + self.set_move_state(data,MoveState::Air); + }, + None=>self.apply_enum_and_body(data), } - }) + } } //state mutated on collision: @@ -945,25 +1016,11 @@ impl PhysicsState { // instruction:PhysicsInstruction::Water // }); // } - - fn next_move_instruction(&self)->Option>{ - //check if you have a valid walk state and create an instruction - match &self.move_state{ - MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>match &walk_state.state{ - WalkEnum::Transient(walk_target)=>Some(TimedInstruction{ - time:walk_target.time, - instruction:PhysicsInstruction::ReachWalkTargetVelocity - }), - WalkEnum::Reached=>None, - } - MoveState::Air=>self.next_strafe_instruction(), - MoveState::Water=>None,//TODO - } - } } + #[derive(Default)] pub struct PhysicsContext{ - pub state:PhysicsState,//this captures the entire state of the physics. + state:PhysicsState,//this captures the entire state of the physics. data:PhysicsData,//data currently loaded into memory which is needded for physics to run, but is not part of the state. } impl instruction::InstructionConsumer for PhysicsContext{ @@ -978,13 +1035,32 @@ impl instruction::InstructionEmitter for PhysicsContext{ } } impl PhysicsContext{ - pub fn spawn(&mut self){ - self.process_instruction(instruction::TimedInstruction{ + pub fn clear(&mut self){ + self.state.clear(); + } + pub fn load_user_settings(&mut self,user_settings:&crate::settings::UserSettings){ + self.process_instruction(TimedInstruction{ time:self.state.time, - instruction: PhysicsInstruction::Input(PhysicsInputInstruction::Reset), + instruction:PhysicsInstruction::SetSensitivity(user_settings.calculate_sensitivity()), }); } - + pub fn spawn(&mut self){ + self.process_instruction(TimedInstruction{ + time:self.state.time, + instruction:PhysicsInstruction::Input(PhysicsInputInstruction::Reset), + }); + } + pub const fn output(&self)->PhysicsOutputState{ + PhysicsOutputState{ + body:self.state.body, + camera:self.state.camera, + camera_offset:self.state.style.camera_offset, + mouse_pos:self.state.input_state.mouse.pos, + } + } + pub const fn get_next_mouse(&self)->&MouseState{ + self.state.input_state.get_next_mouse() + } pub fn generate_models(&mut self,map:&map::CompleteMap){ self.data.modes=map.modes.clone(); let mut used_attributes=Vec::new(); @@ -1054,7 +1130,7 @@ impl PhysicsContext{ } //tickless gaming - pub fn run(&mut self,time_limit:Time){ + fn run(&mut self,time_limit:Time){ //prepare is ommitted - everything is done via instructions. while let Some(instruction)=self.next_instruction(time_limit){//collect //process @@ -1062,33 +1138,14 @@ impl PhysicsContext{ //write hash lol } } -} - - //TODO get rid of this trash - fn refresh_walk_target(s:&mut PhysicsState,data:&PhysicsData)->Planar64Vec3{ - match &mut s.move_state{ - MoveState::Air|MoveState::Water=>s.touching.base_acceleration(&data.models,&s.style,&s.camera,s.controls,&s.next_mouse,s.time), - MoveState::Walk(WalkState{state,contact,jump_direction:_})=>{ - let n=contact_normal(&data.models,&data.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_walk_target_velocity(&s.camera,s.controls,&s.next_mouse,s.time,&n); - s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v); - let normal_accel=-n.dot(gravity)/n.length(); - (*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.walk_speed,normal_accel); - a - }, - MoveState::Ladder(WalkState{state,contact,jump_direction:_})=>{ - let n=contact_normal(&data.models,&data.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.controls,&s.next_mouse,s.time,&n); - s.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v); - (*state,a)=WalkEnum::with_target_velocity(&s.body,&s.style,v,&n,s.style.ladder_speed,s.style.ladder_accel); - a - }, - } + pub fn run_input_instruction(&mut self,instruction:TimedInstruction){ + self.run(instruction.time); + self.process_instruction(TimedInstruction{ + time:instruction.time, + instruction:PhysicsInstruction::Input(instruction.instruction), + }); } +} fn literally_next_instruction_but_with_context(state:&PhysicsState,data:&PhysicsData,time_limit:Time)->Option>{ //JUST POLLING!!! NO MUTATION @@ -1121,20 +1178,6 @@ impl PhysicsContext{ collector.instruction() } -fn get_walk_state(move_state:&MoveState)->Option<&WalkState>{ - match move_state{ - MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>Some(walk_state), - MoveState::Air|MoveState::Water=>None, - } -} - -fn jumped_velocity(models:&PhysicsModels,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,walk_state:&WalkState,v:&mut Planar64Vec3){ - let jump_dir=match &walk_state.jump_direction{ - JumpDirection::FromContactNormal=>contact_normal(models,hitbox_mesh,&walk_state.contact), - &JumpDirection::Exactly(dir)=>dir, - }; - *v=*v+jump_dir*(style.get_jump_deltav()/jump_dir.length()); -} fn contact_normal(models:&PhysicsModels,hitbox_mesh:&HitboxMesh,contact:&ContactCollision)->Planar64Vec3{ let model_mesh=models.mesh(contact.convex_mesh_id); @@ -1167,10 +1210,9 @@ fn set_velocity_cull(body:&mut Body,touching:&mut TouchingState,models:&PhysicsM set_velocity(body,touching,models,hitbox_mesh,v); culled } -fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut v:Planar64Vec3)->Planar64Vec3{ +fn set_velocity(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut v:Planar64Vec3){ touching.constrain_velocity(models,hitbox_mesh,&mut v); body.velocity=v; - v } 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 @@ -1187,10 +1229,9 @@ fn set_acceleration_cull(body:&mut Body,touching:&mut TouchingState,models:&Phys set_acceleration(body,touching,models,hitbox_mesh,a); culled } -fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut a:Planar64Vec3)->Planar64Vec3{ +fn set_acceleration(body:&mut Body,touching:&TouchingState,models:&PhysicsModels,hitbox_mesh:&HitboxMesh,mut a:Planar64Vec3){ touching.constrain_acceleration(models,hitbox_mesh,&mut a); body.acceleration=a; - a } fn teleport(body:&mut Body,touching:&mut TouchingState,models:&PhysicsModels,style:&StyleModifiers,hitbox_mesh:&HitboxMesh,point:Planar64Vec3)->MoveState{ @@ -1279,73 +1320,76 @@ fn run_teleport_behaviour(wormhole:&Option,models |PhysicsInstruction::ReachWalkTargetVelocity |PhysicsInstruction::CollisionStart(_) |PhysicsInstruction::CollisionEnd(_) - |PhysicsInstruction::StrafeTick=>state.advance_time(ins.time), + |PhysicsInstruction::StrafeTick + |PhysicsInstruction::SetSensitivity(_) + =>state.advance_time(ins.time), } match ins.instruction{ - PhysicsInstruction::CollisionStart(c)=>{ - let convex_mesh_id=c.convex_mesh_id(); - match (data.models.attr(convex_mesh_id.model_id),&c){ - (PhysicsCollisionAttributes::Contact{contacting,general},Collision::Contact(contact))=>{ - let mut v=state.body.velocity; - let normal=contact_normal(&data.models,&data.hitbox_mesh,contact); + PhysicsInstruction::CollisionStart(collision)=>{ + let convex_mesh_id=collision.convex_mesh_id(); + match (data.models.attr(convex_mesh_id.model_id),&collision){ + (PhysicsCollisionAttributes::Contact{contacting,general},&Collision::Contact(contact))=>{ + let incident_velocity=state.body.velocity; + //add to touching + state.touching.insert(collision); + //clip v + set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,incident_velocity); match &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))=>{ - //velocity and normal are facing opposite directions so this is inherently negative. - let d=normal.dot(v)*(Planar64::ONE+Planar64::raw(elasticity as i64+1)); - v+=normal*(d/normal.dot(normal)); + let reflected_velocity=state.body.velocity+(state.body.velocity-incident_velocity)*Planar64::raw(elasticity as i64+1); + set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,reflected_velocity); }, - 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: - v=Planar64Vec3::ZERO;//model.velocity + //it's a little bit different because maybe you want to chain ladders together + set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,Planar64Vec3::ZERO);//model.velocity } //ladder walkstate - let gravity=state.touching.base_acceleration(&data.models,&state.style,&state.camera,state.controls,&state.next_mouse,state.time); - let mut target_velocity=state.style.get_ladder_target_velocity(&state.camera,state.controls,&state.next_mouse,state.time,&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); - state.move_state=MoveState::Ladder(walk_state); - set_acceleration(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,a); + let (gravity,target_velocity)=ladder_things(ladder_settings,&contact,&state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state); + let walk_state=ContactMoveState::ladder(ladder_settings,&state.body,gravity,target_velocity,contact); + state.set_move_state(data,MoveState::Ladder(walk_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.controls,&state.next_mouse,state.time); - let mut target_velocity=state.style.get_walk_target_velocity(&state.camera,state.controls,&state.next_mouse,state.time,&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,target_velocity)=ground_things(walk_settings,&contact,&state.touching,&data.models,&data.hitbox_mesh,&state.style,&state.camera,&state.input_state); + let walk_state=ContactMoveState::ground(walk_settings,&state.body,gravity,target_velocity,contact); + state.set_move_state(data,MoveState::Walk(walk_state)); + } }, } - //check ground - state.touching.insert(c); //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,convex_mesh_id); } - //flatten v - state.touching.constrain_velocity(&data.models,&data.hitbox_mesh,&mut v); match &general.booster{ Some(booster)=>{ //DELETE THIS when boosters get converted to height machines match booster{ //&gameplay_attributes::Booster::Affine(transform)=>v=transform.transform_point3(v), - &gameplay_attributes::Booster::Velocity(velocity)=>v+=velocity, + &gameplay_attributes::Booster::Velocity(velocity)=>{ + let boosted_velocity=state.body.velocity+velocity; + //fall through boosters + state.cull_velocity(data,boosted_velocity); + }, &gameplay_attributes::Booster::Energy{direction: _,energy: _}=>todo!(), } }, None=>(), } - let calc_move=if state.style.get_control(StyleModifiers::CONTROL_JUMP,state.controls){ - if let Some(walk_state)=get_walk_state(&state.move_state){ - jumped_velocity(&data.models,&state.style,&data.hitbox_mesh,walk_state,&mut v); - set_velocity_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,v) - }else{false} - }else{false}; + 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); + state.cull_velocity(data,jumped_velocity); + } + } match &general.trajectory{ Some(trajectory)=>{ match trajectory{ @@ -1353,23 +1397,21 @@ fn run_teleport_behaviour(wormhole:&Option,models gameplay_attributes::SetTrajectory::Height(_)=>todo!(), gameplay_attributes::SetTrajectory::TargetPointTime { target_point: _, time: _ }=>todo!(), gameplay_attributes::SetTrajectory::TargetPointSpeed { target_point: _, speed: _, trajectory_choice: _ }=>todo!(), - &gameplay_attributes::SetTrajectory::Velocity(velocity)=>v=velocity, + &gameplay_attributes::SetTrajectory::Velocity(velocity)=>{ + state.cull_velocity(data,velocity); + }, gameplay_attributes::SetTrajectory::DotVelocity { direction: _, dot: _ }=>todo!(), } }, None=>(), } - set_velocity(&mut state.body,&state.touching,&data.models,&data.hitbox_mesh,v); - //not sure if or is correct here - if calc_move||Planar64::ZERO{ //I think that setting the velocity to 0 was preventing surface contacts from entering an infinite loop - state.touching.insert(c); + state.touching.insert(collision); 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,convex_mesh_id); } @@ -1377,83 +1419,100 @@ fn run_teleport_behaviour(wormhole:&Option,models _=>panic!("invalid pair"), } }, - PhysicsInstruction::CollisionEnd(c)=>{ - match data.models.attr(c.convex_mesh_id().model_id){ - PhysicsCollisionAttributes::Contact{contacting:_,general:_}=>{ - state.touching.remove(&c);//remove contact before calling contact_constrain_acceleration + PhysicsInstruction::CollisionEnd(collision)=>{ + match (data.models.attr(collision.convex_mesh_id().model_id),&collision){ + (PhysicsCollisionAttributes::Contact{contacting:_,general:_},&Collision::Contact(contact))=>{ + state.touching.remove(&collision);//remove contact before calling contact_constrain_acceleration //check ground - (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); + //TODO do better + //this is inner code from state.cull_velocity + match state.move_state.get_walk_state(){ + //did you stop touching the thing you were walking on? + Some(walk_state)=>if walk_state.contact==contact{ + state.set_move_state(data,MoveState::Air); + }, + None=>state.apply_enum_and_body(data), + } }, - PhysicsCollisionAttributes::Intersect{intersecting:_,general:_}=>{ - state.touching.remove(&c); + (PhysicsCollisionAttributes::Intersect{intersecting: _,general:_},Collision::Intersect(_))=>{ + state.touching.remove(&collision); }, + _=>panic!("invalid pair"), } }, PhysicsInstruction::StrafeTick=>{ - let control_dir=state.style.get_control_dir(state.controls); - if control_dir!=Planar64Vec3::ZERO{ - let camera_mat=state.camera.simulate_move_rotation_y(state.camera.mouse.lerp(&state.next_mouse,state.time).x); - let control_dir=camera_mat*control_dir; - //normalize but careful for zero - let d=state.body.velocity.dot(control_dir); - if d{ match &mut state.move_state{ - MoveState::Air|MoveState::Water=>(), + MoveState::Air + |MoveState::Water + |MoveState::Fly + =>println!("ReachWalkTargetVelocity fired for non-walking MoveState"), MoveState::Walk(walk_state)|MoveState::Ladder(walk_state)=>{ - match &mut walk_state.state{ - WalkEnum::Reached=>(), - WalkEnum::Transient(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.state=WalkEnum::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!(), } } } }, + PhysicsInstruction::SetSensitivity(sensitivity)=>state.camera.sensitivity=sensitivity, PhysicsInstruction::Input(input_instruction)=>{ let mut b_refresh_walk_target=true; match input_instruction{ PhysicsInputInstruction::SetNextMouse(m)=>{ - state.camera.move_mouse(state.next_mouse.pos); - (state.camera.mouse,state.next_mouse)=(state.next_mouse.clone(),m); + state.camera.move_mouse(state.input_state.mouse_delta()); + state.input_state.set_next_mouse(m); }, PhysicsInputInstruction::ReplaceMouse(m0,m1)=>{ - state.camera.move_mouse(m0.pos); - (state.camera.mouse,state.next_mouse)=(m0,m1); + state.camera.move_mouse(m0.pos-state.input_state.mouse.pos); + state.input_state.replace_mouse(m0,m1); }, - PhysicsInputInstruction::SetMoveForward(s)=>state.set_control(StyleModifiers::CONTROL_MOVEFORWARD,s), - PhysicsInputInstruction::SetMoveLeft(s)=>state.set_control(StyleModifiers::CONTROL_MOVELEFT,s), - PhysicsInputInstruction::SetMoveBack(s)=>state.set_control(StyleModifiers::CONTROL_MOVEBACK,s), - PhysicsInputInstruction::SetMoveRight(s)=>state.set_control(StyleModifiers::CONTROL_MOVERIGHT,s), - PhysicsInputInstruction::SetMoveUp(s)=>state.set_control(StyleModifiers::CONTROL_MOVEUP,s), - PhysicsInputInstruction::SetMoveDown(s)=>state.set_control(StyleModifiers::CONTROL_MOVEDOWN,s), + PhysicsInputInstruction::SetMoveForward(s)=>state.input_state.set_control(Controls::MoveForward,s), + PhysicsInputInstruction::SetMoveLeft(s)=>state.input_state.set_control(Controls::MoveLeft,s), + PhysicsInputInstruction::SetMoveBack(s)=>state.input_state.set_control(Controls::MoveBackward,s), + PhysicsInputInstruction::SetMoveRight(s)=>state.input_state.set_control(Controls::MoveRight,s), + PhysicsInputInstruction::SetMoveUp(s)=>state.input_state.set_control(Controls::MoveUp,s), + PhysicsInputInstruction::SetMoveDown(s)=>state.input_state.set_control(Controls::MoveDown,s), PhysicsInputInstruction::SetJump(s)=>{ - state.set_control(StyleModifiers::CONTROL_JUMP,s); - if let Some(walk_state)=get_walk_state(&state.move_state){ - let mut v=state.body.velocity; - jumped_velocity(&data.models,&state.style,&data.hitbox_mesh,walk_state,&mut v); - if set_velocity_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,v){ - (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); + b_refresh_walk_target=false; + state.input_state.set_control(Controls::Jump,s); + 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); + state.cull_velocity(&data,jumped_velocity); } } - b_refresh_walk_target=false; }, PhysicsInputInstruction::SetZoom(s)=>{ - state.set_control(StyleModifiers::CONTROL_ZOOM,s); + state.input_state.set_control(Controls::Zoom,s); b_refresh_walk_target=false; }, PhysicsInputInstruction::Reset=>{ @@ -1466,16 +1525,25 @@ fn run_teleport_behaviour(wormhole:&Option,models ).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); - (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); + state.set_move_state(data,MoveState::Air); + b_refresh_walk_target=false; + }, + PhysicsInputInstruction::PracticeFly=>{ + match &state.move_state{ + MoveState::Fly=>{ + state.set_move_state(data,MoveState::Air); + }, + _=>{ + state.set_move_state(data,MoveState::Fly); + }, + } b_refresh_walk_target=false; }, PhysicsInputInstruction::Idle=>{b_refresh_walk_target=false;},//literally idle! } if b_refresh_walk_target{ - let a=refresh_walk_target(state,data); - if set_acceleration_cull(&mut state.body,&mut state.touching,&data.models,&data.hitbox_mesh,a){ - (state.move_state,state.body.acceleration)=state.touching.get_move_state(&state.body,&data.models,&state.style,&data.hitbox_mesh,&state.camera,state.controls,&state.next_mouse,state.time); - } + state.apply_input_and_body(data); + state.cull_velocity(data,state.body.velocity); } }, } diff --git a/src/physics_worker.rs b/src/physics_worker.rs index 29a4708..1f0e970 100644 --- a/src/physics_worker.rs +++ b/src/physics_worker.rs @@ -13,6 +13,7 @@ pub enum InputInstruction{ Jump(bool), Zoom(bool), Reset, + PracticeFly, } pub enum Instruction{ Input(InputInstruction), @@ -25,7 +26,7 @@ pub enum Instruction{ pub fn new(mut physics:crate::physics::PhysicsContext,mut graphics_worker:crate::compat_worker::INWorker)->crate::compat_worker::QNWorker>{ let mut mouse_blocking=true; - let mut last_mouse_time=physics.state.next_mouse.time; + let mut last_mouse_time=physics.get_next_mouse().time; let mut timeline=std::collections::VecDeque::new(); crate::compat_worker::QNWorker::new(move |ins:TimedInstruction|{ if if let Some(phys_input)=match &ins.instruction{ @@ -43,7 +44,7 @@ pub enum Instruction{ timeline.push_front(TimedInstruction{ time:last_mouse_time, instruction:PhysicsInputInstruction::ReplaceMouse( - MouseState{time:last_mouse_time,pos:physics.state.next_mouse.pos}, + MouseState{time:last_mouse_time,pos:physics.get_next_mouse().pos}, MouseState{time:ins.time,pos:m} ), }); @@ -62,6 +63,7 @@ pub enum Instruction{ &InputInstruction::Jump(s)=>Some(PhysicsInputInstruction::SetJump(s)), &InputInstruction::Zoom(s)=>Some(PhysicsInputInstruction::SetZoom(s)), InputInstruction::Reset=>Some(PhysicsInputInstruction::Reset), + InputInstruction::PracticeFly=>Some(PhysicsInputInstruction::PracticeFly), }, Instruction::GenerateModels(_)=>Some(PhysicsInputInstruction::Idle), Instruction::ClearModels=>Some(PhysicsInputInstruction::Idle), @@ -79,11 +81,11 @@ pub enum Instruction{ //shitty mice are 125Hz which is 8ms so this should cover that. //setting this to 100us still doesn't print even though it's 10x lower than the polling rate, //so mouse events are probably not handled separately from drawing and fire right before it :( - if Time::from_millis(10){ - graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.state.output(),ins.time,physics.state.next_mouse.pos)).unwrap(); + graphics_worker.send(crate::graphics_worker::Instruction::Render(physics.output(),ins.time,physics.get_next_mouse().pos)).unwrap(); }, Instruction::Resize(size,user_settings)=>{ graphics_worker.send(crate::graphics_worker::Instruction::Resize(size,user_settings)).unwrap(); @@ -124,7 +122,7 @@ pub enum Instruction{ graphics_worker.send(crate::graphics_worker::Instruction::GenerateModels(map)).unwrap(); }, Instruction::ClearModels=>{ - physics.state.clear(); + physics.clear(); graphics_worker.send(crate::graphics_worker::Instruction::ClearModels).unwrap(); }, _=>(), diff --git a/src/window.rs b/src/window.rs index 38406c9..08ed102 100644 --- a/src/window.rs +++ b/src/window.rs @@ -108,6 +108,7 @@ impl WindowContext<'_>{ "q"=>Some(InputInstruction::MoveDown(s)), "z"=>Some(InputInstruction::Zoom(s)), "r"=>if s{Some(InputInstruction::Reset)}else{None}, + "f"=>if s{Some(InputInstruction::PracticeFly)}else{None}, _=>None, }, _=>None, @@ -173,7 +174,7 @@ impl<'a> WindowContextSetup<'a>{ let user_settings=crate::settings::read_user_settings(); let mut physics=crate::physics::PhysicsContext::default(); - physics.state.load_user_settings(&user_settings); + physics.load_user_settings(&user_settings); let mut graphics=crate::graphics::GraphicsState::new(&context.device,&context.queue,&context.config); graphics.load_user_settings(&user_settings);