diff --git a/src/Magnum/Math/Matrix4.h b/src/Magnum/Math/Matrix4.h index 8d713cc82..2b2395af4 100644 --- a/src/Magnum/Math/Matrix4.h +++ b/src/Magnum/Math/Matrix4.h @@ -224,6 +224,15 @@ template class Matrix4: public Matrix4x4 { return perspectiveProjection(Vector2(xyScale, xyScale/aspectRatio), near, far); } + /** + * @brief Matrix oriented towards a specific point + * @param eye Location to place the matrix + * @param target Location towards which the matrix is oriented + * @param up Vector as a guide of which way is up (should not be + * the same direction as `target - eye`) + */ + static Matrix4 lookAt(const Vector3& eye, const Vector3& target, const Vector3& up); + /** * @brief Create matrix from rotation/scaling part and translation part * @param rotationScaling Rotation/scaling part (upper-left 3x3 @@ -528,6 +537,17 @@ template Matrix4 Matrix4::perspectiveProjection(const Vector2& { T(0), T(0), T(2)*far*near*zScale, T(0)}}; } +template Matrix4 Matrix4::lookAt(const Vector3& eye, const Vector3& target, const Vector3& up) { + const Vector3 backward = (eye - target).normalized(); + const Vector3 right = Vector3::cross(up, backward).normalized(); + const Vector3 realUp = Vector3::cross(backward, right); + + return {{ right, T(0)}, + { realUp, T(0)}, + {backward, T(0)}, + { eye, T(1)}}; +} + template inline Matrix3x3 Matrix4::rotation() const { CORRADE_ASSERT(TypeTraits::equals((*this)[0].xyz().dot(), (*this)[1].xyz().dot()) && TypeTraits::equals((*this)[1].xyz().dot(), (*this)[2].xyz().dot()), diff --git a/src/Magnum/Math/Test/Matrix4Test.cpp b/src/Magnum/Math/Test/Matrix4Test.cpp index f3b832d55..e8cfcaa55 100644 --- a/src/Magnum/Math/Test/Matrix4Test.cpp +++ b/src/Magnum/Math/Test/Matrix4Test.cpp @@ -85,6 +85,8 @@ struct Matrix4Test: Corrade::TestSuite::Tester { void orthographicProjection(); void perspectiveProjection(); void perspectiveProjectionFov(); + void lookAt(); + void fromParts(); void rotationScalingPart(); void rotationNormalizedPart(); @@ -131,6 +133,8 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::orthographicProjection, &Matrix4Test::perspectiveProjection, &Matrix4Test::perspectiveProjectionFov, + &Matrix4Test::lookAt, + &Matrix4Test::fromParts, &Matrix4Test::rotationScalingPart, &Matrix4Test::rotationNormalizedPart, @@ -507,6 +511,44 @@ void Matrix4Test::transform() { CORRADE_COMPARE(a.transformPoint(v), Vector3(3.0f, -4.0f, 9.0f)); } +void Matrix4Test::lookAt() { + Matrix4 a = Matrix4::lookAt({0.0f, 0.0f, 0.0f}, + {0.0f, 1.0f, 0.0f}, + {0.0f, 0.0f, 1.0f}); + CORRADE_VERIFY(a.isRigidTransformation()); + CORRADE_COMPARE(a, Matrix4({1.0f, 0.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 1.0f, 0.0f}, + {0.0f, -1.0f, 0.0f, 0.0f}, + {0.0f, 0.0f, 0.0f, 1.0f})); + + Matrix4 b = Matrix4::lookAt({100.0f, 200.0f, 300.0f}, + { 0.0f, 0.0f, 0.0f}, + { 0.0f, 1.0f, 0.0f}); + CORRADE_VERIFY(b.isRigidTransformation()); + CORRADE_COMPARE(b, Matrix4({ 0.948683f, 0.0f, -0.316228f, 0.0f}, + {-0.169031f, 0.845154f, -0.507093f, 0.0f}, + { 0.267261f, 0.534522f, 0.801784f, 0.0f}, + { 100.0f, 200.0f, 300.0f, 1.0f})); + + Matrix4 c = Matrix4::lookAt({3.0f, 0.0f, 0.0f}, + {0.0f, 4.0f, 5.0f}, + {0.0f, 0.0f, 1.0f}); + CORRADE_VERIFY(c.isRigidTransformation()); + CORRADE_COMPARE(c, Matrix4({ 0.8f, 0.6f, 0.0f, 0.0f}, + {0.424264f, -0.565685f, 0.707107f, 0.0f}, + {0.424264f, -0.565685f, -0.707107f, 0.0f}, + { 3.0f, 0.0f, 0.0f, 1.0f})); + + Matrix4 d = Matrix4::lookAt({ 0.0f, 3.0f, 0.0f}, + {-5.0f, 0.0f, -4.0f}, + { 0.0f, 1.0f, 0.0f}); + CORRADE_VERIFY(d.isRigidTransformation()); + CORRADE_COMPARE(d, Matrix4({ 0.624695f, 0.0f, -0.780869f, 0.0f}, + {-0.331295f, 0.905539f, -0.265036f, 0.0f}, + { 0.707107f, 0.424264f, 0.565685f, 0.0f}, + { 0.0f, 3.0f, 0.0f, 1.0f})); +} + void Matrix4Test::debug() { Matrix4 m({3.0f, 5.0f, 8.0f, 4.0f}, {4.0f, 4.0f, 7.0f, 3.0f},