From 85756e5f6f0b194924068404b470c1791ad8c3e8 Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 08:21:42 +0100 Subject: [PATCH 1/7] Revert "Planar projection camera (#42)" This reverts commit 2d34e6da9cd8deb010ceed82eab3d6ed6c98bf60. --- src/camera.rs | 65 +++------------------------ src/prelude/math.rs | 107 -------------------------------------------- 2 files changed, 5 insertions(+), 167 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 76b4fda..b295986 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -159,11 +159,6 @@ pub enum ProjectionType { /// The field of view angle in the vertical direction. field_of_view_y: Radians, }, - /// General planar projection - Planar { - /// The field of view angle in the vertical direction. - field_of_view_y: Radians, - }, } /// @@ -222,24 +217,6 @@ impl Camera { camera } - /// - /// New camera which projects the world with a general planar projection. - /// - pub fn new_planar( - viewport: Viewport, - position: Vec3, - target: Vec3, - up: Vec3, - field_of_view_y: impl Into, - z_near: f32, - z_far: f32, - ) -> Self { - let mut camera = Camera::new(viewport); - camera.set_view(position, target, up); - camera.set_planar_projection(field_of_view_y, z_near, z_far); - camera - } - /// /// Specify the camera to use perspective projection with the given field of view in the y-direction and near and far plane. /// @@ -287,27 +264,6 @@ impl Camera { self.update_frustrum(); } - /// - /// Specify the camera to use planar projection with the given field of view in the y-direction and near and far plane. - /// This can be either a planar or perspective projection depending on the field of view provided, which is permitted to be zero or negative. - /// - pub fn set_planar_projection( - &mut self, - field_of_view_y: impl Into, - z_near: f32, - z_far: f32, - ) { - assert!(z_near < z_far, "Wrong perspective camera parameters"); - self.z_near = z_near; - self.z_far = z_far; - let field_of_view_y = field_of_view_y.into(); - self.projection_type = ProjectionType::Planar { field_of_view_y }; - self.projection = planar(field_of_view_y, self.viewport.aspect(), 2.0, z_near, z_far) - * Mat4::from_translation(vec3(0.0, 0.0, 1.0)); - self.update_screen2ray(); - self.update_frustrum(); - } - /// /// Set the current viewport. /// Returns whether or not the viewport actually changed. @@ -322,9 +278,6 @@ impl Camera { ProjectionType::Perspective { field_of_view_y } => { self.set_perspective_projection(field_of_view_y, self.z_near, self.z_far); } - ProjectionType::Planar { field_of_view_y } => { - self.set_planar_projection(field_of_view_y, self.z_near, self.z_far); - } } true } else { @@ -410,7 +363,7 @@ impl Camera { /// pub fn position_at_pixel(&self, pixel: impl Into) -> Vec3 { match self.projection_type() { - ProjectionType::Orthographic { .. } | ProjectionType::Planar { .. } => { + ProjectionType::Orthographic { .. } => { let coords = self.uv_coordinates_at_pixel(pixel); self.position_at_uv_coordinates(coords) } @@ -423,10 +376,10 @@ impl Camera { /// pub fn position_at_uv_coordinates(&self, coords: impl Into) -> Vec3 { match self.projection_type() { - ProjectionType::Orthographic { .. } | ProjectionType::Planar { .. } => { + ProjectionType::Orthographic { .. } => { let coords = coords.into(); - let screen_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, -1.0); - self.screen2ray.transform_point(screen_pos).to_vec() + let screen_pos = vec4(2. * coords.u - 1., 2. * coords.v - 1.0, -1.0, 1.); + (self.screen2ray * screen_pos).truncate() } ProjectionType::Perspective { .. } => self.position, } @@ -438,7 +391,7 @@ impl Camera { pub fn view_direction_at_pixel(&self, pixel: impl Into) -> Vec3 { match self.projection_type() { ProjectionType::Orthographic { .. } => self.view_direction(), - ProjectionType::Perspective { .. } | ProjectionType::Planar { .. } => { + ProjectionType::Perspective { .. } => { let coords = self.uv_coordinates_at_pixel(pixel); self.view_direction_at_uv_coordinates(coords) } @@ -456,14 +409,6 @@ impl Camera { let screen_pos = vec4(2. * coords.u - 1., 2. * coords.v - 1.0, 0., 1.); (self.screen2ray * screen_pos).truncate().normalize() } - ProjectionType::Planar { .. } => { - let coords = coords.into(); - let start_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, -0.5); - let end_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, 0.5); - (self.screen2ray.transform_point(end_pos) - - self.screen2ray.transform_point(start_pos)) - .normalize() - } } } diff --git a/src/prelude/math.rs b/src/prelude/math.rs index 1bf56ad..7da528c 100644 --- a/src/prelude/math.rs +++ b/src/prelude/math.rs @@ -78,110 +78,3 @@ pub fn rotation_matrix_from_dir_to_dir(source_dir: Vec3, target_dir: Vec3) -> Ma source_dir, target_dir, ))) } - -// NOTE: Can be removed once https://github.com/rustgd/cgmath/pull/556 has been released. - -/// Create a planar projection matrix, which can be either perspective or orthographic. -/// -/// The projection frustum is always `height` units high at the origin along the view direction, -/// making the focal point located at `(0.0, 0.0, cot(fovy / 2.0)) * height / 2.0`. Unlike -/// a standard perspective projection, this allows `fovy` to be zero or negative. -pub fn planar>>( - fovy: A, - aspect: S, - height: S, - near: S, - far: S, -) -> Matrix4 { - PlanarFov { - fovy: fovy.into(), - aspect, - height, - near, - far, - } - .into() -} - -/// A planar projection based on a vertical field-of-view angle. -#[derive(Copy, Clone, Debug, PartialEq)] -struct PlanarFov { - pub fovy: Rad, - pub aspect: S, - pub height: S, - pub near: S, - pub far: S, -} - -impl From> for Matrix4 { - fn from(persp: PlanarFov) -> Matrix4 { - assert!( - persp.fovy > -Rad::turn_div_2(), - "The vertical field of view cannot be less than a negative half turn, found: {:?}", - persp.fovy - ); - assert!( - persp.fovy < Rad::turn_div_2(), - "The vertical field of view cannot be greater than a half turn, found: {:?}", - persp.fovy - ); - assert! { - persp.height >= S::zero(), - "The projection plane height cannot be negative, found: {:?}", - persp.height - } - - let two: S = cgmath::num_traits::cast(2).unwrap(); - let inv_f = Rad::tan(persp.fovy / two); - - let focal_point = -inv_f.recip(); - - assert!( - cgmath::abs_diff_ne!(persp.aspect.abs(), S::zero()), - "The absolute aspect ratio cannot be zero, found: {:?}", - persp.aspect.abs() - ); - assert!( - cgmath::abs_diff_ne!(persp.far, persp.near), - "The far plane and near plane are too close, found: far: {:?}, near: {:?}", - persp.far, - persp.near - ); - assert!( - focal_point < S::min(persp.far, persp.near) || focal_point > S::max(persp.far, persp.near), - "The focal point cannot be between the far and near planes, found: focal: {:?}, far: {:?}, near: {:?}", - focal_point, - persp.far, - persp.near, - ); - - let c0r0 = two / (persp.aspect * persp.height); - let c0r1 = S::zero(); - let c0r2 = S::zero(); - let c0r3 = S::zero(); - - let c1r0 = S::zero(); - let c1r1 = two / persp.height; - let c1r2 = S::zero(); - let c1r3 = S::zero(); - - let c2r0 = S::zero(); - let c2r1 = S::zero(); - let c2r2 = ((persp.far + persp.near) * inv_f + two) / (persp.near - persp.far); - let c2r3 = -inv_f; - - let c3r0 = S::zero(); - let c3r1 = S::zero(); - let c3r2 = (two * persp.far * persp.near * inv_f + (persp.far + persp.near)) - / (persp.near - persp.far); - let c3r3 = S::one(); - - #[cfg_attr(rustfmt, rustfmt_skip)] - Matrix4::new( - c0r0, c0r1, c0r2, c0r3, - c1r0, c1r1, c1r2, c1r3, - c2r0, c2r1, c2r2, c2r3, - c3r0, c3r1, c3r2, c3r3, - ) - } -} From 1c7a1faca628830e16c5eb2c54ffeb58a2bcd9b7 Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 08:24:57 +0100 Subject: [PATCH 2/7] Revert scale camera zoom based on distance to target --- src/camera.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index b295986..41369f0 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -298,7 +298,6 @@ impl Camera { Point3::from_vec(self.target), self.up, ); - self.view[3][3] *= position.distance(target); self.update_screen2ray(); self.update_frustrum(); } @@ -558,7 +557,6 @@ impl Camera { fn update_screen2ray(&mut self) { let mut v = self.view; - v /= v[3][3]; if let ProjectionType::Perspective { .. } = self.projection_type { v[3] = vec4(0.0, 0.0, 0.0, 1.0); } From c7a6cd39ea680f8e928e8f97482a8b11eb4dcdd8 Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 08:25:52 +0100 Subject: [PATCH 3/7] Minor clean-up --- src/camera.rs | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 41369f0..16c0fad 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -226,10 +226,6 @@ impl Camera { z_near: f32, z_far: f32, ) { - assert!( - z_near >= 0.0 || z_near < z_far, - "Wrong perspective camera parameters" - ); self.z_near = z_near; self.z_far = z_far; let field_of_view_y = field_of_view_y.into(); @@ -247,7 +243,6 @@ impl Camera { /// The view frustum depth is `z_near` to `z_far`. /// pub fn set_orthographic_projection(&mut self, height: f32, z_near: f32, z_far: f32) { - assert!(z_near < z_far, "Wrong orthographic camera parameters"); self.z_near = z_near; self.z_far = z_far; let width = height * self.viewport.aspect(); @@ -694,14 +689,10 @@ impl Camera { minimum_distance: f32, maximum_distance: f32, ) { - let minimum_distance = minimum_distance.max(0.0); - assert!( - minimum_distance < maximum_distance, - "minimum_distance larger than maximum_distance" - ); - let distance = point.distance(self.position); if distance > f32::EPSILON { + let minimum_distance = minimum_distance.max(std::f32::EPSILON); + let maximum_distance = maximum_distance.max(minimum_distance); let delta_clamped = distance - (distance - delta).clamp(minimum_distance, maximum_distance); let v = (point - self.position) * delta_clamped / distance; From 59400de381e9ba182c86018a23b2b22bffbc89d5 Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 10:59:35 +0100 Subject: [PATCH 4/7] Scale orthographic projection based on distance to target --- src/camera.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 16c0fad..c5e432d 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -243,17 +243,19 @@ impl Camera { /// The view frustum depth is `z_near` to `z_far`. /// pub fn set_orthographic_projection(&mut self, height: f32, z_near: f32, z_far: f32) { + self.projection_type = ProjectionType::Orthographic { height }; self.z_near = z_near; self.z_far = z_far; + let zoom = self.position.distance(self.target); + let height = zoom * height; let width = height * self.viewport.aspect(); - self.projection_type = ProjectionType::Orthographic { height }; self.projection = cgmath::ortho( -0.5 * width, 0.5 * width, -0.5 * height, 0.5 * height, - z_near, - z_far, + zoom * z_near, + zoom * z_far, ); self.update_screen2ray(); self.update_frustrum(); @@ -701,6 +703,9 @@ impl Camera { self.target + v - v.project_on(self.view_direction()), self.up, ); + if let ProjectionType::Orthographic { height } = self.projection_type { + self.set_orthographic_projection(height, self.z_near, self.z_far); + } } } } From 0b4776af6b4e219ecf3aac466664e623f4ffa4dc Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 11:07:59 +0100 Subject: [PATCH 5/7] Zoom factor functions --- src/camera.rs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/camera.rs b/src/camera.rs index c5e432d..c06ed78 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -708,4 +708,29 @@ impl Camera { } } } + + /// + /// Sets the zoom factor of this camera, ie. the distance to the camera will be `1/zoom_factor`. + /// + pub fn set_zoom_factor(&mut self, zoom_factor: f32) { + let zoom_factor = zoom_factor.max(std::f32::EPSILON); + let position = self.target + (self.position - self.target).normalize() / zoom_factor; + self.set_view(position, self.target, self.up); + + if let ProjectionType::Orthographic { height } = self.projection_type { + self.set_orthographic_projection(height, self.z_near, self.z_far); + } + } + + /// + /// The zoom factor for this camera, which is the same as one over the distance between the camera position and target. + /// + pub fn zoom_factor(&self) -> f32 { + let distance = self.target.distance(self.position); + if distance > f32::EPSILON { + 1.0 / distance + } else { + 0.0 + } + } } From 4ceae1aef3618d1bb0f75d94487daac9686520aa Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 11:08:05 +0100 Subject: [PATCH 6/7] Doc --- src/camera.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/camera.rs b/src/camera.rs index c06ed78..f04428a 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -237,10 +237,11 @@ impl Camera { } /// - /// Specify the camera to use orthographic projection with the given height and depth. + /// Specify the camera to use orthographic projection with the given dimensions. /// The view frustum height is `+/- height/2`. /// The view frustum width is calculated as `height * viewport.width / viewport.height`. /// The view frustum depth is `z_near` to `z_far`. + /// All of the above values are scaled by the zoom factor which is one over the distance between the camera position and target. /// pub fn set_orthographic_projection(&mut self, height: f32, z_near: f32, z_far: f32) { self.projection_type = ProjectionType::Orthographic { height }; From 253986bd2560d753230a2ad0d09b14f864ab1fe5 Mon Sep 17 00:00:00 2001 From: Asger Nyman Christiansen Date: Wed, 27 Nov 2024 11:24:13 +0100 Subject: [PATCH 7/7] Update Orthographic projection when position/target changes --- src/camera.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index f04428a..ed6c818 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -296,6 +296,9 @@ impl Camera { Point3::from_vec(self.target), self.up, ); + if let ProjectionType::Orthographic { height } = self.projection_type { + self.set_orthographic_projection(height, self.z_near, self.z_far); + } self.update_screen2ray(); self.update_frustrum(); } @@ -704,9 +707,6 @@ impl Camera { self.target + v - v.project_on(self.view_direction()), self.up, ); - if let ProjectionType::Orthographic { height } = self.projection_type { - self.set_orthographic_projection(height, self.z_near, self.z_far); - } } } @@ -717,10 +717,6 @@ impl Camera { let zoom_factor = zoom_factor.max(std::f32::EPSILON); let position = self.target + (self.position - self.target).normalize() / zoom_factor; self.set_view(position, self.target, self.up); - - if let ProjectionType::Orthographic { height } = self.projection_type { - self.set_orthographic_projection(height, self.z_near, self.z_far); - } } ///