diff options
author | tcmal <me@aria.rip> | 2024-08-25 17:44:21 +0100 |
---|---|---|
committer | tcmal <me@aria.rip> | 2024-08-25 17:44:21 +0100 |
commit | 2111c1248b08236a839dcf22036f92735bceb31c (patch) | |
tree | 9313da344b7134a913d1d917162e55b35fe1e74f /stockton-render/src/draw/camera.rs | |
parent | 102e166b040030b590df83888a1d1a47d0130f10 (diff) |
chore(all): style formatting and clippy fixes
Diffstat (limited to 'stockton-render/src/draw/camera.rs')
-rw-r--r-- | stockton-render/src/draw/camera.rs | 339 |
1 files changed, 171 insertions, 168 deletions
diff --git a/stockton-render/src/draw/camera.rs b/stockton-render/src/draw/camera.rs index 5737928..cb465fd 100644 --- a/stockton-render/src/draw/camera.rs +++ b/stockton-render/src/draw/camera.rs @@ -15,10 +15,10 @@ //! Things related to converting 3D world space to 2D screen space -use stockton_types::{Vector3, Matrix4}; +use stockton_types::{Matrix4, Vector3}; -use std::f32::consts::PI; use na::{look_at_lh, perspective_lh_zo, Mat4, Vec4}; +use std::f32::consts::PI; /// 90 degrees in radians const R89: f32 = (PI / 180.0) * 89.0; @@ -30,201 +30,204 @@ const R90: f32 = PI / 2.0; const R180: f32 = PI; fn euler_to_direction(euler: &Vector3) -> Vector3 { - let pitch = euler.x; - let yaw = euler.y; - let _roll = euler.z; // TODO: Support camera roll - - Vector3::new( - yaw.sin() * pitch.cos(), - pitch.sin(), - yaw.cos() * pitch.cos() - ) + let pitch = euler.x; + let yaw = euler.y; + let _roll = euler.z; // TODO: Support camera roll + + Vector3::new( + yaw.sin() * pitch.cos(), + pitch.sin(), + yaw.cos() * pitch.cos(), + ) } pub struct CameraSettings { - /// Position of the camera (world units) - pub position: Vector3, + /// Position of the camera (world units) + pub position: Vector3, - /// Rotation of the camera (euler angles in radians) - pub rotation: Vector3, + /// Rotation of the camera (euler angles in radians) + pub rotation: Vector3, - /// The up direction (normalized) - pub up: Vector3, + /// The up direction (normalized) + pub up: Vector3, - /// FOV (radians) - pub fov: f32, + /// FOV (radians) + pub fov: f32, - /// Near clipping plane (world units) - pub near: f32, + /// Near clipping plane (world units) + pub near: f32, - /// Far clipping plane (world units) - pub far: f32, + /// Far clipping plane (world units) + pub far: f32, } /// Holds settings related to the projection of world space to screen space /// Also holds maths for generating important matrices pub struct WorkingCamera { - /// Settings for the camera - settings: CameraSettings, + /// Settings for the camera + settings: CameraSettings, - /// Aspect ratio as a fraction - aspect_ratio: f32, + /// Aspect ratio as a fraction + aspect_ratio: f32, - /// Cached view projection matrix - vp_matrix: Mat4, + /// Cached view projection matrix + vp_matrix: Mat4, - /// If true, cached value needs updated - is_dirty: bool + /// If true, cached value needs updated + is_dirty: bool, } impl WorkingCamera { - /// Return a camera with default settings - pub fn defaults(aspect_ratio: f32) -> WorkingCamera { - WorkingCamera::with_settings(CameraSettings { - position: Vector3::new(0.0, 0.0, 0.0), - rotation: Vector3::new(0.0, R90, 0.0), - up: Vector3::new(0.0, 1.0, 0.0), - fov: f32::to_radians(90.0), - near: 0.1, - far: 1024.0, - }, aspect_ratio) - } - - /// Return a camera with the given settings - pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera { - WorkingCamera { - aspect_ratio, - settings, - vp_matrix: Mat4::identity(), - is_dirty: true - } - } - - /// Get the VP matrix, updating cache if needed - pub fn get_matrix<'a>(&'a mut self) -> &'a Mat4 { - // Update matrix if needed - if self.is_dirty { - self.vp_matrix = self.calc_vp_matrix(); - self.is_dirty = false; - } - - // Return the matrix - &self.vp_matrix - } - - /// Returns a matrix that transforms from world space to screen space - fn calc_vp_matrix(&self) -> Matrix4 { - // Get look direction from euler angles - let direction = euler_to_direction(&self.settings.rotation); - - // Converts world space to camera space - let view_matrix = look_at_lh( - &self.settings.position, - &(direction + &self.settings.position), - &self.settings.up - ); - - // Converts camera space to screen space - let projection_matrix = { - let mut temp = perspective_lh_zo( - self.aspect_ratio, - self.settings.fov, - self.settings.near, - self.settings.far - ); - - // Vulkan's co-ord system is different from OpenGLs - temp[(1, 1)] *= -1.0; - - temp - }; - - // Chain them together into a single matrix - projection_matrix * view_matrix - } - - /// Update the aspect ratio - pub fn update_aspect_ratio(&mut self, new: f32) { - self.aspect_ratio = new; - self.is_dirty = true; - } - - /// Apply rotation of the camera - /// `euler` should be euler angles in degrees - pub fn rotate(&mut self, euler: Vector3) { - // TODO - self.settings.rotation += euler; - - // Clamp -pi/2 < pitch < pi/2 - if self.settings.rotation.x > R89 { - self.settings.rotation.x = R89; - } else if self.settings.rotation.x <= -R89 { - self.settings.rotation.x = -R89; - } - - // -pi < yaw <= pi - if self.settings.rotation.y <= -R180 { - self.settings.rotation.y = R180 - self.settings.rotation.y % -R180; - } else if self.settings.rotation.y > 180.0 { - self.settings.rotation.y = -R180 + self.settings.rotation.y % R180; - } - - self.is_dirty = true; - } - - /// Move the camera by `delta`, relative to the camera's rotation - pub fn move_camera_relative(&mut self, delta: Vector3) { - let rot_matrix = Mat4::from_euler_angles( - -self.settings.rotation.x, - self.settings.rotation.y, - self.settings.rotation.z - ); - - let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0); - self.settings.position.x += new.x; - self.settings.position.y += new.y; - self.settings.position.z += new.z; - - self.is_dirty = true; - } - - pub fn camera_pos(&self) -> Vector3 { - self.settings.position - } + /// Return a camera with default settings + pub fn defaults(aspect_ratio: f32) -> WorkingCamera { + WorkingCamera::with_settings( + CameraSettings { + position: Vector3::new(0.0, 0.0, 0.0), + rotation: Vector3::new(0.0, R90, 0.0), + up: Vector3::new(0.0, 1.0, 0.0), + fov: f32::to_radians(90.0), + near: 0.1, + far: 1024.0, + }, + aspect_ratio, + ) + } + + /// Return a camera with the given settings + pub fn with_settings(settings: CameraSettings, aspect_ratio: f32) -> WorkingCamera { + WorkingCamera { + aspect_ratio, + settings, + vp_matrix: Mat4::identity(), + is_dirty: true, + } + } + + /// Get the VP matrix, updating cache if needed + pub fn get_matrix(&mut self) -> &Mat4 { + // Update matrix if needed + if self.is_dirty { + self.vp_matrix = self.calc_vp_matrix(); + self.is_dirty = false; + } + + // Return the matrix + &self.vp_matrix + } + + /// Returns a matrix that transforms from world space to screen space + fn calc_vp_matrix(&self) -> Matrix4 { + // Get look direction from euler angles + let direction = euler_to_direction(&self.settings.rotation); + + // Converts world space to camera space + let view_matrix = look_at_lh( + &self.settings.position, + &(self.settings.position + direction), + &self.settings.up, + ); + + // Converts camera space to screen space + let projection_matrix = { + let mut temp = perspective_lh_zo( + self.aspect_ratio, + self.settings.fov, + self.settings.near, + self.settings.far, + ); + + // Vulkan's co-ord system is different from OpenGLs + temp[(1, 1)] *= -1.0; + + temp + }; + + // Chain them together into a single matrix + projection_matrix * view_matrix + } + + /// Update the aspect ratio + pub fn update_aspect_ratio(&mut self, new: f32) { + self.aspect_ratio = new; + self.is_dirty = true; + } + + /// Apply rotation of the camera + /// `euler` should be euler angles in degrees + pub fn rotate(&mut self, euler: Vector3) { + // TODO + self.settings.rotation += euler; + + // Clamp -pi/2 < pitch < pi/2 + if self.settings.rotation.x > R89 { + self.settings.rotation.x = R89; + } else if self.settings.rotation.x <= -R89 { + self.settings.rotation.x = -R89; + } + + // -pi < yaw <= pi + if self.settings.rotation.y <= -R180 { + self.settings.rotation.y = R180 - self.settings.rotation.y % -R180; + } else if self.settings.rotation.y > 180.0 { + self.settings.rotation.y = -R180 + self.settings.rotation.y % R180; + } + + self.is_dirty = true; + } + + /// Move the camera by `delta`, relative to the camera's rotation + pub fn move_camera_relative(&mut self, delta: Vector3) { + let rot_matrix = Mat4::from_euler_angles( + -self.settings.rotation.x, + self.settings.rotation.y, + self.settings.rotation.z, + ); + + let new = rot_matrix * Vec4::new(delta.x, delta.y, delta.z, 1.0); + self.settings.position.x += new.x; + self.settings.position.y += new.y; + self.settings.position.z += new.z; + + self.is_dirty = true; + } + + pub fn camera_pos(&self) -> Vector3 { + self.settings.position + } } #[cfg(test)] mod tests { - use stockton_types::Matrix4; -use stockton_types::Vector3; - use draw::camera::WorkingCamera; + use draw::camera::WorkingCamera; + use stockton_types::Matrix4; + use stockton_types::Vector3; - fn contains_nan(mat: &Matrix4) -> bool{ - for x in mat.iter() { - if *x == std::f32::NAN { - return true; - } - } - return false; - } + fn contains_nan(mat: &Matrix4) -> bool { + for x in mat.iter() { + if (*x).is_nan() { + return true; + } + } + false + } - #[test] - fn camera_vp() { - let mut camera = WorkingCamera::defaults(16.0 / 9.0); + #[test] + fn camera_vp() { + let mut camera = WorkingCamera::defaults(16.0 / 9.0); - let old = camera.calc_vp_matrix(); - println!("initial vp matrix: {:?}", old); + let old = camera.calc_vp_matrix(); + println!("initial vp matrix: {:?}", old); - assert!(!contains_nan(&old), "No NaNs for initial matrix"); + assert!(!contains_nan(&old), "No NaNs for initial matrix"); - // Do a 180 - camera.rotate(Vector3::new(0.0, 180.0, 0.0)); + // Do a 180 + camera.rotate(Vector3::new(0.0, 180.0, 0.0)); - let new = camera.calc_vp_matrix(); - assert!(!contains_nan(&new), "No NaNs after rotating"); + let new = camera.calc_vp_matrix(); + assert!(!contains_nan(&new), "No NaNs after rotating"); - println!("new vp matrix: {:?}", new); + println!("new vp matrix: {:?}", new); - assert!(old != new, "VP Matrix changes when camera rotates"); - } + assert!(old != new, "VP Matrix changes when camera rotates"); + } } |