From 38c7d5eb9e81ab8e1aec03673599b25a9aa0c69c Mon Sep 17 00:00:00 2001 From: Aevyrie Date: Wed, 8 Dec 2021 01:31:31 +0000 Subject: [PATCH] Check for NaN in `Camera::world_to_screen()` (#3268) # Objective - Checks for NaN in computed NDC space coordinates, fixing unexpected NaN in a fallible (`Option`) function. ## Solution - Adds a NaN check, in addition to the existing NDC bounds checks. - This is a helper function, and should have no performance impact to the engine itself. - This will help prevent hard-to-trace NaN propagation in user code, by returning `None` instead of `Some(NaN)`. Depends on https://github.com/bevyengine/bevy/pull/3269 for CI error fix. --- crates/bevy_render/src/camera/camera.rs | 8 ++++++-- pipelined/bevy_render2/src/camera/camera.rs | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/crates/bevy_render/src/camera/camera.rs b/crates/bevy_render/src/camera/camera.rs index ea600921ed..f93581af77 100644 --- a/crates/bevy_render/src/camera/camera.rs +++ b/crates/bevy_render/src/camera/camera.rs @@ -55,13 +55,17 @@ impl Camera { 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 behind the camera 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 { return None; } // 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(screen_space_coords) + if !screen_space_coords.is_nan() { + Some(screen_space_coords) + } else { + None + } } } diff --git a/pipelined/bevy_render2/src/camera/camera.rs b/pipelined/bevy_render2/src/camera/camera.rs index 7a05bbe9ff..6bcf544356 100644 --- a/pipelined/bevy_render2/src/camera/camera.rs +++ b/pipelined/bevy_render2/src/camera/camera.rs @@ -54,13 +54,17 @@ impl Camera { 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 behind the camera 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 { return None; } // 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(screen_space_coords) + if !screen_space_coords.is_nan() { + Some(screen_space_coords) + } else { + None + } } }