diff --git a/src/matrix.rs b/src/matrix.rs index ea0e9de7..62335820 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -101,14 +101,29 @@ impl Matrix2 { impl Matrix2 { /// Create a transformation matrix that will cause `unit_x()` to point at /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`. + #[deprecated = "Use Matrix2::look_to"] pub fn look_at(dir: Vector2, up: Vector2) -> Matrix2 { - Matrix2::look_at_stable(dir, up.x * dir.y >= up.y * dir.x) + Matrix2::look_to(dir, up) } /// Crate a transformation that will cause `unit_x()` to point at - /// `dir`. This is similar to `look_at`, but does not take an `up` vector. + /// `dir`. This is similar to `look_to`, but does not take an `up` vector. /// This will not cause `unit_y()` to flip when `dir` crosses over the `up` vector. + #[deprecated = "Use Matrix2::look_to_stable"] pub fn look_at_stable(dir: Vector2, flip: bool) -> Matrix2 { + Matrix2::look_to_stable(dir, flip) + } + + /// Create a transformation matrix that will cause `unit_x()` to point at + /// `dir`. `unit_y()` will be perpendicular to `dir`, and the closest to `up`. + pub fn look_to(dir: Vector2, up: Vector2) -> Matrix2 { + Matrix2::look_to_stable(dir, up.x * dir.y >= up.y * dir.x) + } + + /// Crate a transformation that will cause `unit_x()` to point at + /// `dir`. This is similar to `look_to`, but does not take an `up` vector. + /// This will not cause `unit_y()` to flip when `dir` crosses over the `up` vector. + pub fn look_to_stable(dir: Vector2, flip: bool) -> Matrix2 { let basis1 = dir.normalize(); let basis2 = if flip { Vector2::new(basis1.y, -basis1.x) @@ -189,7 +204,14 @@ impl Matrix3 { /// Create a rotation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. + #[deprecated = "Use Matrix3::look_to_lh or Matrix3::look_to_rh for the right handed variation"] pub fn look_at(dir: Vector3, up: Vector3) -> Matrix3 { + Matrix3::look_to_lh(dir, up) + } + + /// Create a rotation matrix that will cause a vector to point at + /// `dir`, using `up` for orientation. + pub fn look_to_lh(dir: Vector3, up: Vector3) -> Matrix3 { let dir = dir.normalize(); let side = up.cross(dir).normalize(); let up = dir.cross(side).normalize(); @@ -197,6 +219,12 @@ impl Matrix3 { 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_to_rh(dir: Vector3, up: Vector3) -> Matrix3 { + Matrix3::look_to_lh(-dir, up) + } + /// Create a rotation matrix from a rotation around the `x` axis (pitch). pub fn from_angle_x>>(theta: A) -> Matrix3 { // http://en.wikipedia.org/wiki/Rotation_matrix#Basic_rotations @@ -333,7 +361,16 @@ impl Matrix4 { /// Create a homogeneous transformation matrix that will cause a vector to point at /// `dir`, using `up` for orientation. + #[deprecated = "Use Matrix4::look_to_rh or Matrix3::look_to_lh for the left handed variation"] pub fn look_at_dir(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { + Self::look_to_rh(eye, dir, up) + } + + /// Creates a homogeneous right-handed look-at transformation matrix. + /// + /// This matrix will transform a vector to point in `dir` direction, using `up` for orientation + /// assuming a right-handed corrdinate system. + pub fn look_to_rh(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { let f = dir.normalize(); let s = f.cross(up).normalize(); let u = s.cross(f); @@ -347,10 +384,35 @@ impl Matrix4 { ) } + /// Creates a homogeneous left-handed look-at transformation matrix. + /// + /// This matrix will transform a vector to point in `dir` direction, using `up` for orientation + /// assuming a left-handed corrdinate system. + pub fn look_to_lh(eye: Point3, dir: Vector3, up: Vector3) -> Matrix4 { + Matrix4::look_to_rh(eye, -dir, up) + } + /// Create a homogeneous transformation matrix that will cause a vector to point at /// `center`, using `up` for orientation. + #[deprecated = "Use Matrix4::look_at_rh or look_at_lh for the left handed variation"] pub fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { - Matrix4::look_at_dir(eye, center - eye, up) + Matrix4::look_to_rh(eye, center - eye, up) + } + + /// Creates a homogeneous right-handed look-at transformation matrix. + /// + /// This matrix will transform a vector to point at `center`, using `up` for orientation + /// assuming a right-handed corrdinate system. + pub fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { + Matrix4::look_to_rh(eye, center - eye, up) + } + + /// Creates a homogeneous left-handed look-at transformation matrix. + /// + /// This matrix will transform a vector to point at `center`, using `up` for orientation + /// assuming a left-handed corrdinate system. + pub fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { + Matrix4::look_to_lh(eye, center - eye, up) } /// Create a homogeneous transformation matrix from a rotation around the `x` axis (pitch). @@ -1040,7 +1102,17 @@ impl approx::UlpsEq for Matrix4 { impl Transform> for Matrix3 { fn look_at(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { let dir = center - eye; - Matrix3::from(Matrix2::look_at(dir, up)) + Matrix3::from(Matrix2::look_to(dir, up)) + } + + fn look_at_lh(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { + let dir = center - eye; + Matrix3::from(Matrix2::look_to(dir, up)) + } + + fn look_at_rh(eye: Point2, center: Point2, up: Vector2) -> Matrix3 { + let dir = eye - center; + Matrix3::from(Matrix2::look_to(dir, up)) } fn transform_vector(&self, vec: Vector2) -> Vector2 { @@ -1063,7 +1135,17 @@ impl Transform> for Matrix3 { impl Transform> for Matrix3 { fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { let dir = center - eye; - Matrix3::look_at(dir, up) + Matrix3::look_to_lh(dir, up) + } + + fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { + let dir = center - eye; + Matrix3::look_to_lh(dir, up) + } + + fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix3 { + let dir = center - eye; + Matrix3::look_to_rh(dir, up) } fn transform_vector(&self, vec: Vector3) -> Vector3 { @@ -1084,10 +1166,19 @@ impl Transform> for Matrix3 { } impl Transform> for Matrix4 { + #[allow(deprecated)] fn look_at(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { Matrix4::look_at(eye, center, up) } + fn look_at_lh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { + Matrix4::look_at_lh(eye, center, up) + } + + fn look_at_rh(eye: Point3, center: Point3, up: Vector3) -> Matrix4 { + Matrix4::look_at_rh(eye, center, up) + } + fn transform_vector(&self, vec: Vector3) -> Vector3 { (self * vec.extend(S::zero())).truncate() } diff --git a/src/quaternion.rs b/src/quaternion.rs index 6ef034bd..474a8f62 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -484,8 +484,8 @@ impl Rotation for Quaternion { type Space = Point3; #[inline] - fn look_at(dir: Vector3, up: Vector3) -> Quaternion { - Matrix3::look_at(dir, up).into() + fn look_to(dir: Vector3, up: Vector3) -> Quaternion { + Matrix3::look_to_lh(dir, up).into() } #[inline] diff --git a/src/rotation.rs b/src/rotation.rs index e7fa1f6f..27f5b7b2 100644 --- a/src/rotation.rs +++ b/src/rotation.rs @@ -42,9 +42,18 @@ where type Space: EuclideanSpace; /// Create a rotation to a given direction with an 'up' vector. + #[deprecated = "Use Rotation::look_to"] fn look_at( dir: ::Diff, up: ::Diff, + ) -> Self { + Self::look_to(dir, up) + } + + /// Create a rotation to a given direction with an 'up' vector. + fn look_to( + dir: ::Diff, + up: ::Diff, ) -> Self; /// Create a shortest rotation to transform vector 'a' into 'b'. @@ -169,9 +178,9 @@ pub struct Basis2 { } impl Basis2 { - pub fn look_at_stable(dir: Vector2, flip: bool) -> Basis2 { + pub fn look_to_stable(dir: Vector2, flip: bool) -> Basis2 { Basis2 { - mat: Matrix2::look_at_stable(dir, flip), + mat: Matrix2::look_to_stable(dir, flip), } } } @@ -208,9 +217,9 @@ impl Rotation for Basis2 { type Space = Point2; #[inline] - fn look_at(dir: Vector2, up: Vector2) -> Basis2 { + fn look_to(dir: Vector2, up: Vector2) -> Basis2 { Basis2 { - mat: Matrix2::look_at(dir, up), + mat: Matrix2::look_to(dir, up), } } @@ -362,10 +371,11 @@ impl<'a, S: 'a + BaseFloat> iter::Product<&'a Basis3> for Basis3 { impl Rotation for Basis3 { type Space = Point3; + /// Construct a look-at view matrix assuming a left-handed coordinate system. #[inline] - fn look_at(dir: Vector3, up: Vector3) -> Basis3 { + fn look_to(dir: Vector3, up: Vector3) -> Basis3 { Basis3 { - mat: Matrix3::look_at(dir, up), + mat: Matrix3::look_to_lh(dir, up), } } diff --git a/src/transform.rs b/src/transform.rs index 0b3453cb..0fae55a9 100644 --- a/src/transform.rs +++ b/src/transform.rs @@ -30,7 +30,22 @@ use std::ops::Mul; pub trait Transform: Sized + One { /// Create a transformation that rotates a vector to look at `center` from /// `eye`, using `up` for orientation. - fn look_at(eye: P, center: P, up: P::Diff) -> Self; + #[deprecated = "Use look_at_rh or look_at_lh"] + fn look_at(eye: P, center: P, up: P::Diff) -> Self { + Self::look_at_lh(eye, center, up) + } + + /// Creates a right-handed look-at view transform. + /// + /// This transform rotates a vector to look at `center` from `eye`, using `up` for orientation + /// assuming a right-handed coordinate system. + fn look_at_rh(eye: P, center: P, up: P::Diff) -> Self; + + /// Creates a right-handed look-at view transform. + /// + /// This transform rotates a vector to look at `center` from `eye`, using `up` for orientation + /// assuming a left-handed coordinate system. + fn look_at_lh(eye: P, center: P, up: P::Diff) -> Self; /// Transform a vector using this transform. fn transform_vector(&self, vec: P::Diff) -> P::Diff; @@ -104,7 +119,29 @@ where { #[inline] fn look_at(eye: P, center: P, up: P::Diff) -> Decomposed { - let rot = R::look_at(center - eye, up); + let rot = R::look_to(center - eye, up); + let disp = rot.rotate_vector(P::origin() - eye); + Decomposed { + scale: P::Scalar::one(), + rot: rot, + disp: disp, + } + } + + #[inline] + fn look_at_lh(eye: P, center: P, up: P::Diff) -> Decomposed { + let rot = R::look_to(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 { + let rot = R::look_to(eye - center, up); let disp = rot.rotate_vector(P::origin() - eye); Decomposed { scale: P::Scalar::one(), diff --git a/tests/matrix.rs b/tests/matrix.rs index 734c3bbd..fe79de21 100644 --- a/tests/matrix.rs +++ b/tests/matrix.rs @@ -190,18 +190,18 @@ pub mod matrix2 { } #[test] - fn test_look_at() { + fn test_look_to() { // rot should rotate unit_x() to look at the input vector - let rot = Matrix2::look_at(V, Vector2::unit_y()); + let rot = Matrix2::look_to(V, Vector2::unit_y()); assert_eq!(rot * Vector2::unit_x(), V.normalize()); let new_up = Vector2::new(-V.y, V.x).normalize(); assert_eq!(rot * Vector2::unit_y(), new_up); - let rot_down = Matrix2::look_at(V, -1.0 * Vector2::unit_y()); + let rot_down = Matrix2::look_to(V, -1.0 * Vector2::unit_y()); assert_eq!(rot_down * Vector2::unit_x(), V.normalize()); assert_eq!(rot_down * Vector2::unit_y(), -1.0 * new_up); - let rot2 = Matrix2::look_at(-V, Vector2::unit_y()); + let rot2 = Matrix2::look_to(-V, Vector2::unit_y()); assert_eq!(rot2 * Vector2::unit_x(), (-V).normalize()); } } @@ -726,6 +726,37 @@ pub mod matrix3 { ); } } + + #[test] + fn test_look_to_lh() { + let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); + let up = Vector3::unit_y(); + let m = Matrix3::look_to_lh(dir, up); + + assert_ulps_eq!(m, Matrix3::from([ + [0.9486833, -0.16903085, 0.26726127], + [0.0, 0.8451542, 0.53452253], + [-0.31622776, -0.50709254, 0.8017838_f32] + ])); + + #[allow(deprecated)] + { + assert_ulps_eq!(m, Matrix3::look_at(dir, up)); + } + } + + #[test] + fn test_look_to_rh() { + let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); + let up = Vector3::unit_y(); + let m = Matrix3::look_to_rh(dir, up); + + assert_ulps_eq!(m, Matrix3::from([ + [-0.9486833, -0.16903085, -0.26726127], + [0.0, 0.8451542, -0.53452253], + [0.31622776, -0.50709254, -0.8017838_f32] + ])); + } } pub mod matrix4 { @@ -1127,6 +1158,50 @@ pub mod matrix4 { ); } + #[test] + fn test_look_to_rh() { + let eye = Point3::new(10.0, 15.0, 20.0); + let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); + let up = Vector3::unit_y(); + + let m = Matrix4::look_to_rh(eye, dir, up); + #[allow(deprecated)] + { + assert_ulps_eq!(m, Matrix4::look_at_dir(eye, dir, up)); + } + + let expected = Matrix4::from([ + [-0.9486833, -0.16903086, -0.26726127, 0.0], + [0.0, 0.84515435, -0.53452253, 0.0], + [0.31622776, -0.5070926, -0.8017838, 0.0], + [3.1622782, -0.84515476, 26.726126, 1.0_f32] + ]); + assert_ulps_eq!(expected, m); + + let m = Matrix4::look_at_rh(eye, eye + dir, up); + assert_abs_diff_eq!(expected, m, epsilon = 1.0e-4); + } + + #[test] + fn test_look_to_lh() { + let eye = Point3::new(10.0, 15.0, 20.0); + let dir = Vector3::new(1.0, 2.0, 3.0).normalize(); + let up = Vector3::unit_y(); + + let m = Matrix4::look_to_lh(eye, dir, up); + + let expected = Matrix4::from([ + [0.9486833, -0.16903086, 0.26726127, 0.0], + [0.0, 0.84515435, 0.53452253, 0.0], + [-0.31622776, -0.5070926, 0.8017838, 0.0], + [-3.1622782, -0.84515476, -26.726126, 1.0_f32] + ]); + assert_ulps_eq!(expected, m); + + let m = Matrix4::look_at_lh(eye, eye + dir, up); + assert_abs_diff_eq!(expected, m, epsilon = 1.0e-4); + } + mod from { use cgmath::*; diff --git a/tests/transform.rs b/tests/transform.rs index 1dcc357d..926ff832 100644 --- a/tests/transform.rs +++ b/tests/transform.rs @@ -88,16 +88,58 @@ fn test_inverse_vector() { } #[test] +#[allow(deprecated)] fn test_look_at() { 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, Quaternion> = Transform::look_at(eye, center, up); + assert_ulps_eq!(t, Decomposed::, Quaternion>::look_at(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); } +#[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, Quaternion> = Transform::look_at_lh(eye, center, up); + assert_ulps_eq!(t, Decomposed::, Quaternion>::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 = Transform::look_at_lh(eye, center, up); + assert_ulps_eq!(t, Matrix4::::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, Quaternion> = 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, Quaternion> = Transform::look_at_rh(eye, center, up); + assert_ulps_eq!(t, Decomposed::, Quaternion>::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 = Transform::look_at_rh(eye, center, up); + assert_ulps_eq!(t, Matrix4::::look_at_rh(eye, center, up)); + assert_ulps_eq!(&t.transform_point(point), &view_point); +} + #[cfg(feature = "serde")] #[test] fn test_serialize() {