From 5430b9532a39fda22606bf10312bef541ae5c295 Mon Sep 17 00:00:00 2001 From: devil-ira Date: Thu, 29 Sep 2022 18:50:46 +0200 Subject: [PATCH 1/6] bleep --- crates/bevy_math/src/lib.rs | 6 ++- crates/bevy_math/src/ray.rs | 12 +++++ crates/bevy_render/src/camera/camera.rs | 61 ++++++++++++++++++++++--- 3 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 crates/bevy_math/src/ray.rs diff --git a/crates/bevy_math/src/lib.rs b/crates/bevy_math/src/lib.rs index aa22942c43d11..3e397536f8c4c 100644 --- a/crates/bevy_math/src/lib.rs +++ b/crates/bevy_math/src/lib.rs @@ -6,16 +6,18 @@ #![warn(missing_docs)] +mod ray; mod rect; +pub use ray::Ray; pub use rect::Rect; /// The `bevy_math` prelude. pub mod prelude { #[doc(hidden)] pub use crate::{ - BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Rect, UVec2, - UVec3, UVec4, Vec2, Vec3, Vec4, + BVec2, BVec3, BVec4, EulerRot, IVec2, IVec3, IVec4, Mat2, Mat3, Mat4, Quat, Ray, Rect, + UVec2, UVec3, UVec4, Vec2, Vec3, Vec4, }; } diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs new file mode 100644 index 0000000000000..8e1c4ec13be43 --- /dev/null +++ b/crates/bevy_math/src/ray.rs @@ -0,0 +1,12 @@ +use crate::Vec3; +use serde::{Deserialize, Serialize}; + +/// A ray is an infinite line starting at `origin`, going in `direction`. +#[repr(C)] +#[derive(Default, Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] +pub struct Ray { + /// The origin of the ray. + pub origin: Vec3, + /// The direction of the ray. + pub direction: Vec3, +} diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index ed3373b12a20d..939e612e662e3 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -17,7 +17,7 @@ use bevy_ecs::{ reflect::ReflectComponent, system::{Commands, ParamSet, Query, Res}, }; -use bevy_math::{Mat4, UVec2, UVec4, Vec2, Vec3}; +use bevy_math::{Mat4, Ray, UVec2, UVec4, Vec2, Vec3}; use bevy_reflect::prelude::*; use bevy_reflect::FromReflect; use bevy_transform::components::GlobalTransform; @@ -193,8 +193,8 @@ impl Camera { /// Given a position in world space, use the camera to compute the viewport-space coordinates. /// - /// To get the coordinates in Normalized Device Coordinates, you should use - /// [`world_to_ndc`](Self::world_to_ndc). + /// To get the coordinates in Normalized Device Coordinates, + /// you should use [`world_to_ndc`](Self::world_to_ndc). #[doc(alias = "world_to_screen")] pub fn world_to_viewport( &self, @@ -212,17 +212,43 @@ impl Camera { Some((ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * target_size) } + /// Returns a ray originating from the camera, that passes through everything beyond `viewport_position`. + /// + /// The resulting ray starts on the near plane of the camera. + /// + /// If the camera's projection is orthographic the direction of the ray is always equal to `camera_transform.forward()`. + /// + /// To get the world space coordinates with Normalized Device Coordinates, + /// you should use [`ndc_to_world`](Self::ndc_to_world). + pub fn viewport_to_world( + &self, + camera_transform: &GlobalTransform, + viewport_position: Vec2, + ) -> Option { + let target_size = self.logical_viewport_size()?; + let ndc = viewport_position * 2. / target_size - Vec2::ONE; + + let world_near_plane = self.ndc_to_world(camera_transform, ndc.extend(1.))?; + let world_far_plane = self.ndc_to_world(camera_transform, ndc.extend(f32::EPSILON))?; + + Some(Ray { + origin: world_near_plane, + direction: (world_far_plane - world_near_plane).normalize(), + }) + } + /// Given a position in world space, use the camera's viewport to compute the Normalized Device Coordinates. /// - /// Values returned will be between -1.0 and 1.0 when the position is within the viewport. - /// To get the coordinates in the render target's viewport dimensions, you should use - /// [`world_to_viewport`](Self::world_to_viewport). + /// When the position is within the viewport the values returned will be between -1.0 and 1.0 on the X and Y axes, + /// and between 0.0 and 1.0 on the Z axis. + /// To get the coordinates in the render target's viewport dimensions, + /// you should use [`world_to_viewport`](Self::world_to_viewport). pub fn world_to_ndc( &self, camera_transform: &GlobalTransform, world_position: Vec3, ) -> Option { - // Build a transform to convert from world to NDC using camera data + // Build a transformation matrix to convert from world space to NDC using camera data let world_to_ndc: Mat4 = self.computed.projection_matrix * camera_transform.compute_matrix().inverse(); let ndc_space_coords: Vec3 = world_to_ndc.project_point3(world_position); @@ -233,6 +259,27 @@ impl Camera { None } } + + /// Given a position in Normalized Device Coordinates, + /// use the camera's viewport to compute the world space position. + /// + /// When the position is within the viewport the values returned will be between -1.0 and 1.0 on the X and Y axes, + /// and between 0.0 and 1.0 on the Z axis. + /// To get the world space coordinates with the viewport position, + /// you should use [`world_to_viewport`](Self::world_to_viewport). + pub fn ndc_to_world(&self, camera_transform: &GlobalTransform, ndc: Vec3) -> Option { + // Build a transform to convert from NDC to world using camera data + let ndc_to_world = + camera_transform.compute_matrix() * self.computed.projection_matrix.inverse(); + + let world_space_coords = ndc_to_world.project_point3(ndc); + + if !world_space_coords.is_nan() { + Some(world_space_coords) + } else { + None + } + } } /// Configures the [`RenderGraph`](crate::render_graph::RenderGraph) name assigned to be run for a given [`Camera`] entity. From 4f6d62ae2cd86da2def8d09d6806c19abe400c18 Mon Sep 17 00:00:00 2001 From: devil-ira Date: Thu, 29 Sep 2022 19:46:39 +0200 Subject: [PATCH 2/6] comment --- crates/bevy_render/src/camera/camera.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 939e612e662e3..4cdeb29a73252 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -268,7 +268,7 @@ impl Camera { /// To get the world space coordinates with the viewport position, /// you should use [`world_to_viewport`](Self::world_to_viewport). pub fn ndc_to_world(&self, camera_transform: &GlobalTransform, ndc: Vec3) -> Option { - // Build a transform to convert from NDC to world using camera data + // Build a transformation matrix to convert from NDC to world space using camera data let ndc_to_world = camera_transform.compute_matrix() * self.computed.projection_matrix.inverse(); From dedd6b75ee3ac0f59af1fb62b3974614254ca3a0 Mon Sep 17 00:00:00 2001 From: devil-ira Date: Thu, 29 Sep 2022 21:20:55 +0200 Subject: [PATCH 3/6] Remove `#[repr(C)]` and add comment --- crates/bevy_math/src/ray.rs | 1 - crates/bevy_render/src/camera/camera.rs | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_math/src/ray.rs b/crates/bevy_math/src/ray.rs index 8e1c4ec13be43..cd1ec2b552d45 100644 --- a/crates/bevy_math/src/ray.rs +++ b/crates/bevy_math/src/ray.rs @@ -2,7 +2,6 @@ use crate::Vec3; use serde::{Deserialize, Serialize}; /// A ray is an infinite line starting at `origin`, going in `direction`. -#[repr(C)] #[derive(Default, Clone, Copy, Debug, Serialize, Deserialize, PartialEq)] pub struct Ray { /// The origin of the ray. diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 4cdeb29a73252..29fb55786843b 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -229,6 +229,7 @@ impl Camera { let ndc = viewport_position * 2. / target_size - Vec2::ONE; let world_near_plane = self.ndc_to_world(camera_transform, ndc.extend(1.))?; + // Using EPSILON because passing an ndc with Z = 0 returns NaNs. let world_far_plane = self.ndc_to_world(camera_transform, ndc.extend(f32::EPSILON))?; Some(Ray { From 1e69610a0bb439ee743289d053994aa6061097a4 Mon Sep 17 00:00:00 2001 From: devil-ira Date: Thu, 29 Sep 2022 21:28:21 +0200 Subject: [PATCH 4/6] Revert doc comment formatting :0 --- crates/bevy_render/src/camera/camera.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 29fb55786843b..e03a4424bbd5d 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -242,8 +242,8 @@ impl Camera { /// /// When the position is within the viewport the values returned will be between -1.0 and 1.0 on the X and Y axes, /// and between 0.0 and 1.0 on the Z axis. - /// To get the coordinates in the render target's viewport dimensions, - /// you should use [`world_to_viewport`](Self::world_to_viewport). + /// To get the coordinates in the render target's viewport dimensions, you should use + /// [`world_to_viewport`](Self::world_to_viewport). pub fn world_to_ndc( &self, camera_transform: &GlobalTransform, @@ -266,8 +266,8 @@ impl Camera { /// /// When the position is within the viewport the values returned will be between -1.0 and 1.0 on the X and Y axes, /// and between 0.0 and 1.0 on the Z axis. - /// To get the world space coordinates with the viewport position, - /// you should use [`world_to_viewport`](Self::world_to_viewport). + /// To get the world space coordinates with the viewport position, you should use + /// [`world_to_viewport`](Self::world_to_viewport). pub fn ndc_to_world(&self, camera_transform: &GlobalTransform, ndc: Vec3) -> Option { // Build a transformation matrix to convert from NDC to world space using camera data let ndc_to_world = From 6fcc7a2baff100b0dcd9e5ce59cd2235fd04819f Mon Sep 17 00:00:00 2001 From: devil-ira Date: Thu, 29 Sep 2022 21:31:36 +0200 Subject: [PATCH 5/6] whops :o --- crates/bevy_render/src/camera/camera.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index e03a4424bbd5d..13a8c3e347ae3 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -193,8 +193,8 @@ impl Camera { /// Given a position in world space, use the camera to compute the viewport-space coordinates. /// - /// To get the coordinates in Normalized Device Coordinates, - /// you should use [`world_to_ndc`](Self::world_to_ndc). + /// To get the coordinates in Normalized Device Coordinates, you should use + /// [`world_to_ndc`](Self::world_to_ndc). #[doc(alias = "world_to_screen")] pub fn world_to_viewport( &self, @@ -218,8 +218,8 @@ impl Camera { /// /// If the camera's projection is orthographic the direction of the ray is always equal to `camera_transform.forward()`. /// - /// To get the world space coordinates with Normalized Device Coordinates, - /// you should use [`ndc_to_world`](Self::ndc_to_world). + /// To get the world space coordinates with Normalized Device Coordinates, you should use + /// [`ndc_to_world`](Self::ndc_to_world). pub fn viewport_to_world( &self, camera_transform: &GlobalTransform, From 70a13c91c8f22b39a41cb7d58da9f816df6dff20 Mon Sep 17 00:00:00 2001 From: devil-ira Date: Fri, 30 Sep 2022 11:35:06 +0200 Subject: [PATCH 6/6] simplify Co-authored-by: Nathan Ward --- crates/bevy_render/src/camera/camera.rs | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index 13a8c3e347ae3..df2be98df1b08 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -254,11 +254,7 @@ impl Camera { self.computed.projection_matrix * camera_transform.compute_matrix().inverse(); let ndc_space_coords: Vec3 = world_to_ndc.project_point3(world_position); - if !ndc_space_coords.is_nan() { - Some(ndc_space_coords) - } else { - None - } + (!ndc_space_coords.is_nan()).then_some(ndc_space_coords) } /// Given a position in Normalized Device Coordinates, @@ -275,11 +271,7 @@ impl Camera { let world_space_coords = ndc_to_world.project_point3(ndc); - if !world_space_coords.is_nan() { - Some(world_space_coords) - } else { - None - } + (!world_space_coords.is_nan()).then_some(world_space_coords) } }