Browse Source

Math: add off-center variants of 2D/3D orthographic projection as well.

Was just perspective projection right now.
pull/537/head
Vladimír Vondruš 5 years ago
parent
commit
ff1b0ab307
  1. 5
      doc/changelog.dox
  2. 41
      src/Magnum/Math/Matrix3.h
  3. 51
      src/Magnum/Math/Matrix4.h
  4. 23
      src/Magnum/Math/Test/Matrix3Test.cpp
  5. 18
      src/Magnum/Math/Test/Matrix4Test.cpp

5
doc/changelog.dox

@ -123,6 +123,11 @@ See also:
and @ref Math::Matrix4::from(const Matrix3x3<T>&, const Vector3<T>&) to
create a transformation from a rotation and translation part (see
[mosra/magnum#471](https://github.com/mosra/magnum/pull/471))
- Added @ref Math::Matrix4::orthographicProjection(const Vector2<T>&, const Vector2<T>&, T, T)
and @ref Math::Matrix3::projection(const Vector2<T>&, const Vector2<T>&) to
match the classic `glOrtho()` and `gluOrtho2D()` APIs, complementing
@ref Math::Matrix4::perspectiveProjection(const Vector2<T>&, const Vector2<T>&, T, T)
that got added in 2019.10
- Added @ref Math::Intersection::rayRange() (see [mosra/magnum#484](https://github.com/mosra/magnum/pull/484))
- Added @ref Math::RectangularMatrix::RectangularMatrix(IdentityInitT, T)
constructor as it might be useful to create non-square identity matrices as

41
src/Magnum/Math/Matrix3.h

@ -218,16 +218,41 @@ template<class T> class Matrix3: public Matrix3x3<T> {
* \end{pmatrix}
* @f]
*
* Similar to the classic @m_class{m-doc-external} [gluOrtho2D()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluOrtho2D.xml)
* function, except that the projection is always centered.
* If you need an off-center projection (as with the classic
* @m_class{m-doc-external} [gluOrtho2D()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluOrtho2D.xml)
* function, use @ref projection(const Vector2<T>&, const Vector2<T>&).
* @see @ref Matrix4::orthographicProjection(),
* @ref Matrix4::perspectiveProjection()
* @m_keywords{gluOrtho2D()}
*/
static Matrix3<T> projection(const Vector2<T>& size) {
return scaling(T(2.0)/size);
}
/**
* @brief 2D off-center orthographic projection matrix
* @param bottomLeft Bottom left corner of the clipping plane
* @param topRight Top right corner of the clipping plane
* @m_since_latest
*
* @f[
* \boldsymbol{A} = \begin{pmatrix}
* \frac{2}{r - l} & 0 & - \frac{r + l}{r - l} \\
* 0 & \frac{2}{t - b} & - \frac{t + b}{t - b} \\
* 0 & 0 & 1
* \end{pmatrix}
* @f]
*
* Equivalent to the classic @m_class{m-doc-external} [gluOrtho2D()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluOrtho2D.xml)
* function. If @p bottomLeft and @p topRight are a negation of each
* other, this function is equivalent to
* @ref projection(const Vector2<T>&).
*
* @see @ref Matrix4::orthographicProjection(const Vector2<T>&, const Vector2<T>&, T, T),
* @ref Matrix4::perspectiveProjection(const Vector2<T>&, const Vector2<T>&, T, T)
* @m_keywords{gluOrtho2D()}
*/
static Matrix3<T> projection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight);
/**
* @brief Create matrix from rotation/scaling part and translation part
* @param rotationScaling Rotation/scaling part (upper-left 2x2
@ -726,6 +751,16 @@ template<class T> Matrix3<T> Matrix3<T>::rotation(const Rad<T> angle) {
{ T(0), T(0), T(1)}};
}
template<class T> Matrix3<T> Matrix3<T>::projection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight) {
const Vector2<T> difference = topRight - bottomLeft;
const Vector2<T> scale = T(2.0)/difference;
const Vector2<T> offset = (topRight + bottomLeft)/difference;
return {{ scale.x(), T(0), T(0)},
{ T(0), scale.y(), T(0)},
{-offset.x(), -offset.y(), T(1)}};
}
template<class T> Matrix2x2<T> Matrix3<T>::rotation() const {
Matrix2x2<T> rotation{(*this)[0].xy().normalized(),
(*this)[1].xy().normalized()};

51
src/Magnum/Math/Matrix4.h

@ -333,13 +333,43 @@ template<class T> class Matrix4: public Matrix4x4<T> {
* \end{pmatrix}
* @f]
*
* Similar to the classic @m_class{m-doc-external} [glOrtho()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml)
* function, except that the projection is always centered.
* If you need an off-center projection (as with the classic
* @m_class{m-doc-external} [glOrtho()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml)
* function), use @ref orthographicProjection(const Vector2<T>&, const Vector2<T>&, T, T).
* @see @ref perspectiveProjection(), @ref Matrix3::projection()
* @m_keywords{glOrtho()}
*/
static Matrix4<T> orthographicProjection(const Vector2<T>& size, T near, T far);
/**
* @brief 3D off-center orthographic projection matrix
* @param bottomLeft Bottom left corner of the clipping plane
* @param topRight Top right corner of the clipping plane
* @param near Distance to near clipping plane, positive is
* ahead
* @param far Distance to far clipping plane, positive is
* ahead
* @m_since_latest
*
* @f[
* \boldsymbol{A} = \begin{pmatrix}
* \frac{2}{r - l} & 0 & 0 & - \frac{r + l}{r - l} \\
* 0 & \frac{2}{t - b} & 0 & - \frac{t + b}{t - b} \\
* 0 & 0 & \frac{2}{n - f} & \frac{n + f}{n - f} \\
* 0 & 0 & 0 & 1
* \end{pmatrix}
* @f]
*
* Equivalent to the classic @m_class{m-doc-external} [glOrtho()](https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml)
* function. If @p bottomLeft and @p topRight are a negation of each
* other, this function is equivalent to
* @ref orthographicProjection(const Vector2<T>&, T, T).
*
* @see @ref perspectiveProjection(),
* @ref Matrix3::projection(const Vector2<T>&, const Vector2<T>&)
* @m_keywords{glOrtho()}
*/
static Matrix4<T> orthographicProjection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight, T near, T far);
/**
* @brief 3D perspective projection matrix
* @param size Size of near clipping plane
@ -455,8 +485,8 @@ template<class T> class Matrix4: public Matrix4x4<T> {
* @ref perspectiveProjection(const Vector2<T>&, T, T).
*
* @see @ref perspectiveProjection(Rad<T> fov, T, T, T),
* @ref orthographicProjection(), @ref Matrix3::projection(),
* @ref Constants::inf()
* @ref orthographicProjection(const Vector2<T>&, const Vector2<T>&, T, T),
* @ref Matrix3::projection(), @ref Constants::inf()
* @m_keywords{glFrustum()}
*/
static Matrix4<T> perspectiveProjection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight, T near, T far);
@ -1120,6 +1150,17 @@ template<class T> Matrix4<T> Matrix4<T>::orthographicProjection(const Vector2<T>
{ T(0), T(0), near*zScale-T(1), T(1)}};
}
template<class T> Matrix4<T> Matrix4<T>::orthographicProjection(const Vector2<T>& bottomLeft, const Vector2<T>& topRight, const T near, const T far) {
const Vector3<T> difference{topRight - bottomLeft, near - far};
const Vector3<T> scale = T(2.0)/difference;
const Vector3<T> offset = Vector3<T>{topRight + bottomLeft, near + far}/difference;
return {{ scale.x(), T(0), T(0), T(0)},
{ T(0), scale.y(), T(0), T(0)},
{ T(0), T(0), scale.z(), T(0)},
{-offset.x(), -offset.y(), offset.z(), T(1)}};
}
template<class T> Matrix4<T> Matrix4<T>::perspectiveProjection(const Vector2<T>& size, const T near, const T far) {
const Vector2<T> xyScale = 2*near/size;

23
src/Magnum/Math/Test/Matrix3Test.cpp

@ -82,6 +82,7 @@ struct Matrix3Test: Corrade::TestSuite::Tester {
void shearingX();
void shearingY();
void projection();
void projectionOffCenter();
void fromParts();
void rotationScalingPart();
@ -135,6 +136,7 @@ Matrix3Test::Matrix3Test() {
&Matrix3Test::shearingX,
&Matrix3Test::shearingY,
&Matrix3Test::projection,
&Matrix3Test::projectionOffCenter,
&Matrix3Test::fromParts,
&Matrix3Test::rotationScalingPart,
@ -399,11 +401,26 @@ void Matrix3Test::shearingY() {
}
void Matrix3Test::projection() {
Matrix3 expected({2.0f/4.0f, 0.0f, 0.0f},
{ 0.0f, 2.0f/3.0f, 0.0f},
Matrix3 expected({2.0f/5.0f, 0.0f, 0.0f},
{ 0.0f, 2.0f/4.0f, 0.0f},
{ 0.0f, 0.0f, 1.0f});
CORRADE_COMPARE(Matrix3::projection({4.0f, 3.0f}), expected);
CORRADE_COMPARE(Matrix3::projection({5.0f, 4.0f}), expected);
}
void Matrix3Test::projectionOffCenter() {
Matrix3 expected({0.4f, 0.0f, 0.0f},
{0.0f, 0.5f, 0.0f},
{0.4f, 0.25f, 1.0f});
/* Shifted by (-1, -0.5) compared to the projection() test */
Matrix3 actual = Matrix3::projection({-3.5f, -2.5f}, {1.5f, 1.5f});
CORRADE_COMPARE(actual, expected);
/* NDC is left-handed, so point on the near plane top right corner should
be (1, 1), and a point on the far plane bottom left corner should be
(-1, -1) */
CORRADE_COMPARE(actual.transformPoint({1.5f, 1.5f}), (Vector2{1.0f, 1.0f}));
CORRADE_COMPARE(actual.transformPoint({-3.5f, -2.5f}), (Vector2{-1.0f, -1.0f}));
}
void Matrix3Test::fromParts() {

18
src/Magnum/Math/Test/Matrix4Test.cpp

@ -90,6 +90,7 @@ struct Matrix4Test: Corrade::TestSuite::Tester {
void shearingXZ();
void shearingYZ();
void orthographicProjection();
void orthographicProjectionOffCenter();
void perspectiveProjection();
void perspectiveProjectionInfiniteFar();
void perspectiveProjectionFov();
@ -160,6 +161,7 @@ Matrix4Test::Matrix4Test() {
&Matrix4Test::shearingXZ,
&Matrix4Test::shearingYZ,
&Matrix4Test::orthographicProjection,
&Matrix4Test::orthographicProjectionOffCenter,
&Matrix4Test::perspectiveProjection,
&Matrix4Test::perspectiveProjectionInfiniteFar,
&Matrix4Test::perspectiveProjectionFov,
@ -523,6 +525,22 @@ void Matrix4Test::orthographicProjection() {
CORRADE_COMPARE(actual.transformPoint({0.0f, 0.0f, -9.0f}), Vector3(0.0f, 0.0f, +1.0f));
}
void Matrix4Test::orthographicProjectionOffCenter() {
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.4f, 0.25f, -1.25f, 1.0f});
/* Shifted by (-1, -0.5) compared to the orthographicProjection() test */
Matrix4 actual = Matrix4::orthographicProjection({-3.5f, -2.5f}, {1.5f, 1.5f}, 1.0f, 9.0f);
CORRADE_COMPARE(actual, expected);
/* NDC is left-handed, so point on the near plane top right corner should
be (1, 1, -1), and a point on the far plane bottom left corner should be
(-1, -1, 1) */
CORRADE_COMPARE(actual.transformPoint({1.5f, 1.5f, -1.0f}), (Vector3{1.0f, 1.0f, -1.0f}));
CORRADE_COMPARE(actual.transformPoint({-3.5f, -2.5f, -9.0f}), (Vector3{-1.0f, -1.0f, +1.0f}));
}
void Matrix4Test::perspectiveProjection() {
Matrix4 expected({4.0f, 0.0f, 0.0f, 0.0f},
{0.0f, 7.111111f, 0.0f, 0.0f},

Loading…
Cancel
Save