2 * Copyright (c) 2023 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/public-api/math/matrix.h>
23 #include <cstdint> // uint32_t
24 #include <cstring> // memcpy
28 #include <dali/internal/common/matrix-utils.h>
29 #include <dali/internal/render/common/performance-monitor.h>
30 #include <dali/public-api/common/dali-common.h>
31 #include <dali/public-api/math/math-utils.h>
32 #include <dali/public-api/math/quaternion.h>
33 #include <dali/public-api/math/vector3.h>
34 #include <dali/public-api/math/vector4.h>
38 const float ROTATION_EPSILON = 0.003f; // Deliberately large
40 const uint32_t NUM_BYTES_IN_ROW_OF_3(3 * sizeof(float));
41 const uint32_t NUM_BYTES_IN_ROW(4 * sizeof(float));
42 const uint32_t NUM_BYTES_IN_MATRIX(16 * sizeof(float));
43 const uint32_t ROW1_OFFSET(4);
44 const uint32_t ROW2_OFFSET(8);
45 const uint32_t ROW3_OFFSET(12);
50 using Internal::PerformanceMonitor;
53 const float identityArray[] = {
54 1.0f, 0.0f, 0.0f, 0.0f,
55 0.0f, 1.0f, 0.0f, 0.0f,
56 0.0f, 0.0f, 1.0f, 0.0f,
57 0.0f, 0.0f, 0.0f, 1.0f};
60 const Matrix Matrix::IDENTITY(identityArray);
64 memset(mMatrix, 0, NUM_BYTES_IN_MATRIX);
67 Matrix::Matrix(bool initialize)
71 memset(mMatrix, 0, NUM_BYTES_IN_MATRIX);
75 Matrix::Matrix(const float* array)
77 memcpy(mMatrix, array, NUM_BYTES_IN_MATRIX);
80 Matrix::Matrix(const Quaternion& rotation)
82 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 18);
84 float* matrixPtr = &mMatrix[0];
85 Internal::MatrixUtils::ConvertQuaternion(matrixPtr, rotation);
88 Matrix::Matrix(const Matrix& matrix)
90 memcpy(mMatrix, matrix.mMatrix, NUM_BYTES_IN_MATRIX);
93 Matrix& Matrix::operator=(const Matrix& matrix)
95 // no point copying if self assigning
98 memcpy(mMatrix, matrix.mMatrix, NUM_BYTES_IN_MATRIX);
103 Matrix::Matrix(Matrix&& matrix) noexcept
105 memcpy(mMatrix, matrix.mMatrix, NUM_BYTES_IN_MATRIX);
108 Matrix& Matrix::operator=(Matrix&& matrix) noexcept
112 memcpy(mMatrix, matrix.mMatrix, NUM_BYTES_IN_MATRIX);
117 void Matrix::InvertTransform(Matrix& result) const
119 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 12);
121 float* m1 = result.AsFloat();
123 DALI_ASSERT_ALWAYS(EqualsZero(mMatrix[3]) && EqualsZero(mMatrix[7]) && EqualsZero(mMatrix[11]) && Equals(mMatrix[15], 1.0f) && "Must be a transform matrix");
137 m1[10] = mMatrix[10];
140 m1[12] = -((mMatrix[0] * mMatrix[12]) + (mMatrix[1] * mMatrix[13]) + (mMatrix[2] * mMatrix[14]) + (mMatrix[3] * mMatrix[15]));
141 m1[13] = -((mMatrix[4] * mMatrix[12]) + (mMatrix[5] * mMatrix[13]) + (mMatrix[6] * mMatrix[14]) + (mMatrix[7] * mMatrix[15]));
142 m1[14] = -((mMatrix[8] * mMatrix[12]) + (mMatrix[9] * mMatrix[13]) + (mMatrix[10] * mMatrix[14]) + (mMatrix[11] * mMatrix[15]));
146 static bool InvertMatrix(const float* m, float* out)
150 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 192); // 12 x 16 multiples
152 inv[0] = m[5] * m[10] * m[15] - m[5] * m[11] * m[14] - m[9] * m[6] * m[15] + m[9] * m[7] * m[14] + m[13] * m[6] * m[11] - m[13] * m[7] * m[10];
153 inv[4] = -m[4] * m[10] * m[15] + m[4] * m[11] * m[14] + m[8] * m[6] * m[15] - m[8] * m[7] * m[14] - m[12] * m[6] * m[11] + m[12] * m[7] * m[10];
154 inv[8] = m[4] * m[9] * m[15] - m[4] * m[11] * m[13] - m[8] * m[5] * m[15] + m[8] * m[7] * m[13] + m[12] * m[5] * m[11] - m[12] * m[7] * m[9];
155 inv[12] = -m[4] * m[9] * m[14] + m[4] * m[10] * m[13] + m[8] * m[5] * m[14] - m[8] * m[6] * m[13] - m[12] * m[5] * m[10] + m[12] * m[6] * m[9];
156 inv[1] = -m[1] * m[10] * m[15] + m[1] * m[11] * m[14] + m[9] * m[2] * m[15] - m[9] * m[3] * m[14] - m[13] * m[2] * m[11] + m[13] * m[3] * m[10];
157 inv[5] = m[0] * m[10] * m[15] - m[0] * m[11] * m[14] - m[8] * m[2] * m[15] + m[8] * m[3] * m[14] + m[12] * m[2] * m[11] - m[12] * m[3] * m[10];
158 inv[9] = -m[0] * m[9] * m[15] + m[0] * m[11] * m[13] + m[8] * m[1] * m[15] - m[8] * m[3] * m[13] - m[12] * m[1] * m[11] + m[12] * m[3] * m[9];
159 inv[13] = m[0] * m[9] * m[14] - m[0] * m[10] * m[13] - m[8] * m[1] * m[14] + m[8] * m[2] * m[13] + m[12] * m[1] * m[10] - m[12] * m[2] * m[9];
160 inv[2] = m[1] * m[6] * m[15] - m[1] * m[7] * m[14] - m[5] * m[2] * m[15] + m[5] * m[3] * m[14] + m[13] * m[2] * m[7] - m[13] * m[3] * m[6];
161 inv[6] = -m[0] * m[6] * m[15] + m[0] * m[7] * m[14] + m[4] * m[2] * m[15] - m[4] * m[3] * m[14] - m[12] * m[2] * m[7] + m[12] * m[3] * m[6];
162 inv[10] = m[0] * m[5] * m[15] - m[0] * m[7] * m[13] - m[4] * m[1] * m[15] + m[4] * m[3] * m[13] + m[12] * m[1] * m[7] - m[12] * m[3] * m[5];
163 inv[14] = -m[0] * m[5] * m[14] + m[0] * m[6] * m[13] + m[4] * m[1] * m[14] - m[4] * m[2] * m[13] - m[12] * m[1] * m[6] + m[12] * m[2] * m[5];
164 inv[3] = -m[1] * m[6] * m[11] + m[1] * m[7] * m[10] + m[5] * m[2] * m[11] - m[5] * m[3] * m[10] - m[9] * m[2] * m[7] + m[9] * m[3] * m[6];
165 inv[7] = m[0] * m[6] * m[11] - m[0] * m[7] * m[10] - m[4] * m[2] * m[11] + m[4] * m[3] * m[10] + m[8] * m[2] * m[7] - m[8] * m[3] * m[6];
166 inv[11] = -m[0] * m[5] * m[11] + m[0] * m[7] * m[9] + m[4] * m[1] * m[11] - m[4] * m[3] * m[9] - m[8] * m[1] * m[7] + m[8] * m[3] * m[5];
167 inv[15] = m[0] * m[5] * m[10] - m[0] * m[6] * m[9] - m[4] * m[1] * m[10] + m[4] * m[2] * m[9] + m[8] * m[1] * m[6] - m[8] * m[2] * m[5];
169 float det = m[0] * inv[0] + m[1] * inv[4] + m[2] * inv[8] + m[3] * inv[12];
171 // In the case where the determinant is exactly zero, the matrix is non-invertible
179 for(int32_t i = 0; i < 16; i++)
181 out[i] = inv[i] * det;
187 bool Matrix::Invert()
191 return InvertMatrix(temp.AsFloat(), mMatrix);
194 void Matrix::Transpose()
196 float temp = mMatrix[1];
197 mMatrix[1] = mMatrix[4];
201 mMatrix[2] = mMatrix[8];
205 mMatrix[3] = mMatrix[12];
209 mMatrix[6] = mMatrix[9];
213 mMatrix[7] = mMatrix[13];
217 mMatrix[11] = mMatrix[14];
221 void Matrix::SetIdentity()
223 memcpy(mMatrix, identityArray, NUM_BYTES_IN_MATRIX);
226 void Matrix::SetIdentityAndScale(const Vector3& scale)
228 // initialize to zeros
229 memset(mMatrix, 0, NUM_BYTES_IN_MATRIX);
231 // just apply scale on the diagonal
232 mMatrix[0] = scale.x;
233 mMatrix[5] = scale.y;
234 mMatrix[10] = scale.z;
238 void Matrix::SetTranslation(const Vector4& translation)
240 memcpy(mMatrix + ROW3_OFFSET, &translation, NUM_BYTES_IN_ROW);
242 void Matrix::SetTranslation(const Vector3& other)
244 memcpy(mMatrix + ROW3_OFFSET, &other, NUM_BYTES_IN_ROW_OF_3);
248 void Matrix::Multiply(Matrix& result, const Matrix& lhs, const Matrix& rhs)
250 Internal::MatrixUtils::Multiply(result, lhs, rhs);
253 void Matrix::Multiply(Matrix& result, const Matrix& lhs, const Quaternion& rhs)
255 Internal::MatrixUtils::Multiply(result, lhs, rhs);
258 Matrix Matrix::operator*(const Matrix& rhs) const
260 Matrix result(false);
261 Internal::MatrixUtils::Multiply(result, rhs, *this);
265 Matrix& Matrix::operator*=(const Matrix& rhs)
267 Internal::MatrixUtils::MultiplyAssign(*this, rhs);
271 Vector4 Matrix::operator*(const Vector4& rhs) const
273 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 16);
279 temp.x = rhs.x * mMatrix[0] + rhs.y * mMatrix[4] + rhs.z * mMatrix[8] + rhs.w * mMatrix[12];
280 temp.y = rhs.x * mMatrix[1] + rhs.y * mMatrix[5] + rhs.z * mMatrix[9] + rhs.w * mMatrix[13];
281 temp.z = rhs.x * mMatrix[2] + rhs.y * mMatrix[6] + rhs.z * mMatrix[10] + rhs.w * mMatrix[14];
282 temp.w = rhs.x * mMatrix[3] + rhs.y * mMatrix[7] + rhs.z * mMatrix[11] + rhs.w * mMatrix[15];
286 // 64 32bit registers,
288 // d = 64 bit double-word d0 -d31
289 // q =128 bit quad-word q0 -q15 (enough to handle a column of 4 floats in a matrix)
290 // e.g. q0 = d0 and d1
291 // load and stores interleaved as NEON can load and store while calculating
293 "VLD1.F32 {q0}, [%1] \n\t" //q0 = rhs
294 "VLD1.F32 {q9}, [%0]! \n\t"
295 "VMUL.F32 q10, q9, d0[0] \n\t"
296 "VLD1.F32 {q9}, [%0]! \n\t"
297 "VMLA.F32 q10, q9, d0[1] \n\t" //q10 = mMatrix[0..3] * rhs + mMatrix[4..7] * rhs
298 "VLD1.F32 {q9}, [%0]! \n\t"
299 "VMUL.F32 q11, q9, d1[0] \n\t"
300 "VLD1.F32 {q9}, [%0]! \n\t"
301 "VMLA.F32 q11, q9, d1[1] \n\t" //q11 = mMatrix[8..11] * rhs + mMatrix[12..15] * rhs
302 "VADD.F32 q10, q10, q11 \n\t"
303 "VST1.F32 {q10},[%2] \n\t" //temp = q10 + q11
305 : "r"(mMatrix), "r"(&rhs), "r"(&temp)
306 : "q0", "q9", "q10", "q11", "memory");
311 bool Matrix::operator==(const Matrix& rhs) const
314 (fabsf(mMatrix[0] - rhs.mMatrix[0]) <= GetRangedEpsilon(mMatrix[0], rhs.mMatrix[0])) &&
315 (fabsf(mMatrix[1] - rhs.mMatrix[1]) <= GetRangedEpsilon(mMatrix[1], rhs.mMatrix[1])) &&
316 (fabsf(mMatrix[2] - rhs.mMatrix[2]) <= GetRangedEpsilon(mMatrix[2], rhs.mMatrix[2])) &&
317 (fabsf(mMatrix[3] - rhs.mMatrix[3]) <= GetRangedEpsilon(mMatrix[3], rhs.mMatrix[3])) &&
318 (fabsf(mMatrix[4] - rhs.mMatrix[4]) <= GetRangedEpsilon(mMatrix[4], rhs.mMatrix[4])) &&
319 (fabsf(mMatrix[5] - rhs.mMatrix[5]) <= GetRangedEpsilon(mMatrix[5], rhs.mMatrix[5])) &&
320 (fabsf(mMatrix[6] - rhs.mMatrix[6]) <= GetRangedEpsilon(mMatrix[6], rhs.mMatrix[6])) &&
321 (fabsf(mMatrix[7] - rhs.mMatrix[7]) <= GetRangedEpsilon(mMatrix[7], rhs.mMatrix[7])) &&
322 (fabsf(mMatrix[8] - rhs.mMatrix[8]) <= GetRangedEpsilon(mMatrix[8], rhs.mMatrix[8])) &&
323 (fabsf(mMatrix[9] - rhs.mMatrix[9]) <= GetRangedEpsilon(mMatrix[9], rhs.mMatrix[9])) &&
324 (fabsf(mMatrix[10] - rhs.mMatrix[10]) <= GetRangedEpsilon(mMatrix[10], rhs.mMatrix[10])) &&
325 (fabsf(mMatrix[11] - rhs.mMatrix[11]) <= GetRangedEpsilon(mMatrix[11], rhs.mMatrix[11])) &&
326 (fabsf(mMatrix[12] - rhs.mMatrix[12]) <= GetRangedEpsilon(mMatrix[12], rhs.mMatrix[12])) &&
327 (fabsf(mMatrix[13] - rhs.mMatrix[13]) <= GetRangedEpsilon(mMatrix[13], rhs.mMatrix[13])) &&
328 (fabsf(mMatrix[14] - rhs.mMatrix[14]) <= GetRangedEpsilon(mMatrix[14], rhs.mMatrix[14])) &&
329 (fabsf(mMatrix[15] - rhs.mMatrix[15]) <= GetRangedEpsilon(mMatrix[15], rhs.mMatrix[15])));
332 bool Matrix::operator!=(const Matrix& rhs) const
342 void Matrix::OrthoNormalize()
344 Vector4 vector0(GetXAxis());
345 Vector4 vector1(GetYAxis());
346 Vector4 vector2(GetZAxis());
350 vector2 = vector0.Cross(vector1);
351 vector1 = vector2.Cross(vector0);
353 memcpy(mMatrix, &vector0, NUM_BYTES_IN_ROW);
354 memcpy(mMatrix + ROW1_OFFSET, &vector1, NUM_BYTES_IN_ROW);
355 memcpy(mMatrix + ROW2_OFFSET, &vector2, NUM_BYTES_IN_ROW);
358 Vector3 Matrix::GetXAxis() const
360 return Vector3(mMatrix[0], mMatrix[1], mMatrix[2]);
363 Vector3 Matrix::GetYAxis() const
365 return Vector3(mMatrix[4], mMatrix[5], mMatrix[6]);
368 Vector3 Matrix::GetZAxis() const
370 return Vector3(mMatrix[8], mMatrix[9], mMatrix[10]);
373 void Matrix::SetXAxis(const Vector3& axis)
380 void Matrix::SetYAxis(const Vector3& axis)
387 void Matrix::SetZAxis(const Vector3& axis)
391 mMatrix[10] = axis.z;
394 void Matrix::SetTransformComponents(const Vector3& scale,
395 const Quaternion& rotation,
396 const Vector3& translation)
398 if(rotation.IsIdentity())
400 mMatrix[0] = scale.x;
406 mMatrix[5] = scale.y;
412 mMatrix[10] = scale.z;
417 MATH_INCREASE_COUNTER(PerformanceMonitor::MATRIX_MULTIPLYS);
418 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 27); // 27 = 9+18
420 const float xx = rotation.mVector.x * rotation.mVector.x;
421 const float yy = rotation.mVector.y * rotation.mVector.y;
422 const float zz = rotation.mVector.z * rotation.mVector.z;
423 const float xy = rotation.mVector.x * rotation.mVector.y;
424 const float xz = rotation.mVector.x * rotation.mVector.z;
425 const float wx = rotation.mVector.w * rotation.mVector.x;
426 const float wy = rotation.mVector.w * rotation.mVector.y;
427 const float wz = rotation.mVector.w * rotation.mVector.z;
428 const float yz = rotation.mVector.y * rotation.mVector.z;
430 mMatrix[0] = (scale.x * (1.0f - 2.0f * (yy + zz)));
431 mMatrix[1] = (scale.x * (2.0f * (xy + wz)));
432 mMatrix[2] = (scale.x * (2.0f * (xz - wy)));
435 mMatrix[4] = (scale.y * (2.0f * (xy - wz)));
436 mMatrix[5] = (scale.y * (1.0f - 2.0f * (xx + zz)));
437 mMatrix[6] = (scale.y * (2.0f * (yz + wx)));
440 mMatrix[8] = (scale.z * (2.0f * (xz + wy)));
441 mMatrix[9] = (scale.z * (2.0f * (yz - wx)));
442 mMatrix[10] = (scale.z * (1.0f - 2.0f * (xx + yy)));
446 mMatrix[12] = translation.x;
447 mMatrix[13] = translation.y;
448 mMatrix[14] = translation.z;
452 void Matrix::SetInverseTransformComponents(const Vector3& scale,
453 const Quaternion& rotation,
454 const Vector3& translation)
456 Vector3 inverseTranslation = -translation;
457 Vector3 inverseScale(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z);
458 Quaternion inverseRotation(rotation);
459 bool isRotated = !inverseRotation.IsIdentity();
461 // Order of application is translation, rotation, scale.
462 // Ensure translation is relative to scale & rotation:
466 inverseRotation.Invert();
467 inverseTranslation = inverseRotation.Rotate(inverseTranslation);
470 inverseTranslation *= inverseScale;
474 MATH_INCREASE_COUNTER(PerformanceMonitor::MATRIX_MULTIPLYS);
475 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 27); // 27 = 9+18
477 const float xx = inverseRotation.mVector.x * inverseRotation.mVector.x;
478 const float yy = inverseRotation.mVector.y * inverseRotation.mVector.y;
479 const float zz = inverseRotation.mVector.z * inverseRotation.mVector.z;
480 const float xy = inverseRotation.mVector.x * inverseRotation.mVector.y;
481 const float xz = inverseRotation.mVector.x * inverseRotation.mVector.z;
482 const float wx = inverseRotation.mVector.w * inverseRotation.mVector.x;
483 const float wy = inverseRotation.mVector.w * inverseRotation.mVector.y;
484 const float wz = inverseRotation.mVector.w * inverseRotation.mVector.z;
485 const float yz = inverseRotation.mVector.y * inverseRotation.mVector.z;
487 mMatrix[0] = (inverseScale.x * (1.0f - 2.0f * (yy + zz)));
488 mMatrix[1] = (inverseScale.y * (2.0f * (xy + wz)));
489 mMatrix[2] = (inverseScale.z * (2.0f * (xz - wy)));
492 mMatrix[4] = (inverseScale.x * (2.0f * (xy - wz)));
493 mMatrix[5] = (inverseScale.y * (1.0f - 2.0f * (xx + zz)));
494 mMatrix[6] = (inverseScale.z * (2.0f * (yz + wx)));
497 mMatrix[8] = (inverseScale.x * (2.0f * (xz + wy)));
498 mMatrix[9] = (inverseScale.y * (2.0f * (yz - wx)));
499 mMatrix[10] = (inverseScale.z * (1.0f - 2.0f * (xx + yy)));
504 mMatrix[0] = inverseScale.x;
510 mMatrix[5] = inverseScale.y;
516 mMatrix[10] = inverseScale.z;
521 mMatrix[12] = inverseTranslation.x;
522 mMatrix[13] = inverseTranslation.y;
523 mMatrix[14] = inverseTranslation.z;
527 void Matrix::SetInverseTransformComponents(const Vector3& xAxis,
528 const Vector3& yAxis,
529 const Vector3& zAxis,
530 const Vector3& translation)
532 // x, y, z axis parameters represent a orthonormal basis with no scaling, i.e. a rotation matrix.
533 // Invert rotation by transposing in place
535 // Order of application is translation, rotation
537 mMatrix[0] = xAxis.x;
538 mMatrix[1] = yAxis.x;
539 mMatrix[2] = zAxis.x;
542 mMatrix[4] = xAxis.y;
543 mMatrix[5] = yAxis.y;
544 mMatrix[6] = zAxis.y;
547 mMatrix[8] = xAxis.z;
548 mMatrix[9] = yAxis.z;
549 mMatrix[10] = zAxis.z;
556 // Ensure translation is relative to scale & rotation:
558 Vector4 inverseTranslation(-translation.x, -translation.y, -translation.z, 1.0f);
559 inverseTranslation = *this * inverseTranslation; // Rotate inverse translation
560 inverseTranslation.w = 1.0f;
561 SetTranslation(inverseTranslation);
564 Vector3 Matrix::GetScale() const
566 // Derive scale from axis lengths.
567 return Vector3(GetXAxis().Length(), GetYAxis().Length(), GetZAxis().Length());
570 void Matrix::GetTransformComponents(Vector3& position,
571 Quaternion& rotation,
572 Vector3& scale) const
574 position = GetTranslation3();
577 if(!(fabs(scale.x - Vector3::ONE.x) < ROTATION_EPSILON &&
578 fabs(scale.y - Vector3::ONE.y) < ROTATION_EPSILON &&
579 fabs(scale.z - Vector3::ONE.z) < ROTATION_EPSILON))
581 MATH_INCREASE_COUNTER(PerformanceMonitor::MATRIX_MULTIPLYS);
582 MATH_INCREASE_BY(PerformanceMonitor::FLOAT_POINT_MULTIPLY, 9);
584 // Non-identity scale is embedded into rotation matrix. Remove it first:
586 Vector3 inverseScale(1.0f / scale.x, 1.0f / scale.y, 1.0f / scale.z);
587 m.mMatrix[0] *= inverseScale.x;
588 m.mMatrix[1] *= inverseScale.x;
589 m.mMatrix[2] *= inverseScale.x;
590 m.mMatrix[4] *= inverseScale.y;
591 m.mMatrix[5] *= inverseScale.y;
592 m.mMatrix[6] *= inverseScale.y;
593 m.mMatrix[8] *= inverseScale.z;
594 m.mMatrix[9] *= inverseScale.z;
595 m.mMatrix[10] *= inverseScale.z;
597 Quaternion theRotation(m);
599 // If the imaginary components are close to zero, then use null quaternion instead.
600 if(fabs(theRotation.mVector.x) < ROTATION_EPSILON &&
601 fabs(theRotation.mVector.y) < ROTATION_EPSILON &&
602 fabs(theRotation.mVector.z) < ROTATION_EPSILON)
604 theRotation = Quaternion();
606 rotation = theRotation;
610 Quaternion theRotation(*this);
612 // If the imaginary components are close to zero, then use null quaternion instead.
613 if(fabs(theRotation.mVector.x) < ROTATION_EPSILON &&
614 fabs(theRotation.mVector.y) < ROTATION_EPSILON &&
615 fabs(theRotation.mVector.z) < ROTATION_EPSILON)
617 theRotation = Quaternion();
619 rotation = theRotation;
623 std::ostream& operator<<(std::ostream& o, const Matrix& matrix)
625 return o << "[ " << matrix.mMatrix[0] << ", " << matrix.mMatrix[1] << ", " << matrix.mMatrix[2] << ", " << matrix.mMatrix[3] << ", "
626 << matrix.mMatrix[4] << ", " << matrix.mMatrix[5] << ", " << matrix.mMatrix[6] << ", " << matrix.mMatrix[7] << ", "
627 << matrix.mMatrix[8] << ", " << matrix.mMatrix[9] << ", " << matrix.mMatrix[10] << ", " << matrix.mMatrix[11] << ", "
628 << matrix.mMatrix[12] << ", " << matrix.mMatrix[13] << ", " << matrix.mMatrix[14] << ", " << matrix.mMatrix[15] << " ]";