Add viewport transform to QMatrix4x4

This allows to easily create a matrix that performs the transformation
used by OpenGL fixed function to go from normalized device coordinates
to window coordinates.

This comes in useful if you need to perform the NDC->window coordinate
conversion inside a shader.

Change-Id: I183b3545bfb3eb1e8b13fc3172911b46926fcbb7
Reviewed-by: Paul Lemire <paul.lemire@kdab.com>
Reviewed-by: Laszlo Agocs <laszlo.agocs@digia.com>
This commit is contained in:
Sean Harmer 2014-04-19 16:36:35 +01:00 committed by The Qt Project
parent 71fb3633e8
commit ca15825ed4
3 changed files with 88 additions and 0 deletions

View File

@ -1541,6 +1541,52 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe
#endif
/*!
\fn void QMatrix4x4::viewport(const QRectF &rect)
\overload
Sets up viewport transform for viewport bounded by \a rect and with near and far set
to 0 and 1 respectively.
*/
/*!
Multiplies this matrix by another that performs the scale and bias
transformation used by OpenGL to transform from normalized device
coordinates (NDC) to viewport (window) coordinates. That is it maps
points from the cube ranging over [-1, 1] in each dimension to the
viewport with it's near-lower-left corner at (\a left, \a bottom, \a nearPlane)
and with size (\a width, \a height, \a farPlane - \a nearPlane).
This matches the transform used by the fixed function OpenGL viewport
transform controlled by the functions glViewport() and glDepthRange().
*/
void QMatrix4x4::viewport(float left, float bottom, float width, float height, float nearPlane, float farPlane)
{
const float w2 = width / 2.0f;
const float h2 = height / 2.0f;
QMatrix4x4 m(1);
m.m[0][0] = w2;
m.m[1][0] = 0.0f;
m.m[2][0] = 0.0f;
m.m[3][0] = left + w2;
m.m[0][1] = 0.0f;
m.m[1][1] = h2;
m.m[2][1] = 0.0f;
m.m[3][1] = bottom + h2;
m.m[0][2] = 0.0f;
m.m[1][2] = 0.0f;
m.m[2][2] = (farPlane - nearPlane) / 2.0f;
m.m[3][2] = (nearPlane + farPlane) / 2.0f;
m.m[0][3] = 0.0f;
m.m[1][3] = 0.0f;
m.m[2][3] = 0.0f;
m.m[3][3] = 1.0f;
m.flagBits = General;
*this *= m;
}
/*!
\deprecated

View File

@ -148,6 +148,8 @@ public:
#ifndef QT_NO_VECTOR3D
void lookAt(const QVector3D& eye, const QVector3D& center, const QVector3D& up);
#endif
void viewport(const QRectF &rect);
void viewport(float left, float bottom, float width, float height, float nearPlane = 0.0f, float farPlane = 1.0f);
void flipCoordinates();
void copyDataTo(float *values) const;
@ -1077,6 +1079,11 @@ inline float *QMatrix4x4::data()
return *m;
}
inline void QMatrix4x4::viewport(const QRectF &rect)
{
viewport(rect.x(), rect.y(), rect.width(), rect.height());
}
#ifndef QT_NO_DEBUG_STREAM
Q_GUI_EXPORT QDebug operator<<(QDebug dbg, const QMatrix4x4 &m);
#endif

View File

@ -147,6 +147,7 @@ private slots:
void ortho();
void frustum();
void perspective();
void viewport();
void flipCoordinates();
void convertGeneric();
@ -2794,6 +2795,40 @@ void tst_QMatrixNxN::perspective()
QVERIFY(m5.isIdentity());
}
// Test viewport transformations
void tst_QMatrixNxN::viewport()
{
// Uses default depth range of 0->1
QMatrix4x4 m1;
m1.viewport(0.0f, 0.0f, 1024.0f, 768.0f);
// Lower left
QVector4D p1 = m1 * QVector4D(-1.0f, -1.0f, 0.0f, 1.0f);
QVERIFY(qFuzzyIsNull(p1.x()));
QVERIFY(qFuzzyIsNull(p1.y()));
QVERIFY(qFuzzyCompare(p1.z(), 0.5f));
// Lower right
QVector4D p2 = m1 * QVector4D(1.0f, -1.0f, 0.0f, 1.0f);
QVERIFY(qFuzzyCompare(p2.x(), 1024.0f));
QVERIFY(qFuzzyIsNull(p2.y()));
// Upper right
QVector4D p3 = m1 * QVector4D(1.0f, 1.0f, 0.0f, 1.0f);
QVERIFY(qFuzzyCompare(p3.x(), 1024.0f));
QVERIFY(qFuzzyCompare(p3.y(), 768.0f));
// Upper left
QVector4D p4 = m1 * QVector4D(-1.0f, 1.0f, 0.0f, 1.0f);
QVERIFY(qFuzzyIsNull(p4.x()));
QVERIFY(qFuzzyCompare(p4.y(), 768.0f));
// Center
QVector4D p5 = m1 * QVector4D(0.0f, 0.0f, 0.0f, 1.0f);
QVERIFY(qFuzzyCompare(p5.x(), 1024.0f / 2.0f));
QVERIFY(qFuzzyCompare(p5.y(), 768.0f / 2.0f));
}
// Test left-handed vs right-handed coordinate flipping.
void tst_QMatrixNxN::flipCoordinates()
{