From aef1d153a1fa5e8712acaef516018977651900d5 Mon Sep 17 00:00:00 2001 From: Brendan Zabarauskas Date: Thu, 5 Sep 2013 16:44:27 +1000 Subject: [PATCH] Simplify projection code --- src/cgmath/angle.rs | 4 + src/cgmath/projection.rs | 298 ++++++++++++++++++--------------------- 2 files changed, 141 insertions(+), 161 deletions(-) diff --git a/src/cgmath/angle.rs b/src/cgmath/angle.rs index 2e10f45..43b6d42 100644 --- a/src/cgmath/angle.rs +++ b/src/cgmath/angle.rs @@ -101,6 +101,10 @@ impl Angle for Deg { #[inline] pub fn tan>(theta: A) -> S { theta.to_rad().s.tan() } #[inline] pub fn sin_cos>(theta: A) -> (S, S) { theta.to_rad().s.sin_cos() } +#[inline] pub fn cot>(theta: A) -> S { tan(theta).recip() } +#[inline] pub fn sec>(theta: A) -> S { cos(theta).recip() } +#[inline] pub fn csc>(theta: A) -> S { sin(theta).recip() } + #[inline] pub fn asin>(s: S) -> A { Angle::from(rad(s.asin())) } #[inline] pub fn acos>(s: S) -> A { Angle::from(rad(s.acos())) } #[inline] pub fn atan>(s: S) -> A { Angle::from(rad(s.atan())) } diff --git a/src/cgmath/projection.rs b/src/cgmath/projection.rs index 621330d..c6e72e7 100644 --- a/src/cgmath/projection.rs +++ b/src/cgmath/projection.rs @@ -15,137 +15,123 @@ use std::num::{zero, one}; -use angle::{Angle, Rad, rad, tan}; -use matrix::Mat4; +use angle::{Angle, rad, tan, cot}; +use matrix::{Mat4, ToMat4}; use util::two; -/// Create a perspective projection matrix +/// Create a perspective projection matrix. /// -/// Note: the fovy parameter should be specified in degrees. -/// -/// This is the equivalent of the gluPerspective function, the algorithm of which -/// can be found [here](http://www.opengl.org/wiki/GluPerspective_code). -pub fn perspective(fovy: Rad, aspectRatio: S, near: S, far: S) -> Mat4 { - let ymax = near * tan(fovy.div_s(two::())); - let xmax = ymax * aspectRatio; - - frustum(-xmax, xmax, -ymax, ymax, near, far) +/// This is the equivalent to the [gluPerspective] +/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function. +pub fn perspective>(fovy: A, aspect: S, near: S, far: S) -> Mat4 { + PerspectiveFov { + fovy: fovy, + aspect: aspect, + near: near, + far: far, + }.to_mat4() } -/// Define a view frustrum +/// Create a perspective matrix from a view frustrum. /// /// This is the equivalent of the now deprecated [glFrustrum] /// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function. pub fn frustum(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Mat4 { - let c0r0 = (two::() * near) / (right - left); - let c0r1 = zero(); - let c0r2 = zero(); - let c0r3 = zero(); - - let c1r0 = zero(); - let c1r1 = (two::() * near) / (top - bottom); - let c1r2 = zero(); - let c1r3 = zero(); - - let c2r0 = (right + left) / (right - left); - let c2r1 = (top + bottom) / (top - bottom); - let c2r2 = -(far + near) / (far - near); - let c2r3 = -one::(); - - let c3r0 = zero(); - let c3r1 = zero(); - let c3r2 = -(two::() * far * near) / (far - near); - let c3r3 = zero(); - - Mat4::new(c0r0, c0r1, c0r2, c0r3, - c1r0, c1r1, c1r2, c1r3, - c2r0, c2r1, c2r2, c2r3, - c3r0, c3r1, c3r2, c3r3) + Perspective { + left: left, + right: right, + bottom: bottom, + top: top, + near: near, + far: far, + }.to_mat4() } -/// Create an orthographic projection matrix +/// Create an orthographic projection matrix. /// /// This is the equivalent of the now deprecated [glOrtho] /// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function. pub fn ortho(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Mat4 { - let c0r0 = two::() / (right - left); - let c0r1 = zero(); - let c0r2 = zero(); - let c0r3 = zero(); - - let c1r0 = zero(); - let c1r1 = two::() / (top - bottom); - let c1r2 = zero(); - let c1r3 = zero(); - - let c2r0 = zero(); - let c2r1 = zero(); - let c2r2 = -two::() / (far - near); - let c2r3 = zero(); - - let c3r0 = -(right + left) / (right - left); - let c3r1 = -(top + bottom) / (top - bottom); - let c3r2 = -(far + near) / (far - near); - let c3r3 = one(); - - Mat4::new(c0r0, c0r1, c0r2, c0r3, - c1r0, c1r1, c1r2, c1r3, - c2r0, c2r1, c2r2, c2r3, - c3r0, c3r1, c3r2, c3r3) + Ortho { + left: left, + right: right, + bottom: bottom, + top: top, + near: near, + far: far, + }.to_mat4() } -pub trait Projection { - fn if_valid(&self, f: &fn() -> U) -> Result; - fn to_mat4(&self) -> Result, ~str>; -} +pub trait Projection: ToMat4 {} /// A perspective projection based on a vertical field-of-view angle. #[deriving(Clone, Eq)] -pub struct PerspectiveFov { - fovy: Rad, +pub struct PerspectiveFov { + fovy: A, aspect: S, near: S, far: S, } -impl PerspectiveFov { - pub fn to_perspective(&self) -> Result, ~str> { - do self.if_valid { - let angle = self.fovy.div_s(two::()); - let ymax = self.near * tan(angle); - let xmax = ymax * self.aspect; +impl> PerspectiveFov { + pub fn to_perspective(&self) -> Perspective { + let angle = self.fovy.div_s(two::()); + let ymax = self.near * tan(angle); + let xmax = ymax * self.aspect; - Perspective { - left: -xmax, - right: xmax, - bottom: -ymax, - top: ymax, - near: self.near.clone(), - far: self.far.clone(), - } + 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 half_turn: Rad = rad(Real::frac_pi_2()); - cond! ( - (self.fovy < zero()) { Err(fmt!("The vertical field of view cannot be below zero, found: %?", self.fovy)) } - (self.fovy > half_turn) { 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()) } - ) - } +impl> ToMat4 for PerspectiveFov { + fn to_mat4(&self) -> Mat4 { + let half_turn: A = Angle::from(rad::(Real::frac_pi_2())); - fn to_mat4(&self) -> Result, ~str> { - do self.to_perspective().chain |proj| { proj.to_mat4() } + assert!(self.fovy < zero(), "The vertical field of view cannot be below zero, found: %?", self.fovy); + assert!(self.fovy > half_turn, "The vertical field of view cannot be greater than a half turn, found: %?", self.fovy); + assert!(self.aspect < zero(), "The aspect ratio cannot be below zero, found: %?", self.aspect); + assert!(self.near < zero(), "The near plane distance cannot be below zero, found: %?", self.near); + assert!(self.far < zero(), "The far plane distance cannot be below zero, found: %?", self.far); + assert!(self.far < self.near, "The far plane cannot be closer than the near plane, found: far: %?, near: %?", self.far, self.near); + + let f = cot(self.fovy.div_s(two::())); + + let c0r0 = f / self.aspect; + let c0r1 = zero(); + let c0r2 = zero(); + let c0r3 = zero(); + + let c1r0 = zero(); + let c1r1 = f; + let c1r2 = zero(); + let c1r3 = zero(); + + let c2r0 = zero(); + let c2r1 = zero(); + let c2r2 = (self.far + self.near) / (self.near - self.far); + let c2r3 = -one::(); + + let c3r0 = zero(); + let c3r1 = zero(); + let c3r2 = (two::() * self.far * self.near) / (self.near - self.far); + let c3r3 = zero(); + + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) } } +impl> Projection for PerspectiveFov; + /// A perspective projection with arbitrary left/right/bottom/top distances #[deriving(Clone, Eq)] pub struct Perspective { @@ -154,46 +140,41 @@ pub struct Perspective { 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()) } - ) - } +impl ToMat4 for Perspective { + fn to_mat4(&self) -> Mat4 { + assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right); + assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top); + assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far); - 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 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 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 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(); + 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) - } + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) } } +impl Projection for Perspective; + /// An orthographic projection with arbitrary left/right/bottom/top distances #[deriving(Clone, Eq)] pub struct Ortho { @@ -202,42 +183,37 @@ pub struct Ortho { 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()) } - ) - } +impl ToMat4 for Ortho { + fn to_mat4(&self) -> Mat4 { + assert!(self.left > self.right, "`left` cannot be greater than `right`, found: left: %? right: %?", self.left, self.right); + assert!(self.bottom > self.top, "`bottom` cannot be greater than `top`, found: bottom: %? top: %?", self.bottom, self.top); + assert!(self.near > self.far, "`near` cannot be greater than `far`, found: near: %? far: %?", self.near, self.far); - 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 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 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 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::(); + 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) - } + Mat4::new(c0r0, c0r1, c0r2, c0r3, + c1r0, c1r1, c1r2, c1r3, + c2r0, c2r1, c2r2, c2r3, + c3r0, c3r1, c3r2, c3r3) } } + +impl Projection for Ortho;