Make look_at, perspective, and orthographic projection matrices conform to computer-graphics convensions.
The `look_at` method itself has been split into a right-handed and a left-handed variant: `look_at_rh` and `look_at_lh`. Fix #171, #158, #88, #79.
This commit is contained in:
parent
91e14670ed
commit
4c58e37910
|
@ -16,7 +16,7 @@ use structs::rot::{Rot2, Rot3};
|
|||
use quickcheck::{Arbitrary, Gen};
|
||||
|
||||
|
||||
/// Two dimensional isometry.
|
||||
/// Two dimensional **direct** isometry.
|
||||
///
|
||||
/// This is the composition of a rotation followed by a translation. Vectors `Vec2` are not
|
||||
/// affected by the translational component of this transformation while points `Pnt2` are.
|
||||
|
@ -30,7 +30,7 @@ pub struct Iso2<N> {
|
|||
pub translation: Vec2<N>
|
||||
}
|
||||
|
||||
/// Three dimensional isometry.
|
||||
/// Three dimensional **direct** isometry.
|
||||
///
|
||||
/// This is the composition of a rotation followed by a translation. Vectors `Vec3` are not
|
||||
/// affected by the translational component of this transformation while points `Pnt3` are.
|
||||
|
@ -62,20 +62,40 @@ impl<N: Clone + BaseFloat> Iso3<N> {
|
|||
Iso3::new_with_rotmat(eye.as_vec().clone(), new_rotmat)
|
||||
}
|
||||
|
||||
/// Builds a look-at view matrix.
|
||||
/// Builds a right-handed look-at view matrix.
|
||||
///
|
||||
/// This conforms to the common notion of "look-at" matrix from the computer graphics
|
||||
/// community. Its maps the view direction `target - eye` to the **negative** `z` axis and the
|
||||
/// `eye` to the origin.
|
||||
/// This conforms to the common notion of right handed look-at matrix from the computer
|
||||
/// graphics community.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * eye - The eye position.
|
||||
/// * target - The target position.
|
||||
/// * up - The vertical view direction. It must not be to collinear to `eye - target`.
|
||||
/// * up - A vector approximately aligned with required the vertical axis. The only
|
||||
/// requirement of this parameter is to not be collinear to `target - eye`.
|
||||
#[inline]
|
||||
pub fn new_look_at(eye: &Pnt3<N>, target: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> {
|
||||
let new_rotmat = Rot3::new_look_at(&(*target - *eye), up);
|
||||
Iso3::new_with_rotmat(new_rotmat * (-*eye.as_vec()), new_rotmat)
|
||||
pub fn look_at_rh(eye: &Pnt3<N>, target: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> {
|
||||
let rot = Rot3::look_at_rh(&(*target - *eye), up);
|
||||
let trans = rot * (-*eye);
|
||||
|
||||
Iso3::new_with_rotmat(trans.to_vec(), rot)
|
||||
}
|
||||
|
||||
/// Builds a left-handed look-at view matrix.
|
||||
///
|
||||
/// This conforms to the common notion of left handed look-at matrix from the computer
|
||||
/// graphics community.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * eye - The eye position.
|
||||
/// * target - The target position.
|
||||
/// * up - A vector approximately aligned with required the vertical axis. The only
|
||||
/// requirement of this parameter is to not be collinear to `target - eye`.
|
||||
#[inline]
|
||||
pub fn look_at_lh(eye: &Pnt3<N>, target: &Pnt3<N>, up: &Vec3<N>) -> Iso3<N> {
|
||||
let rot = Rot3::look_at_lh(&(*target - *eye), up);
|
||||
let trans = rot * (-*eye);
|
||||
|
||||
Iso3::new_with_rotmat(trans.to_vec(), rot)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,18 +7,24 @@ use quickcheck::{Arbitrary, Gen};
|
|||
|
||||
/// A 3D orthographic projection stored without any matrix.
|
||||
///
|
||||
/// Reading or modifying its individual properties is cheap but applying the transformation is costly.
|
||||
/// This flips the `z` axis and maps a axis-aligned cube to the unit cube with corners varying from
|
||||
/// `(-1, -1, -1)` to `(1, 1, 1)`. Reading or modifying its individual properties is cheap but
|
||||
/// applying the transformation is costly.
|
||||
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)]
|
||||
pub struct Ortho3<N> {
|
||||
width: N,
|
||||
height: N,
|
||||
left: N,
|
||||
right: N,
|
||||
bottom: N,
|
||||
top: N,
|
||||
znear: N,
|
||||
zfar: N
|
||||
}
|
||||
|
||||
/// A 3D orthographic projection stored as a 4D matrix.
|
||||
///
|
||||
/// Reading or modifying its individual properties is costly but applying the transformation is cheap.
|
||||
/// This flips the `z` axis and maps a axis-aligned cube to the unit cube with corners varying from
|
||||
/// `(-1, -1, -1)` to `(1, 1, 1)`. Reading or modifying its individual properties is costly but
|
||||
/// applying the transformation is cheap.
|
||||
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)]
|
||||
pub struct OrthoMat3<N> {
|
||||
mat: Mat4<N>
|
||||
|
@ -26,14 +32,16 @@ pub struct OrthoMat3<N> {
|
|||
|
||||
impl<N: BaseFloat> Ortho3<N> {
|
||||
/// Creates a new 3D orthographic projection.
|
||||
pub fn new(width: N, height: N, znear: N, zfar: N) -> Ortho3<N> {
|
||||
pub fn new(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> Ortho3<N> {
|
||||
assert!(!::is_zero(&(zfar - znear)));
|
||||
assert!(!::is_zero(&width));
|
||||
assert!(!::is_zero(&height));
|
||||
assert!(!::is_zero(&(left - right)));
|
||||
assert!(!::is_zero(&(top - bottom)));
|
||||
|
||||
Ortho3 {
|
||||
width: width,
|
||||
height: height,
|
||||
left: left,
|
||||
right: right,
|
||||
bottom: bottom,
|
||||
top: top,
|
||||
znear: znear,
|
||||
zfar: zfar
|
||||
}
|
||||
|
@ -41,37 +49,51 @@ impl<N: BaseFloat> Ortho3<N> {
|
|||
|
||||
/// Builds a 4D projection matrix (using homogeneous coordinates) for this projection.
|
||||
pub fn to_mat(&self) -> Mat4<N> {
|
||||
self.to_ortho_mat().mat
|
||||
self.to_persp_mat().mat
|
||||
}
|
||||
|
||||
/// Build a `OrthoMat3` representing this projection.
|
||||
pub fn to_ortho_mat(&self) -> OrthoMat3<N> {
|
||||
OrthoMat3::new(self.width, self.height, self.znear, self.zfar)
|
||||
pub fn to_persp_mat(&self) -> OrthoMat3<N> {
|
||||
OrthoMat3::new(self.left, self.right, self.bottom, self.top, self.znear, self.zfar)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature="arbitrary")]
|
||||
impl<N: Arbitrary + BaseFloat> Arbitrary for Ortho3<N> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> Ortho3<N> {
|
||||
let width = reject(g, |x| !::is_zero(x));
|
||||
let height = reject(g, |x| !::is_zero(x));
|
||||
let znear = Arbitrary::arbitrary(g);
|
||||
let zfar = reject(g, |&x: &N| !::is_zero(&(x - znear)));
|
||||
let left = Arbitrary::arbitrary(g);
|
||||
let right = reject(g, |x: &N| *x > left);
|
||||
let bottom = Arbitrary::arbitrary(g);
|
||||
let top = reject(g, |x: &N| *x > bottom);
|
||||
let znear = Arbitrary::arbitrary(g);
|
||||
let zfar = reject(g, |x: &N| *x > znear);
|
||||
Ortho3::new(width, height, znear, zfar)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: BaseFloat + Clone> Ortho3<N> {
|
||||
/// The width of the view cuboid.
|
||||
/// The smallest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn width(&self) -> N {
|
||||
self.width.clone()
|
||||
pub fn left(&self) -> N {
|
||||
self.left.clone()
|
||||
}
|
||||
|
||||
/// The height of the view cuboid.
|
||||
/// The largest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn height(&self) -> N {
|
||||
self.height.clone()
|
||||
pub fn right(&self) -> N {
|
||||
self.right.clone()
|
||||
}
|
||||
|
||||
/// The smallest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn bottom(&self) -> N {
|
||||
self.bottom.clone()
|
||||
}
|
||||
|
||||
/// The largest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn top(&self) -> N {
|
||||
self.top.clone()
|
||||
}
|
||||
|
||||
/// The near plane offset of the view cuboid.
|
||||
|
@ -86,27 +108,45 @@ impl<N: BaseFloat + Clone> Ortho3<N> {
|
|||
self.zfar.clone()
|
||||
}
|
||||
|
||||
/// Sets the width of the view cuboid.
|
||||
/// Sets the smallest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_width(&mut self, width: N) {
|
||||
self.width = width
|
||||
pub fn set_left(&mut self, left: N) {
|
||||
assert!(left < self.right, "The left corner must be farther than the right corner.");
|
||||
self.left = left
|
||||
}
|
||||
|
||||
/// Sets the height of the view cuboid.
|
||||
/// Sets the largest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_height(&mut self, height: N) {
|
||||
self.height = height
|
||||
pub fn set_right(&mut self, right: N) {
|
||||
assert!(right > self.left, "The left corner must be farther than the right corner.");
|
||||
self.right = right
|
||||
}
|
||||
|
||||
/// Sets the smallest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_bottom(&mut self, bottom: N) {
|
||||
assert!(bottom < self.top, "The top corner must be higher than the bottom corner.");
|
||||
self.bottom = bottom
|
||||
}
|
||||
|
||||
/// Sets the largest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_top(&mut self, top: N) {
|
||||
assert!(top > self.bottom, "The top corner must be higher than the left corner.");
|
||||
self.top = top
|
||||
}
|
||||
|
||||
/// Sets the near plane offset of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_znear(&mut self, znear: N) {
|
||||
assert!(znear < self.zfar, "The far plane must be farther than the near plane.");
|
||||
self.znear = znear
|
||||
}
|
||||
|
||||
/// Sets the far plane offset of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_zfar(&mut self, zfar: N) {
|
||||
assert!(zfar > self.znear, "The far plane must be farther than the near plane.");
|
||||
self.zfar = zfar
|
||||
}
|
||||
|
||||
|
@ -114,34 +154,47 @@ impl<N: BaseFloat + Clone> Ortho3<N> {
|
|||
#[inline]
|
||||
pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> {
|
||||
// FIXME: optimize that
|
||||
self.to_ortho_mat().project_pnt(p)
|
||||
self.to_persp_mat().project_pnt(p)
|
||||
}
|
||||
|
||||
/// Projects a vector.
|
||||
#[inline]
|
||||
pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> {
|
||||
// FIXME: optimize that
|
||||
self.to_ortho_mat().project_vec(p)
|
||||
self.to_persp_mat().project_vec(p)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: BaseFloat> OrthoMat3<N> {
|
||||
/// Creates a new orthographic projection matrix from the width, heihgt, znear and zfar planes of the view cuboid.
|
||||
pub fn new(width: N, height: N, znear: N, zfar: N) -> OrthoMat3<N> {
|
||||
assert!(!::is_zero(&(zfar - znear)));
|
||||
assert!(!::is_zero(&width));
|
||||
assert!(!::is_zero(&height));
|
||||
/// Creates a new orthographic projection matrix.
|
||||
pub fn new(left: N, right: N, bottom: N, top: N, znear: N, zfar: N) -> OrthoMat3<N> {
|
||||
assert!(left < right, "The left corner must be farther than the right corner.");
|
||||
assert!(bottom < top, "The top corner must be higher than the bottom corner.");
|
||||
assert!(znear < zfar, "The far plane must be farther than the near plane.");
|
||||
|
||||
let mat: Mat4<N> = ::one();
|
||||
|
||||
let mut res = OrthoMat3 { mat: mat };
|
||||
res.set_width(width);
|
||||
res.set_height(height);
|
||||
res.set_left_and_right(left, right);
|
||||
res.set_bottom_and_top(bottom, top);
|
||||
res.set_znear_and_zfar(znear, zfar);
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
/// Creates a new orthographic projection matrix from an aspect ratio and the vertical field of view.
|
||||
pub fn new_with_fov(aspect: N, vfov: N, znear: N, zfar: N) -> OrthoMat3<N> {
|
||||
assert!(znear < zfar, "The far plane must be farther than the near plane.");
|
||||
assert!(!::is_zero(&aspect));
|
||||
|
||||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
let width = zfar * (vfov / _2).tan();
|
||||
let height = width / aspect;
|
||||
|
||||
OrthoMat3::new(-width / _2, width / _2, -height / _2, height / _2, znear, zfar)
|
||||
}
|
||||
|
||||
/// Creates a new orthographic matrix from a 4D matrix.
|
||||
///
|
||||
/// This is unsafe because the input matrix is not checked to be a orthographic projection.
|
||||
|
@ -158,42 +211,68 @@ impl<N: BaseFloat> OrthoMat3<N> {
|
|||
&self.mat
|
||||
}
|
||||
|
||||
/// The width of the view cuboid.
|
||||
/// The smallest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn width(&self) -> N {
|
||||
<N as Cast<f64>>::from(2.0) / self.mat.m11
|
||||
pub fn left(&self) -> N {
|
||||
(-::one::<N>() - self.mat.m14) / self.mat.m11
|
||||
}
|
||||
|
||||
/// The height of the view cuboid.
|
||||
/// The largest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn height(&self) -> N {
|
||||
<N as Cast<f64>>::from(2.0) / self.mat.m22
|
||||
pub fn right(&self) -> N {
|
||||
(::one::<N>() - self.mat.m14) / self.mat.m11
|
||||
}
|
||||
|
||||
/// The smallest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn bottom(&self) -> N {
|
||||
(-::one::<N>() - self.mat.m24) / self.mat.m22
|
||||
}
|
||||
|
||||
/// The largest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn top(&self) -> N {
|
||||
(::one::<N>() - self.mat.m24) / self.mat.m22
|
||||
}
|
||||
|
||||
/// The near plane offset of the view cuboid.
|
||||
#[inline]
|
||||
pub fn znear(&self) -> N {
|
||||
(self.mat.m34 + ::one()) / self.mat.m33
|
||||
(::one::<N>() + self.mat.m34) / self.mat.m33
|
||||
}
|
||||
|
||||
/// The far plane offset of the view cuboid.
|
||||
#[inline]
|
||||
pub fn zfar(&self) -> N {
|
||||
(self.mat.m34 - ::one()) / self.mat.m33
|
||||
(-::one::<N>() + self.mat.m34) / self.mat.m33
|
||||
}
|
||||
|
||||
/// Sets the width of the view cuboid.
|
||||
/// Sets the smallest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_width(&mut self, width: N) {
|
||||
assert!(!::is_zero(&width));
|
||||
self.mat.m11 = <N as Cast<f64>>::from(2.0) / width;
|
||||
pub fn set_left(&mut self, left: N) {
|
||||
let right = self.right();
|
||||
self.set_left_and_right(left, right);
|
||||
}
|
||||
|
||||
/// Sets the height of the view cuboid.
|
||||
/// Sets the largest x-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_height(&mut self, height: N) {
|
||||
assert!(!::is_zero(&height));
|
||||
self.mat.m22 = <N as Cast<f64>>::from(2.0) / height;
|
||||
pub fn set_right(&mut self, right: N) {
|
||||
let left = self.left();
|
||||
self.set_left_and_right(left, right);
|
||||
}
|
||||
|
||||
/// Sets the smallest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_bottom(&mut self, bottom: N) {
|
||||
let top = self.top();
|
||||
self.set_bottom_and_top(bottom, top);
|
||||
}
|
||||
|
||||
/// Sets the largest y-coordinate of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_top(&mut self, top: N) {
|
||||
let bottom = self.bottom();
|
||||
self.set_bottom_and_top(bottom, top);
|
||||
}
|
||||
|
||||
/// Sets the near plane offset of the view cuboid.
|
||||
|
@ -210,6 +289,22 @@ impl<N: BaseFloat> OrthoMat3<N> {
|
|||
self.set_znear_and_zfar(znear, zfar);
|
||||
}
|
||||
|
||||
/// Sets the view cuboid coordinates along the `x` axis.
|
||||
#[inline]
|
||||
pub fn set_left_and_right(&mut self, left: N, right: N) {
|
||||
assert!(left < right, "The left corner must be farther than the right corner.");
|
||||
self.mat.m11 = <N as Cast<f64>>::from(2.0) / (right - left);
|
||||
self.mat.m14 = -(right + left) / (right - left);
|
||||
}
|
||||
|
||||
/// Sets the view cuboid coordinates along the `y` axis.
|
||||
#[inline]
|
||||
pub fn set_bottom_and_top(&mut self, bottom: N, top: N) {
|
||||
assert!(bottom < top, "The top corner must be higher than the bottom corner.");
|
||||
self.mat.m22 = <N as Cast<f64>>::from(2.0) / (top - bottom);
|
||||
self.mat.m24 = -(top + bottom) / (top - bottom);
|
||||
}
|
||||
|
||||
/// Sets the near and far plane offsets of the view cuboid.
|
||||
#[inline]
|
||||
pub fn set_znear_and_zfar(&mut self, znear: N, zfar: N) {
|
||||
|
@ -222,8 +317,8 @@ impl<N: BaseFloat> OrthoMat3<N> {
|
|||
#[inline]
|
||||
pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> {
|
||||
Pnt3::new(
|
||||
self.mat.m11 * p.x,
|
||||
self.mat.m22 * p.y,
|
||||
self.mat.m11 * p.x + self.mat.m14,
|
||||
self.mat.m22 * p.y + self.mat.m24,
|
||||
self.mat.m33 * p.z + self.mat.m34
|
||||
)
|
||||
}
|
||||
|
@ -251,7 +346,7 @@ impl<N: BaseFloat + Clone> OrthoMat3<N> {
|
|||
impl<N: Arbitrary + BaseFloat> Arbitrary for OrthoMat3<N> {
|
||||
fn arbitrary<G: Gen>(g: &mut G) -> OrthoMat3<N> {
|
||||
let x: Ortho3<N> = Arbitrary::arbitrary(g);
|
||||
x.to_ortho_mat()
|
||||
x.to_persp_mat()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,18 +7,22 @@ use quickcheck::{Arbitrary, Gen};
|
|||
|
||||
/// A 3D perspective projection stored without any matrix.
|
||||
///
|
||||
/// Reading or modifying its individual properties is cheap but applying the transformation is costly.
|
||||
/// This maps a frustrum cube to the unit cube with corners varying from `(-1, -1, -1)` to
|
||||
/// `(1, 1, 1)`. Reading or modifying its individual properties is cheap but applying the
|
||||
/// transformation is costly.
|
||||
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)]
|
||||
pub struct Persp3<N> {
|
||||
aspect: N,
|
||||
fov: N,
|
||||
fovy: N,
|
||||
znear: N,
|
||||
zfar: N
|
||||
}
|
||||
|
||||
/// A 3D perspective projection stored as a 4D matrix.
|
||||
///
|
||||
/// Reading or modifying its individual properties is costly but applying the transformation is cheap.
|
||||
/// This maps a frustrum to the unit cube with corners varying from `(-1, -1, -1)` to
|
||||
/// `(1, 1, 1)`. Reading or modifying its individual properties is costly but applying the
|
||||
/// transformation is cheap.
|
||||
#[derive(Eq, PartialEq, RustcEncodable, RustcDecodable, Clone, Debug, Copy)]
|
||||
pub struct PerspMat3<N> {
|
||||
mat: Mat4<N>
|
||||
|
@ -26,13 +30,13 @@ pub struct PerspMat3<N> {
|
|||
|
||||
impl<N: BaseFloat> Persp3<N> {
|
||||
/// Creates a new 3D perspective projection.
|
||||
pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> Persp3<N> {
|
||||
pub fn new(aspect: N, fovy: N, znear: N, zfar: N) -> Persp3<N> {
|
||||
assert!(!::is_zero(&(zfar - znear)));
|
||||
assert!(!::is_zero(&aspect));
|
||||
|
||||
Persp3 {
|
||||
aspect: aspect,
|
||||
fov: fov,
|
||||
fovy: fovy,
|
||||
znear: znear,
|
||||
zfar: zfar
|
||||
}
|
||||
|
@ -45,7 +49,7 @@ impl<N: BaseFloat> Persp3<N> {
|
|||
|
||||
/// Build a `PerspMat3` representing this projection.
|
||||
pub fn to_persp_mat(&self) -> PerspMat3<N> {
|
||||
PerspMat3::new(self.aspect, self.fov, self.znear, self.zfar)
|
||||
PerspMat3::new(self.aspect, self.fovy, self.znear, self.zfar)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -66,10 +70,10 @@ impl<N: BaseFloat + Clone> Persp3<N> {
|
|||
self.aspect.clone()
|
||||
}
|
||||
|
||||
/// Gets the field of view of the view frustrum.
|
||||
/// Gets the y field of view of the view frustrum.
|
||||
#[inline]
|
||||
pub fn fov(&self) -> N {
|
||||
self.fov.clone()
|
||||
pub fn fovy(&self) -> N {
|
||||
self.fovy.clone()
|
||||
}
|
||||
|
||||
/// Gets the near plane offset of the view frustrum.
|
||||
|
@ -92,12 +96,12 @@ impl<N: BaseFloat + Clone> Persp3<N> {
|
|||
self.aspect = aspect;
|
||||
}
|
||||
|
||||
/// Sets the field of view of the view frustrum.
|
||||
/// Sets the y field of view of the view frustrum.
|
||||
///
|
||||
/// This method does not build any matrix.
|
||||
#[inline]
|
||||
pub fn set_fov(&mut self, fov: N) {
|
||||
self.fov = fov;
|
||||
pub fn set_fovy(&mut self, fovy: N) {
|
||||
self.fovy = fovy;
|
||||
}
|
||||
|
||||
/// Sets the near plane offset of the view frustrum.
|
||||
|
@ -132,19 +136,19 @@ impl<N: BaseFloat + Clone> Persp3<N> {
|
|||
}
|
||||
|
||||
impl<N: BaseFloat> PerspMat3<N> {
|
||||
/// Creates a new persepctive matrix from the aspect ratio, field of view, and near/far planes.
|
||||
pub fn new(aspect: N, fov: N, znear: N, zfar: N) -> PerspMat3<N> {
|
||||
/// Creates a new perspective matrix from the aspect ratio, y field of view, and near/far planes.
|
||||
pub fn new(aspect: N, fovy: N, znear: N, zfar: N) -> PerspMat3<N> {
|
||||
assert!(!::is_zero(&(znear - zfar)));
|
||||
assert!(!::is_zero(&aspect));
|
||||
|
||||
let mat: Mat4<N> = ::one();
|
||||
|
||||
let mut res = PerspMat3 { mat: mat };
|
||||
res.set_fov(fov);
|
||||
res.set_fovy(fovy);
|
||||
res.set_aspect(aspect);
|
||||
res.set_znear_and_zfar(znear, zfar);
|
||||
res.mat.m44 = ::zero();
|
||||
res.mat.m43 = ::one();
|
||||
res.mat.m43 = -::one::<N>();
|
||||
|
||||
res
|
||||
}
|
||||
|
@ -168,12 +172,12 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
/// Gets the `width / height` aspect ratio of the view frustrum.
|
||||
#[inline]
|
||||
pub fn aspect(&self) -> N {
|
||||
-self.mat.m22 / self.mat.m11
|
||||
self.mat.m22 / self.mat.m11
|
||||
}
|
||||
|
||||
/// Gets the field of view of the view frustrum.
|
||||
/// Gets the y field of view of the view frustrum.
|
||||
#[inline]
|
||||
pub fn fov(&self) -> N {
|
||||
pub fn fovy(&self) -> N {
|
||||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
|
||||
|
@ -185,7 +189,7 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
pub fn znear(&self) -> N {
|
||||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
let ratio = (self.mat.m33 + _1) / (self.mat.m33 - _1);
|
||||
let ratio = (-self.mat.m33 + _1) / (-self.mat.m33 - _1);
|
||||
|
||||
self.mat.m34 / (_2 * ratio) - self.mat.m34 / _2
|
||||
}
|
||||
|
@ -195,29 +199,29 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
pub fn zfar(&self) -> N {
|
||||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
let ratio = (self.mat.m33 + _1) / (self.mat.m33 - _1);
|
||||
let ratio = (-self.mat.m33 + _1) / (-self.mat.m33 - _1);
|
||||
|
||||
(self.mat.m34 - ratio * self.mat.m34) / _2
|
||||
}
|
||||
|
||||
// FIXME: add a method to retriev znear and zfar at once ?
|
||||
// FIXME: add a method to retrieve znear and zfar simultaneously?
|
||||
|
||||
/// Updates this projection matrix with a new `width / height` aspect ratio of the view
|
||||
/// frustrum.
|
||||
#[inline]
|
||||
pub fn set_aspect(&mut self, aspect: N) {
|
||||
assert!(!::is_zero(&aspect));
|
||||
self.mat.m11 = -self.mat.m22 / aspect;
|
||||
self.mat.m11 = self.mat.m22 / aspect;
|
||||
}
|
||||
|
||||
/// Updates this projection with a new field of view of the view frustrum.
|
||||
/// Updates this projection with a new y field of view of the view frustrum.
|
||||
#[inline]
|
||||
pub fn set_fov(&mut self, fov: N) {
|
||||
pub fn set_fovy(&mut self, fovy: N) {
|
||||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
|
||||
let old_m22 = self.mat.m22.clone();
|
||||
self.mat.m22 = _1 / (fov / _2).tan();
|
||||
self.mat.m22 = _1 / (fovy / _2).tan();
|
||||
self.mat.m11 = self.mat.m11 * (self.mat.m22 / old_m22);
|
||||
}
|
||||
|
||||
|
@ -241,7 +245,7 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
let _1: N = ::one();
|
||||
let _2 = _1 + _1;
|
||||
|
||||
self.mat.m33 = -(zfar + znear) / (znear - zfar);
|
||||
self.mat.m33 = (zfar + znear) / (znear - zfar);
|
||||
self.mat.m34 = zfar * znear * _2 / (znear - zfar);
|
||||
}
|
||||
|
||||
|
@ -249,7 +253,7 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
#[inline]
|
||||
pub fn project_pnt(&self, p: &Pnt3<N>) -> Pnt3<N> {
|
||||
let _1: N = ::one();
|
||||
let inv_denom = _1 / p.z;
|
||||
let inv_denom = -_1 / p.z;
|
||||
Pnt3::new(
|
||||
self.mat.m11 * p.x * inv_denom,
|
||||
self.mat.m22 * p.y * inv_denom,
|
||||
|
@ -261,7 +265,7 @@ impl<N: BaseFloat> PerspMat3<N> {
|
|||
#[inline]
|
||||
pub fn project_vec(&self, p: &Vec3<N>) -> Vec3<N> {
|
||||
let _1: N = ::one();
|
||||
let inv_denom = _1 / p.z;
|
||||
let inv_denom = -_1 / p.z;
|
||||
Vec3::new(
|
||||
self.mat.m11 * p.x * inv_denom,
|
||||
self.mat.m22 * p.y * inv_denom,
|
||||
|
|
|
@ -218,19 +218,36 @@ impl<N: Clone + BaseFloat> Rot3<N> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Builds a look-at view matrix with no translational component.
|
||||
|
||||
/// Builds a right-handed look-at view matrix without translation.
|
||||
///
|
||||
/// This conforms to the common notion of "look-at" matrix from the computer graphics community.
|
||||
/// Its maps the view direction `dir` to the **negative** `z` axis.
|
||||
/// This conforms to the common notion of right handed look-at matrix from the computer
|
||||
/// graphics community.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * dir - The view direction.
|
||||
/// * up - The vertical direction. The only requirement of this parameter is to not be
|
||||
/// collinear to `dir`.
|
||||
/// * eye - The eye position.
|
||||
/// * target - The target position.
|
||||
/// * up - A vector approximately aligned with required the vertical axis. The only
|
||||
/// requirement of this parameter is to not be collinear to `target - eye`.
|
||||
#[inline]
|
||||
pub fn new_look_at(dir: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
|
||||
pub fn look_at_rh(dir: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
|
||||
Rot3::new_observer_frame(&(-*dir), up).inv().unwrap()
|
||||
}
|
||||
|
||||
/// Builds a left-handed look-at view matrix without translation.
|
||||
///
|
||||
/// This conforms to the common notion of left handed look-at matrix from the computer
|
||||
/// graphics community.
|
||||
///
|
||||
/// # Arguments
|
||||
/// * eye - The eye position.
|
||||
/// * target - The target position.
|
||||
/// * up - A vector approximately aligned with required the vertical axis. The only
|
||||
/// requirement of this parameter is to not be collinear to `target - eye`.
|
||||
#[inline]
|
||||
pub fn look_at_lh(dir: &Vec3<N>, up: &Vec3<N>) -> Rot3<N> {
|
||||
Rot3::new_observer_frame(&(*dir), up).inv().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: Clone + BaseFloat + Cast<f64>>
|
||||
|
|
|
@ -2,8 +2,8 @@ extern crate nalgebra as na;
|
|||
extern crate rand;
|
||||
|
||||
use rand::random;
|
||||
use na::{Pnt2, Pnt3, Vec2, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3, Iso2,
|
||||
Iso3, Sim2, Sim3, BaseFloat, Transform};
|
||||
use na::{Pnt2, Pnt3, Vec2, Vec3, Vec1, Rot2, Rot3, Persp3, PerspMat3, Ortho3, OrthoMat3,
|
||||
Iso2, Iso3, Sim2, Sim3, BaseFloat, Transform};
|
||||
|
||||
#[test]
|
||||
fn test_rotation2() {
|
||||
|
@ -71,25 +71,27 @@ fn test_rot2_angle_between() {
|
|||
|
||||
|
||||
#[test]
|
||||
fn test_look_at_iso3() {
|
||||
fn test_look_at_rh_iso3() {
|
||||
for _ in 0usize .. 10000 {
|
||||
let eye = random::<Pnt3<f64>>();
|
||||
let target = random::<Pnt3<f64>>();
|
||||
let up = random::<Vec3<f64>>();
|
||||
let viewmat = Iso3::new_look_at(&eye, &target, &up);
|
||||
let viewmat = Iso3::look_at_rh(&eye, &target, &up);
|
||||
|
||||
assert_eq!(&(viewmat * eye), &na::orig());
|
||||
let origin: Pnt3<f64> = na::orig();
|
||||
assert_eq!(&(viewmat * eye), &origin);
|
||||
assert!(na::approx_eq(&na::normalize(&(viewmat * (target - eye))), &-Vec3::z()));
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_look_at_rot3() {
|
||||
fn test_look_at_rh_rot3() {
|
||||
for _ in 0usize .. 10000 {
|
||||
let dir = random::<Vec3<f64>>();
|
||||
let up = random::<Vec3<f64>>();
|
||||
let viewmat = Rot3::new_look_at(&dir, &up);
|
||||
let viewmat = Rot3::look_at_rh(&dir, &up);
|
||||
|
||||
println!("found: {}", viewmat * dir);
|
||||
assert!(na::approx_eq(&na::normalize(&(viewmat * dir)), &-Vec3::z()));
|
||||
}
|
||||
}
|
||||
|
@ -124,16 +126,16 @@ fn test_persp() {
|
|||
let mut pm = PerspMat3::new(42.0f64, 0.5, 1.5, 10.0);
|
||||
assert!(p.to_mat() == pm.to_mat());
|
||||
assert!(p.aspect() == 42.0);
|
||||
assert!(p.fov() == 0.5);
|
||||
assert!(p.fovy() == 0.5);
|
||||
assert!(p.znear() == 1.5);
|
||||
assert!(p.zfar() == 10.0);
|
||||
assert!(na::approx_eq(&pm.aspect(), &42.0));
|
||||
assert!(na::approx_eq(&pm.fov(), &0.5));
|
||||
assert!(na::approx_eq(&pm.fovy(), &0.5));
|
||||
assert!(na::approx_eq(&pm.znear(), &1.5));
|
||||
assert!(na::approx_eq(&pm.zfar(), &10.0));
|
||||
|
||||
p.set_fov(0.1);
|
||||
pm.set_fov(0.1);
|
||||
p.set_fovy(0.1);
|
||||
pm.set_fovy(0.1);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_znear(24.0);
|
||||
|
@ -149,52 +151,60 @@ fn test_persp() {
|
|||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
assert!(p.aspect() == 23.0);
|
||||
assert!(p.fov() == 0.1);
|
||||
assert!(p.fovy() == 0.1);
|
||||
assert!(p.znear() == 24.0);
|
||||
assert!(p.zfar() == 61.0);
|
||||
assert!(na::approx_eq(&pm.aspect(), &23.0));
|
||||
assert!(na::approx_eq(&pm.fov(), &0.1));
|
||||
assert!(na::approx_eq(&pm.fovy(), &0.1));
|
||||
assert!(na::approx_eq(&pm.znear(), &24.0));
|
||||
assert!(na::approx_eq(&pm.zfar(), &61.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ortho() {
|
||||
let mut p = Ortho3::new(42.0f64, 0.5, 1.5, 10.0);
|
||||
let mut pm = OrthoMat3::new(42.0f64, 0.5, 1.5, 10.0);
|
||||
let mut p = Ortho3::new(-0.3, 5.2, -3.9, -1.0, 1.5, 10.0);
|
||||
let mut pm = OrthoMat3::new(-0.3, 5.2, -3.9, -1.0, 1.5, 10.0);
|
||||
assert!(p.to_mat() == pm.to_mat());
|
||||
assert!(p.width() == 42.0);
|
||||
assert!(p.height() == 0.5);
|
||||
assert!(p.left() == -0.3);
|
||||
assert!(p.right() == 5.2);
|
||||
assert!(p.bottom() == -3.9);
|
||||
assert!(p.top() == -1.0);
|
||||
assert!(p.znear() == 1.5);
|
||||
assert!(p.zfar() == 10.0);
|
||||
assert!(na::approx_eq(&pm.width(), &42.0));
|
||||
assert!(na::approx_eq(&pm.height(), &0.5));
|
||||
assert!(na::approx_eq(&pm.left(), &-0.3));
|
||||
assert!(na::approx_eq(&pm.right(), &5.2));
|
||||
assert!(na::approx_eq(&pm.bottom(), &-3.9));
|
||||
assert!(na::approx_eq(&pm.top(), &-1.0));
|
||||
assert!(na::approx_eq(&pm.znear(), &1.5));
|
||||
assert!(na::approx_eq(&pm.zfar(), &10.0));
|
||||
|
||||
p.set_width(0.1);
|
||||
pm.set_width(0.1);
|
||||
p.set_left(0.1);
|
||||
pm.set_left(0.1);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_znear(24.0);
|
||||
pm.set_znear(24.0);
|
||||
p.set_right(10.1);
|
||||
pm.set_right(10.1);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_top(24.0);
|
||||
pm.set_top(24.0);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_bottom(-23.0);
|
||||
pm.set_bottom(-23.0);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_zfar(61.0);
|
||||
pm.set_zfar(61.0);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
p.set_height(23.0);
|
||||
pm.set_height(23.0);
|
||||
p.set_znear(21.0);
|
||||
pm.set_znear(21.0);
|
||||
assert!(na::approx_eq(&p.to_mat(), pm.as_mat()));
|
||||
|
||||
assert!(p.height() == 23.0);
|
||||
assert!(p.width() == 0.1);
|
||||
assert!(p.znear() == 24.0);
|
||||
assert!(p.znear() == 21.0);
|
||||
assert!(p.zfar() == 61.0);
|
||||
assert!(na::approx_eq(&pm.height(), &23.0));
|
||||
assert!(na::approx_eq(&pm.width(), &0.1));
|
||||
assert!(na::approx_eq(&pm.znear(), &24.0));
|
||||
assert!(na::approx_eq(&pm.znear(), &21.0));
|
||||
assert!(na::approx_eq(&pm.zfar(), &61.0));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue