summaryrefslogtreecommitdiffstats
path: root/src/system/Qt3DSMatrix.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/system/Qt3DSMatrix.cpp')
-rw-r--r--src/system/Qt3DSMatrix.cpp897
1 files changed, 897 insertions, 0 deletions
diff --git a/src/system/Qt3DSMatrix.cpp b/src/system/Qt3DSMatrix.cpp
new file mode 100644
index 0000000..b5310d0
--- /dev/null
+++ b/src/system/Qt3DSMatrix.cpp
@@ -0,0 +1,897 @@
+/****************************************************************************
+**
+** Copyright (C) 1993-2009 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$
+** 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 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** 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$
+**
+****************************************************************************/
+
+#include "SystemPrefix.h"
+
+//==============================================================================
+// Includes
+//==============================================================================
+#include "Qt3DSMatrix.h"
+#include "Qt3DSVector3.h"
+#include "Qt3DSEulerAngles.h"
+#include "Qt3DSDataLogger.h"
+#include <math.h>
+
+//==============================================================================
+// Namespace
+//==============================================================================
+namespace Q3DStudio {
+
+//==============================================================================
+// Constants
+//==============================================================================
+FLOAT g_IdentityInit[4][4] = { { 1.0f, 0.0f, 0.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f, 0.0f },
+ { 0.0f, 0.0f, 1.0f, 0.0f },
+ { 0.0f, 0.0f, 0.0f, 1.0f } };
+
+const RuntimeMatrix RuntimeMatrix::IDENTITY = RuntimeMatrix(g_IdentityInit);
+extern const FLOAT RUNTIME_EPSILON;
+
+//==============================================================================
+/**
+ * Empty constructor.
+ * Initializes this matrix to the identity matrix.
+ */
+RuntimeMatrix::RuntimeMatrix(const BOOL inInitializeIdentity /*= true*/)
+{
+ if (inInitializeIdentity)
+ Q3DStudio_memcpy(&m_Data, &IDENTITY, sizeof(m_Data));
+}
+
+//==============================================================================
+/**
+ * Copy constructor.
+ * @param inMatrix the source matrix to copy
+ */
+RuntimeMatrix::RuntimeMatrix(const RuntimeMatrix &inMatrix)
+{
+ Q3DStudio_memcpy(&m_Data, &inMatrix.m_Data, sizeof(m_Data));
+}
+
+//==============================================================================
+/**
+ * Assignment constructor.
+ * Initializes the matrix from an array.
+ * @param inComponents an array of 16 values
+ */
+RuntimeMatrix::RuntimeMatrix(const FLOAT inComponents[4][4])
+{
+ if (inComponents)
+ Q3DStudio_memcpy(&m_Data, inComponents, sizeof(m_Data));
+}
+
+//==============================================================================
+/**
+ * Sets this matrix to a NULL matrix with all values at zero.
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Zero()
+{
+ Q3DStudio_memset(&m_Data, 0, sizeof(m_Data));
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Sets the matrix to the identity matrix.
+ * @return reference to this matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Identity()
+{
+ Q3DStudio_memcpy(&m_Data, &IDENTITY, sizeof(m_Data));
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Copies the elements from another matrix.
+ * @param inMatrix the source matrix
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Set(const RuntimeMatrix &inMatrix)
+{
+ Q3DStudio_memcpy(&m_Data, &inMatrix.m_Data, sizeof(m_Data));
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Copies the given components to this matrix.
+ * @param inComponents an array of 16 components
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Set(const FLOAT inComponents[4][4])
+{
+ if (inComponents)
+ Q3DStudio_memcpy(&m_Data, inComponents, sizeof(m_Data));
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Heavily optimized assignment method. This has the same result as creating
+ * four full 4x4 matrices and multiplying them together.
+ * @todo Look into optimized code.
+ * @param inTranslation translation coordinates
+ * @param inRotation Euler angle rotations
+ * @param inScale scaling dimensions
+ * @param inPivot pivot offset vector
+ * @param inRotationOrder rotation order
+ * @param inCoordinateSystem the coordindate system
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Set(const RuntimeVector3 &inTranslation, const RuntimeVector3 &inRotation,
+ const RuntimeVector3 &inScale, const RuntimeVector3 &inPivot, const UINT8 inRotationOrder,
+ const UINT8 inCoordinateSystem)
+{
+ // *this = m_ScaleMatrix * m_PivotMatrix * m_RotMatrix * m_TransMatrix; // Distributed
+ //original - plain speak
+
+ RuntimeVector3 theScaledPivot;
+ theScaledPivot.m_X = -inPivot.m_X * inScale.m_X;
+ theScaledPivot.m_Y = -inPivot.m_Y * inScale.m_Y;
+ theScaledPivot.m_Z = -inPivot.m_Z * inScale.m_Z;
+
+ // This would take care of rotation in the right order
+ RuntimeMatrix theRotation;
+ theRotation.SetRotate(inRotation, inRotationOrder, inCoordinateSystem);
+
+ Q3DStudio_memcpy(&m_Data, &IDENTITY, sizeof(m_Data));
+ m_Data[0][0] = inScale.m_X;
+ m_Data[1][1] = inScale.m_Y;
+ m_Data[2][2] = inScale.m_Z;
+
+ m_Data[3][0] = theScaledPivot.m_X;
+ m_Data[3][1] = theScaledPivot.m_Y;
+
+ if (inCoordinateSystem == ORIENTATION_LEFT_HANDED)
+ m_Data[3][2] = theScaledPivot.m_Z;
+ else
+ m_Data[3][2] = -theScaledPivot.m_Z;
+
+ MultiplyAffine(theRotation); // This could be optimized to only 9 multiplications instead of 16
+ // if you do them by hand
+
+ m_Data[3][0] += inTranslation.m_X;
+ m_Data[3][1] += inTranslation.m_Y;
+
+ if (inCoordinateSystem == ORIENTATION_LEFT_HANDED)
+ m_Data[3][2] += inTranslation.m_Z;
+ else
+ m_Data[3][2] += -inTranslation.m_Z;
+
+ if (inCoordinateSystem == ORIENTATION_LEFT_HANDED)
+ return FlipCoordinateSystem();
+
+ 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
+ */
+RuntimeMatrix &RuntimeMatrix::SetTranslate(const RuntimeVector3 &inTranslate)
+{
+ m_Data[3][0] = inTranslate.m_X;
+ m_Data[3][1] = inTranslate.m_Y;
+ m_Data[3][2] = inTranslate.m_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 rotation
+ * @return reference to this modified matrix
+ */
+// CMatrix& CMatrix::SetRotate( const CQuaternion& inRotation )
+//{
+// inRotation.ToMatrix( *this );
+// 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 rotation matrix
+ * you must make sure to start with the identity matrix.
+ * @param inRotation Euler angle rotation
+ * @param inRotationOrder rotation order
+ * @param inCoordinateSystem the coordindate system
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::SetRotate(const RuntimeVector3 &inRotation, const UINT8 inRotationOrder,
+ const UINT8 inCoordinateSystem)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+ Q3DStudio_UNREFERENCED_PARAMETER(inCoordinateSystem);
+
+ EulerAngles theEulerAngles;
+ switch (inRotationOrder) {
+ case ROTATIONORDER_XYZ:
+ theEulerAngles.x = inRotation.m_X;
+ theEulerAngles.y = inRotation.m_Y;
+ theEulerAngles.z = inRotation.m_Z;
+ theEulerAngles.w = EulOrdXYZs;
+ break;
+ case ROTATIONORDER_XYZR:
+ theEulerAngles.x = inRotation.m_X;
+ theEulerAngles.y = inRotation.m_Y;
+ theEulerAngles.z = inRotation.m_Z;
+ theEulerAngles.w = EulOrdXYZr;
+ break;
+ case ROTATIONORDER_YZX:
+ theEulerAngles.x = inRotation.m_Y;
+ theEulerAngles.y = inRotation.m_Z;
+ theEulerAngles.z = inRotation.m_X;
+ theEulerAngles.w = EulOrdYZXs;
+ break;
+ case ROTATIONORDER_YZXR:
+ theEulerAngles.x = inRotation.m_Y;
+ theEulerAngles.y = inRotation.m_Z;
+ theEulerAngles.z = inRotation.m_X;
+ theEulerAngles.w = EulOrdYZXr;
+ break;
+ case ROTATIONORDER_ZXY:
+ theEulerAngles.x = inRotation.m_Z;
+ theEulerAngles.y = inRotation.m_X;
+ theEulerAngles.z = inRotation.m_Y;
+ theEulerAngles.w = EulOrdZXYs;
+ break;
+ case ROTATIONORDER_ZXYR:
+ theEulerAngles.x = inRotation.m_Z;
+ theEulerAngles.y = inRotation.m_X;
+ theEulerAngles.z = inRotation.m_Y;
+ theEulerAngles.w = EulOrdZXYr;
+ break;
+ case ROTATIONORDER_XZY:
+ theEulerAngles.x = inRotation.m_X;
+ theEulerAngles.y = inRotation.m_Z;
+ theEulerAngles.z = inRotation.m_Y;
+ theEulerAngles.w = EulOrdXZYs;
+ break;
+ case ROTATIONORDER_XZYR:
+ theEulerAngles.x = inRotation.m_X;
+ theEulerAngles.y = inRotation.m_Z;
+ theEulerAngles.z = inRotation.m_Y;
+ theEulerAngles.w = EulOrdXZYr;
+ break;
+ case ROTATIONORDER_YXZ:
+ theEulerAngles.x = inRotation.m_Y;
+ theEulerAngles.y = inRotation.m_X;
+ theEulerAngles.z = inRotation.m_Z;
+ theEulerAngles.w = EulOrdYXZs;
+ break;
+ case ROTATIONORDER_YXZR:
+ theEulerAngles.x = inRotation.m_Y;
+ theEulerAngles.y = inRotation.m_X;
+ theEulerAngles.z = inRotation.m_Z;
+ theEulerAngles.w = EulOrdYXZr;
+ break;
+ case ROTATIONORDER_ZYX:
+ theEulerAngles.x = inRotation.m_Z;
+ theEulerAngles.y = inRotation.m_Y;
+ theEulerAngles.z = inRotation.m_X;
+ theEulerAngles.w = EulOrdZYXs;
+ break;
+ case ROTATIONORDER_ZYXR:
+ theEulerAngles.x = inRotation.m_Z;
+ theEulerAngles.y = inRotation.m_Y;
+ theEulerAngles.z = inRotation.m_X;
+ theEulerAngles.w = EulOrdZYXr;
+ break;
+ default: // defaults to Studio's rotation type
+ theEulerAngles.x = inRotation.m_Y;
+ theEulerAngles.y = inRotation.m_X;
+ theEulerAngles.z = inRotation.m_Z;
+ theEulerAngles.w = EulOrdYXZs;
+ break;
+ }
+
+ theEulerAngles.x *= -1;
+ theEulerAngles.y *= -1;
+ theEulerAngles.z *= -1;
+
+ CEulerAngleConverter theConverter;
+ theConverter.Eul_ToHMatrix(theEulerAngles, m_Data);
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * 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 inScale scale vector
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::SetScale(const RuntimeVector3 &inScale)
+{
+ m_Data[0][0] = inScale.m_X;
+ m_Data[1][1] = inScale.m_Y;
+ m_Data[2][2] = inScale.m_Z;
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Check this matrix against the identity matrix.
+ * @return true if this matrix is equivalent to the identity matrix
+ */
+BOOL RuntimeMatrix::IsIdentity() const
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ return *this == 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 RuntimeMatrix::IsAffine() const
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ return m_Data[0][3] == 0 && m_Data[1][3] == 0 && m_Data[2][3] == 0 && m_Data[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 inTranslation transformation vector applied
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Translate(const RuntimeVector3 &inTranslation)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ m_Data[3][0] += inTranslation.m_X;
+ m_Data[3][1] += inTranslation.m_Y;
+ m_Data[3][2] += inTranslation.m_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 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 inScale the scale vector applied
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Scale(const RuntimeVector3 &inScale)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ m_Data[0][0] *= inScale.m_X;
+ m_Data[0][1] *= inScale.m_X;
+ m_Data[0][2] *= inScale.m_X;
+
+ m_Data[1][0] *= inScale.m_Y;
+ m_Data[1][1] *= inScale.m_Y;
+ m_Data[1][2] *= inScale.m_Y;
+
+ m_Data[2][0] *= inScale.m_Z;
+ m_Data[2][1] *= inScale.m_Z;
+ m_Data[2][2] *= inScale.m_Z;
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Flips the matrix elements around the identity diagonal.
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::Transpose()
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ FLOAT theSwap = m_Data[1][0];
+ m_Data[1][0] = m_Data[0][1];
+ m_Data[0][1] = theSwap;
+
+ theSwap = m_Data[2][0];
+ m_Data[2][0] = m_Data[0][2];
+ m_Data[0][2] = theSwap;
+
+ theSwap = m_Data[3][0];
+ m_Data[3][0] = m_Data[0][3];
+ m_Data[0][3] = theSwap;
+
+ theSwap = m_Data[2][1];
+ m_Data[2][1] = m_Data[1][2];
+ m_Data[1][2] = theSwap;
+
+ theSwap = m_Data[3][1];
+ m_Data[3][1] = m_Data[1][3];
+ m_Data[1][3] = theSwap;
+
+ theSwap = m_Data[3][2];
+ m_Data[3][2] = m_Data[2][3];
+ m_Data[2][3] = theSwap;
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Compute the inverse of a 3D affine matrix; i.e. a matrix with a
+ * dimensionality 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:
+@code
+ | | -1 | -1 -1 |
+ | A C | | A -C A |
+ -1 | | | |
+ M = | | = | |
+ | 0 1 | | 0 1 |
+ | | | |
+@endcode
+ * 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 RuntimeMatrix::Invert()
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ 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_Data[0][0] * m_Data[1][1] * m_Data[2][2];
+ if (theTempDet >= 0.0f)
+ thePositiveDet += theTempDet;
+ else
+ theNegativeDet += theTempDet;
+
+ theTempDet = m_Data[0][1] * m_Data[1][2] * m_Data[2][0];
+ if (theTempDet >= 0.0f)
+ thePositiveDet += theTempDet;
+ else
+ theNegativeDet += theTempDet;
+
+ theTempDet = m_Data[0][2] * m_Data[1][0] * m_Data[2][1];
+ if (theTempDet >= 0.0f)
+ thePositiveDet += theTempDet;
+ else
+ theNegativeDet += theTempDet;
+
+ theTempDet = -m_Data[0][2] * m_Data[1][1] * m_Data[2][0];
+ if (theTempDet >= 0.0f)
+ thePositiveDet += theTempDet;
+ else
+ theNegativeDet += theTempDet;
+
+ theTempDet = -m_Data[0][1] * m_Data[1][0] * m_Data[2][2];
+ if (theTempDet >= 0.0f)
+ thePositiveDet += theTempDet;
+ else
+ theNegativeDet += theTempDet;
+
+ theTempDet = -m_Data[0][0] * m_Data[1][2] * m_Data[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
+ || ::fabs(theDeterminant / (thePositiveDet - theNegativeDet)) >= PRECISIONLIMIT) {
+ RuntimeMatrix theInverse;
+ FLOAT theInvDeterminant = 1.0f / theDeterminant;
+
+ // Calculate inverse(A) = adj(A) / det(A)
+ theInverse.m_Data[0][0] =
+ (m_Data[1][1] * m_Data[2][2] - m_Data[1][2] * m_Data[2][1]) * theInvDeterminant;
+ theInverse.m_Data[1][0] =
+ -(m_Data[1][0] * m_Data[2][2] - m_Data[1][2] * m_Data[2][0]) * theInvDeterminant;
+ theInverse.m_Data[2][0] =
+ (m_Data[1][0] * m_Data[2][1] - m_Data[1][1] * m_Data[2][0]) * theInvDeterminant;
+ theInverse.m_Data[0][1] =
+ -(m_Data[0][1] * m_Data[2][2] - m_Data[0][2] * m_Data[2][1]) * theInvDeterminant;
+ theInverse.m_Data[1][1] =
+ (m_Data[0][0] * m_Data[2][2] - m_Data[0][2] * m_Data[2][0]) * theInvDeterminant;
+ theInverse.m_Data[2][1] =
+ -(m_Data[0][0] * m_Data[2][1] - m_Data[0][1] * m_Data[2][0]) * theInvDeterminant;
+ theInverse.m_Data[0][2] =
+ (m_Data[0][1] * m_Data[1][2] - m_Data[0][2] * m_Data[1][1]) * theInvDeterminant;
+ theInverse.m_Data[1][2] =
+ -(m_Data[0][0] * m_Data[1][2] - m_Data[0][2] * m_Data[1][0]) * theInvDeterminant;
+ theInverse.m_Data[2][2] =
+ (m_Data[0][0] * m_Data[1][1] - m_Data[0][1] * m_Data[1][0]) * theInvDeterminant;
+
+ // Calculate -C * inverse(A)
+ theInverse.m_Data[3][0] =
+ -(m_Data[3][0] * theInverse.m_Data[0][0] + m_Data[3][1] * theInverse.m_Data[1][0]
+ + m_Data[3][2] * theInverse.m_Data[2][0]);
+ theInverse.m_Data[3][1] =
+ -(m_Data[3][0] * theInverse.m_Data[0][1] + m_Data[3][1] * theInverse.m_Data[1][1]
+ + m_Data[3][2] * theInverse.m_Data[2][1]);
+ theInverse.m_Data[3][2] =
+ -(m_Data[3][0] * theInverse.m_Data[0][2] + m_Data[3][1] * theInverse.m_Data[1][2]
+ + m_Data[3][2] * theInverse.m_Data[2][2]);
+
+ // assign 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
+ * @param inMatrix the source matrix
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::MultiplyAffine(const RuntimeMatrix &inMatrix)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ FLOAT theMult[4][4];
+
+ theMult[0][0] = m_Data[0][0] * inMatrix.m_Data[0][0] + m_Data[0][1] * inMatrix.m_Data[1][0]
+ + m_Data[0][2] * inMatrix.m_Data[2][0];
+ theMult[0][1] = m_Data[0][0] * inMatrix.m_Data[0][1] + m_Data[0][1] * inMatrix.m_Data[1][1]
+ + m_Data[0][2] * inMatrix.m_Data[2][1];
+ theMult[0][2] = m_Data[0][0] * inMatrix.m_Data[0][2] + m_Data[0][1] * inMatrix.m_Data[1][2]
+ + m_Data[0][2] * inMatrix.m_Data[2][2];
+ theMult[0][3] = 0;
+
+ theMult[1][0] = m_Data[1][0] * inMatrix.m_Data[0][0] + m_Data[1][1] * inMatrix.m_Data[1][0]
+ + m_Data[1][2] * inMatrix.m_Data[2][0];
+ theMult[1][1] = m_Data[1][0] * inMatrix.m_Data[0][1] + m_Data[1][1] * inMatrix.m_Data[1][1]
+ + m_Data[1][2] * inMatrix.m_Data[2][1];
+ theMult[1][2] = m_Data[1][0] * inMatrix.m_Data[0][2] + m_Data[1][1] * inMatrix.m_Data[1][2]
+ + m_Data[1][2] * inMatrix.m_Data[2][2];
+ theMult[1][3] = 0;
+
+ theMult[2][0] = m_Data[2][0] * inMatrix.m_Data[0][0] + m_Data[2][1] * inMatrix.m_Data[1][0]
+ + m_Data[2][2] * inMatrix.m_Data[2][0];
+ theMult[2][1] = m_Data[2][0] * inMatrix.m_Data[0][1] + m_Data[2][1] * inMatrix.m_Data[1][1]
+ + m_Data[2][2] * inMatrix.m_Data[2][1];
+ theMult[2][2] = m_Data[2][0] * inMatrix.m_Data[0][2] + m_Data[2][1] * inMatrix.m_Data[1][2]
+ + m_Data[2][2] * inMatrix.m_Data[2][2];
+ theMult[2][3] = 0;
+
+ theMult[3][0] = m_Data[3][0] * inMatrix.m_Data[0][0] + m_Data[3][1] * inMatrix.m_Data[1][0]
+ + m_Data[3][2] * inMatrix.m_Data[2][0] + inMatrix.m_Data[3][0];
+ theMult[3][1] = m_Data[3][0] * inMatrix.m_Data[0][1] + m_Data[3][1] * inMatrix.m_Data[1][1]
+ + m_Data[3][2] * inMatrix.m_Data[2][1] + inMatrix.m_Data[3][1];
+ theMult[3][2] = m_Data[3][0] * inMatrix.m_Data[0][2] + m_Data[3][1] * inMatrix.m_Data[1][2]
+ + m_Data[3][2] * inMatrix.m_Data[2][2] + inMatrix.m_Data[3][2];
+ theMult[3][3] = 1.0f;
+
+ return Set(theMult);
+}
+
+//==============================================================================
+/**
+ * Standard matrix multiplication
+ * @todo MF - Convert to SSE Assembly Code
+ * @param inMatrix matrix to multiply with
+ */
+RuntimeMatrix &RuntimeMatrix::Multiply(const RuntimeMatrix &inMatrix)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ FLOAT theMult[4][4];
+
+ theMult[0][0] = m_Data[0][0] * inMatrix.m_Data[0][0] + m_Data[1][0] * inMatrix.m_Data[0][1]
+ + m_Data[2][0] * inMatrix.m_Data[0][2] + m_Data[3][0] * inMatrix.m_Data[0][3];
+ theMult[0][1] = m_Data[0][1] * inMatrix.m_Data[0][0] + m_Data[1][1] * inMatrix.m_Data[0][1]
+ + m_Data[2][1] * inMatrix.m_Data[0][2] + m_Data[3][1] * inMatrix.m_Data[0][3];
+ theMult[0][2] = m_Data[0][2] * inMatrix.m_Data[0][0] + m_Data[1][2] * inMatrix.m_Data[0][1]
+ + m_Data[2][2] * inMatrix.m_Data[0][2] + m_Data[3][2] * inMatrix.m_Data[0][3];
+ theMult[0][3] = m_Data[0][3] * inMatrix.m_Data[0][0] + m_Data[1][3] * inMatrix.m_Data[0][1]
+ + m_Data[2][3] * inMatrix.m_Data[0][2] + m_Data[3][3] * inMatrix.m_Data[0][3];
+ theMult[1][0] = m_Data[0][0] * inMatrix.m_Data[1][0] + m_Data[1][0] * inMatrix.m_Data[1][1]
+ + m_Data[2][0] * inMatrix.m_Data[1][2] + m_Data[3][0] * inMatrix.m_Data[1][3];
+ theMult[1][1] = m_Data[0][1] * inMatrix.m_Data[1][0] + m_Data[1][1] * inMatrix.m_Data[1][1]
+ + m_Data[2][1] * inMatrix.m_Data[1][2] + m_Data[3][1] * inMatrix.m_Data[1][3];
+ theMult[1][2] = m_Data[0][2] * inMatrix.m_Data[1][0] + m_Data[1][2] * inMatrix.m_Data[1][1]
+ + m_Data[2][2] * inMatrix.m_Data[1][2] + m_Data[3][2] * inMatrix.m_Data[1][3];
+ theMult[1][3] = m_Data[0][3] * inMatrix.m_Data[1][0] + m_Data[1][3] * inMatrix.m_Data[1][1]
+ + m_Data[2][3] * inMatrix.m_Data[1][2] + m_Data[3][3] * inMatrix.m_Data[1][3];
+ theMult[2][0] = m_Data[0][0] * inMatrix.m_Data[2][0] + m_Data[1][0] * inMatrix.m_Data[2][1]
+ + m_Data[2][0] * inMatrix.m_Data[2][2] + m_Data[3][0] * inMatrix.m_Data[2][3];
+ theMult[2][1] = m_Data[0][1] * inMatrix.m_Data[2][0] + m_Data[1][1] * inMatrix.m_Data[2][1]
+ + m_Data[2][1] * inMatrix.m_Data[2][2] + m_Data[3][1] * inMatrix.m_Data[2][3];
+ theMult[2][2] = m_Data[0][2] * inMatrix.m_Data[2][0] + m_Data[1][2] * inMatrix.m_Data[2][1]
+ + m_Data[2][2] * inMatrix.m_Data[2][2] + m_Data[3][2] * inMatrix.m_Data[2][3];
+ theMult[2][3] = m_Data[0][3] * inMatrix.m_Data[2][0] + m_Data[1][3] * inMatrix.m_Data[2][1]
+ + m_Data[2][3] * inMatrix.m_Data[2][2] + m_Data[3][3] * inMatrix.m_Data[2][3];
+
+ theMult[3][0] = m_Data[0][0] * inMatrix.m_Data[3][0] + m_Data[1][0] * inMatrix.m_Data[3][1]
+ + m_Data[2][0] * inMatrix.m_Data[3][2] + m_Data[3][0] * inMatrix.m_Data[3][3];
+ theMult[3][1] = m_Data[0][1] * inMatrix.m_Data[3][0] + m_Data[1][1] * inMatrix.m_Data[3][1]
+ + m_Data[2][1] * inMatrix.m_Data[3][2] + m_Data[3][1] * inMatrix.m_Data[3][3];
+ theMult[3][2] = m_Data[0][2] * inMatrix.m_Data[3][0] + m_Data[1][2] * inMatrix.m_Data[3][1]
+ + m_Data[2][2] * inMatrix.m_Data[3][2] + m_Data[3][2] * inMatrix.m_Data[3][3];
+ theMult[3][3] = m_Data[0][3] * inMatrix.m_Data[3][0] + m_Data[1][3] * inMatrix.m_Data[3][1]
+ + m_Data[2][3] * inMatrix.m_Data[3][2] + m_Data[3][3] * inMatrix.m_Data[3][3];
+
+ return Set(theMult);
+}
+
+//==============================================================================
+/**
+ * Toggle between left-hand and right-hand coordinate system
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::FlipCoordinateSystem()
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ // rotation conversion
+ m_Data[0][2] *= -1;
+ m_Data[1][2] *= -1;
+ m_Data[2][0] *= -1;
+ m_Data[2][1] *= -1;
+
+ // translation conversion
+ m_Data[3][2] *= -1;
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Rotate this matrix to align with the rotation of the specified matrix.
+ *
+ * @param inMatrix the maxtrix we are cloning.
+ * @param inMirrorFlag flag indicating that we should flip the z and face the opposite
+ *direction.
+ * @return This matrix after the rotation.
+ */
+RuntimeMatrix &RuntimeMatrix::CloneRotation(const RuntimeMatrix &inMatrix, BOOL inMirrorFlag /*= false*/)
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ // Create the axes
+ RuntimeVector3 theZ(inMatrix.Get(2, 0), inMatrix.Get(2, 1), inMatrix.Get(2, 2));
+ if (inMirrorFlag)
+ theZ *= -1;
+ RuntimeVector3 theY(inMatrix.Get(1, 0), inMatrix.Get(1, 1), inMatrix.Get(1, 2));
+ RuntimeVector3 theX(theY);
+ theX.CrossProduct(theZ);
+
+ // Normalize
+ theX.Normalize();
+ theY.Normalize();
+ theZ.Normalize();
+
+ // Copy it into the matrix
+ m_Data[0][0] = theX.m_X;
+ m_Data[0][1] = theX.m_Y;
+ m_Data[0][2] = theX.m_Z;
+
+ m_Data[1][0] = theY.m_X;
+ m_Data[1][1] = theY.m_Y;
+ m_Data[1][2] = theY.m_Z;
+
+ m_Data[2][0] = theZ.m_X;
+ m_Data[2][1] = theZ.m_Y;
+ m_Data[2][2] = theZ.m_Z;
+
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Compute the square distance between the position vectors of two transforms.
+ * @param inMatrix the other transform, signifying a global object position for example
+ * @return the square of the "distance" between the two transforms
+ */
+FLOAT RuntimeMatrix::SquareDistance(const RuntimeMatrix &inMatrix) const
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ FLOAT theResult = 0;
+ FLOAT theFactor;
+
+ theFactor = m_Data[3][0] - inMatrix.m_Data[3][0];
+ theFactor *= theFactor;
+ theResult += theFactor;
+
+ theFactor = m_Data[3][1] - inMatrix.m_Data[3][1];
+ theFactor *= theFactor;
+ theResult += theFactor;
+
+ theFactor = m_Data[3][2] - inMatrix.m_Data[3][2];
+ theFactor *= theFactor;
+ theResult += theFactor;
+
+ return theResult;
+}
+
+//==============================================================================
+/**
+ * Simple assignment operator.
+ * @param inMatrix new matrix being assigned
+ * @return reference to this modified matrix
+ */
+RuntimeMatrix &RuntimeMatrix::operator=(const RuntimeMatrix &inMatrix)
+{
+ Q3DStudio_memcpy(&m_Data, &inMatrix.m_Data, sizeof(m_Data));
+ return *this;
+}
+
+//==============================================================================
+/**
+ * Compares this matrix's elements with another matrix's and returns true
+ * if the matrices are equivalent.
+ * @param inMatrix the matrix we are comparing against
+ * @return true if all elements are within EPSILON of each other.
+ */
+BOOL RuntimeMatrix::operator==(const RuntimeMatrix &inMatrix) const
+{
+ PerfLogMathEvent1(DATALOGGER_MATRIX);
+
+ for (INT32 iRow = 0; iRow < 4; ++iRow) {
+ for (INT32 iCol = 0; iCol < 4; ++iCol) {
+ if (::fabs(m_Data[iRow][iCol] - inMatrix.m_Data[iRow][iCol]) > RUNTIME_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 the matrix we are comparing against
+ * @return true if one or more elements are more than EPSILON from each other
+ */
+BOOL RuntimeMatrix::operator!=(const RuntimeMatrix &inMatrix) const
+{
+ // Reuse same code path..
+ return !(*this == inMatrix);
+}
+
+//==============================================================================
+/**
+ * Standardized conversion to string.
+ * @param outString string becoming a representation for the matrix
+ */
+// void CMatrix::ToString( AK_STRING& outString ) const
+//{
+// INT8 theBuffer[ 256 ];
+//
+// Q3DStudio_sprintf
+// (
+// theBuffer, 255,
+// "%.2f %.2f %.2f %.2f "
+// "%.2f %.2f %.2f %.2f "
+// "%.2f %.2f %.2f %.2f "
+// "%.2f %.2f %.2f %.2f",
+//
+// m_Data.m[ 0 ][ 0 ],
+// m_Data.m[ 0 ][ 1 ],
+// m_Data.m[ 0 ][ 2 ],
+// m_Data.m[ 0 ][ 3 ],
+//
+// m_Data.m[ 1 ][ 0 ],
+// m_Data.m[ 1 ][ 1 ],
+// m_Data.m[ 1 ][ 2 ],
+// m_Data.m[ 1 ][ 3 ],
+//
+// m_Data.m[ 2 ][ 0 ],
+// m_Data.m[ 2 ][ 1 ],
+// m_Data.m[ 2 ][ 2 ],
+// m_Data.m[ 2 ][ 3 ],
+//
+// m_Data.m[ 3 ][ 0 ],
+// m_Data.m[ 3 ][ 1 ],
+// m_Data.m[ 3 ][ 2 ],
+// m_Data.m[ 3 ][ 3 ]
+// );
+//
+// outString = theBuffer;
+//}
+//
+////==============================================================================
+///**
+// * Standardized conversion from string.
+// * @param inString string being a representation for the matrix
+// */
+// void CMatrix::FromString( const AK_STRING& inString )
+//{
+// std::sscanf
+// (
+// inString.c_str( ),
+//
+// "%f %f %f %f "
+// "%f %f %f %f "
+// "%f %f %f %f "
+// "%f %f %f %f",
+//
+// &m_Data.m[ 0 ][ 0 ],
+// &m_Data.m[ 0 ][ 1 ],
+// &m_Data.m[ 0 ][ 2 ],
+// &m_Data.m[ 0 ][ 3 ],
+//
+// &m_Data.m[ 1 ][ 0 ],
+// &m_Data.m[ 1 ][ 1 ],
+// &m_Data.m[ 1 ][ 2 ],
+// &m_Data.m[ 1 ][ 3 ],
+//
+// &m_Data.m[ 2 ][ 0 ],
+// &m_Data.m[ 2 ][ 1 ],
+// &m_Data.m[ 2 ][ 2 ],
+// &m_Data.m[ 2 ][ 3 ],
+//
+// &m_Data.m[ 3 ][ 0 ],
+// &m_Data.m[ 3 ][ 1 ],
+// &m_Data.m[ 3 ][ 2 ],
+// &m_Data.m[ 3 ][ 3 ]
+// );
+//}
+
+} // namespace Q3DStudio