LiteFX 0.4.1.2025
Computer Graphics Engine
Loading...
Searching...
No Matches
matrix.hpp
1#pragma once
2
3#include <cassert>
4#include <algorithm>
5#include <utility>
6#include <span>
7#include <array>
8#include <vector>
9#include <ranges>
10#include <initializer_list>
11
12#ifdef __cpp_lib_mdspan
13#include <mdspan>
14#endif
15
16#ifdef LITEFX_BUILD_WITH_GLM
17#include <glm/matrix.hpp>
18#endif
19
20#ifdef LITEFX_BUILD_WITH_DIRECTX_MATH
21#include <DirectXMath.h>
22#endif
23
24namespace LiteFX::Math {
25
37 template <typename T, unsigned ROWS, unsigned COLS> requires
38 (ROWS >= 2 && COLS >= 2) && std::is_standard_layout_v<T> && std::is_trivially_copyable_v<T>
39 struct Matrix final {
40 public:
44 static constexpr size_t mat_rows = ROWS;
45
49 static constexpr size_t mat_cols = COLS;
50
54 using scalar_type = T;
55
60
66 template <unsigned rows, unsigned cols>
68
69 protected:
70 using array_type = std::array<scalar_type, mat_rows * mat_cols>;
71 array_type m_elements = { }; // NOLINT
72
73 public:
77 constexpr Matrix() noexcept = default;
78
83 constexpr Matrix(T val) noexcept {
84 std::fill(std::begin(m_elements), std::end(m_elements), val);
85 }
86
91 constexpr Matrix(array_type&& array) noexcept :
92 m_elements(std::move(array))
93 {
94 }
95
100 constexpr Matrix(std::initializer_list<scalar_type> elements) noexcept {
101 std::ranges::move(elements, std::begin(m_elements));
102 }
103
110 template <unsigned rows, unsigned cols>
111 constexpr Matrix(const Matrix<scalar_type, rows, cols>& _other) {
112 for (size_t r { 0 }; r < rows && r < mat_rows; ++r)
113 std::ranges::copy(_other.row(r), std::begin(m_elements) + r * mat_cols);
114 }
115
120 constexpr Matrix(Matrix&& _other) noexcept = default;
121
126 constexpr Matrix(const Matrix& _other) = default;
127
133 constexpr Matrix& operator=(Matrix&& _other) noexcept = default;
134
140 constexpr Matrix& operator=(const Matrix& _other) = default;
141
145 constexpr ~Matrix() noexcept = default;
146
151 constexpr static mat_type identity() noexcept {
152 std::array<scalar_type, mat_rows * mat_cols> data { };
153
154 for (size_t i = 0; i < mat_rows && i < mat_cols; ++i)
155 data[i * mat_cols + i] = 1.0f; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
156
157 return mat_type(std::move(data));
158 }
159
160 public:
165 constexpr const scalar_type* elements() const noexcept {
166 return m_elements.data();
167 }
168
173 constexpr scalar_type* elements() noexcept {
174 return m_elements.data();
175 }
176
181 consteval size_t size() const noexcept {
182 return mat_rows * mat_cols;
183 }
184
189 constexpr auto begin() noexcept {
190 return m_elements.begin();
191 }
192
197 constexpr auto end() noexcept {
198 return m_elements.end();
199 }
200
205 constexpr auto cbegin() const noexcept {
206 return m_elements.cbegin();
207 }
208
213 constexpr auto cend() const noexcept {
214 return m_elements.cend();
215 }
216
223 constexpr scalar_type at(size_t row, size_t col) const noexcept {
224 assert(row < mat_rows && col < mat_cols);
225
226 return m_elements[row * mat_cols + col];
227 };
228
235 constexpr scalar_type& at(size_t row, size_t col) noexcept {
236 assert(row < mat_rows && col < mat_cols);
237
238 return m_elements[row * mat_cols + col];
239 };
240
246 constexpr std::span<const scalar_type> row(size_t row) const noexcept {
247 assert(row < mat_rows);
248
249 return std::span(m_elements.begin() + row * mat_cols, mat_cols);
250 }
251
257 constexpr std::span<scalar_type> row(size_t row) noexcept {
258 assert(row < mat_rows);
259
260 return std::span(m_elements.begin() + row * mat_cols, mat_cols);
261 }
262
272 constexpr std::array<scalar_type, mat_cols> column(size_t col) const noexcept {
273 assert(col <= mat_cols);
274
275 return m_elements | std::views::drop(col) | std::views::stride(mat_cols) | std::ranges::to<std::array<scalar_type, mat_cols>>();
276 }
277
278#ifdef __cpp_multidimensional_subscript
285 constexpr scalar_type operator[](size_t row, size_t col) const noexcept {
286 return this->at(row, col);
287 }
288
295 constexpr scalar_type& operator[](size_t row, size_t col) noexcept {
296 return this->at(row, col);
297 }
298#endif
299
305 constexpr scalar_type operator[](std::pair<size_t, size_t> position) const noexcept {
306 return this->at(position.first, position.second);
307 }
308
314 constexpr scalar_type& operator[](std::pair<size_t, size_t> position) noexcept {
315 return this->at(position.first, position.second);
316 }
317
318#ifdef __cpp_lib_mdspan
322 constexpr operator std::mdspan<const scalar_type, std::extents<std::size_t, mat_rows, mat_cols>>() const noexcept {
323 return std::mdspan<const scalar_type, std::extents<std::size_t, mat_rows, mat_cols>>(m_elements.data());
324 }
325
329 constexpr operator std::mdspan<scalar_type, std::extents<std::size_t, mat_rows, mat_cols>>() noexcept {
330 return std::mdspan<scalar_type, std::extents<std::size_t, mat_rows, mat_cols>>(m_elements.data());
331 }
332#endif
333
337 constexpr operator std::array<T, mat_rows * mat_cols>() const noexcept {
338 return m_elements;
339 }
340
344 constexpr operator std::vector<T>() const noexcept {
345 return std::vector<T>(std::begin(m_elements), std::end(m_elements));
346 }
347
351 constexpr operator std::span<const scalar_type>() const noexcept {
352 return std::span(m_elements.data(), m_elements.size());
353 }
354
358 constexpr operator std::span<scalar_type>() noexcept {
359 return std::span(m_elements.data(), m_elements.size());
360 }
361
371 std::array<scalar_type, mat_cols * mat_rows> data { };
372
373 for (int r{ 0 }; r < mat_rows; ++r)
374 {
375 auto row = this->row(r);
376
377 for (int c{ 0 }; c < mat_cols; ++c)
378 data[c * mat_rows + r] = row[c];
379 }
380
381 return generic_mat_type<mat_cols, mat_rows>(std::move(data));
382 }
383
388 consteval bool symmetric() const noexcept {
389 return ROWS == COLS;
390 }
391
392#ifdef LITEFX_BUILD_WITH_GLM
393 // NOTE: glm stores matrices in column-major order and also initializes them this way.
394 public:
399 constexpr Matrix(const glm::mat<mat_cols, mat_rows, scalar_type>& mat) noexcept {
400 for (int r { 0 }; r < mat_rows; ++r)
401 for (int c { 0 }; c < mat_cols; ++c)
402 m_elements[r * mat_cols + c] = mat[c][r]; // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
403 }
404
409 constexpr Matrix(glm::mat<mat_cols, mat_rows, scalar_type>&& mat) noexcept {
410 for (size_t r { 0 }; r < mat_rows; ++r)
411 for (size_t c { 0 }; c < mat_cols; ++c)
412 m_elements[r * mat_cols + c] = std::move(mat[c][r]); // NOLINT(cppcoreguidelines-pro-bounds-constant-array-index)
413 }
414
419 template <unsigned rows, unsigned cols>
420 constexpr operator glm::mat<cols, rows, scalar_type>() const noexcept requires (mat_rows >= rows && mat_cols >= cols) {
421 std::array<scalar_type, static_cast<size_t>(cols * rows)> data;
422 glm::mat<cols, rows, scalar_type> mat;
423
424 for (size_t c { 0 }; c < cols; ++c)
425 for (size_t r { 0 }; r < rows; ++r)
426 data[c * mat_rows + r] = this->at(r, c);
427
428 std::memcpy(&mat, data.data(), data.size() * sizeof(scalar_type));
429 return mat;
430 }
431
436 constexpr operator glm::mat<2, 2, scalar_type>() const noexcept requires (mat_rows >= 2 && mat_cols >= 2) {
437 return glm::mat<2, 2, scalar_type>(at(0, 0), at(1, 0), at(0, 1), at(1, 1));
438 }
439
444 constexpr operator glm::mat<2, 3, scalar_type>() const noexcept requires (mat_rows >= 3 && mat_cols >= 2) {
445 return glm::mat<2, 3, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(0, 1), at(1, 1), at(2, 1));
446 }
447
452 constexpr operator glm::mat<2, 4, scalar_type>() const noexcept requires (mat_rows >= 4 && mat_cols >= 2) {
453 return glm::mat<2, 4, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(3, 0), at(0, 1), at(1, 1), at(2, 1), at(3, 1));
454 }
455
460 constexpr operator glm::mat<3, 2, scalar_type>() const noexcept requires (mat_rows >= 2 && mat_cols >= 3) {
461 return glm::mat<3, 2, scalar_type>(at(0, 0), at(1, 0), at(0, 1), at(1, 1), at(0, 2), at(1, 2));
462 }
463
468 constexpr operator glm::mat<4, 2, scalar_type>() const noexcept requires (mat_rows >= 2 && mat_cols >= 4) {
469 return glm::mat<4, 2, scalar_type>(at(0, 0), at(1, 0), at(0, 1), at(1, 1), at(0, 2), at(1, 2), at(0, 3), at(1, 3));
470 }
471
476 constexpr operator glm::mat<3, 3, scalar_type>() const noexcept requires (mat_rows >= 3 && mat_cols >= 3) {
477 return glm::mat<3, 3, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(0, 1), at(1, 1), at(2, 1), at(0, 2), at(1, 2), at(2, 2));
478 }
479
484 constexpr operator glm::mat<3, 4, scalar_type>() const noexcept requires (mat_rows >= 4 && mat_cols >= 3) {
485 return glm::mat<3, 4, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(3, 0), at(0, 1), at(1, 1), at(2, 1), at(3, 1), at(0, 2), at(1, 2), at(2, 2), at(3, 2));
486 }
487
492 constexpr operator glm::mat<4, 3, scalar_type>() const noexcept requires (mat_rows >= 3 && mat_cols >= 4) {
493 return glm::mat<4, 3, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(0, 1), at(1, 1), at(2, 1), at(0, 2), at(1, 2), at(2, 2), at(0, 3), at(1, 3), at(2, 3));
494 }
495
500 constexpr operator glm::mat<4, 4, scalar_type>() const noexcept requires (mat_rows >= 4 && mat_cols >= 4) {
501 return glm::mat<4, 4, scalar_type>(at(0, 0), at(1, 0), at(2, 0), at(3, 0), at(0, 1), at(1, 1), at(2, 1), at(3, 1), at(0, 2), at(1, 2), at(2, 2), at(3, 2), at(0, 3), at(1, 3), at(2, 3), at(3, 3));
502 }
503#endif // LITEFX_BUILD_WITH_GLM
504
505#ifdef LITEFX_BUILD_WITH_DIRECTX_MATH
506 public:
511 constexpr Matrix(const DirectX::XMFLOAT3X3& mat) noexcept {
512 for (int r { 0 }; r < 3; ++r)
513 for (int c { 0 }; c < 3; ++c)
514 at(r, c) = mat(r, c);
515 }
516
521 constexpr Matrix(const DirectX::XMFLOAT4X3& mat) noexcept {
522 for (int r { 0 }; r < 4; ++r)
523 for (int c { 0 }; c < 3; ++c)
524 at(r, c) = mat(r, c);
525 }
526
531 constexpr Matrix(const DirectX::XMFLOAT3X4& mat) noexcept {
532 for (int r { 0 }; r < 3; ++r)
533 for (int c { 0 }; c < 4; ++c)
534 at(r, c) = mat(r, c);
535 }
536
541 constexpr Matrix(const DirectX::XMFLOAT4X4& mat) noexcept {
542 for (int r { 0 }; r < 4; ++r)
543 for (int c { 0 }; c < 4; ++c)
544 at(r, c) = mat(r, c);
545 }
546
551 constexpr operator DirectX::XMMATRIX() const noexcept requires ((mat_rows == 3 || mat_rows == 4) && (mat_cols == 3 || mat_cols == 4) && std::convertible_to<scalar_type, float>) {
552 if constexpr (mat_rows == 3 && mat_cols == 3)
553 {
554 DirectX::XMFLOAT3X3 mat = static_cast<DirectX::XMFLOAT3X3>(*this);
555 return DirectX::XMLoadFloat3x3(&mat);
556 }
557 else if constexpr (mat_rows == 3 && mat_cols == 4)
558 {
559 DirectX::XMFLOAT3X4 mat = static_cast<DirectX::XMFLOAT3X4>(*this);
560 return DirectX::XMLoadFloat3x4(&mat);
561 }
562 else if constexpr (mat_rows == 4 && mat_cols == 3)
563 {
564 DirectX::XMFLOAT4X3 mat = static_cast<DirectX::XMFLOAT4X3>(*this);
565 return DirectX::XMLoadFloat4x3(&mat);
566 }
567 if constexpr (mat_rows == 4 && mat_cols == 4)
568 {
569 DirectX::XMFLOAT4X4 mat = static_cast<DirectX::XMFLOAT4X4>(*this);
570 return DirectX::XMLoadFloat4x4(&mat);
571 }
572
573 std::unreachable();
574 }
575
580 constexpr operator DirectX::XMFLOAT3X3() const noexcept requires (mat_rows >= 3 && mat_cols >= 3 && std::convertible_to<scalar_type, float>) {
581 return DirectX::XMFLOAT3X3(at(0, 0), at(0, 1), at(0, 2), at(1, 0), at(1, 2), at(2, 0), at(2, 1), at(2, 2));
582 }
583
588 constexpr operator DirectX::XMFLOAT4X3() const noexcept requires (mat_rows >= 4 && mat_cols >= 3 && std::convertible_to<scalar_type, float>) {
589 return DirectX::XMFLOAT4X3(at(0, 0), at(0, 1), at(0, 2), at(1, 0), at(1, 2), at(2, 0), at(2, 1), at(2, 2), at(3, 0), at(3, 1), at(3, 2));
590 }
591
596 constexpr operator DirectX::XMFLOAT3X4() const noexcept requires (mat_rows >= 3 && mat_cols >= 4 && std::convertible_to<scalar_type, float>) {
597 return DirectX::XMFLOAT3X4(at(0, 0), at(0, 1), at(0, 2), at(0, 3), at(1, 0), at(1, 2), at(1, 3), at(2, 0), at(2, 1), at(2, 2), at(2, 3));
598 }
599
604 constexpr operator DirectX::XMFLOAT4X4() const noexcept requires (mat_rows >= 4 && mat_cols >= 4 && std::convertible_to<scalar_type, float>) {
605 return DirectX::XMFLOAT4X4(at(0, 0), at(0, 1), at(0, 2), at(0, 3), at(1, 0), at(1, 2), at(1, 3), at(2, 0), at(2, 1), at(2, 2), at(2, 3), at(3, 0), at(3, 1), at(3, 2), at(3, 3));
606 }
607#endif // LITEFX_BUILD_WITH_DIRECTX_MATH
608 };
609
614 template<typename T> using TMatrix2 = Matrix<T, 2, 2>;
615
620 template<typename T> using TMatrix3 = Matrix<T, 3, 3>;
621
626 template<typename T> using TMatrix4 = Matrix<T, 4, 4>;
627
632 template<typename T> using TMatrix3x4 = Matrix<T, 3, 4>;
633
634}
Definition math.hpp:30
An algebraic matrix type.
Definition matrix.hpp:39
consteval bool symmetric() const noexcept
Returns whether or not the matrix is symmetric, that is the number of rows and columns are equal.
Definition matrix.hpp:388
constexpr generic_mat_type< mat_cols, mat_rows > transpose() const noexcept
Returns a copy of the matrix where the elements are transposed.
Definition matrix.hpp:370
constexpr Matrix(array_type &&array) noexcept
Initializes a matrix with an array of values.
Definition matrix.hpp:91
constexpr Matrix & operator=(const Matrix &_other)=default
Copies the elements of another matrix into the current matrix.
constexpr scalar_type * elements() noexcept
Returns a pointer to the raw data of the matrix.
Definition matrix.hpp:173
consteval size_t size() const noexcept
Returns the number of elements of the matrix.
Definition matrix.hpp:181
constexpr const scalar_type * elements() const noexcept
Returns a pointer to the raw data of the matrix.
Definition matrix.hpp:165
constexpr scalar_type at(size_t row, size_t col) const noexcept
Returns the element at a specified position.
Definition matrix.hpp:223
constexpr scalar_type & operator[](std::pair< size_t, size_t > position) noexcept
Returns an element of the matrix.
Definition matrix.hpp:314
constexpr scalar_type operator[](std::pair< size_t, size_t > position) const noexcept
Returns an element of the matrix.
Definition matrix.hpp:305
T scalar_type
The type of the matrix elements.
Definition matrix.hpp:54
constexpr Matrix(Matrix &&_other) noexcept=default
Initializes a matrix by taking over another matrix.
constexpr ~Matrix() noexcept=default
Destroys the matrix instance.
constexpr auto end() noexcept
Returns an iterator for that addresses the end of the matrix elements.
Definition matrix.hpp:197
constexpr auto cend() const noexcept
Returns a constant iterator for that addresses the end of the matrix elements.
Definition matrix.hpp:213
constexpr Matrix & operator=(Matrix &&_other) noexcept=default
Moves the elements of the other matrix to the current matrix.
constexpr auto begin() noexcept
Returns an iterator for that addresses the begin of the matrix elements.
Definition matrix.hpp:189
constexpr Matrix() noexcept=default
Initializes an empty matrix.
constexpr std::array< scalar_type, mat_cols > column(size_t col) const noexcept
Returns a copy of a column over the matrix.
Definition matrix.hpp:272
constexpr Matrix(const Matrix &_other)=default
Initializes a matrix with the values provided by another matrix.
constexpr Matrix(const Matrix< scalar_type, rows, cols > &_other)
Initializes a copy from another matrix, that might have different dimensions.
Definition matrix.hpp:111
constexpr std::span< scalar_type > row(size_t row) noexcept
Returns a view over a row of the matrix.
Definition matrix.hpp:257
std::array< scalar_type, mat_rows *mat_cols > array_type
Definition matrix.hpp:70
constexpr auto cbegin() const noexcept
Returns a constant iterator for that addresses the begin of the matrix elements.
Definition matrix.hpp:205
constexpr scalar_type & at(size_t row, size_t col) noexcept
Returns the element at a specified position.
Definition matrix.hpp:235
constexpr Matrix(std::initializer_list< scalar_type > elements) noexcept
Initializes the matrix with a set of values.
Definition matrix.hpp:100
constexpr std::span< const scalar_type > row(size_t row) const noexcept
Returns a view over a row of the matrix.
Definition matrix.hpp:246