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