1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef API_BASE_MATH_MATRIX_UTIL_H
17 #define API_BASE_MATH_MATRIX_UTIL_H
18 
19 #include <base/math/mathf.h>
20 #include <base/math/matrix.h>
21 #include <base/math/quaternion.h>
22 #include <base/math/vector.h>
23 #include <base/math/vector_util.h>
24 #include <base/namespace.h>
25 #include <base/util/log.h>
26 
BASE_BEGIN_NAMESPACE()27 BASE_BEGIN_NAMESPACE()
28 namespace Math {
29 /** \addtogroup group_math_matrixutil
30  *  @{
31  */
32 /** Returns transpose of this matrix */
33 static inline constexpr Mat3X3 Transpose(const Mat3X3& m)
34 {
35     Mat3X3 result;
36 
37     result.x.x = m.x.x;
38     result.y.x = m.x.y;
39     result.z.x = m.x.z;
40 
41     result.x.y = m.y.x;
42     result.y.y = m.y.y;
43     result.z.y = m.y.z;
44 
45     result.x.z = m.z.x;
46     result.y.z = m.z.y;
47     result.z.z = m.z.z;
48 
49     return result;
50 }
51 
52 /** Returns transpose of this matrix */
53 static inline constexpr Mat4X4 Transpose(const Mat4X4& m)
54 {
55     Mat4X4 result;
56 
57     result.x.x = m.x.x;
58     result.y.x = m.x.y;
59     result.z.x = m.x.z;
60     result.w.x = m.x.w;
61 
62     result.x.y = m.y.x;
63     result.y.y = m.y.y;
64     result.z.y = m.y.z;
65     result.w.y = m.y.w;
66 
67     result.x.z = m.z.x;
68     result.y.z = m.z.y;
69     result.z.z = m.z.z;
70     result.w.z = m.z.w;
71 
72     result.x.w = m.w.x;
73     result.y.w = m.w.y;
74     result.z.w = m.w.z;
75     result.w.w = m.w.w;
76 
77     return result;
78 }
79 
80 /** Set scale of this matrix */
81 static inline constexpr Mat3X3 PostScale(const Mat3X3& mat, const Vec2& vec)
82 {
83     Mat3X3 result;
84     result.x = { mat.x.x * vec.x, mat.x.y * vec.y, mat.x.z };
85     result.y = { mat.y.x * vec.x, mat.y.y * vec.y, mat.y.z };
86     result.z = { mat.z.x * vec.x, mat.z.y * vec.y, mat.z.z };
87     return result;
88 }
89 
90 /** Set scale of this matrix */
91 static inline constexpr Mat4X4 PostScale(const Mat4X4& mat, const Vec3& vec)
92 {
93     Mat4X4 result;
94     result.x = { mat.x.x * vec.x, mat.x.y * vec.y, mat.x.z * vec.z, mat.x.w };
95     result.y = { mat.y.x * vec.x, mat.y.y * vec.y, mat.y.z * vec.z, mat.y.w };
96     result.z = { mat.z.x * vec.x, mat.z.y * vec.y, mat.z.z * vec.z, mat.z.w };
97     result.w = { mat.w.x * vec.x, mat.w.y * vec.y, mat.w.z * vec.z, mat.w.w };
98     return result;
99 }
100 
101 /** Set scale of this matrix */
102 static inline constexpr Mat3X3 Scale(const Mat3X3& mat, const Vec2& vec)
103 {
104     Mat3X3 result;
105     result.x = mat.x * vec.x;
106     result.y = mat.y * vec.y;
107     result.z = mat.z;
108     return result;
109 }
110 
111 /** Set scale of this matrix */
112 static inline constexpr Mat4X4 Scale(const Mat4X4& mat, const Vec3& vec)
113 {
114     Mat4X4 result;
115     result.x = mat.x * vec.x;
116     result.y = mat.y * vec.y;
117     result.z = mat.z * vec.z;
118     result.w = mat.w;
119     return result;
120 }
121 
122 /** Get column of the matrix */
123 static inline constexpr Vec3 GetColumn(const Mat3X3& mat, int index)
124 {
125     switch (index) {
126             // the first column of the matrix: Vec3(m00, m10, m20)
127         case 0:
128             return mat.x;
129             // the second column of the matrix:  Vec3(m01, m11, m21)
130         case 1:
131             return mat.y;
132             // the third column of the matrix:  Vec3(m02, m12, m22)
133         case 2:
134             return mat.z;
135 
136         default:
137             BASE_ASSERT(false);
138             return Vec3(0.0f, 0.0f, 0.0f);
139     }
140 }
141 
142 /** Get column of the matrix */
143 static inline constexpr Vec4 GetColumn(const Mat4X4& mat, int index)
144 {
145     switch (index) {
146             // the first column of the matrix: Vec4(m00, m10, m20, m30)
147         case 0:
148             return mat.x;
149             // the second column of the matrix:  Vec4(m01, m11, m21, m31)
150         case 1:
151             return mat.y;
152             // the third column of the matrix:  Vec4(m02, m12, m22, m32)
153         case 2:
154             return mat.z;
155             // the fourth column of the matrix:  Vec4(m03, m13, m23, m33)
156         case 3:
157             return mat.w;
158 
159         default:
160             BASE_ASSERT(false);
161             return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
162     }
163 }
164 
165 /** Returns row of the matrix */
166 static inline constexpr Vec3 GetRow(const Mat3X3& mat, int index)
167 {
168     switch (index) {
169             // the first row of the matrix: Vec4(m00, m01, m02, m03)
170         case 0:
171             return Vec3(mat.x.x, mat.y.x, mat.z.x);
172             // the second row of the matrix: Vec4(m10, m11, m12, m13)
173         case 1:
174             return Vec3(mat.x.y, mat.y.y, mat.z.y);
175             // the third row of the matrix: Vec4(m20, m21, m22, m23)
176         case 2:
177             return Vec3(mat.x.z, mat.y.z, mat.z.z);
178         default:
179             BASE_ASSERT(false);
180             return Vec3(0.0f, 0.0f, 0.0f);
181     }
182 }
183 
184 /** Returns row of the matrix */
185 static inline constexpr Vec4 GetRow(const Mat4X4& mat, int index)
186 {
187     switch (index) {
188             // the first row of the matrix: Vec4(m00, m01, m02, m03)
189         case 0:
190             return Vec4(mat.x.x, mat.y.x, mat.z.x, mat.w.x);
191             // the second row of the matrix: Vec4(m10, m11, m12, m13)
192         case 1:
193             return Vec4(mat.x.y, mat.y.y, mat.z.y, mat.w.y);
194             // the third row of the matrix: Vec4(m20, m21, m22, m23)
195         case 2:
196             return Vec4(mat.x.z, mat.y.z, mat.z.z, mat.w.z);
197             // the fourth row of the matrix: Vec4(m30, m31, m32, m33)
198         case 3:
199             return Vec4(mat.x.w, mat.y.w, mat.z.w, mat.w.w);
200 
201         default:
202             BASE_ASSERT(false);
203             return Vec4(0.0f, 0.0f, 0.0f, 0.0f);
204     }
205 }
206 
207 /** Converts quaternion to matrix 3x3 */
208 static inline constexpr Mat3X3 Mat3Cast(const Quat& quaternion)
209 {
210     Mat3X3 result;
211     const float qXX(quaternion.x * quaternion.x);
212     const float qYY(quaternion.y * quaternion.y);
213     const float qZZ(quaternion.z * quaternion.z);
214     const float qXZ(quaternion.x * quaternion.z);
215     const float qXY(quaternion.x * quaternion.y);
216     const float qYZ(quaternion.y * quaternion.z);
217     const float qWX(quaternion.w * quaternion.x);
218     const float qWY(quaternion.w * quaternion.y);
219     const float qWZ(quaternion.w * quaternion.z);
220 
221     result.x.x = 1.0f - 2.0f * (qYY + qZZ);
222     result.x.y = 2.0f * (qXY + qWZ);
223     result.x.z = 2.0f * (qXZ - qWY);
224 
225     result.y.x = 2.0f * (qXY - qWZ);
226     result.y.y = 1.0f - 2.0f * (qXX + qZZ);
227     result.y.z = 2.0f * (qYZ + qWX);
228 
229     result.z.x = 2.0f * (qXZ + qWY);
230     result.z.y = 2.0f * (qYZ - qWX);
231     result.z.z = 1.0f - 2.0f * (qXX + qYY);
232     return result;
233 }
234 
235 /** Convert 3x3 matrix to 4x4 */
236 static inline constexpr Mat4X4 DimensionalShift(const Mat3X3& mat)
237 {
238     Mat4X4 result;
239     result.x = { mat.x.x, mat.x.y, 0, mat.x.z };
240     result.y = { mat.y.x, mat.y.y, 0, mat.y.z };
241     result.z = { 0, 0, 1, 0 };
242     result.w = { mat.z.x, mat.z.y, 0, mat.z.z };
243     return result;
244 }
245 
246 /** Converts quaternion to matrix 4X4 */
247 static inline constexpr Mat4X4 Mat4Cast(const Quat& quaternion)
248 {
249     return Mat4X4(Mat3Cast(quaternion));
250 }
251 
252 /** Translate by vector2 */
253 static inline constexpr Mat3X3 Translate(const Mat3X3& mat, const Vec2& vec)
254 {
255     Mat3X3 result(mat);
256     result.z = mat.x * vec.x + mat.y * vec.y + mat.z;
257     return result;
258 }
259 
260 /** Translate by vector2 */
261 static inline constexpr Mat4X4 Translate(const Mat4X4& mat, const Vec2& vec)
262 {
263     Mat4X4 result(mat);
264     result.w = mat.x * vec.x + mat.y * vec.y + mat.w;
265     return result;
266 }
267 /** Translate by vector3 */
268 static inline constexpr Mat4X4 Translate(const Mat4X4& mat, const Vec3& vec)
269 {
270     Mat4X4 result(mat);
271     result.w = mat.x * vec.x + mat.y * vec.y + mat.z * vec.z + mat.w;
272     return result;
273 }
274 /** Skew angle in X and Y in radian */
275 static inline Mat4X4 SkewXY(const Mat4X4& mat, const Vec2& vec)
276 {
277     Mat4X4 result(mat);
278     result.x += mat.y * tanf(vec.y);
279     result.y += mat.x * tanf(vec.x);
280     return result;
281 }
282 /** Rotate on Z axis by angle in radian */
283 static inline Mat4X4 RotateZCWRadians(const Mat4X4& mat, float rot)
284 {
285     Mat4X4 result(mat);
286     float co = cosf(rot);
287     float si = sinf(rot);
288     result.x = mat.x * co + mat.y * si;
289     result.y = mat.y * co - mat.x * si;
290     return result;
291 }
292 
293 /** Creates translation, rotation and scaling matrix from translation vector, rotation quaternion and scale vector */
294 static inline constexpr Mat4X4 Trs(
295     const Vec3& translationVector, const Quat& rotationQuaternion, const Vec3& scaleVector)
296 {
297     return Scale(Translate(Mat4X4(1.f), translationVector) * Mat4Cast(rotationQuaternion), scaleVector);
298 }
299 
300 /** Transforms direction by this matrix */
301 static inline constexpr Vec3 MultiplyVector(const Mat4X4& mat, const Vec3& vec)
302 {
303     Vec3 res;
304     res.x = mat.x.x * vec.x + mat.y.x * vec.y + mat.z.x * vec.z;
305     res.y = mat.x.y * vec.x + mat.y.y * vec.y + mat.z.y * vec.z;
306     res.z = mat.x.z * vec.x + mat.y.z * vec.y + mat.z.z * vec.z;
307     return res;
308 }
309 
310 /** Transforms position by this matrix without perspective divide */
311 static inline constexpr Vec2 MultiplyPoint2X4(const Mat4X4& mat4X4, const Vec2& point)
312 {
313     Vec2 result;
314     result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y + mat4X4.w.x;
315     result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y + mat4X4.w.y;
316 
317     return result;
318 }
319 /** Transforms direction by this matrix without perspective divide */
320 static inline constexpr Vec2 MultiplyVector2X4(const Mat4X4& mat4X4, const Vec2& point)
321 {
322     Vec2 result;
323     result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y;
324     result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y;
325 
326     return result;
327 }
328 /** Transforms position by this matrix without perspective divide */
329 static inline constexpr Vec2 MultiplyPoint2X3(const Mat3X3& mat3X3, const Vec2& point)
330 {
331     Vec2 result;
332     result.x = mat3X3.x.x * point.x + mat3X3.y.x * point.y + mat3X3.z.x;
333     result.y = mat3X3.x.y * point.x + mat3X3.y.y * point.y + mat3X3.z.y;
334 
335     return result;
336 }
337 
338 /** Transforms position by this matrix without perspective divide */
339 static inline constexpr Vec3 MultiplyPoint3X4(const Mat4X4& mat4X4, const Vec3& point)
340 {
341     Vec3 result;
342     result.x = mat4X4.x.x * point.x + mat4X4.y.x * point.y + mat4X4.z.x * point.z + mat4X4.w.x;
343     result.y = mat4X4.x.y * point.x + mat4X4.y.y * point.y + mat4X4.z.y * point.z + mat4X4.w.y;
344     result.z = mat4X4.x.z * point.x + mat4X4.y.z * point.y + mat4X4.z.z * point.z + mat4X4.w.z;
345 
346     return result;
347 }
348 
349 /** Inverse matrix with determinant output */
350 static inline constexpr Mat3X3 Inverse(Mat3X3 const& m, float& determinantOut)
351 {
352     determinantOut = m.x.x * (m.y.y * m.z.z - m.z.y * m.y.z) - m.x.y * (m.y.x * m.z.z - m.y.z * m.z.x) +
353                      m.x.z * (m.y.x * m.z.y - m.y.y * m.z.x);
354 
355     const float invdet = 1 / determinantOut;
356 
357     Mat3X3 inverse;
358     inverse.x.x = (m.y.y * m.z.z - m.z.y * m.y.z) * invdet;
359     inverse.x.y = (m.x.z * m.z.y - m.x.y * m.z.z) * invdet;
360     inverse.x.z = (m.x.y * m.y.z - m.x.z * m.y.y) * invdet;
361     inverse.y.x = (m.y.z * m.z.x - m.y.x * m.z.z) * invdet;
362     inverse.y.y = (m.x.x * m.z.z - m.x.z * m.z.x) * invdet;
363     inverse.y.z = (m.y.x * m.x.z - m.x.x * m.y.z) * invdet;
364     inverse.z.x = (m.y.x * m.z.y - m.z.x * m.y.y) * invdet;
365     inverse.z.y = (m.z.x * m.x.y - m.x.x * m.z.y) * invdet;
366     inverse.z.z = (m.x.x * m.y.y - m.y.x * m.x.y) * invdet;
367     return inverse;
368 }
369 
370 /** Inverse matrix */
371 static inline constexpr Mat3X3 Inverse(const Mat3X3& mat3X3)
372 {
373     float determinant = 0.0f;
374     return Inverse(mat3X3, determinant);
375 }
376 
377 /** Inverse matrix with determinant output */
378 static inline constexpr Mat4X4 Inverse(Mat4X4 const& m, float& determinantOut)
379 {
380     const float coef00 = m.z.z * m.w.w - m.w.z * m.z.w;
381     const float coef02 = m.y.z * m.w.w - m.w.z * m.y.w;
382     const float coef03 = m.y.z * m.z.w - m.z.z * m.y.w;
383 
384     const float coef04 = m.z.y * m.w.w - m.w.y * m.z.w;
385     const float coef06 = m.y.y * m.w.w - m.w.y * m.y.w;
386     const float coef07 = m.y.y * m.z.w - m.z.y * m.y.w;
387 
388     const float coef08 = m.z.y * m.w.z - m.w.y * m.z.z;
389     const float coef10 = m.y.y * m.w.z - m.w.y * m.y.z;
390     const float coef11 = m.y.y * m.z.z - m.z.y * m.y.z;
391 
392     const float coef12 = m.z.x * m.w.w - m.w.x * m.z.w;
393     const float coef14 = m.y.x * m.w.w - m.w.x * m.y.w;
394     const float coef15 = m.y.x * m.z.w - m.z.x * m.y.w;
395 
396     const float coef16 = m.z.x * m.w.z - m.w.x * m.z.z;
397     const float coef18 = m.y.x * m.w.z - m.w.x * m.y.z;
398     const float coef19 = m.y.x * m.z.z - m.z.x * m.y.z;
399 
400     const float coef20 = m.z.x * m.w.y - m.w.x * m.z.y;
401     const float coef22 = m.y.x * m.w.y - m.w.x * m.y.y;
402 
403     const float coef23 = m.y.x * m.z.y - m.z.x * m.y.y;
404 
405     const Vec4 fac0(coef00, coef00, coef02, coef03);
406     const Vec4 fac1(coef04, coef04, coef06, coef07);
407     const Vec4 fac2(coef08, coef08, coef10, coef11);
408     const Vec4 fac3(coef12, coef12, coef14, coef15);
409     const Vec4 fac4(coef16, coef16, coef18, coef19);
410     const Vec4 fac5(coef20, coef20, coef22, coef23);
411 
412     const Vec4 vec0(m.y.x, m.x.x, m.x.x, m.x.x);
413     const Vec4 vec1(m.y.y, m.x.y, m.x.y, m.x.y);
414     const Vec4 vec2(m.y.z, m.x.z, m.x.z, m.x.z);
415     const Vec4 vec3(m.y.w, m.x.w, m.x.w, m.x.w);
416 
417     const Vec4 inv0(vec1 * fac0 - vec2 * fac1 + vec3 * fac2);
418     const Vec4 inv1(vec0 * fac0 - vec2 * fac3 + vec3 * fac4);
419     const Vec4 inv2(vec0 * fac1 - vec1 * fac3 + vec3 * fac5);
420     const Vec4 inv3(vec0 * fac2 - vec1 * fac4 + vec2 * fac5);
421 
422     constexpr Vec4 signA(+1.f, -1.f, +1.f, -1.f);
423     constexpr Vec4 signB(-1.f, +1.f, -1.f, +1.f);
424     const Mat4X4 inverse(inv0 * signA, inv1 * signB, inv2 * signA, inv3 * signB);
425 
426     const Vec4 row0(inverse.x.x, inverse.y.x, inverse.z.x, inverse.w.x);
427 
428     const Vec4 dot0(m.x * row0);
429     determinantOut = (dot0.x + dot0.y) + (dot0.z + dot0.w);
430 
431     const float oneOverDeterminant = 1.0f / determinantOut;
432 
433     return inverse * oneOverDeterminant;
434 }
435 
436 /** Get determinant out from matrix 4X4 */
437 static inline constexpr float Determinant(const Mat4X4& mat4X4)
438 {
439     float determinant = 0.0f;
440     Inverse(mat4X4, determinant);
441     return determinant;
442 }
443 
444 /** Inverse matrix */
445 static inline constexpr Mat4X4 Inverse(const Mat4X4& mat4X4)
446 {
447     float determinant = 0.0f;
448     return Inverse(mat4X4, determinant);
449 }
450 
451 /** Inner product */
452 inline constexpr Vec4 operator*(const Mat4X4& m, const Vec4& v)
453 {
454     return { v.x * m.x.x + v.y * m.y.x + v.z * m.z.x + v.w * m.w.x,
455         v.x * m.x.y + v.y * m.y.y + v.z * m.z.y + v.w * m.w.y, v.x * m.x.z + v.y * m.y.z + v.z * m.z.z + v.w * m.w.z,
456         v.x * m.x.w + v.y * m.y.w + v.z * m.z.w + v.w * m.w.w };
457 }
458 
459 inline constexpr Vec3 operator*(const Mat3X3& m, const Vec3& v)
460 {
461     return { v.x * m.x.x + v.y * m.y.x + v.z * m.z.x, v.x * m.x.y + v.y * m.y.y + v.z * m.z.y,
462         v.x * m.x.z + v.y * m.y.z + v.z * m.z.z };
463 }
464 
465 /** Creates matrix for left handed symmetric perspective view frustum, near and far clip planes correspond to z
466  * normalized device coordinates of 0 and +1 */
467 static inline Mat4X4 PerspectiveLhZo(float fovy, float aspect, float zNear, float zFar)
468 {
469     float const tanHalfFovy = tan(fovy / 2.0f);
470 
471     Mat4X4 result(0.f);
472     if (const auto div = (aspect * tanHalfFovy); abs(div) > EPSILON) {
473         result.x.x = 1.0f / div;
474     } else {
475         result.x.x = HUGE_VALF;
476     }
477     if (abs(tanHalfFovy) > EPSILON) {
478         result.y.y = 1.0f / (tanHalfFovy);
479     } else {
480         result.y.y = HUGE_VALF;
481     }
482 
483     if (const auto div = (zFar - zNear); abs(div) > EPSILON) {
484         result.z.z = zFar / div;
485         result.w.z = -(zFar * zNear) / div;
486     } else {
487         result.z.z = HUGE_VALF;
488         result.w.z = HUGE_VALF;
489     }
490     result.z.w = 1.0f;
491     return result;
492 }
493 
494 /** Creates matrix for right handed symmetric perspective view frustum, near and far clip planes correspond to z
495  * normalized device coordinates of 0 and +1 */
496 static inline Mat4X4 PerspectiveRhZo(float fovy, float aspect, float zNear, float zFar)
497 {
498     Mat4X4 result = PerspectiveLhZo(fovy, aspect, zNear, zFar);
499     result.z.z = -result.z.z;
500     result.z.w = -result.z.w;
501 
502     return result;
503 }
504 
505 /** Creates matrix for left handed symmetric perspective view frustum, near and far clip planes correspond to z
506  * normalized device coordinates of -1 and +1 */
507 static inline Mat4X4 PerspectiveLhNo(float fovy, float aspect, float zNear, float zFar)
508 {
509     float const tanHalfFovy = tan(fovy / 2.0f);
510 
511     Mat4X4 result(0.f);
512     if (const auto div = (aspect * tanHalfFovy); abs(div) > EPSILON) {
513         result.x.x = 1.0f / div;
514     } else {
515         result.x.x = HUGE_VALF;
516     }
517     if (abs(tanHalfFovy) > EPSILON) {
518         result.y.y = 1.0f / (tanHalfFovy);
519     } else {
520         result.y.y = HUGE_VALF;
521     }
522 
523     if (const auto div = (zFar - zNear); abs(div) > EPSILON) {
524         result.z.z = (zFar + zNear) / div;
525         result.w.z = -(2.0f * zFar * zNear) / div;
526     } else {
527         result.z.z = HUGE_VALF;
528         result.w.z = HUGE_VALF;
529     }
530     result.z.w = 1.0f;
531     return result;
532 }
533 
534 /** Creates a matrix for right handed symmetric perspective view frustum, near and far clip planes correspond to z
535  * normalized device coordinates of -1 and +1 */
536 static inline Mat4X4 PerspectiveRhNo(float fovy, float aspect, float zNear, float zFar)
537 {
538     Mat4X4 result = PerspectiveLhNo(fovy, aspect, zNear, zFar);
539     result.z.z = -result.z.z;
540     result.z.w = -result.z.w;
541 
542     return result;
543 }
544 
545 /** Creates matrix for orthographic parallel viewing volume using left handed coordinates, near and far clip planes
546  * correspond to z normalized device coordinates of 0 and +1 */
547 static inline Mat4X4 OrthoLhZo(float left, float right, float bottom, float top, float zNear, float zFar)
548 {
549     Mat4X4 result(1.f);
550     result.x.x = 2.0f / (right - left);
551     result.y.y = 2.0f / (top - bottom);
552     result.z.z = 1.0f / (zFar - zNear);
553     result.w.x = -(right + left) / (right - left);
554     result.w.y = -(top + bottom) / (top - bottom);
555     result.w.z = -zNear / (zFar - zNear);
556     return result;
557 }
558 
559 /** Creates matrix for orthographic parallel viewing volume using left handed coordinates, near and far clip planes
560  * correspond to z normalized device coordinates of -1 and +1 */
561 static inline Mat4X4 OrthoLhNo(float left, float right, float bottom, float top, float zNear, float zFar)
562 {
563     Mat4X4 result(1.f);
564     result.x.x = 2.0f / (right - left);
565     result.y.y = 2.0f / (top - bottom);
566     result.z.z = 2.0f / (zFar - zNear);
567     result.w.x = -(right + left) / (right - left);
568     result.w.y = -(top + bottom) / (top - bottom);
569     result.w.z = -(zFar + zNear) / (zFar - zNear);
570     return result;
571 }
572 
573 /** Creates matrix for orthographic parallel viewing volume using right handed coordinates, near and far clip planes
574  * correspond to z normalized device coordinates of 0 and +1 */
575 static inline Mat4X4 OrthoRhZo(float left, float right, float bottom, float top, float zNear, float zFar)
576 {
577     Mat4X4 result = OrthoLhZo(left, right, bottom, top, zNear, zFar);
578     result.z.z = -result.z.z;
579     return result;
580 }
581 
582 /** Creates matrix for orthographic parallel viewing volume using right handed coordinates, near and far clip planes
583  * correspond to z normalized device coordinates of -1 and +1 */
584 static inline Mat4X4 OrthoRhNo(float left, float right, float bottom, float top, float zNear, float zFar)
585 {
586     Mat4X4 result = OrthoLhNo(left, right, bottom, top, zNear, zFar);
587     result.z.z = -result.z.z;
588     return result;
589 }
590 
591 /** Creates matrix for left handed perspective view frustum, near and far clip planes correspond to z normalized device
592  * coordinates of 0 and +1 */
593 static inline Mat4X4 PerspectiveLhZo(float left, float right, float bottom, float top, float zNear, float zFar)
594 {
595     Mat4X4 result(0.f);
596     if (const float width = right - left; abs(width) > EPSILON) {
597         result.x.x = 2.0f * zNear / width;
598         result.z.x = (left + right) / width;
599     } else {
600         result.x.x = HUGE_VALF;
601         result.z.x = HUGE_VALF;
602     }
603 
604     if (const float height = top - bottom; abs(height) > EPSILON) {
605         result.y.y = 2.0f * zNear / height;
606         result.z.y = (top + bottom) / height;
607     } else {
608         result.y.y = HUGE_VALF;
609         result.z.y = HUGE_VALF;
610     }
611 
612     if (const float depth = zFar - zNear; abs(depth) > EPSILON) {
613         result.z.z = zFar / depth;
614         result.w.z = -(zFar * zNear) / depth;
615     } else {
616         result.z.z = HUGE_VALF;
617         result.w.z = HUGE_VALF;
618     }
619     result.z.w = 1.0f;
620     return result;
621 }
622 
623 /** Creates matrix for right handed perspective view frustum, near and far clip planes correspond to z normalized device
624  * coordinates of 0 and +1 */
625 static inline Mat4X4 PerspectiveRhZo(float left, float right, float bottom, float top, float zNear, float zFar)
626 {
627     Mat4X4 result = PerspectiveLhZo(left, right, bottom, top, zNear, zFar);
628     result.z.z = -result.z.z;
629     result.z.w = -result.z.w;
630 
631     return result;
632 }
633 
634 /** Creates matrix for left handed perspective view frustum, near and far clip planes correspond to z normalized device
635  * coordinates of -1 and +1 */
636 static inline Mat4X4 PerspectiveLhNo(float left, float right, float bottom, float top, float zNear, float zFar)
637 {
638     Mat4X4 result(0.f);
639     if (const float width = right - left; abs(width) > EPSILON) {
640         result.x.x = 2.0f * zNear / width;
641         result.z.x = (left + right) / width;
642     } else {
643         result.x.x = HUGE_VALF;
644         result.z.x = HUGE_VALF;
645     }
646 
647     if (const float height = top - bottom; abs(height) > EPSILON) {
648         result.y.y = 2.0f * zNear / height;
649         result.z.y = (top + bottom) / height;
650     } else {
651         result.y.y = HUGE_VALF;
652         result.z.y = HUGE_VALF;
653     }
654 
655     if (const float depth = zFar - zNear; abs(depth) > EPSILON) {
656         result.z.z = (zNear + zFar) / depth;
657         result.w.z = -(2.0f * zFar * zNear) / depth;
658     } else {
659         result.z.z = HUGE_VALF;
660         result.w.z = HUGE_VALF;
661     }
662     result.z.w = 1.0f;
663     return result;
664 }
665 
666 /** Creates matrix for right handed perspective view frustum, near and far clip planes correspond to z normalized device
667  * coordinates of -1 and +1 */
668 static inline Mat4X4 PerspectiveRhNo(float left, float right, float bottom, float top, float zNear, float zFar)
669 {
670     Mat4X4 result = PerspectiveLhNo(left, right, bottom, top, zNear, zFar);
671     result.z.z = -result.z.z;
672     result.z.w = -result.z.w;
673 
674     return result;
675 }
676 
677 /** Right handed look at function */
678 static inline Mat4X4 LookAtRh(Vec3 const& eye, Vec3 const& center, Vec3 const& up)
679 {
680     Vec3 const f(Normalize(center - eye));
681     Vec3 const s(Normalize(Cross(f, up)));
682     Vec3 const u(Cross(s, f));
683 
684     Mat4X4 result(1.f);
685     result.x.x = s.x;
686     result.y.x = s.y;
687     result.z.x = s.z;
688     result.x.y = u.x;
689     result.y.y = u.y;
690     result.z.y = u.z;
691     result.x.z = -f.x;
692     result.y.z = -f.y;
693     result.z.z = -f.z;
694     result.w.x = -Dot(s, eye);
695     result.w.y = -Dot(u, eye);
696     result.w.z = Dot(f, eye);
697     return result;
698 }
699 
700 /** Left handed look at function */
701 static inline Mat4X4 LookAtLh(Vec3 const& eye, Vec3 const& center, Vec3 const& up)
702 {
703     Vec3 const f(Normalize(center - eye));
704     Vec3 const s(Normalize(Cross(up, f)));
705     Vec3 const u(Cross(f, s));
706 
707     Mat4X4 result(1.f);
708     result.x.x = s.x;
709     result.y.x = s.y;
710     result.z.x = s.z;
711     result.x.y = u.x;
712     result.y.y = u.y;
713     result.z.y = u.z;
714     result.x.z = f.x;
715     result.y.z = f.y;
716     result.z.z = f.z;
717     result.w.x = -Dot(s, eye);
718     result.w.y = -Dot(u, eye);
719     result.w.z = -Dot(f, eye);
720     return result;
721 }
722 
723 /** Decompose matrix */
724 static inline bool Decompose(
725     Mat4X4 const& modelMatrix, Vec3& scale, Quat& orientation, Vec3& translation, Vec3& skew, Vec4& perspective)
726 {
727     Mat4X4 localMatrix(modelMatrix);
728 
729     if (localMatrix.w.w != 1.f) {
730         if (abs(localMatrix.w.w) < EPSILON) {
731             return false;
732         }
733 
734         for (size_t i = 0; i < 4U; ++i) {
735             for (size_t j = 0; j < 4U; ++j) {
736                 localMatrix[i][j] /= localMatrix.w.w;
737             }
738         }
739     }
740 
741     if (abs(localMatrix.x.w) >= EPSILON || abs(localMatrix.y.w) >= EPSILON || abs(localMatrix.z.w) >= EPSILON) {
742         Vec4 rightHandSide;
743         rightHandSide.x = localMatrix.x.w;
744         rightHandSide.y = localMatrix.y.w;
745         rightHandSide.z = localMatrix.z.w;
746         rightHandSide.w = localMatrix.w.w;
747 
748         Mat4X4 perspectiveMatrix(localMatrix);
749         for (size_t i = 0U; i < 3U; i++) {
750             perspectiveMatrix[i].w = 0.0f;
751         }
752         perspectiveMatrix.w.w = 1.0f;
753 
754         float determinant;
755         const Mat4X4 inversePerspectiveMatrix = Inverse(perspectiveMatrix, determinant);
756         if (abs(determinant) < EPSILON) {
757             return false;
758         }
759 
760         const Mat4X4 transposedInversePerspectiveMatrix = Transpose(inversePerspectiveMatrix);
761 
762         perspective = transposedInversePerspectiveMatrix * rightHandSide;
763 
764         localMatrix.x.w = localMatrix.y.w = localMatrix.z.w = 0.0f;
765         localMatrix.w.w = 1.0f;
766     } else {
767         perspective = Vec4(0.0f, 0.0f, 0.0f, 1.0f);
768     }
769 
770     translation = Vec3(localMatrix.w);
771     localMatrix.w = Vec4(0.0f, 0.0f, 0.0f, localMatrix.w.w);
772 
773     Vec3 row[3];
774     for (size_t i = 0U; i < 3U; ++i) {
775         for (size_t j = 0U; j < 3U; ++j) {
776             row[i][j] = localMatrix[i][j];
777         }
778     }
779 
780     scale.x = sqrt(Dot(row[0], row[0]));
781 
782     row[0] = Math::Scale(row[0], 1.0f);
783 
784     skew.z = Dot(row[0], row[1]);
785     row[1] = Combine(row[1], row[0], 1.0f, -skew.z);
786 
787     scale.y = sqrt(Dot(row[1], row[1]));
788     row[1] = Math::Scale(row[1], 1.0f);
789     skew.z /= scale.y;
790 
791     skew.y = Dot(row[0], row[2]);
792     row[2] = Combine(row[2], row[0], 1.0f, -skew.y);
793     skew.x = Dot(row[1], row[2]);
794     row[2] = Combine(row[2], row[1], 1.0f, -skew.x);
795 
796     scale.z = sqrt(Dot(row[2], row[2]));
797     row[2] = Math::Scale(row[2], 1.0f);
798     skew.y /= scale.z;
799     skew.x /= scale.z;
800 
801     const Vec3 pDum3 = Cross(row[1], row[2]);
802     if (Dot(row[0], pDum3) < 0) {
803         for (size_t i = 0U; i < 3U; i++) {
804             scale[i] *= -1.0f;
805             row[i] *= -1.0f;
806         }
807     }
808 
809     unsigned i, j, k = 0U;
810     float root;
811     const float trace = row[0].x + row[1].y + row[2].z;
812     if (trace > 0.0f) {
813         root = sqrt(trace + 1.0f);
814         orientation.w = 0.5f * root;
815         root = 0.5f / root; // root cannot be zero as it's square root of at least 1
816         orientation.x = root * (row[1].z - row[2].y);
817         orientation.y = root * (row[2].x - row[0].z);
818         orientation.z = root * (row[0].y - row[1].x);
819     } else { // End if > 0
820         constexpr const unsigned next[3] = { 1U, 2U, 0U };
821         i = 0U;
822         if (row[1].y > row[0].x) {
823             i = 1U;
824         }
825         if (row[2].z > row[i][i]) {
826             i = 2U;
827         }
828         j = next[i];
829         k = next[j];
830 
831         root = sqrt(row[i][i] - row[j][j] - row[k][k] + 1.0f);
832 
833         orientation[i] = 0.5f * root;
834         root = (abs(root) > EPSILON) ? 0.5f / root : HUGE_VALF;
835         orientation[j] = root * (row[i][j] + row[j][i]);
836         orientation[k] = root * (row[i][k] + row[k][i]);
837         orientation.w = root * (row[j][k] - row[k][j]);
838     } // End if <= 0
839 
840     return true;
841 }
842 /** Check if matrix has any rotation/shear components */
843 static inline bool HasRotation(Mat3X3 const& m)
844 {
845     return (m.x.y != 0.f || m.y.x != 0.f);
846 }
847 
848 /** Check if matrix has any rotation/shear components */
849 static inline bool HasRotation(Mat4X4 const& m)
850 {
851     return (m.x.y != 0.f || m.x.z != 0.f || m.y.x != 0.f || m.y.z != 0.f || m.z.x != 0.f || m.z.y != 0.f);
852 }
853 
854 /** @} */
855 } // namespace Math
856 BASE_END_NAMESPACE()
857 
858 #endif // API_BASE_MATH_MATRIX_UTIL_H
859