// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors, // refer to the Cargo.toml file at the top-level directory of this distribution. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use num_traits::{Zero}; use num_traits::cast; use structure::Angle; use angle::Rad; use matrix::Matrix4; use num::BaseFloat; /// Create a perspective projection matrix. /// /// 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) -> Matrix4 { PerspectiveFov { fovy: fovy.into(), aspect: aspect, near: near, far: far, }.into() } /// 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) -> Matrix4 { Perspective { left: left, right: right, bottom: bottom, top: top, near: near, far: far, }.into() } /// 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) -> Matrix4 { Ortho { left: left, right: right, bottom: bottom, top: top, near: near, far: 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))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct PerspectiveFov { pub fovy: Rad, pub aspect: S, pub near: S, pub far: S, } impl PerspectiveFov { pub fn to_perspective(&self) -> Perspective { 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 From> for Matrix4 { fn from(persp: PerspectiveFov) -> Matrix4 { 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(); Matrix4::new(c0r0, c0r1, c0r2, c0r3, c1r0, c1r1, c1r2, c1r3, c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3) } } /// A perspective projection with arbitrary left/right/bottom/top distances #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Perspective { pub left: S, pub right: S, pub bottom: S, pub top: S, pub near: S, pub far: S, } impl From> for Matrix4 { fn from(persp: Perspective) -> Matrix4 { 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(); Matrix4::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 #[derive(Copy, Clone, Debug, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub struct Ortho { pub left: S, pub right: S, pub bottom: S, pub top: S, pub near: S, pub far: S, } impl From> for Matrix4 { fn from(ortho: Ortho) -> Matrix4 { 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(); Matrix4::new(c0r0, c0r1, c0r2, c0r3, c1r0, c1r1, c1r2, c1r3, c2r0, c2r1, c2r2, c2r3, c3r0, c3r1, c3r2, c3r3) } }