diff --git a/src/integer.rs b/src/integer.rs index d5c6879..fb973ab 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -1,6 +1,8 @@ //integer units +#[derive(Clone,Copy,Hash,Debug)] pub struct Time(i64); impl Time{ + pub const ZERO:Self=Self(0); pub const ONE_SECOND:Self=Self(1_000_000_000); pub const ONE_MILLISECOND:Self=Self(1_000_000); pub const ONE_MICROSECOND:Self=Self(1_000); @@ -18,6 +20,35 @@ impl Time{ Self(Self::ONE_NANOSECOND.0*num) } //should I have checked subtraction? force all time variables to be positive? + pub fn nanos(&self)->i64{ + self.0 + } +} +impl std::fmt::Display for Time{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{}s+{}ns",self.0/Self::ONE_SECOND.0,self.0%Self::ONE_SECOND.0) + } +} +impl std::ops::Neg for Time{ + type Output=Time; + #[inline] + fn neg(self)->Self::Output { + Time(-self.0) + } +} +impl std::ops::Add<Time> for Time{ + type Output=Time; + #[inline] + fn add(self,rhs:Self)->Self::Output { + Time(self.0+rhs.0) + } +} +impl std::ops::Sub<Time> for Time{ + type Output=Time; + #[inline] + fn sub(self,rhs:Self)->Self::Output { + Time(self.0-rhs.0) + } } #[derive(Clone,Copy,Hash)] diff --git a/src/physics.rs b/src/physics.rs index dd63374..5fd7b5f 100644 --- a/src/physics.rs +++ b/src/physics.rs @@ -1,6 +1,6 @@ use crate::{instruction::{InstructionEmitter, InstructionConsumer, TimedInstruction}, zeroes::zeroes2}; -use crate::integer::{Planar64,Planar64Vec3,Planar64Mat3,Angle32,Angle32Vec2,Ratio64Vec2}; +use crate::integer::{Time,Planar64,Planar64Vec3,Planar64Mat3,Angle32,Ratio64,Ratio64Vec2}; #[derive(Debug)] pub enum PhysicsInstruction { @@ -54,7 +54,7 @@ pub struct Body { position: Planar64Vec3,//I64 where 2^32 = 1 u velocity: Planar64Vec3,//I64 where 2^32 = 1 u/s acceleration: Planar64Vec3,//I64 where 2^32 = 1 u/s/s - time: TIME,//nanoseconds x xxxxD! + time:Time,//nanoseconds x xxxxD! } pub enum MoveRestriction { @@ -75,7 +75,7 @@ impl InputState { } } impl crate::instruction::InstructionEmitter<InputInstruction> for InputState{ - fn next_instruction(&self, time_limit:crate::body::TIME) -> Option<TimedInstruction<InputInstruction>> { + fn next_instruction(&self, time_limit:crate::body::Time) -> Option<TimedInstruction<InputInstruction>> { //this is polled by PhysicsState for actions like Jump //no, it has to be the other way around. physics is run up until the jump instruction, and then the jump instruction is pushed. self.queue.get(0) @@ -93,24 +93,24 @@ impl crate::instruction::InstructionConsumer<InputInstruction> for InputState{ #[derive(Clone,Debug)] pub struct MouseState { pub pos: glam::IVec2, - pub time: TIME, + pub time:Time, } impl Default for MouseState{ fn default() -> Self { Self { - time:0, + time:Time::ZERO, pos:glam::IVec2::ZERO, } } } impl MouseState { - pub fn lerp(&self,target:&MouseState,time:TIME)->glam::IVec2 { + pub fn lerp(&self,target:&MouseState,time:Time)->glam::IVec2 { let m0=self.pos.as_i64vec2(); let m1=target.pos.as_i64vec2(); //these are deltas - let t1t=(target.time-time) as i64; - let tt0=(time-self.time) as i64; - let dt=(target.time-self.time) as i64; + let t1t=(target.time-time).nanos(); + let tt0=(time-self.time).nanos(); + let dt=(target.time-self.time).nanos(); ((m0*t1t+m1*tt0)/dt).as_ivec2() } } @@ -121,14 +121,14 @@ pub enum WalkEnum{ } pub struct WalkState { pub target_velocity: Planar64Vec3, - pub target_time: TIME, + pub target_time: Time, pub state: WalkEnum, } impl WalkState { pub fn new() -> Self { Self{ target_velocity:Planar64Vec3::ZERO, - target_time:0, + target_time:Time::ZERO, state:WalkEnum::Reached, } } @@ -160,7 +160,7 @@ impl PhysicsCamera { Self{ offset, sensitivity:Ratio64Vec2::ONE.mul_ratio(200_000), - mouse:MouseState{pos:glam::IVec2::ZERO,time:-1},//escape initialization hell divide by zero + mouse:MouseState{pos:glam::IVec2::ZERO,time:-Time::ONE_NANOSECOND},//escape initialization hell divide by zero clamped_mouse_pos:glam::IVec2::ZERO, angle_pitch_lower_limit:-Angle32::FRAC_PI_2, angle_pitch_upper_limit:Angle32::FRAC_PI_2, @@ -210,8 +210,7 @@ pub struct StyleModifiers{ pub friction:Planar64, pub walk_accel:Planar64, pub gravity:Planar64Vec3, - pub strafe_tick_num:TIME, - pub strafe_tick_den:TIME, + pub strafe_tick_rate:Ratio64, pub hitbox_halfsize:Planar64Vec3, } impl std::default::Default for StyleModifiers{ @@ -219,8 +218,7 @@ impl std::default::Default for StyleModifiers{ Self{ controls_mask: !0,//&!(Self::CONTROL_MOVEUP|Self::CONTROL_MOVEDOWN), controls_held: 0, - strafe_tick_num: 100,//100t - strafe_tick_den: 1_000_000_000, + strafe_tick_rate:Ratio64::ONE.div_ratio(100), gravity: Planar64Vec3::new(0,100,0), friction: Planar64::new(12)/10, walk_accel: Planar64::new(90), @@ -280,7 +278,7 @@ impl StyleModifiers{ } pub struct PhysicsState{ - pub time:TIME, + pub time:Time, pub body:Body, pub world:WorldState,//currently there is only one state the world can be in pub game:GameMechanicsState, @@ -391,26 +389,24 @@ impl RelativeCollision { } } -pub type TIME = i64; - impl Body { pub fn with_pva(position:Planar64Vec3,velocity:Planar64Vec3,acceleration:Planar64Vec3) -> Self { Self{ position, velocity, acceleration, - time: 0, + time:Time::ZERO, } } - pub fn extrapolated_position(&self,time: TIME)->Planar64Vec3{ - let dt=(time-self.time) as f64/1_000_000_000f64; - self.position+self.velocity*(dt as f32)+self.acceleration*((0.5*dt*dt) as f32) + pub fn extrapolated_position(&self,time:Time)->Planar64Vec3{ + let dt=time-self.time; + self.position+self.velocity*dt+self.acceleration*(dt*dt*0.5) } - pub fn extrapolated_velocity(&self,time: TIME)->Planar64Vec3{ - let dt=(time-self.time) as f64/1_000_000_000f64; - self.velocity+self.acceleration*(dt as f32) + pub fn extrapolated_velocity(&self,time:Time)->Planar64Vec3{ + let dt=time-self.time; + self.velocity+self.acceleration*dt } - pub fn advance_time(&mut self, time: TIME){ + pub fn advance_time(&mut self,time:Time){ self.position=self.extrapolated_position(time); self.velocity=self.extrapolated_velocity(time); self.time=time; @@ -420,9 +416,9 @@ impl Body { impl Default for PhysicsState{ fn default() -> Self { Self{ - time: 0, spawn_point:Planar64Vec3::new(0,50,0), body: Body::with_pva(Planar64Vec3::new(0,50,0),Planar64Vec3::new(0,0,0),Planar64Vec3::new(0,-100,0)), + time: Time::ZERO, style:StyleModifiers::default(), grounded: false, contacts: std::collections::HashMap::new(), @@ -628,7 +624,7 @@ impl PhysicsState { } } //tickless gaming - pub fn run(&mut self, time_limit:TIME){ + pub 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 @@ -637,7 +633,7 @@ impl PhysicsState { } } - pub fn advance_time(&mut self, time: TIME){ + pub fn advance_time(&mut self, time: Time){ self.body.advance_time(time); self.time=time; } @@ -672,7 +668,7 @@ impl PhysicsState { } fn next_strafe_instruction(&self) -> Option<TimedInstruction<PhysicsInstruction>> { return Some(TimedInstruction{ - time:(self.time*self.style.strafe_tick_num/self.style.strafe_tick_den+1)*self.style.strafe_tick_den/self.style.strafe_tick_num, + time:self.style.strafe_tick_rate.div_int(self.style.strafe_tick_rate.mul_int(self.time)+1), //only poll the physics if there is a before and after mouse event instruction:PhysicsInstruction::StrafeTick }); @@ -727,7 +723,7 @@ impl PhysicsState { let mut a=target_diff/time_delta; self.contact_constrain_acceleration(&mut a); self.body.acceleration=a; - self.walk.target_time=self.body.time+((time_delta as f64)*1_000_000_000f64) as TIME; + self.walk.target_time=self.body.time+Time::from(time_delta); self.walk.state=WalkEnum::Transient; } }else{ @@ -755,7 +751,7 @@ impl PhysicsState { } aabb } - fn predict_collision_end(&self,time:TIME,time_limit:TIME,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> { + fn predict_collision_end(&self,time:Time,time_limit:Time,collision_data:&RelativeCollision) -> Option<TimedInstruction<PhysicsInstruction>> { //must treat cancollide false objects differently: you may not exit through the same face you entered. //RelativeCollsion must reference the full model instead of a particular face //this is Ctrl+C Ctrl+V of predict_collision_start but with v=-v before the calc and t=-t after the calc @@ -773,7 +769,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*-t{ //collect valid t best_time=t_time; @@ -786,7 +782,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.x+a.x*-t<0f32{ //collect valid t best_time=t_time; @@ -818,7 +814,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*-t{ //collect valid t best_time=t_time; @@ -831,7 +827,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.y+a.y*-t<0f32{ //collect valid t best_time=t_time; @@ -863,7 +859,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*-t{ //collect valid t best_time=t_time; @@ -876,7 +872,7 @@ impl PhysicsState { //must be moving towards surface to collide //must beat the current soonest collision time //must be moving towards surface - let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as TIME; + let t_time=self.body.time+((-t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.z+a.z*-t<0f32{ //collect valid t best_time=t_time; @@ -909,7 +905,7 @@ impl PhysicsState { } None } - fn predict_collision_start(&self,time:TIME,time_limit:TIME,model_id:u32) -> Option<TimedInstruction<PhysicsInstruction>> { + fn predict_collision_start(&self,time:Time,time_limit:Time,model_id:u32) -> Option<TimedInstruction<PhysicsInstruction>> { let mesh0=self.mesh(); let mesh1=self.models.get(model_id as usize).unwrap().mesh(); let (p,v,a,body_time)=(self.body.position,self.body.velocity,self.body.acceleration,self.body.time); @@ -921,7 +917,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.x+a.x*t{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -937,7 +933,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.x+a.x*t<0f32{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -954,7 +950,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.y+a.y*t{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -970,7 +966,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.y+a.y*t<0f32{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -987,7 +983,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&0f32<v.z+a.z*t{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -1003,7 +999,7 @@ impl PhysicsState { //must collide now or in the future //must beat the current soonest collision time //must be moving towards surface - let t_time=body_time+((t as f64)*1_000_000_000f64) as TIME; + let t_time=body_time+((t as f64)*1_000_000_000f64) as Time; if time<=t_time&&t_time<best_time&&v.z+a.z*t<0f32{ let dp=self.body.extrapolated_position(t_time)-p; //faces must be overlapping @@ -1031,7 +1027,7 @@ impl PhysicsState { impl crate::instruction::InstructionEmitter<PhysicsInstruction> for PhysicsState { //this little next instruction function can cache its return value and invalidate the cached value by watching the State. - fn next_instruction(&self,time_limit:TIME) -> Option<TimedInstruction<PhysicsInstruction>> { + fn next_instruction(&self,time_limit:Time) -> Option<TimedInstruction<PhysicsInstruction>> { //JUST POLLING!!! NO MUTATION let mut collector = crate::instruction::InstructionCollector::new(time_limit); //check for collision stop instructions with curent contacts