165 lines
4.8 KiB
Rust
165 lines
4.8 KiB
Rust
use glam::{Mat4, Quat, Vec2, Vec3};
|
|
use std::time::Instant;
|
|
use winit::event::{ElementState, VirtualKeyCode};
|
|
|
|
pub trait Camera {
|
|
fn get_eye(&self) -> [f32; 4];
|
|
fn get_vp(&self) -> [[f32; 4]; 4];
|
|
}
|
|
|
|
pub struct Flycam {
|
|
// input
|
|
is_up_pressed: bool,
|
|
is_down_pressed: bool,
|
|
is_forward_pressed: bool,
|
|
is_backward_pressed: bool,
|
|
is_left_pressed: bool,
|
|
is_right_pressed: bool,
|
|
mouse_dx: f32,
|
|
mouse_dy: f32,
|
|
// state
|
|
last_update: Instant,
|
|
pan: f32,
|
|
tilt: f32,
|
|
position: Vec3,
|
|
// constants
|
|
speed: f32,
|
|
turn_speed: f32,
|
|
aspect: f32,
|
|
fovy: f32,
|
|
znear: f32,
|
|
zfar: f32,
|
|
}
|
|
|
|
impl Flycam {
|
|
pub fn new(speed: f32, turn_speed: f32) -> Self {
|
|
Self {
|
|
is_up_pressed: false,
|
|
is_down_pressed: false,
|
|
is_forward_pressed: false,
|
|
is_backward_pressed: false,
|
|
is_left_pressed: false,
|
|
is_right_pressed: false,
|
|
mouse_dx: 0.0,
|
|
mouse_dy: 0.0,
|
|
last_update: Instant::now(),
|
|
pan: 0.0,
|
|
tilt: 0.0,
|
|
position: Vec3::new(0.0, 0.5, 1.0),
|
|
speed,
|
|
turn_speed,
|
|
aspect: 1.0, // TODO compute from size
|
|
fovy: std::f32::consts::FRAC_PI_2,
|
|
znear: 0.01,
|
|
zfar: 100.0,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Flycam {
|
|
pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) {
|
|
let is_pressed = state == ElementState::Pressed;
|
|
match key {
|
|
VirtualKeyCode::Space => {
|
|
self.is_up_pressed = is_pressed;
|
|
}
|
|
VirtualKeyCode::LShift => {
|
|
self.is_down_pressed = is_pressed;
|
|
}
|
|
VirtualKeyCode::W | VirtualKeyCode::Up => {
|
|
self.is_forward_pressed = is_pressed;
|
|
}
|
|
VirtualKeyCode::A | VirtualKeyCode::Left => {
|
|
self.is_left_pressed = is_pressed;
|
|
}
|
|
VirtualKeyCode::S | VirtualKeyCode::Down => {
|
|
self.is_backward_pressed = is_pressed;
|
|
}
|
|
VirtualKeyCode::D | VirtualKeyCode::Right => {
|
|
self.is_right_pressed = is_pressed;
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
|
|
pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
|
|
self.mouse_dx += mouse_dx as f32;
|
|
self.mouse_dy += mouse_dy as f32;
|
|
}
|
|
|
|
pub fn resize(&mut self, width: u32, height: u32) {
|
|
self.aspect = (width as f32) / (height as f32);
|
|
}
|
|
|
|
pub fn update(&mut self) {
|
|
let dt = self.last_update.elapsed();
|
|
self.last_update = Instant::now();
|
|
let dt = dt.as_micros() as f32 / 1_000_000.0;
|
|
|
|
let t = self.turn_speed;
|
|
self.pan += t * self.mouse_dx;
|
|
self.tilt += t * self.mouse_dy;
|
|
self.mouse_dx = 0.0;
|
|
self.mouse_dy = 0.0;
|
|
|
|
let tilt_limit = std::f32::consts::FRAC_PI_2;
|
|
if self.tilt < -tilt_limit {
|
|
self.tilt = -tilt_limit;
|
|
} else if self.tilt > tilt_limit {
|
|
self.tilt = tilt_limit;
|
|
}
|
|
|
|
let s = dt * self.speed;
|
|
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 boom = s * axis(self.is_down_pressed, self.is_up_pressed);
|
|
self.move_position(truck, dolly, boom);
|
|
}
|
|
|
|
fn key_axis(negative: bool, positive: bool) -> f32 {
|
|
if negative {
|
|
if positive {
|
|
0.0
|
|
} else {
|
|
-1.0
|
|
}
|
|
} else {
|
|
if positive {
|
|
1.0
|
|
} else {
|
|
0.0
|
|
}
|
|
}
|
|
}
|
|
|
|
fn move_position(&mut self, truck: f32, dolly: f32, boom: f32) {
|
|
// truck direction from straight down
|
|
let h = Vec2::new(self.pan.sin(), -self.pan.cos());
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
impl Camera for Flycam {
|
|
fn get_eye(&self) -> [f32; 4] {
|
|
self.position.extend(0.0).to_array()
|
|
}
|
|
|
|
fn get_vp(&self) -> [[f32; 4]; 4] {
|
|
let orientation = Quat::from_euler(glam::EulerRot::XYZ, self.tilt, self.pan, 0.0);
|
|
let rotation = Mat4::from_quat(orientation);
|
|
let view = rotation * Mat4::from_translation(-self.position);
|
|
let proj = Mat4::perspective_rh_gl(self.fovy, self.aspect, self.znear, self.zfar);
|
|
let vp = proj * view;
|
|
vp.to_cols_array_2d()
|
|
}
|
|
}
|