Skip to content

Commit

Permalink
Refactor: break up huge match into several small ones in functions
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Apr 24, 2024
1 parent a43dfbd commit bdf6843
Showing 1 changed file with 99 additions and 85 deletions.
184 changes: 99 additions & 85 deletions crates/re_renderer/src/view_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ pub type SharedViewBuilder = Arc<RwLock<Option<ViewBuilder>>>;

/// Configures the camera placement in the orthographic frustum,
/// as well as the coordinate system convention.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum OrthographicCameraMode {
/// Puts the view space origin into the middle of the screen.
///
Expand All @@ -94,7 +94,7 @@ pub enum OrthographicCameraMode {
}

/// How we project from 3D to 2D.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Copy)]
pub enum Projection {
/// Perspective camera looking along the negative z view space axis.
Perspective {
Expand Down Expand Up @@ -124,6 +124,73 @@ pub enum Projection {
},
}

impl Projection {
fn projection_from_view(self, aspect_ratio: f32) -> glam::Mat4 {
match self {
Projection::Perspective {
vertical_fov,
near_plane_distance,
aspect_ratio,
} => {
// We use infinite reverse-z projection matrix
// * great precision both with floating point and integer: https://developer.nvidia.com/content/depth-precision-visualized
// * no need to worry about far plane
glam::Mat4::perspective_infinite_reverse_rh(
vertical_fov,
aspect_ratio,
near_plane_distance,
)
}
Projection::Orthographic {
camera_mode,
vertical_world_size,
far_plane_distance,
} => {
let horizontal_world_size = vertical_world_size * aspect_ratio;

// Note that we inverse z (by swapping near and far plane) to be consistent with our perspective projection.
match camera_mode {
OrthographicCameraMode::NearPlaneCenter => glam::Mat4::orthographic_rh(
-0.5 * horizontal_world_size,
0.5 * horizontal_world_size,
-0.5 * vertical_world_size,
0.5 * vertical_world_size,
far_plane_distance,
0.0,
),
OrthographicCameraMode::TopLeftCornerAndExtendZ => glam::Mat4::orthographic_rh(
0.0,
horizontal_world_size,
vertical_world_size,
0.0,
far_plane_distance,
-far_plane_distance,
),
}
}
}
}

fn tan_half_fov(&self) -> glam::Vec2 {
match self {
Projection::Perspective {
vertical_fov,
aspect_ratio,
..
} => {
// Calculate ratio between screen size and screen distance.
// Great for getting directions from normalized device coordinates.
// (btw. this is the same as [1.0 / projection_from_view[0].x, 1.0 / projection_from_view[1].y])
glam::vec2(
(vertical_fov * 0.5).tan() * aspect_ratio,
(vertical_fov * 0.5).tan(),
)
}
Projection::Orthographic { .. } => glam::vec2(f32::MAX, f32::MAX), // Can't use infinity in shaders
}
}
}

/// How [`Size::AUTO`] is interpreted.
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
Expand Down Expand Up @@ -314,89 +381,36 @@ impl ViewBuilder {
},
);

let (projection_from_view, tan_half_fov, pixel_world_size_from_camera_distance) =
match config.projection_from_view.clone() {
Projection::Perspective {
vertical_fov,
near_plane_distance,
aspect_ratio,
} => {
// We use infinite reverse-z projection matrix
// * great precision both with floating point and integer: https://developer.nvidia.com/content/depth-precision-visualized
// * no need to worry about far plane
let projection_from_view = glam::Mat4::perspective_infinite_reverse_rh(
vertical_fov,
aspect_ratio,
near_plane_distance,
);

// Calculate ratio between screen size and screen distance.
// Great for getting directions from normalized device coordinates.
// (btw. this is the same as [1.0 / projection_from_view[0].x, 1.0 / projection_from_view[1].y])
let tan_half_fov = glam::vec2(
(vertical_fov * 0.5).tan() * aspect_ratio,
(vertical_fov * 0.5).tan(),
);

// Determine how wide a pixel is in world space at unit distance from the camera.
//
// derivation:
// tan(FOV / 2) = (screen_in_world / 2) / distance
// screen_in_world = tan(FOV / 2) * distance * 2
//
// want: pixels in world per distance, i.e (screen_in_world / resolution / distance)
// => (resolution / screen_in_world / distance) = tan(FOV / 2) * distance * 2 / resolution / distance =
// = tan(FOV / 2) * 2.0 / resolution
let pixel_world_size_from_camera_distance =
tan_half_fov.y * 2.0 / config.resolution_in_pixel[1] as f32;

(
projection_from_view,
tan_half_fov,
pixel_world_size_from_camera_distance,
)
}
Projection::Orthographic {
camera_mode,
vertical_world_size,
far_plane_distance,
} => {
let aspect_ratio =
config.resolution_in_pixel[0] as f32 / config.resolution_in_pixel[1] as f32;
let horizontal_world_size = vertical_world_size * aspect_ratio;
// Note that we inverse z (by swapping near and far plane) to be consistent with our perspective projection.
let projection_from_view = match camera_mode {
OrthographicCameraMode::NearPlaneCenter => glam::Mat4::orthographic_rh(
-0.5 * horizontal_world_size,
0.5 * horizontal_world_size,
-0.5 * vertical_world_size,
0.5 * vertical_world_size,
far_plane_distance,
0.0,
),
OrthographicCameraMode::TopLeftCornerAndExtendZ => {
glam::Mat4::orthographic_rh(
0.0,
horizontal_world_size,
vertical_world_size,
0.0,
far_plane_distance,
-far_plane_distance,
)
}
};

let tan_half_fov = glam::vec2(f32::MAX, f32::MAX);
let pixel_world_size_from_camera_distance = vertical_world_size
/ config.resolution_in_pixel[0].max(config.resolution_in_pixel[1]) as f32;

(
projection_from_view,
tan_half_fov,
pixel_world_size_from_camera_distance,
)
}
};
let aspect_ratio =
config.resolution_in_pixel[0] as f32 / config.resolution_in_pixel[1] as f32;

let projection_from_view = config
.projection_from_view
.projection_from_view(aspect_ratio);

let tan_half_fov = config.projection_from_view.tan_half_fov();

let pixel_world_size_from_camera_distance = match config.projection_from_view {
Projection::Perspective { .. } => {
// Determine how wide a pixel is in world space at unit distance from the camera.
//
// derivation:
// tan(FOV / 2) = (screen_in_world / 2) / distance
// screen_in_world = tan(FOV / 2) * distance * 2
//
// want: pixels in world per distance, i.e (screen_in_world / resolution / distance)
// => (resolution / screen_in_world / distance) = tan(FOV / 2) * distance * 2 / resolution / distance =
// = tan(FOV / 2) * 2.0 / resolution
tan_half_fov.y * 2.0 / config.resolution_in_pixel[1] as f32
}
Projection::Orthographic {
vertical_world_size,
..
} => {
vertical_world_size
/ config.resolution_in_pixel[0].max(config.resolution_in_pixel[1]) as f32
}
};

// Finally, apply a viewport transformation to the projection.
let ndc_scale_and_translation = config
Expand Down

0 comments on commit bdf6843

Please sign in to comment.