From 13b3545e833f6175f686c9776e1510db3f3f11eb Mon Sep 17 00:00:00 2001 From: Kim Motoyoshi Kalland Date: Fri, 19 Aug 2011 15:00:41 +0200 Subject: Optimized QMatrix4x4 by improving how 'flagBits' are used. Change-Id: Ic417336489d334e25b547c719d457faf65307cac Reviewed-on: http://codereview.qt.nokia.com/3670 Reviewed-by: Qt Sanity Bot Reviewed-by: Gunnar Sletta --- src/gui/math3d/qmatrix4x4.cpp | 659 +++++++++++++++++-------------- src/gui/math3d/qmatrix4x4.h | 321 ++++++++++----- tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp | 108 ++++- 3 files changed, 666 insertions(+), 422 deletions(-) diff --git a/src/gui/math3d/qmatrix4x4.cpp b/src/gui/math3d/qmatrix4x4.cpp index cef92af66b..1797564a98 100644 --- a/src/gui/math3d/qmatrix4x4.cpp +++ b/src/gui/math3d/qmatrix4x4.cpp @@ -209,7 +209,7 @@ QMatrix4x4::QMatrix4x4(const QMatrix& matrix) m[3][1] = matrix.dy(); m[3][2] = 0.0f; m[3][3] = 1.0f; - flagBits = General; + flagBits = Translation | Scale | Rotation2D; } /*! @@ -316,6 +316,12 @@ QMatrix4x4::QMatrix4x4(const QTransform& transform) Fills all elements of this matrx with \a value. */ +static inline qreal matrixDet2(const qreal m[4][4], int col0, int col1, int row0, int row1) +{ + return m[col0][row0] * m[col1][row1] - m[col0][row1] * m[col1][row0]; +} + + // The 4x4 matrix inverse algorithm is based on that described at: // http://www.j3d.org/matrix_faq/matrfaq_latest.html#Q24 // Some optimization has been done to avoid making copies of 3x3 @@ -329,15 +335,9 @@ static inline qreal matrixDet3 (const qreal m[4][4], int col0, int col1, int col2, int row0, int row1, int row2) { - return m[col0][row0] * - (m[col1][row1] * m[col2][row2] - - m[col1][row2] * m[col2][row1]) - - m[col1][row0] * - (m[col0][row1] * m[col2][row2] - - m[col0][row2] * m[col2][row1]) + - m[col2][row0] * - (m[col0][row1] * m[col1][row2] - - m[col0][row2] * m[col1][row1]); + return m[col0][row0] * matrixDet2(m, col1, col2, row1, row2) + - m[col1][row0] * matrixDet2(m, col0, col2, row1, row2) + + m[col2][row0] * matrixDet2(m, col0, col1, row1, row2); } // Calculate the determinant of a 4x4 matrix. @@ -356,7 +356,13 @@ static inline qreal matrixDet4(const qreal m[4][4]) */ qreal QMatrix4x4::determinant() const { - return qreal(matrixDet4(m)); + if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) + return 1; + if (flagBits < Rotation2D) + return m[0][0] * m[1][1] * m[2][2]; // Translation | Scale + if (flagBits < Perspective) + return matrixDet3(m, 0, 1, 2, 0, 1, 2); + return matrixDet4(m); } /*! @@ -387,10 +393,61 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const if (invertible) *invertible = true; return inv; - } else if (flagBits == Rotation || flagBits == (Rotation | Translation)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale + if (m[0][0] == 0 || m[1][1] == 0 || m[2][2] == 0) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + QMatrix4x4 inv; + inv.m[0][0] = 1.0f / m[0][0]; + inv.m[1][1] = 1.0f / m[1][1]; + inv.m[2][2] = 1.0f / m[2][2]; + inv.m[3][0] = -m[3][0] * inv.m[0][0]; + inv.m[3][1] = -m[3][1] * inv.m[1][1]; + inv.m[3][2] = -m[3][2] * inv.m[2][2]; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; + } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) { if (invertible) *invertible = true; return orthonormalInverse(); + } else if (flagBits < Perspective) { + QMatrix4x4 inv(1); // The "1" says to not load the identity. + + qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + if (det == 0.0f) { + if (invertible) + *invertible = false; + return QMatrix4x4(); + } + det = 1.0f / det; + + inv.m[0][0] = matrixDet2(m, 1, 2, 1, 2) * det; + inv.m[0][1] = -matrixDet2(m, 0, 2, 1, 2) * det; + inv.m[0][2] = matrixDet2(m, 0, 1, 1, 2) * det; + inv.m[0][3] = 0; + inv.m[1][0] = -matrixDet2(m, 1, 2, 0, 2) * det; + inv.m[1][1] = matrixDet2(m, 0, 2, 0, 2) * det; + inv.m[1][2] = -matrixDet2(m, 0, 1, 0, 2) * det; + inv.m[1][3] = 0; + inv.m[2][0] = matrixDet2(m, 1, 2, 0, 1) * det; + inv.m[2][1] = -matrixDet2(m, 0, 2, 0, 1) * det; + inv.m[2][2] = matrixDet2(m, 0, 1, 0, 1) * det; + inv.m[2][3] = 0; + inv.m[3][0] = -inv.m[0][0] * m[3][0] - inv.m[1][0] * m[3][1] - inv.m[2][0] * m[3][2]; + inv.m[3][1] = -inv.m[0][1] * m[3][0] - inv.m[1][1] * m[3][1] - inv.m[2][1] * m[3][2]; + inv.m[3][2] = -inv.m[0][2] * m[3][0] - inv.m[1][2] * m[3][1] - inv.m[2][2] * m[3][2]; + inv.m[3][3] = 1; + inv.flagBits = flagBits; + + if (invertible) + *invertible = true; + return inv; } QMatrix4x4 inv(1); // The "1" says to not load the identity. @@ -419,6 +476,7 @@ QMatrix4x4 QMatrix4x4::inverted(bool *invertible) const inv.m[3][1] = matrixDet3(m, 0, 2, 3, 0, 1, 2) * det; inv.m[3][2] = -matrixDet3(m, 0, 1, 3, 0, 1, 2) * det; inv.m[3][3] = matrixDet3(m, 0, 1, 2, 0, 1, 2) * det; + inv.flagBits = flagBits; if (invertible) *invertible = true; @@ -438,15 +496,29 @@ QMatrix3x3 QMatrix4x4::normalMatrix() const QMatrix3x3 inv; // Handle the simple cases first. - if (flagBits == Identity || flagBits == Translation) { + if (flagBits < Scale) { + // Translation return inv; - } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale if (m[0][0] == 0.0f || m[1][1] == 0.0f || m[2][2] == 0.0f) return inv; inv.data()[0] = 1.0f / m[0][0]; inv.data()[4] = 1.0f / m[1][1]; inv.data()[8] = 1.0f / m[2][2]; return inv; + } else if ((flagBits & ~(Translation | Rotation2D | Rotation)) == Identity) { + qreal *invm = inv.data(); + invm[0 + 0 * 3] = m[0][0]; + invm[1 + 0 * 3] = m[0][1]; + invm[2 + 0 * 3] = m[0][2]; + invm[0 + 1 * 3] = m[1][0]; + invm[1 + 1 * 3] = m[1][1]; + invm[2 + 1 * 3] = m[1][2]; + invm[0 + 2 * 3] = m[2][0]; + invm[1 + 2 * 3] = m[2][1]; + invm[2 + 2 * 3] = m[2][2]; + return inv; } qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); @@ -481,6 +553,8 @@ QMatrix4x4 QMatrix4x4::transposed() const result.m[col][row] = m[row][col]; } } + // When a translation is transposed, it becomes a perspective transformation. + result.flagBits = (flagBits & Translation ? General : flagBits); return result; } @@ -689,6 +763,7 @@ QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor) m.m[3][1] = matrix.m[3][1] / divisor; m.m[3][2] = matrix.m[3][2] / divisor; m.m[3][3] = matrix.m[3][3] / divisor; + m.flagBits = QMatrix4x4::General; return m; } @@ -713,20 +788,20 @@ void QMatrix4x4::scale(const QVector3D& vector) qreal vx = vector.x(); qreal vy = vector.y(); qreal vz = vector.z(); - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = vx; m[1][1] = vy; m[2][2] = vz; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= vx; m[1][1] *= vy; m[2][2] *= vz; - } else if (flagBits == Translation) { - m[0][0] = vx; - m[1][1] = vy; - m[2][2] = vz; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= vx; + m[0][1] *= vx; + m[1][0] *= vy; + m[1][1] *= vy; + m[2][2] *= vz; } else { m[0][0] *= vx; m[0][1] *= vx; @@ -740,9 +815,10 @@ void QMatrix4x4::scale(const QVector3D& vector) m[2][1] *= vz; m[2][2] *= vz; m[2][3] *= vz; - flagBits = General; } + flagBits |= Scale; } + #endif /*! @@ -755,17 +831,17 @@ void QMatrix4x4::scale(const QVector3D& vector) */ void QMatrix4x4::scale(qreal x, qreal y) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = x; m[1][1] = y; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= x; m[1][1] *= y; - } else if (flagBits == Translation) { - m[0][0] = x; - m[1][1] = y; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; } else { m[0][0] *= x; m[0][1] *= x; @@ -775,8 +851,8 @@ void QMatrix4x4::scale(qreal x, qreal y) m[1][1] *= y; m[1][2] *= y; m[1][3] *= y; - flagBits = General; } + flagBits |= Scale; } /*! @@ -789,20 +865,20 @@ void QMatrix4x4::scale(qreal x, qreal y) */ void QMatrix4x4::scale(qreal x, qreal y, qreal z) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = x; m[1][1] = y; m[2][2] = z; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= x; m[1][1] *= y; m[2][2] *= z; - } else if (flagBits == Translation) { - m[0][0] = x; - m[1][1] = y; - m[2][2] = z; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= x; + m[0][1] *= x; + m[1][0] *= y; + m[1][1] *= y; + m[2][2] *= z; } else { m[0][0] *= x; m[0][1] *= x; @@ -816,8 +892,8 @@ void QMatrix4x4::scale(qreal x, qreal y, qreal z) m[2][1] *= z; m[2][2] *= z; m[2][3] *= z; - flagBits = General; } + flagBits |= Scale; } /*! @@ -830,20 +906,20 @@ void QMatrix4x4::scale(qreal x, qreal y, qreal z) */ void QMatrix4x4::scale(qreal factor) { - if (flagBits == Identity) { + if (flagBits < Scale) { m[0][0] = factor; m[1][1] = factor; m[2][2] = factor; - flagBits = Scale; - } else if (flagBits == Scale || flagBits == (Scale | Translation)) { + } else if (flagBits < Rotation2D) { m[0][0] *= factor; m[1][1] *= factor; m[2][2] *= factor; - } else if (flagBits == Translation) { - m[0][0] = factor; - m[1][1] = factor; - m[2][2] = factor; - flagBits |= Scale; + } else if (flagBits < Rotation) { + m[0][0] *= factor; + m[0][1] *= factor; + m[1][0] *= factor; + m[1][1] *= factor; + m[2][2] *= factor; } else { m[0][0] *= factor; m[0][1] *= factor; @@ -857,8 +933,8 @@ void QMatrix4x4::scale(qreal factor) m[2][1] *= factor; m[2][2] *= factor; m[2][3] *= factor; - flagBits = General; } + flagBits |= Scale; } #ifndef QT_NO_VECTOR3D @@ -868,6 +944,7 @@ void QMatrix4x4::scale(qreal factor) \sa scale(), rotate() */ + void QMatrix4x4::translate(const QVector3D& vector) { qreal vx = vector.x(); @@ -877,7 +954,6 @@ void QMatrix4x4::translate(const QVector3D& vector) m[3][0] = vx; m[3][1] = vy; m[3][2] = vz; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += vx; m[3][1] += vy; @@ -886,23 +962,22 @@ void QMatrix4x4::translate(const QVector3D& vector) m[3][0] = m[0][0] * vx; m[3][1] = m[1][1] * vy; m[3][2] = m[2][2] * vz; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * vx; m[3][1] += m[1][1] * vy; m[3][2] += m[2][2] * vz; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * vx + m[1][0] * vy; + m[3][1] += m[0][1] * vx + m[1][1] * vy; + m[3][2] += m[2][2] * vz; } else { m[3][0] += m[0][0] * vx + m[1][0] * vy + m[2][0] * vz; m[3][1] += m[0][1] * vx + m[1][1] * vy + m[2][1] * vz; m[3][2] += m[0][2] * vx + m[1][2] * vy + m[2][2] * vz; m[3][3] += m[0][3] * vx + m[1][3] * vy + m[2][3] * vz; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } - #endif /*! @@ -918,28 +993,25 @@ void QMatrix4x4::translate(qreal x, qreal y) if (flagBits == Identity) { m[3][0] = x; m[3][1] = y; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += x; m[3][1] += y; } else if (flagBits == Scale) { m[3][0] = m[0][0] * x; m[3][1] = m[1][1] * y; - m[3][2] = 0.; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * x; m[3][1] += m[1][1] * y; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; } else { m[3][0] += m[0][0] * x + m[1][0] * y; m[3][1] += m[0][1] * x + m[1][1] * y; m[3][2] += m[0][2] * x + m[1][2] * y; m[3][3] += m[0][3] * x + m[1][3] * y; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } /*! @@ -956,7 +1028,6 @@ void QMatrix4x4::translate(qreal x, qreal y, qreal z) m[3][0] = x; m[3][1] = y; m[3][2] = z; - flagBits = Translation; } else if (flagBits == Translation) { m[3][0] += x; m[3][1] += y; @@ -965,31 +1036,31 @@ void QMatrix4x4::translate(qreal x, qreal y, qreal z) m[3][0] = m[0][0] * x; m[3][1] = m[1][1] * y; m[3][2] = m[2][2] * z; - flagBits |= Translation; - } else if (flagBits == (Scale | Translation)) { + } else if (flagBits == (Translation | Scale)) { m[3][0] += m[0][0] * x; m[3][1] += m[1][1] * y; m[3][2] += m[2][2] * z; + } else if (flagBits < Rotation) { + m[3][0] += m[0][0] * x + m[1][0] * y; + m[3][1] += m[0][1] * x + m[1][1] * y; + m[3][2] += m[2][2] * z; } else { m[3][0] += m[0][0] * x + m[1][0] * y + m[2][0] * z; m[3][1] += m[0][1] * x + m[1][1] * y + m[2][1] * z; m[3][2] += m[0][2] * x + m[1][2] * y + m[2][2] * z; m[3][3] += m[0][3] * x + m[1][3] * y + m[2][3] * z; - if (flagBits == Rotation) - flagBits |= Translation; - else if (flagBits != (Rotation | Translation)) - flagBits = General; } + flagBits |= Translation; } #ifndef QT_NO_VECTOR3D - /*! Multiples this matrix by another that rotates coordinates through \a angle degrees about \a vector. \sa scale(), translate() */ + void QMatrix4x4::rotate(qreal angle, const QVector3D& vector) { rotate(angle, vector.x(), vector.y(), vector.z()); @@ -1009,8 +1080,7 @@ void QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) { if (angle == 0.0f) return; - QMatrix4x4 m(1); // The "1" says to not load the identity. - qreal c, s, ic; + qreal c, s; if (angle == 90.0f || angle == -270.0f) { s = 1.0f; c = 0.0f; @@ -1025,86 +1095,87 @@ void QMatrix4x4::rotate(qreal angle, qreal x, qreal y, qreal z) c = qCos(a); s = qSin(a); } - bool quick = false; if (x == 0.0f) { if (y == 0.0f) { if (z != 0.0f) { // Rotate around the Z axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[1][1] = c; - if (z < 0.0f) { - m.m[1][0] = s; - m.m[0][1] = -s; - } else { - m.m[1][0] = -s; - m.m[0][1] = s; - } - m.flagBits = General; - quick = true; + if (z < 0) + s = -s; + qreal tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; } } else if (z == 0.0f) { // Rotate around the Y axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[2][2] = c; - if (y < 0.0f) { - m.m[2][0] = -s; - m.m[0][2] = s; - } else { - m.m[2][0] = s; - m.m[0][2] = -s; - } - m.flagBits = General; - quick = true; + if (y < 0) + s = -s; + qreal tmp; + m[2][0] = (tmp = m[2][0]) * c + m[0][0] * s; + m[0][0] = m[0][0] * c - tmp * s; + m[2][1] = (tmp = m[2][1]) * c + m[0][1] * s; + m[0][1] = m[0][1] * c - tmp * s; + m[2][2] = (tmp = m[2][2]) * c + m[0][2] * s; + m[0][2] = m[0][2] * c - tmp * s; + m[2][3] = (tmp = m[2][3]) * c + m[0][3] * s; + m[0][3] = m[0][3] * c - tmp * s; + + flagBits |= Rotation; + return; } } else if (y == 0.0f && z == 0.0f) { // Rotate around the X axis. - m.setToIdentity(); - m.m[1][1] = c; - m.m[2][2] = c; - if (x < 0.0f) { - m.m[2][1] = s; - m.m[1][2] = -s; - } else { - m.m[2][1] = -s; - m.m[1][2] = s; - } - m.flagBits = General; - quick = true; + if (x < 0) + s = -s; + qreal tmp; + m[1][0] = (tmp = m[1][0]) * c + m[2][0] * s; + m[2][0] = m[2][0] * c - tmp * s; + m[1][1] = (tmp = m[1][1]) * c + m[2][1] * s; + m[2][1] = m[2][1] * c - tmp * s; + m[1][2] = (tmp = m[1][2]) * c + m[2][2] * s; + m[2][2] = m[2][2] * c - tmp * s; + m[1][3] = (tmp = m[1][3]) * c + m[2][3] * s; + m[2][3] = m[2][3] * c - tmp * s; + + flagBits |= Rotation; + return; } - if (!quick) { - qreal len = x * x + y * y + z * z; - if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { - len = qSqrt(len); - x /= len; - y /= len; - z /= len; - } - ic = 1.0f - c; - m.m[0][0] = x * x * ic + c; - m.m[1][0] = x * y * ic - z * s; - m.m[2][0] = x * z * ic + y * s; - m.m[3][0] = 0.0f; - m.m[0][1] = y * x * ic + z * s; - m.m[1][1] = y * y * ic + c; - m.m[2][1] = y * z * ic - x * s; - m.m[3][1] = 0.0f; - m.m[0][2] = x * z * ic - y * s; - m.m[1][2] = y * z * ic + x * s; - m.m[2][2] = z * z * ic + c; - m.m[3][2] = 0.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; + + qreal len = x * x + y * y + z * z; + if (!qFuzzyCompare(len, qreal(1)) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; } - int flags = flagBits; - *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; + qreal ic = 1.0f - c; + QMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = x * z * ic + y * s; + rot.m[3][0] = 0.0f; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = y * z * ic - x * s; + rot.m[3][1] = 0.0f; + rot.m[0][2] = x * z * ic - y * s; + rot.m[1][2] = y * z * ic + x * s; + rot.m[2][2] = z * z * ic + c; + rot.m[3][2] = 0.0f; + rot.m[0][3] = 0.0f; + rot.m[1][3] = 0.0f; + rot.m[2][3] = 0.0f; + rot.m[3][3] = 1.0f; + rot.flagBits = Rotation; + *this *= rot; } /*! @@ -1116,8 +1187,7 @@ void QMatrix4x4::projectedRotate(qreal angle, qreal x, qreal y, qreal z) // and projection back to 2D in a single step. if (angle == 0.0f) return; - QMatrix4x4 m(1); // The "1" says to not load the identity. - qreal c, s, ic; + qreal c, s; if (angle == 90.0f || angle == -270.0f) { s = 1.0f; c = 0.0f; @@ -1132,82 +1202,74 @@ void QMatrix4x4::projectedRotate(qreal angle, qreal x, qreal y, qreal z) c = qCos(a); s = qSin(a); } - bool quick = false; if (x == 0.0f) { if (y == 0.0f) { if (z != 0.0f) { // Rotate around the Z axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[1][1] = c; - if (z < 0.0f) { - m.m[1][0] = s; - m.m[0][1] = -s; - } else { - m.m[1][0] = -s; - m.m[0][1] = s; - } - m.flagBits = General; - quick = true; + if (z < 0) + s = -s; + qreal tmp; + m[0][0] = (tmp = m[0][0]) * c + m[1][0] * s; + m[1][0] = m[1][0] * c - tmp * s; + m[0][1] = (tmp = m[0][1]) * c + m[1][1] * s; + m[1][1] = m[1][1] * c - tmp * s; + m[0][2] = (tmp = m[0][2]) * c + m[1][2] * s; + m[1][2] = m[1][2] * c - tmp * s; + m[0][3] = (tmp = m[0][3]) * c + m[1][3] * s; + m[1][3] = m[1][3] * c - tmp * s; + + flagBits |= Rotation2D; + return; } } else if (z == 0.0f) { // Rotate around the Y axis. - m.setToIdentity(); - m.m[0][0] = c; - m.m[2][2] = 1.0f; - if (y < 0.0f) { - m.m[0][3] = -s * inv_dist_to_plane; - } else { - m.m[0][3] = s * inv_dist_to_plane; - } - m.flagBits = General; - quick = true; + if (y < 0) + s = -s; + m[0][0] = m[0][0] * c + m[3][0] * s * inv_dist_to_plane; + m[0][1] = m[0][1] * c + m[3][1] * s * inv_dist_to_plane; + m[0][2] = m[0][2] * c + m[3][2] * s * inv_dist_to_plane; + m[0][3] = m[0][3] * c + m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; } } else if (y == 0.0f && z == 0.0f) { // Rotate around the X axis. - m.setToIdentity(); - m.m[1][1] = c; - m.m[2][2] = 1.0f; - if (x < 0.0f) { - m.m[1][3] = s * inv_dist_to_plane; - } else { - m.m[1][3] = -s * inv_dist_to_plane; - } - m.flagBits = General; - quick = true; + if (x < 0) + s = -s; + m[1][0] = m[1][0] * c - m[3][0] * s * inv_dist_to_plane; + m[1][1] = m[1][1] * c - m[3][1] * s * inv_dist_to_plane; + m[1][2] = m[1][2] * c - m[3][2] * s * inv_dist_to_plane; + m[1][3] = m[1][3] * c - m[3][3] * s * inv_dist_to_plane; + flagBits = General; + return; } - if (!quick) { - qreal len = x * x + y * y + z * z; - if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { - len = qSqrt(len); - x /= len; - y /= len; - z /= len; - } - ic = 1.0f - c; - m.m[0][0] = x * x * ic + c; - m.m[1][0] = x * y * ic - z * s; - m.m[2][0] = 0.0f; - m.m[3][0] = 0.0f; - m.m[0][1] = y * x * ic + z * s; - m.m[1][1] = y * y * ic + c; - m.m[2][1] = 0.0f; - m.m[3][1] = 0.0f; - m.m[0][2] = 0.0f; - m.m[1][2] = 0.0f; - m.m[2][2] = 1.0f; - m.m[3][2] = 0.0f; - m.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane; - m.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane; - m.m[2][3] = 0.0f; - m.m[3][3] = 1.0f; + qreal len = x * x + y * y + z * z; + if (!qFuzzyIsNull(len - 1.0f) && !qFuzzyIsNull(len)) { + len = qSqrt(len); + x /= len; + y /= len; + z /= len; } - int flags = flagBits; - *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; + qreal ic = 1.0f - c; + QMatrix4x4 rot(1); // The "1" says to not load the identity. + rot.m[0][0] = x * x * ic + c; + rot.m[1][0] = x * y * ic - z * s; + rot.m[2][0] = 0.0f; + rot.m[3][0] = 0.0f; + rot.m[0][1] = y * x * ic + z * s; + rot.m[1][1] = y * y * ic + c; + rot.m[2][1] = 0.0f; + rot.m[3][1] = 0.0f; + rot.m[0][2] = 0.0f; + rot.m[1][2] = 0.0f; + rot.m[2][2] = 1.0f; + rot.m[3][2] = 0.0f; + rot.m[0][3] = (x * z * ic - y * s) * -inv_dist_to_plane; + rot.m[1][3] = (y * z * ic + x * s) * -inv_dist_to_plane; + rot.m[2][3] = 0.0f; + rot.m[3][3] = 1.0f; + rot.flagBits = General; + *this *= rot; } #ifndef QT_NO_QUATERNION @@ -1249,12 +1311,8 @@ void QMatrix4x4::rotate(const QQuaternion& quaternion) m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; - int flags = flagBits; + m.flagBits = Rotation; *this *= m; - if (flags != Identity) - flagBits = flags | Rotation; - else - flagBits = Rotation; } #endif @@ -1309,22 +1367,6 @@ void QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal n qreal width = right - left; qreal invheight = top - bottom; qreal clip = farPlane - nearPlane; -#ifndef QT_NO_VECTOR3D - if (clip == 2.0f && (nearPlane + farPlane) == 0.0f) { - // We can express this projection as a translate and scale - // which will be more efficient to modify with further - // transformations than producing a "General" matrix. - translate(QVector3D - (-(left + right) / width, - -(top + bottom) / invheight, - 0.0f)); - scale(QVector3D - (2.0f / width, - 2.0f / invheight, - -1.0f)); - return; - } -#endif QMatrix4x4 m(1); m.m[0][0] = 2.0f / width; m.m[1][0] = 0.0f; @@ -1342,10 +1384,10 @@ void QMatrix4x4::ortho(qreal left, qreal right, qreal bottom, qreal top, qreal n m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; + m.flagBits = Translation | Scale; // Apply the projection. *this *= m; - return; } /*! @@ -1383,6 +1425,7 @@ void QMatrix4x4::frustum(qreal left, qreal right, qreal bottom, qreal top, qreal m.m[1][3] = 0.0f; m.m[2][3] = -1.0f; m.m[3][3] = 0.0f; + m.flagBits = General; // Apply the projection. *this *= m; @@ -1426,6 +1469,7 @@ void QMatrix4x4::perspective(qreal angle, qreal aspect, qreal nearPlane, qreal f m.m[1][3] = 0.0f; m.m[2][3] = -1.0f; m.m[3][3] = 0.0f; + m.flagBits = General; // Apply the projection. *this *= m; @@ -1446,7 +1490,6 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe QVector3D upVector = QVector3D::crossProduct(side, forward); QMatrix4x4 m(1); - m.m[0][0] = side.x(); m.m[1][0] = side.y(); m.m[2][0] = side.z(); @@ -1463,6 +1506,7 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe m.m[1][3] = 0.0f; m.m[2][3] = 0.0f; m.m[3][3] = 1.0f; + m.flagBits = Rotation; *this *= m; translate(-eye); @@ -1471,6 +1515,8 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe #endif /*! + \deprecated + Flips between right-handed and left-handed coordinate systems by multiplying the y and z co-ordinates by -1. This is normally used to create a left-handed orthographic view without scaling @@ -1480,17 +1526,13 @@ void QMatrix4x4::lookAt(const QVector3D& eye, const QVector3D& center, const QVe */ void QMatrix4x4::flipCoordinates() { - if (flagBits == Scale || flagBits == (Scale | Translation)) { + // Multiplying the y and z coordinates with -1 does NOT flip between right-handed and + // left-handed coordinate systems, it just rotates 180 degrees around the x axis, so + // I'm deprecating this function. + if (flagBits < Rotation2D) { + // Translation | Scale m[1][1] = -m[1][1]; m[2][2] = -m[2][2]; - } else if (flagBits == Translation) { - m[1][1] = -m[1][1]; - m[2][2] = -m[2][2]; - flagBits |= Scale; - } else if (flagBits == Identity) { - m[1][1] = -1.0f; - m[2][2] = -1.0f; - flagBits = Scale; } else { m[1][0] = -m[1][0]; m[1][1] = -m[1][1]; @@ -1500,8 +1542,8 @@ void QMatrix4x4::flipCoordinates() m[2][1] = -m[2][1]; m[2][2] = -m[2][2]; m[2][3] = -m[2][3]; - flagBits = General; } + flagBits |= Scale; } /*! @@ -1568,12 +1610,9 @@ QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const { if (distanceToPlane == 1024.0f) { // Optimize the common case with constants. - return QTransform(m[0][0], m[0][1], - m[0][3] - m[0][2] * inv_dist_to_plane, - m[1][0], m[1][1], - m[1][3] - m[1][2] * inv_dist_to_plane, - m[3][0], m[3][1], - m[3][3] - m[3][2] * inv_dist_to_plane); + return QTransform(m[0][0], m[0][1], m[0][3] - m[0][2] * inv_dist_to_plane, + m[1][0], m[1][1], m[1][3] - m[1][2] * inv_dist_to_plane, + m[3][0], m[3][1], m[3][3] - m[3][2] * inv_dist_to_plane); } else if (distanceToPlane != 0.0f) { // The following projection matrix is pre-multiplied with "matrix": // | 1 0 0 0 | @@ -1654,7 +1693,13 @@ QTransform QMatrix4x4::toTransform(qreal distanceToPlane) const */ QRect QMatrix4x4::mapRect(const QRect& rect) const { - if (flagBits == (Translation | Scale) || flagBits == Scale) { + if (flagBits < Scale) { + // Translation + return QRect(qRound(rect.x() + m[3][0]), + qRound(rect.y() + m[3][1]), + rect.width(), rect.height()); + } else if (flagBits < Rotation2D) { + // Translation | Scale qreal x = rect.x() * m[0][0] + m[3][0]; qreal y = rect.y() * m[1][1] + m[3][1]; qreal w = rect.width() * m[0][0]; @@ -1668,10 +1713,6 @@ QRect QMatrix4x4::mapRect(const QRect& rect) const y -= h; } return QRect(qRound(x), qRound(y), qRound(w), qRound(h)); - } else if (flagBits == Translation) { - return QRect(qRound(rect.x() + m[3][0]), - qRound(rect.y() + m[3][1]), - rect.width(), rect.height()); } QPoint tl = map(rect.topLeft()); @@ -1698,7 +1739,11 @@ QRect QMatrix4x4::mapRect(const QRect& rect) const */ QRectF QMatrix4x4::mapRect(const QRectF& rect) const { - if (flagBits == (Translation | Scale) || flagBits == Scale) { + if (flagBits < Scale) { + // Translation + return rect.translated(m[3][0], m[3][1]); + } else if (flagBits < Rotation2D) { + // Translation | Scale qreal x = rect.x() * m[0][0] + m[3][0]; qreal y = rect.y() * m[1][1] + m[3][1]; qreal w = rect.width() * m[0][0]; @@ -1712,8 +1757,6 @@ QRectF QMatrix4x4::mapRect(const QRectF& rect) const y -= h; } return QRectF(x, y, w, h); - } else if (flagBits == Translation) { - return rect.translated(m[3][0], m[3][1]); } QPointF tl = map(rect.topLeft()); QPointF tr = map(rect.topRight()); @@ -1780,6 +1823,8 @@ QMatrix4x4 QMatrix4x4::orthonormalInverse() const result.m[3][2] = -(result.m[0][2] * m[3][0] + result.m[1][2] * m[3][1] + result.m[2][2] * m[3][2]); result.m[3][3] = 1.0f; + result.flagBits = flagBits; + return result; } @@ -1805,40 +1850,50 @@ QMatrix4x4 QMatrix4x4::orthonormalInverse() const */ void QMatrix4x4::optimize() { - // If the last element is not 1, then it can never be special. - if (m[3][3] != 1.0f) { - flagBits = General; + // If the last row is not (0, 0, 0, 1), the matrix is not a special type. + flagBits = General; + if (m[0][3] != 0 || m[1][3] != 0 || m[2][3] != 0 || m[3][3] != 1) return; - } - // If the upper three elements m12, m13, and m21 are not all zero, - // or the lower elements below the diagonal are not all zero, then - // the matrix can never be special. - if (m[1][0] != 0.0f || m[2][0] != 0.0f || m[2][1] != 0.0f) { - flagBits = General; - return; - } - if (m[0][1] != 0.0f || m[0][2] != 0.0f || m[0][3] != 0.0f || - m[1][2] != 0.0f || m[1][3] != 0.0f || m[2][3] != 0.0f) { - flagBits = General; - return; + flagBits &= ~Perspective; + + // If the last column is (0, 0, 0, 1), then there is no translation. + if (m[3][0] == 0 && m[3][1] == 0 && m[3][2] == 0) + flagBits &= ~Translation; + + // If the two first elements of row 3 and column 3 are 0, then any rotation must be about Z. + if (!m[0][2] && !m[1][2] && !m[2][0] && !m[2][1]) { + flagBits &= ~Rotation; + // If the six non-diagonal elements in the top left 3x3 matrix are 0, there is no rotation. + if (!m[0][1] && !m[1][0]) { + flagBits &= ~Rotation2D; + // Check for identity. + if (m[0][0] == 1 && m[1][1] == 1 && m[2][2] == 1) + flagBits &= ~Scale; + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + qreal det = matrixDet2(m, 0, 1, 0, 1); + qreal lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1]; + qreal lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1]; + qreal lenZ = m[2][2]; + if (qFuzzyCompare(det, qreal(1)) && qFuzzyCompare(lenX, qreal(1)) + && qFuzzyCompare(lenY, qreal(1)) && qFuzzyCompare(lenZ, qreal(1))) + { + flagBits &= ~Scale; + } + } + } else { + // If the columns are orthonormal and form a right-handed system, then there is no scale. + qreal det = matrixDet3(m, 0, 1, 2, 0, 1, 2); + qreal lenX = m[0][0] * m[0][0] + m[0][1] * m[0][1] + m[0][2] * m[0][2]; + qreal lenY = m[1][0] * m[1][0] + m[1][1] * m[1][1] + m[1][2] * m[1][2]; + qreal lenZ = m[2][0] * m[2][0] + m[2][1] * m[2][1] + m[2][2] * m[2][2]; + if (qFuzzyCompare(det, qreal(1)) && qFuzzyCompare(lenX, qreal(1)) + && qFuzzyCompare(lenY, qreal(1)) && qFuzzyCompare(lenZ, qreal(1))) + { + flagBits &= ~Scale; + } } - - // Determine what we have in the remaining regions of the matrix. - bool identityAlongDiagonal - = (m[0][0] == 1.0f && m[1][1] == 1.0f && m[2][2] == 1.0f); - bool translationPresent - = (m[3][0] != 0.0f || m[3][1] != 0.0f || m[3][2] != 0.0f); - - // Now determine the special matrix type. - if (translationPresent && identityAlongDiagonal) - flagBits = Translation; - else if (translationPresent) - flagBits = (Translation | Scale); - else if (identityAlongDiagonal) - flagBits = Identity; - else - flagBits = Scale; } /*! @@ -1855,18 +1910,24 @@ QDebug operator<<(QDebug dbg, const QMatrix4x4 &m) { // Create a string that represents the matrix type. QByteArray bits; - if ((m.flagBits & QMatrix4x4::Identity) != 0) - bits += "Identity,"; - if ((m.flagBits & QMatrix4x4::General) != 0) - bits += "General,"; - if ((m.flagBits & QMatrix4x4::Translation) != 0) - bits += "Translation,"; - if ((m.flagBits & QMatrix4x4::Scale) != 0) - bits += "Scale,"; - if ((m.flagBits & QMatrix4x4::Rotation) != 0) - bits += "Rotation,"; - if (bits.size() > 0) - bits = bits.left(bits.size() - 1); + if (m.flagBits == QMatrix4x4::Identity) { + bits = "Identity"; + } else if (m.flagBits == QMatrix4x4::General) { + bits = "General"; + } else { + if ((m.flagBits & QMatrix4x4::Translation) != 0) + bits += "Translation,"; + if ((m.flagBits & QMatrix4x4::Scale) != 0) + bits += "Scale,"; + if ((m.flagBits & QMatrix4x4::Rotation2D) != 0) + bits += "Rotation2D,"; + if ((m.flagBits & QMatrix4x4::Rotation) != 0) + bits += "Rotation,"; + if ((m.flagBits & QMatrix4x4::Perspective) != 0) + bits += "Perspective,"; + if (bits.size() > 0) + bits = bits.left(bits.size() - 1); + } // Output in row-major order because it is more human-readable. dbg.nospace() << "QMatrix4x4(type:" << bits.constData() << endl diff --git a/src/gui/math3d/qmatrix4x4.h b/src/gui/math3d/qmatrix4x4.h index bfbe4b9793..2c98dd03e3 100644 --- a/src/gui/math3d/qmatrix4x4.h +++ b/src/gui/math3d/qmatrix4x4.h @@ -80,11 +80,13 @@ public: inline const qreal& operator()(int row, int column) const; inline qreal& operator()(int row, int column); +#ifndef QT_NO_VECTOR4D inline QVector4D column(int index) const; inline void setColumn(int index, const QVector4D& value); inline QVector4D row(int index) const; inline void setRow(int index, const QVector4D& value); +#endif inline bool isIdentity() const; inline void setToIdentity(); @@ -188,16 +190,19 @@ private: qreal m[4][4]; // Column-major order to match OpenGL. int flagBits; // Flag bits from the enum below. + // When matrices are multiplied, the flag bits are or-ed together. enum { - Identity = 0x0001, // Identity matrix - General = 0x0002, // General matrix, unknown contents - Translation = 0x0004, // Contains a simple translation - Scale = 0x0008, // Contains a simple scale - Rotation = 0x0010 // Contains a simple rotation + Identity = 0x0000, // Identity matrix + Translation = 0x0001, // Contains a translation + Scale = 0x0002, // Contains a scale + Rotation2D = 0x0004, // Contains a rotation about the Z axis + Rotation = 0x0008, // Contains an arbitrary rotation + Perspective = 0x0010, // Last row is different from (0, 0, 0, 1) + General = 0x001f // General matrix, unknown contents }; // Construct without initializing identity matrix. - QMatrix4x4(int) { flagBits = General; } + QMatrix4x4(int) { } QMatrix4x4 orthonormalInverse() const; @@ -270,6 +275,7 @@ inline qreal& QMatrix4x4::operator()(int aRow, int aColumn) return m[aColumn][aRow]; } +#ifndef QT_NO_VECTOR4D inline QVector4D QMatrix4x4::column(int index) const { Q_ASSERT(index >= 0 && index < 4); @@ -301,6 +307,7 @@ inline void QMatrix4x4::setRow(int index, const QVector4D& value) m[3][index] = value.w(); flagBits = General; } +#endif Q_GUI_EXPORT QMatrix4x4 operator/(const QMatrix4x4& matrix, qreal divisor); @@ -409,15 +416,100 @@ inline QMatrix4x4& QMatrix4x4::operator-=(const QMatrix4x4& other) inline QMatrix4x4& QMatrix4x4::operator*=(const QMatrix4x4& other) { - if (flagBits == Identity) { - *this = other; - return *this; - } else if (other.flagBits == Identity) { - return *this; - } else { - *this = *this * other; + flagBits |= other.flagBits; + + if (flagBits < Rotation2D) { + m[3][0] += m[0][0] * other.m[3][0]; + m[3][1] += m[1][1] * other.m[3][1]; + m[3][2] += m[2][2] * other.m[3][2]; + + m[0][0] *= other.m[0][0]; + m[1][1] *= other.m[1][1]; + m[2][2] *= other.m[2][2]; return *this; } + + qreal m0, m1, m2; + m0 = m[0][0] * other.m[0][0] + + m[1][0] * other.m[0][1] + + m[2][0] * other.m[0][2] + + m[3][0] * other.m[0][3]; + m1 = m[0][0] * other.m[1][0] + + m[1][0] * other.m[1][1] + + m[2][0] * other.m[1][2] + + m[3][0] * other.m[1][3]; + m2 = m[0][0] * other.m[2][0] + + m[1][0] * other.m[2][1] + + m[2][0] * other.m[2][2] + + m[3][0] * other.m[2][3]; + m[3][0] = m[0][0] * other.m[3][0] + + m[1][0] * other.m[3][1] + + m[2][0] * other.m[3][2] + + m[3][0] * other.m[3][3]; + m[0][0] = m0; + m[1][0] = m1; + m[2][0] = m2; + + m0 = m[0][1] * other.m[0][0] + + m[1][1] * other.m[0][1] + + m[2][1] * other.m[0][2] + + m[3][1] * other.m[0][3]; + m1 = m[0][1] * other.m[1][0] + + m[1][1] * other.m[1][1] + + m[2][1] * other.m[1][2] + + m[3][1] * other.m[1][3]; + m2 = m[0][1] * other.m[2][0] + + m[1][1] * other.m[2][1] + + m[2][1] * other.m[2][2] + + m[3][1] * other.m[2][3]; + m[3][1] = m[0][1] * other.m[3][0] + + m[1][1] * other.m[3][1] + + m[2][1] * other.m[3][2] + + m[3][1] * other.m[3][3]; + m[0][1] = m0; + m[1][1] = m1; + m[2][1] = m2; + + m0 = m[0][2] * other.m[0][0] + + m[1][2] * other.m[0][1] + + m[2][2] * other.m[0][2] + + m[3][2] * other.m[0][3]; + m1 = m[0][2] * other.m[1][0] + + m[1][2] * other.m[1][1] + + m[2][2] * other.m[1][2] + + m[3][2] * other.m[1][3]; + m2 = m[0][2] * other.m[2][0] + + m[1][2] * other.m[2][1] + + m[2][2] * other.m[2][2] + + m[3][2] * other.m[2][3]; + m[3][2] = m[0][2] * other.m[3][0] + + m[1][2] * other.m[3][1] + + m[2][2] * other.m[3][2] + + m[3][2] * other.m[3][3]; + m[0][2] = m0; + m[1][2] = m1; + m[2][2] = m2; + + m0 = m[0][3] * other.m[0][0] + + m[1][3] * other.m[0][1] + + m[2][3] * other.m[0][2] + + m[3][3] * other.m[0][3]; + m1 = m[0][3] * other.m[1][0] + + m[1][3] * other.m[1][1] + + m[2][3] * other.m[1][2] + + m[3][3] * other.m[1][3]; + m2 = m[0][3] * other.m[2][0] + + m[1][3] * other.m[2][1] + + m[2][3] * other.m[2][2] + + m[3][3] * other.m[2][3]; + m[3][3] = m[0][3] * other.m[3][0] + + m[1][3] * other.m[3][1] + + m[2][3] * other.m[3][2] + + m[3][3] * other.m[3][3]; + m[0][3] = m0; + m[1][3] = m1; + m[2][3] = m2; + return *this; } inline QMatrix4x4& QMatrix4x4::operator*=(qreal factor) @@ -501,6 +593,7 @@ inline QMatrix4x4 operator+(const QMatrix4x4& m1, const QMatrix4x4& m2) m.m[3][1] = m1.m[3][1] + m2.m[3][1]; m.m[3][2] = m1.m[3][2] + m2.m[3][2]; m.m[3][3] = m1.m[3][3] + m2.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } @@ -523,81 +616,95 @@ inline QMatrix4x4 operator-(const QMatrix4x4& m1, const QMatrix4x4& m2) m.m[3][1] = m1.m[3][1] - m2.m[3][1]; m.m[3][2] = m1.m[3][2] - m2.m[3][2]; m.m[3][3] = m1.m[3][3] - m2.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } inline QMatrix4x4 operator*(const QMatrix4x4& m1, const QMatrix4x4& m2) { - if (m1.flagBits == QMatrix4x4::Identity) - return m2; - else if (m2.flagBits == QMatrix4x4::Identity) - return m1; + int flagBits = m1.flagBits | m2.flagBits; + if (flagBits < QMatrix4x4::Rotation2D) { + QMatrix4x4 m = m1; + m.m[3][0] += m.m[0][0] * m2.m[3][0]; + m.m[3][1] += m.m[1][1] * m2.m[3][1]; + m.m[3][2] += m.m[2][2] * m2.m[3][2]; + + m.m[0][0] *= m2.m[0][0]; + m.m[1][1] *= m2.m[1][1]; + m.m[2][2] *= m2.m[2][2]; + m.flagBits = flagBits; + return m; + } QMatrix4x4 m(1); - m.m[0][0] = m1.m[0][0] * m2.m[0][0] + - m1.m[1][0] * m2.m[0][1] + - m1.m[2][0] * m2.m[0][2] + - m1.m[3][0] * m2.m[0][3]; - m.m[0][1] = m1.m[0][1] * m2.m[0][0] + - m1.m[1][1] * m2.m[0][1] + - m1.m[2][1] * m2.m[0][2] + - m1.m[3][1] * m2.m[0][3]; - m.m[0][2] = m1.m[0][2] * m2.m[0][0] + - m1.m[1][2] * m2.m[0][1] + - m1.m[2][2] * m2.m[0][2] + - m1.m[3][2] * m2.m[0][3]; - m.m[0][3] = m1.m[0][3] * m2.m[0][0] + - m1.m[1][3] * m2.m[0][1] + - m1.m[2][3] * m2.m[0][2] + - m1.m[3][3] * m2.m[0][3]; - m.m[1][0] = m1.m[0][0] * m2.m[1][0] + - m1.m[1][0] * m2.m[1][1] + - m1.m[2][0] * m2.m[1][2] + - m1.m[3][0] * m2.m[1][3]; - m.m[1][1] = m1.m[0][1] * m2.m[1][0] + - m1.m[1][1] * m2.m[1][1] + - m1.m[2][1] * m2.m[1][2] + - m1.m[3][1] * m2.m[1][3]; - m.m[1][2] = m1.m[0][2] * m2.m[1][0] + - m1.m[1][2] * m2.m[1][1] + - m1.m[2][2] * m2.m[1][2] + - m1.m[3][2] * m2.m[1][3]; - m.m[1][3] = m1.m[0][3] * m2.m[1][0] + - m1.m[1][3] * m2.m[1][1] + - m1.m[2][3] * m2.m[1][2] + - m1.m[3][3] * m2.m[1][3]; - m.m[2][0] = m1.m[0][0] * m2.m[2][0] + - m1.m[1][0] * m2.m[2][1] + - m1.m[2][0] * m2.m[2][2] + - m1.m[3][0] * m2.m[2][3]; - m.m[2][1] = m1.m[0][1] * m2.m[2][0] + - m1.m[1][1] * m2.m[2][1] + - m1.m[2][1] * m2.m[2][2] + - m1.m[3][1] * m2.m[2][3]; - m.m[2][2] = m1.m[0][2] * m2.m[2][0] + - m1.m[1][2] * m2.m[2][1] + - m1.m[2][2] * m2.m[2][2] + - m1.m[3][2] * m2.m[2][3]; - m.m[2][3] = m1.m[0][3] * m2.m[2][0] + - m1.m[1][3] * m2.m[2][1] + - m1.m[2][3] * m2.m[2][2] + - m1.m[3][3] * m2.m[2][3]; - m.m[3][0] = m1.m[0][0] * m2.m[3][0] + - m1.m[1][0] * m2.m[3][1] + - m1.m[2][0] * m2.m[3][2] + - m1.m[3][0] * m2.m[3][3]; - m.m[3][1] = m1.m[0][1] * m2.m[3][0] + - m1.m[1][1] * m2.m[3][1] + - m1.m[2][1] * m2.m[3][2] + - m1.m[3][1] * m2.m[3][3]; - m.m[3][2] = m1.m[0][2] * m2.m[3][0] + - m1.m[1][2] * m2.m[3][1] + - m1.m[2][2] * m2.m[3][2] + - m1.m[3][2] * m2.m[3][3]; - m.m[3][3] = m1.m[0][3] * m2.m[3][0] + - m1.m[1][3] * m2.m[3][1] + - m1.m[2][3] * m2.m[3][2] + - m1.m[3][3] * m2.m[3][3]; + m.m[0][0] = m1.m[0][0] * m2.m[0][0] + + m1.m[1][0] * m2.m[0][1] + + m1.m[2][0] * m2.m[0][2] + + m1.m[3][0] * m2.m[0][3]; + m.m[0][1] = m1.m[0][1] * m2.m[0][0] + + m1.m[1][1] * m2.m[0][1] + + m1.m[2][1] * m2.m[0][2] + + m1.m[3][1] * m2.m[0][3]; + m.m[0][2] = m1.m[0][2] * m2.m[0][0] + + m1.m[1][2] * m2.m[0][1] + + m1.m[2][2] * m2.m[0][2] + + m1.m[3][2] * m2.m[0][3]; + m.m[0][3] = m1.m[0][3] * m2.m[0][0] + + m1.m[1][3] * m2.m[0][1] + + m1.m[2][3] * m2.m[0][2] + + m1.m[3][3] * m2.m[0][3]; + + m.m[1][0] = m1.m[0][0] * m2.m[1][0] + + m1.m[1][0] * m2.m[1][1] + + m1.m[2][0] * m2.m[1][2] + + m1.m[3][0] * m2.m[1][3]; + m.m[1][1] = m1.m[0][1] * m2.m[1][0] + + m1.m[1][1] * m2.m[1][1] + + m1.m[2][1] * m2.m[1][2] + + m1.m[3][1] * m2.m[1][3]; + m.m[1][2] = m1.m[0][2] * m2.m[1][0] + + m1.m[1][2] * m2.m[1][1] + + m1.m[2][2] * m2.m[1][2] + + m1.m[3][2] * m2.m[1][3]; + m.m[1][3] = m1.m[0][3] * m2.m[1][0] + + m1.m[1][3] * m2.m[1][1] + + m1.m[2][3] * m2.m[1][2] + + m1.m[3][3] * m2.m[1][3]; + + m.m[2][0] = m1.m[0][0] * m2.m[2][0] + + m1.m[1][0] * m2.m[2][1] + + m1.m[2][0] * m2.m[2][2] + + m1.m[3][0] * m2.m[2][3]; + m.m[2][1] = m1.m[0][1] * m2.m[2][0] + + m1.m[1][1] * m2.m[2][1] + + m1.m[2][1] * m2.m[2][2] + + m1.m[3][1] * m2.m[2][3]; + m.m[2][2] = m1.m[0][2] * m2.m[2][0] + + m1.m[1][2] * m2.m[2][1] + + m1.m[2][2] * m2.m[2][2] + + m1.m[3][2] * m2.m[2][3]; + m.m[2][3] = m1.m[0][3] * m2.m[2][0] + + m1.m[1][3] * m2.m[2][1] + + m1.m[2][3] * m2.m[2][2] + + m1.m[3][3] * m2.m[2][3]; + + m.m[3][0] = m1.m[0][0] * m2.m[3][0] + + m1.m[1][0] * m2.m[3][1] + + m1.m[2][0] * m2.m[3][2] + + m1.m[3][0] * m2.m[3][3]; + m.m[3][1] = m1.m[0][1] * m2.m[3][0] + + m1.m[1][1] * m2.m[3][1] + + m1.m[2][1] * m2.m[3][2] + + m1.m[3][1] * m2.m[3][3]; + m.m[3][2] = m1.m[0][2] * m2.m[3][0] + + m1.m[1][2] * m2.m[3][1] + + m1.m[2][2] * m2.m[3][2] + + m1.m[3][2] * m2.m[3][3]; + m.m[3][3] = m1.m[0][3] * m2.m[3][0] + + m1.m[1][3] * m2.m[3][1] + + m1.m[2][3] * m2.m[3][2] + + m1.m[3][3] * m2.m[3][3]; + m.flagBits = flagBits; return m; } @@ -633,19 +740,16 @@ inline QVector3D operator*(const QMatrix4x4& matrix, const QVector3D& vector) qreal x, y, z, w; if (matrix.flagBits == QMatrix4x4::Identity) { return vector; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QVector3D(vector.x() + matrix.m[3][0], - vector.y() + matrix.m[3][1], - vector.z() + matrix.m[3][2]); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QVector3D(vector.x() * matrix.m[0][0] + matrix.m[3][0], vector.y() * matrix.m[1][1] + matrix.m[3][1], vector.z() * matrix.m[2][2] + matrix.m[3][2]); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QVector3D(vector.x() * matrix.m[0][0], - vector.y() * matrix.m[1][1], - vector.z() * matrix.m[2][2]); + } else if (matrix.flagBits < QMatrix4x4::Rotation) { + // Translation | Scale | Rotation2D + return QVector3D(vector.x() * matrix.m[0][0] + vector.y() * matrix.m[1][0] + matrix.m[3][0], + vector.x() * matrix.m[0][1] + vector.y() * matrix.m[1][1] + matrix.m[3][1], + vector.z() * matrix.m[2][2] + matrix.m[3][2]); } else { x = vector.x() * matrix.m[0][0] + vector.y() * matrix.m[1][0] + @@ -771,16 +875,13 @@ inline QPoint operator*(const QMatrix4x4& matrix, const QPoint& point) yin = point.y(); if (matrix.flagBits == QMatrix4x4::Identity) { return point; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QPoint(qRound(xin + matrix.m[3][0]), - qRound(yin + matrix.m[3][1])); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QPoint(qRound(xin * matrix.m[0][0] + matrix.m[3][0]), qRound(yin * matrix.m[1][1] + matrix.m[3][1])); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QPoint(qRound(xin * matrix.m[0][0]), - qRound(yin * matrix.m[1][1])); + } else if (matrix.flagBits < QMatrix4x4::Perspective) { + return QPoint(qRound(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0]), + qRound(xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1])); } else { x = xin * matrix.m[0][0] + yin * matrix.m[1][0] + @@ -806,16 +907,13 @@ inline QPointF operator*(const QMatrix4x4& matrix, const QPointF& point) yin = point.y(); if (matrix.flagBits == QMatrix4x4::Identity) { return point; - } else if (matrix.flagBits == QMatrix4x4::Translation) { - return QPointF(xin + matrix.m[3][0], - yin + matrix.m[3][1]); - } else if (matrix.flagBits == - (QMatrix4x4::Translation | QMatrix4x4::Scale)) { + } else if (matrix.flagBits < QMatrix4x4::Rotation2D) { + // Translation | Scale return QPointF(xin * matrix.m[0][0] + matrix.m[3][0], yin * matrix.m[1][1] + matrix.m[3][1]); - } else if (matrix.flagBits == QMatrix4x4::Scale) { - return QPointF(xin * matrix.m[0][0], - yin * matrix.m[1][1]); + } else if (matrix.flagBits < QMatrix4x4::Perspective) { + return QPointF(xin * matrix.m[0][0] + yin * matrix.m[1][0] + matrix.m[3][0], + xin * matrix.m[0][1] + yin * matrix.m[1][1] + matrix.m[3][1]); } else { x = xin * matrix.m[0][0] + yin * matrix.m[1][0] + @@ -853,6 +951,7 @@ inline QMatrix4x4 operator-(const QMatrix4x4& matrix) m.m[3][1] = -matrix.m[3][1]; m.m[3][2] = -matrix.m[3][2]; m.m[3][3] = -matrix.m[3][3]; + m.flagBits = QMatrix4x4::General; return m; } @@ -875,6 +974,7 @@ inline QMatrix4x4 operator*(qreal factor, const QMatrix4x4& matrix) m.m[3][1] = matrix.m[3][1] * factor; m.m[3][2] = matrix.m[3][2] * factor; m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QMatrix4x4::General; return m; } @@ -897,6 +997,7 @@ inline QMatrix4x4 operator*(const QMatrix4x4& matrix, qreal factor) m.m[3][1] = matrix.m[3][1] * factor; m.m[3][2] = matrix.m[3][2] * factor; m.m[3][3] = matrix.m[3][3] * factor; + m.flagBits = QMatrix4x4::General; return m; } @@ -939,9 +1040,11 @@ inline QVector3D QMatrix4x4::map(const QVector3D& point) const inline QVector3D QMatrix4x4::mapVector(const QVector3D& vector) const { - if (flagBits == Identity || flagBits == Translation) { + if (flagBits < Scale) { + // Translation return vector; - } else if (flagBits == Scale || flagBits == (Translation | Scale)) { + } else if (flagBits < Rotation2D) { + // Translation | Scale return QVector3D(vector.x() * m[0][0], vector.y() * m[1][1], vector.z() * m[2][2]); diff --git a/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp b/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp index 1b4d770ac6..98a559af85 100644 --- a/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp +++ b/tests/auto/qmatrixnxn/tst_qmatrixnxn.cpp @@ -1834,6 +1834,18 @@ void tst_QMatrixNxN::inverted4x4_data() QTest::newRow("invertible") << (void *)invertible.v << (void *)inverted.v << true; + static Matrix4 const invertible2 = { + {1.0f, 2.0f, 4.0f, 2.0f, + 8.0f, 3.0f, 5.0f, 3.0f, + 6.0f, 7.0f, 9.0f, 4.0f, + 0.0f, 0.0f, 0.0f, 1.0f} + }; + static Matrix4 inverted2; + m4Inverse(invertible2, inverted2); + + QTest::newRow("invertible2") + << (void *)invertible2.v << (void *)inverted2.v << true; + static Matrix4 const translate = { {1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 1.0f, 0.0f, 3.0f, @@ -1907,12 +1919,12 @@ void tst_QMatrixNxN::orthonormalInverse4x4() m2.rotate(45.0, 1.0, 0.0, 0.0); m2.translate(10.0, 0.0, 0.0); - // Use optimize() to drop the internal flags that + // Use operator() to drop the internal flags that // mark the matrix as orthonormal. This will force inverted() // to compute m3.inverted() the long way. We can then compare // the result to what the faster algorithm produces on m2. QMatrix4x4 m3 = m2; - m3.optimize(); + m3(0, 0); bool invertible; QVERIFY(qFuzzyCompare(m2.inverted(&invertible), m3.inverted())); QVERIFY(invertible); @@ -1920,7 +1932,7 @@ void tst_QMatrixNxN::orthonormalInverse4x4() QMatrix4x4 m4; m4.rotate(45.0, 0.0, 1.0, 0.0); QMatrix4x4 m5 = m4; - m5.optimize(); + m5(0, 0); QVERIFY(qFuzzyCompare(m4.inverted(), m5.inverted())); QMatrix4x4 m6; @@ -1928,7 +1940,7 @@ void tst_QMatrixNxN::orthonormalInverse4x4() m1.translate(-20.0, 20.0, 15.0); m1.rotate(25, 1.0, 0.0, 0.0); QMatrix4x4 m7 = m6; - m7.optimize(); + m7(0, 0); QVERIFY(qFuzzyCompare(m6.inverted(), m7.inverted())); } @@ -2449,6 +2461,11 @@ void tst_QMatrixNxN::normalMatrix_data() 0.0f, 7.0f, 0.0f, 5.0f, 0.0f, 0.0f, 9.0f, -3.0f, 0.0f, 0.0f, 0.0f, 1.0f}; + static qreal const rotateValues[16] = + {0.0f, 0.0f, 1.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 1.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f}; static qreal const nullScaleValues1[16] = {0.0f, 0.0f, 0.0f, 4.0f, 0.0f, 7.0f, 0.0f, 5.0f, @@ -2468,6 +2485,7 @@ void tst_QMatrixNxN::normalMatrix_data() QTest::newRow("translate") << (void *)translateValues; QTest::newRow("scale") << (void *)scaleValues; QTest::newRow("both") << (void *)bothValues; + QTest::newRow("rotate") << (void *)rotateValues; QTest::newRow("null scale 1") << (void *)nullScaleValues1; QTest::newRow("null scale 2") << (void *)nullScaleValues2; QTest::newRow("null scale 3") << (void *)nullScaleValues3; @@ -2844,11 +2862,13 @@ void tst_QMatrixNxN::convertGeneric() // Copy of "flagBits" in qmatrix4x4.h. enum { - Identity = 0x0001, // Identity matrix - General = 0x0002, // General matrix, unknown contents - Translation = 0x0004, // Contains a simple translation - Scale = 0x0008, // Contains a simple scale - Rotation = 0x0010 // Contains a simple rotation + Identity = 0x0000, // Identity matrix + Translation = 0x0001, // Contains a translation + Scale = 0x0002, // Contains a scale + Rotation2D = 0x0004, // Contains a rotation about the Z axis + Rotation = 0x0008, // Contains an arbitrary rotation + Perspective = 0x0010, // Last row is different from (0, 0, 0, 1) + General = 0x001f // General matrix, unknown contents }; // Structure that allows direct access to "flagBits" for testing. @@ -2886,17 +2906,73 @@ void tst_QMatrixNxN::optimize_data() 0.0f, 0.0f, 1.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - QTest::newRow("scale") + QTest::newRow("translate") << (void *)translateValues << (int)Translation; - static qreal bothValues[16] = { + static qreal scaleTranslateValues[16] = { 1.0f, 0.0f, 0.0f, 2.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 4.0f, 0.0f, 0.0f, 0.0f, 1.0f }; - QTest::newRow("both") - << (void *)bothValues << (int)(Scale | Translation); + QTest::newRow("scaleTranslate") + << (void *)scaleTranslateValues << (int)(Scale | Translation); + + static qreal rotateValues[16] = { + 0.0f, 1.0f, 0.0f, 0.0f, + -1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("rotate") + << (void *)rotateValues << (int)Rotation2D; + + // Left-handed system, not a simple rotation. + static qreal scaleRotateValues[16] = { + 0.0f, 1.0f, 0.0f, 0.0f, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scaleRotate") + << (void *)scaleRotateValues << (int)(Scale | Rotation2D); + + static qreal matrix2x2Values[16] = { + 1.0f, 2.0f, 0.0f, 0.0f, + 8.0f, 3.0f, 0.0f, 0.0f, + 0.0f, 0.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("matrix2x2") + << (void *)matrix2x2Values << (int)(Scale | Rotation2D); + + static qreal matrix3x3Values[16] = { + 1.0f, 2.0f, 4.0f, 0.0f, + 8.0f, 3.0f, 5.0f, 0.0f, + 6.0f, 7.0f, 9.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("matrix3x3") + << (void *)matrix3x3Values << (int)(Scale | Rotation2D | Rotation); + + static qreal rotateTranslateValues[16] = { + 0.0f, 1.0f, 0.0f, 1.0f, + -1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 0.0f, 1.0f, 3.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("rotateTranslate") + << (void *)rotateTranslateValues << (int)(Translation | Rotation2D); + + // Left-handed system, not a simple rotation. + static qreal scaleRotateTranslateValues[16] = { + 0.0f, 1.0f, 0.0f, 1.0f, + 1.0f, 0.0f, 0.0f, 2.0f, + 0.0f, 0.0f, 1.0f, 3.0f, + 0.0f, 0.0f, 0.0f, 1.0f + }; + QTest::newRow("scaleRotateTranslate") + << (void *)scaleRotateTranslateValues << (int)(Translation | Scale | Rotation2D); static qreal belowValues[16] = { 1.0f, 0.0f, 0.0f, 0.0f, @@ -3240,7 +3316,6 @@ void tst_QMatrixNxN::mapVector() QFETCH(void *, mValues); QMatrix4x4 m1((const qreal *)mValues); - m1.optimize(); QVector3D v(3.5f, -1.0f, 2.5f); @@ -3250,10 +3325,15 @@ void tst_QMatrixNxN::mapVector() v.x() * m1(2, 0) + v.y() * m1(2, 1) + v.z() * m1(2, 2)); QVector3D actual = m1.mapVector(v); + m1.optimize(); + QVector3D actual2 = m1.mapVector(v); QVERIFY(fuzzyCompare(actual.x(), expected.x())); QVERIFY(fuzzyCompare(actual.y(), expected.y())); QVERIFY(fuzzyCompare(actual.z(), expected.z())); + QVERIFY(fuzzyCompare(actual2.x(), expected.x())); + QVERIFY(fuzzyCompare(actual2.y(), expected.y())); + QVERIFY(fuzzyCompare(actual2.z(), expected.z())); } class tst_QMatrixNxN4x4Properties : public QObject -- cgit v1.2.3