From bbd797dd7c5e7145b9e401bc78c992643a65167a Mon Sep 17 00:00:00 2001 From: Maximilian Ammann Date: Tue, 18 Jan 2022 16:31:03 +0100 Subject: [PATCH] Clean up camera --- src/input/pan_handler.rs | 6 +-- src/render/camera.rs | 100 +++++++-------------------------------- 2 files changed, 19 insertions(+), 87 deletions(-) diff --git a/src/input/pan_handler.rs b/src/input/pan_handler.rs index 01515120..da851f4e 100644 --- a/src/input/pan_handler.rs +++ b/src/input/pan_handler.rs @@ -26,10 +26,8 @@ impl UpdateState for PanHandler { let perspective = &state.perspective; let view_proj = reference_camera.calc_view_proj(perspective); - let start = - reference_camera.project_screen_to_world(&start_window_position, &view_proj); - let current = - reference_camera.project_screen_to_world(&window_position, &view_proj); + let start = reference_camera.window_to_world_z0(&start_window_position, &view_proj); + let current = reference_camera.window_to_world_z0(&window_position, &view_proj); let delta = start - current; if self.start_camera_position.is_none() { diff --git a/src/render/camera.rs b/src/render/camera.rs index f24f3cc0..58d3cb2a 100644 --- a/src/render/camera.rs +++ b/src/render/camera.rs @@ -77,7 +77,7 @@ impl Camera { ) } - fn dx_matrix(width: f64, height: f64) -> Matrix4 { + fn clip_to_window_transform(width: f64, height: f64) -> Matrix4 { // Adapted from: https://docs.microsoft.com/en-us/windows/win32/direct3d9/viewports-and-clipping#viewport-rectangle let min_depth = 0.0; let max_depth = 1.0; @@ -86,8 +86,6 @@ impl Camera { let ox = x + width / 2.0; let oy = y + height / 2.0; let oz = min_depth; - let px = width as f64; - let py = height as f64; let pz = max_depth - min_depth; Matrix4::from_cols( Vector4::new(width as f64 / 2.0, 0.0, 0.0, 0.0), @@ -97,9 +95,9 @@ impl Camera { ) } + // Adopted from: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkViewport.html + // and https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ fn clip_to_window(clip: &Vector4, width: f64, height: f64) -> Vector3 { - // Adopted from: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkViewport.html - // and https://matthewwellings.com/blog/the-new-vulkan-coordinate-system/ #[rustfmt::skip] let ndc = Vector4::new( clip.x / clip.w, @@ -125,57 +123,21 @@ impl Camera { Vector3::new(px / 2.0 * xd + ox, py / 2.0 * yd + oy, pz * zd + oz) } + // https://docs.microsoft.com/en-us/windows/win32/dxtecharts/the-direct3d-transformation-pipeline // https://docs.microsoft.com/en-us/windows/win32/dxtecharts/the-direct3d-transformation-pipeline fn clip_to_window_matrix(clip: &Vector4, width: f64, height: f64) -> Vector4 { - let w = clip.w; - let z = clip.z; - println!("z in clip space: {z}"); - println!("w in clip space: {w}"); - #[rustfmt::skip] - let ndc = Vector4::new( + let ndc = Vector4::new( clip.x / clip.w, clip.y / clip.w, clip.z / clip.w, clip.w / clip.w ); - let window = Self::dx_matrix(width, height) * ndc; + let window = Self::clip_to_window_transform(width, height) * ndc; window } - // https://docs.microsoft.com/en-us/windows/win32/dxtecharts/the-direct3d-transformation-pipeline - fn window_to_clip( - window: &Vector3, - origin_clip_space: &Vector4, - width: f64, - height: f64, - ) -> Vector4 { - let z = window.z; - println!("z in window space: {z}"); - #[rustfmt::skip] - let fixed_window = Vector4::new( - window.x, - window.y, - window.z, - 1.0 - ); - - let ndc = Self::dx_matrix(width, height).invert().unwrap() * fixed_window; - - let w = origin_clip_space.w; - - #[rustfmt::skip] - let clip = Vector4::new( - ndc.x * w, - ndc.y * w, - ndc.z * w, - w, - ); - - clip - } - fn window_to_world( window: &Vector3, view_proj: &Matrix4, @@ -190,7 +152,10 @@ impl Camera { 1.0 ); - let ndc = Self::dx_matrix(width, height).invert().unwrap() * fixed_window; + let ndc = Self::clip_to_window_transform(width, height) + .invert() + .unwrap() + * fixed_window; let unprojected = view_proj.invert().unwrap() * ndc; let world = Vector3::new( unprojected.x / unprojected.w, @@ -200,6 +165,7 @@ impl Camera { world } + /// Alternative implementation to `window_to_world` fn window_to_world_nalgebra( window: &Vector3, view_proj: &Matrix4, @@ -221,30 +187,19 @@ impl Camera { world } - pub fn project_screen_to_world( + pub fn window_to_world_z0( &self, window: &Vector2, view_proj: &Matrix4, ) -> Vector3 { - /* let origin_clip_space = (view_proj * Vector4::new(0.0, 0.0, 0.0, 1.0)); - - let origin_window_space = - Self::clip_to_window_matrix(&origin_clip_space, self.width, self.height); - let reverse_clip = Self::window_to_clip( - &Vector3::new(window.x, window.y, origin_window_space.z), - &origin_clip_space, - self.width, - self.height, - );*/ - - let near_world = Camera::window_to_world_nalgebra( + let near_world = Camera::window_to_world( &Vector3::new(window.x, window.y, 0.0), &view_proj, self.width, self.height, ); - let far_world = Camera::window_to_world_nalgebra( + let far_world = Camera::window_to_world( &Vector3::new(window.x, window.y, 1.0), &view_proj, self.width, @@ -330,35 +285,15 @@ mod tests { let window = Camera::clip_to_window_matrix(&clip, width, height); println!("window (matrix): {:?}", window); - let origin_window_space = Camera::clip_to_window_matrix(&origin_clip_space, width, height); - let reverse_clip = Camera::window_to_clip( - &Vector3::new(window.x, window.y, origin_window_space.z), - &origin_clip_space, - width, - height, - ); - let reverse_world = view_proj.invert().unwrap() * reverse_clip; - - println!("r clip: {:?}", reverse_clip); - println!("r world: {:?}", reverse_world); - // --------- nalgebra - let scale = 1.0 / origin_clip_space.w; - - let origin_window_space_nalgebra = Vector3::new( - 0.0 + (width * (origin_clip_space.x * scale + 1.0) * 0.5), - 0.0 + (height * (origin_clip_space.y * scale + 1.0) * 0.5), - origin_clip_space.z * scale, - ); - println!("r origin (nalgebra): {:?}", origin_window_space_nalgebra); println!( "r world (nalgebra): {:?}", Camera::window_to_world_nalgebra(&window.truncate(), &view_proj, width, height) ); - // -------- - // pdf trick + // -------- far vs. near plane trick + let near_world = Camera::window_to_world_nalgebra( &Vector3::new(window.x, window.y, 0.0), &view_proj, @@ -378,8 +313,7 @@ mod tests { println!("u: {:?}", u); let unprojected = near_world + u * (far_world - near_world); println!("unprojected: {:?}", unprojected); - //assert!(Vector3::new(world_pos.x, world_pos.y, world_pos.z).abs_diff_eq(&unprojected, 0.05)); - //.------ + assert!(Vector3::new(world_pos.x, world_pos.y, world_pos.z).abs_diff_eq(&unprojected, 0.05)); // ---- test for unproject