camera.rs 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. use cgmath::*;
  2. use std::f32::consts::FRAC_PI_2;
  3. use std::time::Duration;
  4. use winit::dpi::PhysicalPosition;
  5. use winit::event::*;
  6. #[rustfmt::skip]
  7. pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
  8. 1.0, 0.0, 0.0, 0.0,
  9. 0.0, 1.0, 0.0, 0.0,
  10. 0.0, 0.0, 0.5, 0.0,
  11. 0.0, 0.0, 0.5, 1.0,
  12. );
  13. #[derive(Debug)]
  14. pub struct Camera {
  15. pub position: Point3<f32>,
  16. yaw: Rad<f32>,
  17. pitch: Rad<f32>,
  18. }
  19. impl Camera {
  20. pub fn new<V: Into<Point3<f32>>, Y: Into<Rad<f32>>, P: Into<Rad<f32>>>(
  21. position: V,
  22. yaw: Y,
  23. pitch: P,
  24. ) -> Self {
  25. Self {
  26. position: position.into(),
  27. yaw: yaw.into(),
  28. pitch: pitch.into(),
  29. }
  30. }
  31. pub fn calc_matrix(&self) -> Matrix4<f32> {
  32. Matrix4::look_at_dir(
  33. self.position,
  34. Vector3::new(self.yaw.0.cos(), self.pitch.0.sin(), self.yaw.0.sin()).normalize(),
  35. Vector3::unit_y(),
  36. )
  37. }
  38. }
  39. pub struct Projection {
  40. aspect: f32,
  41. fovy: Rad<f32>,
  42. znear: f32,
  43. zfar: f32,
  44. }
  45. impl Projection {
  46. pub fn new<F: Into<Rad<f32>>>(width: u32, height: u32, fovy: F, znear: f32, zfar: f32) -> Self {
  47. Self {
  48. aspect: width as f32 / height as f32,
  49. fovy: fovy.into(),
  50. znear,
  51. zfar,
  52. }
  53. }
  54. pub fn resize(&mut self, width: u32, height: u32) {
  55. self.aspect = width as f32 / height as f32;
  56. }
  57. pub fn calc_matrix(&self) -> Matrix4<f32> {
  58. OPENGL_TO_WGPU_MATRIX * perspective(self.fovy, self.aspect, self.znear, self.zfar)
  59. }
  60. }
  61. #[derive(Debug)]
  62. pub struct CameraController {
  63. amount_left: f32,
  64. amount_right: f32,
  65. amount_forward: f32,
  66. amount_backward: f32,
  67. amount_up: f32,
  68. amount_down: f32,
  69. rotate_horizontal: f32,
  70. rotate_vertical: f32,
  71. scroll: f32,
  72. speed: f32,
  73. sensitivity: f32,
  74. }
  75. impl CameraController {
  76. pub fn new(speed: f32, sensitivity: f32) -> Self {
  77. Self {
  78. amount_left: 0.0,
  79. amount_right: 0.0,
  80. amount_forward: 0.0,
  81. amount_backward: 0.0,
  82. amount_up: 0.0,
  83. amount_down: 0.0,
  84. rotate_horizontal: 0.0,
  85. rotate_vertical: 0.0,
  86. scroll: 0.0,
  87. speed,
  88. sensitivity,
  89. }
  90. }
  91. pub fn process_keyboard(&mut self, key: VirtualKeyCode, state: ElementState) -> bool {
  92. let amount = if state == ElementState::Pressed {
  93. 1.0
  94. } else {
  95. 0.0
  96. };
  97. match key {
  98. VirtualKeyCode::W | VirtualKeyCode::Up => {
  99. self.amount_forward = amount;
  100. true
  101. }
  102. VirtualKeyCode::S | VirtualKeyCode::Down => {
  103. self.amount_backward = amount;
  104. true
  105. }
  106. VirtualKeyCode::A | VirtualKeyCode::Left => {
  107. self.amount_left = amount;
  108. true
  109. }
  110. VirtualKeyCode::D | VirtualKeyCode::Right => {
  111. self.amount_right = amount;
  112. true
  113. }
  114. VirtualKeyCode::Space => {
  115. self.amount_up = amount;
  116. true
  117. }
  118. VirtualKeyCode::LShift => {
  119. self.amount_down = amount;
  120. true
  121. }
  122. _ => false,
  123. }
  124. }
  125. pub fn process_mouse(&mut self, mouse_dx: f64, mouse_dy: f64) {
  126. self.rotate_horizontal = mouse_dx as f32;
  127. self.rotate_vertical = mouse_dy as f32;
  128. }
  129. pub fn process_scroll(&mut self, delta: &MouseScrollDelta) {
  130. self.scroll = match delta {
  131. // I'm assuming a line is about 100 pixels
  132. MouseScrollDelta::LineDelta(_, scroll) => -scroll * 0.5,
  133. MouseScrollDelta::PixelDelta(PhysicalPosition { y: scroll, .. }) => -*scroll as f32,
  134. };
  135. }
  136. pub fn update_camera(&mut self, camera: &mut Camera, dt: Duration) {
  137. let dt = dt.as_secs_f32();
  138. // Move forward/backward and left/right
  139. let (yaw_sin, yaw_cos) = camera.yaw.0.sin_cos();
  140. let forward = Vector3::new(yaw_cos, 0.0, yaw_sin).normalize();
  141. let right = Vector3::new(-yaw_sin, 0.0, yaw_cos).normalize();
  142. camera.position += forward * (self.amount_forward - self.amount_backward) * self.speed * dt;
  143. camera.position += right * (self.amount_right - self.amount_left) * self.speed * dt;
  144. // Move in/out (aka. "zoom")
  145. // Note: this isn't an actual zoom. The camera's position
  146. // changes when zooming. I've added this to make it easier
  147. // to get closer to an object you want to focus on.
  148. let (pitch_sin, pitch_cos) = camera.pitch.0.sin_cos();
  149. let scrollward =
  150. Vector3::new(pitch_cos * yaw_cos, pitch_sin, pitch_cos * yaw_sin).normalize();
  151. camera.position += scrollward * self.scroll * self.speed * self.sensitivity * dt;
  152. self.scroll = 0.0;
  153. // Move up/down. Since we don't use roll, we can just
  154. // modify the y coordinate directly.
  155. camera.position.y += (self.amount_up - self.amount_down) * self.speed * dt;
  156. // Rotate
  157. camera.yaw += Rad(self.rotate_horizontal) * self.sensitivity * dt;
  158. camera.pitch += Rad(-self.rotate_vertical) * self.sensitivity * dt;
  159. // If process_mouse isn't called every frame, these values
  160. // will not get set to zero, and the camera will rotate
  161. // when moving in a non cardinal direction.
  162. self.rotate_horizontal = 0.0;
  163. self.rotate_vertical = 0.0;
  164. // Keep the camera's angle from going too high/low.
  165. if camera.pitch < -Rad(FRAC_PI_2) {
  166. camera.pitch = -Rad(FRAC_PI_2);
  167. } else if camera.pitch > Rad(FRAC_PI_2) {
  168. camera.pitch = Rad(FRAC_PI_2);
  169. }
  170. }
  171. }