Skip to content

Commit

Permalink
Add planar projection support to camera
Browse files Browse the repository at this point in the history
  • Loading branch information
thatcomputerguy0101 committed Nov 28, 2024
1 parent edae58b commit 68a6d6c
Show file tree
Hide file tree
Showing 2 changed files with 86 additions and 10 deletions.
92 changes: 84 additions & 8 deletions src/camera.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,11 @@ pub enum ProjectionType {
/// The field of view angle in the vertical direction.
field_of_view_y: Radians,
},
/// General planar projection
Planar {
/// The field of view angle in the vertical direction.
field_of_view_y: Radians,
},
}

///
Expand Down Expand Up @@ -279,6 +284,24 @@ impl Camera {
camera
}

///
/// New camera which projects the world with a general planar projection.
///
pub fn new_planar(
viewport: Viewport,
position: Vec3,
target: Vec3,
up: Vec3,
field_of_view_y: impl Into<Radians>,
z_near: f32,
z_far: f32,
) -> Self {
let mut camera = Camera::new(viewport);
camera.set_view(position, target, up);
camera.set_planar_projection(field_of_view_y, z_near, z_far);
camera
}

///
/// Specify the camera to use perspective projection with the given field of view in the y-direction and near and far plane.
///
Expand Down Expand Up @@ -320,6 +343,41 @@ impl Camera {
);
}

///
/// Specify the camera to use planar projection with the given field of view in the y-direction and near and far plane.
/// This can be either a planar or perspective projection depending on the field of view provided, which is permitted to be zero or negative.
///
pub fn set_planar_projection(
&mut self,
field_of_view_y: impl Into<Radians>,
mut z_near: f32,
mut z_far: f32,
) {
assert!(z_near < z_far, "Wrong perspective camera parameters");
self.z_near = z_near;
self.z_far = z_far;
let field_of_view_y = field_of_view_y.into();
let depth = self.position.distance(self.target);
let height = 2.0 * depth;
self.projection_type = ProjectionType::Planar { field_of_view_y };
let focal = -Rad::cot(field_of_view_y / 2.0) * depth;
z_near -= depth;
z_far -= depth;
// Required to ensure near/far plane does not cross focal point when at close zoom levels
if focal < 0.0 && z_near < focal {
z_near = focal + 0.001;
} else if focal > 0.0 && z_far > focal {
z_far = focal - 0.001;
}
self.projection = cgmath::planar(
field_of_view_y,
self.viewport.aspect(),
height,
z_near,
z_far,
) * Mat4::from_translation(vec3(0.0, 0.0, depth));
}

///
/// Set the current viewport.
/// Returns whether or not the viewport actually changed.
Expand All @@ -334,6 +392,9 @@ impl Camera {
ProjectionType::Perspective { field_of_view_y } => {
self.set_perspective_projection(field_of_view_y, self.z_near, self.z_far);
}
ProjectionType::Planar { field_of_view_y } => {
self.set_planar_projection(field_of_view_y, self.z_near, self.z_far);
}
}
true
} else {
Expand All @@ -354,9 +415,15 @@ impl Camera {
Point3::from_vec(self.target),
self.up,
);
if let ProjectionType::Orthographic { height } = self.projection_type {
self.set_orthographic_projection(height, self.z_near, self.z_far);
}
match self.projection_type {
ProjectionType::Orthographic { height } => {
self.set_orthographic_projection(height, self.z_near, self.z_far)
}
ProjectionType::Planar { field_of_view_y } => {
self.set_planar_projection(field_of_view_y, self.z_near, self.z_far)
}
_ => {}
};
}

/// Returns the [Frustum] for this camera.
Expand All @@ -369,7 +436,7 @@ impl Camera {
///
pub fn position_at_pixel(&self, pixel: impl Into<PixelPoint>) -> Vec3 {
match self.projection_type() {
ProjectionType::Orthographic { .. } => {
ProjectionType::Orthographic { .. } | ProjectionType::Planar { .. } => {
let coords = self.uv_coordinates_at_pixel(pixel);
self.position_at_uv_coordinates(coords)
}
Expand All @@ -382,10 +449,10 @@ impl Camera {
///
pub fn position_at_uv_coordinates(&self, coords: impl Into<UvCoordinate>) -> Vec3 {
match self.projection_type() {
ProjectionType::Orthographic { .. } => {
ProjectionType::Orthographic { .. } | ProjectionType::Planar { .. } => {
let coords = coords.into();
let screen_pos = vec4(2. * coords.u - 1., 2. * coords.v - 1.0, -1.0, 1.);
(self.screen2ray() * screen_pos).truncate()
let screen_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, -1.0);
self.screen2ray().transform_point(screen_pos).to_vec()
}
ProjectionType::Perspective { .. } => self.position,
}
Expand All @@ -397,7 +464,7 @@ impl Camera {
pub fn view_direction_at_pixel(&self, pixel: impl Into<PixelPoint>) -> Vec3 {
match self.projection_type() {
ProjectionType::Orthographic { .. } => self.view_direction(),
ProjectionType::Perspective { .. } => {
ProjectionType::Perspective { .. } | ProjectionType::Planar { .. } => {
let coords = self.uv_coordinates_at_pixel(pixel);
self.view_direction_at_uv_coordinates(coords)
}
Expand All @@ -415,6 +482,14 @@ impl Camera {
let screen_pos = vec4(2. * coords.u - 1., 2. * coords.v - 1.0, 0., 1.);
(self.screen2ray() * screen_pos).truncate().normalize()
}
ProjectionType::Planar { .. } => {
let coords = coords.into();
let start_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, -0.5);
let end_pos = Point3::new(2. * coords.u - 1., 2. * coords.v - 1.0, 0.5);
(self.screen2ray().transform_point(end_pos)
- self.screen2ray().transform_point(start_pos))
.normalize()
}
}
}

Expand Down Expand Up @@ -563,6 +638,7 @@ impl Camera {
fn screen2ray(&self) -> Mat4 {
let mut v = self.view;
if let ProjectionType::Perspective { .. } = self.projection_type {
v /= v[3][3];
v[3] = vec4(0.0, 0.0, 0.0, 1.0);
}
(self.projection * v)
Expand Down
4 changes: 2 additions & 2 deletions src/prelude/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
//!
pub use cgmath::{
dot, frustum, ortho, perspective, vec2, vec3, vec4, Deg, Matrix2, Matrix3, Matrix4, Point2,
Point3, Quaternion, Rad, Vector2, Vector3, Vector4,
dot, frustum, ortho, perspective, planar, vec2, vec3, vec4, Deg, Matrix2, Matrix3, Matrix4,
Point2, Point3, Quaternion, Rad, Vector2, Vector3, Vector4,
};
pub use cgmath::{
Angle, EuclideanSpace, InnerSpace, Matrix, MetricSpace, One, Rotation, Rotation2, Rotation3,
Expand Down

0 comments on commit 68a6d6c

Please sign in to comment.