DirectZ
Loading...
Searching...
No Matches
math.hpp
Go to the documentation of this file.
1
5#pragma once
6#include <cassert>
7#include <cmath>
8#include <random>
9#include <chrono>
10#include <stdexcept>
11#include <string.h>
12#include "Renderer.hpp"
13namespace dz
14{
26 template <typename T, size_t N>
27 struct vec
28 {
29 private:
30
38 void load_args(size_t& i)
39 { }
40
51 template <typename... Args>
52 void load_args(size_t& i, T arg, Args... args)
53 {
54 data[i++] = arg;
55 load_args(i, args...);
56 }
57
65 void load_all(T val)
66 {
67 for (size_t i = 0; i < N; ++i)
68 data[i] = val;
69 }
70
71 public:
72
77
89 template <typename... Args>
90 vec(Args... args)
91 {
92 if constexpr (sizeof...(args) == 0)
93 load_all(T());
94 else if constexpr (sizeof...(args) == 1)
95 load_all(args...);
96 else
97 {
98 size_t i = 0;
99 load_args(i, args...);
100 }
101 }
102
113 template <typename OT, size_t ON>
114 vec(const vec<OT, ON>& other)
115 {
116 size_t i = 0;
117 for (; i < N && i < ON; ++i)
118 data[i] = other.data[i];
119 if (i < N)
120 for (; i < N; ++i)
121 data[i] = T();
122 }
123
132 vec& operator=(const vec& other)
133 {
134 memcpy(data, other.data, sizeof(T) * N);
135 return *this;
136 }
137
146 inline T& operator[](size_t i)
147 {
148 return data[i];
149 }
150
159 inline const T& operator[](size_t i) const
160 {
161 return data[i];
162 }
163
175 template <typename O>
176 vec& operator*=(const O& other)
177 {
178 for (size_t i = 0; i < N; ++i)
179 {
180 if constexpr (std::is_same_v<O, T>)
181 data[i] *= other;
182 else if constexpr (std::is_same_v<O, vec<T, N>>)
183 data[i] *= other.data[i];
184 else
185 throw std::runtime_error("Unsupported O type");
186 }
187 return *this;
188 }
189
201 template <typename O>
202 vec& operator/=(const O& other)
203 {
204 for (size_t i = 0; i < N; ++i)
205 {
206 if constexpr (std::is_same_v<O, T>)
207 data[i] /= other;
208 else if constexpr (std::is_same_v<O, vec<T, N>>)
209 data[i] /= other.data[i];
210 else
211 throw std::runtime_error("Unsupported O type");
212 }
213 return *this;
214 }
215
227 template <typename O>
228 vec& operator+=(const O& other)
229 {
230 for (size_t i = 0; i < N; ++i)
231 {
232 if constexpr (std::is_same_v<O, T>)
233 data[i] += other;
234 else if constexpr (std::is_same_v<O, vec<T, N>>)
235 data[i] += other.data[i];
236 else
237 throw std::runtime_error("Unsupported O type");
238 }
239 return *this;
240 }
241
253 template <typename O>
254 vec& operator-=(const O& other)
255 {
256 for (size_t i = 0; i < N; ++i)
257 {
258 if constexpr (std::is_same_v<O, T>)
259 data[i] -= other;
260 else if constexpr (std::is_same_v<O, vec<T, N>>)
261 data[i] -= other.data[i];
262 else
263 throw std::runtime_error("Unsupported O type");
264 }
265 return *this;
266 }
267
277 template <typename O>
278 vec operator*(const O& other) const
279 {
280 auto new_vec = *this;
281 return (new_vec *= other);
282 }
283
293 template <typename O>
294 vec operator/(const O& other) const
295 {
296 auto new_vec = *this;
297 return (new_vec /= other);
298 }
299
309 template <typename O>
310 vec operator+(const O& other) const
311 {
312 auto new_vec = *this;
313 return (new_vec += other);
314 }
315
325 template <typename O>
326 vec operator-(const O& other) const
327 {
328 auto new_vec = *this;
329 return (new_vec -= other);
330 }
331
340 {
341 auto new_vec = *this;
342 for (size_t i = 0; i < N; ++i)
343 new_vec.data[i] *= T(-1);
344 return new_vec;
345 }
346
354 T length() const
355 {
356 T sum = T(0);
357 for (size_t i = 0; i < N; ++i)
358 {
359 sum += data[i] * data[i];
360 }
361 return std::sqrt(sum);
362 }
363
372 {
373 T invLen = T(1) / length();
374 vec result;
375 for (size_t i = 0; i < N; ++i)
376 {
377 result.data[i] = data[i] * invLen;
378 }
379 return result;
380 }
381 };
382
383 template <typename T, size_t N>
384 struct color_vec : public vec<T, N>
385 {
387
388 using Base::data;
389 using Base::operator=;
390 using Base::operator+=;
391 using Base::operator-=;
392 using Base::operator*=;
393 using Base::operator/=;
394 using Base::operator+;
395 using Base::operator-;
396 using Base::operator*;
397 using Base::operator/;
398 using Base::operator[];
399 using Base::length;
400 using Base::normalize;
401
402 template <typename... Args>
403 color_vec(Args&&... args) : Base(std::forward<Args>(args)...) { }
404
405 template <typename OT, size_t ON>
406 color_vec(const vec<OT, ON>& other) : Base(other) { }
407
408 color_vec() = default;
409 color_vec(const color_vec&) = default;
410 color_vec& operator=(const color_vec&) = default;
411 color_vec(color_vec&&) noexcept = default;
412 color_vec& operator=(color_vec&&) noexcept = default;
413 };
414
429 template <typename T, size_t C, size_t R>
430 struct mat
431 {
438
448 mat(T initial = T())
449 {
450 auto sizebytes = sizeof(T) * C * R;
451 memset(matrix, 0, sizebytes);
452
453 // Initialize diagonal elements up to min(C, R) with initial value
454 for (size_t c = 0; c < C && c < R; c++)
455 matrix[c][c] = initial;
456 }
457
466 mat& operator=(const mat& other)
467 {
468 memcpy(matrix, other.matrix, sizeof(T) * C * R);
469 return *this;
470 }
471
480 inline vec<T, R>& operator[](size_t i)
481 {
482 return *(vec<T, R>*)matrix[i];
483 }
484
491 inline const vec<T, R>& operator[](size_t i) const
492 {
493 return *(const vec<T, R>*)matrix[i];
494 }
495
506 template <size_t N>
508 {
509 static_assert(C == 4 && R == 4 && N == 3, "translate requires a 4x4 matrix and 3D vector");
510 auto& pos = (vec<T, N>&)(*this)[3];
511 pos += v;
512 return *this;
513 }
514
515 template <size_t N>
516 inline static mat translate_static(const vec<T, N>& v)
517 {
518 mat m(1.0f);
519 static_assert(C == 4 && R == 4 && N == 3, "translate requires a 4x4 matrix and 3D vector");
520 auto& pos = (vec<T, N>&)(m)[3];
521 pos += v;
522 return m;
523 }
524
535 template <size_t N>
537 {
538 for (size_t l = 0; l < N; ++l)
539 {
540 auto& y = (vec<T, N>&)(*this)[l];
541 y *= v[l];
542 }
543 return *this;
544 }
545
546 template <size_t N>
547 inline static mat scale_static(const vec<T, N>& v)
548 {
549 mat m(1.0f);
550 for (size_t l = 0; l < N; ++l)
551 {
552 auto& y = (vec<T, N>&)(m)[l];
553 y *= v[l];
554 }
555 return m;
556 }
557
573 template <size_t N>
574 mat& rotate(T angle, const vec<T, N>& axis)
575 {
576 static_assert(N == 2 || N == 3, "Rotation axis must be 2D or 3D");
577 constexpr size_t D = N;
578
579 T c = std::cos(angle);
580 T s = std::sin(angle);
581 T ic = T(1) - c;
582
583 vec<T, 3> a{};
584 if constexpr (N == 2)
585 {
586 a[0] = axis[0];
587 a[1] = axis[1];
588 a[2] = T(0); // implicit Z = 0 for 2D rotation
589 }
590 else
591 {
592 a = axis.normalize();
593 }
594
595 T x = a[0];
596 T y = a[1];
597 T z = a[2];
598
599 // Precompute rotation matrix components
600 T r00, r01, r02;
601 T r10, r11, r12;
602 T r20, r21, r22;
603
604 if constexpr (N == 2)
605 {
606 r00 = c; r01 = -s; r02 = 0;
607 r10 = s; r11 = c; r12 = 0;
608 r20 = 0; r21 = 0; r22 = 1;
609 }
610 else
611 {
612 r00 = x * x * ic + c;
613 r01 = x * y * ic - z * s;
614 r02 = x * z * ic + y * s;
615
616 r10 = y * x * ic + z * s;
617 r11 = y * y * ic + c;
618 r12 = y * z * ic - x * s;
619
620 r20 = z * x * ic - y * s;
621 r21 = z * y * ic + x * s;
622 r22 = z * z * ic + c;
623 }
624
625 // Apply rotation matrix to each column vector
626 for (size_t cidx = 0; cidx < C; ++cidx)
627 {
628 vec<T, R>& col = (vec<T, R>&)(*this)[cidx];
629
630 T vx = (D > 0) ? col[0] : T(0);
631 T vy = (D > 1) ? col[1] : T(0);
632 T vz = (D > 2) ? col[2] : T(0);
633
634 if constexpr (N == 2)
635 {
636 col[0] = vx * r00 + vy * r01;
637 col[1] = vx * r10 + vy * r11;
638 }
639 else
640 {
641 col[0] = vx * r00 + vy * r01 + vz * r02;
642 col[1] = vx * r10 + vy * r11 + vz * r12;
643 col[2] = vx * r20 + vy * r21 + vz * r22;
644 }
645 // Remaining rows (3 and above) remain unchanged
646 }
647
648 return *this;
649 }
650
651 template <size_t N>
652 inline static mat rotate_static(T angle, const vec<T, N>& axis)
653 {
654 static_assert(N == 2 || N == 3, "Rotation axis must be 2D or 3D");
655 constexpr size_t D = N;
656
657 mat m(1.0f);
658
659 T c = std::cos(angle);
660 T s = std::sin(angle);
661 T ic = T(1) - c;
662
663 vec<T, 3> a{};
664 if constexpr (N == 2)
665 {
666 a[0] = axis[0];
667 a[1] = axis[1];
668 a[2] = T(0); // implicit Z = 0 for 2D rotation
669 }
670 else
671 {
672 a = axis.normalize();
673 }
674
675 T x = a[0];
676 T y = a[1];
677 T z = a[2];
678
679 // Precompute rotation matrix components
680 T r00, r01, r02;
681 T r10, r11, r12;
682 T r20, r21, r22;
683
684 if constexpr (N == 2)
685 {
686 r00 = c; r01 = -s; r02 = 0;
687 r10 = s; r11 = c; r12 = 0;
688 r20 = 0; r21 = 0; r22 = 1;
689 }
690 else
691 {
692 r00 = x * x * ic + c;
693 r01 = x * y * ic - z * s;
694 r02 = x * z * ic + y * s;
695
696 r10 = y * x * ic + z * s;
697 r11 = y * y * ic + c;
698 r12 = y * z * ic - x * s;
699
700 r20 = z * x * ic - y * s;
701 r21 = z * y * ic + x * s;
702 r22 = z * z * ic + c;
703 }
704
705 // Apply rotation matrix to each column vector
706 for (size_t cidx = 0; cidx < C; ++cidx)
707 {
708 vec<T, R>& col = (vec<T, R>&)(m)[cidx];
709
710 T vx = (D > 0) ? col[0] : T(0);
711 T vy = (D > 1) ? col[1] : T(0);
712 T vz = (D > 2) ? col[2] : T(0);
713
714 if constexpr (N == 2)
715 {
716 col[0] = vx * r00 + vy * r01;
717 col[1] = vx * r10 + vy * r11;
718 }
719 else
720 {
721 col[0] = vx * r00 + vy * r01 + vz * r02;
722 col[1] = vx * r10 + vy * r11 + vz * r12;
723 col[2] = vx * r20 + vy * r21 + vz * r22;
724 }
725 // Remaining rows (3 and above) remain unchanged
726 }
727
728 return m;
729 }
730
745 template <size_t N>
746 mat& rotateAround(T angle, const vec<T, N>& axis, const vec<T, N>& point)
747 {
748 static_assert(N == 2 || N == 3, "Rotation axis must be 2D or 3D");
749
750 // Translate pivot to origin
751 translate<N>(-point);
752
753 // Rotate around origin
754 rotate<N>(angle, axis);
755
756 // Translate pivot back to original location
757 translate(point);
758
759 return *this;
760 }
761
772 {
773 static_assert(C == R, "Inverse only defined for square matrices");
774 constexpr size_t N = C;
775 mat<T, N, N> result;
776 mat<T, N, N> copy = *this;
777 result = mat<T, N, N>((T)1); // Initialize result to identity
778
779 for (size_t i = 0; i < N; ++i)
780 {
781 T pivot = copy.matrix[i][i];
782 if (std::abs(pivot) < std::numeric_limits<T>::epsilon())
783 {
784 size_t swap_row = i + 1;
785 while (swap_row < N && std::abs(copy.matrix[swap_row][i]) < std::numeric_limits<T>::epsilon())
786 ++swap_row;
787 if (swap_row == N)
788 assert(false && "Matrix is singular and cannot be inverted");
789 for (size_t k = 0; k < N; ++k)
790 {
791 std::swap(copy.matrix[i][k], copy.matrix[swap_row][k]);
792 std::swap(result.matrix[i][k], result.matrix[swap_row][k]);
793 }
794 pivot = copy.matrix[i][i];
795 }
796
797 T inv_pivot = (T)1 / pivot;
798 for (size_t j = 0; j < N; ++j)
799 {
800 copy.matrix[i][j] *= inv_pivot;
801 result.matrix[i][j] *= inv_pivot;
802 }
803
804 for (size_t row = 0; row < N; ++row)
805 {
806 if (row == i)
807 continue;
808 T factor = copy.matrix[row][i];
809 for (size_t col = 0; col < N; ++col)
810 {
811 copy.matrix[row][col] -= factor * copy.matrix[i][col];
812 result.matrix[row][col] -= factor * result.matrix[i][col];
813 }
814 }
815 }
816
817 return result;
818 }
819
832 template <size_t C2, size_t R2>
834 {
835 static_assert(C == R2, "Matrix multiplication dimension mismatch");
836
837 // Temporary storage for results
838 T temp[C2][R];
839
840 for (size_t c = 0; c < C2; c++)
841 {
842 for (size_t r = 0; r < R; r++)
843 {
844 T sum = T(0);
845 for (size_t k = 0; k < C; k++)
846 {
847 sum += matrix[k][r] * rhs.matrix[c][k];
848 }
849 temp[c][r] = sum;
850 }
851 }
852
853 // Enforce that output size matches current matrix dimensions
854 static_assert(C2 == C && R == R2, "operator*= requires output size equals current matrix size");
855
856 memcpy(matrix, temp, sizeof(temp));
857
858 return *this;
859 }
860
871 template <size_t C2, size_t R2>
873 {
874 mat<T, C, R> temp(*this);
875 return temp *= rhs;
876 }
877
879 {
880 mat<T, R, C> result {};
881 for (size_t i = 0; i < C; ++i)
882 {
883 for (size_t j = 0; j < R; ++j)
884 {
885 result[j][i] = (*this)[i][j];
886 }
887 }
888 return result;
889 }
890 };
891
921 template<typename T>
922 mat<T, 4, 4> lookAt(const vec<T, 3>& eye, const vec<T, 3>& center, const vec<T, 3>& up)
923 {
924 vec<T, 3> f = (center - eye);
925 T f_len = std::sqrt(f[0]*f[0] + f[1]*f[1] + f[2]*f[2]);
926 f /= f_len;
927
928 vec<T, 3> up_n = up;
929 T up_len = std::sqrt(up[0]*up[0] + up[1]*up[1] + up[2]*up[2]);
930 up_n /= up_len;
931
932 vec<T, 3> s = {
933 f[1] * up_n[2] - f[2] * up_n[1],
934 f[2] * up_n[0] - f[0] * up_n[2],
935 f[0] * up_n[1] - f[1] * up_n[0]
936 };
937 T s_len = std::sqrt(s[0]*s[0] + s[1]*s[1] + s[2]*s[2]);
938 s /= s_len;
939
940 vec<T, 3> u = {
941 s[1] * f[2] - s[2] * f[1],
942 s[2] * f[0] - s[0] * f[2],
943 s[0] * f[1] - s[1] * f[0]
944 };
945
946 mat<T, 4, 4> result((T)1);
947 result[0][0] = s[0]; result[1][0] = s[1]; result[2][0] = s[2];
948 result[0][1] = u[0]; result[1][1] = u[1]; result[2][1] = u[2];
949 result[0][2] = -f[0]; result[1][2] = -f[1]; result[2][2] = -f[2];
950 result[3][0] = - (s[0]*eye[0] + s[1]*eye[1] + s[2]*eye[2]);
951 result[3][1] = - (u[0]*eye[0] + u[1]*eye[1] + u[2]*eye[2]);
952 result[3][2] = + (f[0]*eye[0] + f[1]*eye[1] + f[2]*eye[2]);
953 return result;
954 }
955
956 template<typename T>
957 mat<T, 4, 4> perspectiveRH_ZO(T fovy, T aspect, T zNear, T zFar)
958 {
959 assert(std::abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
960
961 T const tanHalfFovy = tan(fovy / static_cast<T>(2));
962
963 mat<T, 4, 4> result(static_cast<T>(0));
964 result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
965 result[1][1] = static_cast<T>(1) / (tanHalfFovy);
966 result[2][2] = zFar / (zNear - zFar);
967 result[2][3] = - static_cast<T>(1);
968 result[3][2] = -(zFar * zNear) / (zFar - zNear);
969 return result;
970 }
971
972 template<typename T>
973 mat<T, 4, 4> perspectiveRH_NO(T fovy, T aspect, T zNear, T zFar)
974 {
975 assert(std::abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
976
977 T const tanHalfFovy = tan(fovy / static_cast<T>(2));
978
979 mat<T, 4, 4> result(static_cast<T>(0));
980 result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
981 result[1][1] = static_cast<T>(1) / (tanHalfFovy);
982 result[2][2] = - (zFar + zNear) / (zFar - zNear);
983 result[2][3] = - static_cast<T>(1);
984 result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
985 return result;
986 }
987
988 template<typename T>
989 mat<T, 4, 4> perspectiveLH_ZO(T fovy, T aspect, T zNear, T zFar)
990 {
991 assert(std::abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
992
993 T const tanHalfFovy = tan(fovy / static_cast<T>(2));
994
995 mat<T, 4, 4> result(static_cast<T>(0));
996 result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
997 result[1][1] = static_cast<T>(1) / (tanHalfFovy);
998 result[2][2] = zFar / (zFar - zNear);
999 result[2][3] = static_cast<T>(1);
1000 result[3][2] = -(zFar * zNear) / (zFar - zNear);
1001 return result;
1002 }
1003
1004 template<typename T>
1005 mat<T, 4, 4> perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar)
1006 {
1007 assert(std::abs(aspect - std::numeric_limits<T>::epsilon()) > static_cast<T>(0));
1008
1009 T const tanHalfFovy = tan(fovy / static_cast<T>(2));
1010
1011 mat<T, 4, 4> result(static_cast<T>(0));
1012 result[0][0] = static_cast<T>(1) / (aspect * tanHalfFovy);
1013 result[1][1] = static_cast<T>(1) / (tanHalfFovy);
1014 result[2][2] = (zFar + zNear) / (zFar - zNear);
1015 result[2][3] = static_cast<T>(1);
1016 result[3][2] = - (static_cast<T>(2) * zFar * zNear) / (zFar - zNear);
1017 return result;
1018 }
1019
1043 template<typename T>
1044 mat<T, 4, 4> perspective(T fovy, T aspect, T zNear, T zFar)
1045 {
1046// #if GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_ZO
1047// return perspectiveLH_ZO(fovy, aspect, zNear, zFar);
1048// #elif GLM_CONFIG_CLIP_CONTROL == GLM_CLIP_CONTROL_LH_NO
1049// return perspectiveLH_NO(fovy, aspect, zNear, zFar);
1050#if defined(RENDERER_VULKAN)
1051 auto m = perspectiveRH_ZO(fovy, aspect, zNear, zFar);
1052#elif defined(RENDERER_GL)
1053 auto m = perspectiveRH_NO(fovy, aspect, zNear, zFar);
1054#endif
1055 // m[1][1] *= -1.0f;
1056 return m;
1057 }
1058
1059 template<typename T>
1061 {
1062 T const range = tan(fovy / static_cast<T>(2)) * zNear;
1063 T const left = -range * aspect;
1064 T const right = range * aspect;
1065 T const bottom = -range;
1066 T const top = range;
1067
1068 mat<T, 4, 4> result(static_cast<T>(0));
1069 result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
1070 result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
1071 result[2][2] = - static_cast<T>(1);
1072 result[2][3] = - static_cast<T>(1);
1073 result[3][2] = - static_cast<T>(2) * zNear;
1074 return result;
1075 }
1076
1077 template<typename T>
1079 {
1080 T const range = tan(fovy / static_cast<T>(2)) * zNear;
1081 T const left = -range * aspect;
1082 T const right = range * aspect;
1083 T const bottom = -range;
1084 T const top = range;
1085
1086 mat<T, 4, 4> result(static_cast<T>(0));
1087 result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
1088 result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
1089 result[2][2] = - static_cast<T>(1);
1090 result[2][3] = - static_cast<T>(1);
1091 result[3][2] = - zNear;
1092 return result;
1093 }
1094
1095 template<typename T>
1097 {
1098 T const range = tan(fovy / static_cast<T>(2)) * zNear;
1099 T const left = -range * aspect;
1100 T const right = range * aspect;
1101 T const bottom = -range;
1102 T const top = range;
1103
1104 mat<T, 4, 4> result(T(0));
1105 result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
1106 result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
1107 result[2][2] = static_cast<T>(1);
1108 result[2][3] = static_cast<T>(1);
1109 result[3][2] = - static_cast<T>(2) * zNear;
1110 return result;
1111 }
1112
1113 template<typename T>
1115 {
1116 T const range = tan(fovy / static_cast<T>(2)) * zNear;
1117 T const left = -range * aspect;
1118 T const right = range * aspect;
1119 T const bottom = -range;
1120 T const top = range;
1121
1122 mat<T, 4, 4> result(T(0));
1123 result[0][0] = (static_cast<T>(2) * zNear) / (right - left);
1124 result[1][1] = (static_cast<T>(2) * zNear) / (top - bottom);
1125 result[2][2] = static_cast<T>(1);
1126 result[2][3] = static_cast<T>(1);
1127 result[3][2] = - zNear;
1128 return result;
1129 }
1130
1152 template<typename T>
1153 mat<T, 4, 4> infinitePerspective(T fovy, T aspect, T zNear)
1154 {
1155#if defined(RENDERER_VULKAN)
1156 auto m = infinitePerspectiveRH_ZO(fovy, aspect, zNear);
1157#elif defined(RENDERER_GL)
1158 auto m = infinitePerspectiveRH_NO(fovy, aspect, zNear);
1159#endif
1160 // m[1][1] *= -1.0f;
1161 return m;
1162 }
1163
1184 template<typename T>
1185 mat<T, 4, 4> orthographic(T left, T right, T bottom, T top, T znear, T zfar)
1186 {
1187 mat<T, 4, 4> result((T)1);
1188 result[0][0] = (T)2 / (right - left);
1189 result[1][1] = (T)2 / (top - bottom);
1190 result[2][2] = -(T)1 / (zfar - znear);
1191 result[3][0] = -(right + left) / (right - left);
1192 result[3][1] = -(top + bottom) / (top - bottom);
1193 result[3][2] = -(znear) / (zfar - znear);
1194 // result[1][1] *= -1.0f;
1195 return result;
1196 }
1197
1207 template<typename T>
1209 {
1210 return degrees * T(0.01745329251994329576923690768489);
1211 }
1212
1222 template<typename T>
1224 {
1225 return radians * T(57.295779513082320876798154814105);
1226 }
1227
1235 struct Random
1236 {
1237 private:
1238
1242 inline static std::random_device _randomDevice = {};
1243
1247 inline static std::mt19937 _mt19937 = std::mt19937(_randomDevice());
1248
1254 inline static void reseed_mt19937_with_current_time()
1255 {
1256 auto now = std::chrono::high_resolution_clock::now();
1257 auto duration = now.time_since_epoch();
1258 auto nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();
1259 std::uint32_t new_seed = static_cast<std::uint32_t>(nanos & 0xFFFFFFFF);
1260 _mt19937.seed(new_seed);
1261 }
1262
1263 public:
1264
1280 template <typename T>
1281 static const T value(const T min, const T max, const size_t seed = (std::numeric_limits<size_t>::max)())
1282 {
1283 std::mt19937* mt19937Pointer = nullptr;
1284
1285 if (seed != (std::numeric_limits<size_t>::max)())
1286 {
1287 // Create a temporary engine with the provided seed
1288 mt19937Pointer = new std::mt19937(seed);
1289 }
1290 else
1291 {
1292 // Reseed static engine with current time and use it
1293 reseed_mt19937_with_current_time();
1294 mt19937Pointer = &Random::_mt19937;
1295 }
1296
1297 if constexpr (std::is_floating_point<T>::value)
1298 {
1299 std::uniform_real_distribution<T> distrib(min, max);
1300 auto value = distrib(*mt19937Pointer);
1301
1302 if (seed != (std::numeric_limits<size_t>::max)())
1303 {
1304 delete mt19937Pointer;
1305 mt19937Pointer = nullptr;
1306 }
1307
1308 return value;
1309 }
1310 else if constexpr (std::is_integral<T>::value)
1311 {
1312 std::uniform_int_distribution<T> distrib(min, max);
1313 auto value = distrib(*mt19937Pointer);
1314
1315 if (seed != (std::numeric_limits<size_t>::max)())
1316 {
1317 delete mt19937Pointer;
1318 mt19937Pointer = nullptr;
1319 }
1320
1321 return value;
1322 }
1323
1324 throw std::runtime_error("Type is not supported by Random::value");
1325 };
1326
1339 template <typename T>
1340 static const T value(const T min, const T max, std::mt19937& mt19937)
1341 {
1342 if constexpr (std::is_floating_point<T>::value)
1343 {
1344 std::uniform_real_distribution<T> distrib(min, max);
1345 return distrib(mt19937);
1346 }
1347 else if constexpr (std::is_integral<T>::value)
1348 {
1349 std::uniform_int_distribution<T> distrib(min, max);
1350 return distrib(mt19937);
1351 }
1352 throw std::runtime_error("Type is not supported by Random::value");
1353 };
1354
1366 template <typename T>
1367 static const T valueFromRandomRange(const std::vector<std::pair<T, T>>& ranges,
1368 const size_t seed = (std::numeric_limits<size_t>::max)())
1369 {
1370 auto rangesSize = ranges.size();
1371 auto rangesData = ranges.data();
1372
1373 // Select a random index to pick a range
1374 size_t rangeIndex = Random::value<size_t>(0, rangesSize - 1, seed);
1375
1376 // Generate random value from the selected range
1377 auto& range = rangesData[rangeIndex];
1378 return Random::value(range.first, range.second, seed);
1379 };
1380
1391 template <typename T>
1392 static const T valueFromRandomRange(const std::vector<std::pair<T, T>>& ranges, std::mt19937& mt19937)
1393 {
1394 auto rangesSize = ranges.size();
1395 auto rangesData = ranges.data();
1396
1397 // Select a random index to pick a range
1398 size_t rangeIndex = Random::value<size_t>(0, rangesSize - 1, mt19937);
1399
1400 // Generate random value from the selected range
1401 auto& range = rangesData[rangeIndex];
1402 return Random::value(range.first, range.second, mt19937);
1403 };
1404 };
1405
1414 template <typename T, size_t N>
1415 struct AABB
1416 {
1419
1425 AABB() = default;
1426
1434 : min(min)
1435 , max(max)
1436 {}
1437 };
1438
1448 template <typename T>
1449 struct quat : public vec<T, 4>
1450 {
1452
1453 using Base::data;
1454 using Base::operator=;
1455 using Base::operator+=;
1456 using Base::operator-=;
1457 using Base::operator*=;
1458 using Base::operator/=;
1459 using Base::operator+;
1460 using Base::operator-;
1461 using Base::operator*;
1462 using Base::operator/;
1463 using Base::operator[];
1464 using Base::length;
1465 using Base::normalize;
1466
1470 quat() = default;
1471
1476 quat(const quat&) = default;
1477
1483 quat& operator=(const quat&) = default;
1484
1489 quat(quat&&) noexcept = default;
1490
1496 quat& operator=(quat&&) noexcept = default;
1497
1503 template <typename... Args>
1504 quat(Args&&... args) : Base(std::forward<Args>(args)...) { }
1505
1513 quat(T w, T x, T y, T z)
1514 {
1515 data[0] = w;
1516 data[1] = x;
1517 data[2] = y;
1518 data[3] = z;
1519 }
1520
1529 {
1530 return quat<T>(data[0], -data[1], -data[2], -data[3]);
1531 }
1532
1541 {
1542 quat<T> conj = conjugate();
1543 T norm_sq = T(0);
1544 for (size_t i = 0; i < 4; ++i)
1545 norm_sq += data[i] * data[i];
1546 return conj / norm_sq;
1547 }
1548
1556 {
1557 quat<T> qv(0, v[0], v[1], v[2]);
1558 quat<T> result = (*this) * qv * inverse();
1559 return vec<T, 3>(result[1], result[2], result[3]);
1560 }
1561
1570 quat<T> operator*(const quat<T>& rhs) const
1571 {
1572 T w = data[0] * rhs[0] - data[1] * rhs[1] - data[2] * rhs[2] - data[3] * rhs[3];
1573 T x = data[0] * rhs[1] + data[1] * rhs[0] + data[2] * rhs[3] - data[3] * rhs[2];
1574 T y = data[0] * rhs[2] - data[1] * rhs[3] + data[2] * rhs[0] + data[3] * rhs[1];
1575 T z = data[0] * rhs[3] + data[1] * rhs[2] - data[2] * rhs[1] + data[3] * rhs[0];
1576 return quat<T>(w, x, y, z);
1577 }
1578
1586 static quat<T> from_axis_angle(const vec<T, 3>& axis, T angle)
1587 {
1588 T half_angle = angle * T(0.5);
1589 T s = std::sin(half_angle);
1590 return quat<T>(std::cos(half_angle), axis[0] * s, axis[1] * s, axis[2] * s);
1591 }
1592
1599 void to_axis_angle(vec<T, 3>& out_axis, T& out_angle) const
1600 {
1601 quat<T> norm_q = this->normalize();
1602 out_angle = T(2) * std::acos(norm_q[0]);
1603 T s = std::sqrt(T(1) - norm_q[0] * norm_q[0]);
1604 if (s < T(1e-6))
1605 {
1606 out_axis = vec<T, 3>(1, 0, 0);
1607 }
1608 else
1609 {
1610 out_axis = vec<T, 3>(norm_q[1] / s, norm_q[2] / s, norm_q[3] / s);
1611 }
1612 }
1613
1620 {
1621 T w = data[0], x = data[1], y = data[2], z = data[3];
1622 T xx = x * x, yy = y * y, zz = z * z;
1623 T xy = x * y, xz = x * z, yz = y * z;
1624 T wx = w * x, wy = w * y, wz = w * z;
1625
1626 mat<T, 4, 4> m = {};
1627 m[0][0] = T(1) - T(2) * (yy + zz);
1628 m[0][1] = T(2) * (xy - wz);
1629 m[0][2] = T(2) * (xz + wy);
1630 m[0][3] = T(0);
1631
1632 m[1][0] = T(2) * (xy + wz);
1633 m[1][1] = T(1) - T(2) * (xx + zz);
1634 m[1][2] = T(2) * (yz - wx);
1635 m[1][3] = T(0);
1636
1637 m[2][0] = T(2) * (xz - wy);
1638 m[2][1] = T(2) * (yz + wx);
1639 m[2][2] = T(1) - T(2) * (xx + yy);
1640 m[2][3] = T(0);
1641
1642 m[3][0] = T(0);
1643 m[3][1] = T(0);
1644 m[3][2] = T(0);
1645 m[3][3] = T(1);
1646
1647 return m;
1648 }
1649 };
1650
1651 // Convert a unit quaternion to a 3x3 rotation matrix
1652 // Assumes quat is normalized
1653 // R = (1 - 2yy - 2zz, 2xy - 2wz, 2xz + 2wy)
1654 // (2xy + 2wz, 1 - 2xx - 2zz, 2yz - 2wx)
1655 // (2xz - 2wy, 2yz + 2wx, 1 - 2xx - 2yy)
1656 template <typename T>
1658 {
1659 T w = q[0];
1660 T x = q[1];
1661 T y = q[2];
1662 T z = q[3];
1663
1664 T xx = x * x;
1665 T yy = y * y;
1666 T zz = z * z;
1667 T xy = x * y;
1668 T xz = x * z;
1669 T yz = y * z;
1670 T wx = w * x;
1671 T wy = w * y;
1672 T wz = w * z;
1673
1674 mat<T, 3, 3> result(T(1));
1675 result[0][0] = T(1) - T(2) * (yy + zz);
1676 result[0][1] = T(2) * (xy - wz);
1677 result[0][2] = T(2) * (xz + wy);
1678 result[1][0] = T(2) * (xy + wz);
1679 result[1][1] = T(1) - T(2) * (xx + zz);
1680 result[1][2] = T(2) * (yz - wx);
1681 result[2][0] = T(2) * (xz - wy);
1682 result[2][1] = T(2) * (yz + wx);
1683 result[2][2] = T(1) - T(2) * (xx + yy);
1684 return result;
1685 }
1686
1695 template <typename T>
1696 quat<T> slerp(const quat<T>& a, const quat<T>& b, T t)
1697 {
1698 quat<T> q1 = a.normalize();
1699 quat<T> q2 = b.normalize();
1700
1701 T dot = T(0);
1702 for (int i = 0; i < 4; ++i)
1703 dot += q1[i] * q2[i];
1704
1705 if (dot < T(0))
1706 {
1707 dot = -dot;
1708 q2 = q2 * T(-1);
1709 }
1710
1711 const T DOT_THRESHOLD = T(0.9995);
1712 if (dot > DOT_THRESHOLD)
1713 {
1714 quat<T> result = q1 + (q2 - q1) * t;
1715 return result.normalize();
1716 }
1717
1718 T theta_0 = std::acos(dot);
1719 T theta = theta_0 * t;
1720 T sin_theta = std::sin(theta);
1721 T sin_theta_0 = std::sin(theta_0);
1722
1723 T s0 = std::cos(theta) - dot * sin_theta / sin_theta_0;
1724 T s1 = sin_theta / sin_theta_0;
1725
1726 return (q1 * s0 + q2 * s1).normalize();
1727 }
1728
1736 template <typename T>
1738 {
1739 T half = angle * T(0.5);
1740 T s = std::sin(half);
1741 return quat<T>(std::cos(half), axis[0] * s, axis[1] * s, axis[2] * s);
1742 }
1743
1750 template <typename T>
1752 {
1753 mat<T, 4, 4> m(T(1));
1755 for (size_t i = 0; i < 3; ++i)
1756 for (size_t j = 0; j < 3; ++j)
1757 m[i][j] = r[i][j];
1758 return m;
1759 }
1760}
Definition DirectZ.hpp:39
mat< T, 3, 3 > quat_to_mat3(const quat< T > &q)
Definition math.hpp:1657
@ a
Definition D7Stream.hpp:22
@ u
Definition D7Stream.hpp:21
@ T
Definition D7Stream.hpp:19
mat< T, 4, 4 > perspectiveLH_ZO(T fovy, T aspect, T zNear, T zFar)
Definition math.hpp:989
T radians(T degrees)
Converts an angle from degrees to radians.
Definition math.hpp:1208
mat< T, 4, 4 > perspectiveRH_ZO(T fovy, T aspect, T zNear, T zFar)
Definition math.hpp:957
mat< T, 4, 4 > lookAt(const vec< T, 3 > &eye, const vec< T, 3 > &center, const vec< T, 3 > &up)
Constructs a right-handed view matrix simulating a camera "look at" transform.
Definition math.hpp:922
quat< T > quat_from_axis_angle(const vec< T, 3 > &axis, T angle)
Constructs a quaternion from an axis-angle representation.
Definition math.hpp:1737
mat< T, 4, 4 > infinitePerspective(T fovy, T aspect, T zNear)
Creates an infinite perspective projection matrix.
Definition math.hpp:1153
mat< T, 4, 4 > orthographic(T left, T right, T bottom, T top, T znear, T zfar)
Creates an orthographic projection matrix.
Definition math.hpp:1185
T degrees(T radians)
Converts an angle from radians to degrees.
Definition math.hpp:1223
mat< T, 4, 4 > perspective(T fovy, T aspect, T zNear, T zFar)
Creates a perspective projection matrix suitable for rendering 3D scenes.
Definition math.hpp:1044
quat< T > slerp(const quat< T > &a, const quat< T > &b, T t)
Spherical linear interpolation between two quaternions.
Definition math.hpp:1696
mat< T, 4, 4 > infinitePerspectiveRH_ZO(T fovy, T aspect, T zNear)
Definition math.hpp:1078
mat< T, 4, 4 > quat_to_mat4(const quat< T > &q)
Converts a quaternion to a 4x4 rotation matrix.
Definition math.hpp:1751
mat< T, 4, 4 > infinitePerspectiveRH_NO(T fovy, T aspect, T zNear)
Definition math.hpp:1060
mat< T, 4, 4 > perspectiveRH_NO(T fovy, T aspect, T zNear, T zFar)
Definition math.hpp:973
mat< T, 4, 4 > perspectiveLH_NO(T fovy, T aspect, T zNear, T zFar)
Definition math.hpp:1005
mat< T, 4, 4 > infinitePerspectiveLH_ZO(T fovy, T aspect, T zNear)
Definition math.hpp:1114
mat< T, 4, 4 > infinitePerspectiveLH_NO(T fovy, T aspect, T zNear)
Definition math.hpp:1096
@ s
Definition Window.hpp:180
@ C
Definition Window.hpp:130
@ l
Definition Window.hpp:173
@ j
Definition Window.hpp:171
@ y
Definition Window.hpp:186
@ c
Definition Window.hpp:163
@ r
Definition Window.hpp:179
@ m
Definition Window.hpp:174
@ q
Definition Window.hpp:178
@ i
Definition Window.hpp:170
@ k
Definition Window.hpp:172
@ N
Definition Window.hpp:141
@ f
Definition Window.hpp:167
@ b
Definition Window.hpp:164
@ x
Definition Window.hpp:185
@ v
Definition Window.hpp:183
@ e
Definition Window.hpp:166
@ R
Definition Window.hpp:145
@ t
Definition Window.hpp:181
@ w
Definition Window.hpp:184
@ O
Definition Window.hpp:142
@ D
Definition Window.hpp:131
@ z
Definition Window.hpp:187
AABB()=default
Default constructor.
vec< T, N > max
Definition math.hpp:1418
AABB(vec< T, N > min, vec< T, N > max)
Construct an AABB with specified minimum and maximum corners.
Definition math.hpp:1433
vec< T, N > min
Definition math.hpp:1417
Utility class for generating random values with optional seeding and support for multiple numeric typ...
Definition math.hpp:1236
static const T value(const T min, const T max, const size_t seed=(std::numeric_limits< size_t >::max)())
Generate a uniformly distributed random value in [min, max].
Definition math.hpp:1281
static const T valueFromRandomRange(const std::vector< std::pair< T, T > > &ranges, const size_t seed=(std::numeric_limits< size_t >::max)())
Generate a uniformly distributed random value from multiple specified ranges.
Definition math.hpp:1367
static const T value(const T min, const T max, std::mt19937 &mt19937)
Generate a uniformly distributed random value in [min, max] using a provided mt19937 engine.
Definition math.hpp:1340
static const T valueFromRandomRange(const std::vector< std::pair< T, T > > &ranges, std::mt19937 &mt19937)
Generate a uniformly distributed random value from multiple specified ranges using external mt19937 e...
Definition math.hpp:1392
color_vec(color_vec &&) noexcept=default
color_vec(Args &&... args)
Definition math.hpp:403
color_vec()=default
color_vec(const color_vec &)=default
vec< T, N > Base
Definition math.hpp:386
color_vec & operator=(const color_vec &)=default
color_vec(const vec< OT, ON > &other)
Definition math.hpp:406
A generic fixed-size matrix template supporting common matrix operations.
Definition math.hpp:431
mat & rotateAround(T angle, const vec< T, N > &axis, const vec< T, N > &point)
Rotates the matrix around a specified pivot point.
Definition math.hpp:746
const vec< T, R > & operator[](size_t i) const
Provides access to the column vector at index i (const).
Definition math.hpp:491
mat & operator=(const mat &other)
Copy assignment operator.
Definition math.hpp:466
mat< T, C, R > inverse() const
Computes the inverse of a square matrix.
Definition math.hpp:771
mat< T, C2, R > operator*(const mat< T, C2, R2 > &rhs) const
Returns a new matrix which is the product of this matrix and rhs.
Definition math.hpp:872
static mat translate_static(const vec< T, N > &v)
Definition math.hpp:516
static mat rotate_static(T angle, const vec< T, N > &axis)
Definition math.hpp:652
T matrix[C][R]
Raw matrix storage, column-major order.
Definition math.hpp:437
vec< T, R > & operator[](size_t i)
Provides access to the column vector at index i (non-const).
Definition math.hpp:480
static mat scale_static(const vec< T, N > &v)
Definition math.hpp:547
mat & scale(const vec< T, N > &v)
Applies a component-wise scale to the matrix columns.
Definition math.hpp:536
mat & rotate(T angle, const vec< T, N > &axis)
Rotates the matrix in-place around the specified axis by the given angle.
Definition math.hpp:574
constexpr mat< T, R, C > transpose()
Definition math.hpp:878
mat & translate(const vec< T, N > &v)
Applies a translation to a 4x4 matrix by adding a vector to its position component.
Definition math.hpp:507
mat(T initial=T())
Constructs a matrix initialized to zero with optional diagonal initialization.
Definition math.hpp:448
mat< T, C, R > & operator*=(const mat< T, C2, R2 > &rhs)
Performs in-place multiplication by another matrix.
Definition math.hpp:833
A quaternion class for representing rotations in 3D space.
Definition math.hpp:1450
vec< T, 4 > Base
Definition math.hpp:1451
quat(const quat &)=default
Copy constructor.
quat(T w, T x, T y, T z)
Construct quaternion from explicit components.
Definition math.hpp:1513
mat< T, 4, 4 > to_mat4() const
Converts the quaternion to a 4x4 rotation matrix.
Definition math.hpp:1619
quat()=default
Default constructor. Components are uninitialized.
void to_axis_angle(vec< T, 3 > &out_axis, T &out_angle) const
Converts the quaternion to axis-angle representation.
Definition math.hpp:1599
quat< T > inverse() const
Returns the inverse of the quaternion.
Definition math.hpp:1540
vec< T, 3 > rotate(const vec< T, 3 > &v) const
Rotates a 3D vector using this quaternion.
Definition math.hpp:1555
T data[N]
Underlying array storing vector components.
Definition math.hpp:76
vec normalize() const
Returns a normalized (unit length) copy of the vector.
Definition math.hpp:371
quat< T > conjugate() const
Returns the conjugate of the quaternion.
Definition math.hpp:1528
quat & operator=(const quat &)=default
Copy assignment operator.
static quat< T > from_axis_angle(const vec< T, 3 > &axis, T angle)
Constructs a quaternion from an axis-angle representation.
Definition math.hpp:1586
quat(quat &&) noexcept=default
Move constructor.
quat< T > operator*(const quat< T > &rhs) const
Quaternion multiplication.
Definition math.hpp:1570
Template struct representing an N-dimensional vector of type T.
Definition math.hpp:28
vec & operator+=(const O &other)
Compound addition-assignment operator.
Definition math.hpp:228
vec operator/(const O &other) const
Division operator returning a new vector.
Definition math.hpp:294
vec operator-() const
Unary negation operator.
Definition math.hpp:339
vec operator-(const O &other) const
Subtraction operator returning a new vector.
Definition math.hpp:326
vec & operator-=(const O &other)
Compound subtraction-assignment operator.
Definition math.hpp:254
vec & operator/=(const O &other)
Compound division-assignment operator.
Definition math.hpp:202
T length() const
Computes the Euclidean length (magnitude) of the vector.
Definition math.hpp:354
vec operator*(const O &other) const
Multiplication operator returning a new vector.
Definition math.hpp:278
vec(const vec< OT, ON > &other)
Copy constructor to convert from vectors of different types and/or sizes.
Definition math.hpp:114
vec & operator=(const vec &other)
Assignment operator for component-wise copy.
Definition math.hpp:132
const T & operator[](size_t i) const
Index operator for const access to vector components.
Definition math.hpp:159
vec operator+(const O &other) const
Addition operator returning a new vector.
Definition math.hpp:310
T & operator[](size_t i)
Index operator for non-const access to vector components.
Definition math.hpp:146
T data[N]
Underlying array storing vector components.
Definition math.hpp:76
vec normalize() const
Returns a normalized (unit length) copy of the vector.
Definition math.hpp:371
vec(Args... args)
Variadic constructor to initialize vector components.
Definition math.hpp:90
vec & operator*=(const O &other)
Compound multiplication-assignment operator.
Definition math.hpp:176