From 9a122658815b87d8cfb98606cf8e56df3bbff745 Mon Sep 17 00:00:00 2001 From: Quaternions Date: Thu, 12 Oct 2023 04:32:12 -0700 Subject: [PATCH] implement PhysicsCamera --- src/integer.rs | 131 +++++++++++++++++++++++++++++++++++++++++++++++++ src/physics.rs | 62 ++++++++++++++--------- 2 files changed, 170 insertions(+), 23 deletions(-) diff --git a/src/integer.rs b/src/integer.rs index dc273df..965739a 100644 --- a/src/integer.rs +++ b/src/integer.rs @@ -20,6 +20,47 @@ impl Time{ //should I have checked subtraction? force all time variables to be positive? } +#[derive(Clone,Copy,Hash)] +pub struct Ratio64{ + num:i64, + den:std::num::NonZeroU64, +} +impl Ratio64{ + pub const ONE:Self=Ratio64{num:1,den:unsafe{std::num::NonZeroU64::new_unchecked(1)}}; + pub fn mul_ratio(self,rhs:i64)->Self{ + Self{ + num:self.num*rhs, + den:self.den + } + } + pub fn mul_int(self,rhs:i64)->i64{ + rhs*self.num/self.den.get() as i64 + } + pub fn rhs_div_int(self,rhs:i64)->i64{ + rhs*self.den.get() as i64/self.num + } +} +#[derive(Clone,Hash)] +pub struct Ratio64Vec2{ + pub x:Ratio64, + pub y:Ratio64, +} +impl Ratio64Vec2{ + pub const ONE:Self=Self{x:Ratio64::ONE,y:Ratio64::ONE}; + pub fn mul_ratio(self,rhs:i64)->Self{ + Self{ + x:self.x.mul_ratio(rhs), + y:self.y.mul_ratio(rhs), + } + } + pub fn mul_int(self,rhs:glam::I64Vec2)->glam::I64Vec2{ + glam::i64vec2( + self.x.mul_int(rhs.x), + self.y.mul_int(rhs.y), + ) + } +} + ///[-1.0,1.0] = [-2^30,2^30] pub struct Unit32(i32); impl Unit32{ @@ -30,17 +71,83 @@ impl Unit32{ } ///[-pi,pi) = [-2^31,2^31-1] +#[derive(Clone,Copy,Hash)] pub struct Angle32(i32); impl Angle32{ + pub const FRAC_PI_2:Self=Self(2<<31); + pub const PI:Self=Self(-2<<32); + #[inline] + pub fn wrap_from_i64(theta:i64)->Self{ + //TODO: make this good + Self((theta.wrapping_add(2<<31).rem_euclid(2<<32)-(2<<31)) as i32) + } + #[inline] + pub fn clamp_from_i64(theta:i64)->Self{ + //TODO: make this good + Self(theta.clamp(i32::MIN as i64,i32::MAX as i64) as i32) + } + #[inline] + pub fn get(&self)->i32{ + self.0 + } + /// Note that theta_min can be larger than theta_max and it will clamp the other way + #[inline] + pub fn clamp(&self,theta_min:Self,theta_max:Self)->Self{ + //TODO: make this good + theta_min+Self::wrap_from_i64((self.0.wrapping_sub(theta_min.0) as i64).rem_euclid(2<<32).clamp(0,(theta_max.0.wrapping_sub(theta_min.0) as i64).rem_euclid(2<<32))) + } #[inline] pub fn cos(&self)->Unit32{ + //TODO: fix this rounding towards 0 Unit32(unsafe{((self.0 as f64*(std::f64::consts::PI/((2<<31) as f64))).cos()*((2<<30) as f64)).to_int_unchecked()}) } #[inline] pub fn sin(&self)->Unit32{ + //TODO: fix this rounding towards 0 Unit32(unsafe{((self.0 as f64*(std::f64::consts::PI/((2<<31) as f64))).sin()*((2<<30) as f64)).to_int_unchecked()}) } } +impl Into for Angle32{ + fn into(self)->f32{ + //TODO: make this good + (self.0 as f64/-(i32::MIN as f64)*std::f64::consts::PI) as f32 + } +} +impl std::ops::Neg for Angle32{ + type Output=Angle32; + #[inline] + fn neg(self)->Self::Output{ + Angle32(self.0.wrapping_neg()) + } +} +impl std::ops::Add for Angle32{ + type Output=Angle32; + #[inline] + fn add(self,rhs:Self)->Self::Output { + Angle32(self.0.wrapping_add(rhs.0)) + } +} +impl std::ops::Sub for Angle32{ + type Output=Angle32; + #[inline] + fn sub(self,rhs:Self)->Self::Output { + Angle32(self.0.wrapping_sub(rhs.0)) + } +} +impl std::ops::Mul for Angle32{ + type Output=Angle32; + #[inline] + fn mul(self,rhs:i32)->Self::Output { + Angle32(self.0.wrapping_mul(rhs)) + } +} +impl std::ops::Mul for Angle32{ + type Output=Angle32; + #[inline] + fn mul(self,rhs:Self)->Self::Output { + Angle32(self.0.wrapping_mul(rhs.0)) + } +} ///[-pi,pi) = [-2^31,2^31-1] pub struct Angle32Vec2(glam::IVec2); impl Angle32Vec2{ @@ -153,6 +260,30 @@ pub struct Planar64Mat3{ z_axis:Planar64Vec3, } +impl Planar64Mat3{ + #[inline] + pub fn from_cols(x_axis:Planar64Vec3,y_axis:Planar64Vec3,z_axis:Planar64Vec3)->Self{ + Self{ + x_axis, + y_axis, + z_axis, + } + } + #[inline] + pub fn from_rotation_y(angle:Angle32)->Self{ + let theta=angle.0 as f64*(std::f64::consts::PI/((2<<31) as f64)); + let (s,c)=theta.sin_cos(); + let (c,s)=(c*((2<<32) as f64),s*((2<<32) as f64)); + //TODO: fix this rounding towards 0 + let (c,s):(i64,i64)=(unsafe{c.to_int_unchecked()},unsafe{s.to_int_unchecked()}); + Self::from_cols( + Planar64Vec3(glam::i64vec3(c,0,-s)), + Planar64Vec3::Y, + Planar64Vec3(glam::i64vec3(s,0,c)), + ) + } +} + ///[-1.0,1.0] = [-2^32,2^32] #[derive(Clone,Copy)] pub struct Planar64Affine3{ diff --git a/src/physics.rs b/src/physics.rs index efa3add..2ac74e8 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,Angle32Vec2}; +use crate::integer::{Planar64,Planar64Vec3,Planar64Mat3,Angle32,Angle32Vec2,Ratio64Vec2}; #[derive(Debug)] pub enum PhysicsInstruction { @@ -153,41 +153,57 @@ impl WalkState { } } + + #[derive(Clone)] pub struct PhysicsCamera { offset: Planar64Vec3, - angles: glam::DVec2,//YAW AND THEN PITCH //punch: Planar64Vec3, //punch_velocity: Planar64Vec3, - sensitivity: glam::DVec2, - mouse:MouseState, + 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_pitch_lower_limit:Angle32, + angle_pitch_upper_limit:Angle32, + //angle limits could be an enum + struct that defines whether it's limited and selects clamp or wrap depending + // enum AngleLimit{ + // Unlimited, + // Limited(Angle32,Angle32), + // } + //pitch_limit:AngleLimit, + //yaw_limit:AngleLimit, } -#[inline] -fn mat3_from_rotation_y_f64(angle: f64) -> glam::Mat3 { - let (sina, cosa) = angle.sin_cos(); - glam::Mat3::from_cols( - Planar64Vec3::new(cosa as f32, 0.0, -sina as f32), - Planar64Vec3::Y, - Planar64Vec3::new(sina as f32, 0.0, cosa as f32), - ) -} impl PhysicsCamera { pub fn from_offset(offset:Planar64Vec3) -> Self { Self{ offset, - angles: glam::DVec2::ZERO, - sensitivity: glam::dvec2(1.0/1024.0,1.0/1024.0), + sensitivity:Ratio64Vec2::ONE.mul_ratio(200_000), mouse:MouseState{pos:glam::IVec2::ZERO,time:-1},//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, } } - pub fn simulate_move_angles(&self, mouse_pos: glam::IVec2) -> glam::DVec2 { - let mut a=self.angles-self.sensitivity*(mouse_pos-self.mouse.pos).as_dvec2(); - a.y=a.y.clamp(-std::f64::consts::FRAC_PI_2, std::f64::consts::FRAC_PI_2); - return a + pub fn move_mouse(&mut self,mouse_pos:glam::IVec2){ + let mut unclamped_mouse_pos=self.clamped_mouse_pos+mouse_pos-self.mouse.pos; + unclamped_mouse_pos.y=unclamped_mouse_pos.y.clamp( + self.sensitivity.x.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; } - fn simulate_move_rotation_y(&self, mouse_pos_x: i32) -> glam::Mat3 { - mat3_from_rotation_y_f64(self.angles.x-self.sensitivity.x*((mouse_pos_x-self.mouse.pos.x) as f64)) + 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()); + 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_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); + Planar64Mat3::from_rotation_y(Angle32::wrap_from_i64(ax)) } } @@ -1227,11 +1243,11 @@ impl crate::instruction::InstructionConsumer for PhysicsStat let mut refresh_walk_target_velocity=true; match input_instruction{ PhysicsInputInstruction::SetNextMouse(m) => { - self.camera.angles=self.camera.simulate_move_angles(self.next_mouse.pos); + self.camera.move_mouse(self.next_mouse.pos); (self.camera.mouse,self.next_mouse)=(self.next_mouse.clone(),m); }, PhysicsInputInstruction::ReplaceMouse(m0,m1) => { - self.camera.angles=self.camera.simulate_move_angles(m0.pos); + self.camera.move_mouse(m0.pos); (self.camera.mouse,self.next_mouse)=(m0,m1); }, PhysicsInputInstruction::SetMoveForward(s) => self.set_control(StyleModifiers::CONTROL_MOVEFORWARD,s),