use num_traits::Zero;
use num_traits::cast;
use structure::Angle;
use angle::Rad;
use matrix::Matrix4;
use num::BaseFloat;
pub fn perspective<S: BaseFloat, A: Into<Rad<S>>>(
fovy: A,
aspect: S,
near: S,
far: S,
) -> Matrix4<S> {
PerspectiveFov {
fovy: fovy.into(),
aspect: aspect,
near: near,
far: far,
}.into()
}
pub fn frustum<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
Perspective {
left: left,
right: right,
bottom: bottom,
top: top,
near: near,
far: far,
}.into()
}
pub fn ortho<S: BaseFloat>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
Ortho {
left: left,
right: right,
bottom: bottom,
top: top,
near: near,
far: far,
}.into()
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "rustc-serialize", derive(RustcEncodable, RustcDecodable))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct PerspectiveFov<S> {
pub fovy: Rad<S>,
pub aspect: S,
pub near: S,
pub far: S,
}
impl<S: BaseFloat> PerspectiveFov<S> {
pub fn to_perspective(&self) -> Perspective<S> {
let two: S = cast(2).unwrap();
let angle = self.fovy / two;
let ymax = self.near * Rad::tan(angle);
let xmax = ymax * self.aspect;
Perspective {
left: -xmax,
right: xmax,
bottom: -ymax,
top: ymax,
near: self.near.clone(),
far: self.far.clone(),
}
}
}
impl<S: BaseFloat> From<PerspectiveFov<S>> for Matrix4<S> {
fn from(persp: PerspectiveFov<S>) -> Matrix4<S> {
assert!(
persp.fovy > Rad::zero(),
"The vertical field of view cannot be below zero, 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.aspect > S::zero(),
"The aspect ratio cannot be below zero, found: {:?}",
persp.aspect
);
assert!(
persp.near > S::zero(),
"The near plane distance cannot be below zero, found: {:?}",
persp.near
);
assert!(
persp.far > S::zero(),
"The far plane distance cannot be below zero, found: {:?}",
persp.far
);
assert!(
persp.far > persp.near,
"The far plane cannot be closer than the near plane, found: far: {:?}, near: {:?}",
persp.far,
persp.near
);
let two: S = cast(2).unwrap();
let f = Rad::cot(persp.fovy / two);
let c0r0 = f / persp.aspect;
let c0r1 = S::zero();
let c0r2 = S::zero();
let c0r3 = S::zero();
let c1r0 = S::zero();
let c1r1 = f;
let c1r2 = S::zero();
let c1r3 = S::zero();
let c2r0 = S::zero();
let c2r1 = S::zero();
let c2r2 = (persp.far + persp.near) / (persp.near - persp.far);
let c2r3 = -S::one();
let c3r0 = S::zero();
let c3r1 = S::zero();
let c3r2 = (two * persp.far * persp.near) / (persp.near - persp.far);
let c3r3 = S::zero();
#[cfg_attr(rustfmt, rustfmt_skip)]
Matrix4::new(
c0r0, c0r1, c0r2, c0r3,
c1r0, c1r1, c1r2, c1r3,
c2r0, c2r1, c2r2, c2r3,
c3r0, c3r1, c3r2, c3r3,
)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Perspective<S> {
pub left: S,
pub right: S,
pub bottom: S,
pub top: S,
pub near: S,
pub far: S,
}
impl<S: BaseFloat> From<Perspective<S>> for Matrix4<S> {
fn from(persp: Perspective<S>) -> Matrix4<S> {
assert!(
persp.left <= persp.right,
"`left` cannot be greater than `right`, found: left: {:?} right: {:?}",
persp.left,
persp.right
);
assert!(
persp.bottom <= persp.top,
"`bottom` cannot be greater than `top`, found: bottom: {:?} top: {:?}",
persp.bottom,
persp.top
);
assert!(
persp.near <= persp.far,
"`near` cannot be greater than `far`, found: near: {:?} far: {:?}",
persp.near,
persp.far
);
let two: S = cast(2i8).unwrap();
let c0r0 = (two * persp.near) / (persp.right - persp.left);
let c0r1 = S::zero();
let c0r2 = S::zero();
let c0r3 = S::zero();
let c1r0 = S::zero();
let c1r1 = (two * persp.near) / (persp.top - persp.bottom);
let c1r2 = S::zero();
let c1r3 = S::zero();
let c2r0 = (persp.right + persp.left) / (persp.right - persp.left);
let c2r1 = (persp.top + persp.bottom) / (persp.top - persp.bottom);
let c2r2 = -(persp.far + persp.near) / (persp.far - persp.near);
let c2r3 = -S::one();
let c3r0 = S::zero();
let c3r1 = S::zero();
let c3r2 = -(two * persp.far * persp.near) / (persp.far - persp.near);
let c3r3 = S::zero();
#[cfg_attr(rustfmt, rustfmt_skip)]
Matrix4::new(
c0r0, c0r1, c0r2, c0r3,
c1r0, c1r1, c1r2, c1r3,
c2r0, c2r1, c2r2, c2r3,
c3r0, c3r1, c3r2, c3r3,
)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Ortho<S> {
pub left: S,
pub right: S,
pub bottom: S,
pub top: S,
pub near: S,
pub far: S,
}
impl<S: BaseFloat> From<Ortho<S>> for Matrix4<S> {
fn from(ortho: Ortho<S>) -> Matrix4<S> {
let two: S = cast(2).unwrap();
let c0r0 = two / (ortho.right - ortho.left);
let c0r1 = S::zero();
let c0r2 = S::zero();
let c0r3 = S::zero();
let c1r0 = S::zero();
let c1r1 = two / (ortho.top - ortho.bottom);
let c1r2 = S::zero();
let c1r3 = S::zero();
let c2r0 = S::zero();
let c2r1 = S::zero();
let c2r2 = -two / (ortho.far - ortho.near);
let c2r3 = S::zero();
let c3r0 = -(ortho.right + ortho.left) / (ortho.right - ortho.left);
let c3r1 = -(ortho.top + ortho.bottom) / (ortho.top - ortho.bottom);
let c3r2 = -(ortho.far + ortho.near) / (ortho.far - ortho.near);
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,
)
}
}