1 use std::f32::consts::{FRAC_PI_2, PI}; 2 use std::time::{Duration, Instant}; 3 4 use cgmath::{Decomposed, InnerSpace, Matrix4, Point3, Rad, Vector3}; 5 use winit_24::dpi::{LogicalPosition, PhysicalPosition}; 6 use winit_24::event::{ElementState, MouseScrollDelta, VirtualKeyCode}; 7 use crate::render::OPENGL_TO_WGPU_MATRIX; 8 use imgui::Condition; 9 use imgui::*; 10 11 #[derive(Clone, Copy, Debug, PartialEq)] 12 pub struct Camera { 13 pub position: Point3<f32>, 14 pub yaw: Rad<f32>, 15 pub pitch: Rad<f32>, 16 } 17 18 impl Camera { 19 pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>( 20 position: V, 21 yaw: Y, 22 pitch: P, 23 ) -> Self { 24 Self { 25 position: position.into(), 26 yaw: yaw.into(), 27 pitch: pitch.into(), 28 } 29 } 30 31 pub fn calc_matrix(&self, projection: cgmath::Matrix4<f32>) -> Matrix4<f32> { 32 33 let view_vector = Point3::new( 34 (1.0 * self.pitch.0.sin() * self.yaw.0.sin()), 35 (1.0 * self.pitch.0.cos()), 36 (1.0 * self.pitch.0.sin() * self.yaw.0.cos()), 37 ); 38 39 let mx_view = Matrix4::look_at( 40 self.position, 41 Point3::new( 42 view_vector.x + self.position.x, 43 view_vector.y + self.position.y, 44 view_vector.z + self.position.z, 45 ), 46 Vector3::unit_y(), 47 ); 48 // I don't know how this works, but it limits pitching to like 49 // 70 degrees. Lame 50 // let mx_view = Matrix4::look_at_dir( 51 // self.position, 52 // Vector3::new( 53 // self.yaw.0.cos(), 54 // self.pitch.0.sin(), 55 // self.yaw.0.sin(), 56 // ).normalize(), 57 // Vector3::unit_y(), 58 // ); 59 let mx_correction = OPENGL_TO_WGPU_MATRIX; 60 mx_correction * projection * mx_view 61 } 62 } 63 64 #[derive(Debug)] 65 pub struct CameraController { 66 amount_left: f32, 67 amount_right: f32, 68 amount_forward: f32, 69 amount_backward: f32, 70 amount_up: f32, 71 amount_down: f32, 72 rotate_horizontal: f32, 73 rotate_vertical: f32, 74 scroll: f32, 75 speed: f32, 76 sensitivity: f32, 77 active: bool, 78 } 79 80 impl CameraController { 81 pub fn new(speed: f32, sensitivity: f32) -> Self { 82 Self { 83 amount_left: 0.0, 84 amount_right: 0.0, 85 amount_forward: 0.0, 86 amount_backward: 0.0, 87 amount_up: 0.0, 88 amount_down: 0.0, 89 rotate_horizontal: 0.0, 90 rotate_vertical: 0.0, 91 scroll: 0.0, 92 speed, 93 sensitivity, 94 active: true 95 } 96 } 97 98 pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool { 99 let amount = if state == ElementState::Pressed { 100 1.0 101 } else { 102 0.0 103 }; 104 let active = match key { 105 VirtualKeyCode::P => { 106 if state == ElementState::Pressed { 107 self.active = !self.active; 108 } 109 self.active 110 }, 111 _ => {self.active} 112 }; 113 if active { 114 match key { 115 VirtualKeyCode::W | VirtualKeyCode::Up => { 116 self.amount_forward = amount; 117 true 118 } 119 VirtualKeyCode::S | VirtualKeyCode::Down => { 120 self.amount_backward = amount; 121 true 122 } 123 VirtualKeyCode::A | VirtualKeyCode::Left => { 124 self.amount_left = amount; 125 true 126 } 127 VirtualKeyCode::D | VirtualKeyCode::Right => { 128 self.amount_right = amount; 129 true 130 } 131 VirtualKeyCode::Space => { 132 self.amount_up = amount; 133 true 134 } 135 VirtualKeyCode::LShift => { 136 self.amount_down = amount; 137 true 138 } 139 _ => false, 140 } 141 } else { 142 false 143 } 144 } 145 146 pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) { 147 if self.active { 148 self.rotate_horizontal = -mouse_dx as f32; 149 self.rotate_vertical = mouse_dy as f32; 150 } 151 } 152 153 pub fn update_camera(&mut self, camera: &mut Camera, dt: f32) { 154 155 // Move forward/backward and left/right 156 let view_vector = Vector3::new( 157 (1.0 * camera.pitch.0.sin() * camera.yaw.0.sin()), 158 (1.0 * camera.pitch.0.cos()), 159 (1.0 * camera.pitch.0.sin() * camera.yaw.0.cos()), 160 ); 161 162 // Offset the yaw 90 degrees and set the pitch to level for our 163 // right / left hand translation vectors 164 let offset = camera.yaw.0 + PI/2.0; 165 let pitch = PI/2.0; 166 let left_vector = Vector3::new( 167 (1.0 * pitch.sin() * offset.sin()), 168 (1.0 * pitch.cos()), 169 (1.0 * pitch.sin() * offset.cos()), 170 ); 171 172 camera.position += view_vector * (self.amount_forward - self.amount_backward) * self.speed * dt; 173 camera.position += left_vector * (self.amount_left - self.amount_right) * self.speed * dt; 174 camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt; 175 176 // Rotate 177 camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt; 178 camera.pitch += Rad(self.rotate_vertical) * self.sensitivity * dt; 179 180 self.rotate_horizontal = 0.0; 181 self.rotate_vertical = 0.0; 182 183 // Keep the camera's angle from going too high/low. 184 if camera.pitch < -Rad(0.001) { 185 camera.pitch = -Rad(0.001); 186 } else if camera.pitch > Rad(PI - 0.001) { 187 camera.pitch = Rad(PI - 0.001); 188 } 189 } 190 } 191