diff --git a/SECURITY.md b/SECURITY.md index 190b1a5..f114405 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -2,4 +2,4 @@ ## Reporting a Vulnerability -Please report security issues to `orange-cpp@yandex.com` \ No newline at end of file +Please report security issues to `orange-cpp@yandex.ru` \ No newline at end of file diff --git a/include/omath/Angle.hpp b/include/omath/Angle.hpp new file mode 100644 index 0000000..8ddaa2e --- /dev/null +++ b/include/omath/Angle.hpp @@ -0,0 +1,156 @@ +// +// Created by Orange on 11/30/2024. +// + +#pragma once +#include "omath/Angles.hpp" + + +namespace omath +{ + enum class AngleFlags + { + Normalized = 0, + Clamped = 1, + }; + + template + requires std::is_arithmetic_v + class Angle + { + Type m_angle; + public: + + constexpr explicit Angle(const Type& degrees) + { + if constexpr (flags == AngleFlags::Normalized) + m_angle = angles::WrapAngle(degrees, min, max); + + else if constexpr (flags == AngleFlags::Clamped) + m_angle = std::clamp(degrees, min, max); + else + { + static_assert(false); + std::unreachable(); + } + } + + [[nodiscard]] + constexpr static Angle FromDegrees(const Type& degrees) + { + return {degrees}; + } + + [[nodiscard]] + constexpr static Angle FromRadians(const Type& degrees) + { + return {angles::RadiansToDegrees(degrees)}; + } + + [[nodiscard]] + constexpr const Type& operator*() const + { + return m_angle; + } + + [[nodiscard]] + constexpr Type& operator*() + { + return m_angle; + } + + [[nodiscard]] + constexpr const Type& Value() const + { + return **std::as_const(this); + } + + [[nodiscard]] + constexpr Type& Value() + { + return **this; + } + + [[nodiscard]] + constexpr Type AsRadians() const + { + return angles::RadiansToDegrees(m_angle); + } + + [[nodiscard]] + Type Sin() const + { + return std::sin(AsRadians()); + } + + [[nodiscard]] + Type Cos() const + { + return std::sin(AsRadians()); + } + + [[nodiscard]] + Type Tan() const + { + return std::tan(AsRadians()); + } + + [[nodiscard]] + Type Atan() const + { + return std::atan(AsRadians()); + } + + [[nodiscard]] + Type Cot() const + { + return Cos() / Sin(); + } + + [[nodiscard]] + constexpr Angle& operator+=(const Type& other) + { + if constexpr (flags == AngleFlags::Normalized) + m_angle = angles::WrapAngle(m_angle + other, min, max); + + else if constexpr (flags == AngleFlags::Clamped) + m_angle = std::clamp(m_angle + other, min, max); + else + { + static_assert(false); + std::unreachable(); + } + + return *this; + } + + [[nodiscard]] + constexpr Angle& operator-=(const Type& other) + { + return operator+=(-other); + } + + [[nodiscard]] + constexpr Angle& operator+(const Type& other) + { + if constexpr (flags == AngleFlags::Normalized) + return {angles::WrapAngle(m_angle + other, min, max)}; + + else if constexpr (flags == AngleFlags::Clamped) + return {std::clamp(m_angle + other, min, max)}; + + else + static_assert(false); + + std::unreachable(); + } + + [[nodiscard]] + constexpr Angle& operator-(const Type& other) + { + return operator+(-other); + } + + + }; +} diff --git a/include/omath/Angles.hpp b/include/omath/Angles.hpp index 0dc6d61..7b0a3d1 100644 --- a/include/omath/Angles.hpp +++ b/include/omath/Angles.hpp @@ -4,21 +4,23 @@ #pragma once #include +#include + namespace omath::angles { - template - requires std::is_floating_point_v - [[nodiscard]] constexpr float RadiansToDegrees(const type& radians) + template + requires std::is_floating_point_v + [[nodiscard]] constexpr float RadiansToDegrees(const Type& radians) { - return radians * (type(180) / std::numbers::pi_v); + return radians * (Type(180) / std::numbers::pi_v); } - template - requires std::is_floating_point_v - [[nodiscard]] constexpr float DegreesToRadians(const type& degrees) + template + requires std::is_floating_point_v + [[nodiscard]] constexpr float DegreesToRadians(const Type& degrees) { - return degrees * (std::numbers::pi_v / type(180)); + return degrees * (std::numbers::pi_v / Type(180)); } template @@ -32,14 +34,31 @@ namespace omath::angles return RadiansToDegrees(vertFov); } - template - requires std::is_floating_point_v - [[nodiscard]] type VerticalFovToHorizontal(const type& vertFov, const type& aspect) + template + requires std::is_floating_point_v + [[nodiscard]] Type VerticalFovToHorizontal(const Type& vertFov, const Type& aspect) { const auto fovRad = DegreesToRadians(vertFov); - const auto horFov = type(2) * std::atan(std::tan(fovRad / type(2)) * aspect); + const auto horFov = Type(2) * std::atan(std::tan(fovRad / Type(2)) * aspect); return RadiansToDegrees(horFov); } + + template + requires std::is_arithmetic_v + [[nodiscard]] Type WrapAngle(const Type& angle, const Type& min, const Type& max) + { + if (angle <= max && angle >= min) + return angle; + + const Type range = max - min; + + Type wrappedAngle = std::fmod(angle - min, range); + + if (wrappedAngle < 0) + wrappedAngle += range; + + return wrappedAngle + min; + } } diff --git a/include/omath/Mat.hpp b/include/omath/Mat.hpp index 1e717ab..536ca37 100644 --- a/include/omath/Mat.hpp +++ b/include/omath/Mat.hpp @@ -25,7 +25,7 @@ namespace omath }; template - requires (std::is_floating_point_v || std::is_integral_v) + requires std::is_arithmetic_v class Mat final { public: @@ -34,7 +34,7 @@ namespace omath Clear(); } - constexpr Mat(const std::initializer_list>& rows) + constexpr Mat(const std::initializer_list >& rows) { if (rows.size() != Rows) throw std::invalid_argument("Initializer list rows size does not match template parameter Rows"); @@ -59,12 +59,12 @@ namespace omath std::copy_n(rawData, Rows * Columns, m_data.begin()); } - constexpr Mat(const Mat &other) noexcept + constexpr Mat(const Mat& other) noexcept { m_data = other.m_data; } - constexpr Mat(Mat &&other) noexcept + constexpr Mat(Mat&& other) noexcept { m_data = std::move(other.m_data); } @@ -87,7 +87,7 @@ namespace omath return {Rows, Columns}; } - [[nodiscard]] constexpr const Type &At(const size_t rowIndex, const size_t columnIndex) const + [[nodiscard]] constexpr const Type& At(const size_t rowIndex, const size_t columnIndex) const { if (rowIndex >= Rows || columnIndex >= Columns) throw std::out_of_range("Index out of range"); @@ -105,9 +105,9 @@ namespace omath } } - [[nodiscard]] constexpr Type &At(const size_t rowIndex, const size_t columnIndex) + [[nodiscard]] constexpr Type& At(const size_t rowIndex, const size_t columnIndex) { - return const_cast(std::as_const(*this).At(rowIndex, columnIndex)); + return const_cast(std::as_const(*this).At(rowIndex, columnIndex)); } [[nodiscard]] @@ -126,14 +126,15 @@ namespace omath Set(0); } - constexpr void Set(const Type &value) noexcept + constexpr void Set(const Type& value) noexcept { std::ranges::fill(m_data, value); } // Operator overloading for multiplication with another Mat template - constexpr Mat operator*(const Mat &other) const + constexpr Mat operator*( + const Mat& other) const { Mat result; @@ -148,7 +149,7 @@ namespace omath return result; } - constexpr Mat &operator*=(const Type &f) noexcept + constexpr Mat& operator*=(const Type& f) noexcept { for (size_t i = 0; i < Rows; ++i) for (size_t j = 0; j < Columns; ++j) @@ -157,19 +158,20 @@ namespace omath } template - constexpr Mat operator*=(const Mat &other) + constexpr Mat operator*=( + const Mat& other) { return *this = *this * other; } - constexpr Mat operator*(const Type &f) const noexcept + constexpr Mat operator*(const Type& f) const noexcept { Mat result(*this); result *= f; return result; } - constexpr Mat &operator/=(const Type &f) noexcept + constexpr Mat& operator/=(const Type& f) noexcept { for (size_t i = 0; i < Rows; ++i) for (size_t j = 0; j < Columns; ++j) @@ -177,14 +179,14 @@ namespace omath return *this; } - constexpr Mat operator/(const Type &f) const noexcept + constexpr Mat operator/(const Type& f) const noexcept { Mat result(*this); result /= f; return result; } - constexpr Mat &operator=(const Mat &other) noexcept + constexpr Mat& operator=(const Mat& other) noexcept { if (this == &other) return *this; @@ -194,7 +196,7 @@ namespace omath return *this; } - constexpr Mat &operator=(Mat &&other) noexcept + constexpr Mat& operator=(Mat&& other) noexcept { if (this == &other) return *this; @@ -260,15 +262,15 @@ namespace omath } [[nodiscard]] - constexpr const std::array& RawArray() const + constexpr const std::array& RawArray() const { return m_data; } [[nodiscard]] - constexpr std::array& RawArray() + constexpr std::array& RawArray() { - return const_cast>(std::as_const(*this).RawArray()); + return const_cast>(std::as_const(*this).RawArray()); } [[nodiscard]] @@ -289,20 +291,20 @@ namespace omath } [[nodiscard]] - bool operator==(const Mat & mat) const + bool operator==(const Mat& mat) const { return m_data == mat.m_data; } [[nodiscard]] - bool operator!=(const Mat & mat) const + bool operator!=(const Mat& mat) const { return !operator==(mat); } // Static methods that return fixed-size matrices [[nodiscard]] - constexpr static Mat<4, 4> ToScreenMat(const Type &screenWidth, const Type &screenHeight) noexcept + constexpr static Mat<4, 4> ToScreenMat(const Type& screenWidth, const Type& screenHeight) noexcept { return { @@ -314,7 +316,7 @@ namespace omath } [[nodiscard]] - constexpr static Mat<4, 4> TranslationMat(const Vector3 &diff) noexcept + constexpr static Mat<4, 4> TranslationMat(const Vector3& diff) noexcept { return { @@ -326,24 +328,24 @@ namespace omath } [[nodiscard]] - constexpr static Mat<4, 4> OrientationMat(const Vector3 &forward, const Vector3 &right, - const Vector3 &up) noexcept + constexpr static Mat<4, 4> OrientationMat(const Vector3& forward, const Vector3& right, + const Vector3& up) noexcept { return { {right.x, up.x, forward.x, 0}, {right.y, up.y, forward.y, 0}, {right.z, up.z, forward.z, 0}, - {0, 0, 0, 1}, + {0, 0, 0, 1}, }; } [[nodiscard]] - constexpr static Mat<4, 4> ProjectionMat(const Type &fieldOfView, const Type &aspectRatio, - const Type &near, const Type &far, const Type &lensZoom) noexcept + constexpr static Mat<4, 4> ProjectionMat(const Type& fieldOfView, const Type& aspectRatio, + const Type& near, const Type& far, const Type& lensZoom) noexcept { - const Type &fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2); - const Type &frustumHeight = far - near; + const Type& fovHalfTan = std::tan(angles::DegreesToRadians(fieldOfView) / 2); + const Type& frustumHeight = far - near; return { @@ -355,13 +357,13 @@ namespace omath } [[nodiscard]] - constexpr static Mat<4, 1> MatRowFromVector(const Vector3 &vector) noexcept + constexpr static Mat<4, 1> MatRowFromVector(const Vector3& vector) noexcept { return {{vector.x, vector.y, vector.z, 1}}; } [[nodiscard]] - constexpr static Mat<1, 4> MatColumnFromVector(const Vector3 &vector) noexcept + constexpr static Mat<1, 4> MatColumnFromVector(const Vector3& vector) noexcept { return { diff --git a/include/omath/Triangle3d.hpp b/include/omath/Triangle3d.hpp index f755ebf..370aa95 100644 --- a/include/omath/Triangle3d.hpp +++ b/include/omath/Triangle3d.hpp @@ -10,6 +10,7 @@ namespace omath { public: Triangle3d(const Vector3& vertex1, const Vector3& vertex2, const Vector3& vertex3); + Vector3 m_vertex1; Vector3 m_vertex2; Vector3 m_vertex3; @@ -29,4 +30,4 @@ namespace omath [[nodiscard]] Vector3 SideBVector() const; }; -} \ No newline at end of file +} diff --git a/include/omath/ViewAngles.hpp b/include/omath/ViewAngles.hpp new file mode 100644 index 0000000..cf36ca2 --- /dev/null +++ b/include/omath/ViewAngles.hpp @@ -0,0 +1,35 @@ +// +// Created by Orange on 11/30/2024. +// +#pragma once +#include +#include <__algorithm/clamp.h> + +#include "omath/Angles.hpp" + + +namespace omath +{ + template + requires std::is_arithmetic_v + class ViewAngles + { + Type pitch; + Type yaw; + Type roll; + + constexpr void SetPitch(const Type& newPitch) + { + pitch = std::clamp(newPitch, min, max); + } + void SetYaw(const Type& newYaw) + { + yaw = std::clamp(newYaw, min, max); + } + void SetRoll(const Type& newRoll) + { + roll = angles::WrapAngle(newRoll, min, max); + } + + }; +} diff --git a/include/omath/engines/source.hpp b/include/omath/engines/source.hpp index 32c248a..61eb1b6 100644 --- a/include/omath/engines/source.hpp +++ b/include/omath/engines/source.hpp @@ -12,6 +12,7 @@ namespace omath::source constexpr Vector3 kAbsForward = {1, 0, 0}; + template requires std::is_floating_point_v || std::is_integral_v [[nodiscard]] Mat<4, 4, Type, MatStoreType::COLUMN_MAJOR> PerspectiveProjectionMatrix( const float fieldOfView, const Type &aspectRatio, const Type &near, const Type &far) @@ -26,4 +27,6 @@ namespace omath::source {0, 0, 1, 0}, }; } + + } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 109cdc7..dbde66f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(unit-tests general/UnitTestVector4.cpp general/UnitTestLineTrace.cpp general/UnitTestAngles.cpp + general/UnitTestViewAngles.cpp engines/UnitTestOpenGL.cpp engines/UnitTestUnityEngine.cpp diff --git a/tests/general/UnitTestAngles.cpp b/tests/general/UnitTestAngles.cpp index 324ce4d..37b00ff 100644 --- a/tests/general/UnitTestAngles.cpp +++ b/tests/general/UnitTestAngles.cpp @@ -3,7 +3,7 @@ // #include #include - +#include TEST(UnitTestAngles, RadiansToDeg) { @@ -35,4 +35,16 @@ TEST(UnitTestAngles, VerticalToHorizontal) const auto horizontalFov = omath::angles::VerticalFovToHorizontal(vFov, aspectRation); EXPECT_NEAR(horizontalFov, 89.99f, 0.01f); +} +TEST(UnitTestAngles, WrapAngle) +{ + const float wrapped = omath::angles::WrapAngle(361.f, 0.f, 360.f); + + EXPECT_NEAR(wrapped, 1.f, 0.01f); +} +TEST(UnitTestAngles, WrapAngleNegativeRange) +{ + const float wrapped = omath::angles::WrapAngle(-90.f, 0.f, 360.f); + + EXPECT_NEAR(wrapped, 270.f, 0.01f); } \ No newline at end of file diff --git a/tests/general/UnitTestViewAngles.cpp b/tests/general/UnitTestViewAngles.cpp new file mode 100644 index 0000000..97c90e3 --- /dev/null +++ b/tests/general/UnitTestViewAngles.cpp @@ -0,0 +1,4 @@ +// +// Created by Orange on 11/30/2024. +// +#include \ No newline at end of file