/****************************************************************************
**
** Copyright (C) 1999-2004 NVIDIA Corporation.
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt 3D Studio.
**
** $QT_BEGIN_LICENSE:GPL-EXCEPT$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
//==============================================================================
// CONSTRUCTION
//==============================================================================
//==============================================================================
/**
* Empty constructor. Initializes this matrix to the identity matrix.
*/
CMatrix::CMatrix()
{
::memcpy(m, s_Identity, sizeof(m));
}
//==============================================================================
/**
* Copy constructor.
* @param inMatrix is the source matrix to copy
*/
CMatrix::CMatrix(const CMatrix &inMatrix)
{
::memcpy(m, inMatrix.m, sizeof(m));
}
//==============================================================================
/**
* Assignment constructor. Initializes the matrix from an array.
* @param inComponents is an array of 16 values
*/
CMatrix::CMatrix(const float inComponents[])
{
::memcpy(m, inComponents, sizeof(m));
}
//==============================================================================
// INITIALIZATION
//==============================================================================
//==============================================================================
/**
* Sets this matrix to a NULL matrix with all values at zero.
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Zero()
{
::memset(m, 0, sizeof(m));
return *this;
}
//==============================================================================
/**
* Sets the matrix to the identity matrix.
* @return reference to this matrix
*/
CMatrix &CMatrix::Identity()
{
::memcpy(m, s_Identity, sizeof(m));
return *this;
}
//==============================================================================
/**
* Copies the elements from another matrix.
* @param inMatrix is the source matrix
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Set(const CMatrix &inMatrix)
{
::memcpy(m, inMatrix.m, sizeof(m));
return *this;
}
//==============================================================================
/**
* Copies the given components to this matrix.
* @param inComponents is an array of 16 components
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Set(const float inComponents[])
{
::memcpy(m, inComponents, sizeof(m));
return *this;
}
//==============================================================================
/**
* Heavily optimized assignment method. This has the same result as creating
* four full 4x4 matrices and multiplying them together.
* @param inTranslation translation coordinates
* @param inRotation euler angle rotations
* @param inScale scaling dimensions
* @param inPivot pivot offset vector
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Set(const CVector3 &inTranslation, const CEulerAngles &inRotation,
const CVector3 &inScale, const CVector3 &inPivot)
{
// TODO: MF - Since we are bypassing quaternions we have to assert that these angles are Studio
// angles
// assert( CEulerAngles::YXZr == inRotation.m_Order );
// Precalculate the sins and cosins.
float theSinX = ::sin(inRotation.m_Angle.x);
float theSinY = ::sin(inRotation.m_Angle.y);
float theSinZ = ::sin(inRotation.m_Angle.z);
float theCosX = ::cos(inRotation.m_Angle.x);
float theCosY = ::cos(inRotation.m_Angle.y);
float theCosZ = ::cos(inRotation.m_Angle.z);
// Precalulate the scaled rotation 3x3 matrix and populate the rotation section of our 4x4
// matrix
float r1 = inScale.x * ((theCosZ * theCosY) + (theSinX * theSinY * theSinZ));
m[0][0] = r1;
float r4 = inScale.x * (theCosX * theSinZ);
m[0][1] = r4;
float r7 = -inScale.x * ((theSinY * theCosZ) + (theSinZ * theCosY * theSinX));
m[0][2] = r7;
m[0][3] = 0;
float r2 = -inScale.y * ((theSinZ * theCosY) + (theSinX * theSinY * theCosZ));
m[1][0] = r2;
float r5 = inScale.y * (theCosX * theCosZ);
m[1][1] = r5;
float r8 = inScale.y * ((theSinY * theSinZ) + (theSinX * theCosY * theCosZ));
m[1][2] = r8;
m[1][3] = 0;
float r3 = inScale.z * (theSinY * theCosX);
m[2][0] = r3;
float r6 = -inScale.z * (theSinX);
m[2][1] = r6;
float r9 = inScale.z * (theCosX * theCosY);
m[2][2] = r9;
m[2][3] = 0;
// Reuse the scaled rotation matrix in the translation/pivot section of out 4x4 matrix
m[3][0] = inTranslation.x - r1 * inPivot.x - r2 * inPivot.y - r3 * inPivot.z;
m[3][1] = inTranslation.y - r4 * inPivot.x - r5 * inPivot.y - r6 * inPivot.z;
m[3][2] = inTranslation.z - r7 * inPivot.x - r8 * inPivot.y - r9 * inPivot.z;
m[3][3] = 1.0f;
return *this;
}
//==============================================================================
/**
* Sets the translation portion of the matrix without affecting the rest.
* If you want a pure translation matrix you must make sure to start with
* the identity matrix.
* @param inTranslate a translation vector
* @return reference to this modified matrix
*/
CMatrix &CMatrix::SetTranslate(const CVector3 &inTranslate)
{
m[3][0] = inTranslate.x;
m[3][1] = inTranslate.y;
m[3][2] = inTranslate.z;
return *this;
}
//==============================================================================
/**
* Sets the rotation portion of the matrix without affecting the rest.
* FYI: The rotate and scale portions overlap. If you want a pure scale matrix
* you must make sure to start with the identity matrix.
* @param inRotation a quaternion describing the rotaion
* @return reference to this modified matrix
*/
CMatrix &CMatrix::SetRotate(const CQuaternion &inRotation)
{
inRotation.ToMatrix(*this);
return *this;
/* // Precalculate the sin and cos.
float theSinX = (float) sin( inRotation.x );
float theSinY = (float) sin( inRotation.y );
float theSinZ = (float) sin( inRotation.z );
float theCosX = (float) cos( inRotation.x );
float theCosY = (float) cos( inRotation.y );
float theCosZ = (float) cos( inRotation.z );
// Fill out the matrix.
m[0][0] = ( theCosZ * theCosY ) + ( theSinX * theSinY * theSinZ );
m[0][1] = -theCosX * -theSinZ;
m[0][2] = - ( theSinY * theCosZ ) + ( theSinZ * theCosY * theSinX );
m[1][0] = - ( theSinZ * theCosY ) + ( theSinX * theSinY * theCosZ );
m[1][1] = theCosX * theCosZ;
m[1][2] = ( theSinY * theSinZ ) + ( theSinX * theCosY * theCosZ );
m[2][0] = theSinY * theCosX;
m[2][1] = - theSinX;
m[2][2] = theCosX * theCosY;
// Set tranlate portion of matrix.
m[ 3 ][ 0 ] = 0.0f;
m[ 3 ][ 1 ] = 0.0f;
m[ 3 ][ 2 ] = 0.0f;
m[ 3 ][ 3 ] = 1.0f;*/
}
//==============================================================================
/**
* Sets the scale portion of the matrix without affecting the rest.
* FYI: The rotate and scale portions overlap. If you want a pure scale matrix
* you must make sure to start with the identity matrix.
* @param inTranslate a scale vector
* @return reference to this modified matrix
*/
CMatrix &CMatrix::SetScale(const CVector3 &inScale)
{
m[0][0] = inScale.x;
m[1][1] = inScale.y;
m[2][2] = inScale.z;
return *this;
}
//==============================================================================
/**
* Sets the current matrix to describe a perspective matrix that produces
* a perspective projection. This is a DX style 0..1 clipping projection matrix.
*
* n=near, f=far, l=left, r=right, t=top, b=bottom
* A = (r+l)/(r-l)
* B = (t+b)/(t-b)
* C = f/(f-n)
* D = -fn/(f-n)
*
* 2n/(r-l) 0 0 0
* 0 2n/(t-b) 0 0
* A B C 1
* 0 0 D 0
*
* @param inOrthographic true if the matrix should be orthographic, false for
* @return reference to this modified matrix
*/
CMatrix &CMatrix::SetFrustum(const bool inOrthographic, const float inNear, const float inFar,
const float inLeft, const float inTop, const float inRight,
const float inBottom)
{
float theWidth = inRight - inLeft;
float theHeight = inBottom - inTop;
float theClipDist = inFar - inNear;
if (theWidth > 0 && theHeight > 0 && theClipDist > 0) {
float theWidthRecip = 1.0f / theWidth;
float theHeightRecip = 1.0f / theHeight;
float theClipRecip = 1.0f / theClipDist;
Identity();
if (inOrthographic) {
m[0][0] = 2.0f * theWidthRecip;
m[1][1] = 2.0f * theHeightRecip;
m[2][2] = 1.0f * theClipRecip;
m[3][0] = -(inRight + inLeft) * theWidthRecip;
m[3][1] = -(inTop + inBottom) * theHeightRecip;
m[3][2] = (-inFar * theClipRecip) + 1.0f; //-(inFar+inNear) * theClipRecip;
m[3][3] = 1.0f;
} else {
m[0][0] = 2.0f * inNear * theWidthRecip;
m[1][1] = 2.0f * inNear * theHeightRecip;
m[2][0] = -(inRight + inLeft) * theWidthRecip;
m[2][1] = -(inBottom + inTop) * theHeightRecip;
m[2][2] = inFar * theClipRecip; // C
m[2][3] = 1.0f;
m[3][2] = (-inFar * inNear) * theClipRecip; // D
m[3][3] = 0.0f;
}
}
return *this;
}
//==============================================================================
// FUNCTIONS
//==============================================================================
//==============================================================================
/**
* Check this matrix against the identity matrix.
* @return true if this matrix is equivalent to the identity matrix
*/
bool CMatrix::IsIdentity() const
{
return *this == s_Identity;
}
//==============================================================================
/**
* Checks to see if this matrix is affine.
* An affine matrix has the last row being [ 0 0 0 1 ]
* @return true if the matrix is affine
*/
bool CMatrix::IsAffine() const
{
return m[0][3] == 0 && m[1][3] == 0 && m[2][3] == 0 && m[3][3] == 1.0f;
}
//==============================================================================
/**
* Appends the matrix to include a translation. Equivalent to post-multiplying
* this matrix with a transformation matrix derived from the given vector.
* @param inVector is the transformation vector applied
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Translate(const CVector3 &inTranslation)
{
m[3][0] += m[0][0] * inTranslation.x + m[1][0] * inTranslation.y + m[2][0] * inTranslation.z;
m[3][1] += m[0][1] * inTranslation.x + m[1][1] * inTranslation.y + m[2][1] * inTranslation.z;
m[3][2] += m[0][2] * inTranslation.x + m[1][2] * inTranslation.y + m[2][2] * inTranslation.z;
return *this;
}
//==============================================================================
/**
* Appends the matrix to include a rotation. Equivalent to post-multiplying
* this matrix with a rotation matrix derived from the given quaternion.
* @param inRotation is the rotation quaternion applied
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Rotate(const CQuaternion &inRotation)
{
CMatrix theRotation;
return MultiplyAffine(inRotation.ToMatrix(theRotation));
}
//==============================================================================
/**
* Appends the matrix to include scaling. Equivalent to post-multiplying
* this matrix with a scale matrix derived from the given vector.
* @param inVector is the scale vector applied
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Scale(const CVector3 &inScale)
{
m[0][0] *= inScale.x;
m[0][1] *= inScale.x;
m[0][2] *= inScale.x;
m[1][0] *= inScale.y;
m[1][1] *= inScale.y;
m[1][2] *= inScale.y;
m[2][0] *= inScale.z;
m[2][1] *= inScale.z;
m[2][2] *= inScale.z;
return *this;
}
//==============================================================================
/**
* Flips the matrix elements around the identity diagonal.
* @return reference to this modified matrix
*/
CMatrix &CMatrix::Transpose()
{
float theSwap = m[1][0];
m[1][0] = m[0][1];
m[0][1] = theSwap;
theSwap = m[2][0];
m[2][0] = m[0][2];
m[0][2] = theSwap;
theSwap = m[3][0];
m[3][0] = m[0][3];
m[0][3] = theSwap;
theSwap = m[2][1];
m[2][1] = m[1][2];
m[1][2] = theSwap;
theSwap = m[3][1];
m[3][1] = m[1][3];
m[1][3] = theSwap;
theSwap = m[3][2];
m[3][2] = m[2][3];
m[2][3] = theSwap;
return *this;
}
//==============================================================================
/**
* Computes the inverse of a 3D affine matrix; i.e. a matrix with a dimen-
* sionality of 4 where the bottom row has the entries (0, 0, 0, 1).
*
* This procedure treats the 4 by 4 matrix as a block matrix and
* calculates the inverse of one submatrix for a significant performance
* improvement over a general procedure that can invert any non-singular matrix:
* (see code for correct formatting of matrix)
*
* | | -1 | -1 -1 |
* | A C | | A -C A |
* -1 | | | |
* M = | | = | |
* | 0 1 | | 0 1 |
* | | | |
*
* where M is a 4 by 4 matrix,
* A is the 3 by 3 upper left submatrix of M,
* C is the 3 by 1 upper right submatrix of M.
*
* @return the determinant of matrix
*/
float CMatrix::Invert()
{
const float PRECISIONLIMIT(1.0e-07f);
float thePositiveDet(0.0f);
float theNegativeDet(0.0f);
float theTempDet;
// Calculate the determinant of submatrix A and determine if the
// the matrix is singular as limited by the float precision.
theTempDet = m[0][0] * m[1][1] * m[2][2];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
theTempDet = m[0][1] * m[1][2] * m[2][0];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
theTempDet = m[0][2] * m[1][0] * m[2][1];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
theTempDet = -m[0][2] * m[1][1] * m[2][0];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
theTempDet = -m[0][1] * m[1][0] * m[2][2];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
theTempDet = -m[0][0] * m[1][2] * m[2][1];
if (theTempDet >= 0.0f)
thePositiveDet += theTempDet;
else
theNegativeDet += theTempDet;
// Is the submatrix A nonsingular? (i.e. there is an inverse?)
float theDeterminant = thePositiveDet + theNegativeDet;
if (theDeterminant != 0
|| ::abs(theDeterminant / (thePositiveDet - theNegativeDet)) >= PRECISIONLIMIT) {
CMatrix theInverse;
float theInvDeterminant = 1.0f / theDeterminant;
// Calculate inverse(A) = adj(A) / det(A)
theInverse.m[0][0] = (m[1][1] * m[2][2] - m[1][2] * m[2][1]) * theInvDeterminant;
theInverse.m[1][0] = -(m[1][0] * m[2][2] - m[1][2] * m[2][0]) * theInvDeterminant;
theInverse.m[2][0] = (m[1][0] * m[2][1] - m[1][1] * m[2][0]) * theInvDeterminant;
theInverse.m[0][1] = -(m[0][1] * m[2][2] - m[0][2] * m[2][1]) * theInvDeterminant;
theInverse.m[1][1] = (m[0][0] * m[2][2] - m[0][2] * m[2][0]) * theInvDeterminant;
theInverse.m[2][1] = -(m[0][0] * m[2][1] - m[0][1] * m[2][0]) * theInvDeterminant;
theInverse.m[0][2] = (m[0][1] * m[1][2] - m[0][2] * m[1][1]) * theInvDeterminant;
theInverse.m[1][2] = -(m[0][0] * m[1][2] - m[0][2] * m[1][0]) * theInvDeterminant;
theInverse.m[2][2] = (m[0][0] * m[1][1] - m[0][1] * m[1][0]) * theInvDeterminant;
// Calculate -C * inverse(A)
theInverse.m[3][0] = -(m[3][0] * theInverse.m[0][0] + m[3][1] * theInverse.m[1][0]
+ m[3][2] * theInverse.m[2][0]);
theInverse.m[3][1] = -(m[3][0] * theInverse.m[0][1] + m[3][1] * theInverse.m[1][1]
+ m[3][2] * theInverse.m[2][1]);
theInverse.m[3][2] = -(m[3][0] * theInverse.m[0][2] + m[3][1] * theInverse.m[1][2]
+ m[3][2] * theInverse.m[2][2]);
// Assing ourselves to the inverse
*this = theInverse;
}
return theDeterminant;
}
//==============================================================================
/**
* Fast multiplication of two affine 4x4 matrices. No affine pre-check.
* TODO: MF - Convert to SSE Assembly Code
* @return reference to this modified matrix
*/
CMatrix &CMatrix::MultiplyAffine(const CMatrix &inMatrix)
{
float theMult[4][4];
theMult[0][0] =
m[0][0] * inMatrix.m[0][0] + m[0][1] * inMatrix.m[1][0] + m[0][2] * inMatrix.m[2][0];
theMult[0][1] =
m[0][0] * inMatrix.m[0][1] + m[0][1] * inMatrix.m[1][1] + m[0][2] * inMatrix.m[2][1];
theMult[0][2] =
m[0][0] * inMatrix.m[0][2] + m[0][1] * inMatrix.m[1][2] + m[0][2] * inMatrix.m[2][2];
theMult[0][3] = 0;
theMult[1][0] =
m[1][0] * inMatrix.m[0][0] + m[1][1] * inMatrix.m[1][0] + m[1][2] * inMatrix.m[2][0];
theMult[1][1] =
m[1][0] * inMatrix.m[0][1] + m[1][1] * inMatrix.m[1][1] + m[1][2] * inMatrix.m[2][1];
theMult[1][2] =
m[1][0] * inMatrix.m[0][2] + m[1][1] * inMatrix.m[1][2] + m[1][2] * inMatrix.m[2][2];
theMult[1][3] = 0;
theMult[2][0] =
m[2][0] * inMatrix.m[0][0] + m[2][1] * inMatrix.m[1][0] + m[2][2] * inMatrix.m[2][0];
theMult[2][1] =
m[2][0] * inMatrix.m[0][1] + m[2][1] * inMatrix.m[1][1] + m[2][2] * inMatrix.m[2][1];
theMult[2][2] =
m[2][0] * inMatrix.m[0][2] + m[2][1] * inMatrix.m[1][2] + m[2][2] * inMatrix.m[2][2];
theMult[2][3] = 0;
theMult[3][0] = m[3][0] * inMatrix.m[0][0] + m[3][1] * inMatrix.m[1][0]
+ m[3][2] * inMatrix.m[2][0] + inMatrix.m[3][0];
theMult[3][1] = m[3][0] * inMatrix.m[0][1] + m[3][1] * inMatrix.m[1][1]
+ m[3][2] * inMatrix.m[2][1] + inMatrix.m[3][1];
theMult[3][2] = m[3][0] * inMatrix.m[0][2] + m[3][1] * inMatrix.m[1][2]
+ m[3][2] * inMatrix.m[2][2] + inMatrix.m[3][2];
theMult[3][3] = 1.0f;
Set(&theMult[0][0]);
return *this;
}
//==============================================================================
// OPERATORS
//==============================================================================
//==============================================================================
/**
* Simple assignment operator.
* @param inIndex
* @return reference to this modified matrix
*/
CMatrix &CMatrix::operator=(const CMatrix &inMatrix)
{
::memcpy(m, inMatrix.m, sizeof(m));
return *this;
}
//==============================================================================
/**
* Compares this matrix's elements with another matrix's and returns true
* if the matrices are equivalent.
* @param inMatrix is the matrix we are comparing against
* @return true if all elements are within EPSILON of each other.
*/
bool CMatrix::operator==(const CMatrix &inMatrix) const
{
for (int iRow = 0; iRow < 4; ++iRow)
for (int iCol = 0; iCol < 4; ++iCol)
if (::abs(m[iRow][iCol] - inMatrix.m[iRow][iCol]) > EPSILON)
return false;
return true;
}
//==============================================================================
/**
* Compares this matrix's elements with another matrix's and returns true
* if the matrices are not equivalent.
* @param inMatrix is the matrix we are comparing against
* @return true if one or more elements are more than EPSILON from each other
*/
bool CMatrix::operator!=(const CMatrix &inMatrix) const
{
for (int iRow = 0; iRow < 4; ++iRow)
for (int iCol = 0; iCol < 4; ++iCol)
if (::abs(m[iRow][iCol] - inMatrix.m[iRow][iCol]) <= EPSILON)
return false;
return true;
}
//==============================================================================
/**
* Allows direct access to the row. This allows fast direct access in the
* form of CMatrix[row][column].
* @param inRow is the row index, starting at zero
* @return a pointer to the first element
*/
float *CMatrix::operator[](int inRow)
{
return &m[inRow][0];
}
//==============================================================================
/**
* Allows const direct access to the row. This allows fast direct access in the
* form of CMatrix[row][column] from other const code.
* @param inRow is the row index, starting at zero
* @return a const pointer to the first element
*/
const float *CMatrix::operator[](int inRow) const
{
return &m[inRow][0];
}