Skip to content

Commit

Permalink
Merge pull request #556 from thatcomputerguy0101/planar_projection
Browse files Browse the repository at this point in the history
Planar projection (perspective/orthographic combination projection)
  • Loading branch information
aloucks authored Oct 27, 2024
2 parents d5e765d + 8c29a7e commit 53e3f91
Show file tree
Hide file tree
Showing 2 changed files with 107 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ The library provides:
- rotation matrices: `Basis2`, `Basis3`
- angle units: `Rad`, `Deg`
- points: `Point2`, `Point3`
- perspective projections: `Perspective`, `PerspectiveFov`, `Ortho`
- perspective projections: `Perspective`, `PerspectiveFov`, `Ortho`, `PlanarFov`
- spatial transformations: `AffineMatrix3`, `Transform3`

Not all of the functionality has been implemented yet, and the existing code
Expand Down
106 changes: 106 additions & 0 deletions src/projection.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,28 @@ pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S
.into()
}

/// Create a planar projection matrix, which can be either perspective or orthographic.
///
/// The projection frustum is always `height` units high at the origin along the view direction,
/// making the focal point located at `(0.0, 0.0, cot(fovy / 2.0)) * height / 2.0`. Unlike
/// a standard perspective projection, this allows `fovy` to be zero or negative.
pub fn planar<S: BaseFloat, A: Into<Rad<S>>>(
fovy: A,
aspect: S,
height: S,
near: S,
far: S,
) -> Matrix4<S> {
PlanarFov {
fovy: fovy.into(),
aspect,
height,
near,
far,
}
.into()
}

/// A perspective projection based on a vertical field-of-view angle.
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
Expand Down Expand Up @@ -283,3 +305,87 @@ impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
)
}
}

/// A planar projection based on a vertical field-of-view angle.
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PlanarFov<S> {
pub fovy: Rad<S>,
pub aspect: S,
pub height: S,
pub near: S,
pub far: S,
}

impl<S: BaseFloat> From<PlanarFov<S>> for Matrix4<S> {
fn from(persp: PlanarFov<S>) -> Matrix4<S> {
assert!(
persp.fovy > -Rad::turn_div_2(),
"The vertical field of view cannot be less than a negative half turn, found: {:?}",
persp.fovy
);
assert!(
persp.fovy < Rad::turn_div_2(),
"The vertical field of view cannot be greater than a half turn, found: {:?}",
persp.fovy
);
assert! {
persp.height >= S::zero(),
"The projection plane height cannot be negative, found: {:?}",
persp.height
}

let two: S = cast(2).unwrap();
let inv_f = Rad::tan(persp.fovy / two);

let focal_point = -inv_f.recip();

assert!(
abs_diff_ne!(persp.aspect.abs(), S::zero()),
"The absolute aspect ratio cannot be zero, found: {:?}",
persp.aspect.abs()
);
assert!(
abs_diff_ne!(persp.far, persp.near),
"The far plane and near plane are too close, found: far: {:?}, near: {:?}",
persp.far,
persp.near
);
assert!(
focal_point < S::min(persp.far, persp.near) || focal_point > S::max(persp.far, persp.near),
"The focal point cannot be between the far and near planes, found: focal: {:?}, far: {:?}, near: {:?}",
focal_point,
persp.far,
persp.near,
);

let c0r0 = two / (persp.aspect * persp.height);
let c0r1 = S::zero();
let c0r2 = S::zero();
let c0r3 = S::zero();

let c1r0 = S::zero();
let c1r1 = two / persp.height;
let c1r2 = S::zero();
let c1r3 = S::zero();

let c2r0 = S::zero();
let c2r1 = S::zero();
let c2r2 = ((persp.far + persp.near) * inv_f + two) / (persp.near - persp.far);
let c2r3 = -inv_f;

let c3r0 = S::zero();
let c3r1 = S::zero();
let c3r2 = (two * persp.far * persp.near * inv_f + (persp.far + persp.near))
/ (persp.near - persp.far);
let c3r3 = S::one();

#[cfg_attr(rustfmt, rustfmt_skip)]
Matrix4::new(
c0r0, c0r1, c0r2, c0r3,
c1r0, c1r1, c1r2, c1r3,
c2r0, c2r1, c2r2, c2r3,
c3r0, c3r1, c3r2, c3r3,
)
}
}

0 comments on commit 53e3f91

Please sign in to comment.