From bf1dd601d774faef5806819f7b70040dadee7e6f Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Wed, 4 Sep 2013 14:39:21 +1000 Subject: [PATCH] Add projection types --- src/cgmath/projection.rs | 148 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/src/cgmath/projection.rs b/src/cgmath/projection.rs index 51c4652..366d2ae 100644 --- a/src/cgmath/projection.rs +++ b/src/cgmath/projection.rs @@ -92,3 +92,151 @@ pub fn ortho(left: S, right: S, bottom: S, top: S, near: S, fa c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3) } + +pub trait Projection { + fn if_valid(&self, f: &fn() -> U) -> Result; + fn to_mat4(&self) -> Result, ~str>; +} + +/// A perspective projection based on a vertical field-of-view angle. +#[deriving(Clone, Eq)] +pub struct PerspectiveFov { + fovy: S, //radians + aspect: S, + near: S, + far: S, +} + +impl PerspectiveFov { + pub fn to_perspective(&self) -> Result, ~str> { + do self.if_valid { + let angle = self.fovy / two::(); + let ymax = self.near * angle.tan(); + let xmax = ymax * self.aspect; + + Perspective { + left: -xmax, + right: xmax, + bottom: -ymax, + top: ymax, + near: self.near.clone(), + far: self.far.clone(), + } + } + } +} + +impl Projection for PerspectiveFov { + fn if_valid(&self, f: &fn() -> U) -> Result { + let frac_pi_2: S = Real::frac_pi_2(); + cond! ( + (self.fovy < zero()) { Err(fmt!("The vertical field of view cannot be below zero, found: %?", self.fovy)) } + (self.fovy > frac_pi_2) { Err(fmt!("The vertical field of view cannot be greater than a half turn, found: %?", self.fovy)) } + (self.aspect < zero()) { Err(fmt!("The aspect ratio cannot be below zero, found: %?", self.aspect)) } + (self.near < zero()) { Err(fmt!("The near plane distance cannot be below zero, found: %?", self.near)) } + (self.far < zero()) { Err(fmt!("The far plane distance cannot be below zero, found: %?", self.far)) } + (self.far < self.near) { Err(fmt!("The far plane cannot be closer than the near plane, found: far: %?, near: %?", self.far, self.near)) } + _ { Ok(f()) } + ) + } + + fn to_mat4(&self) -> Result, ~str> { + do self.to_perspective().chain |proj| { proj.to_mat4() } + } +} + +/// A perspective projection with arbitrary left/right/bottom/top distances +#[deriving(Clone, Eq)] +pub struct Perspective { + left: S, right: S, + bottom: S, top: S, + near: S, far: S, +} + +impl Projection for Perspective { + fn if_valid(&self, f: &fn() -> U) -> Result { + cond! ( + (self.left > self.right) { Err(fmt!("`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right)) } + (self.bottom > self.top) { Err(fmt!("`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top)) } + (self.near > self.far) { Err(fmt!("`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far)) } + _ { Ok(f()) } + ) + } + + fn to_mat4(&self) -> Result, ~str> { + do self.if_valid { + let c0r0 = (two::() * self.near) / (self.right - self.left); + let c0r1 = zero(); + let c0r2 = zero(); + let c0r3 = zero(); + + let c1r0 = zero(); + let c1r1 = (two::() * self.near) / (self.top - self.bottom); + let c1r2 = zero(); + let c1r3 = zero(); + + let c2r0 = (self.right + self.left) / (self.right - self.left); + let c2r1 = (self.top + self.bottom) / (self.top - self.bottom); + let c2r2 = -(self.far + self.near) / (self.far - self.near); + let c2r3 = -one::(); + + let c3r0 = zero(); + let c3r1 = zero(); + let c3r2 = -(two::() * self.far * self.near) / (self.far - self.near); + let c3r3 = zero(); + + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) + } + } +} + +/// An orthographic projection with arbitrary left/right/bottom/top distances +#[deriving(Clone, Eq)] +pub struct Ortho { + left: S, right: S, + bottom: S, top: S, + near: S, far: S, +} + +impl Projection for Ortho { + fn if_valid(&self, f: &fn() -> U) -> Result { + cond! ( + (self.left > self.right) { Err(fmt!("`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right)) } + (self.bottom > self.top) { Err(fmt!("`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top)) } + (self.near > self.far) { Err(fmt!("`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far)) } + _ { Ok(f()) } + ) + } + + fn to_mat4(&self) -> Result, ~str> { + do self.if_valid { + let c0r0 = two::() / (self.right - self.left); + let c0r1 = zero(); + let c0r2 = zero(); + let c0r3 = zero(); + + let c1r0 = zero(); + let c1r1 = two::() / (self.top - self.bottom); + let c1r2 = zero(); + let c1r3 = zero(); + + let c2r0 = zero(); + let c2r1 = zero(); + let c2r2 = -two::() / (self.far - self.near); + let c2r3 = -one::(); + + let c3r0 = -(self.right + self.left) / (self.right - self.left); + let c3r1 = -(self.top + self.bottom) / (self.top - self.bottom); + let c3r2 = -(self.far + self.near) / (self.far - self.near); + let c3r3 = one::(); + + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) + } + } +}