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:
Sébastien Crozet 2016-03-31 21:22:02 +02:00
parent 91e14670ed
commit 4c58e37910
5 changed files with 279 additions and 133 deletions

View File

@ -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)
}
}

View File

@ -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()
}
}

View File

@ -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,

View File

@ -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>>

View File

@ -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));
}