camera.rs
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