implement PhysicsCamera

This commit is contained in:
Quaternions 2023-10-12 04:32:12 -07:00
parent 3ff73ed0bc
commit 9a12265881
2 changed files with 170 additions and 23 deletions

View File

@ -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<f32> 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<Angle32> for Angle32{
type Output=Angle32;
#[inline]
fn add(self,rhs:Self)->Self::Output {
Angle32(self.0.wrapping_add(rhs.0))
}
}
impl std::ops::Sub<Angle32> for Angle32{
type Output=Angle32;
#[inline]
fn sub(self,rhs:Self)->Self::Output {
Angle32(self.0.wrapping_sub(rhs.0))
}
}
impl std::ops::Mul<i32> for Angle32{
type Output=Angle32;
#[inline]
fn mul(self,rhs:i32)->Self::Output {
Angle32(self.0.wrapping_mul(rhs))
}
}
impl std::ops::Mul<Angle32> 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{

View File

@ -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<PhysicsInstruction> 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),