diff --git a/CMakeLists.txt b/CMakeLists.txt index 33d905d..4520150 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -129,6 +129,7 @@ add_library(fennec STATIC include/fennec/fproc/filesystem/file.h source/fproc/filesystem/file.cpp include/fennec/fproc/filesystem/path.h source/fproc/filesystem/path.cpp include/fennec/math/ext/transform.h + include/fennec/math/ext/quaternion.h ) # add metaprogramming templates as a dependency and also force documentation to be generated when fennec is compiled diff --git a/include/fennec/fproc/strings/cstring.h b/include/fennec/fproc/strings/cstring.h index 4ac4588..2169ea4 100644 --- a/include/fennec/fproc/strings/cstring.h +++ b/include/fennec/fproc/strings/cstring.h @@ -63,7 +63,7 @@ public: /// /// \brief Default Constructor, initializes with nullptr constexpr cstring() - : _str(nullptr), _size(0), _const(true) { + : _str(nullptr), _size(0), _const(true) { } /// @@ -71,9 +71,9 @@ public: /// \param str the buffer to wrap /// \param n the number of characters in the buffer plus the null terminator constexpr cstring(char* str, size_t n) - : _str(str) - , _size(n - 1) - , _const(false) { + : _str(str) + , _size(n - 1) + , _const(false) { assert(_str[n - 1] == '\0', "Invalid NTBS."); } @@ -94,9 +94,9 @@ public: /// \param str the buffer to wrap /// \param n the number of characters in the buffer plus the null terminator constexpr cstring(const char* str, size_t n) - : _cstr(str) - , _size(n - 1) - , _const(true) { + : _cstr(str) + , _size(n - 1) + , _const(true) { assert(_cstr[n - 1] == '\0', "Invalid NTBS."); } @@ -106,9 +106,9 @@ public: /// \tparam n the number of characters in the buffer plus the null terminator template constexpr cstring(const char(&str)[n]) - : _cstr(str) - , _size(n - 1) - , _const(true) { + : _cstr(str) + , _size(n - 1) + , _const(true) { assert(_cstr[n - 1] == '\0', "Invalid NTBS."); } @@ -116,9 +116,9 @@ public: /// \brief Move Constructor /// \param str object to move constexpr cstring(cstring&& str) noexcept - : _cstr(str._cstr) - , _size(str._size) - , _const(str._const) { + : _cstr(str._cstr) + , _size(str._size) + , _const(str._const) { str._cstr = nullptr; str._size = 0; str._const = true; diff --git a/include/fennec/fproc/strings/string.h b/include/fennec/fproc/strings/string.h index 171bc66..39ced8a 100644 --- a/include/fennec/fproc/strings/string.h +++ b/include/fennec/fproc/strings/string.h @@ -55,7 +55,7 @@ public: /// /// \brief Default Constructor, initializes empty string constexpr _string() - : _str() { + : _str() { } /// @@ -64,7 +64,7 @@ public: /// /// \details adds additional character for null termination. constexpr _string(size_t n) - : _string('\0', n) { + : _string('\0', n) { } /// @@ -74,7 +74,7 @@ public: /// /// \details adds additional character for null termination. constexpr _string(char c, size_t n) - : _str(n + 1) { + : _str(n + 1) { fennec::memset(_str.data(), c, n); _str[n] = '\0'; } /// @@ -86,7 +86,7 @@ public: /// This constructor makes the assumption that `len` is the intended number of characters. template constexpr _string(const char str[n]) - : _str(str, n + 1) { + : _str(str, n + 1) { ::strncpy(_str.data(), str, n); _str[n] = '\0'; } @@ -99,7 +99,7 @@ public: /// \details adds additional character for null termination. Ignores whether str is null-terminated. /// This constructor makes the assumption that `len` is the intended number of characters. constexpr _string(const char* str, size_t n) - : _str(str, n + 1) { + : _str(str, n + 1) { ::strncpy(_str.data(), str, n); _str[n] = '\0'; } @@ -112,7 +112,7 @@ public: /// \details adds additional character for null termination. Ignores whether str is null-terminated. /// This constructor makes the assumption that `len` is the intended number of characters. constexpr _string(const cstring& str) - : _str(str, str.size() + 1) { + : _str(str, str.size() + 1) { _str[str.size()] = '\0'; } @@ -120,11 +120,12 @@ public: /// \brief String Copy Constructor /// \param str the string to copy constexpr _string(const string& str) - : _string(str, str.size()) { + : _string(str, str.size()) { } constexpr _string(_string&& str) noexcept - : _str(fennec::move(str._str)) { } + : _str(fennec::move(str._str)) { + } /// /// \brief String Destructor, cleans up the underlying allocation diff --git a/include/fennec/fproc/strings/wcstring.h b/include/fennec/fproc/strings/wcstring.h index e40603e..fce3b33 100644 --- a/include/fennec/fproc/strings/wcstring.h +++ b/include/fennec/fproc/strings/wcstring.h @@ -66,7 +66,9 @@ public: /// /// \brief Default Constructor, initializes with nullptr constexpr wcstring() - : _str(nullptr), _size(0), _const(true) { + : _str(nullptr) + , _size(0) + , _const(true) { } /// @@ -74,9 +76,9 @@ public: /// \param str the buffer to wrap /// \param n the number of characters in the buffer plus the null terminator constexpr wcstring(wchar_t* str, size_t n) - : _str(str) - , _size(n - 1) - , _const(false) { + : _str(str) + , _size(n - 1) + , _const(false) { assert(_str[n - 1] == '\0', "Invalid NTBS."); } @@ -97,9 +99,9 @@ public: /// \param str the buffer to wrap /// \param n the number of characters in the buffer plus the null terminator constexpr wcstring(const wchar_t* str, size_t n) - : _cstr(str) - , _size(n - 1) - , _const(true) { + : _cstr(str) + , _size(n - 1) + , _const(true) { assert(_cstr[n - 1] == '\0', "Invalid NTBS."); } @@ -109,9 +111,9 @@ public: /// \tparam n the number of characters in the buffer plus the null terminator template constexpr wcstring(const wchar_t(&str)[n]) - : _cstr(str) - , _size(n - 1) - , _const(true) { + : _cstr(str) + , _size(n - 1) + , _const(true) { assert(_cstr[n - 1] == '\0', "Invalid NTBS."); } @@ -119,9 +121,9 @@ public: /// \brief Move Constructor /// \param str object to move constexpr wcstring(wcstring&& str) noexcept - : _cstr(str._cstr) - , _size(str._size) - , _const(str._const) { + : _cstr(str._cstr) + , _size(str._size) + , _const(str._const) { str._cstr = nullptr; str._size = 0; str._const = true; diff --git a/include/fennec/fproc/strings/wstring.h b/include/fennec/fproc/strings/wstring.h index 2826fda..a7139fd 100644 --- a/include/fennec/fproc/strings/wstring.h +++ b/include/fennec/fproc/strings/wstring.h @@ -55,7 +55,7 @@ public: /// /// \brief Default Constructor, initializes empty string constexpr _wstring() - : _str() { + : _str() { } /// @@ -64,7 +64,7 @@ public: /// /// \details adds additional wchar_tacter for null termination. constexpr _wstring(size_t n) - : _wstring('\0', n) { + : _wstring('\0', n) { } /// @@ -74,7 +74,7 @@ public: /// /// \details adds additional wchar_tacter for null termination. constexpr _wstring(wchar_t c, size_t n) - : _str(n + 1) { + : _str(n + 1) { fennec::wmemset(_str.data(), c, n); _str[n] = '\0'; } /// @@ -86,7 +86,7 @@ public: /// This constructor makes the assumption that `len` is the intended number of wchar_tacters. template constexpr _wstring(const wchar_t str[n]) - : _str(str, n + 1) { + : _str(str, n + 1) { ::wcsncpy(_str.data(), str, n); _str[n] = '\0'; } @@ -99,7 +99,7 @@ public: /// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated. /// This constructor makes the assumption that `len` is the intended number of wchar_tacters. constexpr _wstring(const wchar_t* str, size_t n) - : _str(str, n + 1) { + : _str(str, n + 1) { ::wcsncpy(_str.data(), str, n); _str[n] = '\0'; } @@ -112,7 +112,7 @@ public: /// \details adds additional wchar_tacter for null termination. Ignores whether str is null-terminated. /// This constructor makes the assumption that `len` is the intended number of wchar_tacters. constexpr _wstring(const wcstring& str) - : _str(str, str.size() + 1) { + : _str(str, str.size() + 1) { _str[str.size()] = '\0'; } @@ -120,11 +120,12 @@ public: /// \brief String Copy Constructor /// \param str the string to copy constexpr _wstring(const wstring& str) - : _wstring(str, str.size() - 1) { + : _wstring(str, str.size() - 1) { } constexpr _wstring(_wstring&& str) noexcept - : _str(fennec::move(str._str)) { } + : _str(fennec::move(str._str)) { + } /// /// \brief String Destructor, cleans up the underlying allocation diff --git a/include/fennec/math/ext/quaternion.h b/include/fennec/math/ext/quaternion.h new file mode 100644 index 0000000..b32c3d0 --- /dev/null +++ b/include/fennec/math/ext/quaternion.h @@ -0,0 +1,369 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_MATH_EXT_QUATERNION_H +#define FENNEC_MATH_EXT_QUATERNION_H + +#include +#include + +namespace fennec +{ + +template struct quaternion; + + +using quat = quaternion; +using dquat = quaternion; + +/// +/// \brief Dot Function +/// \param q The Quaternion +/// \returns The square sum of all components. +template +constexpr genType dot(const quaternion& x, const quaternion& y) { + return x.w*y.w + x.x*y.x + x.y*y.y + x.z*y.z; +} + +/// +/// \brief Square Norm Function +/// \param q The Quaternion +/// \returns The square sum of all components. +template +constexpr genType sqnorm(const quaternion& q) { + return fennec::dot(q, q); +} + +/// +/// \brief Norm Function +/// \param q The Quaternion +/// \returns The Hamilton Tensor of `q` +template +constexpr genType norm(const quaternion& q) { + return fennec::sqrt(sqnorm(q)); +} + +/// +/// \brief Unit Function (Versor) +/// \param q The Quaternion +/// \returns A Quaternion of `q` with norm `1` +template +constexpr quaternion unit(const quaternion& q) { + genType n = fennec::norm(q); + return q * (genType(1) / n); +} + +/// +/// \brief Reciprocal Function +/// \param q The Quaternion +/// \returns The quaternion \f${q}^{-1}\f$ +template +constexpr quaternion reciprocal(const quaternion& q) { + return ~q / fennec::sqnorm(q); +} + +template +constexpr quaternion mix(const quaternion& x, const quaternion& y, genType a) { + genType cT = fennec::dot(x, y); + quaternion z = cT < genType(0) ? -y : y; + cT = cT < genType(0) ? -cT : cT; + + if (cT > genType(1) - numeric_limits::epsilon()) { + return x * (genType(1) - a) + y * a; + } + + genType t = fennec::acos(cT); + return (fennec::sin(x * (genType(1) - a) * t) + z * (fennec::sin(a * t))) / fennec::sin(t); +} + +template +struct quaternion : detail::vector_base_type +{ +public: +// Typedefs ============================================================================================================ + + using base_type = detail::vector_base_type; + using scalar_t = ScalarT; + using quat_t = quaternion; + using vec3_t = vec; + using vec4_t = vec; + + using base_type::data; + using base_type::x, base_type::y, base_type::z, base_type::w; + + +// Constructors ======================================================================================================== + + /// + /// \brief Default Constructor, creates a quaternion such that the real part is `1` and all others are `0` + constexpr quaternion() { + data = { 0, 0, 0, 1 }; + } + + /// + /// \brief Component Constructor + /// \param w The real component + /// \param x The coefficient of the `i` component + /// \param y The coefficient of the `j` component + /// \param z The coefficient of the `k` component + constexpr quaternion(scalar_t w, scalar_t x, scalar_t y, scalar_t z) { + data = { x, y, z, w }; + } + + /// + /// \brief Vector Constructor + /// \param v A 4d vector + constexpr quaternion(const vec& v) { + data = v.data; + } + + /// + /// \brief Swizzle Constructor + /// \param s A 4d swizzle + template requires(sizeof...(IndicesV) == 4) + constexpr quaternion(const detail::swizzle_storage& s) { + data[0] = s[0]; + data[1] = s[1]; + data[2] = s[2]; + data[3] = s[3]; + } + + /// + /// \brief Copy Constructor + /// \param q The quaternion to copy + constexpr quaternion(const quaternion& q) { + data = q.data; + } + + /// + /// \brief Move Constructor + /// \param q The quaternion to move + constexpr quaternion(quaternion&& q) noexcept { + data = q.data; + } + + +// Assignment Operators ================================================================================================ + + /// + /// \brief Copy Assignment Operator + /// \param q The quaternion to copy + constexpr quat_t& operator=(const quat_t& q) { + data = q.data; + return *this; + } + + /// + /// \brief Move Assignment Operator + /// \param q The quaternion to move + constexpr quat_t& operator=(quat_t&& q) noexcept { + data = q.data; + return *this; + } + + +// Comparison Operators ================================================================================================ + + /// + /// \brief Equality Operator + /// \param lhs the left hand side of the expression + /// \param rhs the right hand side of the expression + /// \returns `true` when all components of `lhs` and `rhs` are equal, false otherwise + constexpr friend bool operator==(const quat_t& lhs, const quat_t& rhs) { + return lhs.data == rhs.data; + } + + /// + /// \brief Inequality Operator + /// \param lhs the left hand side of the expression + /// \param rhs the right hand side of the expression + /// \returns `true` when any component of `lhs` and `rhs` are not equal, false otherwise + constexpr friend bool operator!=(const quat_t& lhs, const quat_t& rhs) { + return lhs.data != rhs.data; + } + + +// Unary Operators ===================================================================================================== + + /// + /// \brief Unary Negation Operator + /// \param rhs The quaternion to negate + /// \returns A quaternion with each component of `rhs` negated. + constexpr friend quat_t operator-(const quat_t& rhs) { + return quat_t(-rhs.w, -rhs.x, -rhs.y, -rhs.z); + } + + /// + /// \brief Unary Conjugation Operator + /// \param rhs The quaternion to conjugate + /// \returns A quaternion with each vector component of `rhs` negated. + constexpr friend quat_t operator~(const quat_t& rhs) { + return quat_t(rhs.w, -rhs.x, -rhs.y, -rhs.z); + } + + +// Quaternion Scalar Arithmetic Operators ============================================================================== + + /// + /// \brief Quaternion-Scalar Multiplication Operator + /// \param lhs The quaternion + /// \param rhs The scalar + /// \returns A quaternion with each component of `lhs` multiplied by `rhs` + constexpr friend quat_t operator*(const quat_t& lhs, scalar_t rhs) { + return quat_t(lhs.w * rhs, lhs.x * rhs, lhs.y * rhs, lhs.z * rhs); + } + + /// + /// \brief Scalar-Quaternion Multiplication Operator + /// \param lhs The scalar + /// \param rhs The quaternion + /// \returns A quaternion with each component of `rhs` multiplied by `lhs` + constexpr friend quat_t operator*(scalar_t lhs, const quat_t& rhs) { + return quat_t(lhs * rhs.w, lhs * rhs.x, lhs * rhs.y, lhs * rhs.z); + } + + /// + /// \brief Quaternion-Scalar Division Operator + /// \param lhs The quaternion + /// \param rhs The scalar + /// \returns A quaternion with each component of `lhs` divided by `rhs` + constexpr friend quat_t operator/(const quat_t& lhs, scalar_t rhs) { + return quat_t(lhs.w / rhs, lhs.x / rhs, lhs.y / rhs, lhs.z / rhs); + } + + /// + /// \brief Scalar-Quaternion Division Operator + /// \param lhs The scalar + /// \param rhs The quaternion + /// \returns A quaternion with each component of `rhs` divided by `lhs` + constexpr friend quat_t operator/(scalar_t lhs, const quat_t& rhs) { + return quat_t(lhs / rhs.w, lhs / rhs.x, lhs / rhs.y, lhs / rhs.z); + } + + +// Quaternion Scalar Arithmetic Assignment Operators =================================================================== + + /// + /// \brief Quaternion-Scalar Multiplication Assignment Operator + /// \param lhs The quaternion + /// \param rhs The scalar + /// \returns `lhs` with each component multiplied by `rhs` + constexpr friend quat_t& operator*=(quat_t& lhs, scalar_t rhs) { + lhs.x *= rhs; + lhs.y *= rhs; + lhs.z *= rhs; + lhs.w *= rhs; + return lhs; + } + + /// + /// \brief Quaternion-Scalar Division Assignment Operator + /// \param lhs The quaternion + /// \param rhs The scalar + /// \returns `lhs` with each component divided by `rhs` + constexpr friend quat_t& operator/=(const quat_t& lhs, scalar_t rhs) { + lhs.x /= rhs; + lhs.y /= rhs; + lhs.z /= rhs; + lhs.w /= rhs; + return lhs; + } + + +// Quaternion Vector Arithmetic Operators ============================================================================== + + /// \brief + constexpr friend vec3_t operator*(const quat_t& q, const vec3_t& v) { + const vec3_t u = q.xyz; + const vec3_t uv = fennec::cross(u, v); + const vec3_t uuv = fennec::cross(u, uv); + return v + (uv*q.w + uuv) * scalar_t(2); + } + + constexpr friend vec3_t operator*(const vec3_t& v, const quat_t& q) { + return fennec::reciprocal(q) * v; + } + + constexpr friend vec4_t operator*(const quat_t& q, const vec4_t& v) { + return vec4_t(q * v.xyz, v.w); + } + + constexpr friend vec4_t operator*(const vec4_t& v, const quat_t& q) { + return fennec::reciprocal(q) * v; + } + + +// Quaternion Quaternion Arithmetic Operators ========================================================================== + + constexpr friend quaternion operator+(const quaternion& lhs, const quaternion& rhs) { + return quaternion( + lhs.w + rhs.w, + lhs.x + rhs.x, + lhs.y + rhs.y, + lhs.z + rhs.z + ); + } + + constexpr friend quaternion operator-(const quaternion& lhs, const quaternion& rhs) { + return quaternion( + lhs.w - rhs.w, + lhs.x - rhs.x, + lhs.y - rhs.y, + lhs.z - rhs.z + ); + } + + constexpr friend quaternion operator*(const quaternion& lhs, const quaternion& rhs) { + return quaternion( + lhs.w*rhs.w - lhs.x*rhs.x - lhs.y*rhs.y - lhs.z * rhs.z, + lhs.w*rhs.x + lhs.x*rhs.w + lhs.y*rhs.z - lhs.z * rhs.y, + lhs.w*rhs.y - lhs.x*rhs.z + lhs.y*rhs.w + lhs.z * rhs.x, + lhs.w*rhs.z - lhs.x*rhs.y - lhs.y*rhs.x + lhs.z * rhs.w + ); + } + + +// Quaternion Quaternion Arithmetic Assignment Operators =============================================================== + + constexpr friend quaternion& operator+=(quaternion& lhs, const quaternion& rhs) { + lhs.w += rhs.w; + lhs.x += rhs.x; + lhs.y += rhs.y; + lhs.z += rhs.z; + return lhs; + } + + constexpr friend quaternion& operator-=(quaternion& lhs, const quaternion& rhs) { + lhs.w -= rhs.w; + lhs.x -= rhs.x; + lhs.y -= rhs.y; + lhs.z -= rhs.z; + return lhs; + } + + constexpr friend quaternion& operator*=(quaternion& lhs, const quaternion& rhs) { + return lhs = lhs * rhs; + } + +private: + +}; + +} + +#endif // FENNEC_MATH_EXT_QUATERNION_H diff --git a/include/fennec/math/ext/transform.h b/include/fennec/math/ext/transform.h index eadf4c4..900d978 100644 --- a/include/fennec/math/ext/transform.h +++ b/include/fennec/math/ext/transform.h @@ -33,9 +33,9 @@ namespace fennec template constexpr mat translation(const vec& x) { return mat( - 1, 0, 0 - , 0, 1, 0 - , x, 1 + 1, 0, 0, + 0, 1, 0, + x, 1 ); } @@ -46,10 +46,10 @@ constexpr mat translation(const vec& x) { template constexpr mat translation(const vec& x) { return mat( - 1, 0, 0, 0 - , 0, 1, 0, 0 - , 0, 0, 1, 0 - , x, 1 + 1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + x, 1 ); } @@ -60,9 +60,9 @@ constexpr mat translation(const vec& x) { template constexpr mat scaling(const vec& x) { return mat( - x.x, 0, 0 - , 0, x.y, 0 - , 0, 0, 1 + x.x, 0, 0, + 0, x.y, 0, + 0, 0, 1 ); } @@ -73,10 +73,10 @@ constexpr mat scaling(const vec& x) { template constexpr mat scaling(const vec& x) { return mat( - x.x, 0, 0, 0 - , 0, x.y, 0, 0 - , 0, 0, x.z, 0 - , 0, 0, 0, 1 + x.x, 0, 0, 0, + 0, x.y, 0, 0, + 0, 0, x.z, 0, + 0, 0, 0, 1 ); } @@ -93,9 +93,9 @@ constexpr mat rotation(const genType a) { // Calculate the matrix return mat( - c, -s, 0 - , s, c, 0 - , 0, 0, 1 + c, -s, 0, + s, c, 0, + 0, 0, 1 ); } @@ -118,63 +118,83 @@ constexpr mat rotation(const vec& A, genType a) { // Calculate the Matrix return mat( - A.x * u.x + c, A.x * u.y + v.z, A.x * u.z - v.y, 0 - , A.x * u.y - v.z, A.y * u.y + c, A.y * u.z + v.x, 0 - , A.x * u.z + v.y, A.y * u.z - v.x, A.z * u.z + c, 0 - , 0, 0, 0, 1 + A.x * u.x + c, A.x * u.y + v.z, A.x * u.z - v.y, 0, + A.x * u.y - v.z, A.y * u.y + c, A.y * u.z + v.x, 0, + A.x * u.z + v.y, A.y * u.z - v.x, A.z * u.z + c, 0, + 0, 0, 0, 1 ); } -template -constexpr mat rotation_x(genType a) { +/// \brief enum to denote the unit-axis of rotation +enum rot_ +{ + rot_x = 0 +, rot_y +, rot_z +}; + +/// +/// \brief Create a 3d rotation matrix for an angle about a cartesian unit axis +/// \tparam axis the unit axis to rotate around +/// \param a the angle, in radians +/// \returns a matrix that rotates vectors around a unit axis +template +constexpr mat rotation(genType a) { + // https://en.wikipedia.org/wiki/Rotation_matrix#Basic_3D_rotations // Calculate sin and cos terms const genType c = fennec::cos(a); const genType s = fennec::sin(a); // Calculate the matrix - return mat( - 1, 0, 0, 0 - , 0, c, -s, 0 - , 0, s, c, 0 - , 0, 0, 0, 1 - ); + if constexpr(axis == rot_x) { + return mat( + 1, 0, 0, 0, + 0, c, -s, 0, + 0, s, c, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(axis == rot_y) { + return mat( + c, 0, s, 0, + 0, 0, 0, 0, + -s, 0, c, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(axis == rot_z) { + return mat( + c, -s, 0, 0, + s, c, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1 + ); + } + + return mat(); } -template -constexpr mat rotation_y(genType a) { +/// +/// \brief enum to denote the order rotations are applied in +enum order_ +{ + order_xyz +, order_xzy +, order_yxz +, order_yzx +, order_zxy +, order_zyx +}; - // Calculate sin and cos terms - const genType c = fennec::cos(a); - const genType s = fennec::sin(a); - - // Calculate the matrix - return mat( - c, 0, s, 0 - , 0, 0, 0, 0 - , -s, 0, c, 0 - , 0, 0, 0, 1 - ); -} - -template -constexpr mat rotation_z(const genType a) { - - // Calculate sin and cos terms - const genType c = fennec::cos(a); - const genType s = fennec::sin(a); - - // Calculate the matrix - return mat( - c, -s, 0, 0 - , s, c, 0, 0 - , 0, 0, 1, 0 - , 0, 0, 0, 1 - ); -} - -template +/// +/// \brief Create 3d rotation matrix from euler angles +/// \tparam order the order in which to apply the rotations +/// \param E the euler angles, taken as tait-bryan (pitch, yaw, roll) +/// \returns a matrix that rotates vectors +template constexpr mat rotation(const vec& E) { + // expanded using a CAS // Calculate sin and cos terms const genType ca = fennec::cos(E.x); @@ -185,12 +205,56 @@ constexpr mat rotation(const vec& E) { const genType sg = fennec::sin(E.z); // Calculate the matrix - return mat( - ca*cb, sa*sb, -sb, 0 - , ca*sb*sg - sa*cg, sa*sb*sg + ca*cg, cb*sg, 0 - , ca*sb*cg + sa*sg, sa*sb*cg - ca*sg, cb*cg, 0 - , 0, 0, 0, 1 - ); + if constexpr(order == order_xyz) { + return mat( + cb*cg, -cg*sg, sb, 0, + ca*sg + cg*sa*sb, ca*cg - sa*sb*sg, -ca*sa, 0, + sa*sg - ca*cg*sb, ca*sb*sg + cg*sa, ca*cb, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(order == order_xzy) { + return mat( + cb*cg, -sg, cg*sb, 0, + ca*cb*sg + sa*sb, ca*cg, cb*sa*sg - ca*sb, 0, + cb*sa*sg - ca*sb, cg*sa, ca*cb + sa*sb*sg, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(order == order_yxz) { + return mat( + cb*cg + sa*sb*sg, cg*sa*sb - cb*sg, ca*sb, 0, + ca*sg, ca*cg, -sa, 0, + cb*sa*sg - cg*sb, cb*cg*sa + sb*sg, ca*cb, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(order == order_yzx) { + return mat( + cb*cg, sa*sb - ca*cb*sg, ca*sb + cb*sa*sg, 0, + sg, ca*cg, -cg*sa, 0, + -cg*sb, ca*sb*sg + cb*sa, ca*cb - sa*sb*sg, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(order == order_zxy) { + return mat( + cb*cg - sa*sb*sg, -ca*sg, cb*sa*sg + cg*sb, 0, + cb*sg + cg*sa*sb, ca*cg, sb*sg - cb*cg*sa, 0, + -ca*sb, sa, ca*cb, 0, + 0, 0, 0, 1 + ); + } + else if constexpr(order == order_zyx) { + return mat( + cb*cg, cg*sa*sb - ca*sg, ca*cg*sb + sa*sg, 0, + cb*sg, ca*cg + sa*sb*sg, ca*sb*sg - cg*sa, 0, + -sb, cb*sa, ca*cb, 0, + 0, 0, 0, 1 + ); + } + + return mat(); } } diff --git a/include/fennec/math/geometric.h b/include/fennec/math/geometric.h index ade6d21..08eb0a5 100644 --- a/include/fennec/math/geometric.h +++ b/include/fennec/math/geometric.h @@ -227,7 +227,7 @@ constexpr vector cross(const vector& x, const vect /// \param x template constexpr vector normalize(const vector& x) { - return x / fennec::length(x); + return x * fennec::inversesqrt(fennec::dot(x, x)); } diff --git a/include/fennec/math/matrix.h b/include/fennec/math/matrix.h index ac1473f..c0827e9 100644 --- a/include/fennec/math/matrix.h +++ b/include/fennec/math/matrix.h @@ -250,7 +250,7 @@ struct matrix /// \details /// \param mat matrix to copy constexpr matrix(const matrix_t& mat) - : data{ mat.data } { + : data{ mat.data } { } @@ -260,7 +260,7 @@ struct matrix /// \details /// \param mat matrix to move constexpr matrix(matrix_t&& mat) noexcept - : data{ mat.data } { + : data{ mat.data } { } diff --git a/include/fennec/math/vector.h b/include/fennec/math/vector.h index e3773e0..a39f4a1 100644 --- a/include/fennec/math/vector.h +++ b/include/fennec/math/vector.h @@ -257,8 +257,7 @@ struct vector : detail::vector_base_type /// /// \details /// \param s scalar value to initialize with - explicit constexpr vector(scalar_t s) - : vector() { + explicit constexpr vector(scalar_t s) { ((data[IndicesV] = s), ...); } @@ -268,8 +267,7 @@ struct vector : detail::vector_base_type /// /// \details /// \param s scalar value to initialize with - explicit constexpr vector(int_t s) requires(not is_same_v) - : vector() { + explicit constexpr vector(int_t s) requires(not is_same_v) { if constexpr (N == 1) data[0] = ScalarT(s); else ((data[IndicesV] = ScalarT(s)), ...); } @@ -280,8 +278,7 @@ struct vector : detail::vector_base_type /// /// \details /// \param s scalar value to initialize with - explicit constexpr vector(double_t s) requires(not is_same_v) - : vector() { + explicit constexpr vector(double_t s) requires(not is_same_v) { if constexpr (N == 1) data[0] = ScalarT(s); else ((data[IndicesV] = ScalarT(s)), ...); } diff --git a/include/fennec/memory/allocator.h b/include/fennec/memory/allocator.h index a1c93e7..f12a8fa 100644 --- a/include/fennec/memory/allocator.h +++ b/include/fennec/memory/allocator.h @@ -302,14 +302,14 @@ public: /// /// \brief Default Constructor, initializes internal data to `null` and the capacity to `0` constexpr allocation() noexcept - : _data(nullptr), _capacity(0) { + : _data(nullptr), _capacity(0) { } /// /// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes /// \param n The number of elements of type `T` to allocate for constexpr allocation(size_t n) noexcept - : _data(nullptr), _capacity(0) { + : _data(nullptr), _capacity(0) { allocate(n); } @@ -319,7 +319,7 @@ public: /// \param data the buffer to copy /// \param n the number of elements constexpr allocation(const T* data, size_t n) - : allocation(n) { + : allocation(n) { fennec::memcpy(_data, data, n); } @@ -327,7 +327,9 @@ public: /// \brief Sized Constructor, initializes the allocation with a block of size `n * sizeof(T)` bytes /// \param n The number of elements of type `T` to allocate for constexpr allocation(size_t n, align_t align) noexcept - : _data(nullptr), _capacity(0), _alignment(zero()) { + : _data(nullptr) + , _capacity(0) + , _alignment(zero()) { allocate(n, align); } @@ -337,7 +339,7 @@ public: /// \param data the buffer to copy /// \param n the number of elements constexpr allocation(const T* data, size_t n, align_t align) - : allocation(n, align) { + : allocation(n, align) { fennec::memcpy(_data, data, n); } @@ -347,7 +349,9 @@ public: /// /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(const alloc_t& alloc) noexcept - : _alloc(alloc), _data(nullptr), _capacity(0) { + : _alloc(alloc) + , _data(nullptr) + , _capacity(0) { } /// @@ -357,7 +361,9 @@ public: /// /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(size_t n, const alloc_t& alloc) noexcept - : _alloc(alloc), _data(nullptr), _capacity(0) { + : _alloc(alloc) + , _data(nullptr) + , _capacity(0) { allocate(n); } @@ -370,7 +376,7 @@ public: /// /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(const T* data, size_t n, const alloc_t& alloc) - : allocation(n, alloc) { + : allocation(n, alloc) { fennec::memcpy(_data, data, n); } @@ -381,7 +387,10 @@ public: /// /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(size_t n, align_t align, const alloc_t& alloc) noexcept - : _alloc(alloc), _data(nullptr), _capacity(0), _alignment(zero()) { + : _alloc(alloc) + , _data(nullptr) + , _capacity(0) + , _alignment(zero()) { allocate(n, align); } @@ -394,7 +403,7 @@ public: /// /// \details This constructor should be used when the type `AllocT` needs internal data. constexpr allocation(const T* data, size_t n, align_t align, const alloc_t& alloc) - : allocation(n, align, alloc) { + : allocation(n, align, alloc) { fennec::memcpy(_data, data, n); } @@ -402,7 +411,9 @@ public: /// \brief Copy Constructor, creates an allocation of equal size and performs a byte-wise copy /// \param alloc The allocation to copy constexpr allocation(const allocation& alloc) noexcept - : _alloc(alloc._alloc), _data(_alloc.allocate(alloc._capacity)), _capacity(alloc._capacity) { + : _alloc(alloc._alloc) + , _data(_alloc.allocate(alloc._capacity)) + , _capacity(alloc._capacity) { fennec::memcpy(_data, alloc._data, alloc._capacity * sizeof(T)); } @@ -411,7 +422,9 @@ public: /// can safely destruct /// \param alloc The allocation to move constexpr allocation(allocation&& alloc) noexcept - : _alloc(alloc._alloc), _data(alloc._data), _capacity(alloc._capacity) { + : _alloc(alloc._alloc) + , _data(alloc._data) + , _capacity(alloc._capacity) { alloc._data = nullptr; alloc._capacity = 0; } diff --git a/include/fennec/memory/pointers.h b/include/fennec/memory/pointers.h index dd432e2..7a69727 100644 --- a/include/fennec/memory/pointers.h +++ b/include/fennec/memory/pointers.h @@ -107,7 +107,8 @@ public: /// \param ptr The resource to own /// \param del The deleter explicit constexpr unique_ptr(pointer_t ptr, const delete_t& del) - : _delete(del), _handle(ptr) { + : _delete(del) + , _handle(ptr) { } /// @@ -115,14 +116,15 @@ public: /// \param ptr The resource to own /// \param del The deleter explicit constexpr unique_ptr(pointer_t ptr, delete_t&& del) - : _delete(del), _handle(ptr) { + : _delete(del) + , _handle(ptr) { } /// /// \brief Move Constructor, transfers ownership from `other` /// \param other The unique_ptr to take ownership from constexpr unique_ptr(unique_ptr&& other) - : _handle(other._handle) { + : _handle(other._handle) { other._handle = nullptr; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 4ccf89d..9483079 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -25,6 +25,8 @@ add_executable(fennec-test main.cpp tests/test_fproc.h tests/fproc/test_io.h printing.h + tests/math/test_ext.h + tests/math/ext/test_quaternion.h ) target_compile_definitions(fennec-test PUBLIC FENNEC_TEST_CWD="${CMAKE_SOURCE_DIR}/bin/${FENNEC_BUILD_NAME}" diff --git a/test/printing.h b/test/printing.h index b61f52a..6147f32 100644 --- a/test/printing.h +++ b/test/printing.h @@ -26,6 +26,7 @@ #include #include #include +#include namespace fennec { @@ -52,6 +53,11 @@ inline std::ostream& operator<<(std::ostream& os, const matrix +inline std::ostream& operator<<(std::ostream& os, const quaternion& q) { + return os << "< " << q.w << " + " << q.x << " i + " << q.y << " j + " << q.z << " k >"; +} + // Helper for printing strings inline std::ostream& operator<<(std::ostream& os, const cstring& str) { return os << *str; diff --git a/test/tests/math/ext/test_quaternion.h b/test/tests/math/ext/test_quaternion.h new file mode 100644 index 0000000..6be1706 --- /dev/null +++ b/test/tests/math/ext/test_quaternion.h @@ -0,0 +1,47 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#ifndef FENNEC_TEST_MATH_EXT_QUATERNION_H +#define FENNEC_TEST_MATH_EXT_QUATERNION_H + +#include + +#include "../../../test.h" + +namespace fennec +{ + +namespace test +{ + +inline void fennec_test_math_quaternion() { + + fennec_test_section("constructors"); + + fennec_test_spacer(1); + + fennec_test_run(quat(), quat(1, 0, 0, 0)); + + fennec_test_spacer(2); +} + +} + +} + +#endif // FENNEC_TEST_MATH_EXT_QUATERNION_H diff --git a/test/tests/math/test_ext.h b/test/tests/math/test_ext.h new file mode 100644 index 0000000..14c5cb6 --- /dev/null +++ b/test/tests/math/test_ext.h @@ -0,0 +1,45 @@ +// ===================================================================================================================== +// fennec, a free and open source game engine +// Copyright © 2025 Medusa Slockbower +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// ===================================================================================================================== + +#include "../../test.h" + +#include "./ext/test_quaternion.h" + +#ifndef FENNEC_TEST_MATH_EXT_H +#define FENNEC_TEST_MATH_EXT_H + +namespace fennec +{ + +namespace test +{ + +inline void fennec_test_math_ext() { + + fennec_test_subheader("quaternion tests"); + fennec_test_spacer(2); + fennec_test_math_quaternion(); + fennec_test_spacer(3); + +} + +} + +} + +#endif // FENNEC_TEST_MATH_EXT_H diff --git a/test/tests/test_math.h b/test/tests/test_math.h index c856566..273bd98 100644 --- a/test/tests/test_math.h +++ b/test/tests/test_math.h @@ -29,6 +29,8 @@ #include "math/test_relational.h" #include "math/test_trigonometric.h" +#include "math/test_ext.h" + namespace fennec { @@ -76,6 +78,11 @@ inline void fennec_test_math() fennec_test_spacer(2); fennec_test_math_trigonometric(); fennec_test_spacer(3); + + fennec_test_header("math extension tests"); + fennec_test_spacer(2); + fennec_test_math_ext(); + fennec_test_spacer(3); } }