Add Transform::{look_at_rh, look_at_lh} and deprecate look_at

Corresponding functions have been added to Matrix4 and Decomposed
and are now consistent.

Matrix3, Matrix2, and the Rotation trait are only partially updated.
This commit is contained in:
Aaron Loucks 2020-06-08 18:40:50 -04:00 committed by Dzmitry Malyshau
parent 164808eebc
commit 213fc580a9
3 changed files with 154 additions and 0 deletions

View file

@ -189,6 +189,7 @@ impl<S: BaseFloat> Matrix3<S> {
/// Create a rotation matrix that will cause a vector to point at /// Create a rotation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation. /// `dir`, using `up` for orientation.
#[deprecated = "Use Matrix3::look_at_lh"]
pub fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Matrix3<S> { pub fn look_at(dir: Vector3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = dir.normalize(); let dir = dir.normalize();
let side = up.cross(dir).normalize(); let side = up.cross(dir).normalize();
@ -197,6 +198,26 @@ impl<S: BaseFloat> Matrix3<S> {
Matrix3::from_cols(side, up, dir).transpose() Matrix3::from_cols(side, up, dir).transpose()
} }
/// Create a rotation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation.
pub fn look_at_lh(dir: Vector3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = dir.normalize();
let side = up.cross(dir).normalize();
let up = dir.cross(side).normalize();
Matrix3::from_cols(side, up, dir).transpose()
}
/// Create a rotation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation.
pub fn look_at_rh(dir: Vector3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = -dir.normalize();
let side = up.cross(dir).normalize();
let up = dir.cross(side).normalize();
Matrix3::from_cols(side, up, dir).transpose()
}
/// Create a rotation matrix from a rotation around the `x` axis (pitch). /// Create a rotation matrix from a rotation around the `x` axis (pitch).
pub fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Matrix3<S> { pub fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Matrix3<S> {
// http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
@ -333,6 +354,7 @@ impl<S: BaseFloat> Matrix4<S> {
/// Create a homogeneous transformation matrix that will cause a vector to point at /// Create a homogeneous transformation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation. /// `dir`, using `up` for orientation.
#[deprecated = "Use Matrix4::look_to_rh"]
pub fn look_at_dir(eye: Point3<S>, dir: Vector3<S>, up: Vector3<S>) -> Matrix4<S> { pub fn look_at_dir(eye: Point3<S>, dir: Vector3<S>, up: Vector3<S>) -> Matrix4<S> {
let f = dir.normalize(); let f = dir.normalize();
let s = f.cross(up).normalize(); let s = f.cross(up).normalize();
@ -347,12 +369,47 @@ impl<S: BaseFloat> Matrix4<S> {
) )
} }
/// Create a homogeneous transformation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation.
pub fn look_to_rh(eye: Point3<S>, dir: Vector3<S>, up: Vector3<S>) -> Matrix4<S> {
let f = dir.normalize();
let s = f.cross(up).normalize();
let u = s.cross(f);
#[cfg_attr(rustfmt, rustfmt_skip)]
Matrix4::new(
s.x.clone(), u.x.clone(), -f.x.clone(), S::zero(),
s.y.clone(), u.y.clone(), -f.y.clone(), S::zero(),
s.z.clone(), u.z.clone(), -f.z.clone(), S::zero(),
-eye.dot(s), -eye.dot(u), eye.dot(f), S::one(),
)
}
/// Create a homogeneous transformation matrix that will cause a vector to point at
/// `dir`, using `up` for orientation.
pub fn look_to_lh(eye: Point3<S>, dir: Vector3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_to_rh(eye, -dir, up)
}
/// Create a homogeneous transformation matrix that will cause a vector to point at /// Create a homogeneous transformation matrix that will cause a vector to point at
/// `center`, using `up` for orientation. /// `center`, using `up` for orientation.
#[deprecated = "Use Matrix4::look_at_rh"]
pub fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> { pub fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_at_dir(eye, center - eye, up) Matrix4::look_at_dir(eye, center - eye, up)
} }
/// Create a homogeneous transformation matrix that will cause a vector to point at
/// `center`, using `up` for orientation.
pub fn look_at_rh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_to_rh(eye, center - eye, up)
}
/// Create a homogeneous transformation matrix that will cause a vector to point at
/// `center`, using `up` for orientation.
pub fn look_at_lh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_to_lh(eye, center - eye, up)
}
/// Create a homogeneous transformation matrix from a rotation around the `x` axis (pitch). /// Create a homogeneous transformation matrix from a rotation around the `x` axis (pitch).
pub fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Matrix4<S> { pub fn from_angle_x<A: Into<Rad<S>>>(theta: A) -> Matrix4<S> {
// http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations
@ -1043,6 +1100,16 @@ impl<S: BaseFloat> Transform<Point2<S>> for Matrix3<S> {
Matrix3::from(Matrix2::look_at(dir, up)) Matrix3::from(Matrix2::look_at(dir, up))
} }
fn look_at_lh(eye: Point2<S>, center: Point2<S>, up: Vector2<S>) -> Matrix3<S> {
let dir = center - eye;
Matrix3::from(Matrix2::look_at(dir, up))
}
fn look_at_rh(eye: Point2<S>, center: Point2<S>, up: Vector2<S>) -> Matrix3<S> {
let dir = eye - center;
Matrix3::from(Matrix2::look_at(dir, up))
}
fn transform_vector(&self, vec: Vector2<S>) -> Vector2<S> { fn transform_vector(&self, vec: Vector2<S>) -> Vector2<S> {
(self * vec.extend(S::zero())).truncate() (self * vec.extend(S::zero())).truncate()
} }
@ -1066,6 +1133,16 @@ impl<S: BaseFloat> Transform<Point3<S>> for Matrix3<S> {
Matrix3::look_at(dir, up) Matrix3::look_at(dir, up)
} }
fn look_at_lh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = center - eye;
Matrix3::look_at_lh(dir, up)
}
fn look_at_rh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix3<S> {
let dir = center - eye;
Matrix3::look_at_rh(dir, up)
}
fn transform_vector(&self, vec: Vector3<S>) -> Vector3<S> { fn transform_vector(&self, vec: Vector3<S>) -> Vector3<S> {
self * vec self * vec
} }
@ -1084,10 +1161,19 @@ impl<S: BaseFloat> Transform<Point3<S>> for Matrix3<S> {
} }
impl<S: BaseFloat> Transform<Point3<S>> for Matrix4<S> { impl<S: BaseFloat> Transform<Point3<S>> for Matrix4<S> {
fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> { fn look_at(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_at(eye, center, up) Matrix4::look_at(eye, center, up)
} }
fn look_at_lh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_at_lh(eye, center, up)
}
fn look_at_rh(eye: Point3<S>, center: Point3<S>, up: Vector3<S>) -> Matrix4<S> {
Matrix4::look_at_rh(eye, center, up)
}
fn transform_vector(&self, vec: Vector3<S>) -> Vector3<S> { fn transform_vector(&self, vec: Vector3<S>) -> Vector3<S> {
(self * vec.extend(S::zero())).truncate() (self * vec.extend(S::zero())).truncate()
} }

View file

@ -30,8 +30,17 @@ use std::ops::Mul;
pub trait Transform<P: EuclideanSpace>: Sized + One { pub trait Transform<P: EuclideanSpace>: Sized + One {
/// Create a transformation that rotates a vector to look at `center` from /// Create a transformation that rotates a vector to look at `center` from
/// `eye`, using `up` for orientation. /// `eye`, using `up` for orientation.
#[deprecated = "Use look_at_rh or look_at_lh"]
fn look_at(eye: P, center: P, up: P::Diff) -> Self; fn look_at(eye: P, center: P, up: P::Diff) -> Self;
/// Create a transformation that rotates a vector to look at `center` from
/// `eye`, using `up` for orientation.
fn look_at_rh(eye: P, center: P, up: P::Diff) -> Self;
/// Create a transformation that rotates a vector to look at `center` from
/// `eye`, using `up` for orientation.
fn look_at_lh(eye: P, center: P, up: P::Diff) -> Self;
/// Transform a vector using this transform. /// Transform a vector using this transform.
fn transform_vector(&self, vec: P::Diff) -> P::Diff; fn transform_vector(&self, vec: P::Diff) -> P::Diff;
@ -113,6 +122,28 @@ where
} }
} }
#[inline]
fn look_at_lh(eye: P, center: P, up: P::Diff) -> Decomposed<P::Diff, R> {
let rot = R::look_at(center - eye, up);
let disp = rot.rotate_vector(P::origin() - eye);
Decomposed {
scale: P::Scalar::one(),
rot: rot,
disp: disp,
}
}
#[inline]
fn look_at_rh(eye: P, center: P, up: P::Diff) -> Decomposed<P::Diff, R> {
let rot = R::look_at(eye - center, up);
let disp = rot.rotate_vector(P::origin() - eye);
Decomposed {
scale: P::Scalar::one(),
rot: rot,
disp: disp,
}
}
#[inline] #[inline]
fn transform_vector(&self, vec: P::Diff) -> P::Diff { fn transform_vector(&self, vec: P::Diff) -> P::Diff {
self.rot.rotate_vector(vec * self.scale) self.rot.rotate_vector(vec * self.scale)

View file

@ -88,6 +88,7 @@ fn test_inverse_vector() {
} }
#[test] #[test]
#[allow(deprecated)]
fn test_look_at() { fn test_look_at() {
let eye = Point3::new(0.0f64, 0.0, -5.0); let eye = Point3::new(0.0f64, 0.0, -5.0);
let center = Point3::new(0.0f64, 0.0, 0.0); let center = Point3::new(0.0f64, 0.0, 0.0);
@ -98,6 +99,42 @@ fn test_look_at() {
assert_ulps_eq!(&t.transform_point(point), &view_point); assert_ulps_eq!(&t.transform_point(point), &view_point);
} }
#[test]
fn test_look_at_lh() {
let eye = Point3::new(0.0f64, 0.0, -5.0);
let center = Point3::new(0.0f64, 0.0, 0.0);
let up = Vector3::new(1.0f64, 0.0, 0.0);
let t: Decomposed<Vector3<f64>, Quaternion<f64>> = Transform::look_at_lh(eye, center, up);
let point = Point3::new(1.0f64, 0.0, 0.0);
let view_point = Point3::new(0.0f64, 1.0, 5.0);
assert_ulps_eq!(&t.transform_point(point), &view_point);
// Decomposed::look_at_lh and Matrix4::look_at_lh should be consistent
let t: Matrix4<f64> = Transform::look_at_lh(eye, center, up);
assert_ulps_eq!(&t.transform_point(point), &view_point);
// Decomposed::look_at is inconsistent and deprecated, but verify that the behvaior
// remains the same until removed.
#[allow(deprecated)]
let t: Decomposed<Vector3<f64>, Quaternion<f64>> = Transform::look_at(eye, center, up);
assert_ulps_eq!(&t.transform_point(point), &view_point);
}
#[test]
fn test_look_at_rh() {
let eye = Point3::new(0.0f64, 0.0, -5.0);
let center = Point3::new(0.0f64, 0.0, 0.0);
let up = Vector3::new(1.0f64, 0.0, 0.0);
let t: Decomposed<Vector3<f64>, Quaternion<f64>> = Transform::look_at_rh(eye, center, up);
let point = Point3::new(1.0f64, 0.0, 0.0);
let view_point = Point3::new(0.0f64, 1.0, -5.0);
assert_ulps_eq!(&t.transform_point(point), &view_point);
// Decomposed::look_at_rh and Matrix4::look_at_rh should be consistent
let t: Matrix4<f64> = Transform::look_at_rh(eye, center, up);
assert_ulps_eq!(&t.transform_point(point), &view_point);
}
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
#[test] #[test]
fn test_serialize() { fn test_serialize() {