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 +++++++++++++++++++++++------------------- 1 file changed, 360 insertions(+), 299 deletions(-) (limited to 'src/gui/math3d/qmatrix4x4.cpp') 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 -- cgit v1.2.3