use std::f32::consts::{FRAC_PI_2, PI}; use std::time::{Duration, Instant}; use cgmath::{Decomposed, InnerSpace, Matrix4, Point3, Rad, Vector3}; use winit_24::dpi::{LogicalPosition, PhysicalPosition}; use winit_24::event::{ElementState, MouseScrollDelta, VirtualKeyCode}; use crate::render::OPENGL_TO_WGPU_MATRIX; use imgui::Condition; use imgui::*; #[derive(Clone, Copy, Debug, PartialEq)] pub struct Camera { pub position: Point3, pub yaw: Rad, pub pitch: Rad, } impl Camera { pub fn new>, Y: Into>, P: Into>>( position: V, yaw: Y, pitch: P, ) -> Self { Self { position: position.into(), yaw: yaw.into(), pitch: pitch.into(), } } pub fn calc_matrix(&self, projection: cgmath::Matrix4) -> Matrix4 { let view_vector = Point3::new( (1.0 * self.pitch.0.sin() * self.yaw.0.sin()), (1.0 * self.pitch.0.cos()), (1.0 * self.pitch.0.sin() * self.yaw.0.cos()), ); let mx_view = Matrix4::look_at( self.position, Point3::new( view_vector.x + self.position.x, view_vector.y + self.position.y, view_vector.z + self.position.z, ), Vector3::unit_y(), ); // I don't know how this works, but it limits pitching to like // 70 degrees. Lame // let mx_view = Matrix4::look_at_dir( // self.position, // Vector3::new( // self.yaw.0.cos(), // self.pitch.0.sin(), // self.yaw.0.sin(), // ).normalize(), // Vector3::unit_y(), // ); let mx_correction = OPENGL_TO_WGPU_MATRIX; mx_correction * projection * mx_view } } #[derive(Debug)] pub struct CameraController { amount_left: f32, amount_right: f32, amount_forward: f32, amount_backward: f32, amount_up: f32, amount_down: f32, rotate_horizontal: f32, rotate_vertical: f32, scroll: f32, speed: f32, sensitivity: f32, active: bool, } impl CameraController { pub fn new(speed: f32, sensitivity: f32) -> Self { Self { amount_left: 0.0, amount_right: 0.0, amount_forward: 0.0, amount_backward: 0.0, amount_up: 0.0, amount_down: 0.0, rotate_horizontal: 0.0, rotate_vertical: 0.0, scroll: 0.0, speed, sensitivity, active: true } } pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool { let amount = if state == ElementState::Pressed { 1.0 } else { 0.0 }; let active = match key { VirtualKeyCode::P => { if state == ElementState::Pressed { self.active = !self.active; } self.active }, _ => {self.active} }; if active { match key { VirtualKeyCode::W | VirtualKeyCode::Up => { self.amount_forward = amount; true } VirtualKeyCode::S | VirtualKeyCode::Down => { self.amount_backward = amount; true } VirtualKeyCode::A | VirtualKeyCode::Left => { self.amount_left = amount; true } VirtualKeyCode::D | VirtualKeyCode::Right => { self.amount_right = amount; true } VirtualKeyCode::Space => { self.amount_up = amount; true } VirtualKeyCode::LShift => { self.amount_down = amount; true } _ => false, } } else { false } } pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) { if self.active { self.rotate_horizontal = -mouse_dx as f32; self.rotate_vertical = mouse_dy as f32; } } pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) { // Move forward/backward and left/right let view_vector = Vector3::new( (1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()), (1.0 * camera.pitch.0.cos()), (1.0 * camera.pitch.0.sin() * camera.yaw.0.cos()), ); // Offset the yaw 90 degrees and set the pitch to level for our // right / left hand translation vectors let offset = camera.yaw.0 + PI/2.0; let pitch = PI/2.0; let left_vector = Vector3::new( (1.0 * pitch.sin() * offset.sin()), (1.0 * pitch.cos()), (1.0 * pitch.sin() * offset.cos()), ); camera.position += view_vector * (self.amount_forward - self.amount_backward) * self.speed * dt; camera.position += left_vector * (self.amount_left - self.amount_right) * self.speed * dt; camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt; // Rotate camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt; camera.pitch += Rad(self.rotate_vertical) * self.sensitivity * dt; self.rotate_horizontal = 0.0; self.rotate_vertical = 0.0; // Keep the camera's angle from going too high/low. if camera.pitch < -Rad(0.001) { camera.pitch = -Rad(0.001); } else if camera.pitch > Rad(PI - 0.001) { camera.pitch = Rad(PI - 0.001); } } }