Skip to main content

mat4.ixx File

4x4 matrix type and utilities. More...

Included Headers

#include <cassert> #include <functional> #include <helios.math.types:vec4> #include <helios.math.concepts> #include <helios.math.TransformType>

Namespaces Index

namespacehelios
namespacemath

Mathematical operations and types. More...

Classes Index

structmat4<T>

Represents a 4x4 matrix, stored in column-major order. More...

Description

4x4 matrix type and utilities.

File Listing

The file content with the documentation metadata removed is:

1/**
2 * @file mat4.ixx
3 * @brief 4x4 matrix type and utilities.
4 */
5module;
6
7#include <cassert>
8#include <functional>
9
10export module helios.math.types:mat4;
11
12import helios.math.TransformType;
13import :vec3;
14import :vec4;
15
16
17import helios.math.concepts;
18
19
20export namespace helios::math {
21
22
23
24 /**
25 * @brief Represents a 4x4 matrix, stored in column-major order.
26 *
27 * The `mat4` struct provides a lightweight and efficient way to handle 4D
28 * matrix mathematics for numeric data types.
29 * It stores its components as type `T`. For convenient access, type aliases
30 * `mat4f`, `mat4d` and `mat4i` are available, providing float/double/integer
31 * matrix representatives.
32 *
33 * @tparam T the numeric type of the matrix components.
34 */
35 template<helios::math::Numeric T>
36 struct mat4 {
37 private:
38 /**
39 * @brief Internal array storing matrix components.
40 *
41 * Components are stored in column-major order, that is, each 4 subsequent elements
42 * represent a matrix column.
43 */
44 T m[16];
45
46 public:
47 /**
48 * @brief Default constructor. Initializes all components to 0
49 */
50 explicit constexpr mat4() noexcept : m{} {};
51
52 /**
53 * @brief Creates a diagonal matrix. The diagonal components are initialized
54 * with the value f.
55 *
56 * @param f The scalar value for the diagonal components.
57 */
58 explicit constexpr mat4(const T f) noexcept
59 : m{ f, T{}, T{}, T{},
60 T{}, f, T{}, T{},
61 T{}, T{}, f, T{},
62 T{}, T{}, T{}, f} {}
63
64 /**
65 * @brief Creates a diagonal matrix with the components of vec3<T> as the
66 * diagonal elements. Element at [4, 4] is set to 1
67 *
68 * @param f The vector containing the diagonal components.
69 */
70 explicit constexpr mat4(const vec3<T> f) noexcept
71 : m{ f[0], T{}, T{}, T{},
72 T{}, f[1], T{}, T{},
73 T{}, T{}, f[2], T{},
74 T{}, T{}, T{}, static_cast<T>(1)} {}
75
76 /**
77 * @brief Constructs a new `mat4` with all 16 components explicitly specified.
78 *
79 * @details The values are stored in column major order, that is, the first 4 arguments
80 * represent the first column, and so on.
81 *
82 * Parameter naming convention: `fR_C` where R is the row index and C is the column index.
83 */
84 constexpr mat4(
85 const T f0_0, const T f1_0, const T f2_0, const T f3_0,
86 const T f0_1, const T f1_1, const T f2_1, const T f3_1,
87 const T f0_2, const T f1_2, const T f2_2, const T f3_2,
88 const T f0_3, const T f1_3, const T f2_3, const T f3_3
89 ) : m{
90 f0_0, f1_0, f2_0, f3_0,
91 f0_1, f1_1, f2_1, f3_1,
92 f0_2, f1_2, f2_2, f3_2,
93 f0_3, f1_3, f2_3, f3_3
94 } { }
95
96
97 /**
98 * @brief Convenient method to construct a 4x4 identity matrix
99 *
100 * @tparam T The numeric type of the matrix components.
101 *
102 * @return A new mat4<T>-identity matrix-
103 */
104 static mat4<T> identity() noexcept {
105 return mat4<T>(1);
106 }
107
108
109 /**
110 * @brief Provides read-only access to a matrix component.
111 *
112 * Elements are _accessed_ in column major order: `m[row + col * 4]`,
113 * while the specified indices represent an usual mxn-matrix access,
114 * i.e. for a given 2x4-matrix
115 * [0, 1, 2, 3,
116 * 4, 5, 6, 7,
117 * 8, 9, 10, 11,
118 * 12, 13, 14, 15]
119 *
120 * a call to row(0, 2) returns "2", while the matrix is internally stored
121 * as (1, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15).
122 *
123 * Bounds checking is performed via `assert` in debug builds.
124 *
125 * @param row The zero based row index.
126 * @param col The zero based col index.
127 *
128 * @return A const ref to the component at the specified position.
129 */
130 constexpr const T& operator()(const size_t row, const size_t col) const {
131 assert(row < 4 && col < 4 && "mat4 - Index out of bounds.");
132 return m[row + col * 4];
133 }
134
135
136 /**
137 * @brief Provides read-write access to a matrix component.
138 *
139 * Elements are _accessed_ in column major order: `m[row + col * 4]`,
140 * while the specified indices represent an usual mxn-matrix access,
141 * i.e. for a given 2x4-matrix
142 * [0, 1, 2, 3,
143 * 4, 5, 6, 7,
144 * 8, 9, 10, 11,
145 * 12, 13, 14, 15]
146 *
147 * a call to row(0, 2) returns "2", while the matrix is internally stored
148 * as (0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15).
149 *
150 * Bounds checking is performed via `assert` in debug builds.
151 *
152 * @param row The zero based row index.
153 * @param col The zero based col index.
154 *
155 * @return A ref to the component at the specified position.
156 */
157 constexpr T& operator()(const size_t row, const size_t col) {
158 assert(row < 4 && col < 4 && "mat4 - Index out of bounds.");
159 return m[row + col * 4];
160 }
161
162
163 /**
164 * @brief Compares this matrix element's with the rgt matrix considering
165 * an epsilon value.
166 * Returns true if for all elements the equation |a-b| <= EPSILON
167 * holds.
168 *
169 * EPSILON is set to 0.00001
170 *
171 * @param rgt The other matix to compare with this matrix for equality.
172 *
173 * @return True is the elements of the matrices are considered equal,
174 * otherwise false.
175 *
176 * @see https://realtimecollisiondetection.net/blog/?p=89
177 *
178 * @todo account for abs (values close to zero) and rel
179 * (larger values), move EPSILON to global constant
180 */
181 constexpr bool same(const mat4<T>& rgt) const {
182
183 static const T EPSILON = static_cast<T>(0.00001);
184
185 const auto* leftPtr = value_ptr(*this);
186 const auto* rgtPtr = value_ptr(rgt);
187
188 for (int i = 0; i < 16; i++) {
189 if (std::fabs(leftPtr[i] - rgtPtr[i]) > EPSILON) {
190 return false;
191 }
192 }
193
194 return true;
195 }
196
197
198 /**
199 * @brief Strictly compares the elements of this matrix with the elements
200 * of the rgt matrix.
201 *
202 * @param rgt The right matrix to compare for equal values
203 *
204 * @return True if all elements are equal (==), false otherwise.
205 */
206 constexpr bool operator==(const mat4<T>& rgt) const {
207
208 const auto* leftPtr = value_ptr(*this);
209 const auto* rgtPtr = value_ptr(rgt);
210
211 for (int i = 0; i < 16; i++) {
212 if (leftPtr[i] != rgtPtr[i]) {
213 return false;
214 }
215 }
216
217 return true;
218 }
219
220
221 /**
222 * @brief Performs matrix-multiplication with another `mat4`.
223 * This matrix is the left operand, while `m` is the right operand.
224 *
225 * @param m The right-hand side `mat4<T>` for multiplication.
226 *
227 * @return A new `mat4<T>`, representing the result of the matrix-multiplication.
228 */
229 constexpr mat4<T> operator*(const mat4<T>& m) const {
230 mat4<T> A{};
231 for (int row = 0; row < 4; row++) {
232 for (int col = 0; col < 4; col++) {
233 T sum = T{};
234 sum += (*this)(row, 0) * m(0, col);
235 sum += (*this)(row, 1) * m(1, col);
236 sum += (*this)(row, 2) * m(2, col);
237 sum += (*this)(row, 3) * m(3, col);
238 A(row, col) = sum;
239 }
240 }
241
242 return A;
243 }
244
245
246 /**
247 * @brief Performs matrix-vector-multiplication with a `vec4<T> v`.
248 *
249 * This matrix is the left operand, while `v` is the right operand.
250 *
251 * @param v The right-hand side `vec4<T>` for multiplication.
252 *
253 * @return A new `vec4<T>`, representing the result of the matrix-vector-multiplication.
254 */
255 constexpr vec4<T> operator*(const vec4<T>& v) const {
256 const auto m = *this;
257 return vec4<T>{
258 m(0, 0) * v[0] + m(0, 1) * v[1] + m(0, 2) * v[2] + m(0, 3) * v[3],
259 m(1, 0) * v[0] + m(1, 1) * v[1] + m(1, 2) * v[2] + m(1, 3) * v[3],
260 m(2, 0) * v[0] + m(2, 1) * v[1] + m(2, 2) * v[2] + m(2, 3) * v[3],
261 m(3, 0) * v[0] + m(3, 1) * v[1] + m(3, 2) * v[2] + m(3, 3) * v[3]
262 };
263 }
264
265 /**
266 * @brief Computes the transpose of a 4x4 matrix.
267 *
268 * This function swaps rows and columns: M^T_{ij} = M_{ji}
269 *
270 * @note This is **not** a general-purpose inverse function. For matrices containing
271 * non-uniform scale or shear, use a full inverse calculation instead.
272 *
273 * @tparam T The numeric type of the matrix elements.
274 *
275 * @return The transposed matrix.
276 */
278 const auto m = *this;
280 m(0, 0), m(0, 1), m(0, 2), m(0, 3),
281 m(1, 0), m(1, 1), m(1, 2), m(1, 3),
282 m(2, 0), m(2, 1), m(2, 2), m(2, 3),
283 m(3, 0), m(3, 1), m(3, 2), m(3, 3)
284 };
285 }
286
287 /**
288 * @brief Returns the i-th column of the matrix.
289 *
290 * @param i The zero-based index of the column (0-3).
291 * @return A vec4<T> representing the column.
292 */
293 helios::math::vec4<T> column(unsigned int i) const noexcept {
294 assert(i <= 3 && "unexpected value for column");
295 const auto m = *this;
297 m(0, i), m(1, i), m(2, i), m(3, i)
298 };
299 }
300
301 /**
302 * @brief Computes the inverse of the matrix.
303 *
304 * @return The inverse matrix.
305 *
306 * @see [Len16, Listing 1.11, 44]
307 */
308 constexpr helios::math::mat4<T> inverse() const noexcept {
309 const auto m = *this;
310
311 const auto a = column(0).toVec3();
312 const auto b = column(1).toVec3();
313 const auto c = column(2).toVec3();
314 const auto d = column(3).toVec3();
315
316 const T x = m(3, 0);
317 const T y = m(3, 1);
318 const T z = m(3, 2);
319 const T w = m(3, 3);
320
321 auto s = helios::math::cross(a, b);
322 auto t = helios::math::cross(c, d);
323 auto u = a*y - b*x;
324 auto v = c*w - d*z;
325
326 T invDet = static_cast<T>(1) / (helios::math::dot(s, v) + helios::math::dot(t, u));
327
328 s = s * invDet;
329 t = t * invDet;
330 u = u * invDet;
331 v = v * invDet;
332
333 auto r0 = (helios::math::cross(b, v)) + t*y;
334 auto r1 = (helios::math::cross(v, a)) - t*x;
335 auto r2 = (helios::math::cross(d, u)) + s*w;
336 auto r3 = (helios::math::cross(u, c)) - s*z;
337
339 r0[0], r0[1], r0[2], -helios::math::dot(b, t),
340 r1[0], r1[1], r1[2], helios::math::dot(a, t),
341 r2[0], r2[1], r2[2], -helios::math::dot(d, s),
342 r3[0], r3[1], r3[2], helios::math::dot(c, s)
343 };
344 }
345
346 /**
347 * @brief Extracts the translation component from this matrix.
348 *
349 * @details Returns the translation vector stored in column 3 (elements [0,3], [1,3], [2,3]).
350 * This represents the position offset in a standard TRS (Translation-Rotation-Scale) matrix.
351 *
352 * @return A vec3<T> containing the x, y, z translation components.
353 */
355 const auto m = *this;
357 m(0, 3), m(1, 3), m(2, 3)
358 };
359 }
360
361 /**
362 * @brief Creates a new matrix with the specified translation, preserving other components.
363 *
364 * @details Returns a copy of this matrix with column 3 (translation) replaced by the
365 * given vector. The w-component [3,3] is set to 1 for homogeneous coordinates.
366 *
367 * @param v The new translation vector (x, y, z).
368 *
369 * @return A new mat4<T> with the updated translation component.
370 */
372 const auto m = *this;
374 m(0, 0), m(1, 0), m(2, 0), m(3, 0),
375 m(0, 1), m(1, 1), m(2, 1), m(3, 1),
376 m(0, 2), m(1, 2), m(2, 2), m(3, 2),
377 v[0], v[1], v[2], static_cast<T>(1)
378 };
379 }
380
381 /**
382 * @brief Returns a new 4x4 matrix derived by applying a scaling transformation.
383 *
384 * This function scales the current matrix by modifying specific components
385 * based on the scaling factors provided in the input vector.
386 *
387 * @tparam T The numeric type used for the matrix and vector components.
388 * @param v A 3D vector representing the scaling factors along the x, y, and z axes.
389 * @return A new 4x4 matrix with the scaling transformation applied.
390 * @note The operation maintains column-major order for the matrix components.
391 */
393 const auto m = *this;
395 m(0, 0) * v[0], m(1, 0) * v[0], m(2, 0) * v[0], m(3, 0) * v[0],
396 m(0, 1) * v[1], m(1, 1) * v[1], m(2, 1) * v[1], m(3, 1) * v[1],
397 m(0, 2) * v[2], m(1, 2) * v[2], m(2, 2) * v[2], m(3, 2) * v[2],
398 m(0, 3), m(1, 3), m(2, 3), m(3, 3),
399 };
400 }
401
402
403 /**
404 * @brief Creates a new matrix with the specified translation, preserving other components.
405 *
406 * @details Overload accepting a vec4. Only the xyz components are used; the w component
407 * is ignored and set to 1 in the resulting matrix.
408 *
409 * @param v The new translation vector (x, y, z, w). The w component is ignored.
410 *
411 * @return A new mat4<T> with the updated translation component.
412 */
414 const auto m = *this;
416 m(0, 0), m(1, 0), m(2, 0), m(3, 0),
417 m(0, 1), m(1, 1), m(2, 1), m(3, 1),
418 m(0, 2), m(1, 2), m(2, 2), m(3, 2),
419 v[0], v[1], v[2], static_cast<T>(1)
420 };
421 }
422
423 /**
424 * @brief Decomposes this matrix, extracting only specified components.
425 *
426 * @details This function extracts Translation, Rotation, and/or Scale components
427 * from this 4x4 matrix based on the provided `TransformType` mask.
428 * Components not included in the mask are replaced with identity values.
429 *
430 * The decomposition supports the following cases:
431 * - **TransformType::All**: Returns the original matrix unchanged.
432 * - **TransformType::Translation**: Extracts only the translation (column 3).
433 * - **TransformType::Rotation**: Extracts the normalized rotation from the upper-left 3x3.
434 * - **TransformType::Scale**: Extracts the scale factors (column vector lengths).
435 * - **Combined flags**: Extracts multiple components as specified.
436 *
437 * @note For combined Rotation + Scale without Translation, the upper-left 3x3
438 * is copied directly. For Rotation-only, scale is removed by normalizing columns.
439 *
440 * @tparam T The numeric type of the matrix elements.
441 *
442 * @param type Bitmask specifying which components to extract.
443 *
444 * @return A new matrix containing only the requested transform components.
445 *
446 * @see helios::math::TransformType
447 * @see helios::math::transformTypeMatch()
448 */
450 const auto m = *this;
452 return m;
453 }
454
455 auto id = identity();
457 id(0, 3) = m(0, 3);
458 id(1, 3) = m(1, 3);
459 id(2, 3) = m(2, 3);
460 }
461
464 id(0, 0) = m(0, 0); id(0, 1) = m(0, 1); id(0, 2) = m(0, 2);
465 id(1, 0) = m(1, 0); id(1, 1) = m(1, 1); id(1, 2) = m(1, 2);
466 id(2, 0) = m(2, 0); id(2, 1) = m(2, 1); id(2, 2) = m(2, 2);
467 } else {
468
469 const auto bx = vec3<T>(m(0, 0), m(1, 0), m(2, 0));
470 const auto by = vec3<T>(m(0, 1), m(1, 1), m(2, 1));
471 const auto bz = vec3<T>(m(0, 2), m(1, 2), m(2, 2));
472
473 const auto sx = bx.length();
474 const auto sy = by.length();
475 const auto sz = bz.length();
476
478 vec3<T> rx = sx != 0 ? bx/sx : vec3<T>{1, 0, 0};
479 vec3<T> ry = sy != 0 ? by/sy : vec3<T>{0, 1, 0};
480 vec3<T> rz = sz != 0 ? bz/sz : vec3<T>{0, 0, 1};
481
482 id(0, 0) = rx[0]; id(0, 1) = ry[0]; id(0, 2) = rz[0];
483 id(1, 0) = rx[1]; id(1, 1) = ry[1]; id(1, 2) = rz[1];
484 id(2, 0) = rx[2]; id(2, 1) = ry[2]; id(2, 2) = rz[2];
486 id(0, 0) = sx;
487 id(1, 1) = sy;
488 id(2, 2) = sz;
489 }
490 }
491
492 return id;
493 }
494
495 };
496
497
498 /**
499 * @brief Returns a const pointer to the first element of the matrix's components.
500 *
501 * Useful for APIs that expect a pointer to matrix data, like OpenGL.
502 *
503 * @tparam T The numeric type of the matrix components.
504 *
505 * @param m A reference to the `mat4<T>` matrix.
506 *
507 * @return A const pointer to the element at [0, 0].
508 */
509 template<helios::math::Numeric T>
510 const T* value_ptr(const mat4<T>& m) noexcept {
511 return &m(0, 0);
512 }
513
514
515 /**
516 * @brief Returns a pointer to the first element of the matrix's components.
517 *
518 * Useful for APIs that expect a pointer to matrix data, like OpenGL.
519 *
520 * @tparam T The numeric type of the matrix components.
521 *
522 * @param m A reference to the `mat4<T>` matrix.
523 *
524 * @return A pointer to the element at [0, 0].
525 */
526 template<helios::math::Numeric T>
527 T* value_ptr(mat4<T>& m) noexcept {
528 return &m(0, 0);
529 }
530
531 /**
532 * @brief Type alias for a 4x4 float matrix.
533 */
535
536 /**
537 * @brief Type alias for a 4x4 double matrix.
538 */
540
541 /**
542 * @brief Type alias for a 4x4 integer matrix.
543 */
545
546} // namespace helios::math

Generated via doxygen2docusaurus 2.0.0 by Doxygen 1.15.0.