2014-05-26 17:10:04 +00:00
|
|
|
// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
|
2013-09-03 05:23:56 +00:00
|
|
|
// refer to the AUTHORS 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.
|
|
|
|
|
2014-11-20 15:28:02 +00:00
|
|
|
use std::num::cast;
|
2013-09-03 05:23:56 +00:00
|
|
|
|
2013-09-17 05:28:27 +00:00
|
|
|
use angle::{Angle, tan, cot};
|
2013-09-05 07:19:31 +00:00
|
|
|
use frustum::Frustum;
|
2014-04-14 01:30:24 +00:00
|
|
|
use matrix::{Matrix4, ToMatrix4};
|
2014-11-20 15:28:02 +00:00
|
|
|
use num::{BaseFloat, zero, one};
|
2013-09-19 04:52:40 +00:00
|
|
|
use plane::Plane;
|
2013-09-03 05:23:56 +00:00
|
|
|
|
2013-09-05 06:44:27 +00:00
|
|
|
/// Create a perspective projection matrix.
|
2013-09-03 05:23:56 +00:00
|
|
|
///
|
2013-09-05 06:44:27 +00:00
|
|
|
/// This is the equivalent to the [gluPerspective]
|
|
|
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/gluPerspective.xml) function.
|
2014-09-18 14:19:26 +00:00
|
|
|
pub fn perspective<S: BaseFloat + 'static, A: Angle<S>>(fovy: A, aspect: S, near: S, far: S) -> Matrix4<S> {
|
2013-09-05 06:44:27 +00:00
|
|
|
PerspectiveFov {
|
|
|
|
fovy: fovy,
|
|
|
|
aspect: aspect,
|
|
|
|
near: near,
|
|
|
|
far: far,
|
2014-04-14 01:30:24 +00:00
|
|
|
}.to_matrix4()
|
2013-09-03 05:23:56 +00:00
|
|
|
}
|
|
|
|
|
2013-09-05 06:44:27 +00:00
|
|
|
/// Create a perspective matrix from a view frustrum.
|
2013-09-03 05:23:56 +00:00
|
|
|
///
|
|
|
|
/// This is the equivalent of the now deprecated [glFrustrum]
|
|
|
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glFrustum.xml) function.
|
2014-09-18 14:19:26 +00:00
|
|
|
pub fn frustum<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
2013-09-05 06:44:27 +00:00
|
|
|
Perspective {
|
|
|
|
left: left,
|
|
|
|
right: right,
|
|
|
|
bottom: bottom,
|
|
|
|
top: top,
|
|
|
|
near: near,
|
|
|
|
far: far,
|
2014-04-14 01:30:24 +00:00
|
|
|
}.to_matrix4()
|
2013-09-03 05:23:56 +00:00
|
|
|
}
|
|
|
|
|
2013-09-05 06:44:27 +00:00
|
|
|
/// Create an orthographic projection matrix.
|
2013-09-03 05:23:56 +00:00
|
|
|
///
|
|
|
|
/// This is the equivalent of the now deprecated [glOrtho]
|
|
|
|
/// (http://www.opengl.org/sdk/docs/man2/xhtml/glOrtho.xml) function.
|
2014-09-18 14:19:26 +00:00
|
|
|
pub fn ortho<S: BaseFloat + 'static>(left: S, right: S, bottom: S, top: S, near: S, far: S) -> Matrix4<S> {
|
2013-09-05 06:44:27 +00:00
|
|
|
Ortho {
|
|
|
|
left: left,
|
|
|
|
right: right,
|
|
|
|
bottom: bottom,
|
|
|
|
top: top,
|
|
|
|
near: near,
|
|
|
|
far: far,
|
2014-04-14 01:30:24 +00:00
|
|
|
}.to_matrix4()
|
2013-09-03 05:23:56 +00:00
|
|
|
}
|
2013-09-04 04:39:21 +00:00
|
|
|
|
2014-04-14 01:30:24 +00:00
|
|
|
pub trait Projection<S>: ToMatrix4<S> {
|
2013-09-05 07:19:31 +00:00
|
|
|
fn to_frustum(&self) -> Frustum<S>;
|
|
|
|
}
|
2013-09-04 04:39:21 +00:00
|
|
|
|
|
|
|
/// A perspective projection based on a vertical field-of-view angle.
|
2015-01-03 21:29:26 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
2013-09-05 06:44:27 +00:00
|
|
|
pub struct PerspectiveFov<S, A> {
|
2014-04-01 11:00:17 +00:00
|
|
|
pub fovy: A,
|
|
|
|
pub aspect: S,
|
|
|
|
pub near: S,
|
|
|
|
pub far: S,
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
|
2014-05-26 17:10:04 +00:00
|
|
|
impl<S: BaseFloat, A: Angle<S>> PerspectiveFov<S, A> {
|
2013-09-05 06:44:27 +00:00
|
|
|
pub fn to_perspective(&self) -> Perspective<S> {
|
2014-06-26 04:26:15 +00:00
|
|
|
let angle = self.fovy.div_s(cast(2i).unwrap());
|
2013-10-12 23:52:21 +00:00
|
|
|
let ymax = self.near * tan(angle.to_rad());
|
2013-09-05 06:44:27 +00:00
|
|
|
let xmax = ymax * self.aspect;
|
|
|
|
|
|
|
|
Perspective {
|
|
|
|
left: -xmax,
|
|
|
|
right: xmax,
|
|
|
|
bottom: -ymax,
|
|
|
|
top: ymax,
|
|
|
|
near: self.near.clone(),
|
|
|
|
far: self.far.clone(),
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-18 14:19:26 +00:00
|
|
|
impl<S: BaseFloat + 'static, A: Angle<S>> Projection<S> for PerspectiveFov<S, A> {
|
2013-09-05 07:19:31 +00:00
|
|
|
fn to_frustum(&self) -> Frustum<S> {
|
2013-09-19 04:52:40 +00:00
|
|
|
// TODO: Could this be faster?
|
2014-04-14 01:30:24 +00:00
|
|
|
Frustum::from_matrix4(self.to_matrix4())
|
2013-09-05 07:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-26 17:10:04 +00:00
|
|
|
impl<S: BaseFloat, A: Angle<S>> ToMatrix4<S> for PerspectiveFov<S, A> {
|
2014-04-14 01:30:24 +00:00
|
|
|
fn to_matrix4(&self) -> Matrix4<S> {
|
2013-09-17 05:28:27 +00:00
|
|
|
let half_turn: A = Angle::turn_div_2();
|
2013-09-05 06:44:27 +00:00
|
|
|
|
2014-05-28 17:53:45 +00:00
|
|
|
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);
|
2013-09-05 06:44:27 +00:00
|
|
|
|
2014-06-26 04:26:15 +00:00
|
|
|
let f = cot(self.fovy.div_s(cast(2i).unwrap()).to_rad());
|
|
|
|
let two: S = cast(2i).unwrap();
|
2013-09-05 06:44:27 +00:00
|
|
|
|
|
|
|
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::<S>();
|
|
|
|
|
|
|
|
let c3r0 = zero();
|
|
|
|
let c3r1 = zero();
|
2013-09-19 04:57:36 +00:00
|
|
|
let c3r2 = (two * self.far * self.near) / (self.near - self.far);
|
2013-09-05 06:44:27 +00:00
|
|
|
let c3r3 = zero();
|
|
|
|
|
2014-04-14 01:30:24 +00:00
|
|
|
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
2014-04-14 01:41:29 +00:00
|
|
|
c1r0, c1r1, c1r2, c1r3,
|
|
|
|
c2r0, c2r1, c2r2, c2r3,
|
|
|
|
c3r0, c3r1, c3r2, c3r3)
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// A perspective projection with arbitrary left/right/bottom/top distances
|
2015-01-03 21:29:26 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
2013-09-04 04:39:21 +00:00
|
|
|
pub struct Perspective<S> {
|
2014-04-01 11:00:17 +00:00
|
|
|
pub left: S, right: S,
|
|
|
|
pub bottom: S, top: S,
|
|
|
|
pub near: S, far: S,
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
|
2014-09-18 14:19:26 +00:00
|
|
|
impl<S: BaseFloat + 'static> Projection<S> for Perspective<S> {
|
2013-09-05 07:19:31 +00:00
|
|
|
fn to_frustum(&self) -> Frustum<S> {
|
2013-09-19 04:52:40 +00:00
|
|
|
// TODO: Could this be faster?
|
2014-04-14 01:30:24 +00:00
|
|
|
Frustum::from_matrix4(self.to_matrix4())
|
2013-09-05 07:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-09-18 14:19:26 +00:00
|
|
|
impl<S: BaseFloat + 'static> ToMatrix4<S> for Perspective<S> {
|
2014-04-14 01:30:24 +00:00
|
|
|
fn to_matrix4(&self) -> Matrix4<S> {
|
2014-07-18 15:55:53 +00:00
|
|
|
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);
|
2013-09-05 06:44:27 +00:00
|
|
|
|
2014-06-26 04:26:15 +00:00
|
|
|
let two: S = cast(2i).unwrap();
|
2013-09-19 04:57:36 +00:00
|
|
|
|
|
|
|
let c0r0 = (two * self.near) / (self.right - self.left);
|
2013-09-05 06:44:27 +00:00
|
|
|
let c0r1 = zero();
|
|
|
|
let c0r2 = zero();
|
|
|
|
let c0r3 = zero();
|
|
|
|
|
|
|
|
let c1r0 = zero();
|
2013-09-19 04:57:36 +00:00
|
|
|
let c1r1 = (two * self.near) / (self.top - self.bottom);
|
2013-09-05 06:44:27 +00:00
|
|
|
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::<S>();
|
|
|
|
|
|
|
|
let c3r0 = zero();
|
|
|
|
let c3r1 = zero();
|
2013-09-19 04:57:36 +00:00
|
|
|
let c3r2 = -(two * self.far * self.near) / (self.far - self.near);
|
2013-09-05 06:44:27 +00:00
|
|
|
let c3r3 = zero();
|
|
|
|
|
2014-04-14 01:30:24 +00:00
|
|
|
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
2014-04-14 01:41:29 +00:00
|
|
|
c1r0, c1r1, c1r2, c1r3,
|
|
|
|
c2r0, c2r1, c2r2, c2r3,
|
|
|
|
c3r0, c3r1, c3r2, c3r3)
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// An orthographic projection with arbitrary left/right/bottom/top distances
|
2015-01-03 21:29:26 +00:00
|
|
|
#[derive(Copy, Clone, PartialEq, RustcEncodable, RustcDecodable)]
|
2013-09-04 04:39:21 +00:00
|
|
|
pub struct Ortho<S> {
|
2014-04-01 11:00:17 +00:00
|
|
|
pub left: S, right: S,
|
|
|
|
pub bottom: S, top: S,
|
|
|
|
pub near: S, far: S,
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
|
2014-05-26 17:10:04 +00:00
|
|
|
impl<S: BaseFloat> Projection<S> for Ortho<S> {
|
2013-09-05 07:19:31 +00:00
|
|
|
fn to_frustum(&self) -> Frustum<S> {
|
2013-09-19 04:52:40 +00:00
|
|
|
Frustum {
|
|
|
|
left: Plane::from_abcd( one::<S>(), zero::<S>(), zero::<S>(), self.left.clone()),
|
|
|
|
right: Plane::from_abcd(-one::<S>(), zero::<S>(), zero::<S>(), self.right.clone()),
|
|
|
|
bottom: Plane::from_abcd(zero::<S>(), one::<S>(), zero::<S>(), self.bottom.clone()),
|
|
|
|
top: Plane::from_abcd(zero::<S>(), -one::<S>(), zero::<S>(), self.top.clone()),
|
|
|
|
near: Plane::from_abcd(zero::<S>(), zero::<S>(), -one::<S>(), self.near.clone()),
|
|
|
|
far: Plane::from_abcd(zero::<S>(), zero::<S>(), one::<S>(), self.far.clone()),
|
|
|
|
}
|
2013-09-05 07:19:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-26 17:10:04 +00:00
|
|
|
impl<S: BaseFloat> ToMatrix4<S> for Ortho<S> {
|
2014-04-14 01:30:24 +00:00
|
|
|
fn to_matrix4(&self) -> Matrix4<S> {
|
2014-06-26 04:26:15 +00:00
|
|
|
let two: S = cast(2i).unwrap();
|
2013-09-19 04:57:36 +00:00
|
|
|
|
|
|
|
let c0r0 = two / (self.right - self.left);
|
2013-09-05 06:44:27 +00:00
|
|
|
let c0r1 = zero();
|
|
|
|
let c0r2 = zero();
|
|
|
|
let c0r3 = zero();
|
|
|
|
|
|
|
|
let c1r0 = zero();
|
2013-09-19 04:57:36 +00:00
|
|
|
let c1r1 = two / (self.top - self.bottom);
|
2013-09-05 06:44:27 +00:00
|
|
|
let c1r2 = zero();
|
|
|
|
let c1r3 = zero();
|
|
|
|
|
|
|
|
let c2r0 = zero();
|
|
|
|
let c2r1 = zero();
|
2013-09-19 04:57:36 +00:00
|
|
|
let c2r2 = -two / (self.far - self.near);
|
2014-10-05 00:09:56 +00:00
|
|
|
let c2r3 = zero();
|
2013-09-05 06:44:27 +00:00
|
|
|
|
|
|
|
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::<S>();
|
|
|
|
|
2014-04-14 01:30:24 +00:00
|
|
|
Matrix4::new(c0r0, c0r1, c0r2, c0r3,
|
2014-04-14 01:41:29 +00:00
|
|
|
c1r0, c1r1, c1r2, c1r3,
|
|
|
|
c2r0, c2r1, c2r2, c2r3,
|
|
|
|
c3r0, c3r1, c3r2, c3r3)
|
2013-09-04 04:39:21 +00:00
|
|
|
}
|
|
|
|
}
|