SleakEngine 1.0.0
C++23 multi-backend game engine
Loading...
Searching...
No Matches
Matrix.hpp
Go to the documentation of this file.
1#ifndef _MATRIX_H_
2#define _MATRIX_H_
3
4#include <Core/OSDef.hpp>
5#include "Math.hpp"
6#include <iomanip>
7#include <iostream>
8#include <stdexcept>
9#include <type_traits>
10
11#include "Vector.hpp"
12#include "Quaternion.hpp"
13
14namespace Sleak {
15namespace Math {
16/// Row-major fixed-size matrix with the usual algebra plus 4x4 view/projection
17/// factory methods. Rows==Cols gives an identity default.
18/// @ingroup math
19template <typename T, size_t Rows, size_t Cols>
20class Matrix {
21 public:
22 // Optimized default constructor
23 constexpr Matrix() noexcept {
24 if constexpr (Rows == Cols) {
25 // Identity matrix for square matrices
26 for (size_t i = 0; i < Rows; ++i) {
27 for (size_t j = 0; j < Cols; ++j) {
28 data[i][j] =
29 (i == j) ? static_cast<T>(1) : static_cast<T>(0);
30 }
31 }
32 } else {
33 // Zero matrix for non-square matrices
34 std::memset(data, 0, sizeof(data));
35 }
36 }
37
38 // Optimized initializer list constructor with bounds checking
39 Matrix(std::initializer_list<std::initializer_list<T>> list) {
40 if (list.size() != Rows) {
41 throw std::invalid_argument(
42 "Number of rows does not match matrix dimension");
43 }
44
45 size_t i = 0;
46 for (const auto& row : list) {
47 if (row.size() != Cols) {
48 throw std::invalid_argument(
49 "Number of columns does not match matrix dimension");
50 }
51
52 size_t j = 0;
53 for (const auto& val : row) {
54 data[i][j++] = val;
55 }
56 ++i;
57 }
58 }
59
60 // Bounds-checked element access
61 T& operator()(size_t row, size_t col) {
62 if (row >= Rows || col >= Cols) {
63 throw std::out_of_range("Matrix indices out of range");
64 }
65 return data[row][col];
66 }
67
68 const T& operator()(size_t row, size_t col) const {
69 if (row >= Rows || col >= Cols) {
70 throw std::out_of_range("Matrix indices out of range");
71 }
72 return data[row][col];
73 }
74
75 // Efficient matrix addition
78 for (size_t i = 0; i < Rows; ++i) {
79 for (size_t j = 0; j < Cols; ++j) {
80 result(i, j) = data[i][j] + other(i, j);
81 }
82 }
83 return result;
84 }
85
86 // Efficient matrix subtraction
89 for (size_t i = 0; i < Rows; ++i) {
90 for (size_t j = 0; j < Cols; ++j) {
91 result(i, j) = data[i][j] - other(i, j);
92 }
93 }
94 return result;
95 }
96
97 // Optimized matrix multiplication with early exit for zero-like cases
98 template <size_t OtherCols>
100 const Matrix<T, Cols, OtherCols>& other) const {
102
103 // Quick zero check
104 bool isZero = true;
105 for (size_t i = 0; i < Rows; ++i) {
106 for (size_t j = 0; j < OtherCols; ++j) {
107 T sum = static_cast<T>(0);
108 for (size_t k = 0; k < Cols; ++k) {
109 sum += data[i][k] * other(k, j);
110 }
111 result(i, j) = sum;
112
113 // Track if result is non-zero
114 if (std::abs(sum) > std::numeric_limits<T>::epsilon()) {
115 isZero = false;
116 }
117 }
118 }
119
120 return result;
121 }
122
123 /// Returns the transposed matrix; does not modify this one.
126 for (size_t i = 0; i < Rows; ++i) {
127 for (size_t j = 0; j < Cols; ++j) {
128 result(j, i) = data[i][j];
129 }
130 }
131 return result;
132 }
133
134 // Improved determinant calculation with recursive approach
135 T Determinant() const {
136 static_assert(Rows == Cols,
137 "Determinant is only defined for square matrices");
138
139 if constexpr (Rows == 1) return data[0][0];
140 if constexpr (Rows == 2) {
141 return data[0][0] * data[1][1] - data[0][1] * data[1][0];
142 }
143 if constexpr (Rows == 3) {
144 return data[0][0] *
145 (data[1][1] * data[2][2] - data[1][2] * data[2][1]) -
146 data[0][1] *
147 (data[1][0] * data[2][2] - data[1][2] * data[2][0]) +
148 data[0][2] *
149 (data[1][0] * data[2][1] - data[1][1] * data[2][0]);
150 }
151
152 throw std::runtime_error(
153 "Determinant not implemented for matrices larger than 3x3");
154 }
155
156 // Improved inverse for 2x2 matrices with error handling
158 static_assert(Rows == Cols,
159 "Inverse is only defined for square matrices");
160
161 T det = Determinant();
162 if (std::abs(det) < std::numeric_limits<T>::epsilon()) {
163 throw std::runtime_error(
164 "Matrix is singular (determinant is near zero)");
165 }
166
167 if constexpr (Rows == 2) {
169 result(0, 0) = data[1][1] / det;
170 result(0, 1) = -data[0][1] / det;
171 result(1, 0) = -data[1][0] / det;
172 result(1, 1) = data[0][0] / det;
173 return result;
174 }
175
176 throw std::runtime_error(
177 "Inverse not implemented for matrices larger than 2x2");
178 }
179
180 // Optimized output method
181 std::string ToString() const {
182 std::ostringstream ss;
183 ss << "Matrix:\n";
184 for (size_t i = 0; i < Rows; ++i) {
185 ss << "| ";
186 for (size_t j = 0; j < Cols; ++j) {
187 ss << std::setw(10) << std::fixed << std::setprecision(4)
188 << data[i][j] << " ";
189 }
190 ss << "|\n";
191 }
192 return ss.str();
193 }
194
195 // Stream operator
196 friend std::ostream& operator<<(std::ostream& os,
197 const Matrix<T, Rows, Cols>& matrix) {
198 os << matrix.ToString();
199 return os;
200 }
201
202 // Static matrix creation methods remain the same
204 static_assert(Rows == Cols,
205 "Identity matrix is only defined for square matrices");
207 return result;
208 }
209
210 /// Builds a left-handed perspective projection (reversed-Z: near maps to depth 1).
211 static Matrix<T, 4, 4> Perspective(T fovY, T aspectRatio, T nearPlane,
212 T farPlane) {
213 static_assert(Rows == 4 && Cols == 4, "Perspective matrix must be 4x4");
214 T tanHalfFovY = tan(fovY / 2.0f);
215
217
218 result(0, 0) = 1.0f / (aspectRatio * tanHalfFovY);
219 result(1, 1) = 1.0f / tanHalfFovY;
220 result(2, 2) = farPlane / (farPlane - nearPlane);
221 result(2, 3) = 1.0f;
222 result(3, 2) = (-nearPlane * farPlane) / (farPlane - nearPlane);
223 result(3, 3) = 0.0f;
224
225 return result;
226 }
227
228
229
230 /// Builds an orthographic projection over the given box.
231 static Matrix<T, 4, 4> Orthographic(T left, T right, T bottom, T top, T nearPlane, T farPlane) {
232 static_assert(Rows == 4 && Cols == 4, "Orthographic matrix must be 4x4");
233
235
236 // Diagonal elements
237 result(0, 0) = 2.0f / (right - left); // Scale X
238 result(1, 1) = 2.0f / (top - bottom); // Scale Y
239 result(2, 2) = -2.0f / (farPlane - nearPlane); // Scale Z (negative for right-handed systems)
240
241 // Translation elements
242 result(0, 3) = -(right + left) / (right - left); // Translate X
243 result(1, 3) = -(top + bottom) / (top - bottom); // Translate Y
244 result(2, 3) = -(farPlane + nearPlane) / (farPlane - nearPlane); // Translate Z
245
246 return result;
247 }
248
249 /// Left-handed view matrix looking from eye toward center.
251 const Vector<T, 3>& center,
252 const Vector<T, 3>& up) {
253 static_assert(Rows == 4 && Cols == 4, "View matrix must be 4x4");
254
255 Vector<T, 3> zAxis = (center - eye).Normalized();
256 Vector<T, 3> xAxis = up.Cross(zAxis).Normalized();
257 Vector<T, 3> yAxis = zAxis.Cross(xAxis);
258
259 Matrix<T, 4, 4> result;
260
261 // Assign basis vectors correctly (Column-major order)
262 result(0, 0) = xAxis[0];
263 result(1, 0) = xAxis[1];
264 result(2, 0) = xAxis[2];
265 result(3, 0) = -xAxis.Dot(eye);
266
267 result(0, 1) = yAxis[0];
268 result(1, 1) = yAxis[1];
269 result(2, 1) = yAxis[2];
270 result(3, 1) = -yAxis.Dot(eye);
271
272 result(0, 2) = +zAxis[0];
273 result(1, 2) = +zAxis[1];
274 result(2, 2) = +zAxis[2];
275 result(3, 2) = -zAxis.Dot(eye);
276
277 result(0, 3) = 0;
278 result(1, 3) = 0;
279 result(2, 3) = 0;
280 result(3, 3) = 1;
281
282 return result;
283 }
284
285
286
287 /// Right-handed view matrix looking from eye toward center.
289 const Vector<T, 3>& center,
290 const Vector<T, 3>& up) {
291 Vector<T, 3> zAxis = (center - eye).Normalized(); // Notice: reversed direction
292 Vector<T, 3> xAxis = up.Cross(zAxis).Normalized();
293 Vector<T, 3> yAxis = zAxis.Cross(xAxis);
294
296 result(0, 0) = xAxis[0];
297 result(1, 0) = xAxis[1];
298 result(2, 0) = xAxis[2];
299
300 result(0, 1) = yAxis[0];
301 result(1, 1) = yAxis[1];
302 result(2, 1) = yAxis[2];
303
304 result(0, 2) = -zAxis[0]; // Negate z-axis
305 result(1, 2) = -zAxis[1];
306 result(2, 2) = -zAxis[2];
307
308 result(3, 0) = -xAxis.Dot(eye);
309 result(3, 1) = -yAxis.Dot(eye);
310 result(3, 2) = zAxis.Dot(eye); // Negate translation for right-handed
311 return result;
312 }
313
314 /// Left-handed view matrix looking from eye along a direction (rather than at a point).
316 const Vector<T, 3>& direction,
317 const Vector<T, 3>& up) {
318 Vector<T, 3> zAxis = direction.Normalized(); // Forward direction
319 Vector<T, 3> xAxis = up.Cross(zAxis).Normalized();
320 Vector<T, 3> yAxis = zAxis.Cross(xAxis);
321
323 result(0, 0) = xAxis[0];
324 result(1, 0) = xAxis[1];
325 result(2, 0) = xAxis[2];
326
327 result(0, 1) = yAxis[0];
328 result(1, 1) = yAxis[1];
329 result(2, 1) = yAxis[2];
330
331 result(0, 2) = zAxis[0];
332 result(1, 2) = zAxis[1];
333 result(2, 2) = zAxis[2];
334
335 result(3, 0) = -xAxis.Dot(eye);
336 result(3, 1) = -yAxis.Dot(eye);
337 result(3, 2) = -zAxis.Dot(eye);
338
339 return result;
340 }
341
342 /// View matrix for a yaw/pitch-driven free camera at position.
343 static Matrix<T, 4, 4> FreeLook(const Vector<T, 3>& position,
344 T yaw, T pitch) {
345 Vector<T, 3> forward;
346 forward[0] = cos(yaw) * cos(pitch);
347 forward[1] = sin(pitch);
348 forward[2] = sin(yaw) * cos(pitch);
349
350 return LookToLH(position, forward, Vector<T, 3>{0, 1, 0});
351 }
352
353 /// View matrix for a camera orbiting target at the given distance and angles.
355 T distance,
356 T theta, T phi) {
357 Vector<T, 3> direction = {
358 sin(phi) * cos(theta),
359 cos(phi),
360 sin(phi) * sin(theta)
361 };
362
363 Vector<T, 3> eye = target + direction * distance;
364 return LookAtLH(eye, target, Vector<T, 3>{0, 1, 0});
365 }
366
367 /// Builds a translation matrix.
368 static Matrix<T, 4, 4> Translate(const Vector3D& translation) {
370
371 result(3, 0) = translation.GetX(); // Move to last row, first column
372 result(3, 1) = translation.GetY(); // Move to last row, second column
373 result(3, 2) = translation.GetZ(); // Move to last row, third column
374
375 return result;
376 }
377
378
379 /// Builds a rotation matrix from a quaternion.
380 static Matrix<T, 4, 4> Rotate(const Quaternion& rotation) {
381 return rotation.toRotationMatrix(); // Assuming Quaternion has toRotationMatrix()
382 }
383
384 /// Builds a matrix that scales about the origin.
385 static Matrix<T, 4, 4> Scale(const Vector3D& scale) {
386 Matrix<T, 4, 4> result =
387 Matrix<T, 4, 4>::Identity(); // Ensure identity
388 result(0, 0) = scale.GetX();
389 result(1, 1) = scale.GetY();
390 result(2, 2) = scale.GetZ();
391 return result;
392 }
393
394 /// Builds a matrix that scales about an arbitrary center point.
395 static Matrix<T, 4, 4> Scale(const Vector3D& scale, const Vector3D& center) {
396 Matrix<T, 4, 4> translateToOrigin = Translate(center*(-1));
397
399 scaling(0, 0) = scale.GetX();
400 scaling(1, 1) = scale.GetY();
401 scaling(2, 2) = scale.GetZ();
402
403 Matrix<T, 4, 4> translateBack = Translate(center);
404
405 return translateBack * scaling * translateToOrigin;
406 }
407
408 private:
409 T data[Rows][Cols];
410};
411
412// Type alias for 4x4 matrix
414
415
416#ifdef PLATFORM_WIN
417#include <DirectXMath.h>
418
419/// Converts a DirectXMath matrix into Sleak's Matrix4 (row-major copy).
420static Matrix<float, 4, 4> XMToMatrix(DirectX::XMMATRIX mat) {
422 DirectX::XMMATRIX worldMatrix;
423
424 DirectX::XMMATRIX normalMatrix = XMMatrixTranspose(XMMatrixInverse(nullptr, worldMatrix));
425
426 // Store the matrix in row-major order
427 DirectX::XMFLOAT4X4 floatMat;
428 DirectX::XMStoreFloat4x4(&floatMat, mat);
429
430 // Copy the elements from XMFLOAT4X4 to Sleak::Math::Matrix4
431 for (int row = 0; row < 4; ++row) {
432 for (int col = 0; col < 4; ++col) {
433 matr(row, col) = floatMat.m[row][col];
434 }
435 }
436
437 return matr;
438}
439
440#endif
441
442} // namespace Math
443} // namespace Sleak
444
445#endif // _MATRIX_H_
static Matrix< T, 4, 4 > Scale(const Vector3D &scale, const Vector3D &center)
Builds a matrix that scales about an arbitrary center point.
Definition Matrix.hpp:395
T Determinant() const
Definition Matrix.hpp:135
static Matrix< T, Rows, Rows > Identity()
Definition Matrix.hpp:203
Matrix< T, Rows, Cols > Inverse() const
Definition Matrix.hpp:157
static Matrix< T, 4, 4 > FreeLook(const Vector< T, 3 > &position, T yaw, T pitch)
View matrix for a yaw/pitch-driven free camera at position.
Definition Matrix.hpp:343
static Matrix< T, 4, 4 > Perspective(T fovY, T aspectRatio, T nearPlane, T farPlane)
Builds a left-handed perspective projection (reversed-Z: near maps to depth 1).
Definition Matrix.hpp:211
static Matrix< T, 4, 4 > Orthographic(T left, T right, T bottom, T top, T nearPlane, T farPlane)
Builds an orthographic projection over the given box.
Definition Matrix.hpp:231
static Matrix< T, 4, 4 > LookTo(const Vector< T, 3 > &eye, const Vector< T, 3 > &direction, const Vector< T, 3 > &up)
Left-handed view matrix looking from eye along a direction (rather than at a point).
Definition Matrix.hpp:315
T & operator()(size_t row, size_t col)
Definition Matrix.hpp:61
Matrix(std::initializer_list< std::initializer_list< T > > list)
Definition Matrix.hpp:39
Matrix< T, Cols, Rows > Transpose() const
Returns the transposed matrix; does not modify this one.
Definition Matrix.hpp:124
const T & operator()(size_t row, size_t col) const
Definition Matrix.hpp:68
static Matrix< T, 4, 4 > Rotate(const Quaternion &rotation)
Builds a rotation matrix from a quaternion.
Definition Matrix.hpp:380
static Matrix< T, 4, 4 > Scale(const Vector3D &scale)
Builds a matrix that scales about the origin.
Definition Matrix.hpp:385
friend std::ostream & operator<<(std::ostream &os, const Matrix< T, Rows, Cols > &matrix)
Definition Matrix.hpp:196
static Matrix< T, 4, 4 > Translate(const Vector3D &translation)
Builds a translation matrix.
Definition Matrix.hpp:368
static Matrix< T, 4, 4 > OrbitView(const Vector< T, 3 > &target, T distance, T theta, T phi)
View matrix for a camera orbiting target at the given distance and angles.
Definition Matrix.hpp:354
Matrix< T, Rows, OtherCols > operator*(const Matrix< T, Cols, OtherCols > &other) const
Definition Matrix.hpp:99
static Matrix< T, 4, 4 > LookAt(const Vector< T, 3 > &eye, const Vector< T, 3 > &center, const Vector< T, 3 > &up)
Left-handed view matrix looking from eye toward center.
Definition Matrix.hpp:250
Matrix< T, Rows, Cols > operator-(const Matrix< T, Rows, Cols > &other) const
Definition Matrix.hpp:87
Matrix< T, Rows, Cols > operator+(const Matrix< T, Rows, Cols > &other) const
Definition Matrix.hpp:76
static Matrix< T, 4, 4 > LookAtRH(const Vector< T, 3 > &eye, const Vector< T, 3 > &center, const Vector< T, 3 > &up)
Right-handed view matrix looking from eye toward center.
Definition Matrix.hpp:288
constexpr Matrix() noexcept
Definition Matrix.hpp:23
std::string ToString() const
Definition Matrix.hpp:181
Represents a quaternion for 3D rotations.
Matrix< float, 4, 4 > toRotationMatrix() const
float GetY() const
Definition Vector.hpp:361
float GetX() const
Definition Vector.hpp:360
float GetZ() const
Definition Vector.hpp:362
T Dot(const Vector< T, N > &other) const
Definition Vector.hpp:103
std::enable_if< M==3, Vector< T, N > >::type Cross(const Vector< T, N > &other) const
Definition Vector.hpp:114
Vector< T, N > Normalized() const
Definition Vector.hpp:144
Vectors, matrices, quaternions, colors, AABBs, and random helpers.
Matrix< float, 4, 4 > Matrix4
Definition Matrix.hpp:413
Root namespace for everything the engine exposes.
Definition Camera.hpp:10