From 7d0a5236187ad40d4edb7c5a18435aad819018c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sat, 12 Jan 2013 22:45:20 +0100 Subject: [PATCH] Moved projection matrix computation to Math namespace. --- src/Math/Matrix3.h | 12 ++++++- src/Math/Matrix4.h | 57 +++++++++++++++++++++++++++++- src/Math/Test/Matrix3Test.cpp | 10 ++++++ src/Math/Test/Matrix4Test.cpp | 32 +++++++++++++++++ src/SceneGraph/Camera2D.h | 3 +- src/SceneGraph/Camera2D.hpp | 3 +- src/SceneGraph/Camera3D.h | 37 +++++++++++-------- src/SceneGraph/Camera3D.hpp | 32 +++++++---------- src/SceneGraph/Test/CameraTest.cpp | 55 +++++++--------------------- 9 files changed, 158 insertions(+), 83 deletions(-) diff --git a/src/Math/Matrix3.h b/src/Math/Matrix3.h index 0e55d5169..2fa99114e 100644 --- a/src/Math/Matrix3.h +++ b/src/Math/Matrix3.h @@ -25,7 +25,7 @@ namespace Magnum { namespace Math { /** -@brief 3x3 matrix for affine transformations in 2D +@brief 3x3 matrix for transformations in 2D @tparam T Data type Provides functions for transformations in 2D. See Matrix4 for 3D @@ -96,6 +96,16 @@ template class Matrix3: public Matrix<3, T> { return from(Matrix<2, T>() - T(2)*normal*normal.transposed(), {}); } + /** + * @brief 2D projection matrix + * @param size Size of the view + * + * @see Matrix4::orthographicProjection(), Matrix4::perspectiveProjection() + */ + static Matrix3 projection(const Vector2& size) { + return scaling(2.0f/size); + } + /** * @brief Create matrix from rotation/scaling part and translation part * @param rotationScaling Rotation/scaling part (upper-left 2x2 diff --git a/src/Math/Matrix4.h b/src/Math/Matrix4.h index ce34891e0..89bd78224 100644 --- a/src/Math/Matrix4.h +++ b/src/Math/Matrix4.h @@ -25,7 +25,7 @@ namespace Magnum { namespace Math { /** -@brief 4x4 matrix for affine transformations in 3D +@brief 4x4 matrix for transformations in 3D @tparam T Data type Provides functions for transformations in 3D. See Matrix3 for 2D @@ -182,6 +182,61 @@ template class Matrix4: public Matrix<4, T> { return from(Matrix<3, T>() - T(2)*normal*normal.transposed(), {}); } + /** + * @brief 3D orthographic projection matrix + * @param size Size of the view + * @param near Near clipping plane + * @param far Far clipping plane + * + * @see perspectiveProjection(), Matrix3::projection() + */ + static Matrix4 orthographicProjection(const Vector2& size, T near, T far) { + Vector2 xyScale = T(2.0)/size; + T zScale = T(2.0)/(near-far); + + return Matrix4( /* Column-major! */ + xyScale.x(), T(0.0), T(0.0), T(0.0), + T(0.0), xyScale.y(), T(0.0), T(0.0), + T(0.0), T(0.0), zScale, T(0.0), + T(0.0), T(0.0), near*zScale-1, T(1.0) + ); + } + + /** + * @brief 3D perspective projection matrix + * @param size Size of near clipping plane + * @param near Near clipping plane + * @param far Far clipping plane + * + * @see orthographicProjection(), Matrix3::projection() + */ + static Matrix4 perspectiveProjection(const Vector2& size, T near, T far) { + Vector2 xyScale = 2*near/size; + T zScale = T(1.0)/(near-far); + + return Matrix4( /* Column-major! */ + xyScale.x(), T(0.0), T(0.0), T(0.0), + T(0.0), xyScale.y(), T(0.0), T(0.0), + T(0.0), T(0.0), (far+near)*zScale, T(-1.0), + T(0.0), T(0.0), (2*far*near)*zScale, T(0.0) + ); + } + + /** + * @brief 3D perspective projection matrix + * @param fov Field of view angle (horizontal, in radians) + * @param aspectRatio Aspect ratio + * @param near Near clipping plane + * @param far Far clipping plane + * + * @see orthographicProjection(), Matrix3::projection() + */ + static Matrix4 perspectiveProjection(T fov, T aspectRatio, T near, T far) { + T xyScale = 2*std::tan(fov/2)*near; + + return perspectiveProjection(Vector2(xyScale, xyScale/aspectRatio), near, far); + } + /** * @brief Create matrix from rotation/scaling part and translation part * @param rotationScaling Rotation/scaling part (upper-left 3x3 diff --git a/src/Math/Test/Matrix3Test.cpp b/src/Math/Test/Matrix3Test.cpp index bc57f5e51..0fd87ca8b 100644 --- a/src/Math/Test/Matrix3Test.cpp +++ b/src/Math/Test/Matrix3Test.cpp @@ -34,6 +34,7 @@ class Matrix3Test: public Corrade::TestSuite::Tester { void scaling(); void rotation(); void reflection(); + void projection(); void fromParts(); void rotationScalingPart(); void rotationPart(); @@ -54,6 +55,7 @@ Matrix3Test::Matrix3Test() { &Matrix3Test::scaling, &Matrix3Test::rotation, &Matrix3Test::reflection, + &Matrix3Test::projection, &Matrix3Test::fromParts, &Matrix3Test::rotationScalingPart, &Matrix3Test::rotationPart, @@ -136,6 +138,14 @@ void Matrix3Test::reflection() { CORRADE_COMPARE(actual, expected); } +void Matrix3Test::projection() { + Matrix3 expected(2.0f/4.0f, 0.0f, 0.0f, + 0.0f, 2.0f/3.0f, 0.0f, + 0.0f, 0.0f, 1.0f); + + CORRADE_COMPARE(Matrix3::projection({4.0f, 3.0f}), expected); +} + void Matrix3Test::fromParts() { Matrix2 rotationScaling( 3.0f, 5.0f, diff --git a/src/Math/Test/Matrix4Test.cpp b/src/Math/Test/Matrix4Test.cpp index 7787eb49b..9d7fe88a3 100644 --- a/src/Math/Test/Matrix4Test.cpp +++ b/src/Math/Test/Matrix4Test.cpp @@ -37,6 +37,9 @@ class Matrix4Test: public Corrade::TestSuite::Tester { void rotationY(); void rotationZ(); void reflection(); + void orthographicProjection(); + void perspectiveProjection(); + void perspectiveProjectionFov(); void fromParts(); void rotationScalingPart(); void rotationPart(); @@ -60,6 +63,9 @@ Matrix4Test::Matrix4Test() { &Matrix4Test::rotationY, &Matrix4Test::rotationZ, &Matrix4Test::reflection, + &Matrix4Test::orthographicProjection, + &Matrix4Test::perspectiveProjection, + &Matrix4Test::perspectiveProjectionFov, &Matrix4Test::fromParts, &Matrix4Test::rotationScalingPart, &Matrix4Test::rotationPart, @@ -180,6 +186,32 @@ void Matrix4Test::reflection() { CORRADE_COMPARE(actual, expected); } +void Matrix4Test::orthographicProjection() { + Matrix4 expected(0.4f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.5f, 0.0f, 0.0f, + 0.0f, 0.0f, -0.25f, 0.0f, + 0.0f, 0.0f, -1.25f, 1.0f); + CORRADE_COMPARE(Matrix4::orthographicProjection({5.0f, 4.0f}, 1, 9), expected); +} + +void Matrix4Test::perspectiveProjection() { + Matrix4 expected(4.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 7.111111f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.9411764f, -1.0f, + 0.0f, 0.0f, -94.1176452f, 0.0f); + + CORRADE_COMPARE(Matrix4::perspectiveProjection({16.0f, 9.0f}, 32.0f, 100), expected); +} + +void Matrix4Test::perspectiveProjectionFov() { + Matrix4 expected(4.1652994f, 0.0f, 0.0f, 0.0f, + 0.0f, 9.788454f, 0.0f, 0.0f, + 0.0f, 0.0f, -1.9411764f, -1.0f, + 0.0f, 0.0f, -94.1176452f, 0.0f); + + CORRADE_COMPARE(Matrix4::perspectiveProjection(deg(27.0f), 2.35f, 32.0f, 100), expected); +} + void Matrix4Test::fromParts() { Matrix3 rotationScaling( 3.0f, 5.0f, 8.0f, diff --git a/src/SceneGraph/Camera2D.h b/src/SceneGraph/Camera2D.h index 591cd4261..5154b3f4a 100644 --- a/src/SceneGraph/Camera2D.h +++ b/src/SceneGraph/Camera2D.h @@ -66,8 +66,7 @@ class MAGNUM_SCENEGRAPH_EXPORT Camera2D: public AbstractCamera<2, T> { * @param size Size of the view * @return Pointer to self (for method chaining) * - * The area of given size will be scaled down to range @f$ [-1; 1] @f$ - * on all directions. + * @see Matrix3::projection() */ Camera2D* setProjection(const Math::Vector2& size); diff --git a/src/SceneGraph/Camera2D.hpp b/src/SceneGraph/Camera2D.hpp index 818e5ea19..9bea284fc 100644 --- a/src/SceneGraph/Camera2D.hpp +++ b/src/SceneGraph/Camera2D.hpp @@ -27,8 +27,7 @@ using namespace std; namespace Magnum { namespace SceneGraph { template Camera2D* Camera2D::setProjection(const Math::Vector2& size) { - /* Scale the volume down so it fits in (-1, 1) in all directions */ - AbstractCamera<2, T>::rawProjectionMatrix = Math::Matrix3::scaling(2.0f/size); + AbstractCamera<2, T>::rawProjectionMatrix = Math::Matrix3::projection(size); AbstractCamera<2, T>::fixAspectRatio(); return this; diff --git a/src/SceneGraph/Camera3D.h b/src/SceneGraph/Camera3D.h index ea0aca366..24180fd9c 100644 --- a/src/SceneGraph/Camera3D.h +++ b/src/SceneGraph/Camera3D.h @@ -36,7 +36,7 @@ OpenGL unit cube `[(-1, -1, -1); (1, 1, 1)]` with orthographic projection and doesn't do any aspect ratio correction. Common setup example: @code SceneGraph::Camera3D<>* camera = new SceneGraph::Camera3D<>(&cameraObject); -camera->setPerspective(deg(35.0f), 0.001f, 100.0f) +camera->setPerspective({}, 0.001f, 100.0f) ->setAspectRatioPolicy(SceneGraph::AspectRatioPolicy::Extend); @endcode @@ -60,35 +60,42 @@ class MAGNUM_SCENEGRAPH_EXPORT Camera3D: public AbstractCamera<3, T> { /** * @brief Constructor * @param object %Object holding this feature - * - * Sets orthographic projection to the default OpenGL cube - * (range @f$ [-1; 1] @f$ in all directions). - * @see setOrthographic(), setPerspective() */ inline explicit Camera3D(AbstractObject<3, T>* object): AbstractCamera<3, T>(object), _near(0.0f), _far(0.0f) {} /** * @brief Set orthographic projection - * @param size Size of the view - * @param near Near clipping plane - * @param far Far clipping plane + * @param size Size of the view + * @param near Near clipping plane + * @param far Far clipping plane * @return Pointer to self (for method chaining) * - * The volume of given size will be scaled down to range @f$ [-1; 1] @f$ - * on all directions. + * @see setPerspective(), Matrix4::orthographicProjection() */ Camera3D* setOrthographic(const Math::Vector2& size, T near, T far); /** * @brief Set perspective projection - * @param fov Field of view angle - * @param near Near clipping plane - * @param far Far clipping plane + * @param size Size of near clipping plane + * @param near Near clipping plane + * @param far Far clipping plane + * @return Pointer to self (for method chaining) + * + * @see setOrthographic(), Matrix4::perspectiveProjection() + */ + Camera3D* setPerspective(const Math::Vector2& size, T near, T far); + + /** + * @brief Set perspective projection + * @param fov Field of view angle (horizontal, in radians) + * @param aspectRatio Aspect ratio + * @param near Near clipping plane + * @param far Far clipping plane * @return Pointer to self (for method chaining) * - * @todo Aspect ratio + * @see setOrthographic(), Matrix4::perspectiveProjection() */ - Camera3D* setPerspective(T fov, T near, T far); + Camera3D* setPerspective(T fov, T aspectRatio, T near, T far); /** @brief Near clipping plane */ inline T near() const { return _near; } diff --git a/src/SceneGraph/Camera3D.hpp b/src/SceneGraph/Camera3D.hpp index 23bee2e44..0e97d1715 100644 --- a/src/SceneGraph/Camera3D.hpp +++ b/src/SceneGraph/Camera3D.hpp @@ -27,37 +27,31 @@ using namespace std; namespace Magnum { namespace SceneGraph { template Camera3D* Camera3D::setOrthographic(const Math::Vector2& size, T near, T far) { + /** @todo Get near/far from the matrix */ _near = near; _far = far; - Math::Vector2 xyScale = T(2.0)/size; - T zScale = T(2.0)/(near-far); - - AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4( - xyScale.x(), T(0.0), T(0.0), T(0.0), - T(0.0), xyScale.y(), T(0.0), T(0.0), - T(0.0), T(0.0), zScale, T(0.0), - T(0.0), T(0.0), near*zScale-1, T(1.0) - ); - + AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4::orthographicProjection(size, near, far); AbstractCamera<3, T>::fixAspectRatio(); return this; } -template Camera3D* Camera3D::setPerspective(T fov, T near, T far) { +template Camera3D* Camera3D::setPerspective(const Math::Vector2& size, T near, T far) { + /** @todo Get near/far from the matrix */ _near = near; _far = far; - T xyScale = T(1.0)/tan(fov/2); /* == near/size */ - T zScale = T(1.0)/(near-far); + AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4::perspectiveProjection(size, near, far); + AbstractCamera<3, T>::fixAspectRatio(); + return this; +} - AbstractCamera<3, T>::rawProjectionMatrix = Matrix4( - xyScale, T(0.0), T(0.0), T(0.0), - T(0.0), xyScale, T(0.0), T(0.0), - T(0.0), T(0.0), (far+near)*zScale, T(-1.0), - T(0.0), T(0.0), (2*far*near)*zScale, T(0.0) - ); +template Camera3D* Camera3D::setPerspective(T fov, T aspectRatio, T near, T far) { + /** @todo Get near/far from the matrix */ + _near = near; + _far = far; + AbstractCamera<3, T>::rawProjectionMatrix = Math::Matrix4::perspectiveProjection(fov, aspectRatio, near, far); AbstractCamera<3, T>::fixAspectRatio(); return this; } diff --git a/src/SceneGraph/Test/CameraTest.cpp b/src/SceneGraph/Test/CameraTest.cpp index e0a737e24..2a3ba52f9 100644 --- a/src/SceneGraph/Test/CameraTest.cpp +++ b/src/SceneGraph/Test/CameraTest.cpp @@ -33,9 +33,9 @@ class CameraTest: public Corrade::TestSuite::Tester { void fixAspectRatio(); void defaultProjection2D(); void defaultProjection3D(); - void projection2D(); - void orthographic(); - void perspective(); + void projectionSize2D(); + void projectionSizeOrthographic(); + void projectionSizePerspective(); void projectionSizeViewport(); void draw(); }; @@ -50,9 +50,9 @@ CameraTest::CameraTest() { addTests(&CameraTest::fixAspectRatio, &CameraTest::defaultProjection2D, &CameraTest::defaultProjection3D, - &CameraTest::projection2D, - &CameraTest::orthographic, - &CameraTest::perspective, + &CameraTest::projectionSize2D, + &CameraTest::projectionSizeOrthographic, + &CameraTest::projectionSizePerspective, &CameraTest::projectionSizeViewport, &CameraTest::draw); } @@ -113,58 +113,27 @@ void CameraTest::defaultProjection3D() { CORRADE_COMPARE(camera.projectionSize(), Vector2(2.0f)); } -void CameraTest::projection2D() { +void CameraTest::projectionSize2D() { Vector2 projectionSize(4.0f, 3.0f); Object2D o; Camera2D camera(&o); camera.setProjection(projectionSize); - - Matrix3 a(2.0f/4.0f, 0.0f, 0.0f, - 0.0f, 2.0f/3.0f, 0.0f, - 0.0f, 0.0f, 1.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), a); CORRADE_COMPARE(camera.projectionSize(), projectionSize); } -void CameraTest::orthographic() { - Vector2 projectionSize(5); +void CameraTest::projectionSizeOrthographic() { + Vector2 projectionSizeRectangle(5.0f, 4.0f); Object3D o; Camera3D camera(&o); - camera.setOrthographic(projectionSize, 1, 9); - - Matrix4 a(0.4f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.4f, 0.0f, 0.0f, - 0.0f, 0.0f, -0.25f, 0.0f, - 0.0f, 0.0f, -1.25f, 1.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), a); - CORRADE_COMPARE(camera.projectionSize(), projectionSize); - - Vector2 projectionSizeRectangle(5.0f, 4.0f); camera.setOrthographic(projectionSizeRectangle, 1, 9); - - Matrix4 rectangle(0.4f, 0.0f, 0.0f, 0.0f, - 0.0f, 0.5f, 0.0f, 0.0f, - 0.0f, 0.0f, -0.25f, 0.0f, - 0.0f, 0.0f, -1.25f, 1.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), rectangle); CORRADE_COMPARE(camera.projectionSize(), projectionSizeRectangle); } -void CameraTest::perspective() { +void CameraTest::projectionSizePerspective() { Object3D o; Camera3D camera(&o); - camera.setPerspective(deg(27.0f), 32.0f, 100); - - Matrix4 a(4.1652994f, 0.0f, 0.0f, 0.0f, - 0.0f, 4.1652994f, 0.0f, 0.0f, - 0.0f, 0.0f, -1.9411764f, -1.0f, - 0.0f, 0.0f, -94.1176452f, 0.0f); - - CORRADE_COMPARE(camera.projectionMatrix(), a); - CORRADE_COMPARE(camera.projectionSize(), Vector2(0.48015756f)); + camera.setPerspective(deg(27.0f), 2.35f, 32.0f, 100); + CORRADE_COMPARE(camera.projectionSize(), Vector2(0.48015756f, 0.204322f)); } void CameraTest::projectionSizeViewport() {