Overhaul flycam impl
This commit is contained in:
parent
cccc62e5a8
commit
24bcc55e74
141
src/camera.rs
141
src/camera.rs
|
@ -9,8 +9,10 @@ pub trait Camera {
|
||||||
|
|
||||||
pub struct Flycam {
|
pub struct Flycam {
|
||||||
// input
|
// input
|
||||||
is_up_pressed: bool,
|
is_world_up_pressed: bool,
|
||||||
is_down_pressed: bool,
|
is_world_down_pressed: bool,
|
||||||
|
is_cam_up_pressed: bool,
|
||||||
|
is_cam_down_pressed: bool,
|
||||||
is_forward_pressed: bool,
|
is_forward_pressed: bool,
|
||||||
is_backward_pressed: bool,
|
is_backward_pressed: bool,
|
||||||
is_left_pressed: bool,
|
is_left_pressed: bool,
|
||||||
|
@ -19,12 +21,15 @@ pub struct Flycam {
|
||||||
mouse_dy: f32,
|
mouse_dy: f32,
|
||||||
// state
|
// state
|
||||||
last_update: Instant,
|
last_update: Instant,
|
||||||
pan: f32,
|
euler_y: f32,
|
||||||
tilt: f32,
|
euler_x: f32,
|
||||||
|
velocity: Vec3,
|
||||||
position: Vec3,
|
position: Vec3,
|
||||||
// constants
|
// constants
|
||||||
speed: f32,
|
turn_sensitivity: f32,
|
||||||
turn_speed: f32,
|
thrust_mag: f32,
|
||||||
|
friction_coeff: f32,
|
||||||
|
drag_coeff: f32,
|
||||||
aspect: f32,
|
aspect: f32,
|
||||||
fovy: f32,
|
fovy: f32,
|
||||||
znear: f32,
|
znear: f32,
|
||||||
|
@ -32,10 +37,12 @@ pub struct Flycam {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flycam {
|
impl Flycam {
|
||||||
pub fn new(speed: f32, turn_speed: f32) -> Self {
|
pub fn new(turn_sensitivity: f32, thrust_mag: f32, friction_coeff: f32, drag_coeff: f32) -> Self {
|
||||||
Self {
|
Self {
|
||||||
is_up_pressed: false,
|
is_world_up_pressed: false,
|
||||||
is_down_pressed: false,
|
is_world_down_pressed: false,
|
||||||
|
is_cam_up_pressed: false,
|
||||||
|
is_cam_down_pressed: false,
|
||||||
is_forward_pressed: false,
|
is_forward_pressed: false,
|
||||||
is_backward_pressed: false,
|
is_backward_pressed: false,
|
||||||
is_left_pressed: false,
|
is_left_pressed: false,
|
||||||
|
@ -43,11 +50,14 @@ impl Flycam {
|
||||||
mouse_dx: 0.0,
|
mouse_dx: 0.0,
|
||||||
mouse_dy: 0.0,
|
mouse_dy: 0.0,
|
||||||
last_update: Instant::now(),
|
last_update: Instant::now(),
|
||||||
pan: 0.0,
|
euler_y: 0.0,
|
||||||
tilt: 0.0,
|
euler_x: 0.0,
|
||||||
|
velocity: Vec3::new(0.0, 0.0, 0.0),
|
||||||
position: Vec3::new(0.0, 0.5, 1.0),
|
position: Vec3::new(0.0, 0.5, 1.0),
|
||||||
speed,
|
turn_sensitivity,
|
||||||
turn_speed,
|
thrust_mag,
|
||||||
|
friction_coeff,
|
||||||
|
drag_coeff,
|
||||||
aspect: 1.0, // TODO compute from size
|
aspect: 1.0, // TODO compute from size
|
||||||
fovy: std::f32::consts::FRAC_PI_2,
|
fovy: std::f32::consts::FRAC_PI_2,
|
||||||
znear: 0.01,
|
znear: 0.01,
|
||||||
|
@ -61,10 +71,16 @@ impl Flycam {
|
||||||
let is_pressed = state == ElementState::Pressed;
|
let is_pressed = state == ElementState::Pressed;
|
||||||
match key {
|
match key {
|
||||||
VirtualKeyCode::Space => {
|
VirtualKeyCode::Space => {
|
||||||
self.is_up_pressed = is_pressed;
|
self.is_world_up_pressed = is_pressed;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::LShift => {
|
VirtualKeyCode::LShift => {
|
||||||
self.is_down_pressed = is_pressed;
|
self.is_world_down_pressed = is_pressed;
|
||||||
|
}
|
||||||
|
VirtualKeyCode::Q => {
|
||||||
|
self.is_cam_down_pressed = is_pressed;
|
||||||
|
}
|
||||||
|
VirtualKeyCode::E => {
|
||||||
|
self.is_cam_up_pressed = is_pressed;
|
||||||
}
|
}
|
||||||
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
||||||
self.is_forward_pressed = is_pressed;
|
self.is_forward_pressed = is_pressed;
|
||||||
|
@ -96,55 +112,82 @@ impl Flycam {
|
||||||
self.last_update = Instant::now();
|
self.last_update = Instant::now();
|
||||||
let dt = dt.as_micros() as f32 / 1_000_000.0;
|
let dt = dt.as_micros() as f32 / 1_000_000.0;
|
||||||
|
|
||||||
let t = self.turn_speed;
|
self.update_orientation(dt);
|
||||||
self.pan += t * self.mouse_dx;
|
self.update_kinematic(dt);
|
||||||
self.tilt += t * self.mouse_dy;
|
}
|
||||||
|
|
||||||
|
fn update_orientation(&mut self, dt: f32) {
|
||||||
|
let t = self.turn_sensitivity;
|
||||||
|
|
||||||
|
self.euler_x -= t * self.mouse_dy; // mouse +y = 2D plane down = look down = 3d space -x
|
||||||
|
self.euler_y -= t * self.mouse_dx; // mouse +x = 2D plane right = look to the right = 3d space -y
|
||||||
|
|
||||||
self.mouse_dx = 0.0;
|
self.mouse_dx = 0.0;
|
||||||
self.mouse_dy = 0.0;
|
self.mouse_dy = 0.0;
|
||||||
|
|
||||||
let tilt_limit = std::f32::consts::FRAC_PI_2;
|
// Clamp euler_x to [-pi/2, pi/2]
|
||||||
if self.tilt < -tilt_limit {
|
let euler_x_limit = std::f32::consts::FRAC_PI_2;
|
||||||
self.tilt = -tilt_limit;
|
if self.euler_x < -euler_x_limit {
|
||||||
} else if self.tilt > tilt_limit {
|
self.euler_x = -euler_x_limit;
|
||||||
self.tilt = tilt_limit;
|
} else if self.euler_x > euler_x_limit {
|
||||||
|
self.euler_x = euler_x_limit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let s = dt * self.speed;
|
fn update_kinematic(&mut self, dt: f32) {
|
||||||
|
let net_acc = self.get_thrust_acc() + self.get_friction_acc() + self.get_drag_acc();
|
||||||
|
|
||||||
|
let delta_vel = net_acc * dt;
|
||||||
|
self.velocity += delta_vel;
|
||||||
|
|
||||||
|
let delta_pos = self.velocity * dt;
|
||||||
|
self.position += delta_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_thrust_acc(&self) -> glam::Vec3 {
|
||||||
let axis = Self::key_axis;
|
let axis = Self::key_axis;
|
||||||
let truck = s * axis(self.is_backward_pressed, self.is_forward_pressed);
|
|
||||||
let dolly = s * axis(self.is_right_pressed, self.is_left_pressed);
|
let thruster_cam_x = axis(self.is_left_pressed, self.is_right_pressed);
|
||||||
let boom = s * axis(self.is_down_pressed, self.is_up_pressed);
|
let thruster_cam_y = axis(self.is_cam_down_pressed, self.is_cam_up_pressed);
|
||||||
self.move_position(truck, dolly, boom);
|
let thruster_cam_z = -axis(self.is_backward_pressed, self.is_forward_pressed); // forward is -z
|
||||||
|
let thruster_world_y = axis(self.is_world_down_pressed, self.is_world_up_pressed);
|
||||||
|
|
||||||
|
let thrusters_cam = Vec3::new(thruster_cam_x, thruster_cam_y, thruster_cam_z);
|
||||||
|
let thrusters_world = Vec3::new(0.0, thruster_world_y, 0.0);
|
||||||
|
|
||||||
|
let cam_to_world = self.get_orientation();
|
||||||
|
let thrusters_total = thrusters_world + cam_to_world * thrusters_cam;
|
||||||
|
|
||||||
|
self.thrust_mag * thrusters_total
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_friction_acc(&self) -> glam::Vec3 {
|
||||||
|
self.friction_coeff * -self.velocity
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_drag_acc(&self) -> glam::Vec3 {
|
||||||
|
0.5 * self.drag_coeff * -self.velocity * self.velocity.length()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn key_axis(negative: bool, positive: bool) -> f32 {
|
fn key_axis(negative: bool, positive: bool) -> f32 {
|
||||||
if negative {
|
if negative {
|
||||||
if positive {
|
if positive {
|
||||||
0.0
|
0.0 // positive + negative cancel out
|
||||||
} else {
|
} else {
|
||||||
-1.0
|
-1.0 // negative only
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if positive {
|
if positive {
|
||||||
1.0
|
1.0 // positive only
|
||||||
} else {
|
} else {
|
||||||
0.0
|
0.0 // neutral
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn move_position(&mut self, truck: f32, dolly: f32, boom: f32) {
|
fn get_orientation(&self) -> glam::Quat {
|
||||||
// truck direction from straight down
|
// returns rotation from camera axes to world axes
|
||||||
let h = Vec2::new(self.pan.sin(), -self.pan.cos());
|
Quat::from_euler(glam::EulerRot::YXZ, self.euler_y, self.euler_x, 0.0)
|
||||||
// truck direction from the side
|
|
||||||
let v = Vec2::new(self.tilt.cos(), -self.tilt.sin());
|
|
||||||
// composite to get forward direction
|
|
||||||
let truck_to = Vec3::new(h.x * v.x, v.y, h.y * v.x);
|
|
||||||
|
|
||||||
let dolly_to = Vec3::new(-self.pan.cos(), 0.0, -self.pan.sin());
|
|
||||||
|
|
||||||
self.position += (truck_to * truck) + (dolly_to * dolly);
|
|
||||||
self.position.y += boom;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,10 +197,14 @@ impl Camera for Flycam {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_vp(&self) -> [[f32; 4]; 4] {
|
fn get_vp(&self) -> [[f32; 4]; 4] {
|
||||||
let orientation = Quat::from_euler(glam::EulerRot::XYZ, self.tilt, self.pan, 0.0);
|
// view matrix is inverted camera pose (world space to camera space)
|
||||||
let rotation = Mat4::from_quat(orientation);
|
let rotation = Mat4::from_quat(self.get_orientation().inverse());
|
||||||
let view = rotation * Mat4::from_translation(-self.position);
|
let translation = Mat4::from_translation(-self.position);
|
||||||
|
let view = rotation * translation;
|
||||||
|
|
||||||
|
// perspective projection
|
||||||
let proj = Mat4::perspective_rh_gl(self.fovy, self.aspect, self.znear, self.zfar);
|
let proj = Mat4::perspective_rh_gl(self.fovy, self.aspect, self.znear, self.zfar);
|
||||||
|
|
||||||
let vp = proj * view;
|
let vp = proj * view;
|
||||||
vp.to_cols_array_2d()
|
vp.to_cols_array_2d()
|
||||||
}
|
}
|
||||||
|
|
|
@ -149,7 +149,7 @@ async fn make_window_renderer(window: &winit::window::Window) -> Renderer {
|
||||||
fn main() {
|
fn main() {
|
||||||
let event_loop = EventLoop::new();
|
let event_loop = EventLoop::new();
|
||||||
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
let window = WindowBuilder::new().build(&event_loop).unwrap();
|
||||||
let mut camera = Flycam::new(10.0, 0.002);
|
let mut camera = Flycam::new(0.002, 50.0, 5.0, 5.0);
|
||||||
let mut is_grabbed = false;
|
let mut is_grabbed = false;
|
||||||
let mut ren = pollster::block_on(make_window_renderer(&window));
|
let mut ren = pollster::block_on(make_window_renderer(&window));
|
||||||
// let mut state: Box<dyn WorldState> = Box::new(Planets::new(&mut ren));
|
// let mut state: Box<dyn WorldState> = Box::new(Planets::new(&mut ren));
|
||||||
|
|
Loading…
Reference in New Issue