simplified API to get NDC from camera and world position (#4041)

# Objective

- After #3412, `Camera::world_to_screen` got a little bit uglier to use by needing to provide both `Windows` and `Assets<Image>`, even though only one would be needed b697e73c3d/crates/bevy_render/src/camera/camera.rs (L117-L123)
- Some time, exact coordinates are not needed but normalized device coordinates is enough

## Solution

- Add a function to just get NDC
This commit is contained in:
François 2022-05-03 19:51:18 +00:00
parent 2b6e67f4cb
commit 4a9932fa8e

View file

@ -122,6 +122,9 @@ impl Default for DepthCalculation {
impl Camera { impl Camera {
/// Given a position in world space, use the camera to compute the screen space coordinates. /// Given a position in world space, use the camera to compute the screen space coordinates.
///
/// To get the coordinates in Normalized Device Coordinates, you should use
/// [`world_to_ndc`](Self::world_to_ndc).
pub fn world_to_screen( pub fn world_to_screen(
&self, &self,
windows: &Windows, windows: &Windows,
@ -130,18 +133,33 @@ impl Camera {
world_position: Vec3, world_position: Vec3,
) -> Option<Vec2> { ) -> Option<Vec2> {
let window_size = self.target.get_logical_size(windows, images)?; let window_size = self.target.get_logical_size(windows, images)?;
// Build a transform to convert from world to NDC using camera data let ndc_space_coords = self.world_to_ndc(camera_transform, world_position)?;
let world_to_ndc: Mat4 =
self.projection_matrix * camera_transform.compute_matrix().inverse();
let ndc_space_coords: Vec3 = world_to_ndc.project_point3(world_position);
// NDC z-values outside of 0 < z < 1 are outside the camera frustum and are thus not in screen space // NDC z-values outside of 0 < z < 1 are outside the camera frustum and are thus not in screen space
if ndc_space_coords.z < 0.0 || ndc_space_coords.z > 1.0 { if ndc_space_coords.z < 0.0 || ndc_space_coords.z > 1.0 {
return None; return None;
} }
// Once in NDC space, we can discard the z element and rescale x/y to fit the screen // Once in NDC space, we can discard the z element and rescale x/y to fit the screen
let screen_space_coords = (ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * window_size; Some((ndc_space_coords.truncate() + Vec2::ONE) / 2.0 * window_size)
if !screen_space_coords.is_nan() { }
Some(screen_space_coords)
/// Given a position in world space, use the camera to compute the Normalized Device Coordinates.
///
/// Values returned will be between -1.0 and 1.0 when the position is in screen space.
/// To get the coordinates in the render target dimensions, you should use
/// [`world_to_screen`](Self::world_to_screen).
pub fn world_to_ndc(
&self,
camera_transform: &GlobalTransform,
world_position: Vec3,
) -> Option<Vec3> {
// Build a transform to convert from world to NDC using camera data
let world_to_ndc: Mat4 =
self.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 { } else {
None None
} }