aboutsummaryrefslogtreecommitdiff
path: root/stockton-render/src/draw/camera.rs
diff options
context:
space:
mode:
authortcmal <me@aria.rip>2024-08-25 17:44:21 +0100
committertcmal <me@aria.rip>2024-08-25 17:44:21 +0100
commit2111c1248b08236a839dcf22036f92735bceb31c (patch)
tree9313da344b7134a913d1d917162e55b35fe1e74f /stockton-render/src/draw/camera.rs
parent102e166b040030b590df83888a1d1a47d0130f10 (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.rs339
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");
+ }
}