From 19e8792a7557ba062e7da1dc22c0499a73ad8331 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 1 Sep 2025 01:25:06 +0300 Subject: [PATCH 1/8] Enables module support and modernizes CMake config This commit introduces support for C++20 modules by: - Adding module files (*.ixx) to the build process. - Updating the CMake configuration to handle modules correctly. - Moves Vector2/3/4 to module files - Adds a matrix module with common matrix operations This also includes modernizing the CMake configuration: - Bump cmake version to 3.31 to support modules - Adding install directories to be GNU compliant - Add install rules for modules - Remove vector3 include from vector2 header. --- CMakeLists.txt | 14 +- modules/linear_algebra/omath.mat.ixx | 525 +++++++++++++++++++++++ modules/linear_algebra/omath.vector2.ixx | 252 +++++++++++ modules/linear_algebra/omath.vector3.ixx | 312 ++++++++++++++ modules/linear_algebra/omath.vector4.ixx | 222 ++++++++++ modules/trigonometry/angle.ixx | 208 +++++++++ tests/general/unit_test_vector2.cpp | 3 +- tests/general/unit_test_vector3.cpp | 3 +- tests/general/unit_test_vector4.cpp | 2 +- 9 files changed, 1536 insertions(+), 5 deletions(-) create mode 100644 modules/linear_algebra/omath.mat.ixx create mode 100644 modules/linear_algebra/omath.vector2.ixx create mode 100644 modules/linear_algebra/omath.vector3.ixx create mode 100644 modules/linear_algebra/omath.vector4.ixx create mode 100644 modules/trigonometry/angle.ixx diff --git a/CMakeLists.txt b/CMakeLists.txt index f2ebbed8..d442ff41 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,9 +1,9 @@ -cmake_minimum_required(VERSION 3.26) +cmake_minimum_required(VERSION 3.31) project(omath VERSION 3.5.0 LANGUAGES CXX) include(CMakePackageConfigHelpers) - +include(GNUInstallDirs) option(OMATH_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL}) option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON) @@ -29,6 +29,7 @@ message(STATUS "[${PROJECT_NAME}]: Legacy features support ${OMATH_ENABLE_LEGACY file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") +file(GLOB_RECURSE OMATH_MODULES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/modules/*.ixx") if (OMATH_BUILD_AS_SHARED_LIBRARY) @@ -37,6 +38,12 @@ else () add_library(${PROJECT_NAME} STATIC ${OMATH_SOURCES} ${OMATH_HEADERS}) endif () +target_sources(${PROJECT_NAME} + PUBLIC + FILE_SET cxx_modules TYPE CXX_MODULES + BASE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/modules + FILES ${OMATH_MODULES} +) add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME}) target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}") @@ -132,6 +139,8 @@ install(TARGETS ${PROJECT_NAME} ARCHIVE DESTINATION lib COMPONENT ${PROJECT_NAME} # For static libraries LIBRARY DESTINATION lib COMPONENT ${PROJECT_NAME} # For shared libraries RUNTIME DESTINATION bin COMPONENT ${PROJECT_NAME} # For executables (on Windows) + FILE_SET cxx_modules DESTINATION ${CMAKE_INSTALL_DATADIR}/${PROJECT_NAME}/modules + CXX_MODULES_BMI DESTINATION ${CMAKE_INSTALL_LIBDIR}/cxx/bmi ) # Install headers as part of omath_component @@ -142,6 +151,7 @@ install(EXPORT ${PROJECT_NAME}Targets FILE ${PROJECT_NAME}Targets.cmake NAMESPACE ${PROJECT_NAME}:: DESTINATION lib/cmake/${PROJECT_NAME} COMPONENT ${PROJECT_NAME} + CXX_MODULES_DIRECTORY ${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}/cxx-modules ) diff --git a/modules/linear_algebra/omath.mat.ixx b/modules/linear_algebra/omath.mat.ixx new file mode 100644 index 00000000..25d525e7 --- /dev/null +++ b/modules/linear_algebra/omath.mat.ixx @@ -0,0 +1,525 @@ +// +// Created by Vlad on 9/1/2025. +// +module; + +#include +#include +#include +#include +#include +#include +#include +#include + +export module omath.mat; + +import omath.vector3; +import omath.angle; + +export namespace omath +{ + struct MatSize + { + size_t rows, columns; + }; + + enum class MatStoreType : uint8_t + { + ROW_MAJOR = 0, + COLUMN_MAJOR + }; + + template concept MatTemplateEqual + = (M1::rows == M2::rows) && (M1::columns == M2::columns) + && std::is_same_v && (M1::store_type == M2::store_type); + + template + requires std::is_arithmetic_v + class Mat final + { + public: + constexpr Mat() noexcept + { + clear(); + } + + [[nodiscard]] + constexpr static MatStoreType get_store_ordering() noexcept + { + return StoreType; + } + 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"); + + auto row_it = rows.begin(); + for (size_t i = 0; i < Rows; ++i, ++row_it) + { + if (row_it->size() != Columns) + throw std::invalid_argument( + "All rows must have the same number of columns as template parameter Columns"); + + auto col_it = row_it->begin(); + for (size_t j = 0; j < Columns; ++j, ++col_it) + { + at(i, j) = std::move(*col_it); + } + } + } + + constexpr explicit Mat(const Type* raw_data) + { + std::copy_n(raw_data, Rows * Columns, m_data.begin()); + } + + constexpr Mat(const Mat& other) noexcept + { + m_data = other.m_data; + } + + [[nodiscard]] + constexpr Type& operator[](const size_t row, const size_t col) + { + return at(row, col); + } + + [[nodiscard]] + constexpr Type& operator[](const size_t row, const size_t col) const + { + return at(row, col); + } + + constexpr Mat(Mat&& other) noexcept + { + m_data = std::move(other.m_data); + } + + [[nodiscard]] + static constexpr size_t row_count() noexcept + { + return Rows; + } + + [[nodiscard]] + static constexpr size_t columns_count() noexcept + { + return Columns; + } + + [[nodiscard]] + static consteval MatSize size() noexcept + { + return {Rows, Columns}; + } + + [[nodiscard]] + constexpr const Type& at(const size_t row_index, const size_t column_index) const + { +#if !defined(NDEBUG) && defined(OMATH_SUPRESS_SAFETY_CHECKS) + if (row_index >= Rows || column_index >= Columns) + throw std::out_of_range("Index out of range"); +#endif + if constexpr (StoreType == MatStoreType::ROW_MAJOR) + return m_data[row_index * Columns + column_index]; + + else if constexpr (StoreType == MatStoreType::COLUMN_MAJOR) + return m_data[row_index + column_index * Rows]; + + else + { + static_assert(false, "Invalid matrix access convention"); + std::unreachable(); + } + } + + [[nodiscard]] constexpr Type& at(const size_t row_index, const size_t column_index) + { + return const_cast(std::as_const(*this).at(row_index, column_index)); + } + + [[nodiscard]] + constexpr Type sum() const noexcept + { + return std::accumulate(m_data.begin(), m_data.end(), static_cast(0)); + } + + constexpr void clear() noexcept + { + set(static_cast(0)); + } + + constexpr void set(const Type& value) noexcept + { + std::ranges::fill(m_data, value); + } + + // Operator overloading for multiplication with another Mat + template [[nodiscard]] + constexpr Mat + operator*(const Mat& other) const + { + Mat result; + + for (size_t i = 0; i < Rows; ++i) + for (size_t j = 0; j < OtherColumns; ++j) + { + Type sum = 0; + for (size_t k = 0; k < Columns; ++k) + sum += at(i, k) * other.at(k, j); + result.at(i, j) = sum; + } + return result; + } + + constexpr Mat& operator*=(const Type& f) noexcept + { + std::ranges::for_each(m_data, [&f](auto& val) { val *= f; }); + return *this; + } + + template constexpr Mat + operator*=(const Mat& other) + { + return *this = *this * other; + } + + [[nodiscard]] + constexpr Mat operator*(const Type& value) const noexcept + { + Mat result(*this); + result *= value; + return result; + } + + constexpr Mat& operator/=(const Type& value) noexcept + { + std::ranges::for_each(m_data, [&value](auto& val) { val /= value; }); + return *this; + } + + [[nodiscard]] + constexpr Mat operator/(const Type& value) const noexcept + { + Mat result(*this); + result /= value; + return result; + } + + constexpr Mat& operator=(const Mat& other) noexcept + { + if (this != &other) + m_data = other.m_data; + + return *this; + } + + constexpr Mat& operator=(Mat&& other) noexcept + { + if (this != &other) + m_data = std::move(other.m_data); + + return *this; + } + + [[nodiscard]] + constexpr Mat transposed() const noexcept + { + Mat transposed; + for (size_t i = 0; i < Rows; ++i) + for (size_t j = 0; j < Columns; ++j) + transposed.at(j, i) = at(i, j); + + return transposed; + } + + [[nodiscard]] + constexpr Type determinant() const + { + static_assert(Rows == Columns, "Determinant is only defined for square matrices."); + + if constexpr (Rows == 1) + return at(0, 0); + + if constexpr (Rows == 2) + return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0); + + if constexpr (Rows > 2) + { + Type det = 0; + for (size_t column = 0; column < Columns; ++column) + { + const Type cofactor = at(0, column) * alg_complement(0, column); + det += cofactor; + } + return det; + } + std::unreachable(); + } + + [[nodiscard]] + constexpr Mat strip(const size_t row, const size_t column) const + { + static_assert(Rows - 1 > 0 && Columns - 1 > 0); + Mat result; + for (size_t i = 0, m = 0; i < Rows; ++i) + { + if (i == row) + continue; + for (size_t j = 0, n = 0; j < Columns; ++j) + { + if (j == column) + continue; + result.at(m, n) = at(i, j); + ++n; + } + ++m; + } + return result; + } + + [[nodiscard]] + constexpr Type minor(const size_t row, const size_t column) const + { + return strip(row, column).determinant(); + } + + [[nodiscard]] + constexpr Type alg_complement(const size_t row, const size_t column) const + { + const auto minor_value = minor(row, column); + return (row + column + 2) % 2 == 0 ? minor_value : -minor_value; + } + + [[nodiscard]] + constexpr const std::array& raw_array() const + { + return m_data; + } + + [[nodiscard]] + constexpr std::array& raw_array() + { + return m_data; + } + + [[nodiscard]] + std::string to_string() const noexcept + { + std::ostringstream oss; + oss << "[["; + + for (size_t i = 0; i < Rows; ++i) + { + if (i > 0) + oss << " ["; + + for (size_t j = 0; j < Columns; ++j) + { + oss << std::setw(9) << std::fixed << std::setprecision(3) << at(i, j); + if (j != Columns - 1) + oss << ", "; + } + oss << (i == Rows - 1 ? "]]" : "]\n"); + } + return oss.str(); + } + + [[nodiscard]] + bool operator==(const Mat& mat) const + { + return m_data == mat.m_data; + } + + [[nodiscard]] + bool operator!=(const Mat& mat) const + { + return !operator==(mat); + } + + // Static methods that return fixed-size matrices + [[nodiscard]] + constexpr static Mat<4, 4> to_screen_mat(const Type& screen_width, const Type& screen_height) noexcept + { + return { + {screen_width / 2, 0, 0, 0}, + {0, -screen_height / 2, 0, 0}, + {0, 0, 1, 0}, + {screen_width / 2, screen_height / 2, 0, 1}, + }; + } + + [[nodiscard]] + constexpr std::optional inverted() const + { + const auto det = determinant(); + + if (det == 0) + return std::nullopt; + + const auto transposed_mat = transposed(); + Mat result; + + for (std::size_t row = 0; row < Rows; row++) + for (std::size_t column = 0; column < Rows; column++) + result.at(row, column) = transposed_mat.alg_complement(row, column); + + result /= det; + + return {result}; + } + + private: + std::array m_data; + }; + + template [[nodiscard]] + constexpr Mat<1, 4, Type, St> mat_row_from_vector(const Vector3& vector) noexcept + { + return {{vector.x, vector.y, vector.z, 1}}; + } + + template [[nodiscard]] + constexpr Mat<4, 1, Type, St> mat_column_from_vector(const Vector3& vector) noexcept + { + return {{vector.x}, {vector.y}, {vector.z}, {1}}; + } + + template + [[nodiscard]] + constexpr Mat<4, 4, Type, St> mat_translation(const Vector3& diff) noexcept + { + return + { + {1, 0, 0, diff.x}, + {0, 1, 0, diff.y}, + {0, 0, 1, diff.z}, + {0, 0, 0, 1}, + }; + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_rotation_axis_x(const Angle& angle) noexcept + { + return + { + {1, 0, 0, 0}, + {0, angle.cos(), -angle.sin(), 0}, + {0, angle.sin(), angle.cos(), 0}, + {0, 0, 0, 1} + }; + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_rotation_axis_y(const Angle& angle) noexcept + { + return + { + {angle.cos(), 0, angle.sin(), 0}, + {0 , 1, 0, 0}, + {-angle.sin(), 0, angle.cos(), 0}, + {0 , 0, 0, 1} + }; + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_rotation_axis_z(const Angle& angle) noexcept + { + return + { + {angle.cos(), -angle.sin(), 0, 0}, + {angle.sin(), angle.cos(), 0, 0}, + { 0, 0, 1, 0}, + { 0, 0, 0, 1}, + }; + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_camera_view(const Vector3& forward, const Vector3& right, + const Vector3& up, const Vector3& camera_origin) noexcept + { + return Mat<4, 4, Type, St> + { + {right.x, right.y, right.z, 0}, + {up.x, up.y, up.z, 0}, + {forward.x, forward.y, forward.z, 0}, + {0, 0, 0, 1}, + } * mat_translation(-camera_origin); + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_perspective_left_handed(const float field_of_view, const float aspect_ratio, + const float near, const float far) noexcept + { + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f); + + return {{1.f / (aspect_ratio * fov_half_tan), 0.f, 0.f, 0.f}, + {0.f, 1.f / fov_half_tan, 0.f, 0.f}, + {0.f, 0.f, (far + near) / (far - near), -(2.f * near * far) / (far - near)}, + {0.f, 0.f, 1.f, 0.f}}; + } + + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_perspective_right_handed(const float field_of_view, const float aspect_ratio, + const float near, const float far) noexcept + { + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f); + + return {{1.f / (aspect_ratio * fov_half_tan), 0.f, 0.f, 0.f}, + {0.f, 1.f / fov_half_tan, 0.f, 0.f}, + {0.f, 0.f, -(far + near) / (far - near), -(2.f * near * far) / (far - near)}, + {0.f, 0.f, -1.f, 0.f}}; + } + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, + const Type bottom, const Type top, + const Type near, const Type far) noexcept + { + return + { + { static_cast(2) / (right - left), 0.f, 0.f, -(right + left) / (right - left)}, + { 0.f, static_cast(2) / (top - bottom), 0.f, -(top + bottom) / (top - bottom)}, + { 0.f, 0.f, static_cast(2) / (far - near), -(far + near) / (far - near) }, + { 0.f, 0.f, 0.f, 1.f } + }; + } + template + [[nodiscard]] + Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, + const Type bottom, const Type top, + const Type near, const Type far) noexcept + { + return + { + { static_cast(2) / (right - left), 0.f, 0.f, -(right + left) / (right - left)}, + { 0.f, static_cast(2) / (top - bottom), 0.f, -(top + bottom) / (top - bottom)}, + { 0.f, 0.f, -static_cast(2) / (far - near), -(far + near) / (far - near) }, + { 0.f, 0.f, 0.f, 1.f } + }; + } + +} // namespace omath + +export template +struct std::formatter> // NOLINT(*-dcl58-cpp) +{ + using MatType = omath::Mat; + [[nodiscard]] + static constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + [[nodiscard]] + static auto format(const MatType& mat, std::format_context& ctx) + { + return std::format_to(ctx.out(), "{}", mat.to_string()); + } +}; \ No newline at end of file diff --git a/modules/linear_algebra/omath.vector2.ixx b/modules/linear_algebra/omath.vector2.ixx new file mode 100644 index 00000000..be450c90 --- /dev/null +++ b/modules/linear_algebra/omath.vector2.ixx @@ -0,0 +1,252 @@ +// +// Created by Vlad on 9/1/2025. +// +module; + +#include +#include +#include + +export module omath.vector2; + + +#ifdef OMATH_IMGUI_INTEGRATION +#include +#endif + +export namespace omath +{ + template + requires std::is_arithmetic_v + class Vector2 + { + public: + Type x = static_cast(0); + Type y = static_cast(0); + + // Constructors + constexpr Vector2() = default; + + constexpr Vector2(const Type& x, const Type& y) noexcept: x(x), y(y) + { + } + + // Equality operators + [[nodiscard]] + constexpr bool operator==(const Vector2& other) const noexcept + { + return x == other.x && y == other.y; + } + + [[nodiscard]] + constexpr bool operator!=(const Vector2& other) const noexcept + { + return !(*this == other); + } + + // Compound assignment operators + constexpr Vector2& operator+=(const Vector2& other) noexcept + { + x += other.x; + y += other.y; + + return *this; + } + + constexpr Vector2& operator-=(const Vector2& other) noexcept + { + x -= other.x; + y -= other.y; + + return *this; + } + + constexpr Vector2& operator*=(const Vector2& other) noexcept + { + x *= other.x; + y *= other.y; + + return *this; + } + + constexpr Vector2& operator/=(const Vector2& other) noexcept + { + x /= other.x; + y /= other.y; + + return *this; + } + + constexpr Vector2& operator*=(const Type& value) noexcept + { + x *= value; + y *= value; + + return *this; + } + + constexpr Vector2& operator/=(const Type& value) noexcept + { + x /= value; + y /= value; + + return *this; + } + + constexpr Vector2& operator+=(const Type& value) noexcept + { + x += value; + y += value; + + return *this; + } + + constexpr Vector2& operator-=(const Type& value) noexcept + { + x -= value; + y -= value; + + return *this; + } + + // Basic vector operations + [[nodiscard]] Type distance_to(const Vector2& other) const noexcept + { + return std::sqrt(distance_to_sqr(other)); + } + + [[nodiscard]] constexpr Type distance_to_sqr(const Vector2& other) const noexcept + { + return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y); + } + + [[nodiscard]] constexpr Type dot(const Vector2& other) const noexcept + { + return x * other.x + y * other.y; + } + +#ifndef _MSC_VER + [[nodiscard]] constexpr Type length() const noexcept + { + return std::hypot(this->x, this->y); + } + + [[nodiscard]] constexpr Vector2 normalized() const noexcept + { + const Type len = length(); + return len > 0.f ? *this / len : *this; + } +#else + [[nodiscard]] Type length() const noexcept + { + return std::hypot(x, y); + } + + [[nodiscard]] Vector2 normalized() const noexcept + { + const Type len = length(); + return len > static_cast(0) ? *this / len : *this; + } +#endif + [[nodiscard]] constexpr Type length_sqr() const noexcept + { + return x * x + y * y; + } + + constexpr Vector2& abs() noexcept + { + // FIXME: Replace with std::abs, if it will become constexprable + x = x < static_cast(0) ? -x : x; + y = y < static_cast(0) ? -y : y; + return *this; + } + + [[nodiscard]] constexpr Vector2 operator-() const noexcept + { + return {-x, -y}; + } + + // Binary arithmetic operators + [[nodiscard]] constexpr Vector2 operator+(const Vector2& other) const noexcept + { + return {x + other.x, y + other.y}; + } + + [[nodiscard]] constexpr Vector2 operator-(const Vector2& other) const noexcept + { + return {x - other.x, y - other.y}; + } + + [[nodiscard]] constexpr Vector2 operator*(const Type& value) const noexcept + { + return {x * value, y * value}; + } + + [[nodiscard]] constexpr Vector2 operator/(const Type& value) const noexcept + { + return {x / value, y / value}; + } + + // Sum of elements + [[nodiscard]] constexpr Type sum() const noexcept + { + return x + y; + } + + [[nodiscard]] + bool operator<(const Vector2& other) const noexcept + { + return length() < other.length(); + } + [[nodiscard]] + bool operator>(const Vector2& other) const noexcept + { + return length() > other.length(); + } + + [[nodiscard]] + bool operator<=(const Vector2& other) const noexcept + { + return length() <= other.length(); + } + + [[nodiscard]] + bool operator>=(const Vector2& other) const noexcept + { + return length() >= other.length(); + } + + [[nodiscard]] + constexpr std::tuple as_tuple() const noexcept + { + return std::make_tuple(x, y); + } +#ifdef OMATH_IMGUI_INTEGRATION + [[nodiscard]] + ImVec2 to_im_vec2() const noexcept + { + return {static_cast(this->x), static_cast(this->y)}; + } + [[nodiscard]] + static Vector2 from_im_vec2(const ImVec2& other) noexcept + { + return {static_cast(other.x), static_cast(other.y)}; + } +#endif + }; +} // namespace omath + +export template +struct std::formatter> // NOLINT(*-dcl58-cpp) +{ + [[nodiscard]] + static constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + [[nodiscard]] + static auto format(const omath::Vector2& vec, std::format_context& ctx) + { + return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); + } +}; \ No newline at end of file diff --git a/modules/linear_algebra/omath.vector3.ixx b/modules/linear_algebra/omath.vector3.ixx new file mode 100644 index 00000000..fe63735c --- /dev/null +++ b/modules/linear_algebra/omath.vector3.ixx @@ -0,0 +1,312 @@ +// +// Created by Vlad on 9/1/2025. +// +module; + +#include "omath/angle.hpp" +#include +#include +#include + +export module omath.vector3; +import omath.vector2; + + +export namespace omath +{ + enum class Vector3Error + { + IMPOSSIBLE_BETWEEN_ANGLE, + }; + + template + requires std::is_arithmetic_v + class Vector3 : public Vector2 + { + public: + Type z = static_cast(0); + constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept: Vector2(x, y), z(z) + { + } + constexpr Vector3() noexcept: Vector2() {}; + + [[nodiscard]] constexpr bool operator==(const Vector3& other) const noexcept + { + return Vector2::operator==(other) && (other.z == z); + } + + [[nodiscard]] constexpr bool operator!=(const Vector3& other) const noexcept + { + return !(*this == other); + } + + constexpr Vector3& operator+=(const Vector3& other) noexcept + { + Vector2::operator+=(other); + z += other.z; + + return *this; + } + + constexpr Vector3& operator-=(const Vector3& other) noexcept + { + Vector2::operator-=(other); + z -= other.z; + + return *this; + } + + constexpr Vector3& operator*=(const Type& value) noexcept + { + Vector2::operator*=(value); + z *= value; + + return *this; + } + + constexpr Vector3& operator*=(const Vector3& other) noexcept + { + Vector2::operator*=(other); + z *= other.z; + + return *this; + } + + constexpr Vector3& operator/=(const Vector3& other) noexcept + { + Vector2::operator/=(other); + z /= other.z; + + return *this; + } + + constexpr Vector3& operator+=(const Type& value) noexcept + { + Vector2::operator+=(value); + z += value; + + return *this; + } + + constexpr Vector3& operator/=(const Type& value) noexcept + { + Vector2::operator/=(value); + z /= value; + + return *this; + } + + constexpr Vector3& operator-=(const Type& value) noexcept + { + Vector2::operator-=(value); + z -= value; + + return *this; + } + + constexpr Vector3& abs() noexcept + { + Vector2::abs(); + z = z < 0.f ? -z : z; + + return *this; + } + + [[nodiscard]] constexpr Type distance_to_sqr(const Vector3& other) const noexcept + { + return (*this - other).length_sqr(); + } + + [[nodiscard]] constexpr Type dot(const Vector3& other) const noexcept + { + return Vector2::dot(other) + z * other.z; + } + +#ifndef _MSC_VER + [[nodiscard]] constexpr Type length() const + { + return std::hypot(this->x, this->y, z); + } + + [[nodiscard]] constexpr Type length_2d() const + { + return Vector2::length(); + } + [[nodiscard]] Type distance_to(const Vector3& other) const + { + return (*this - other).length(); + } + [[nodiscard]] constexpr Vector3 normalized() const + { + const Type length_value = this->length(); + + return length_value != 0 ? *this / length_value : *this; + } +#else + [[nodiscard]] Type length() const noexcept + { + return std::hypot(this->x, this->y, z); + } + + [[nodiscard]] Vector3 normalized() const noexcept + { + const Type len = this->length(); + + return len != static_cast(0) ? *this / len : *this; + } + + [[nodiscard]] Type length_2d() const noexcept + { + return Vector2::length(); + } + + [[nodiscard]] Type distance_to(const Vector3& v_other) const noexcept + { + return (*this - v_other).length(); + } +#endif + + [[nodiscard]] constexpr Type length_sqr() const noexcept + { + return Vector2::length_sqr() + z * z; + } + + [[nodiscard]] constexpr Vector3 operator-() const noexcept + { + return {-this->x, -this->y, -z}; + } + + [[nodiscard]] constexpr Vector3 operator+(const Vector3& other) const noexcept + { + return {this->x + other.x, this->y + other.y, z + other.z}; + } + + [[nodiscard]] constexpr Vector3 operator-(const Vector3& other) const noexcept + { + return {this->x - other.x, this->y - other.y, z - other.z}; + } + + [[nodiscard]] constexpr Vector3 operator*(const Type& value) const noexcept + { + return {this->x * value, this->y * value, z * value}; + } + + [[nodiscard]] constexpr Vector3 operator*(const Vector3& other) const noexcept + { + return {this->x * other.x, this->y * other.y, z * other.z}; + } + + [[nodiscard]] constexpr Vector3 operator/(const Type& value) const noexcept + { + return {this->x / value, this->y / value, z / value}; + } + + [[nodiscard]] constexpr Vector3 operator/(const Vector3& other) const noexcept + { + return {this->x / other.x, this->y / other.y, z / other.z}; + } + + [[nodiscard]] constexpr Vector3 cross(const Vector3& other) const noexcept + { + return {this->y * other.z - z * other.y, z * other.x - this->x * other.z, + this->x * other.y - this->y * other.x}; + } + + [[nodiscard]] constexpr Type sum() const noexcept + { + return sum_2d() + z; + } + + [[nodiscard]] std::expected, Vector3Error> + angle_between(const Vector3& other) const noexcept + { + const auto bottom = length() * other.length(); + + if (bottom == static_cast(0)) + return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE); + + return Angle::from_radians(std::acos(dot(other) / bottom)); + } + + [[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept + { + if (const auto angle = angle_between(other)) + return angle->as_degrees() == static_cast(90); + + return false; + } + + [[nodiscard]] constexpr Type sum_2d() const noexcept + { + return Vector2::sum(); + } + + [[nodiscard]] constexpr std::tuple as_tuple() const noexcept + { + return std::make_tuple(this->x, this->y, z); + } + + [[nodiscard]] Vector3 view_angle_to(const Vector3& other) const noexcept + { + const auto distance = distance_to(other); + const auto delta = other - *this; + + return {angles::radians_to_degrees(std::asin(delta.z / distance)), + angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0}; + } + + [[nodiscard]] + bool operator<(const Vector3& other) const noexcept + { + return length() < other.length(); + } + + [[nodiscard]] + bool operator>(const Vector3& other) const noexcept + { + return length() > other.length(); + } + + [[nodiscard]] + bool operator<=(const Vector3& other) const noexcept + { + return length() <= other.length(); + } + + [[nodiscard]] + bool operator>=(const Vector3& other) const noexcept + { + return length() >= other.length(); + } + }; +} // namespace omath + +export template<> struct std::hash> +{ + std::size_t operator()(const omath::Vector3& vec) const noexcept + { + std::size_t hash = 0; + constexpr std::hash hasher; + + hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + hash ^= hasher(vec.z) + 0x9e3779b9 + (hash << 6) + (hash >> 2); + + return hash; + } +}; + +export template +struct std::formatter> // NOLINT(*-dcl58-cpp) +{ + [[nodiscard]] + static constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + [[nodiscard]] + static auto format(const omath::Vector3& vec, std::format_context& ctx) + { + return std::format_to(ctx.out(), "[{}, {}, {}]", vec.x, vec.y, vec.z); + } +}; \ No newline at end of file diff --git a/modules/linear_algebra/omath.vector4.ixx b/modules/linear_algebra/omath.vector4.ixx new file mode 100644 index 00000000..921210f5 --- /dev/null +++ b/modules/linear_algebra/omath.vector4.ixx @@ -0,0 +1,222 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include +#include + + +export module omath.vector4; + +import omath.vector3; + +export namespace omath +{ + template + requires std::is_arithmetic_v + class Vector4 : public Vector3 + { + public: + Type w; + + constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3(x, y, z), w(w) + { + } + constexpr Vector4() noexcept: Vector3(), w(static_cast(0)) {}; + + [[nodiscard]] + constexpr bool operator==(const Vector4& other) const noexcept + { + return Vector3::operator==(other) && w == other.w; + } + + [[nodiscard]] + constexpr bool operator!=(const Vector4& other) const noexcept + { + return !(*this == other); + } + + constexpr Vector4& operator+=(const Vector4& other) noexcept + { + Vector3::operator+=(other); + w += other.w; + + return *this; + } + + constexpr Vector4& operator-=(const Vector4& other) noexcept + { + Vector3::operator-=(other); + w -= other.w; + + return *this; + } + + constexpr Vector4& operator*=(const Type& value) noexcept + { + Vector3::operator*=(value); + w *= value; + + return *this; + } + + constexpr Vector4& operator*=(const Vector4& other) noexcept + { + Vector3::operator*=(other); + w *= other.w; + + return *this; + } + + constexpr Vector4& operator/=(const Type& value) noexcept + { + Vector3::operator/=(value); + w /= value; + + return *this; + } + + constexpr Vector4& operator/=(const Vector4& other) noexcept + { + Vector3::operator/=(other); + w /= other.w; + return *this; + } + + [[nodiscard]] constexpr Type length_sqr() const noexcept + { + return Vector3::length_sqr() + w * w; + } + + [[nodiscard]] constexpr Type dot(const Vector4& other) const noexcept + { + return Vector3::dot(other) + w * other.w; + } + + [[nodiscard]] Type length() const noexcept + { + return std::sqrt(length_sqr()); + } + + constexpr Vector4& abs() noexcept + { + Vector3::abs(); + w = w < 0.f ? -w : w; + + return *this; + } + constexpr Vector4& clamp(const Type& min, const Type& max) noexcept + { + this->x = std::clamp(this->x, min, max); + this->y = std::clamp(this->y, min, max); + this->z = std::clamp(this->z, min, max); + + return *this; + } + + [[nodiscard]] + constexpr Vector4 operator-() const noexcept + { + return {-this->x, -this->y, -this->z, -w}; + } + + [[nodiscard]] + constexpr Vector4 operator+(const Vector4& other) const noexcept + { + return {this->x + other.x, this->y + other.y, this->z + other.z, w + other.w}; + } + + [[nodiscard]] + constexpr Vector4 operator-(const Vector4& other) const noexcept + { + return {this->x - other.x, this->y - other.y, this->z - other.z, w - other.w}; + } + + [[nodiscard]] + constexpr Vector4 operator*(const Type& value) const noexcept + { + return {this->x * value, this->y * value, this->z * value, w * value}; + } + + [[nodiscard]] + constexpr Vector4 operator*(const Vector4& other) const noexcept + { + return {this->x * other.x, this->y * other.y, this->z * other.z, w * other.w}; + } + + [[nodiscard]] + constexpr Vector4 operator/(const Type& value) const noexcept + { + return {this->x / value, this->y / value, this->z / value, w / value}; + } + + [[nodiscard]] + constexpr Vector4 operator/(const Vector4& other) const noexcept + { + return {this->x / other.x, this->y / other.y, this->z / other.z, w / other.w}; + } + + [[nodiscard]] + constexpr Type sum() const noexcept + { + return Vector3::sum() + w; + } + + [[nodiscard]] + bool operator<(const Vector4& other) const noexcept + { + return length() < other.length(); + } + + [[nodiscard]] + bool operator>(const Vector4& other) const noexcept + { + return length() > other.length(); + } + + [[nodiscard]] + bool operator<=(const Vector4& other) const noexcept + { + return length() <= other.length(); + } + + [[nodiscard]] + bool operator>=(const Vector4& other) const noexcept + { + return length() >= other.length(); + } + +#ifdef OMATH_IMGUI_INTEGRATION + [[nodiscard]] + ImVec4 to_im_vec4() const noexcept + { + return { + static_cast(this->x), + static_cast(this->y), + static_cast(this->z), + static_cast(w), + }; + } + [[nodiscard]] + static Vector4 from_im_vec4(const ImVec4& other) noexcept + { + return {static_cast(other.x), static_cast(other.y), static_cast(other.z)}; + } +#endif +}; +} // namespace omath + +export template +struct std::formatter> // NOLINT(*-dcl58-cpp) +{ + [[nodiscard]] + static constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + [[nodiscard]] + static auto format(const omath::Vector4& vec, std::format_context& ctx) + { + return std::format_to(ctx.out(), "[{}, {}, {}, {}]", vec.x, vec.y, vec.z, vec.w); + } +}; \ No newline at end of file diff --git a/modules/trigonometry/angle.ixx b/modules/trigonometry/angle.ixx new file mode 100644 index 00000000..e48ec9ff --- /dev/null +++ b/modules/trigonometry/angle.ixx @@ -0,0 +1,208 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include +#include +#include +#include +#include + +export module omath.angle; + +export namespace omath +{ + template + requires std::is_floating_point_v + [[nodiscard]] constexpr Type radians_to_degrees(const Type& radians) noexcept + { + return radians * (static_cast(180) / std::numbers::pi_v); + } + + template + requires std::is_floating_point_v + [[nodiscard]] constexpr Type degrees_to_radians(const Type& degrees) noexcept + { + return degrees * (std::numbers::pi_v / static_cast(180)); + } + + template + requires std::is_floating_point_v + [[nodiscard]] Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept + { + const auto fov_rad = degrees_to_radians(horizontal_fov); + + const auto vert_fov = static_cast(2) * std::atan(std::tan(fov_rad / static_cast(2)) / aspect); + + return radians_to_degrees(vert_fov); + } + + template + requires std::is_floating_point_v + [[nodiscard]] Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept + { + const auto fov_as_radians = degrees_to_radians(vertical_fov); + + const auto horizontal_fov = + static_cast(2) * std::atan(std::tan(fov_as_radians / static_cast(2)) * aspect); + + return radians_to_degrees(horizontal_fov); + } + + template + requires std::is_arithmetic_v + [[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept + { + if (angle <= max && angle >= min) + return angle; + + const Type range = max - min; + + Type wrapped_angle = std::fmod(angle - min, range); + + if (wrapped_angle < 0) + wrapped_angle += range; + + return wrapped_angle + min; + } + + enum class AngleFlags + { + Normalized = 0, + Clamped = 1, + }; + + template + requires std::is_arithmetic_v + class Angle + { + Type m_angle; + constexpr explicit Angle(const Type& degrees) noexcept + { + if constexpr (flags == AngleFlags::Normalized) + m_angle = wrap_angle(degrees, min, max); + + else if constexpr (flags == AngleFlags::Clamped) + m_angle = std::clamp(degrees, min, max); + else + { + static_assert(false); + std::unreachable(); + } + } + + public: + [[nodiscard]] + constexpr static Angle from_degrees(const Type& degrees) noexcept + { + return Angle{degrees}; + } + constexpr Angle() noexcept: m_angle(0) + { + } + [[nodiscard]] + constexpr static Angle from_radians(const Type& degrees) noexcept + { + return Angle{radians_to_degrees(degrees)}; + } + + [[nodiscard]] + constexpr const Type& operator*() const noexcept + { + return m_angle; + } + + [[nodiscard]] + constexpr Type as_degrees() const noexcept + { + return m_angle; + } + + [[nodiscard]] + constexpr Type as_radians() const noexcept + { + return degrees_to_radians(m_angle); + } + + [[nodiscard]] + Type sin() const noexcept + { + return std::sin(as_radians()); + } + + [[nodiscard]] + Type cos() const noexcept + { + return std::cos(as_radians()); + } + + [[nodiscard]] + Type tan() const noexcept + { + return std::tan(as_radians()); + } + + [[nodiscard]] + Type atan() const noexcept + { + return std::atan(as_radians()); + } + + [[nodiscard]] + Type cot() const noexcept + { + return cos() / sin(); + } + + constexpr Angle& operator+=(const Angle& other) noexcept + { + if constexpr (flags == AngleFlags::Normalized) + m_angle = wrap_angle(m_angle + other.m_angle, min, max); + + else if constexpr (flags == AngleFlags::Clamped) + m_angle = std::clamp(m_angle + other.m_angle, min, max); + else + { + static_assert(false); + std::unreachable(); + } + + return *this; + } + + [[nodiscard]] + constexpr std::partial_ordering operator<=>(const Angle& other) const noexcept = default; + + constexpr Angle& operator-=(const Angle& other) noexcept + { + return operator+=(-other); + } + + [[nodiscard]] + constexpr Angle& operator+(const Angle& other) noexcept + { + if constexpr (flags == AngleFlags::Normalized) + return {wrap_angle(m_angle + other.m_angle, min, max)}; + + else if constexpr (flags == AngleFlags::Clamped) + return {std::clamp(m_angle + other.m_angle, min, max)}; + + else + static_assert(false); + + std::unreachable(); + } + + [[nodiscard]] + constexpr Angle& operator-(const Angle& other) noexcept + { + return operator+(-other); + } + + [[nodiscard]] + constexpr Angle operator-() const noexcept + { + return Angle{-m_angle}; + } + }; +} // namespace omath \ No newline at end of file diff --git a/tests/general/unit_test_vector2.cpp b/tests/general/unit_test_vector2.cpp index a8f641d7..510cddda 100644 --- a/tests/general/unit_test_vector2.cpp +++ b/tests/general/unit_test_vector2.cpp @@ -2,7 +2,8 @@ // Created by Vlad on 02.09.2024. // -#include +import omath.vector2; + #include // For FLT_MAX and FLT_MIN #include // For std::isinf and std::isnan #include diff --git a/tests/general/unit_test_vector3.cpp b/tests/general/unit_test_vector3.cpp index f5cc9fb3..80d1d36d 100644 --- a/tests/general/unit_test_vector3.cpp +++ b/tests/general/unit_test_vector3.cpp @@ -2,12 +2,13 @@ // Created by Vlad on 01.09.2024. // -#include #include // For FLT_MAX, FLT_MIN #include #include #include // For std::numeric_limits +import omath.vector3; + using namespace omath; class UnitTestVector3 : public ::testing::Test diff --git a/tests/general/unit_test_vector4.cpp b/tests/general/unit_test_vector4.cpp index d274f7a5..2a1d3fbf 100644 --- a/tests/general/unit_test_vector4.cpp +++ b/tests/general/unit_test_vector4.cpp @@ -5,10 +5,10 @@ // Vector4Test.cpp // -#include #include #include // For std::numeric_limits +import omath.vector4; using namespace omath; class UnitTestVector4 : public ::testing::Test From 1026c4b3337acc996fc8cc60e507e8cb20461922 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 1 Sep 2025 02:14:56 +0300 Subject: [PATCH 2/8] Refactors to use C++20 modules Migrates the project to use C++20 modules for improved build times and code organization. This change involves: - Converting header files to module interface units (.ixx). - Updating CMakeLists.txt to disable unity builds. - Removing redundant includes. - Modifying namespaces and import statements to align with module structure. --- CMakeLists.txt | 2 +- include/omath/3d_primitives/box.hpp | 16 -- include/omath/3d_primitives/plane.hpp | 16 -- include/omath/collision/line_tracer.hpp | 37 ---- .../3d_primites/omath.box.ixx | 19 +- modules/3d_primites/omath.plane.ixx | 23 ++ modules/3d_primites/omath.triangle.ixx | 88 ++++++++ modules/collision/omath.line_tracer.ixx | 90 ++++++++ .../iw_engine/iw_engine.formulas.ixx | 5 + .../iw_engine/omath.iw_engine.camera.ixx | 5 + .../iw_engine/omath.iw_engine.constants.ixx | 26 +++ .../trait/omath.iw_engine.camera_trait.ixx | 5 + modules/linear_algebra/omath.vector3.ixx | 12 +- modules/linear_algebra/omath.vector4.ixx | 3 + modules/omath.ixx | 5 + modules/projection/omath.camera.ixx | 206 ++++++++++++++++++ modules/projection/omath.proj_error_codes.ixx | 16 ++ .../{angle.ixx => omath.angle.ixx} | 0 modules/trigonometry/omath.view_angles.ixx | 16 ++ modules/utility/omath.color.ixx | 193 ++++++++++++++++ source/3d_primitives/plane.cpp | 19 -- source/collision/line_tracer.cpp | 63 ------ tests/general/unit_test_box_primitive.cpp | 4 +- tests/general/unit_test_color.cpp | 3 +- tests/general/unit_test_line_trace.cpp | 5 +- tests/general/unit_test_triangle.cpp | 3 +- 26 files changed, 709 insertions(+), 171 deletions(-) delete mode 100644 include/omath/3d_primitives/box.hpp delete mode 100644 include/omath/3d_primitives/plane.hpp delete mode 100644 include/omath/collision/line_tracer.hpp rename source/3d_primitives/box.cpp => modules/3d_primites/omath.box.ixx (76%) create mode 100644 modules/3d_primites/omath.plane.ixx create mode 100644 modules/3d_primites/omath.triangle.ixx create mode 100644 modules/collision/omath.line_tracer.ixx create mode 100644 modules/game_engines/iw_engine/iw_engine.formulas.ixx create mode 100644 modules/game_engines/iw_engine/omath.iw_engine.camera.ixx create mode 100644 modules/game_engines/iw_engine/omath.iw_engine.constants.ixx create mode 100644 modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx create mode 100644 modules/omath.ixx create mode 100644 modules/projection/omath.camera.ixx create mode 100644 modules/projection/omath.proj_error_codes.ixx rename modules/trigonometry/{angle.ixx => omath.angle.ixx} (100%) create mode 100644 modules/trigonometry/omath.view_angles.ixx create mode 100644 modules/utility/omath.color.ixx delete mode 100644 source/3d_primitives/plane.cpp delete mode 100644 source/collision/line_tracer.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d442ff41..33608c54 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,7 @@ option(OMATH_IMGUI_INTEGRATION "Omath will define method to convert omath types option(OMATH_BUILD_EXAMPLES "Build example projects with you can learn & play" OFF) option(OMATH_STATIC_MSVC_RUNTIME_LIBRARY "Force Omath to link static runtime" OFF) option(OMATH_SUPRESS_SAFETY_CHECKS "Supress some safety checks in release build to improve general performance" ON) -option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" ON) +option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" OFF) option(OMATH_ENABLE_LEGACY "Will enable legacy classes that MUST be used ONLY for backward compatibility" OFF) message(STATUS "[${PROJECT_NAME}]: Building on ${CMAKE_HOST_SYSTEM_NAME}") diff --git a/include/omath/3d_primitives/box.hpp b/include/omath/3d_primitives/box.hpp deleted file mode 100644 index 3abb9a44..00000000 --- a/include/omath/3d_primitives/box.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by Vlad on 4/18/2025. -// - -#pragma once -#include "omath/linear_algebra/vector3.hpp" -#include "omath/triangle.hpp" -#include - -namespace omath::primitives -{ - [[nodiscard]] - std::array>, 12> create_box(const Vector3& top, const Vector3& bottom, - const Vector3& dir_forward, const Vector3& dir_right, - float ratio = 4.f) noexcept; -} diff --git a/include/omath/3d_primitives/plane.hpp b/include/omath/3d_primitives/plane.hpp deleted file mode 100644 index 39c0f2c4..00000000 --- a/include/omath/3d_primitives/plane.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// Created by Vlad on 8/28/2025. -// - -#pragma once -#include "omath/linear_algebra/vector3.hpp" -#include "omath/triangle.hpp" -#include - -namespace omath::primitives -{ - [[nodiscard]] - std::array>, 2> create_plane(const Vector3& vertex_a, - const Vector3& vertex_b, - const Vector3& direction, float size) noexcept; -} diff --git a/include/omath/collision/line_tracer.hpp b/include/omath/collision/line_tracer.hpp deleted file mode 100644 index f2c16275..00000000 --- a/include/omath/collision/line_tracer.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Orange on 11/13/2024. -// -#pragma once - -#include "omath/linear_algebra/vector3.hpp" -#include "omath/triangle.hpp" - -namespace omath::collision -{ - class Ray - { - public: - Vector3 start; - Vector3 end; - bool infinite_length = false; - - [[nodiscard]] - Vector3 direction_vector() const noexcept; - - [[nodiscard]] - Vector3 direction_vector_normalized() const noexcept; - }; - class LineTracer - { - public: - LineTracer() = delete; - - [[nodiscard]] - static bool can_trace_line(const Ray& ray, const Triangle>& triangle) noexcept; - - // Realization of Möller–Trumbore intersection algorithm - // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm - [[nodiscard]] - static Vector3 get_ray_hit_point(const Ray& ray, const Triangle>& triangle) noexcept; - }; -} // namespace omath::collision diff --git a/source/3d_primitives/box.cpp b/modules/3d_primites/omath.box.ixx similarity index 76% rename from source/3d_primitives/box.cpp rename to modules/3d_primites/omath.box.ixx index 9c251b98..5b840698 100644 --- a/source/3d_primitives/box.cpp +++ b/modules/3d_primites/omath.box.ixx @@ -1,13 +1,18 @@ // -// Created by Vlad on 4/18/2025. +// Created by Vlad on 9/1/2025. // -#include "omath/3d_primitives/box.hpp" +module; +#include -namespace omath::primitives +export module omath.box; +export import omath.triangle; + +export namespace omath::primitives { - std::array>, 12> create_box(const Vector3& top, const Vector3& bottom, - const Vector3& dir_forward, - const Vector3& dir_right, const float ratio) noexcept + [[nodiscard]] + std::array, 12> create_box(const Vector3& top, const Vector3& bottom, + const Vector3& dir_forward, const Vector3& dir_right, + const float ratio = 4.f) noexcept { const auto height = top.distance_to(bottom); const auto side_size = height / ratio; @@ -51,4 +56,4 @@ namespace omath::primitives return poly; } -} // namespace omath::primitives +} // namespace omath::primitives \ No newline at end of file diff --git a/modules/3d_primites/omath.plane.ixx b/modules/3d_primites/omath.plane.ixx new file mode 100644 index 00000000..ac9d6ef1 --- /dev/null +++ b/modules/3d_primites/omath.plane.ixx @@ -0,0 +1,23 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include + +export module omath.plane; +export import omath.triangle; + +export namespace omath::primitives +{ + [[nodiscard]] + std::array, 2> create_plane(const Vector3& vertex_a, const Vector3& vertex_b, + const Vector3& direction, const float size) noexcept + { + const auto second_vertex_a = vertex_a + direction * size; + return std::array + { + Triangle{second_vertex_a, vertex_a, vertex_b}, + Triangle{second_vertex_a, vertex_b + direction * size, vertex_b} + }; + } +} diff --git a/modules/3d_primites/omath.triangle.ixx b/modules/3d_primites/omath.triangle.ixx new file mode 100644 index 00000000..ff1a6cd3 --- /dev/null +++ b/modules/3d_primites/omath.triangle.ixx @@ -0,0 +1,88 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include + +export module omath.triangle; +export import omath.vector3; + +export namespace omath +{ + /* + v1 + |\ + | \ + a | \ hypot + | \ + v2 ----- v3 + b + */ + + template> + class Triangle final + { + public: + constexpr Triangle() = default; + constexpr Triangle(const Vector& vertex1, const Vector& vertex2, const Vector& vertex3) + : m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3) + { + } + + Vector m_vertex1; + Vector m_vertex2; + Vector m_vertex3; + + [[nodiscard]] + constexpr Vector calculate_normal() const + { + const auto b = side_b_vector(); + const auto a = side_a_vector(); + + return b.cross(a).normalized(); + } + + [[nodiscard]] + float side_a_length() const + { + return m_vertex1.distance_to(m_vertex2); + } + + [[nodiscard]] + float side_b_length() const + { + return m_vertex3.distance_to(m_vertex2); + } + + [[nodiscard]] + constexpr Vector side_a_vector() const + { + return m_vertex1 - m_vertex2; + } + + [[nodiscard]] + constexpr float hypot() const + { + return m_vertex1.distance_to(m_vertex3); + } + [[nodiscard]] + constexpr bool is_rectangular() const + { + const auto side_a = side_a_length(); + const auto side_b = side_b_length(); + const auto hypot_value = hypot(); + + return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f; + } + [[nodiscard]] + constexpr Vector side_b_vector() const + { + return m_vertex3 - m_vertex2; + } + [[nodiscard]] + constexpr Vector mid_point() const + { + return (m_vertex1 + m_vertex2 + m_vertex3) / 3; + } + }; +} // namespace omath diff --git a/modules/collision/omath.line_tracer.ixx b/modules/collision/omath.line_tracer.ixx new file mode 100644 index 00000000..48d853a4 --- /dev/null +++ b/modules/collision/omath.line_tracer.ixx @@ -0,0 +1,90 @@ +// +// Created by Vlad on 9/1/2025. +// +module; + +#include +#include + +export module omath.line_tracer; +export import omath.triangle; + + +export namespace omath::collision +{ + class Ray + { + public: + Vector3 start; + Vector3 end; + bool infinite_length = false; + + [[nodiscard]] + Vector3 direction_vector() const noexcept + { + return end - start; + } + + [[nodiscard]] + Vector3 direction_vector_normalized() const noexcept + { + return direction_vector().normalized(); + } + }; + class LineTracer + { + public: + LineTracer() = delete; + + [[nodiscard]] + static bool can_trace_line(const Ray& ray, const Triangle<>& triangle) noexcept + { + return get_ray_hit_point(ray, triangle) == ray.end; + } + + // Realization of Möller–Trumbore intersection algorithm + // https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm + [[nodiscard]] + static Vector3 get_ray_hit_point(const Ray& ray, const Triangle<>& triangle) noexcept + { + constexpr float k_epsilon = std::numeric_limits::epsilon(); + + const auto side_a = triangle.side_a_vector(); + const auto side_b = triangle.side_b_vector(); + + const auto ray_dir = ray.direction_vector(); + + const auto p = ray_dir.cross(side_b); + const auto det = side_a.dot(p); + + if (std::abs(det) < k_epsilon) + return ray.end; + + const auto inv_det = 1.0f / det; + const auto t = ray.start - triangle.m_vertex2; + const auto u = t.dot(p) * inv_det; + + if ((u < 0 && std::abs(u) > k_epsilon) || (u > 1 && std::abs(u - 1) > k_epsilon)) + return ray.end; + + const auto q = t.cross(side_a); + // ReSharper disable once CppTooWideScopeInitStatement + const auto v = ray_dir.dot(q) * inv_det; + + if ((v < 0 && std::abs(v) > k_epsilon) || (u + v > 1 && std::abs(u + v - 1) > k_epsilon)) + return ray.end; + + const auto t_hit = side_b.dot(q) * inv_det; + + if (ray.infinite_length) + { + if (t_hit <= k_epsilon) + return ray.end; + } + else if (t_hit <= k_epsilon || t_hit > 1.0f - k_epsilon) + return ray.end; + + return ray.start + ray_dir * t_hit; + } + }; +} // namespace omath::collision diff --git a/modules/game_engines/iw_engine/iw_engine.formulas.ixx b/modules/game_engines/iw_engine/iw_engine.formulas.ixx new file mode 100644 index 00000000..bc3f6b56 --- /dev/null +++ b/modules/game_engines/iw_engine/iw_engine.formulas.ixx @@ -0,0 +1,5 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module formulas; diff --git a/modules/game_engines/iw_engine/omath.iw_engine.camera.ixx b/modules/game_engines/iw_engine/omath.iw_engine.camera.ixx new file mode 100644 index 00000000..1090437a --- /dev/null +++ b/modules/game_engines/iw_engine/omath.iw_engine.camera.ixx @@ -0,0 +1,5 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath.iw_engine.camera; diff --git a/modules/game_engines/iw_engine/omath.iw_engine.constants.ixx b/modules/game_engines/iw_engine/omath.iw_engine.constants.ixx new file mode 100644 index 00000000..09b29ab9 --- /dev/null +++ b/modules/game_engines/iw_engine/omath.iw_engine.constants.ixx @@ -0,0 +1,26 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module constants; +import omath.vector3; +import omath.mat; +import omath.angle; +import omath.view_angles; + + +export namespace omath::iw_engine +{ + constexpr Vector3 k_abs_up = {0, 0, 1}; + constexpr Vector3 k_abs_right = {0, -1, 0}; + constexpr Vector3 k_abs_forward = {1, 0, 0}; + + using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; + using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; + using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; + using PitchAngle = Angle; + using YawAngle = Angle; + using RollAngle = Angle; + + using ViewAngles = omath::ViewAngles; +} // namespace omath::iw_engine \ No newline at end of file diff --git a/modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx b/modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx new file mode 100644 index 00000000..560bbc7e --- /dev/null +++ b/modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx @@ -0,0 +1,5 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath.iw_engine.camera_trait; diff --git a/modules/linear_algebra/omath.vector3.ixx b/modules/linear_algebra/omath.vector3.ixx index fe63735c..caf9c750 100644 --- a/modules/linear_algebra/omath.vector3.ixx +++ b/modules/linear_algebra/omath.vector3.ixx @@ -3,14 +3,18 @@ // module; -#include "omath/angle.hpp" #include #include #include +#include + +#ifdef OMATH_IMGUI_INTEGRATION +#include +#endif export module omath.vector3; import omath.vector2; - +import omath.angle; export namespace omath { @@ -251,8 +255,8 @@ export namespace omath const auto distance = distance_to(other); const auto delta = other - *this; - return {angles::radians_to_degrees(std::asin(delta.z / distance)), - angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0}; + return {radians_to_degrees(std::asin(delta.z / distance)), + radians_to_degrees(std::atan2(delta.y, delta.x)), 0}; } [[nodiscard]] diff --git a/modules/linear_algebra/omath.vector4.ixx b/modules/linear_algebra/omath.vector4.ixx index 921210f5..36f80abd 100644 --- a/modules/linear_algebra/omath.vector4.ixx +++ b/modules/linear_algebra/omath.vector4.ixx @@ -5,6 +5,9 @@ module; #include #include +#ifdef OMATH_IMGUI_INTEGRATION +#include +#endif export module omath.vector4; diff --git a/modules/omath.ixx b/modules/omath.ixx new file mode 100644 index 00000000..5a01bb9e --- /dev/null +++ b/modules/omath.ixx @@ -0,0 +1,5 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath; diff --git a/modules/projection/omath.camera.ixx b/modules/projection/omath.camera.ixx new file mode 100644 index 00000000..d99a4543 --- /dev/null +++ b/modules/projection/omath.camera.ixx @@ -0,0 +1,206 @@ +// +// Created by Vlad on 9/1/2025. +// +module; + +#include +#include +#include + +export module omath.camera; + +export import omath.vector3; +export import omath.angle; +export import omath.error_codes; + +export namespace omath::projection +{ + class ViewPort final + { + public: + float m_width; + float m_height; + + [[nodiscard]] constexpr float aspect_ratio() const + { + return m_width / m_height; + } + }; + using FieldOfView = Angle; + + template + concept CameraEngineConcept = + requires(const Vector3& cam_origin, const Vector3& look_at, const ViewAnglesType& angles, + const FieldOfView& fov, const ViewPort& viewport, float znear, float zfar) { + // Presence + return types + { T::calc_look_at_angle(cam_origin, look_at) } -> std::same_as; + { T::calc_view_matrix(angles, cam_origin) } -> std::same_as; + { T::calc_projection_matrix(fov, viewport, znear, zfar) } -> std::same_as; + + // Enforce noexcept as in the trait declaration + requires noexcept(T::calc_look_at_angle(cam_origin, look_at)); + requires noexcept(T::calc_view_matrix(angles, cam_origin)); + requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar)); + }; + + template + requires CameraEngineConcept + class Camera final + { + public: + ~Camera() = default; + Camera(const Vector3& position, const ViewAnglesType& view_angles, const ViewPort& view_port, + const FieldOfView& fov, const float near, const float far) noexcept + : m_view_port(view_port), m_field_of_view(fov), m_far_plane_distance(far), m_near_plane_distance(near), + m_view_angles(view_angles), m_origin(position) + { + } + + protected: + void look_at(const Vector3& target) + { + m_view_angles = TraitClass::calc_look_at_angle(m_origin, target); + } + + [[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept + { + return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance, + m_far_plane_distance) + * TraitClass::calc_view_matrix(m_view_angles, m_origin); + } + + public: + [[nodiscard]] const Mat4X4Type& get_view_projection_matrix() const noexcept + { + if (!m_view_projection_matrix.has_value()) + m_view_projection_matrix = calc_view_projection_matrix(); + + return m_view_projection_matrix.value(); + } + + void set_field_of_view(const FieldOfView& fov) noexcept + { + m_field_of_view = fov; + m_view_projection_matrix = std::nullopt; + } + + void set_near_plane(const float near) noexcept + { + m_near_plane_distance = near; + m_view_projection_matrix = std::nullopt; + } + + void set_far_plane(const float far) noexcept + { + m_far_plane_distance = far; + m_view_projection_matrix = std::nullopt; + } + + void set_view_angles(const ViewAnglesType& view_angles) noexcept + { + m_view_angles = view_angles; + m_view_projection_matrix = std::nullopt; + } + + void set_origin(const Vector3& origin) noexcept + { + m_origin = origin; + m_view_projection_matrix = std::nullopt; + } + + void set_view_port(const ViewPort& view_port) noexcept + { + m_view_port = view_port; + m_view_projection_matrix = std::nullopt; + } + + [[nodiscard]] const FieldOfView& get_field_of_view() const noexcept + { + return m_field_of_view; + } + + [[nodiscard]] const float& get_near_plane() const noexcept + { + return m_near_plane_distance; + } + + [[nodiscard]] const float& get_far_plane() const noexcept + { + return m_far_plane_distance; + } + + [[nodiscard]] const ViewAnglesType& get_view_angles() const noexcept + { + return m_view_angles; + } + + [[nodiscard]] const Vector3& get_origin() const noexcept + { + return m_origin; + } + + [[nodiscard]] std::expected, Error> + world_to_screen(const Vector3& world_position) const noexcept + { + auto normalized_cords = world_to_view_port(world_position); + + if (!normalized_cords.has_value()) + return std::unexpected{normalized_cords.error()}; + + return ndc_to_screen_position(*normalized_cords); + } + + [[nodiscard]] std::expected, Error> + world_to_view_port(const Vector3& world_position) const noexcept + { + auto projected = get_view_projection_matrix() + * mat_column_from_vector(world_position); + + if (projected.at(3, 0) == 0.0f) + return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + + projected /= projected.at(3, 0); + + if (is_ndc_out_of_bounds(projected)) + return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + + return Vector3{projected.at(0, 0), projected.at(1, 0), projected.at(2, 0)}; + } + + protected: + ViewPort m_view_port{}; + Angle m_field_of_view; + + mutable std::optional m_view_projection_matrix; + + float m_far_plane_distance; + float m_near_plane_distance; + + ViewAnglesType m_view_angles; + Vector3 m_origin; + + private: + template + [[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept + { + return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; }); + } + + [[nodiscard]] Vector3 ndc_to_screen_position(const Vector3& ndc) const noexcept + { +/* + ^ + | y + 1 | + | + | + -1 ---------0--------- 1 --> x + | + | + -1 | + v +*/ + return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (1.f - ndc.y) / 2.f * m_view_port.m_height, ndc.z}; + } + }; +} // namespace omath::projection \ No newline at end of file diff --git a/modules/projection/omath.proj_error_codes.ixx b/modules/projection/omath.proj_error_codes.ixx new file mode 100644 index 00000000..c3e9fe70 --- /dev/null +++ b/modules/projection/omath.proj_error_codes.ixx @@ -0,0 +1,16 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include + +export module omath.error_codes; + + +export namespace omath::projection +{ + enum class Error : uint16_t + { + WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS, + }; +} \ No newline at end of file diff --git a/modules/trigonometry/angle.ixx b/modules/trigonometry/omath.angle.ixx similarity index 100% rename from modules/trigonometry/angle.ixx rename to modules/trigonometry/omath.angle.ixx diff --git a/modules/trigonometry/omath.view_angles.ixx b/modules/trigonometry/omath.view_angles.ixx new file mode 100644 index 00000000..44e8d24b --- /dev/null +++ b/modules/trigonometry/omath.view_angles.ixx @@ -0,0 +1,16 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath.view_angles; + +export namespace omath +{ + template + struct ViewAngles + { + PitchType pitch; + YawType yaw; + RollType roll; + }; +} // namespace omath diff --git a/modules/utility/omath.color.ixx b/modules/utility/omath.color.ixx new file mode 100644 index 00000000..665be612 --- /dev/null +++ b/modules/utility/omath.color.ixx @@ -0,0 +1,193 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include +#include +#include +#include + +#ifdef OMATH_IMGUI_INTEGRATION +#include +#endif + +export module omath.color; +import omath.vector4; + +export namespace omath +{ + struct Hsv + { + float hue{}; + float saturation{}; + float value{}; + }; + + class Color final : public Vector4 + { + public: + constexpr Color(const float r, const float g, const float b, const float a) noexcept: Vector4(r, g, b, a) + { + clamp(0.f, 1.f); + } + + constexpr explicit Color() noexcept = default; + [[nodiscard]] + constexpr static Color from_rgba(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) noexcept + { + return Color{Vector4(r, g, b, a) / 255.f}; + } + + [[nodiscard]] + constexpr static Color from_hsv(float hue, const float saturation, const float value) noexcept + { + float r{}, g{}, b{}; + + hue = std::clamp(hue, 0.f, 1.f); + + const int i = static_cast(hue * 6.f); + const float f = hue * 6.f - static_cast(i); + const float p = value * (1 - saturation); + const float q = value * (1 - f * saturation); + const float t = value * (1 - (1 - f) * saturation); + + switch (i % 6) + { + case 0: + r = value, g = t, b = p; + break; + case 1: + r = q, g = value, b = p; + break; + case 2: + r = p, g = value, b = t; + break; + case 3: + r = p, g = q, b = value; + break; + case 4: + r = t, g = p, b = value; + break; + case 5: + r = value, g = p, b = q; + break; + + default: + return {0.f, 0.f, 0.f, 0.f}; + } + + return {r, g, b, 1.f}; + } + + [[nodiscard]] + constexpr static Color from_hsv(const Hsv& hsv) noexcept + { + return from_hsv(hsv.hue, hsv.saturation, hsv.value); + } + + [[nodiscard]] + constexpr Hsv to_hsv() const noexcept + { + Hsv hsv_data; + + const float& red = x; + const float& green = y; + const float& blue = z; + + const float max = std::max({red, green, blue}); + const float min = std::min({red, green, blue}); + const float delta = max - min; + + if (delta == 0.f) + hsv_data.hue = 0.f; + + else if (max == red) + hsv_data.hue = 60.f * (std::fmodf(((green - blue) / delta), 6.f)); + else if (max == green) + hsv_data.hue = 60.f * (((blue - red) / delta) + 2.f); + else if (max == blue) + hsv_data.hue = 60.f * (((red - green) / delta) + 4.f); + + if (hsv_data.hue < 0.f) + hsv_data.hue += 360.f; + + hsv_data.hue /= 360.f; + hsv_data.saturation = max == 0.f ? 0.f : delta / max; + hsv_data.value = max; + + return hsv_data; + } + + constexpr explicit Color(const Vector4& vec) noexcept: Vector4(vec) + { + clamp(0.f, 1.f); + } + constexpr void set_hue(const float hue) noexcept + { + auto hsv = to_hsv(); + hsv.hue = hue; + + *this = from_hsv(hsv); + } + + constexpr void set_saturation(const float saturation) noexcept + { + auto hsv = to_hsv(); + hsv.saturation = saturation; + + *this = from_hsv(hsv); + } + + constexpr void set_value(const float value) noexcept + { + auto hsv = to_hsv(); + hsv.value = value; + + *this = from_hsv(hsv); + } + [[nodiscard]] + constexpr Color blend(const Color& other, float ratio) const noexcept + { + ratio = std::clamp(ratio, 0.f, 1.f); + return Color(*this * (1.f - ratio) + other * ratio); + } + + [[nodiscard]] static constexpr Color red() + { + return {1.f, 0.f, 0.f, 1.f}; + } + [[nodiscard]] static constexpr Color green() + { + return {0.f, 1.f, 0.f, 1.f}; + } + [[nodiscard]] static constexpr Color blue() + { + return {0.f, 0.f, 1.f, 1.f}; + } +#ifdef OMATH_IMGUI_INTEGRATION + [[nodiscard]] + ImColor to_im_color() const noexcept + { + return {to_im_vec4()}; + } +#endif + }; +} // namespace omath +export template<> +struct std::formatter // NOLINT(*-dcl58-cpp) +{ + [[nodiscard]] + static constexpr auto parse(std::format_parse_context& ctx) + { + return ctx.begin(); + } + [[nodiscard]] + static auto format(const omath::Color& col, std::format_context& ctx) + { + return std::format_to(ctx.out(), "[r:{}, g:{}, b:{}, a:{}]", + static_cast(col.x * 255.f), + static_cast(col.y * 255.f), + static_cast(col.z * 255.f), + static_cast(col.w * 255.f)); + } +}; \ No newline at end of file diff --git a/source/3d_primitives/plane.cpp b/source/3d_primitives/plane.cpp deleted file mode 100644 index c5418109..00000000 --- a/source/3d_primitives/plane.cpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by Vlad on 8/28/2025. -// -#include "omath/3d_primitives/plane.hpp" - -namespace omath::primitives -{ - std::array>, 2> create_plane(const Vector3& vertex_a, - const Vector3& vertex_b, - const Vector3& direction, const float size) noexcept - { - const auto second_vertex_a = vertex_a + direction * size; - return std::array - { - Triangle{second_vertex_a, vertex_a, vertex_b}, - Triangle{second_vertex_a, vertex_b + direction * size, vertex_b} - }; - } -} // namespace omath::primitives \ No newline at end of file diff --git a/source/collision/line_tracer.cpp b/source/collision/line_tracer.cpp deleted file mode 100644 index 311c99a2..00000000 --- a/source/collision/line_tracer.cpp +++ /dev/null @@ -1,63 +0,0 @@ -// -// Created by Orange on 11/13/2024. -// -#include "omath/collision/line_tracer.hpp" - -namespace omath::collision -{ - bool LineTracer::can_trace_line(const Ray& ray, const Triangle>& triangle) noexcept - { - return get_ray_hit_point(ray, triangle) == ray.end; - } - Vector3 Ray::direction_vector() const noexcept - { - return end - start; - } - - Vector3 Ray::direction_vector_normalized() const noexcept - { - return direction_vector().normalized(); - } - - Vector3 LineTracer::get_ray_hit_point(const Ray& ray, const Triangle>& triangle) noexcept - { - constexpr float k_epsilon = std::numeric_limits::epsilon(); - - const auto side_a = triangle.side_a_vector(); - const auto side_b = triangle.side_b_vector(); - - const auto ray_dir = ray.direction_vector(); - - const auto p = ray_dir.cross(side_b); - const auto det = side_a.dot(p); - - if (std::abs(det) < k_epsilon) - return ray.end; - - const auto inv_det = 1.0f / det; - const auto t = ray.start - triangle.m_vertex2; - const auto u = t.dot(p) * inv_det; - - if ((u < 0 && std::abs(u) > k_epsilon) || (u > 1 && std::abs(u - 1) > k_epsilon)) - return ray.end; - - const auto q = t.cross(side_a); - // ReSharper disable once CppTooWideScopeInitStatement - const auto v = ray_dir.dot(q) * inv_det; - - if ((v < 0 && std::abs(v) > k_epsilon) || (u + v > 1 && std::abs(u + v - 1) > k_epsilon)) - return ray.end; - - const auto t_hit = side_b.dot(q) * inv_det; - - if (ray.infinite_length) - { - if (t_hit <= k_epsilon) - return ray.end; - } - else if (t_hit <= k_epsilon || t_hit > 1.0f - k_epsilon) - return ray.end; - - return ray.start + ray_dir * t_hit; - } -} // namespace omath::collision diff --git a/tests/general/unit_test_box_primitive.cpp b/tests/general/unit_test_box_primitive.cpp index 215f2229..78d636b2 100644 --- a/tests/general/unit_test_box_primitive.cpp +++ b/tests/general/unit_test_box_primitive.cpp @@ -2,5 +2,7 @@ // Created by Vlad on 4/18/2025. // #include -#include +import omath.box; +import omath.plane; + diff --git a/tests/general/unit_test_color.cpp b/tests/general/unit_test_color.cpp index 0f0d332b..9f67a771 100644 --- a/tests/general/unit_test_color.cpp +++ b/tests/general/unit_test_color.cpp @@ -2,8 +2,9 @@ // Created by Vlad on 01.09.2024. // #include -#include +import omath.color; +import omath.vector4; using namespace omath; diff --git a/tests/general/unit_test_line_trace.cpp b/tests/general/unit_test_line_trace.cpp index 31cb6a79..26daa016 100644 --- a/tests/general/unit_test_line_trace.cpp +++ b/tests/general/unit_test_line_trace.cpp @@ -2,12 +2,9 @@ // Revised unit‑test suite for LineTracer (segment‑based Möller–Trumbore) // Pure ASCII: avoids non‑standard characters that MSVC rejects. // -#include "omath/linear_algebra/vector3.hpp" -#include "omath/collision/line_tracer.hpp" -#include "omath/triangle.hpp" #include "gtest/gtest.h" #include - +import omath.line_tracer; using namespace omath; using namespace omath::collision; diff --git a/tests/general/unit_test_triangle.cpp b/tests/general/unit_test_triangle.cpp index 643ecdc2..83882629 100644 --- a/tests/general/unit_test_triangle.cpp +++ b/tests/general/unit_test_triangle.cpp @@ -1,11 +1,10 @@ // // Created by Orange on 1/6/2025. // -#include "omath/triangle.hpp" -#include "omath/linear_algebra/vector3.hpp" #include // For std::sqrt, std::isinf, std::isnan #include +import omath.triangle; using namespace omath; class UnitTestTriangle : public ::testing::Test From 333bdc5a98721fe3a1804bf2c5e88acc29b9b414 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 1 Sep 2025 12:37:58 +0300 Subject: [PATCH 3/8] Removes omath namespace prefix Removes the unnecessary `omath` namespace prefix from several files to improve code readability and maintainability. The `std::formatter` specialization for Vector2 is updated to include a CharT template parameter, providing improved compatibility with different character types. --- modules/3d_primites/{omath.box.ixx => box.ixx} | 0 modules/3d_primites/{omath.plane.ixx => plane.ixx} | 0 modules/3d_primites/{omath.triangle.ixx => triangle.ixx} | 0 modules/collision/{omath.line_tracer.ixx => line_tracer.ixx} | 0 .../iw_engine/{omath.iw_engine.camera.ixx => camera.ixx} | 0 .../{omath.iw_engine.constants.ixx => constants.ixx} | 0 .../iw_engine/{iw_engine.formulas.ixx => formulas.ixx} | 0 .../{omath.iw_engine.camera_trait.ixx => camera_trait.ixx} | 0 modules/linear_algebra/{omath.mat.ixx => mat.ixx} | 0 modules/linear_algebra/{omath.vector2.ixx => vector2.ixx} | 4 ++-- modules/linear_algebra/{omath.vector3.ixx => vector3.ixx} | 0 modules/linear_algebra/{omath.vector4.ixx => vector4.ixx} | 0 modules/projection/{omath.camera.ixx => camera.ixx} | 0 .../{omath.proj_error_codes.ixx => proj_error_codes.ixx} | 0 modules/trigonometry/{omath.angle.ixx => angle.ixx} | 0 .../trigonometry/{omath.view_angles.ixx => view_angles.ixx} | 0 modules/utility/{omath.color.ixx => color.ixx} | 0 17 files changed, 2 insertions(+), 2 deletions(-) rename modules/3d_primites/{omath.box.ixx => box.ixx} (100%) rename modules/3d_primites/{omath.plane.ixx => plane.ixx} (100%) rename modules/3d_primites/{omath.triangle.ixx => triangle.ixx} (100%) rename modules/collision/{omath.line_tracer.ixx => line_tracer.ixx} (100%) rename modules/game_engines/iw_engine/{omath.iw_engine.camera.ixx => camera.ixx} (100%) rename modules/game_engines/iw_engine/{omath.iw_engine.constants.ixx => constants.ixx} (100%) rename modules/game_engines/iw_engine/{iw_engine.formulas.ixx => formulas.ixx} (100%) rename modules/game_engines/iw_engine/trait/{omath.iw_engine.camera_trait.ixx => camera_trait.ixx} (100%) rename modules/linear_algebra/{omath.mat.ixx => mat.ixx} (100%) rename modules/linear_algebra/{omath.vector2.ixx => vector2.ixx} (98%) rename modules/linear_algebra/{omath.vector3.ixx => vector3.ixx} (100%) rename modules/linear_algebra/{omath.vector4.ixx => vector4.ixx} (100%) rename modules/projection/{omath.camera.ixx => camera.ixx} (100%) rename modules/projection/{omath.proj_error_codes.ixx => proj_error_codes.ixx} (100%) rename modules/trigonometry/{omath.angle.ixx => angle.ixx} (100%) rename modules/trigonometry/{omath.view_angles.ixx => view_angles.ixx} (100%) rename modules/utility/{omath.color.ixx => color.ixx} (100%) diff --git a/modules/3d_primites/omath.box.ixx b/modules/3d_primites/box.ixx similarity index 100% rename from modules/3d_primites/omath.box.ixx rename to modules/3d_primites/box.ixx diff --git a/modules/3d_primites/omath.plane.ixx b/modules/3d_primites/plane.ixx similarity index 100% rename from modules/3d_primites/omath.plane.ixx rename to modules/3d_primites/plane.ixx diff --git a/modules/3d_primites/omath.triangle.ixx b/modules/3d_primites/triangle.ixx similarity index 100% rename from modules/3d_primites/omath.triangle.ixx rename to modules/3d_primites/triangle.ixx diff --git a/modules/collision/omath.line_tracer.ixx b/modules/collision/line_tracer.ixx similarity index 100% rename from modules/collision/omath.line_tracer.ixx rename to modules/collision/line_tracer.ixx diff --git a/modules/game_engines/iw_engine/omath.iw_engine.camera.ixx b/modules/game_engines/iw_engine/camera.ixx similarity index 100% rename from modules/game_engines/iw_engine/omath.iw_engine.camera.ixx rename to modules/game_engines/iw_engine/camera.ixx diff --git a/modules/game_engines/iw_engine/omath.iw_engine.constants.ixx b/modules/game_engines/iw_engine/constants.ixx similarity index 100% rename from modules/game_engines/iw_engine/omath.iw_engine.constants.ixx rename to modules/game_engines/iw_engine/constants.ixx diff --git a/modules/game_engines/iw_engine/iw_engine.formulas.ixx b/modules/game_engines/iw_engine/formulas.ixx similarity index 100% rename from modules/game_engines/iw_engine/iw_engine.formulas.ixx rename to modules/game_engines/iw_engine/formulas.ixx diff --git a/modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx b/modules/game_engines/iw_engine/trait/camera_trait.ixx similarity index 100% rename from modules/game_engines/iw_engine/trait/omath.iw_engine.camera_trait.ixx rename to modules/game_engines/iw_engine/trait/camera_trait.ixx diff --git a/modules/linear_algebra/omath.mat.ixx b/modules/linear_algebra/mat.ixx similarity index 100% rename from modules/linear_algebra/omath.mat.ixx rename to modules/linear_algebra/mat.ixx diff --git a/modules/linear_algebra/omath.vector2.ixx b/modules/linear_algebra/vector2.ixx similarity index 98% rename from modules/linear_algebra/omath.vector2.ixx rename to modules/linear_algebra/vector2.ixx index be450c90..7768a359 100644 --- a/modules/linear_algebra/omath.vector2.ixx +++ b/modules/linear_algebra/vector2.ixx @@ -236,8 +236,8 @@ export namespace omath }; } // namespace omath -export template -struct std::formatter> // NOLINT(*-dcl58-cpp) +export template +struct std::formatter, CharT> // NOLINT(*-dcl58-cpp) { [[nodiscard]] static constexpr auto parse(std::format_parse_context& ctx) diff --git a/modules/linear_algebra/omath.vector3.ixx b/modules/linear_algebra/vector3.ixx similarity index 100% rename from modules/linear_algebra/omath.vector3.ixx rename to modules/linear_algebra/vector3.ixx diff --git a/modules/linear_algebra/omath.vector4.ixx b/modules/linear_algebra/vector4.ixx similarity index 100% rename from modules/linear_algebra/omath.vector4.ixx rename to modules/linear_algebra/vector4.ixx diff --git a/modules/projection/omath.camera.ixx b/modules/projection/camera.ixx similarity index 100% rename from modules/projection/omath.camera.ixx rename to modules/projection/camera.ixx diff --git a/modules/projection/omath.proj_error_codes.ixx b/modules/projection/proj_error_codes.ixx similarity index 100% rename from modules/projection/omath.proj_error_codes.ixx rename to modules/projection/proj_error_codes.ixx diff --git a/modules/trigonometry/omath.angle.ixx b/modules/trigonometry/angle.ixx similarity index 100% rename from modules/trigonometry/omath.angle.ixx rename to modules/trigonometry/angle.ixx diff --git a/modules/trigonometry/omath.view_angles.ixx b/modules/trigonometry/view_angles.ixx similarity index 100% rename from modules/trigonometry/omath.view_angles.ixx rename to modules/trigonometry/view_angles.ixx diff --git a/modules/utility/omath.color.ixx b/modules/utility/color.ixx similarity index 100% rename from modules/utility/omath.color.ixx rename to modules/utility/color.ixx From c926b179a8c47e350d8a8e002deccb3616908f49 Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 1 Sep 2025 13:20:30 +0300 Subject: [PATCH 4/8] Migrates IW Engine code to modules Converts IW Engine related headers and source files to modern C++ modules. This change improves compilation times and encapsulation. --- include/omath/engines/iw_engine/camera.hpp | 13 ---- include/omath/engines/iw_engine/constants.hpp | 25 ------- include/omath/engines/iw_engine/formulas.hpp | 26 -------- .../engines/iw_engine/traits/camera_trait.hpp | 24 ------- modules/game_engines/iw_engine/camera.ixx | 7 ++ modules/game_engines/iw_engine/constants.ixx | 2 +- modules/game_engines/iw_engine/formulas.ixx | 65 ++++++++++++++++++- .../iw_engine/trait/camera_trait.ixx | 39 ++++++++++- modules/trigonometry/angle.ixx | 4 +- source/engines/iw_engine/formulas.cpp | 53 --------------- .../engines/iw_engine/traits/camera_trait.cpp | 27 -------- tests/engines/unit_test_iw_engine.cpp | 8 +-- tests/general/unit_test_vector2.cpp | 4 +- tests/general/unit_test_vector3.cpp | 1 + 14 files changed, 118 insertions(+), 180 deletions(-) delete mode 100644 include/omath/engines/iw_engine/camera.hpp delete mode 100644 include/omath/engines/iw_engine/constants.hpp delete mode 100644 include/omath/engines/iw_engine/formulas.hpp delete mode 100644 include/omath/engines/iw_engine/traits/camera_trait.hpp delete mode 100644 source/engines/iw_engine/formulas.cpp delete mode 100644 source/engines/iw_engine/traits/camera_trait.cpp diff --git a/include/omath/engines/iw_engine/camera.hpp b/include/omath/engines/iw_engine/camera.hpp deleted file mode 100644 index 2efe29b1..00000000 --- a/include/omath/engines/iw_engine/camera.hpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Vlad on 3/17/2025. -// - -#pragma once -#include "omath/engines/iw_engine/constants.hpp" -#include "omath/projection/camera.hpp" -#include "traits/camera_trait.hpp" - -namespace omath::iw_engine -{ - using Camera = projection::Camera; -} // namespace omath::iw_engine \ No newline at end of file diff --git a/include/omath/engines/iw_engine/constants.hpp b/include/omath/engines/iw_engine/constants.hpp deleted file mode 100644 index 63d1177d..00000000 --- a/include/omath/engines/iw_engine/constants.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by Vlad on 3/17/2025. -// - -#pragma once -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include - -namespace omath::iw_engine -{ - constexpr Vector3 k_abs_up = {0, 0, 1}; - constexpr Vector3 k_abs_right = {0, -1, 0}; - constexpr Vector3 k_abs_forward = {1, 0, 0}; - - using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; - using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; - using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; - using PitchAngle = Angle; - using YawAngle = Angle; - using RollAngle = Angle; - - using ViewAngles = omath::ViewAngles; -} // namespace omath::iw_engine \ No newline at end of file diff --git a/include/omath/engines/iw_engine/formulas.hpp b/include/omath/engines/iw_engine/formulas.hpp deleted file mode 100644 index 433987da..00000000 --- a/include/omath/engines/iw_engine/formulas.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Vlad on 3/17/2025. -// - -#pragma once -#include "omath/engines/iw_engine/constants.hpp" - -namespace omath::iw_engine -{ - [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - - [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; -} // namespace omath::iw_engine diff --git a/include/omath/engines/iw_engine/traits/camera_trait.hpp b/include/omath/engines/iw_engine/traits/camera_trait.hpp deleted file mode 100644 index 88c21569..00000000 --- a/include/omath/engines/iw_engine/traits/camera_trait.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Vlad on 8/10/2025. -// - -#pragma once -#include "omath/engines/iw_engine/constants.hpp" -#include "omath/projection/camera.hpp" - -namespace omath::iw_engine -{ - class CameraTrait final - { - public: - [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; - - [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept; - }; - -} // namespace omath::iw_engine \ No newline at end of file diff --git a/modules/game_engines/iw_engine/camera.ixx b/modules/game_engines/iw_engine/camera.ixx index 1090437a..4ce134b8 100644 --- a/modules/game_engines/iw_engine/camera.ixx +++ b/modules/game_engines/iw_engine/camera.ixx @@ -3,3 +3,10 @@ // export module omath.iw_engine.camera; +import omath.camera; +import omath.iw_engine.constants; +import omath.iw_engine.camera_trait; +export namespace omath::iw_engine +{ + using Camera = projection::Camera; +} \ No newline at end of file diff --git a/modules/game_engines/iw_engine/constants.ixx b/modules/game_engines/iw_engine/constants.ixx index 09b29ab9..31aa2858 100644 --- a/modules/game_engines/iw_engine/constants.ixx +++ b/modules/game_engines/iw_engine/constants.ixx @@ -2,7 +2,7 @@ // Created by Vlad on 9/1/2025. // -export module constants; +export module omath.iw_engine.constants; import omath.vector3; import omath.mat; import omath.angle; diff --git a/modules/game_engines/iw_engine/formulas.ixx b/modules/game_engines/iw_engine/formulas.ixx index bc3f6b56..382c923c 100644 --- a/modules/game_engines/iw_engine/formulas.ixx +++ b/modules/game_engines/iw_engine/formulas.ixx @@ -1,5 +1,68 @@ // // Created by Vlad on 9/1/2025. // +module; +#include -export module formulas; +export module omath.iw_engine.formulas; +import omath.vector3; +import omath.iw_engine.constants; +import omath.mat; +import omath.angle; + +export namespace omath::iw_engine +{ + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); + } + + [[nodiscard]] + Vector3 forward_vector(const ViewAngles& angles) noexcept + { + { + const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); + + return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; + } + } + + [[nodiscard]] + Vector3 right_vector(const ViewAngles& angles) noexcept + { + const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); + + return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; + } + + [[nodiscard]] + Vector3 up_vector(const ViewAngles& angles) noexcept + { + const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); + + return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; + } + + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); + } + + [[nodiscard]] + Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, + const float far) noexcept + { + // NOTE: Need magic number to fix fov calculation, since IW engine inherit Quake proj matrix calculation + constexpr auto k_multiply_factor = 0.75f; + + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; + + return { + {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, + {0, 1.f / (fov_half_tan), 0, 0}, + {0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)}, + {0, 0, 1, 0}, + }; + }; +} // namespace omath::iw_engine diff --git a/modules/game_engines/iw_engine/trait/camera_trait.ixx b/modules/game_engines/iw_engine/trait/camera_trait.ixx index 560bbc7e..d1ca3f6f 100644 --- a/modules/game_engines/iw_engine/trait/camera_trait.ixx +++ b/modules/game_engines/iw_engine/trait/camera_trait.ixx @@ -1,5 +1,42 @@ // // Created by Vlad on 9/1/2025. // - +module; +#include export module omath.iw_engine.camera_trait; + +import omath.iw_engine.constants; +import omath.vector3; +import omath.camera; +import omath.iw_engine.formulas; + +export namespace omath::iw_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept + { + const auto distance = cam_origin.distance_to(look_at); + const auto delta = cam_origin - look_at; + + return {PitchAngle::from_radians(-std::asin(delta.z / distance)), + YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; + } + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return iw_engine::calc_view_matrix(angles, cam_origin); + } + + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + float near, float far) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); + } + }; + +} // namespace omath::iw_engine \ No newline at end of file diff --git a/modules/trigonometry/angle.ixx b/modules/trigonometry/angle.ixx index e48ec9ff..4220e556 100644 --- a/modules/trigonometry/angle.ixx +++ b/modules/trigonometry/angle.ixx @@ -4,6 +4,7 @@ module; #include #include +#include #include #include #include @@ -170,9 +171,6 @@ export namespace omath return *this; } - [[nodiscard]] - constexpr std::partial_ordering operator<=>(const Angle& other) const noexcept = default; - constexpr Angle& operator-=(const Angle& other) noexcept { return operator+=(-other); diff --git a/source/engines/iw_engine/formulas.cpp b/source/engines/iw_engine/formulas.cpp deleted file mode 100644 index c346d50c..00000000 --- a/source/engines/iw_engine/formulas.cpp +++ /dev/null @@ -1,53 +0,0 @@ -// -// Created by Vlad on 3/19/2025. -// -#include "omath/engines/iw_engine/formulas.hpp" - -namespace omath::iw_engine -{ - - Vector3 forward_vector(const ViewAngles& angles) noexcept - { - const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); - - return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; - } - - Vector3 right_vector(const ViewAngles& angles) noexcept - { - const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); - - return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; - } - Vector3 up_vector(const ViewAngles& angles) noexcept - { - const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); - - return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; - } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); - } - - Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); - } - - Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, - const float far) noexcept - { - // NOTE: Need magic number to fix fov calculation, since IW engine inherit Quake proj matrix calculation - constexpr auto k_multiply_factor = 0.75f; - - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; - - return { - {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, - {0, 1.f / (fov_half_tan), 0, 0}, - {0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)}, - {0, 0, 1, 0}, - }; - }; -} // namespace omath::iw_engine diff --git a/source/engines/iw_engine/traits/camera_trait.cpp b/source/engines/iw_engine/traits/camera_trait.cpp deleted file mode 100644 index 6619751d..00000000 --- a/source/engines/iw_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/iw_engine/traits/camera_trait.hpp" -#include "omath/engines/iw_engine/formulas.hpp" -namespace omath::iw_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; - - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return iw_engine::calc_view_matrix(angles, cam_origin); - } - Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov, - const projection::ViewPort& view_port, const float near, - const float far) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); - } -} // namespace omath::iw_engine \ No newline at end of file diff --git a/tests/engines/unit_test_iw_engine.cpp b/tests/engines/unit_test_iw_engine.cpp index 199fa9b2..87cd2f61 100644 --- a/tests/engines/unit_test_iw_engine.cpp +++ b/tests/engines/unit_test_iw_engine.cpp @@ -2,10 +2,10 @@ // Created by Vlad on 3/17/2025. // #include -#include -#include -#include - +import omath.iw_engine.constants; +import omath.iw_engine.formulas; +import omath.iw_engine.camera; +import omath.camera; TEST(unit_test_iw_engine, ForwardVector) { diff --git a/tests/general/unit_test_vector2.cpp b/tests/general/unit_test_vector2.cpp index 510cddda..144be74d 100644 --- a/tests/general/unit_test_vector2.cpp +++ b/tests/general/unit_test_vector2.cpp @@ -2,12 +2,12 @@ // Created by Vlad on 02.09.2024. // -import omath.vector2; - +#include #include // For FLT_MAX and FLT_MIN #include // For std::isinf and std::isnan #include +import omath.vector2; using namespace omath; class UnitTestVector2 : public ::testing::Test diff --git a/tests/general/unit_test_vector3.cpp b/tests/general/unit_test_vector3.cpp index 80d1d36d..f39be7c4 100644 --- a/tests/general/unit_test_vector3.cpp +++ b/tests/general/unit_test_vector3.cpp @@ -2,6 +2,7 @@ // Created by Vlad on 01.09.2024. // +#include #include // For FLT_MAX, FLT_MIN #include #include From 396bc7110d870f92c01491fba29673e2145249af Mon Sep 17 00:00:00 2001 From: Orange Date: Mon, 1 Sep 2025 16:48:33 +0300 Subject: [PATCH 5/8] Adds projectile prediction engine Introduces a projectile prediction engine with a legacy implementation. This engine provides functionality to calculate the aim point for projectiles, taking into account target movement, gravity, and projectile properties. It uses a simulation approach to find the optimal launch angle. The engine is designed with an interface for easier extension and future implementations. --- .../proj_pred_engine.ixx | 22 ++ .../proj_pred_engine_legacy.ixx | 267 ++++++++++++++++++ modules/projectile_prediction/projectile.ixx | 17 ++ modules/projectile_prediction/target.ixx | 17 ++ 4 files changed, 323 insertions(+) create mode 100644 modules/projectile_prediction/proj_pred_engine.ixx create mode 100644 modules/projectile_prediction/proj_pred_engine_legacy.ixx create mode 100644 modules/projectile_prediction/projectile.ixx create mode 100644 modules/projectile_prediction/target.ixx diff --git a/modules/projectile_prediction/proj_pred_engine.ixx b/modules/projectile_prediction/proj_pred_engine.ixx new file mode 100644 index 00000000..d2a2e0d0 --- /dev/null +++ b/modules/projectile_prediction/proj_pred_engine.ixx @@ -0,0 +1,22 @@ +// +// Created by Vlad on 9/1/2025. +// +module; +#include + +export module omath.projectile_prediction.proj_pred_engine; + +import omath.projectile_prediction.target; +import omath.projectile_prediction.projectile; + +namespace omath::projectile_prediction +{ + class ProjPredEngineInterface + { + public: + [[nodiscard]] + virtual std::optional> maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const = 0; + virtual ~ProjPredEngineInterface() = default; + }; +} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/modules/projectile_prediction/proj_pred_engine_legacy.ixx b/modules/projectile_prediction/proj_pred_engine_legacy.ixx new file mode 100644 index 00000000..d6a79274 --- /dev/null +++ b/modules/projectile_prediction/proj_pred_engine_legacy.ixx @@ -0,0 +1,267 @@ +// +// Created by Vlad on 9/1/2025. +// + +module; + +#include + +export module omath.projectile_prediction.proj_pred_engine; + +export import omath.projectile_prediction.projectile; +export import omath.projectile_prediction.target; + +namespace omath::projectile_prediction +{ + template + concept PredEngineConcept = + requires(const Projectile& projectile, const Target& target, const Vector3& vec_a, + const Vector3& vec_b, + Vector3 v3, // by-value for calc_viewpoint_from_angles + float pitch, float yaw, float time, float gravity, std::optional maybe_pitch) { + // Presence + return types + { + T::predict_projectile_position(projectile, pitch, yaw, time, gravity) + } -> std::same_as>; + { T::predict_target_position(target, time, gravity) } -> std::same_as>; + { T::calc_vector_2d_distance(vec_a) } -> std::same_as; + { T::get_vector_height_coordinate(vec_b) } -> std::same_as; + { T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as>; + { T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as; + { T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as; + + // Enforce noexcept as in PredEngineTrait + requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity)); + requires noexcept(T::predict_target_position(target, time, gravity)); + requires noexcept(T::calc_vector_2d_distance(vec_a)); + requires noexcept(T::get_vector_height_coordinate(vec_b)); + requires noexcept(T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch)); + requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); + requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); + }; + template + requires PredEngineConcept + class ProjPredEngineLegacy final : public ProjPredEngineInterface + { + public: + explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, + const float maximum_simulation_time, const float distance_tolerance) + : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), + m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) + { + } + + [[nodiscard]] + std::optional> maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const override + { + for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) + { + const auto predicted_target_position = + EngineTrait::predict_target_position(target, time, m_gravity_constant); + + const auto projectile_pitch = + maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); + + if (!projectile_pitch.has_value()) [[unlikely]] + continue; + + if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), + time)) + continue; + + return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); + } + return std::nullopt; + } + + private: + const float m_gravity_constant; + const float m_simulation_time_step; + const float m_maximum_simulation_time; + const float m_distance_tolerance; + + // Realization of this formula: + // https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target + /* + \[ + \theta \;=\; \arctan\!\Biggl( + \frac{% + v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,} + }{% + gx + }\Biggr) + \] + */ + [[nodiscard]] + std::optional + maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, + const Vector3& target_position) const noexcept + { + const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; + + if (bullet_gravity == 0.f) + return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position); + + const auto delta = target_position - projectile.m_origin; + + const auto distance2d = EngineTrait::calc_vector_2d_distance(delta); + const auto distance2d_sqr = distance2d * distance2d; + const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; + + float root = launch_speed_sqr * launch_speed_sqr + - bullet_gravity + * (bullet_gravity * distance2d_sqr + + 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr); + + if (root < 0.0f) [[unlikely]] + return std::nullopt; + + root = std::sqrt(root); + const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); + + return angles::radians_to_degrees(angle); + } + [[nodiscard]] + bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, + const float pitch, const float time) const noexcept + { + const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position); + const auto projectile_position = + EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant); + + return projectile_position.distance_to(target_position) <= m_distance_tolerance; + } + }; +} // namespace omath::projectile_prediction + +#include "omath/engines/source_engine/traits/pred_engine_trait.hpp" +#include "omath/linear_algebra/vector3.hpp" +#include "omath/projectile_prediction/proj_pred_engine.hpp" +#include "omath/projectile_prediction/projectile.hpp" +#include "omath/projectile_prediction/target.hpp" +#include + +namespace omath::projectile_prediction +{ + template + concept PredEngineConcept = + requires(const Projectile& projectile, const Target& target, const Vector3& vec_a, + const Vector3& vec_b, + Vector3 v3, // by-value for calc_viewpoint_from_angles + float pitch, float yaw, float time, float gravity, std::optional maybe_pitch) { + // Presence + return types + { + T::predict_projectile_position(projectile, pitch, yaw, time, gravity) + } -> std::same_as>; + { T::predict_target_position(target, time, gravity) } -> std::same_as>; + { T::calc_vector_2d_distance(vec_a) } -> std::same_as; + { T::get_vector_height_coordinate(vec_b) } -> std::same_as; + { T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as>; + { T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as; + { T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as; + + // Enforce noexcept as in PredEngineTrait + requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity)); + requires noexcept(T::predict_target_position(target, time, gravity)); + requires noexcept(T::calc_vector_2d_distance(vec_a)); + requires noexcept(T::get_vector_height_coordinate(vec_b)); + requires noexcept(T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch)); + requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); + requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); + }; + template + requires PredEngineConcept + class ProjPredEngineLegacy final : public ProjPredEngineInterface + { + public: + explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, + const float maximum_simulation_time, const float distance_tolerance) + : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), + m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) + { + } + + [[nodiscard]] + std::optional> maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const override + { + for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) + { + const auto predicted_target_position = + EngineTrait::predict_target_position(target, time, m_gravity_constant); + + const auto projectile_pitch = + maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); + + if (!projectile_pitch.has_value()) [[unlikely]] + continue; + + if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), + time)) + continue; + + return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); + } + return std::nullopt; + } + + private: + const float m_gravity_constant; + const float m_simulation_time_step; + const float m_maximum_simulation_time; + const float m_distance_tolerance; + + // Realization of this formula: + // https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target + /* + \[ + \theta \;=\; \arctan\!\Biggl( + \frac{% + v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,} + }{% + gx + }\Biggr) + \] + */ + [[nodiscard]] + std::optional + maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, + const Vector3& target_position) const noexcept + { + const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; + + if (bullet_gravity == 0.f) + return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position); + + const auto delta = target_position - projectile.m_origin; + + const auto distance2d = EngineTrait::calc_vector_2d_distance(delta); + const auto distance2d_sqr = distance2d * distance2d; + const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; + + float root = launch_speed_sqr * launch_speed_sqr + - bullet_gravity + * (bullet_gravity * distance2d_sqr + + 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr); + + if (root < 0.0f) [[unlikely]] + return std::nullopt; + + root = std::sqrt(root); + const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); + + return angles::radians_to_degrees(angle); + } + [[nodiscard]] + bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, + const float pitch, const float time) const noexcept + { + const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position); + const auto projectile_position = + EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant); + + return projectile_position.distance_to(target_position) <= m_distance_tolerance; + } + }; +} // namespace omath::projectile_prediction diff --git a/modules/projectile_prediction/projectile.ixx b/modules/projectile_prediction/projectile.ixx new file mode 100644 index 00000000..f9f31420 --- /dev/null +++ b/modules/projectile_prediction/projectile.ixx @@ -0,0 +1,17 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath.projectile_prediction.projectile; +export import omath.vector3; + +export namespace omath::projectile_prediction +{ + class Projectile final + { + public: + Vector3 m_origin; + float m_launch_speed{}; + float m_gravity_scale{}; + }; +} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/modules/projectile_prediction/target.ixx b/modules/projectile_prediction/target.ixx new file mode 100644 index 00000000..94809efd --- /dev/null +++ b/modules/projectile_prediction/target.ixx @@ -0,0 +1,17 @@ +// +// Created by Vlad on 9/1/2025. +// + +export module omath.projectile_prediction.target; +export import omath.vector3; + +export namespace omath::projectile_prediction +{ + class Target final + { + public: + Vector3 m_origin; + Vector3 m_velocity; + bool m_is_airborne{}; + }; +} // namespace omath::projectile_prediction \ No newline at end of file From 7fe256350f3b29b3bb4e48c37d7f53f86e6077ec Mon Sep 17 00:00:00 2001 From: Orange Date: Tue, 2 Sep 2025 23:15:52 +0300 Subject: [PATCH 6/8] Adds IW Engine prediction trait Introduces a new trait for projectile prediction specifically tailored for the IW Engine. The new trait offers specialized methods for calculating projectile and target positions, handling 2D distances, and determining viewpoint angles, addressing the specific needs of the IW Engine's projectile prediction system. Also, refactors the legacy prediction engine to use the new IW Engine trait. --- .../iw_engine/trait/pred_engine_trait.ixx | 85 +++++++++++ .../proj_pred_engine.ixx | 2 +- .../proj_pred_engine_legacy.ixx | 144 ++---------------- modules/projectile_prediction/projectile.ixx | 2 +- 4 files changed, 96 insertions(+), 137 deletions(-) create mode 100644 modules/game_engines/iw_engine/trait/pred_engine_trait.ixx diff --git a/modules/game_engines/iw_engine/trait/pred_engine_trait.ixx b/modules/game_engines/iw_engine/trait/pred_engine_trait.ixx new file mode 100644 index 00000000..a02b21f5 --- /dev/null +++ b/modules/game_engines/iw_engine/trait/pred_engine_trait.ixx @@ -0,0 +1,85 @@ +// +// Created by Vlad on 9/2/2025. +// +module; +#include +#include + +export module omath.iw_engine.pred_engine_trait; + +import omath.vector3; +import omath.iw_engine.formulas; +import omath.iw_engine.constants; +import omath.projectile_prediction.projectile; +import omath.projectile_prediction.target; +import omath.angle; + +export namespace omath::iw_engine +{ + class PredEngineTrait final + { + public: + constexpr static Vector3 predict_projectile_position(const projectile_prediction::Projectile& projectile, + const float pitch, const float yaw, + const float time, const float gravity) noexcept + { + auto current_pos = projectile.m_origin + + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + RollAngle::from_degrees(0)}) + * projectile.m_launch_speed * time; + current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; + + return current_pos; + } + [[nodiscard]] + static constexpr Vector3 predict_target_position(const projectile_prediction::Target& target, + const float time, const float gravity) noexcept + { + auto predicted = target.m_origin + target.m_velocity * time; + + if (target.m_is_airborne) + predicted.z -= gravity * (time * time) * 0.5f; + + return predicted; + } + [[nodiscard]] + static float calc_vector_2d_distance(const Vector3& delta) noexcept + { + return std::sqrt(delta.x * delta.x + delta.y * delta.y); + } + + [[nodiscard]] + constexpr static float get_vector_height_coordinate(const Vector3& vec) noexcept + { + return vec.z; + } + + [[nodiscard]] + static Vector3 calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, + Vector3 predicted_target_position, + const std::optional projectile_pitch) noexcept + { + const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); + const auto height = delta2d * std::tan(degrees_to_radians(projectile_pitch.value())); + + return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height}; + } + // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: + // 89 look up, -89 look down + [[nodiscard]] + static float calc_direct_pitch_angle(const Vector3& origin, const Vector3& view_to) noexcept + { + const auto distance = origin.distance_to(view_to); + const auto delta = view_to - origin; + + return radians_to_degrees(std::asin(delta.z / distance)); + } + [[nodiscard]] + static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept + { + const auto delta = view_to - origin; + + return radians_to_degrees(std::atan2(delta.y, delta.x)); + }; + }; +} // namespace omath::iw_engine \ No newline at end of file diff --git a/modules/projectile_prediction/proj_pred_engine.ixx b/modules/projectile_prediction/proj_pred_engine.ixx index d2a2e0d0..48e90d3e 100644 --- a/modules/projectile_prediction/proj_pred_engine.ixx +++ b/modules/projectile_prediction/proj_pred_engine.ixx @@ -9,7 +9,7 @@ export module omath.projectile_prediction.proj_pred_engine; import omath.projectile_prediction.target; import omath.projectile_prediction.projectile; -namespace omath::projectile_prediction +export namespace omath::projectile_prediction { class ProjPredEngineInterface { diff --git a/modules/projectile_prediction/proj_pred_engine_legacy.ixx b/modules/projectile_prediction/proj_pred_engine_legacy.ixx index d6a79274..072df0ca 100644 --- a/modules/projectile_prediction/proj_pred_engine_legacy.ixx +++ b/modules/projectile_prediction/proj_pred_engine_legacy.ixx @@ -4,145 +4,19 @@ module; +#include #include -export module omath.projectile_prediction.proj_pred_engine; +export module omath.projectile_prediction.proj_pred_legacy; export import omath.projectile_prediction.projectile; export import omath.projectile_prediction.target; -namespace omath::projectile_prediction -{ - template - concept PredEngineConcept = - requires(const Projectile& projectile, const Target& target, const Vector3& vec_a, - const Vector3& vec_b, - Vector3 v3, // by-value for calc_viewpoint_from_angles - float pitch, float yaw, float time, float gravity, std::optional maybe_pitch) { - // Presence + return types - { - T::predict_projectile_position(projectile, pitch, yaw, time, gravity) - } -> std::same_as>; - { T::predict_target_position(target, time, gravity) } -> std::same_as>; - { T::calc_vector_2d_distance(vec_a) } -> std::same_as; - { T::get_vector_height_coordinate(vec_b) } -> std::same_as; - { T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as>; - { T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as; - { T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as; - - // Enforce noexcept as in PredEngineTrait - requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity)); - requires noexcept(T::predict_target_position(target, time, gravity)); - requires noexcept(T::calc_vector_2d_distance(vec_a)); - requires noexcept(T::get_vector_height_coordinate(vec_b)); - requires noexcept(T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch)); - requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); - requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); - }; - template - requires PredEngineConcept - class ProjPredEngineLegacy final : public ProjPredEngineInterface - { - public: - explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, - const float maximum_simulation_time, const float distance_tolerance) - : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), - m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) - { - } - - [[nodiscard]] - std::optional> maybe_calculate_aim_point(const Projectile& projectile, - const Target& target) const override - { - for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) - { - const auto predicted_target_position = - EngineTrait::predict_target_position(target, time, m_gravity_constant); - - const auto projectile_pitch = - maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); - - if (!projectile_pitch.has_value()) [[unlikely]] - continue; - - if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), - time)) - continue; - - return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); - } - return std::nullopt; - } - - private: - const float m_gravity_constant; - const float m_simulation_time_step; - const float m_maximum_simulation_time; - const float m_distance_tolerance; - - // Realization of this formula: - // https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target - /* - \[ - \theta \;=\; \arctan\!\Biggl( - \frac{% - v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,} - }{% - gx - }\Biggr) - \] - */ - [[nodiscard]] - std::optional - maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, - const Vector3& target_position) const noexcept - { - const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; - - if (bullet_gravity == 0.f) - return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position); - - const auto delta = target_position - projectile.m_origin; - - const auto distance2d = EngineTrait::calc_vector_2d_distance(delta); - const auto distance2d_sqr = distance2d * distance2d; - const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; - - float root = launch_speed_sqr * launch_speed_sqr - - bullet_gravity - * (bullet_gravity * distance2d_sqr - + 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr); - - if (root < 0.0f) [[unlikely]] - return std::nullopt; - - root = std::sqrt(root); - const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); - - return angles::radians_to_degrees(angle); - } - [[nodiscard]] - bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, - const float pitch, const float time) const noexcept - { - const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position); - const auto projectile_position = - EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant); - - return projectile_position.distance_to(target_position) <= m_distance_tolerance; - } - }; -} // namespace omath::projectile_prediction - -#include "omath/engines/source_engine/traits/pred_engine_trait.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include "omath/projectile_prediction/proj_pred_engine.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" -#include +import omath.projectile_prediction.proj_pred_engine; +import omath.iw_engine.pred_engine_trait; +import omath.angle; -namespace omath::projectile_prediction +export namespace omath::projectile_prediction { template concept PredEngineConcept = @@ -170,7 +44,7 @@ namespace omath::projectile_prediction requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); }; - template + template requires PredEngineConcept class ProjPredEngineLegacy final : public ProjPredEngineInterface { @@ -251,7 +125,7 @@ namespace omath::projectile_prediction root = std::sqrt(root); const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); - return angles::radians_to_degrees(angle); + return radians_to_degrees(angle); } [[nodiscard]] bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, @@ -264,4 +138,4 @@ namespace omath::projectile_prediction return projectile_position.distance_to(target_position) <= m_distance_tolerance; } }; -} // namespace omath::projectile_prediction +} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/modules/projectile_prediction/projectile.ixx b/modules/projectile_prediction/projectile.ixx index f9f31420..37adaa26 100644 --- a/modules/projectile_prediction/projectile.ixx +++ b/modules/projectile_prediction/projectile.ixx @@ -2,7 +2,7 @@ // Created by Vlad on 9/1/2025. // -export module omath.projectile_prediction.projectile; +export module omath.projectile_prediction. projectile; export import omath.vector3; export namespace omath::projectile_prediction From f0c303bad4b5824943922bb9d69007d36ba36ec6 Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 3 Sep 2025 00:17:46 +0300 Subject: [PATCH 7/8] Refactors game engine modules to use C++ modules Migrates game engine-specific code (IW, OpenGL, Source, Unity) to C++ modules for improved modularity, compilation times, and code organization. Removes old header files and source files, replacing them with module interface units (.ixx). This change enhances the project's structure and maintainability by leveraging modern C++ module features. --- CMakeLists.txt | 6 +- examples/example_proj_mat_builder.cpp | 5 +- include/omath/angle.hpp | 166 ------ include/omath/angles.hpp | 64 --- include/omath/color.hpp | 186 ------- .../iw_engine/traits/pred_engine_trait.hpp | 79 --- .../omath/engines/opengl_engine/camera.hpp | 12 - .../omath/engines/opengl_engine/formulas.hpp | 26 - .../opengl_engine/traits/camera_trait.hpp | 24 - .../omath/engines/source_engine/camera.hpp | 11 - .../omath/engines/source_engine/formulas.hpp | 25 - .../source_engine/traits/camera_trait.hpp | 24 - include/omath/engines/unity_engine/camera.hpp | 13 - .../omath/engines/unity_engine/formulas.hpp | 26 - .../unity_engine/traits/camera_trait.hpp | 24 - .../omath/engines/unreal_engine/camera.hpp | 13 - .../omath/engines/unreal_engine/formulas.hpp | 26 - .../unreal_engine/traits/camera_trait.hpp | 24 - include/omath/linear_algebra/mat.hpp | 527 ------------------ include/omath/linear_algebra/matrix.hpp | 127 ----- include/omath/linear_algebra/vector2.hpp | 250 --------- include/omath/linear_algebra/vector3.hpp | 311 ----------- include/omath/linear_algebra/vector4.hpp | 218 -------- include/omath/omath.hpp | 84 --- include/omath/pathfinding/a_star.hpp | 30 - include/omath/pathfinding/navigation_mesh.hpp | 37 -- .../proj_pred_engine.hpp | 19 - .../proj_pred_engine_avx2.hpp | 28 - .../proj_pred_engine_legacy.hpp | 134 ----- .../projectile_prediction/projectile.hpp | 17 - .../omath/projectile_prediction/target.hpp | 17 - include/omath/projection/camera.hpp | 204 ------- include/omath/projection/error_codes.hpp | 14 - include/omath/triangle.hpp | 85 --- include/omath/view_angles.hpp | 15 - modules/game_engines/iw_engine/camera.ixx | 3 + modules/game_engines/iw_engine/constants.ixx | 1 + modules/game_engines/iw_engine/formulas.ixx | 2 + .../{trait => traits}/camera_trait.ixx | 2 +- .../{trait => traits}/pred_engine_trait.ixx | 0 modules/game_engines/opengl_engine/camera.ixx | 14 + .../game_engines/opengl_engine/constants.ixx | 15 +- .../game_engines/opengl_engine/formulas.ixx | 46 +- .../opengl_engine/traits/camera_trait.ixx | 43 ++ .../traits/pred_engine_trait.ixx | 25 +- modules/game_engines/source_engine/camera.ixx | 13 + .../game_engines/source_engine/constants.ixx | 17 +- .../game_engines/source_engine/formulas.ixx | 39 +- .../source_engine/traits/camera_trait.ixx | 40 ++ .../traits/pred_engine_trait.ixx | 26 +- modules/game_engines/unity_engine/camera.ixx | 14 + .../game_engines/unity_engine/constants.ixx | 15 +- .../game_engines/unity_engine/formulas.ixx | 38 +- .../unity_engine/traits/camera_trait.ixx | 42 ++ .../unity_engine/traits/pred_engine_trait.ixx | 23 +- modules/game_engines/unreal_engine/camera.ixx | 14 + .../game_engines/unreal_engine/constants.ixx | 14 +- .../game_engines/unreal_engine/formulas.ixx | 40 +- .../unreal_engine/traits/camera_trait.ixx | 40 ++ .../traits/pred_engine_trait.ixx | 26 +- modules/linear_algebra/mat.ixx | 9 +- modules/pathfinding/a_star.ixx | 115 ++++ modules/pathfinding/navigation_mesh.ixx | 110 ++++ .../proj_pred_engine_avx2.ixx | 177 ++++++ .../proj_pred_engine_legacy.ixx | 4 +- .../opengl_engine/traits/camera_trait.cpp | 28 - .../source_engine/traits/camera_trait.cpp | 27 - .../unity_engine/traits/camera_trait.cpp | 27 - .../unreal_engine/traits/camera_trait.cpp | 27 - source/matrix.cpp | 364 ------------ source/pathfinding/a_star.cpp | 99 ---- source/pathfinding/navigation_mesh.cpp | 91 --- .../proj_pred_engine_avx2.cpp | 158 ------ tests/engines/unit_test_open_gl.cpp | 7 +- tests/engines/unit_test_source_engine.cpp | 8 +- tests/engines/unit_test_unity_engine.cpp | 8 +- tests/engines/unit_test_unreal_engine.cpp | 6 +- tests/general/unit_test_a_star.cpp | 7 +- tests/general/unit_test_angles.cpp | 15 +- tests/general/unit_test_mat.cpp | 5 +- tests/general/unit_test_prediction.cpp | 7 +- tests/general/unit_test_projection.cpp | 6 +- tests/general/unit_test_view_angles.cpp | 1 - 83 files changed, 886 insertions(+), 3843 deletions(-) delete mode 100644 include/omath/angle.hpp delete mode 100644 include/omath/angles.hpp delete mode 100644 include/omath/color.hpp delete mode 100644 include/omath/engines/iw_engine/traits/pred_engine_trait.hpp delete mode 100644 include/omath/engines/opengl_engine/camera.hpp delete mode 100644 include/omath/engines/opengl_engine/formulas.hpp delete mode 100644 include/omath/engines/opengl_engine/traits/camera_trait.hpp delete mode 100644 include/omath/engines/source_engine/camera.hpp delete mode 100644 include/omath/engines/source_engine/formulas.hpp delete mode 100644 include/omath/engines/source_engine/traits/camera_trait.hpp delete mode 100644 include/omath/engines/unity_engine/camera.hpp delete mode 100644 include/omath/engines/unity_engine/formulas.hpp delete mode 100644 include/omath/engines/unity_engine/traits/camera_trait.hpp delete mode 100644 include/omath/engines/unreal_engine/camera.hpp delete mode 100644 include/omath/engines/unreal_engine/formulas.hpp delete mode 100644 include/omath/engines/unreal_engine/traits/camera_trait.hpp delete mode 100644 include/omath/linear_algebra/mat.hpp delete mode 100644 include/omath/linear_algebra/matrix.hpp delete mode 100644 include/omath/linear_algebra/vector2.hpp delete mode 100644 include/omath/linear_algebra/vector3.hpp delete mode 100644 include/omath/linear_algebra/vector4.hpp delete mode 100644 include/omath/omath.hpp delete mode 100644 include/omath/pathfinding/a_star.hpp delete mode 100644 include/omath/pathfinding/navigation_mesh.hpp delete mode 100644 include/omath/projectile_prediction/proj_pred_engine.hpp delete mode 100644 include/omath/projectile_prediction/proj_pred_engine_avx2.hpp delete mode 100644 include/omath/projectile_prediction/proj_pred_engine_legacy.hpp delete mode 100644 include/omath/projectile_prediction/projectile.hpp delete mode 100644 include/omath/projectile_prediction/target.hpp delete mode 100644 include/omath/projection/camera.hpp delete mode 100644 include/omath/projection/error_codes.hpp delete mode 100644 include/omath/triangle.hpp delete mode 100644 include/omath/view_angles.hpp rename modules/game_engines/iw_engine/{trait => traits}/camera_trait.ixx (93%) rename modules/game_engines/iw_engine/{trait => traits}/pred_engine_trait.ixx (100%) create mode 100644 modules/game_engines/opengl_engine/camera.ixx rename include/omath/engines/opengl_engine/constants.hpp => modules/game_engines/opengl_engine/constants.ixx (75%) rename source/engines/opengl_engine/formulas.cpp => modules/game_engines/opengl_engine/formulas.ixx (66%) create mode 100644 modules/game_engines/opengl_engine/traits/camera_trait.ixx rename include/omath/engines/opengl_engine/traits/pred_engine_trait.hpp => modules/game_engines/opengl_engine/traits/pred_engine_trait.ixx (84%) create mode 100644 modules/game_engines/source_engine/camera.ixx rename include/omath/engines/source_engine/constants.hpp => modules/game_engines/source_engine/constants.ixx (71%) rename source/engines/source_engine/formulas.cpp => modules/game_engines/source_engine/formulas.ixx (66%) create mode 100644 modules/game_engines/source_engine/traits/camera_trait.ixx rename include/omath/engines/source_engine/traits/pred_engine_trait.hpp => modules/game_engines/source_engine/traits/pred_engine_trait.ixx (83%) create mode 100644 modules/game_engines/unity_engine/camera.ixx rename include/omath/engines/unity_engine/constants.hpp => modules/game_engines/unity_engine/constants.ixx (72%) rename source/engines/unity_engine/formulas.cpp => modules/game_engines/unity_engine/formulas.ixx (76%) create mode 100644 modules/game_engines/unity_engine/traits/camera_trait.ixx rename include/omath/engines/unity_engine/traits/pred_engine_trait.hpp => modules/game_engines/unity_engine/traits/pred_engine_trait.ixx (83%) create mode 100644 modules/game_engines/unreal_engine/camera.ixx rename include/omath/engines/unreal_engine/constants.hpp => modules/game_engines/unreal_engine/constants.ixx (75%) rename source/engines/unreal_engine/formulas.cpp => modules/game_engines/unreal_engine/formulas.ixx (76%) create mode 100644 modules/game_engines/unreal_engine/traits/camera_trait.ixx rename include/omath/engines/unreal_engine/traits/pred_engine_trait.hpp => modules/game_engines/unreal_engine/traits/pred_engine_trait.ixx (82%) create mode 100644 modules/pathfinding/a_star.ixx create mode 100644 modules/pathfinding/navigation_mesh.ixx create mode 100644 modules/projectile_prediction/proj_pred_engine_avx2.ixx delete mode 100644 source/engines/opengl_engine/traits/camera_trait.cpp delete mode 100644 source/engines/source_engine/traits/camera_trait.cpp delete mode 100644 source/engines/unity_engine/traits/camera_trait.cpp delete mode 100644 source/engines/unreal_engine/traits/camera_trait.cpp delete mode 100644 source/matrix.cpp delete mode 100644 source/pathfinding/a_star.cpp delete mode 100644 source/pathfinding/navigation_mesh.cpp delete mode 100644 source/projectile_prediction/proj_pred_engine_avx2.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 33608c54..4758a70a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,15 +27,13 @@ message(STATUS "[${PROJECT_NAME}]: AVX2 feature status ${OMATH_USE_AVX2}") message(STATUS "[${PROJECT_NAME}]: ImGUI integration feature status ${OMATH_IMGUI_INTEGRATION}") message(STATUS "[${PROJECT_NAME}]: Legacy features support ${OMATH_ENABLE_LEGACY}") -file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") -file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp") file(GLOB_RECURSE OMATH_MODULES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/modules/*.ixx") if (OMATH_BUILD_AS_SHARED_LIBRARY) - add_library(${PROJECT_NAME} SHARED ${OMATH_SOURCES} ${OMATH_HEADERS}) + add_library(${PROJECT_NAME} SHARED) else () - add_library(${PROJECT_NAME} STATIC ${OMATH_SOURCES} ${OMATH_HEADERS}) + add_library(${PROJECT_NAME} STATIC) endif () target_sources(${PROJECT_NAME} diff --git a/examples/example_proj_mat_builder.cpp b/examples/example_proj_mat_builder.cpp index 5857e373..172efcea 100644 --- a/examples/example_proj_mat_builder.cpp +++ b/examples/example_proj_mat_builder.cpp @@ -5,7 +5,8 @@ #include #include -#include +#include +import omath.opengl_engine.formulas; int main() @@ -36,5 +37,5 @@ int main() const auto mat = omath::opengl_engine::calc_perspective_projection_matrix(fov, view_port_width / view_port_height, near, far); - std::print("{}", mat.to_string()); + std::print("{}", mat); }; diff --git a/include/omath/angle.hpp b/include/omath/angle.hpp deleted file mode 100644 index a13b27fa..00000000 --- a/include/omath/angle.hpp +++ /dev/null @@ -1,166 +0,0 @@ -// -// Created by Orange on 11/30/2024. -// - -#pragma once -#include "omath/angles.hpp" -#include -#include -#include - -namespace omath -{ - enum class AngleFlags - { - Normalized = 0, - Clamped = 1, - }; - - template - requires std::is_arithmetic_v - class Angle - { - Type m_angle; - constexpr explicit Angle(const Type& degrees) noexcept - { - if constexpr (flags == AngleFlags::Normalized) - m_angle = angles::wrap_angle(degrees, min, max); - - else if constexpr (flags == AngleFlags::Clamped) - m_angle = std::clamp(degrees, min, max); - else - { - static_assert(false); - std::unreachable(); - } - } - - public: - [[nodiscard]] - constexpr static Angle from_degrees(const Type& degrees) noexcept - { - return Angle{degrees}; - } - constexpr Angle() noexcept: m_angle(0) - { - } - [[nodiscard]] - constexpr static Angle from_radians(const Type& degrees) noexcept - { - return Angle{angles::radians_to_degrees(degrees)}; - } - - [[nodiscard]] - constexpr const Type& operator*() const noexcept - { - return m_angle; - } - - [[nodiscard]] - constexpr Type as_degrees() const noexcept - { - return m_angle; - } - - [[nodiscard]] - constexpr Type as_radians() const noexcept - { - return angles::degrees_to_radians(m_angle); - } - - [[nodiscard]] - Type sin() const noexcept - { - return std::sin(as_radians()); - } - - [[nodiscard]] - Type cos() const noexcept - { - return std::cos(as_radians()); - } - - [[nodiscard]] - Type tan() const noexcept - { - return std::tan(as_radians()); - } - - [[nodiscard]] - Type atan() const noexcept - { - return std::atan(as_radians()); - } - - [[nodiscard]] - Type cot() const noexcept - { - return cos() / sin(); - } - - constexpr Angle& operator+=(const Angle& other) noexcept - { - if constexpr (flags == AngleFlags::Normalized) - m_angle = angles::wrap_angle(m_angle + other.m_angle, min, max); - - else if constexpr (flags == AngleFlags::Clamped) - m_angle = std::clamp(m_angle + other.m_angle, min, max); - else - { - static_assert(false); - std::unreachable(); - } - - return *this; - } - - [[nodiscard]] - constexpr std::partial_ordering operator<=>(const Angle& other) const noexcept = default; - - constexpr Angle& operator-=(const Angle& other) noexcept - { - return operator+=(-other); - } - - [[nodiscard]] - constexpr Angle& operator+(const Angle& other) noexcept - { - if constexpr (flags == AngleFlags::Normalized) - return {angles::wrap_angle(m_angle + other.m_angle, min, max)}; - - else if constexpr (flags == AngleFlags::Clamped) - return {std::clamp(m_angle + other.m_angle, min, max)}; - - else - static_assert(false); - - std::unreachable(); - } - - [[nodiscard]] - constexpr Angle& operator-(const Angle& other) noexcept - { - return operator+(-other); - } - - [[nodiscard]] - constexpr Angle operator-() const noexcept - { - return Angle{-m_angle}; - } - }; -} // namespace omath -template -struct std::formatter> // NOLINT(*-dcl58-cpp) -{ - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const omath::Angle& deg, std::format_context& ctx) - { - return std::format_to(ctx.out(), "{}deg", deg.as_degrees()); - } -}; \ No newline at end of file diff --git a/include/omath/angles.hpp b/include/omath/angles.hpp deleted file mode 100644 index ce07dd83..00000000 --- a/include/omath/angles.hpp +++ /dev/null @@ -1,64 +0,0 @@ -// -// Created by vlad on 11/6/23. -// - -#pragma once -#include -#include - -namespace omath::angles -{ - template - requires std::is_floating_point_v - [[nodiscard]] constexpr Type radians_to_degrees(const Type& radians) noexcept - { - return radians * (static_cast(180) / std::numbers::pi_v); - } - - template - requires std::is_floating_point_v - [[nodiscard]] constexpr Type degrees_to_radians(const Type& degrees) noexcept - { - return degrees * (std::numbers::pi_v / static_cast(180)); - } - - template - requires std::is_floating_point_v - [[nodiscard]] Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept - { - const auto fov_rad = degrees_to_radians(horizontal_fov); - - const auto vert_fov = static_cast(2) * std::atan(std::tan(fov_rad / static_cast(2)) / aspect); - - return radians_to_degrees(vert_fov); - } - - template - requires std::is_floating_point_v - [[nodiscard]] Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept - { - const auto fov_as_radians = degrees_to_radians(vertical_fov); - - const auto horizontal_fov = - static_cast(2) * std::atan(std::tan(fov_as_radians / static_cast(2)) * aspect); - - return radians_to_degrees(horizontal_fov); - } - - template - requires std::is_arithmetic_v - [[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept - { - if (angle <= max && angle >= min) - return angle; - - const Type range = max - min; - - Type wrapped_angle = std::fmod(angle - min, range); - - if (wrapped_angle < 0) - wrapped_angle += range; - - return wrapped_angle + min; - } -} // namespace omath::angles diff --git a/include/omath/color.hpp b/include/omath/color.hpp deleted file mode 100644 index 69e554fc..00000000 --- a/include/omath/color.hpp +++ /dev/null @@ -1,186 +0,0 @@ -// -// Created by vlad on 2/4/24. -// - -#pragma once - -#include "linear_algebra/vector4.hpp" -#include - -namespace omath -{ - struct Hsv - { - float hue{}; - float saturation{}; - float value{}; - }; - - class Color final : public Vector4 - { - public: - constexpr Color(const float r, const float g, const float b, const float a) noexcept: Vector4(r, g, b, a) - { - clamp(0.f, 1.f); - } - - constexpr explicit Color() noexcept = default; - [[nodiscard]] - constexpr static Color from_rgba(const uint8_t r, const uint8_t g, const uint8_t b, const uint8_t a) noexcept - { - return Color{Vector4(r, g, b, a) / 255.f}; - } - - [[nodiscard]] - constexpr static Color from_hsv(float hue, const float saturation, const float value) noexcept - { - float r{}, g{}, b{}; - - hue = std::clamp(hue, 0.f, 1.f); - - const int i = static_cast(hue * 6.f); - const float f = hue * 6.f - static_cast(i); - const float p = value * (1 - saturation); - const float q = value * (1 - f * saturation); - const float t = value * (1 - (1 - f) * saturation); - - switch (i % 6) - { - case 0: - r = value, g = t, b = p; - break; - case 1: - r = q, g = value, b = p; - break; - case 2: - r = p, g = value, b = t; - break; - case 3: - r = p, g = q, b = value; - break; - case 4: - r = t, g = p, b = value; - break; - case 5: - r = value, g = p, b = q; - break; - - default: - return {0.f, 0.f, 0.f, 0.f}; - } - - return {r, g, b, 1.f}; - } - - [[nodiscard]] - constexpr static Color from_hsv(const Hsv& hsv) noexcept - { - return from_hsv(hsv.hue, hsv.saturation, hsv.value); - } - - [[nodiscard]] - constexpr Hsv to_hsv() const noexcept - { - Hsv hsv_data; - - const float& red = x; - const float& green = y; - const float& blue = z; - - const float max = std::max({red, green, blue}); - const float min = std::min({red, green, blue}); - const float delta = max - min; - - if (delta == 0.f) - hsv_data.hue = 0.f; - - else if (max == red) - hsv_data.hue = 60.f * (std::fmodf(((green - blue) / delta), 6.f)); - else if (max == green) - hsv_data.hue = 60.f * (((blue - red) / delta) + 2.f); - else if (max == blue) - hsv_data.hue = 60.f * (((red - green) / delta) + 4.f); - - if (hsv_data.hue < 0.f) - hsv_data.hue += 360.f; - - hsv_data.hue /= 360.f; - hsv_data.saturation = max == 0.f ? 0.f : delta / max; - hsv_data.value = max; - - return hsv_data; - } - - constexpr explicit Color(const Vector4& vec) noexcept: Vector4(vec) - { - clamp(0.f, 1.f); - } - constexpr void set_hue(const float hue) noexcept - { - auto hsv = to_hsv(); - hsv.hue = hue; - - *this = from_hsv(hsv); - } - - constexpr void set_saturation(const float saturation) noexcept - { - auto hsv = to_hsv(); - hsv.saturation = saturation; - - *this = from_hsv(hsv); - } - - constexpr void set_value(const float value) noexcept - { - auto hsv = to_hsv(); - hsv.value = value; - - *this = from_hsv(hsv); - } - [[nodiscard]] - constexpr Color blend(const Color& other, float ratio) const noexcept - { - ratio = std::clamp(ratio, 0.f, 1.f); - return Color(*this * (1.f - ratio) + other * ratio); - } - - [[nodiscard]] static constexpr Color red() - { - return {1.f, 0.f, 0.f, 1.f}; - } - [[nodiscard]] static constexpr Color green() - { - return {0.f, 1.f, 0.f, 1.f}; - } - [[nodiscard]] static constexpr Color blue() - { - return {0.f, 0.f, 1.f, 1.f}; - } -#ifdef OMATH_IMGUI_INTEGRATION - [[nodiscard]] - ImColor to_im_color() const noexcept - { - return {to_im_vec4()}; - } -#endif - }; -} // namespace omath -template<> -struct std::formatter // NOLINT(*-dcl58-cpp) -{ - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const omath::Color& col, std::format_context& ctx) - { - return std::format_to(ctx.out(), "[r:{}, g:{}, b:{}, a:{}]", - static_cast(col.x * 255.f), - static_cast(col.y * 255.f), - static_cast(col.z * 255.f), - static_cast(col.w * 255.f)); - } -}; \ No newline at end of file diff --git a/include/omath/engines/iw_engine/traits/pred_engine_trait.hpp b/include/omath/engines/iw_engine/traits/pred_engine_trait.hpp deleted file mode 100644 index 7961268a..00000000 --- a/include/omath/engines/iw_engine/traits/pred_engine_trait.hpp +++ /dev/null @@ -1,79 +0,0 @@ -// -// Created by Vlad on 8/6/2025. -// -#pragma once - -#include "omath/engines/iw_engine/formulas.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" -#include - -namespace omath::iw_engine -{ - class PredEngineTrait final - { - public: - constexpr static Vector3 predict_projectile_position(const projectile_prediction::Projectile& projectile, - const float pitch, const float yaw, - const float time, const float gravity) noexcept - { - auto current_pos = projectile.m_origin - + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), - RollAngle::from_degrees(0)}) - * projectile.m_launch_speed * time; - current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; - - return current_pos; - } - [[nodiscard]] - static constexpr Vector3 predict_target_position(const projectile_prediction::Target& target, - const float time, const float gravity) noexcept - { - auto predicted = target.m_origin + target.m_velocity * time; - - if (target.m_is_airborne) - predicted.z -= gravity * (time * time) * 0.5f; - - return predicted; - } - [[nodiscard]] - static float calc_vector_2d_distance(const Vector3& delta) noexcept - { - return std::sqrt(delta.x * delta.x + delta.y * delta.y); - } - - [[nodiscard]] - constexpr static float get_vector_height_coordinate(const Vector3& vec) noexcept - { - return vec.z; - } - - [[nodiscard]] - static Vector3 calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, - Vector3 predicted_target_position, - const std::optional projectile_pitch) noexcept - { - const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); - const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); - - return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height}; - } - // Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be: - // 89 look up, -89 look down - [[nodiscard]] - static float calc_direct_pitch_angle(const Vector3& origin, const Vector3& view_to) noexcept - { - const auto distance = origin.distance_to(view_to); - const auto delta = view_to - origin; - - return angles::radians_to_degrees(std::asin(delta.z / distance)); - } - [[nodiscard]] - static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept - { - const auto delta = view_to - origin; - - return angles::radians_to_degrees(std::atan2(delta.y, delta.x)); - }; - }; -} // namespace omath::iw_engine \ No newline at end of file diff --git a/include/omath/engines/opengl_engine/camera.hpp b/include/omath/engines/opengl_engine/camera.hpp deleted file mode 100644 index e69ee340..00000000 --- a/include/omath/engines/opengl_engine/camera.hpp +++ /dev/null @@ -1,12 +0,0 @@ -// -// Created by Orange on 12/23/2024. -// -#pragma once -#include "omath/engines/opengl_engine/constants.hpp" -#include "omath/projection/camera.hpp" -#include "traits/camera_trait.hpp" - -namespace omath::opengl_engine -{ - using Camera = projection::Camera; -} // namespace omath::opengl_engine \ No newline at end of file diff --git a/include/omath/engines/opengl_engine/formulas.hpp b/include/omath/engines/opengl_engine/formulas.hpp deleted file mode 100644 index 31714f56..00000000 --- a/include/omath/engines/opengl_engine/formulas.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Orange on 12/23/2024. -// -#pragma once -#include "omath/engines/opengl_engine/constants.hpp" - - -namespace omath::opengl_engine -{ - [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - - [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; -} // namespace omath::opengl_engine diff --git a/include/omath/engines/opengl_engine/traits/camera_trait.hpp b/include/omath/engines/opengl_engine/traits/camera_trait.hpp deleted file mode 100644 index 3fb57c07..00000000 --- a/include/omath/engines/opengl_engine/traits/camera_trait.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Vlad on 8/10/2025. -// - -#pragma once -#include "omath/engines/opengl_engine/constants.hpp" -#include "omath/projection/camera.hpp" - -namespace omath::opengl_engine -{ - class CameraTrait final - { - public: - [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; - - [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept; - }; - -} // namespace omath::opengl_engine \ No newline at end of file diff --git a/include/omath/engines/source_engine/camera.hpp b/include/omath/engines/source_engine/camera.hpp deleted file mode 100644 index 6769d40d..00000000 --- a/include/omath/engines/source_engine/camera.hpp +++ /dev/null @@ -1,11 +0,0 @@ -// -// Created by Orange on 12/4/2024. -// -#pragma once -#include "omath/engines/source_engine/constants.hpp" -#include "omath/projection/camera.hpp" -#include "traits/camera_trait.hpp" -namespace omath::source_engine -{ - using Camera = projection::Camera; -} // namespace omath::source_engine \ No newline at end of file diff --git a/include/omath/engines/source_engine/formulas.hpp b/include/omath/engines/source_engine/formulas.hpp deleted file mode 100644 index a51a6333..00000000 --- a/include/omath/engines/source_engine/formulas.hpp +++ /dev/null @@ -1,25 +0,0 @@ -// -// Created by Orange on 12/4/2024. -// -#pragma once -#include "omath/engines/source_engine/constants.hpp" - -namespace omath::source_engine -{ - [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - - [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; -} // namespace omath::source_engine diff --git a/include/omath/engines/source_engine/traits/camera_trait.hpp b/include/omath/engines/source_engine/traits/camera_trait.hpp deleted file mode 100644 index d027d25e..00000000 --- a/include/omath/engines/source_engine/traits/camera_trait.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Vlad on 8/10/2025. -// - -#pragma once -#include "omath/engines/source_engine/constants.hpp" -#include "omath/projection/camera.hpp" - -namespace omath::source_engine -{ - class CameraTrait final - { - public: - [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; - - [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept; - }; - -} // namespace omath::source_engine \ No newline at end of file diff --git a/include/omath/engines/unity_engine/camera.hpp b/include/omath/engines/unity_engine/camera.hpp deleted file mode 100644 index 642290a3..00000000 --- a/include/omath/engines/unity_engine/camera.hpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Vlad on 3/22/2025. -// - -#pragma once -#include "omath/engines/unity_engine/constants.hpp" -#include "omath/projection/camera.hpp" -#include "traits/camera_trait.hpp" - -namespace omath::unity_engine -{ - using Camera = projection::Camera; -} // namespace omath::unity_engine \ No newline at end of file diff --git a/include/omath/engines/unity_engine/formulas.hpp b/include/omath/engines/unity_engine/formulas.hpp deleted file mode 100644 index be071367..00000000 --- a/include/omath/engines/unity_engine/formulas.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Vlad on 3/22/2025. -// - -#pragma once -#include "omath/engines/unity_engine/constants.hpp" - -namespace omath::unity_engine -{ - [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - - [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; -} // namespace omath::unity_engine diff --git a/include/omath/engines/unity_engine/traits/camera_trait.hpp b/include/omath/engines/unity_engine/traits/camera_trait.hpp deleted file mode 100644 index 2d98b9db..00000000 --- a/include/omath/engines/unity_engine/traits/camera_trait.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Vlad on 8/10/2025. -// - -#pragma once -#include "omath/engines/unity_engine/formulas.hpp" -#include "omath/projection/camera.hpp" - -namespace omath::unity_engine -{ - class CameraTrait final - { - public: - [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; - - [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept; - }; - -} // namespace omath::unity_engine \ No newline at end of file diff --git a/include/omath/engines/unreal_engine/camera.hpp b/include/omath/engines/unreal_engine/camera.hpp deleted file mode 100644 index 30d5daf5..00000000 --- a/include/omath/engines/unreal_engine/camera.hpp +++ /dev/null @@ -1,13 +0,0 @@ -// -// Created by Vlad on 3/22/2025. -// - -#pragma once -#include "omath/engines/unreal_engine/constants.hpp" -#include "omath/projection/camera.hpp" -#include "traits/camera_trait.hpp" - -namespace omath::unreal_engine -{ - using Camera = projection::Camera; -} // namespace omath::unreal_engine \ No newline at end of file diff --git a/include/omath/engines/unreal_engine/formulas.hpp b/include/omath/engines/unreal_engine/formulas.hpp deleted file mode 100644 index 4e601ce8..00000000 --- a/include/omath/engines/unreal_engine/formulas.hpp +++ /dev/null @@ -1,26 +0,0 @@ -// -// Created by Vlad on 3/22/2025. -// - -#pragma once -#include "omath/engines/unreal_engine/constants.hpp" - -namespace omath::unreal_engine -{ - [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - - [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; - - [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept; -} // namespace omath::unreal_engine diff --git a/include/omath/engines/unreal_engine/traits/camera_trait.hpp b/include/omath/engines/unreal_engine/traits/camera_trait.hpp deleted file mode 100644 index f2de27e4..00000000 --- a/include/omath/engines/unreal_engine/traits/camera_trait.hpp +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Vlad on 8/10/2025. -// - -#pragma once -#include "omath/engines/unreal_engine/formulas.hpp" -#include "omath/projection/camera.hpp" - -namespace omath::unreal_engine -{ - class CameraTrait final - { - public: - [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; - - [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; - [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept; - }; - -} // namespace omath::unreal_engine \ No newline at end of file diff --git a/include/omath/linear_algebra/mat.hpp b/include/omath/linear_algebra/mat.hpp deleted file mode 100644 index 95276dcc..00000000 --- a/include/omath/linear_algebra/mat.hpp +++ /dev/null @@ -1,527 +0,0 @@ -// -// Created by vlad on 9/29/2024. -// -#pragma once -#include "vector3.hpp" -#include -#include -#include -#include -#include -#include -#include - -#ifdef near -#undef near -#endif - -#ifdef far -#undef far -#endif - -namespace omath -{ - struct MatSize - { - size_t rows, columns; - }; - - enum class MatStoreType : uint8_t - { - ROW_MAJOR = 0, - COLUMN_MAJOR - }; - - template concept MatTemplateEqual - = (M1::rows == M2::rows) && (M1::columns == M2::columns) - && std::is_same_v && (M1::store_type == M2::store_type); - - template - requires std::is_arithmetic_v - class Mat final - { - public: - constexpr Mat() noexcept - { - clear(); - } - - [[nodiscard]] - constexpr static MatStoreType get_store_ordering() noexcept - { - return StoreType; - } - 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"); - - auto row_it = rows.begin(); - for (size_t i = 0; i < Rows; ++i, ++row_it) - { - if (row_it->size() != Columns) - throw std::invalid_argument( - "All rows must have the same number of columns as template parameter Columns"); - - auto col_it = row_it->begin(); - for (size_t j = 0; j < Columns; ++j, ++col_it) - { - at(i, j) = std::move(*col_it); - } - } - } - - constexpr explicit Mat(const Type* raw_data) - { - std::copy_n(raw_data, Rows * Columns, m_data.begin()); - } - - constexpr Mat(const Mat& other) noexcept - { - m_data = other.m_data; - } - - [[nodiscard]] - constexpr Type& operator[](const size_t row, const size_t col) - { - return at(row, col); - } - - [[nodiscard]] - constexpr Type& operator[](const size_t row, const size_t col) const - { - return at(row, col); - } - - constexpr Mat(Mat&& other) noexcept - { - m_data = std::move(other.m_data); - } - - [[nodiscard]] - static constexpr size_t row_count() noexcept - { - return Rows; - } - - [[nodiscard]] - static constexpr size_t columns_count() noexcept - { - return Columns; - } - - [[nodiscard]] - static consteval MatSize size() noexcept - { - return {Rows, Columns}; - } - - [[nodiscard]] - constexpr const Type& at(const size_t row_index, const size_t column_index) const - { -#if !defined(NDEBUG) && defined(OMATH_SUPRESS_SAFETY_CHECKS) - if (row_index >= Rows || column_index >= Columns) - throw std::out_of_range("Index out of range"); -#endif - if constexpr (StoreType == MatStoreType::ROW_MAJOR) - return m_data[row_index * Columns + column_index]; - - else if constexpr (StoreType == MatStoreType::COLUMN_MAJOR) - return m_data[row_index + column_index * Rows]; - - else - { - static_assert(false, "Invalid matrix access convention"); - std::unreachable(); - } - } - - [[nodiscard]] constexpr Type& at(const size_t row_index, const size_t column_index) - { - return const_cast(std::as_const(*this).at(row_index, column_index)); - } - - [[nodiscard]] - constexpr Type sum() const noexcept - { - return std::accumulate(m_data.begin(), m_data.end(), static_cast(0)); - } - - constexpr void clear() noexcept - { - set(static_cast(0)); - } - - constexpr void set(const Type& value) noexcept - { - std::ranges::fill(m_data, value); - } - - // Operator overloading for multiplication with another Mat - template [[nodiscard]] - constexpr Mat - operator*(const Mat& other) const - { - Mat result; - - for (size_t i = 0; i < Rows; ++i) - for (size_t j = 0; j < OtherColumns; ++j) - { - Type sum = 0; - for (size_t k = 0; k < Columns; ++k) - sum += at(i, k) * other.at(k, j); - result.at(i, j) = sum; - } - return result; - } - - constexpr Mat& operator*=(const Type& f) noexcept - { - std::ranges::for_each(m_data, [&f](auto& val) { val *= f; }); - return *this; - } - - template constexpr Mat - operator*=(const Mat& other) - { - return *this = *this * other; - } - - [[nodiscard]] - constexpr Mat operator*(const Type& value) const noexcept - { - Mat result(*this); - result *= value; - return result; - } - - constexpr Mat& operator/=(const Type& value) noexcept - { - std::ranges::for_each(m_data, [&value](auto& val) { val /= value; }); - return *this; - } - - [[nodiscard]] - constexpr Mat operator/(const Type& value) const noexcept - { - Mat result(*this); - result /= value; - return result; - } - - constexpr Mat& operator=(const Mat& other) noexcept - { - if (this != &other) - m_data = other.m_data; - - return *this; - } - - constexpr Mat& operator=(Mat&& other) noexcept - { - if (this != &other) - m_data = std::move(other.m_data); - - return *this; - } - - [[nodiscard]] - constexpr Mat transposed() const noexcept - { - Mat transposed; - for (size_t i = 0; i < Rows; ++i) - for (size_t j = 0; j < Columns; ++j) - transposed.at(j, i) = at(i, j); - - return transposed; - } - - [[nodiscard]] - constexpr Type determinant() const - { - static_assert(Rows == Columns, "Determinant is only defined for square matrices."); - - if constexpr (Rows == 1) - return at(0, 0); - - if constexpr (Rows == 2) - return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0); - - if constexpr (Rows > 2) - { - Type det = 0; - for (size_t column = 0; column < Columns; ++column) - { - const Type cofactor = at(0, column) * alg_complement(0, column); - det += cofactor; - } - return det; - } - std::unreachable(); - } - - [[nodiscard]] - constexpr Mat strip(const size_t row, const size_t column) const - { - static_assert(Rows - 1 > 0 && Columns - 1 > 0); - Mat result; - for (size_t i = 0, m = 0; i < Rows; ++i) - { - if (i == row) - continue; - for (size_t j = 0, n = 0; j < Columns; ++j) - { - if (j == column) - continue; - result.at(m, n) = at(i, j); - ++n; - } - ++m; - } - return result; - } - - [[nodiscard]] - constexpr Type minor(const size_t row, const size_t column) const - { - return strip(row, column).determinant(); - } - - [[nodiscard]] - constexpr Type alg_complement(const size_t row, const size_t column) const - { - const auto minor_value = minor(row, column); - return (row + column + 2) % 2 == 0 ? minor_value : -minor_value; - } - - [[nodiscard]] - constexpr const std::array& raw_array() const - { - return m_data; - } - - [[nodiscard]] - constexpr std::array& raw_array() - { - return m_data; - } - - [[nodiscard]] - std::string to_string() const noexcept - { - std::ostringstream oss; - oss << "[["; - - for (size_t i = 0; i < Rows; ++i) - { - if (i > 0) - oss << " ["; - - for (size_t j = 0; j < Columns; ++j) - { - oss << std::setw(9) << std::fixed << std::setprecision(3) << at(i, j); - if (j != Columns - 1) - oss << ", "; - } - oss << (i == Rows - 1 ? "]]" : "]\n"); - } - return oss.str(); - } - - [[nodiscard]] - bool operator==(const Mat& mat) const - { - return m_data == mat.m_data; - } - - [[nodiscard]] - bool operator!=(const Mat& mat) const - { - return !operator==(mat); - } - - // Static methods that return fixed-size matrices - [[nodiscard]] - constexpr static Mat<4, 4> to_screen_mat(const Type& screen_width, const Type& screen_height) noexcept - { - return { - {screen_width / 2, 0, 0, 0}, - {0, -screen_height / 2, 0, 0}, - {0, 0, 1, 0}, - {screen_width / 2, screen_height / 2, 0, 1}, - }; - } - - [[nodiscard]] - constexpr std::optional inverted() const - { - const auto det = determinant(); - - if (det == 0) - return std::nullopt; - - const auto transposed_mat = transposed(); - Mat result; - - for (std::size_t row = 0; row < Rows; row++) - for (std::size_t column = 0; column < Rows; column++) - result.at(row, column) = transposed_mat.alg_complement(row, column); - - result /= det; - - return {result}; - } - - private: - std::array m_data; - }; - - template [[nodiscard]] - constexpr static Mat<1, 4, Type, St> mat_row_from_vector(const Vector3& vector) noexcept - { - return {{vector.x, vector.y, vector.z, 1}}; - } - - template [[nodiscard]] - constexpr static Mat<4, 1, Type, St> mat_column_from_vector(const Vector3& vector) noexcept - { - return {{vector.x}, {vector.y}, {vector.z}, {1}}; - } - - template - [[nodiscard]] - constexpr Mat<4, 4, Type, St> mat_translation(const Vector3& diff) noexcept - { - return - { - {1, 0, 0, diff.x}, - {0, 1, 0, diff.y}, - {0, 0, 1, diff.z}, - {0, 0, 0, 1}, - }; - } - - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_rotation_axis_x(const Angle& angle) noexcept - { - return - { - {1, 0, 0, 0}, - {0, angle.cos(), -angle.sin(), 0}, - {0, angle.sin(), angle.cos(), 0}, - {0, 0, 0, 1} - }; - } - - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_rotation_axis_y(const Angle& angle) noexcept - { - return - { - {angle.cos(), 0, angle.sin(), 0}, - {0 , 1, 0, 0}, - {-angle.sin(), 0, angle.cos(), 0}, - {0 , 0, 0, 1} - }; - } - - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_rotation_axis_z(const Angle& angle) noexcept - { - return - { - {angle.cos(), -angle.sin(), 0, 0}, - {angle.sin(), angle.cos(), 0, 0}, - { 0, 0, 1, 0}, - { 0, 0, 0, 1}, - }; - } - - template - [[nodiscard]] - static Mat<4, 4, Type, St> mat_camera_view(const Vector3& forward, const Vector3& right, - const Vector3& up, const Vector3& camera_origin) noexcept - { - return Mat<4, 4, Type, St> - { - {right.x, right.y, right.z, 0}, - {up.x, up.y, up.z, 0}, - {forward.x, forward.y, forward.z, 0}, - {0, 0, 0, 1}, - } * mat_translation(-camera_origin); - } - - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_perspective_left_handed(const float field_of_view, const float aspect_ratio, - const float near, const float far) noexcept - { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); - - return {{1.f / (aspect_ratio * fov_half_tan), 0.f, 0.f, 0.f}, - {0.f, 1.f / fov_half_tan, 0.f, 0.f}, - {0.f, 0.f, (far + near) / (far - near), -(2.f * near * far) / (far - near)}, - {0.f, 0.f, 1.f, 0.f}}; - } - - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_perspective_right_handed(const float field_of_view, const float aspect_ratio, - const float near, const float far) noexcept - { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); - - return {{1.f / (aspect_ratio * fov_half_tan), 0.f, 0.f, 0.f}, - {0.f, 1.f / fov_half_tan, 0.f, 0.f}, - {0.f, 0.f, -(far + near) / (far - near), -(2.f * near * far) / (far - near)}, - {0.f, 0.f, -1.f, 0.f}}; - } - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, - const Type bottom, const Type top, - const Type near, const Type far) noexcept - { - return - { - { static_cast(2) / (right - left), 0.f, 0.f, -(right + left) / (right - left)}, - { 0.f, static_cast(2) / (top - bottom), 0.f, -(top + bottom) / (top - bottom)}, - { 0.f, 0.f, static_cast(2) / (far - near), -(far + near) / (far - near) }, - { 0.f, 0.f, 0.f, 1.f } - }; - } - template - [[nodiscard]] - Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, - const Type bottom, const Type top, - const Type near, const Type far) noexcept - { - return - { - { static_cast(2) / (right - left), 0.f, 0.f, -(right + left) / (right - left)}, - { 0.f, static_cast(2) / (top - bottom), 0.f, -(top + bottom) / (top - bottom)}, - { 0.f, 0.f, -static_cast(2) / (far - near), -(far + near) / (far - near) }, - { 0.f, 0.f, 0.f, 1.f } - }; - } - -} // namespace omath - -template -struct std::formatter> // NOLINT(*-dcl58-cpp) -{ - using MatType = omath::Mat; - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const MatType& mat, std::format_context& ctx) - { - return std::format_to(ctx.out(), "{}", mat.to_string()); - } -}; \ No newline at end of file diff --git a/include/omath/linear_algebra/matrix.hpp b/include/omath/linear_algebra/matrix.hpp deleted file mode 100644 index 32e9a87f..00000000 --- a/include/omath/linear_algebra/matrix.hpp +++ /dev/null @@ -1,127 +0,0 @@ -#pragma once - -/* -THIS CODE IS DEPRECATED NEVER EVER USE Matrix CLASS -AND VERY SLOW USE Mat INSTEAD!!!!!!!!!!! -⠛⠛⣿⣿⣿⣿⣿⡷⢶⣦⣶⣶⣤⣤⣤⣀⠀⠀⠀ -⠀⠀⠀⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣷⡀⠀ -⠀⠀⠀⠉⠉⠉⠙⠻⣿⣿⠿⠿⠛⠛⠛⠻⣿⣿⣇⠀ -⠀⠀⢤⣀⣀⣀⠀⠀⢸⣷⡄⠀⣁⣀⣤⣴⣿⣿⣿⣆ -⠀⠀⠀⠀⠹⠏⠀⠀⠀⣿⣧⠀⠹⣿⣿⣿⣿⣿⡿⣿ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠛⠿⠇⢀⣼⣿⣿⠛⢯⡿⡟ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠦⠴⢿⢿⣿⡿⠷⠀⣿⠀ -⠀⠀⠀⠀⠀⠀⠀⠙⣷⣶⣶⣤⣤⣤⣤⣤⣶⣦⠃⠀ -⠀⠀⠀⠀⠀⠀⠀⢐⣿⣾⣿⣿⣿⣿⣿⣿⣿⣿⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠈⣿⣿⣿⣿⣿⣿⣿⣿⣿⡇⠀⠀ -⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠻⢿⣿⣿⣿⣿⠟⠁ -*/ - -#ifdef OMATH_ENABLE_LEGACY -#include "omath/vector3.hpp" -#include -#include -#include - -namespace omath -{ - - class Matrix final - { - public: - Matrix(); - Matrix(size_t rows, size_t columns); - - Matrix(const std::initializer_list>& rows); - - [[nodiscard]] - static Matrix to_screen_matrix(float screen_width, float screen_height); - - [[nodiscard]] - static Matrix translation_matrix(const Vector3& diff); - - [[nodiscard]] - static Matrix orientation_matrix(const Vector3& forward, const Vector3& right, - const Vector3& up); - - [[nodiscard]] - static Matrix projection_matrix(float field_of_view, float aspect_ratio, float near, float far); - - Matrix(const Matrix& other); - - Matrix(size_t rows, size_t columns, const float* raw_data); - - Matrix(Matrix&& other) noexcept; - - [[nodiscard]] - size_t row_count() const noexcept; - - [[nodiscard]] - float& operator[](size_t row, size_t column); - - [[nodiscard]] - size_t columns_count() const noexcept; - - [[nodiscard]] - std::pair size() const noexcept; - - [[nodiscard]] - float& at(size_t row, size_t col); - - [[nodiscard]] - float sum(); - - void set_data_from_raw(const float* raw_matrix); - - [[nodiscard]] - Matrix transpose() const; - - void set(float val); - - [[nodiscard]] - const float& at(size_t row, size_t col) const; - - Matrix operator*(const Matrix& other) const; - - Matrix& operator*=(const Matrix& other); - - Matrix operator*(float f) const; - - Matrix& operator*=(float f); - - Matrix& operator/=(float f); - - void clear(); - - [[nodiscard]] - Matrix strip(size_t row, size_t column) const; - - [[nodiscard]] - float minor(size_t i, size_t j) const; - - [[nodiscard]] - float alg_complement(size_t i, size_t j) const; - - [[nodiscard]] - float determinant() const; - - [[nodiscard]] - const float* raw() const; - - Matrix& operator=(const Matrix& other); - - Matrix& operator=(Matrix&& other) noexcept; - - Matrix operator/(float f) const; - - [[nodiscard]] - std::string to_string() const; - - ~Matrix(); - - private: - size_t m_rows; - size_t m_columns; - std::unique_ptr m_data; - }; -} // namespace omath -#endif diff --git a/include/omath/linear_algebra/vector2.hpp b/include/omath/linear_algebra/vector2.hpp deleted file mode 100644 index 7e8ddc83..00000000 --- a/include/omath/linear_algebra/vector2.hpp +++ /dev/null @@ -1,250 +0,0 @@ -// -// Created by Vlad on 02.09.2024. -// - -#pragma once -#include -#include -#include - -#ifdef OMATH_IMGUI_INTEGRATION -#include -#endif - -namespace omath -{ - - template - requires std::is_arithmetic_v - class Vector2 - { - public: - Type x = static_cast(0); - Type y = static_cast(0); - - // Constructors - constexpr Vector2() = default; - - constexpr Vector2(const Type& x, const Type& y) noexcept: x(x), y(y) - { - } - - // Equality operators - [[nodiscard]] - constexpr bool operator==(const Vector2& other) const noexcept - { - return x == other.x && y == other.y; - } - - [[nodiscard]] - constexpr bool operator!=(const Vector2& other) const noexcept - { - return !(*this == other); - } - - // Compound assignment operators - constexpr Vector2& operator+=(const Vector2& other) noexcept - { - x += other.x; - y += other.y; - - return *this; - } - - constexpr Vector2& operator-=(const Vector2& other) noexcept - { - x -= other.x; - y -= other.y; - - return *this; - } - - constexpr Vector2& operator*=(const Vector2& other) noexcept - { - x *= other.x; - y *= other.y; - - return *this; - } - - constexpr Vector2& operator/=(const Vector2& other) noexcept - { - x /= other.x; - y /= other.y; - - return *this; - } - - constexpr Vector2& operator*=(const Type& value) noexcept - { - x *= value; - y *= value; - - return *this; - } - - constexpr Vector2& operator/=(const Type& value) noexcept - { - x /= value; - y /= value; - - return *this; - } - - constexpr Vector2& operator+=(const Type& value) noexcept - { - x += value; - y += value; - - return *this; - } - - constexpr Vector2& operator-=(const Type& value) noexcept - { - x -= value; - y -= value; - - return *this; - } - - // Basic vector operations - [[nodiscard]] Type distance_to(const Vector2& other) const noexcept - { - return std::sqrt(distance_to_sqr(other)); - } - - [[nodiscard]] constexpr Type distance_to_sqr(const Vector2& other) const noexcept - { - return (x - other.x) * (x - other.x) + (y - other.y) * (y - other.y); - } - - [[nodiscard]] constexpr Type dot(const Vector2& other) const noexcept - { - return x * other.x + y * other.y; - } - -#ifndef _MSC_VER - [[nodiscard]] constexpr Type length() const noexcept - { - return std::hypot(this->x, this->y); - } - - [[nodiscard]] constexpr Vector2 normalized() const noexcept - { - const Type len = length(); - return len > 0.f ? *this / len : *this; - } -#else - [[nodiscard]] Type length() const noexcept - { - return std::hypot(x, y); - } - - [[nodiscard]] Vector2 normalized() const noexcept - { - const Type len = length(); - return len > static_cast(0) ? *this / len : *this; - } -#endif - [[nodiscard]] constexpr Type length_sqr() const noexcept - { - return x * x + y * y; - } - - constexpr Vector2& abs() noexcept - { - // FIXME: Replace with std::abs, if it will become constexprable - x = x < static_cast(0) ? -x : x; - y = y < static_cast(0) ? -y : y; - return *this; - } - - [[nodiscard]] constexpr Vector2 operator-() const noexcept - { - return {-x, -y}; - } - - // Binary arithmetic operators - [[nodiscard]] constexpr Vector2 operator+(const Vector2& other) const noexcept - { - return {x + other.x, y + other.y}; - } - - [[nodiscard]] constexpr Vector2 operator-(const Vector2& other) const noexcept - { - return {x - other.x, y - other.y}; - } - - [[nodiscard]] constexpr Vector2 operator*(const Type& value) const noexcept - { - return {x * value, y * value}; - } - - [[nodiscard]] constexpr Vector2 operator/(const Type& value) const noexcept - { - return {x / value, y / value}; - } - - // Sum of elements - [[nodiscard]] constexpr Type sum() const noexcept - { - return x + y; - } - - [[nodiscard]] - bool operator<(const Vector2& other) const noexcept - { - return length() < other.length(); - } - [[nodiscard]] - bool operator>(const Vector2& other) const noexcept - { - return length() > other.length(); - } - - [[nodiscard]] - bool operator<=(const Vector2& other) const noexcept - { - return length() <= other.length(); - } - - [[nodiscard]] - bool operator>=(const Vector2& other) const noexcept - { - return length() >= other.length(); - } - - [[nodiscard]] - constexpr std::tuple as_tuple() const noexcept - { - return std::make_tuple(x, y); - } -#ifdef OMATH_IMGUI_INTEGRATION - [[nodiscard]] - ImVec2 to_im_vec2() const noexcept - { - return {static_cast(this->x), static_cast(this->y)}; - } - [[nodiscard]] - static Vector2 from_im_vec2(const ImVec2& other) noexcept - { - return {static_cast(other.x), static_cast(other.y)}; - } -#endif - }; -} // namespace omath - -template -struct std::formatter> // NOLINT(*-dcl58-cpp) -{ - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const omath::Vector2& vec, std::format_context& ctx) - { - return std::format_to(ctx.out(), "[{}, {}]", vec.x, vec.y); - } -}; diff --git a/include/omath/linear_algebra/vector3.hpp b/include/omath/linear_algebra/vector3.hpp deleted file mode 100644 index e5f4fdaf..00000000 --- a/include/omath/linear_algebra/vector3.hpp +++ /dev/null @@ -1,311 +0,0 @@ -// -// Created by vlad on 10/28/23. -// - -#pragma once - -#include "omath/angle.hpp" -#include "omath/linear_algebra/vector2.hpp" -#include -#include -#include - -namespace omath -{ - - enum class Vector3Error - { - IMPOSSIBLE_BETWEEN_ANGLE, - }; - - template - requires std::is_arithmetic_v - class Vector3 : public Vector2 - { - public: - Type z = static_cast(0); - constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept: Vector2(x, y), z(z) - { - } - constexpr Vector3() noexcept: Vector2() {}; - - [[nodiscard]] constexpr bool operator==(const Vector3& other) const noexcept - { - return Vector2::operator==(other) && (other.z == z); - } - - [[nodiscard]] constexpr bool operator!=(const Vector3& other) const noexcept - { - return !(*this == other); - } - - constexpr Vector3& operator+=(const Vector3& other) noexcept - { - Vector2::operator+=(other); - z += other.z; - - return *this; - } - - constexpr Vector3& operator-=(const Vector3& other) noexcept - { - Vector2::operator-=(other); - z -= other.z; - - return *this; - } - - constexpr Vector3& operator*=(const Type& value) noexcept - { - Vector2::operator*=(value); - z *= value; - - return *this; - } - - constexpr Vector3& operator*=(const Vector3& other) noexcept - { - Vector2::operator*=(other); - z *= other.z; - - return *this; - } - - constexpr Vector3& operator/=(const Vector3& other) noexcept - { - Vector2::operator/=(other); - z /= other.z; - - return *this; - } - - constexpr Vector3& operator+=(const Type& value) noexcept - { - Vector2::operator+=(value); - z += value; - - return *this; - } - - constexpr Vector3& operator/=(const Type& value) noexcept - { - Vector2::operator/=(value); - z /= value; - - return *this; - } - - constexpr Vector3& operator-=(const Type& value) noexcept - { - Vector2::operator-=(value); - z -= value; - - return *this; - } - - constexpr Vector3& abs() noexcept - { - Vector2::abs(); - z = z < 0.f ? -z : z; - - return *this; - } - - [[nodiscard]] constexpr Type distance_to_sqr(const Vector3& other) const noexcept - { - return (*this - other).length_sqr(); - } - - [[nodiscard]] constexpr Type dot(const Vector3& other) const noexcept - { - return Vector2::dot(other) + z * other.z; - } - -#ifndef _MSC_VER - [[nodiscard]] constexpr Type length() const - { - return std::hypot(this->x, this->y, z); - } - - [[nodiscard]] constexpr Type length_2d() const - { - return Vector2::length(); - } - [[nodiscard]] Type distance_to(const Vector3& other) const - { - return (*this - other).length(); - } - [[nodiscard]] constexpr Vector3 normalized() const - { - const Type length_value = this->length(); - - return length_value != 0 ? *this / length_value : *this; - } -#else - [[nodiscard]] Type length() const noexcept - { - return std::hypot(this->x, this->y, z); - } - - [[nodiscard]] Vector3 normalized() const noexcept - { - const Type len = this->length(); - - return len != static_cast(0) ? *this / len : *this; - } - - [[nodiscard]] Type length_2d() const noexcept - { - return Vector2::length(); - } - - [[nodiscard]] Type distance_to(const Vector3& v_other) const noexcept - { - return (*this - v_other).length(); - } -#endif - - [[nodiscard]] constexpr Type length_sqr() const noexcept - { - return Vector2::length_sqr() + z * z; - } - - [[nodiscard]] constexpr Vector3 operator-() const noexcept - { - return {-this->x, -this->y, -z}; - } - - [[nodiscard]] constexpr Vector3 operator+(const Vector3& other) const noexcept - { - return {this->x + other.x, this->y + other.y, z + other.z}; - } - - [[nodiscard]] constexpr Vector3 operator-(const Vector3& other) const noexcept - { - return {this->x - other.x, this->y - other.y, z - other.z}; - } - - [[nodiscard]] constexpr Vector3 operator*(const Type& value) const noexcept - { - return {this->x * value, this->y * value, z * value}; - } - - [[nodiscard]] constexpr Vector3 operator*(const Vector3& other) const noexcept - { - return {this->x * other.x, this->y * other.y, z * other.z}; - } - - [[nodiscard]] constexpr Vector3 operator/(const Type& value) const noexcept - { - return {this->x / value, this->y / value, z / value}; - } - - [[nodiscard]] constexpr Vector3 operator/(const Vector3& other) const noexcept - { - return {this->x / other.x, this->y / other.y, z / other.z}; - } - - [[nodiscard]] constexpr Vector3 cross(const Vector3& other) const noexcept - { - return {this->y * other.z - z * other.y, z * other.x - this->x * other.z, - this->x * other.y - this->y * other.x}; - } - - [[nodiscard]] constexpr Type sum() const noexcept - { - return sum_2d() + z; - } - - [[nodiscard]] std::expected, Vector3Error> - angle_between(const Vector3& other) const noexcept - { - const auto bottom = length() * other.length(); - - if (bottom == static_cast(0)) - return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE); - - return Angle::from_radians(std::acos(dot(other) / bottom)); - } - - [[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept - { - if (const auto angle = angle_between(other)) - return angle->as_degrees() == static_cast(90); - - return false; - } - - [[nodiscard]] constexpr Type sum_2d() const noexcept - { - return Vector2::sum(); - } - - [[nodiscard]] constexpr std::tuple as_tuple() const noexcept - { - return std::make_tuple(this->x, this->y, z); - } - - [[nodiscard]] Vector3 view_angle_to(const Vector3& other) const noexcept - { - const auto distance = distance_to(other); - const auto delta = other - *this; - - return {angles::radians_to_degrees(std::asin(delta.z / distance)), - angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0}; - } - - [[nodiscard]] - bool operator<(const Vector3& other) const noexcept - { - return length() < other.length(); - } - - [[nodiscard]] - bool operator>(const Vector3& other) const noexcept - { - return length() > other.length(); - } - - [[nodiscard]] - bool operator<=(const Vector3& other) const noexcept - { - return length() <= other.length(); - } - - [[nodiscard]] - bool operator>=(const Vector3& other) const noexcept - { - return length() >= other.length(); - } - }; -} // namespace omath - -template<> struct std::hash> -{ - std::size_t operator()(const omath::Vector3& vec) const noexcept - { - std::size_t hash = 0; - constexpr std::hash hasher; - - hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2); - hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2); - hash ^= hasher(vec.z) + 0x9e3779b9 + (hash << 6) + (hash >> 2); - - return hash; - } -}; - -template -struct std::formatter> // NOLINT(*-dcl58-cpp) -{ - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const omath::Vector3& vec, std::format_context& ctx) - { - return std::format_to(ctx.out(), "[{}, {}, {}]", vec.x, vec.y, vec.z); - } -}; diff --git a/include/omath/linear_algebra/vector4.hpp b/include/omath/linear_algebra/vector4.hpp deleted file mode 100644 index 95c095f6..00000000 --- a/include/omath/linear_algebra/vector4.hpp +++ /dev/null @@ -1,218 +0,0 @@ -// -// Vector4.h -// -#pragma once - -#include -#include "omath/linear_algebra/vector3.hpp" - -namespace omath -{ - template - requires std::is_arithmetic_v - class Vector4 : public Vector3 - { - public: - Type w; - - constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3(x, y, z), w(w) - { - } - constexpr Vector4() noexcept: Vector3(), w(static_cast(0)) {}; - - [[nodiscard]] - constexpr bool operator==(const Vector4& other) const noexcept - { - return Vector3::operator==(other) && w == other.w; - } - - [[nodiscard]] - constexpr bool operator!=(const Vector4& other) const noexcept - { - return !(*this == other); - } - - constexpr Vector4& operator+=(const Vector4& other) noexcept - { - Vector3::operator+=(other); - w += other.w; - - return *this; - } - - constexpr Vector4& operator-=(const Vector4& other) noexcept - { - Vector3::operator-=(other); - w -= other.w; - - return *this; - } - - constexpr Vector4& operator*=(const Type& value) noexcept - { - Vector3::operator*=(value); - w *= value; - - return *this; - } - - constexpr Vector4& operator*=(const Vector4& other) noexcept - { - Vector3::operator*=(other); - w *= other.w; - - return *this; - } - - constexpr Vector4& operator/=(const Type& value) noexcept - { - Vector3::operator/=(value); - w /= value; - - return *this; - } - - constexpr Vector4& operator/=(const Vector4& other) noexcept - { - Vector3::operator/=(other); - w /= other.w; - return *this; - } - - [[nodiscard]] constexpr Type length_sqr() const noexcept - { - return Vector3::length_sqr() + w * w; - } - - [[nodiscard]] constexpr Type dot(const Vector4& other) const noexcept - { - return Vector3::dot(other) + w * other.w; - } - - [[nodiscard]] Type length() const noexcept - { - return std::sqrt(length_sqr()); - } - - constexpr Vector4& abs() noexcept - { - Vector3::abs(); - w = w < 0.f ? -w : w; - - return *this; - } - constexpr Vector4& clamp(const Type& min, const Type& max) noexcept - { - this->x = std::clamp(this->x, min, max); - this->y = std::clamp(this->y, min, max); - this->z = std::clamp(this->z, min, max); - - return *this; - } - - [[nodiscard]] - constexpr Vector4 operator-() const noexcept - { - return {-this->x, -this->y, -this->z, -w}; - } - - [[nodiscard]] - constexpr Vector4 operator+(const Vector4& other) const noexcept - { - return {this->x + other.x, this->y + other.y, this->z + other.z, w + other.w}; - } - - [[nodiscard]] - constexpr Vector4 operator-(const Vector4& other) const noexcept - { - return {this->x - other.x, this->y - other.y, this->z - other.z, w - other.w}; - } - - [[nodiscard]] - constexpr Vector4 operator*(const Type& value) const noexcept - { - return {this->x * value, this->y * value, this->z * value, w * value}; - } - - [[nodiscard]] - constexpr Vector4 operator*(const Vector4& other) const noexcept - { - return {this->x * other.x, this->y * other.y, this->z * other.z, w * other.w}; - } - - [[nodiscard]] - constexpr Vector4 operator/(const Type& value) const noexcept - { - return {this->x / value, this->y / value, this->z / value, w / value}; - } - - [[nodiscard]] - constexpr Vector4 operator/(const Vector4& other) const noexcept - { - return {this->x / other.x, this->y / other.y, this->z / other.z, w / other.w}; - } - - [[nodiscard]] - constexpr Type sum() const noexcept - { - return Vector3::sum() + w; - } - - [[nodiscard]] - bool operator<(const Vector4& other) const noexcept - { - return length() < other.length(); - } - - [[nodiscard]] - bool operator>(const Vector4& other) const noexcept - { - return length() > other.length(); - } - - [[nodiscard]] - bool operator<=(const Vector4& other) const noexcept - { - return length() <= other.length(); - } - - [[nodiscard]] - bool operator>=(const Vector4& other) const noexcept - { - return length() >= other.length(); - } - -#ifdef OMATH_IMGUI_INTEGRATION - [[nodiscard]] - ImVec4 to_im_vec4() const noexcept - { - return { - static_cast(this->x), - static_cast(this->y), - static_cast(this->z), - static_cast(w), - }; - } - [[nodiscard]] - static Vector4 from_im_vec4(const ImVec4& other) noexcept - { - return {static_cast(other.x), static_cast(other.y), static_cast(other.z)}; - } -#endif -}; -} // namespace omath - -template -struct std::formatter> // NOLINT(*-dcl58-cpp) -{ - [[nodiscard]] - static constexpr auto parse(std::format_parse_context& ctx) - { - return ctx.begin(); - } - [[nodiscard]] - static auto format(const omath::Vector4& vec, std::format_context& ctx) - { - return std::format_to(ctx.out(), "[{}, {}, {}, {}]", vec.x, vec.y, vec.z, vec.w); - } -}; \ No newline at end of file diff --git a/include/omath/omath.hpp b/include/omath/omath.hpp deleted file mode 100644 index a867dd17..00000000 --- a/include/omath/omath.hpp +++ /dev/null @@ -1,84 +0,0 @@ -// -// omath.hpp - Main header file that includes all omath library components -// Created for the omath library -// - -#pragma once - -// Basic math utilities -#include "omath/angles.hpp" -#include "omath/angle.hpp" - -// Vector classes (in dependency order) -#include "omath/linear_algebra/vector2.hpp" -#include "omath/linear_algebra/vector4.hpp" -#include "omath/linear_algebra/vector3.hpp" - -// Matrix classes -#include "linear_algebra/matrix.hpp" -#include "omath/linear_algebra/mat.hpp" - -// Color functionality -#include "omath/color.hpp" - -// Geometric primitives -#include "omath/triangle.hpp" -#include "omath/view_angles.hpp" - -// 3D primitives -#include "omath/3d_primitives/box.hpp" - -// Collision detection -#include "omath/collision/line_tracer.hpp" - -// Pathfinding algorithms -#include "omath/pathfinding/a_star.hpp" -#include "omath/pathfinding/navigation_mesh.hpp" - -// Projectile prediction -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" -#include "omath/projectile_prediction/proj_pred_engine.hpp" -#include "omath/projectile_prediction/proj_pred_engine_legacy.hpp" -#include "omath/projectile_prediction/proj_pred_engine_avx2.hpp" - -// Projection functionality -#include "omath/projection/error_codes.hpp" -#include "omath/projection/camera.hpp" - -// Engine-specific implementations - -// IW Engine -#include "omath/engines/iw_engine/constants.hpp" -#include "omath/engines/iw_engine/formulas.hpp" -#include "omath/engines/iw_engine/camera.hpp" -#include "omath/engines/iw_engine/traits/camera_trait.hpp" -#include "omath/engines/iw_engine/traits/pred_engine_trait.hpp" - -// OpenGL Engine -#include "omath/engines/opengl_engine/constants.hpp" -#include "omath/engines/opengl_engine/formulas.hpp" -#include "omath/engines/opengl_engine/camera.hpp" -#include "omath/engines/opengl_engine/traits/camera_trait.hpp" -#include "omath/engines/opengl_engine/traits/pred_engine_trait.hpp" - -// Source Engine -#include "omath/engines/source_engine/constants.hpp" -#include "omath/engines/source_engine/formulas.hpp" -#include "omath/engines/source_engine/camera.hpp" -#include "omath/engines/source_engine/traits/camera_trait.hpp" -#include "omath/engines/source_engine/traits/pred_engine_trait.hpp" - -// Unity Engine -#include "omath/engines/unity_engine/constants.hpp" -#include "omath/engines/unity_engine/formulas.hpp" -#include "omath/engines/unity_engine/camera.hpp" -#include "omath/engines/unity_engine/traits/camera_trait.hpp" -#include "omath/engines/unity_engine/traits/pred_engine_trait.hpp" - -// Unreal Engine -#include "omath/engines/unreal_engine/constants.hpp" -#include "omath/engines/unreal_engine/formulas.hpp" -#include "omath/engines/unreal_engine/camera.hpp" -#include "omath/engines/unreal_engine/traits/camera_trait.hpp" -#include "omath/engines/unreal_engine/traits/pred_engine_trait.hpp" \ No newline at end of file diff --git a/include/omath/pathfinding/a_star.hpp b/include/omath/pathfinding/a_star.hpp deleted file mode 100644 index 218a059c..00000000 --- a/include/omath/pathfinding/a_star.hpp +++ /dev/null @@ -1,30 +0,0 @@ -// -// Created by Vlad on 28.07.2024. -// - -#pragma once -#include "omath/linear_algebra/vector3.hpp" -#include "omath/pathfinding/navigation_mesh.hpp" -#include - -namespace omath::pathfinding -{ - struct PathNode; - class Astar final - { - public: - [[nodiscard]] - static std::vector> find_path(const Vector3& start, const Vector3& end, - const NavigationMesh& nav_mesh) noexcept; - - private: - [[nodiscard]] - static std::vector> - reconstruct_final_path(const std::unordered_map, PathNode>& closed_list, - const Vector3& current) noexcept; - - [[nodiscard]] - static auto get_perfect_node(const std::unordered_map, PathNode>& open_list, - const Vector3& end_vertex) noexcept; - }; -} // namespace omath::pathfinding diff --git a/include/omath/pathfinding/navigation_mesh.hpp b/include/omath/pathfinding/navigation_mesh.hpp deleted file mode 100644 index 35cc6e6b..00000000 --- a/include/omath/pathfinding/navigation_mesh.hpp +++ /dev/null @@ -1,37 +0,0 @@ -// -// Created by Vlad on 28.07.2024. -// - -#pragma once - -#include "omath/linear_algebra/vector3.hpp" -#include -#include -#include - -namespace omath::pathfinding -{ - - enum Error - { - }; - - class NavigationMesh final - { - public: - [[nodiscard]] - std::expected, std::string> get_closest_vertex(const Vector3& point) const noexcept; - - [[nodiscard]] - const std::vector>& get_neighbors(const Vector3& vertex) const noexcept; - - [[nodiscard]] - bool empty() const; - - [[nodiscard]] std::vector serialize() const noexcept; - - void deserialize(const std::vector& raw) noexcept; - - std::unordered_map, std::vector>> m_vertex_map; - }; -} // namespace omath::pathfinding diff --git a/include/omath/projectile_prediction/proj_pred_engine.hpp b/include/omath/projectile_prediction/proj_pred_engine.hpp deleted file mode 100644 index bbd5a546..00000000 --- a/include/omath/projectile_prediction/proj_pred_engine.hpp +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by Vlad on 2/23/2025. -// -#pragma once -#include "omath/linear_algebra/vector3.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" - -namespace omath::projectile_prediction -{ - class ProjPredEngineInterface - { - public: - [[nodiscard]] - virtual std::optional> maybe_calculate_aim_point(const Projectile& projectile, - const Target& target) const = 0; - virtual ~ProjPredEngineInterface() = default; - }; -} // namespace omath::projectile_prediction diff --git a/include/omath/projectile_prediction/proj_pred_engine_avx2.hpp b/include/omath/projectile_prediction/proj_pred_engine_avx2.hpp deleted file mode 100644 index e4a7dc51..00000000 --- a/include/omath/projectile_prediction/proj_pred_engine_avx2.hpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Vlad on 2/23/2025. -// -#pragma once -#include "omath/projectile_prediction/proj_pred_engine.hpp" - -namespace omath::projectile_prediction -{ - class ProjPredEngineAvx2 final : public ProjPredEngineInterface - { - public: - [[nodiscard]] std::optional> - maybe_calculate_aim_point(const Projectile& projectile, const Target& target) const override; - - ProjPredEngineAvx2(float gravity_constant, float simulation_time_step, float maximum_simulation_time); - ~ProjPredEngineAvx2() override = default; - - private: - [[nodiscard]] static std::optional calculate_pitch(const Vector3& proj_origin, - const Vector3& target_pos, - float bullet_gravity, float v0, float time) ; - - // We use [[maybe_unused]] here since AVX2 is not available for ARM and ARM64 CPU - [[maybe_unused]] const float m_gravity_constant; - [[maybe_unused]] const float m_simulation_time_step; - [[maybe_unused]] const float m_maximum_simulation_time; - }; -} // namespace omath::projectile_prediction diff --git a/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp b/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp deleted file mode 100644 index 231db3b7..00000000 --- a/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp +++ /dev/null @@ -1,134 +0,0 @@ -// -// Created by Vlad on 6/9/2024. -// - -#pragma once - -#include "omath/linear_algebra/vector3.hpp" -#include "omath/engines/source_engine/traits/pred_engine_trait.hpp" -#include "omath/projectile_prediction/proj_pred_engine.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" -#include - -namespace omath::projectile_prediction -{ - template - concept PredEngineConcept = - requires(const Projectile& projectile, const Target& target, const Vector3& vec_a, - const Vector3& vec_b, - Vector3 v3, // by-value for calc_viewpoint_from_angles - float pitch, float yaw, float time, float gravity, std::optional maybe_pitch) { - // Presence + return types - { T::predict_projectile_position(projectile, pitch, yaw, time, gravity) } -> std::same_as>; - { T::predict_target_position(target, time, gravity) } -> std::same_as>; - { T::calc_vector_2d_distance(vec_a) } -> std::same_as; - { T::get_vector_height_coordinate(vec_b) } -> std::same_as; - { T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as>; - { T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as; - { T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as; - - // Enforce noexcept as in PredEngineTrait - requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity)); - requires noexcept(T::predict_target_position(target, time, gravity)); - requires noexcept(T::calc_vector_2d_distance(vec_a)); - requires noexcept(T::get_vector_height_coordinate(vec_b)); - requires noexcept(T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch)); - requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); - requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); - }; - template - requires PredEngineConcept - class ProjPredEngineLegacy final : public ProjPredEngineInterface - { - public: - explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, - const float maximum_simulation_time, const float distance_tolerance) - : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), - m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) - { - } - - [[nodiscard]] - std::optional> maybe_calculate_aim_point(const Projectile& projectile, - const Target& target) const override - { - for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) - { - const auto predicted_target_position = - EngineTrait::predict_target_position(target, time, m_gravity_constant); - - const auto projectile_pitch = - maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); - - if (!projectile_pitch.has_value()) [[unlikely]] - continue; - - if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), - time)) - continue; - - return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); - } - return std::nullopt; - } - - private: - const float m_gravity_constant; - const float m_simulation_time_step; - const float m_maximum_simulation_time; - const float m_distance_tolerance; - - // Realization of this formula: - // https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target - /* - \[ - \theta \;=\; \arctan\!\Biggl( - \frac{% - v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,} - }{% - gx - }\Biggr) - \] - */ - [[nodiscard]] - std::optional - maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, - const Vector3& target_position) const noexcept - { - const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; - - if (bullet_gravity == 0.f) - return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position); - - const auto delta = target_position - projectile.m_origin; - - const auto distance2d = EngineTrait::calc_vector_2d_distance(delta); - const auto distance2d_sqr = distance2d * distance2d; - const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; - - float root = launch_speed_sqr * launch_speed_sqr - - bullet_gravity - * (bullet_gravity * distance2d_sqr - + 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr); - - if (root < 0.0f) [[unlikely]] - return std::nullopt; - - root = std::sqrt(root); - const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); - - return angles::radians_to_degrees(angle); - } - [[nodiscard]] - bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, - const float pitch, const float time) const noexcept - { - const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position); - const auto projectile_position = - EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant); - - return projectile_position.distance_to(target_position) <= m_distance_tolerance; - } - }; -} // namespace omath::projectile_prediction diff --git a/include/omath/projectile_prediction/projectile.hpp b/include/omath/projectile_prediction/projectile.hpp deleted file mode 100644 index c4560ed4..00000000 --- a/include/omath/projectile_prediction/projectile.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// Created by Vlad on 6/9/2024. -// - -#pragma once -#include "omath/linear_algebra/vector3.hpp" - -namespace omath::projectile_prediction -{ - class Projectile final - { - public: - Vector3 m_origin; - float m_launch_speed{}; - float m_gravity_scale{}; - }; -} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/include/omath/projectile_prediction/target.hpp b/include/omath/projectile_prediction/target.hpp deleted file mode 100644 index 5b3f5db2..00000000 --- a/include/omath/projectile_prediction/target.hpp +++ /dev/null @@ -1,17 +0,0 @@ -// -// Created by Vlad on 6/9/2024. -// - -#pragma once -#include "omath/linear_algebra/vector3.hpp" - -namespace omath::projectile_prediction -{ - class Target final - { - public: - Vector3 m_origin; - Vector3 m_velocity; - bool m_is_airborne{}; - }; -} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/include/omath/projection/camera.hpp b/include/omath/projection/camera.hpp deleted file mode 100644 index be44e8e8..00000000 --- a/include/omath/projection/camera.hpp +++ /dev/null @@ -1,204 +0,0 @@ -// -// Created by Vlad on 27.08.2024. -// - -#pragma once - -#include "omath/projection/error_codes.hpp" -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include -#include - -namespace omath::projection -{ - class ViewPort final - { - public: - float m_width; - float m_height; - - [[nodiscard]] constexpr float aspect_ratio() const - { - return m_width / m_height; - } - }; - using FieldOfView = Angle; - - template - concept CameraEngineConcept = - requires(const Vector3& cam_origin, const Vector3& look_at, const ViewAnglesType& angles, - const FieldOfView& fov, const ViewPort& viewport, float znear, float zfar) { - // Presence + return types - { T::calc_look_at_angle(cam_origin, look_at) } -> std::same_as; - { T::calc_view_matrix(angles, cam_origin) } -> std::same_as; - { T::calc_projection_matrix(fov, viewport, znear, zfar) } -> std::same_as; - - // Enforce noexcept as in the trait declaration - requires noexcept(T::calc_look_at_angle(cam_origin, look_at)); - requires noexcept(T::calc_view_matrix(angles, cam_origin)); - requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar)); - }; - - template - requires CameraEngineConcept - class Camera final - { - public: - ~Camera() = default; - Camera(const Vector3& position, const ViewAnglesType& view_angles, const ViewPort& view_port, - const FieldOfView& fov, const float near, const float far) noexcept - : m_view_port(view_port), m_field_of_view(fov), m_far_plane_distance(far), m_near_plane_distance(near), - m_view_angles(view_angles), m_origin(position) - { - } - - protected: - void look_at(const Vector3& target) - { - m_view_angles = TraitClass::calc_look_at_angle(m_origin, target); - } - - [[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept - { - return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance, - m_far_plane_distance) - * TraitClass::calc_view_matrix(m_view_angles, m_origin); - } - - public: - [[nodiscard]] const Mat4X4Type& get_view_projection_matrix() const noexcept - { - if (!m_view_projection_matrix.has_value()) - m_view_projection_matrix = calc_view_projection_matrix(); - - return m_view_projection_matrix.value(); - } - - void set_field_of_view(const FieldOfView& fov) noexcept - { - m_field_of_view = fov; - m_view_projection_matrix = std::nullopt; - } - - void set_near_plane(const float near) noexcept - { - m_near_plane_distance = near; - m_view_projection_matrix = std::nullopt; - } - - void set_far_plane(const float far) noexcept - { - m_far_plane_distance = far; - m_view_projection_matrix = std::nullopt; - } - - void set_view_angles(const ViewAnglesType& view_angles) noexcept - { - m_view_angles = view_angles; - m_view_projection_matrix = std::nullopt; - } - - void set_origin(const Vector3& origin) noexcept - { - m_origin = origin; - m_view_projection_matrix = std::nullopt; - } - - void set_view_port(const ViewPort& view_port) noexcept - { - m_view_port = view_port; - m_view_projection_matrix = std::nullopt; - } - - [[nodiscard]] const FieldOfView& get_field_of_view() const noexcept - { - return m_field_of_view; - } - - [[nodiscard]] const float& get_near_plane() const noexcept - { - return m_near_plane_distance; - } - - [[nodiscard]] const float& get_far_plane() const noexcept - { - return m_far_plane_distance; - } - - [[nodiscard]] const ViewAnglesType& get_view_angles() const noexcept - { - return m_view_angles; - } - - [[nodiscard]] const Vector3& get_origin() const noexcept - { - return m_origin; - } - - [[nodiscard]] std::expected, Error> - world_to_screen(const Vector3& world_position) const noexcept - { - auto normalized_cords = world_to_view_port(world_position); - - if (!normalized_cords.has_value()) - return std::unexpected{normalized_cords.error()}; - - return ndc_to_screen_position(*normalized_cords); - } - - [[nodiscard]] std::expected, Error> - world_to_view_port(const Vector3& world_position) const noexcept - { - auto projected = get_view_projection_matrix() - * mat_column_from_vector(world_position); - - if (projected.at(3, 0) == 0.0f) - return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); - - projected /= projected.at(3, 0); - - if (is_ndc_out_of_bounds(projected)) - return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); - - return Vector3{projected.at(0, 0), projected.at(1, 0), projected.at(2, 0)}; - } - - protected: - ViewPort m_view_port{}; - Angle m_field_of_view; - - mutable std::optional m_view_projection_matrix; - - float m_far_plane_distance; - float m_near_plane_distance; - - ViewAnglesType m_view_angles; - Vector3 m_origin; - - private: - template - [[nodiscard]] constexpr static bool is_ndc_out_of_bounds(const Type& ndc) noexcept - { - return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; }); - } - - [[nodiscard]] Vector3 ndc_to_screen_position(const Vector3& ndc) const noexcept - { -/* - ^ - | y - 1 | - | - | - -1 ---------0--------- 1 --> x - | - | - -1 | - v -*/ - return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (1.f - ndc.y) / 2.f * m_view_port.m_height, ndc.z}; - } - }; -} // namespace omath::projection diff --git a/include/omath/projection/error_codes.hpp b/include/omath/projection/error_codes.hpp deleted file mode 100644 index ae29fc07..00000000 --- a/include/omath/projection/error_codes.hpp +++ /dev/null @@ -1,14 +0,0 @@ -// -// Created by Vlad on 03.09.2024. -// - -#pragma once -#include - -namespace omath::projection -{ - enum class Error : uint16_t - { - WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS, - }; -} \ No newline at end of file diff --git a/include/omath/triangle.hpp b/include/omath/triangle.hpp deleted file mode 100644 index 519c48cb..00000000 --- a/include/omath/triangle.hpp +++ /dev/null @@ -1,85 +0,0 @@ -// -// Created by Orange on 11/13/2024. -// -#pragma once -#include "linear_algebra/vector3.hpp" - -namespace omath -{ - /* - v1 - |\ - | \ - a | \ hypot - | \ - v2 ----- v3 - b - */ - - template - class Triangle final - { - public: - constexpr Triangle() = default; - constexpr Triangle(const Vector& vertex1, const Vector& vertex2, const Vector& vertex3) - : m_vertex1(vertex1), m_vertex2(vertex2), m_vertex3(vertex3) - { - } - - Vector3 m_vertex1; - Vector3 m_vertex2; - Vector3 m_vertex3; - - [[nodiscard]] - constexpr Vector3 calculate_normal() const - { - const auto b = side_b_vector(); - const auto a = side_a_vector(); - - return b.cross(a).normalized(); - } - - [[nodiscard]] - float side_a_length() const - { - return m_vertex1.distance_to(m_vertex2); - } - - [[nodiscard]] - float side_b_length() const - { - return m_vertex3.distance_to(m_vertex2); - } - - [[nodiscard]] - constexpr Vector3 side_a_vector() const - { - return m_vertex1 - m_vertex2; - } - - [[nodiscard]] - constexpr float hypot() const - { - return m_vertex1.distance_to(m_vertex3); - } - [[nodiscard]] - constexpr bool is_rectangular() const - { - const auto side_a = side_a_length(); - const auto side_b = side_b_length(); - const auto hypot_value = hypot(); - - return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f; - } - [[nodiscard]] - constexpr Vector3 side_b_vector() const - { - return m_vertex3 - m_vertex2; - } - [[nodiscard]] - constexpr Vector3 mid_point() const - { - return (m_vertex1 + m_vertex2 + m_vertex3) / 3; - } - }; -} // namespace omath diff --git a/include/omath/view_angles.hpp b/include/omath/view_angles.hpp deleted file mode 100644 index cd63640a..00000000 --- a/include/omath/view_angles.hpp +++ /dev/null @@ -1,15 +0,0 @@ -// -// Created by Orange on 11/30/2024. -// -#pragma once - -namespace omath -{ - template - struct ViewAngles - { - PitchType pitch; - YawType yaw; - RollType roll; - }; -} // namespace omath diff --git a/modules/game_engines/iw_engine/camera.ixx b/modules/game_engines/iw_engine/camera.ixx index 4ce134b8..2c6ac489 100644 --- a/modules/game_engines/iw_engine/camera.ixx +++ b/modules/game_engines/iw_engine/camera.ixx @@ -3,9 +3,12 @@ // export module omath.iw_engine.camera; + import omath.camera; import omath.iw_engine.constants; import omath.iw_engine.camera_trait; + + export namespace omath::iw_engine { using Camera = projection::Camera; diff --git a/modules/game_engines/iw_engine/constants.ixx b/modules/game_engines/iw_engine/constants.ixx index 31aa2858..a4672584 100644 --- a/modules/game_engines/iw_engine/constants.ixx +++ b/modules/game_engines/iw_engine/constants.ixx @@ -3,6 +3,7 @@ // export module omath.iw_engine.constants; + import omath.vector3; import omath.mat; import omath.angle; diff --git a/modules/game_engines/iw_engine/formulas.ixx b/modules/game_engines/iw_engine/formulas.ixx index 382c923c..91d7704a 100644 --- a/modules/game_engines/iw_engine/formulas.ixx +++ b/modules/game_engines/iw_engine/formulas.ixx @@ -2,9 +2,11 @@ // Created by Vlad on 9/1/2025. // module; + #include export module omath.iw_engine.formulas; + import omath.vector3; import omath.iw_engine.constants; import omath.mat; diff --git a/modules/game_engines/iw_engine/trait/camera_trait.ixx b/modules/game_engines/iw_engine/traits/camera_trait.ixx similarity index 93% rename from modules/game_engines/iw_engine/trait/camera_trait.ixx rename to modules/game_engines/iw_engine/traits/camera_trait.ixx index d1ca3f6f..02940afc 100644 --- a/modules/game_engines/iw_engine/trait/camera_trait.ixx +++ b/modules/game_engines/iw_engine/traits/camera_trait.ixx @@ -33,7 +33,7 @@ export namespace omath::iw_engine [[nodiscard]] static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - float near, float far) noexcept + const float near, const float far) noexcept { return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); } diff --git a/modules/game_engines/iw_engine/trait/pred_engine_trait.ixx b/modules/game_engines/iw_engine/traits/pred_engine_trait.ixx similarity index 100% rename from modules/game_engines/iw_engine/trait/pred_engine_trait.ixx rename to modules/game_engines/iw_engine/traits/pred_engine_trait.ixx diff --git a/modules/game_engines/opengl_engine/camera.ixx b/modules/game_engines/opengl_engine/camera.ixx new file mode 100644 index 00000000..ee085b44 --- /dev/null +++ b/modules/game_engines/opengl_engine/camera.ixx @@ -0,0 +1,14 @@ +// +// Created by Vlad on 9/2/2025. +// + +export module omath.opengl_engine.camera; + +export import omath.camera; +import omath.opengl_engine.constants; +import omath.opengl_engine.camera_trait; + +export namespace omath::opengl_engine +{ + using Camera = projection::Camera; +} \ No newline at end of file diff --git a/include/omath/engines/opengl_engine/constants.hpp b/modules/game_engines/opengl_engine/constants.ixx similarity index 75% rename from include/omath/engines/opengl_engine/constants.hpp rename to modules/game_engines/opengl_engine/constants.ixx index 4f5a955a..16c86f29 100644 --- a/include/omath/engines/opengl_engine/constants.hpp +++ b/modules/game_engines/opengl_engine/constants.ixx @@ -1,14 +1,15 @@ // -// Created by Orange on 12/23/2024. +// Created by Vlad on 9/2/2025. // -#pragma once -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include +export module omath.opengl_engine.constants; -namespace omath::opengl_engine +import omath.vector3; +import omath.mat; +import omath.angle; +import omath.view_angles; + +export namespace omath::opengl_engine { constexpr Vector3 k_abs_up = {0, 1, 0}; constexpr Vector3 k_abs_right = {1, 0, 0}; diff --git a/source/engines/opengl_engine/formulas.cpp b/modules/game_engines/opengl_engine/formulas.ixx similarity index 66% rename from source/engines/opengl_engine/formulas.cpp rename to modules/game_engines/opengl_engine/formulas.ixx index 4a14ab2f..2570d659 100644 --- a/source/engines/opengl_engine/formulas.cpp +++ b/modules/game_engines/opengl_engine/formulas.ixx @@ -1,46 +1,62 @@ // -// Created by Vlad on 3/19/2025. +// Created by Vlad on 9/2/2025. // -#include "omath/engines/opengl_engine/formulas.hpp" +module; +#include +export module omath.opengl_engine.formulas; -namespace omath::opengl_engine +import omath.vector3; +import omath.opengl_engine.constants; +import omath.mat; +import omath.angle; + +export namespace omath::opengl_engine { + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_x(-angles.pitch) + * mat_rotation_axis_y(-angles.yaw) + * mat_rotation_axis_z(angles.roll); + } + [[nodiscard]] Vector3 forward_vector(const ViewAngles& angles) noexcept { - const auto vec - = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); + const auto vec = + rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 right_vector(const ViewAngles& angles) noexcept { - const auto vec - = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); + const auto vec = + rotation_matrix(angles) * mat_column_from_vector(k_abs_right); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 up_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } - Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { return mat_camera_view(-forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_x(-angles.pitch) - * mat_rotation_axis_y(-angles.yaw) - * mat_rotation_axis_z(angles.roll); - } + + [[nodiscard]] Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f); return { {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, diff --git a/modules/game_engines/opengl_engine/traits/camera_trait.ixx b/modules/game_engines/opengl_engine/traits/camera_trait.ixx new file mode 100644 index 00000000..4597e703 --- /dev/null +++ b/modules/game_engines/opengl_engine/traits/camera_trait.ixx @@ -0,0 +1,43 @@ +// +// Created by Vlad on 9/2/2025. +// +module; +#include + +export module omath.opengl_engine.camera_trait; + +import omath.vector3; +import omath.view_angles; +import omath.opengl_engine.constants; +import omath.opengl_engine.formulas; +import omath.camera; + +export namespace omath::opengl_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept + { + const auto distance = cam_origin.distance_to(look_at); + const auto delta = cam_origin - look_at; + + return {PitchAngle::from_radians(-std::asin(delta.y / distance)), + YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; + } + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return opengl_engine::calc_view_matrix(angles, cam_origin); + } + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + float near, float far) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); + } + }; + +} // namespace omath::opengl_engine \ No newline at end of file diff --git a/include/omath/engines/opengl_engine/traits/pred_engine_trait.hpp b/modules/game_engines/opengl_engine/traits/pred_engine_trait.ixx similarity index 84% rename from include/omath/engines/opengl_engine/traits/pred_engine_trait.hpp rename to modules/game_engines/opengl_engine/traits/pred_engine_trait.ixx index 9c014ff8..19969e17 100644 --- a/include/omath/engines/opengl_engine/traits/pred_engine_trait.hpp +++ b/modules/game_engines/opengl_engine/traits/pred_engine_trait.ixx @@ -1,11 +1,18 @@ // -// Created by Vlad on 8/6/2025. +// Created by Vlad on 9/2/2025. // -#pragma once -#include "omath/engines/opengl_engine/formulas.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" +module; #include +#include + +export module omath.opengl_engine.pred_engine_trait; + +import omath.opengl_engine.formulas; +import omath.opengl_engine.constants; +import omath.vector3; +import omath.projectile_prediction.projectile; +import omath.projectile_prediction.target; +import omath.angle; namespace omath::opengl_engine { @@ -53,7 +60,7 @@ namespace omath::opengl_engine const std::optional projectile_pitch) noexcept { const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); - const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); + const auto height = delta2d * std::tan(degrees_to_radians(projectile_pitch.value())); return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z}; } @@ -65,14 +72,14 @@ namespace omath::opengl_engine const auto distance = origin.distance_to(view_to); const auto delta = view_to - origin; - return angles::radians_to_degrees(std::asin(delta.y / distance)); + return radians_to_degrees(std::asin(delta.y / distance)); } [[nodiscard]] static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept { const auto delta = view_to - origin; - return angles::radians_to_degrees(std::atan2(delta.z, delta.x)); + return radians_to_degrees(std::atan2(delta.z, delta.x)); }; }; -} // namespace omath::opengl_engine +} \ No newline at end of file diff --git a/modules/game_engines/source_engine/camera.ixx b/modules/game_engines/source_engine/camera.ixx new file mode 100644 index 00000000..83e1a689 --- /dev/null +++ b/modules/game_engines/source_engine/camera.ixx @@ -0,0 +1,13 @@ +// +// Created by Vlad on 9/2/2025. +// + +export module omath.source_engine.camera; +import omath.camera; +export import omath.source_engine.constants; +export import omath.source_engine.camera_trait; + +export namespace omath::source_engine +{ + using Camera = projection::Camera; +} \ No newline at end of file diff --git a/include/omath/engines/source_engine/constants.hpp b/modules/game_engines/source_engine/constants.ixx similarity index 71% rename from include/omath/engines/source_engine/constants.hpp rename to modules/game_engines/source_engine/constants.ixx index 33df18ba..89bff3c9 100644 --- a/include/omath/engines/source_engine/constants.hpp +++ b/modules/game_engines/source_engine/constants.ixx @@ -1,14 +1,15 @@ // -// Created by Orange on 12/4/2024. +// Created by Vlad on 9/2/2025. // -#pragma once -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include +export module omath.source_engine.constants; -namespace omath::source_engine +import omath.vector3; +import omath.mat; +import omath.angle; +import omath.view_angles; + +export namespace omath::source_engine { constexpr Vector3 k_abs_up = {0, 0, 1}; constexpr Vector3 k_abs_right = {0, -1, 0}; @@ -22,4 +23,4 @@ namespace omath::source_engine using RollAngle = Angle; using ViewAngles = omath::ViewAngles; -} // namespace omath::source_engine +} \ No newline at end of file diff --git a/source/engines/source_engine/formulas.cpp b/modules/game_engines/source_engine/formulas.ixx similarity index 66% rename from source/engines/source_engine/formulas.cpp rename to modules/game_engines/source_engine/formulas.ixx index 326507e8..88eda5ac 100644 --- a/source/engines/source_engine/formulas.cpp +++ b/modules/game_engines/source_engine/formulas.ixx @@ -1,10 +1,26 @@ // -// Created by Vlad on 3/19/2025. +// Created by Vlad on 9/2/2025. // -#include +module; +#include -namespace omath::source_engine +export module omath.source_engine.formulas; + +import omath.vector3; +import omath.source_engine.constants; +import omath.view_angles; +import omath.mat; +import omath.angle; + +export namespace omath::source_engine { + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); + } + + [[nodiscard]] Vector3 forward_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); @@ -12,17 +28,14 @@ namespace omath::source_engine return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_z(angles.yaw) * mat_rotation_axis_y(angles.pitch) * mat_rotation_axis_x(angles.roll); - } - + [[nodiscard]] Vector3 right_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + [[nodiscard]] Vector3 up_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); @@ -30,18 +43,18 @@ namespace omath::source_engine return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } - Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { return mat_camera_view(forward_vector(angles), right_vector(angles), up_vector(angles), cam_origin); } - Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, - const float far) noexcept + [[nodiscard]] + Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept { // NOTE: Need magic number to fix fov calculation, since source inherit Quake proj matrix calculation constexpr auto k_multiply_factor = 0.75f; - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f) * k_multiply_factor; return { {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, @@ -50,4 +63,4 @@ namespace omath::source_engine {0, 0, 1, 0}, }; } -} // namespace omath::source_engine +} // namespace omath::source_engine \ No newline at end of file diff --git a/modules/game_engines/source_engine/traits/camera_trait.ixx b/modules/game_engines/source_engine/traits/camera_trait.ixx new file mode 100644 index 00000000..650157a5 --- /dev/null +++ b/modules/game_engines/source_engine/traits/camera_trait.ixx @@ -0,0 +1,40 @@ +// +// Created by Vlad on 9/2/2025. +// +module; +#include + +export module omath.source_engine.camera_trait; + +import omath.source_engine.constants; +import omath.source_engine.formulas; +import omath.vector3; +import omath.camera; +export namespace omath::source_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept + { + const auto distance = cam_origin.distance_to(look_at); + const auto delta = cam_origin - look_at; + + return {PitchAngle::from_radians(-std::asin(delta.z / distance)), + YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; + } + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return source_engine::calc_view_matrix(angles, cam_origin); + } + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + const float near, const float far) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); + } + }; +} \ No newline at end of file diff --git a/include/omath/engines/source_engine/traits/pred_engine_trait.hpp b/modules/game_engines/source_engine/traits/pred_engine_trait.ixx similarity index 83% rename from include/omath/engines/source_engine/traits/pred_engine_trait.hpp rename to modules/game_engines/source_engine/traits/pred_engine_trait.ixx index ca9771e1..a37d3381 100644 --- a/include/omath/engines/source_engine/traits/pred_engine_trait.hpp +++ b/modules/game_engines/source_engine/traits/pred_engine_trait.ixx @@ -1,14 +1,20 @@ // -// Created by Vlad on 8/3/2025. +// Created by Vlad on 9/2/2025. // - -#pragma once -#include "omath/engines/source_engine/formulas.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" +module; +#include #include -namespace omath::source_engine +export module omath.source_engine.pred_engine_trait; + +import omath.vector3; +import omath.angle; +import omath.source_engine.formulas; +import omath.source_engine.constants; +import omath.projectile_prediction.target; +import omath.projectile_prediction.projectile; + +export namespace omath::source_engine { class PredEngineTrait final { @@ -54,7 +60,7 @@ namespace omath::source_engine const std::optional projectile_pitch) noexcept { const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); - const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); + const auto height = delta2d * std::tan(degrees_to_radians(projectile_pitch.value())); return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height}; } @@ -66,14 +72,14 @@ namespace omath::source_engine const auto distance = origin.distance_to(view_to); const auto delta = view_to - origin; - return angles::radians_to_degrees(std::asin(delta.z / distance)); + return radians_to_degrees(std::asin(delta.z / distance)); } [[nodiscard]] static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept { const auto delta = view_to - origin; - return angles::radians_to_degrees(std::atan2(delta.y, delta.x)); + return radians_to_degrees(std::atan2(delta.y, delta.x)); }; }; } // namespace omath::source_engine \ No newline at end of file diff --git a/modules/game_engines/unity_engine/camera.ixx b/modules/game_engines/unity_engine/camera.ixx new file mode 100644 index 00000000..032fdf13 --- /dev/null +++ b/modules/game_engines/unity_engine/camera.ixx @@ -0,0 +1,14 @@ +// +// Created by Vlad on 9/3/2025. +// + +export module omath.unity_engine.camera; +import omath.camera; +import omath.unity_engine.constants; +import omath.unity_engine.camera_trait; + + +export namespace omath::unity_engine +{ + using Camera = projection::Camera; +} \ No newline at end of file diff --git a/include/omath/engines/unity_engine/constants.hpp b/modules/game_engines/unity_engine/constants.ixx similarity index 72% rename from include/omath/engines/unity_engine/constants.hpp rename to modules/game_engines/unity_engine/constants.ixx index 81151bc6..444653f3 100644 --- a/include/omath/engines/unity_engine/constants.hpp +++ b/modules/game_engines/unity_engine/constants.ixx @@ -1,15 +1,16 @@ // -// Created by Vlad on 3/22/2025. +// Created by Vlad on 9/3/2025. // -#pragma once +export module omath.unity_engine.constants; -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include +export import omath.vector3; +export import omath.vector3; +export import omath.mat; +export import omath.angle; +export import omath.view_angles; -namespace omath::unity_engine +export namespace omath::unity_engine { constexpr Vector3 k_abs_up = {0, 1, 0}; constexpr Vector3 k_abs_right = {1, 0, 0}; diff --git a/source/engines/unity_engine/formulas.cpp b/modules/game_engines/unity_engine/formulas.ixx similarity index 76% rename from source/engines/unity_engine/formulas.cpp rename to modules/game_engines/unity_engine/formulas.ixx index 4560e277..405e08f2 100644 --- a/source/engines/unity_engine/formulas.cpp +++ b/modules/game_engines/unity_engine/formulas.ixx @@ -1,43 +1,57 @@ // -// Created by Vlad on 3/22/2025. +// Created by Vlad on 9/3/2025. // -#include "omath/engines/unity_engine/formulas.hpp" +module; +#include -namespace omath::unity_engine +export module omath.unity_engine.formulas; +export import omath.unity_engine.constants; + +export namespace omath::unity_engine { + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_x(angles.pitch) + * mat_rotation_axis_y(angles.yaw) + * mat_rotation_axis_z(angles.roll); + } + + [[nodiscard]] Vector3 forward_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 right_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 up_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } - Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { return mat_camera_view(forward_vector(angles), -right_vector(angles), up_vector(angles), cam_origin); } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_x(angles.pitch) - * mat_rotation_axis_y(angles.yaw) - * mat_rotation_axis_z(angles.roll); - } + + [[nodiscard]] Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f); return { {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, @@ -46,4 +60,4 @@ namespace omath::unity_engine {0, 0, -1.f, 0}, }; } -} // namespace omath::unity_engine +} // namespace omath::unity_engine \ No newline at end of file diff --git a/modules/game_engines/unity_engine/traits/camera_trait.ixx b/modules/game_engines/unity_engine/traits/camera_trait.ixx new file mode 100644 index 00000000..3d1fd465 --- /dev/null +++ b/modules/game_engines/unity_engine/traits/camera_trait.ixx @@ -0,0 +1,42 @@ +// +// Created by Vlad on 9/3/2025. +// +module; +#include + +export module omath.unity_engine.camera_trait; +import omath.unity_engine.formulas; +import omath.projectile_prediction.projectile; +import omath.projectile_prediction.target; +import omath.camera; + +export namespace omath::unity_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept + { + const auto distance = cam_origin.distance_to(look_at); + const auto delta = cam_origin - look_at; + + return {PitchAngle::from_radians(-std::asin(delta.y / distance)), + YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; + } + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return unity_engine::calc_view_matrix(angles, cam_origin); + } + + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + const float near, const float far) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); + } + }; + +} \ No newline at end of file diff --git a/include/omath/engines/unity_engine/traits/pred_engine_trait.hpp b/modules/game_engines/unity_engine/traits/pred_engine_trait.ixx similarity index 83% rename from include/omath/engines/unity_engine/traits/pred_engine_trait.hpp rename to modules/game_engines/unity_engine/traits/pred_engine_trait.ixx index 5851e4c6..956a72f4 100644 --- a/include/omath/engines/unity_engine/traits/pred_engine_trait.hpp +++ b/modules/game_engines/unity_engine/traits/pred_engine_trait.ixx @@ -1,13 +1,16 @@ // -// Created by Vlad on 8/6/2025. +// Created by Vlad on 9/3/2025. // -#pragma once -#include "omath/engines/unity_engine/formulas.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" +module; +#include #include +export module omath.unity_engine.pred_engine_trait; +import omath.unity_engine.formulas; +import omath.projectile_prediction.target; +import omath.projectile_prediction.projectile; +import omath.angle; -namespace omath::unity_engine +export namespace omath::unity_engine { class PredEngineTrait final { @@ -53,7 +56,7 @@ namespace omath::unity_engine const std::optional projectile_pitch) noexcept { const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); - const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); + const auto height = delta2d * std::tan(degrees_to_radians(projectile_pitch.value())); return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z}; } @@ -65,14 +68,14 @@ namespace omath::unity_engine const auto distance = origin.distance_to(view_to); const auto delta = view_to - origin; - return angles::radians_to_degrees(std::asin(delta.y / distance)); + return radians_to_degrees(std::asin(delta.y / distance)); } [[nodiscard]] static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept { const auto delta = view_to - origin; - return angles::radians_to_degrees(std::atan2(delta.z, delta.x)); + return radians_to_degrees(std::atan2(delta.z, delta.x)); }; }; -} // namespace omath::unity_engine +} // namespace omath::unity_engine \ No newline at end of file diff --git a/modules/game_engines/unreal_engine/camera.ixx b/modules/game_engines/unreal_engine/camera.ixx new file mode 100644 index 00000000..789853c7 --- /dev/null +++ b/modules/game_engines/unreal_engine/camera.ixx @@ -0,0 +1,14 @@ +// +// Created by Vlad on 9/3/2025. +// + +export module omath.unreal_engine.camera; +export import omath.camera; +import omath.unreal_engine.constants; +import omath.unreal_engine.camera_trait; + + +export namespace omath::unreal_engine +{ + using Camera = projection::Camera; +} \ No newline at end of file diff --git a/include/omath/engines/unreal_engine/constants.hpp b/modules/game_engines/unreal_engine/constants.ixx similarity index 75% rename from include/omath/engines/unreal_engine/constants.hpp rename to modules/game_engines/unreal_engine/constants.ixx index e2e0eb7e..917ab871 100644 --- a/include/omath/engines/unreal_engine/constants.hpp +++ b/modules/game_engines/unreal_engine/constants.ixx @@ -1,15 +1,15 @@ // -// Created by Vlad on 3/22/2025. +// Created by Vlad on 9/3/2025. // -#pragma once +export module omath.unreal_engine.constants; -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" -#include -#include +export import omath.vector3; +export import omath.mat; +export import omath.angle; +export import omath.view_angles; -namespace omath::unreal_engine +export namespace omath::unreal_engine { constexpr Vector3 k_abs_up = {0, 0, 1}; constexpr Vector3 k_abs_right = {0, 1, 0}; diff --git a/source/engines/unreal_engine/formulas.cpp b/modules/game_engines/unreal_engine/formulas.ixx similarity index 76% rename from source/engines/unreal_engine/formulas.cpp rename to modules/game_engines/unreal_engine/formulas.ixx index 6f02ae85..3a0b9d11 100644 --- a/source/engines/unreal_engine/formulas.cpp +++ b/modules/game_engines/unreal_engine/formulas.ixx @@ -1,43 +1,59 @@ // -// Created by Vlad on 3/22/2025. +// Created by Vlad on 9/3/2025. // -#include "omath/engines/unreal_engine/formulas.hpp" +module; +#include -namespace omath::unreal_engine +export module omath.unreal_engine.formulas; + +export import omath.unreal_engine.constants; +import omath.angle; + +export namespace omath::unreal_engine { + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_x(angles.roll) + * mat_rotation_axis_y(angles.pitch) + * mat_rotation_axis_z(angles.yaw); + } + + [[nodiscard]] Vector3 forward_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 right_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } + + [[nodiscard]] Vector3 up_vector(const ViewAngles& angles) noexcept { const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up); return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; } - Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { return mat_camera_view(forward_vector(angles), -right_vector(angles), up_vector(angles), cam_origin); } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_x(angles.roll) - * mat_rotation_axis_y(angles.pitch) - * mat_rotation_axis_z(angles.yaw); - } + + [[nodiscard]] Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); + const float fov_half_tan = std::tan(degrees_to_radians(field_of_view) / 2.f); return { {1.f / (aspect_ratio * fov_half_tan), 0, 0, 0}, @@ -46,4 +62,4 @@ namespace omath::unreal_engine {0, 0, -1.f, 0}, }; } -} // namespace omath::unreal_engine +} // namespace omath::unreal_engine \ No newline at end of file diff --git a/modules/game_engines/unreal_engine/traits/camera_trait.ixx b/modules/game_engines/unreal_engine/traits/camera_trait.ixx new file mode 100644 index 00000000..991fe656 --- /dev/null +++ b/modules/game_engines/unreal_engine/traits/camera_trait.ixx @@ -0,0 +1,40 @@ +// +// Created by Vlad on 9/3/2025. +// +module; +#include +export module omath.unreal_engine.camera_trait; + +import omath.unreal_engine.constants; +import omath.unreal_engine.formulas; +import omath.camera; + +export namespace omath::unreal_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept + { + const auto distance = cam_origin.distance_to(look_at); + const auto delta = cam_origin - look_at; + + return {PitchAngle::from_radians(-std::asin(delta.z / distance)), + YawAngle::from_radians(std::atan2(delta.x, delta.y)), RollAngle::from_radians(0.f)}; + } + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept + { + return unreal_engine::calc_view_matrix(angles, cam_origin); + } + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + const float near, const float far) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); + } + }; + +} // namespace omath::unreal_engine \ No newline at end of file diff --git a/include/omath/engines/unreal_engine/traits/pred_engine_trait.hpp b/modules/game_engines/unreal_engine/traits/pred_engine_trait.ixx similarity index 82% rename from include/omath/engines/unreal_engine/traits/pred_engine_trait.hpp rename to modules/game_engines/unreal_engine/traits/pred_engine_trait.ixx index a57b8807..8b9b94b0 100644 --- a/include/omath/engines/unreal_engine/traits/pred_engine_trait.hpp +++ b/modules/game_engines/unreal_engine/traits/pred_engine_trait.ixx @@ -1,13 +1,19 @@ // -// Created by Vlad on 8/6/2025. +// Created by Vlad on 9/3/2025. // -#pragma once -#include "omath/engines/unreal_engine/formulas.hpp" -#include "omath/projectile_prediction/projectile.hpp" -#include "omath/projectile_prediction/target.hpp" +module; +#include #include -namespace omath::unreal_engine +export module omath.unreal_engine.pred_engine_trait; + +import omath.unreal_engine.constants; +import omath.unreal_engine.formulas; +import omath.projectile_prediction.projectile; +import omath.projectile_prediction.target; +import omath.angle; + +export namespace omath::unreal_engine { class PredEngineTrait final { @@ -53,7 +59,7 @@ namespace omath::unreal_engine const std::optional projectile_pitch) noexcept { const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); - const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); + const auto height = delta2d * std::tan(degrees_to_radians(projectile_pitch.value())); return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height}; } @@ -65,14 +71,14 @@ namespace omath::unreal_engine const auto distance = origin.distance_to(view_to); const auto delta = view_to - origin; - return angles::radians_to_degrees(std::asin(delta.z / distance)); + return radians_to_degrees(std::asin(delta.z / distance)); } [[nodiscard]] static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept { const auto delta = view_to - origin; - return angles::radians_to_degrees(std::atan2(delta.y, delta.x)); + return radians_to_degrees(std::atan2(delta.y, delta.x)); }; }; -} // namespace omath::unreal_engine +} // namespace omath::unreal_engine \ No newline at end of file diff --git a/modules/linear_algebra/mat.ixx b/modules/linear_algebra/mat.ixx index 25d525e7..b526c122 100644 --- a/modules/linear_algebra/mat.ixx +++ b/modules/linear_algebra/mat.ixx @@ -86,7 +86,7 @@ export namespace omath } [[nodiscard]] - constexpr Type& operator[](const size_t row, const size_t col) const + constexpr const Type& operator[](const size_t row, const size_t col) const { return at(row, col); } @@ -329,7 +329,12 @@ export namespace omath [[nodiscard]] bool operator==(const Mat& mat) const { - return m_data == mat.m_data; + for (size_t i = 0; i < Rows * Columns; ++i) + { + if (m_data[i] != mat.m_data[i]) + return false; + } + return true; } [[nodiscard]] diff --git a/modules/pathfinding/a_star.ixx b/modules/pathfinding/a_star.ixx new file mode 100644 index 00000000..5b38fbee --- /dev/null +++ b/modules/pathfinding/a_star.ixx @@ -0,0 +1,115 @@ +// +// Created by Vlad on 9/3/2025. +// +module; +#include +#include +#include +#include + +export module omath.pathfinding.a_star; +export import omath.vector3; +export import omath.pathfinding.navigation_mesh; + +namespace omath::pathfinding +{ + struct PathNode final + { + std::optional> came_from; + float g_cost = 0.f; + }; +} // namespace omath::pathfinding + +export namespace omath::pathfinding +{ + class Astar final + { + public: + [[nodiscard]] + static std::vector> find_path(const Vector3& start, const Vector3& end, + const NavigationMesh& nav_mesh) noexcept + { + std::unordered_map, PathNode> closed_list; + std::unordered_map, PathNode> open_list; + + const auto maybe_start_vertex = nav_mesh.get_closest_vertex(start); + const auto maybe_end_vertex = nav_mesh.get_closest_vertex(end); + + if (!maybe_start_vertex || !maybe_end_vertex) + return {}; + + const auto start_vertex = maybe_start_vertex.value(); + const auto end_vertex = maybe_end_vertex.value(); + + open_list.emplace(start_vertex, PathNode{std::nullopt, 0.f}); + + while (!open_list.empty()) + { + auto current_it = get_perfect_node(open_list, end_vertex); + + const auto current = current_it->first; + const auto current_node = current_it->second; + + if (current == end_vertex) + return reconstruct_final_path(closed_list, current); + + closed_list.emplace(current, current_node); + open_list.erase(current_it); + + for (const auto& neighbor: nav_mesh.get_neighbors(current)) + { + if (closed_list.contains(neighbor)) + continue; + + const float tentative_g_cost = current_node.g_cost + neighbor.distance_to(current); + + // ReSharper disable once CppTooWideScopeInitStatement + const auto open_it = open_list.find(neighbor); + + if (open_it == open_list.end() || tentative_g_cost < open_it->second.g_cost) + open_list[neighbor] = PathNode{current, tentative_g_cost}; + } + } + + return {}; + } + + private: + [[nodiscard]] + static std::vector> + reconstruct_final_path(const std::unordered_map, PathNode>& closed_list, + const Vector3& current) noexcept + { + std::vector> path; + std::optional current_opt = current; + + while (current_opt) + { + path.push_back(*current_opt); + + auto it = closed_list.find(*current_opt); + + if (it == closed_list.end()) + break; + + current_opt = it->second.came_from; + } + + std::ranges::reverse(path); + return path; + } + + [[nodiscard]] + static auto get_perfect_node(const std::unordered_map, PathNode>& open_list, + const Vector3& end_vertex) noexcept + { + return std::ranges::min_element(open_list, + [&end_vertex](const auto& a, const auto& b) + { + const float fa = a.second.g_cost + a.first.distance_to(end_vertex); + const float fb = b.second.g_cost + b.first.distance_to(end_vertex); + return fa < fb; + }); + } + }; +} // namespace omath::pathfinding diff --git a/modules/pathfinding/navigation_mesh.ixx b/modules/pathfinding/navigation_mesh.ixx new file mode 100644 index 00000000..8217b945 --- /dev/null +++ b/modules/pathfinding/navigation_mesh.ixx @@ -0,0 +1,110 @@ +// +// Created by Vlad on 9/3/2025. +// +module; +#include +#include +#include +#include +#include +export module omath.pathfinding.navigation_mesh; +export import omath.vector3; + +export namespace omath::pathfinding +{ + + enum Error + { + }; + + class NavigationMesh final + { + public: + [[nodiscard]] + std::expected, std::string> get_closest_vertex(const Vector3& point) const noexcept + { + const auto res = std::ranges::min_element(m_vertex_map, [&point](const auto& a, const auto& b) + { return a.first.distance_to(point) < b.first.distance_to(point); }); + + if (res == m_vertex_map.cend()) + return std::unexpected("Failed to get clossest point"); + + return res->first; + } + + [[nodiscard]] + const std::vector>& get_neighbors(const Vector3& vertex) const noexcept + { + return m_vertex_map.at(vertex); + } + + [[nodiscard]] + bool empty() const + { + return m_vertex_map.empty(); + } + + [[nodiscard]] std::vector serialize() const noexcept + { + auto dump_to_vector = [](const T& t, std::vector& vec) + { + for (size_t i = 0; i < sizeof(t); i++) + vec.push_back(*(reinterpret_cast(&t) + i)); + }; + + std::vector raw; + + for (const auto& [vertex, neighbors]: m_vertex_map) + { + const auto neighbors_count = neighbors.size(); + + dump_to_vector(vertex, raw); + dump_to_vector(neighbors_count, raw); + + for (const auto& neighbor: neighbors) + dump_to_vector(neighbor, raw); + } + return raw; + } + + void deserialize(const std::vector& raw) noexcept + { + auto load_from_vector = [](const std::vector& vec, size_t& offset, auto& value) + { + if (offset + sizeof(value) > vec.size()) + { + throw std::runtime_error("Deserialize: Invalid input data size."); + } + std::copy_n(vec.data() + offset, sizeof(value), reinterpret_cast(&value)); + offset += sizeof(value); + }; + + m_vertex_map.clear(); + + size_t offset = 0; + + while (offset < raw.size()) + { + Vector3 vertex; + load_from_vector(raw, offset, vertex); + + uint16_t neighbors_count; + load_from_vector(raw, offset, neighbors_count); + + std::vector> neighbors; + neighbors.reserve(neighbors_count); + + for (size_t i = 0; i < neighbors_count; ++i) + { + Vector3 neighbor; + load_from_vector(raw, offset, neighbor); + neighbors.push_back(neighbor); + } + + m_vertex_map.emplace(vertex, std::move(neighbors)); + } + } + + std::unordered_map, std::vector>> m_vertex_map; + }; +} // namespace omath::pathfinding diff --git a/modules/projectile_prediction/proj_pred_engine_avx2.ixx b/modules/projectile_prediction/proj_pred_engine_avx2.ixx new file mode 100644 index 00000000..e5ac4bf1 --- /dev/null +++ b/modules/projectile_prediction/proj_pred_engine_avx2.ixx @@ -0,0 +1,177 @@ +// +// Created by Vlad on 9/3/2025. +// +module; +#include +#include + +#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) +#include +#else +#include +#endif + +export module omath.projectile_prediction.proj_pred_engine_avx2; + +import omath.projectile_prediction.proj_pred_engine; +import omath.projectile_prediction.projectile; +import omath.projectile_prediction.target; + +namespace omath::projectile_prediction +{ + class ProjPredEngineAvx2 final : public ProjPredEngineInterface + { + public: + [[nodiscard]] std::optional> + maybe_calculate_aim_point([[maybe_unused]] const Projectile& projectile, + [[maybe_unused]] const Target& target) const override + { +#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) + const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; + const float v0 = projectile.m_launchSpeed; + const float v0Sqr = v0 * v0; + const Vector3 projOrigin = projectile.m_origin; + + constexpr int SIMD_FACTOR = 8; + float currentTime = m_simulationTimeStep; + + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) + { + const __m256 times = + _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, + currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, + currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, + currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); + + const __m256 targetX = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); + const __m256 targetY = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); + const __m256 timesSq = _mm256_mul_ps(times, times); + const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, + _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), + timesSq, _mm256_set1_ps(target.m_origin.z))); + + const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); + const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); + const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); + + const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); + + const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); + const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); + const __m256 termSq = _mm256_mul_ps(term, term); + const __m256 numerator = _mm256_add_ps(dSqr, termSq); + const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero + const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); + + const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); + const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); + + const unsigned validMask = _mm256_movemask_ps(mask); + + if (!validMask) + continue; + + alignas(32) float validTimes[SIMD_FACTOR]; + _mm256_store_ps(validTimes, times); + + for (int i = 0; i < SIMD_FACTOR; ++i) + { + if (!(validMask & (1 << i))) + continue; + + const float candidateTime = validTimes[i]; + + if (candidateTime > m_maximumSimulationTime) + continue; + + // Fine search around candidate time + for (float fineTime = candidateTime - m_simulationTimeStep * 2; + fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) + { + if (fineTime < 0) + continue; + + const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + } + } + + // Fallback scalar processing for remaining times + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) + { + const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + + return std::nullopt; +#else + throw std::runtime_error( + std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); +#endif + } + + ProjPredEngineAvx2(const float gravity_constant, const float simulation_time_step, + const float maximum_simulation_time) + : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), + m_maximum_simulation_time(maximum_simulation_time) + { + } + ~ProjPredEngineAvx2() override = default; + + private: + [[nodiscard]] static std::optional calculate_pitch([[maybe_unused]] const Vector3& proj_origin, + [[maybe_unused]] const Vector3& target_pos, + [[maybe_unused]] float bullet_gravity, + [[maybe_unused]] float v0, + [[maybe_unused]] float time) + { +#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) + if (time <= 0.0f) + return std::nullopt; + + const Vector3 delta = target_pos - proj_origin; + const float dSqr = delta.x * delta.x + delta.y * delta.y; + const float h = delta.z; + + const float term = h + 0.5f * bullet_gravity * time * time; + const float requiredV0Sqr = (dSqr + term * term) / (time * time); + const float v0Sqr = v0 * v0; + + if (requiredV0Sqr > v0Sqr + 1e-3f) + return std::nullopt; + + if (dSqr == 0.0f) + return term >= 0.0f ? 90.0f : -90.0f; + + const float d = std::sqrt(dSqr); + const float tanTheta = term / d; + return angles::RadiansToDegrees(std::atan(tanTheta)); +#else + throw std::runtime_error( + std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); +#endif + } + + // We use [[maybe_unused]] here since AVX2 is not available for ARM and ARM64 CPU + [[maybe_unused]] const float m_gravity_constant; + [[maybe_unused]] const float m_simulation_time_step; + [[maybe_unused]] const float m_maximum_simulation_time; + }; +} // namespace omath::projectile_prediction \ No newline at end of file diff --git a/modules/projectile_prediction/proj_pred_engine_legacy.ixx b/modules/projectile_prediction/proj_pred_engine_legacy.ixx index 072df0ca..75158a31 100644 --- a/modules/projectile_prediction/proj_pred_engine_legacy.ixx +++ b/modules/projectile_prediction/proj_pred_engine_legacy.ixx @@ -13,7 +13,7 @@ export import omath.projectile_prediction.projectile; export import omath.projectile_prediction.target; import omath.projectile_prediction.proj_pred_engine; -import omath.iw_engine.pred_engine_trait; +import omath.source_engine.pred_engine_trait; import omath.angle; export namespace omath::projectile_prediction @@ -44,7 +44,7 @@ export namespace omath::projectile_prediction requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b)); requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b)); }; - template + template requires PredEngineConcept class ProjPredEngineLegacy final : public ProjPredEngineInterface { diff --git a/source/engines/opengl_engine/traits/camera_trait.cpp b/source/engines/opengl_engine/traits/camera_trait.cpp deleted file mode 100644 index 98f886df..00000000 --- a/source/engines/opengl_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,28 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/opengl_engine/traits/camera_trait.hpp" -#include "omath/engines/opengl_engine/formulas.hpp" - -namespace omath::opengl_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; - - return {PitchAngle::from_radians(-std::asin(delta.y / distance)), - YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return opengl_engine::calc_view_matrix(angles, cam_origin); - } - Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov, - const projection::ViewPort& view_port, const float near, - const float far) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); - } -} // namespace omath::opengl_engine \ No newline at end of file diff --git a/source/engines/source_engine/traits/camera_trait.cpp b/source/engines/source_engine/traits/camera_trait.cpp deleted file mode 100644 index 688ee3b2..00000000 --- a/source/engines/source_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/source_engine/traits/camera_trait.hpp" -#include "omath/engines/source_engine/formulas.hpp" -namespace omath::source_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; - - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return source_engine::calc_view_matrix(angles, cam_origin); - } - Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov, - const projection::ViewPort& view_port, const float near, - const float far) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); - } -} // namespace omath::source_engine \ No newline at end of file diff --git a/source/engines/unity_engine/traits/camera_trait.cpp b/source/engines/unity_engine/traits/camera_trait.cpp deleted file mode 100644 index d76426b7..00000000 --- a/source/engines/unity_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/unity_engine/traits/camera_trait.hpp" - -namespace omath::unity_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; - - return {PitchAngle::from_radians(-std::asin(delta.y / distance)), - YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return unity_engine::calc_view_matrix(angles, cam_origin); - } - Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov, - const projection::ViewPort& view_port, const float near, - const float far) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); - } -} // namespace omath::unity_engine \ No newline at end of file diff --git a/source/engines/unreal_engine/traits/camera_trait.cpp b/source/engines/unreal_engine/traits/camera_trait.cpp deleted file mode 100644 index 5eb81262..00000000 --- a/source/engines/unreal_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/unreal_engine/traits/camera_trait.hpp" - -namespace omath::unreal_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; - - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.x, delta.y)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return unreal_engine::calc_view_matrix(angles, cam_origin); - } - Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov, - const projection::ViewPort& view_port, const float near, - const float far) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far); - } -} // namespace omath::unreal_engine \ No newline at end of file diff --git a/source/matrix.cpp b/source/matrix.cpp deleted file mode 100644 index 322ea967..00000000 --- a/source/matrix.cpp +++ /dev/null @@ -1,364 +0,0 @@ -#ifdef OMATH_ENABLE_LEGACY - -#include "omath/matrix.hpp" -#include "omath/angles.hpp" -#include "omath/vector3.hpp" -#include -#include -#include -#include - -namespace omath -{ - Matrix::Matrix(const size_t rows, const size_t columns) - { - if (rows == 0 and columns == 0) - throw std::runtime_error("Matrix cannot be 0x0"); - - m_rows = rows; - m_columns = columns; - - m_data = std::make_unique(m_rows * m_columns); - - set(0.f); - } - - Matrix::Matrix(const std::initializer_list>& rows) - { - m_rows = rows.size(); - m_columns = rows.begin()->size(); - - for (const auto& row: rows) - if (row.size() != m_columns) - throw std::invalid_argument("All rows must have the same number of columns."); - - m_data = std::make_unique(m_rows * m_columns); - - size_t i = 0; - for (const auto& row: rows) - { - size_t j = 0; - for (const auto& value: row) - at(i, j++) = value; - ++i; - } - } - - Matrix::Matrix(const Matrix& other) - { - m_rows = other.m_rows; - m_columns = other.m_columns; - - m_data = std::make_unique(m_rows * m_columns); - - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - at(i, j) = other.at(i, j); - } - - Matrix::Matrix(const size_t rows, const size_t columns, const float* raw_data) - { - m_rows = rows; - m_columns = columns; - - m_data = std::make_unique(m_rows * m_columns); - - for (size_t i = 0; i < rows * columns; ++i) - at(i / rows, i % columns) = raw_data[i]; - } - - size_t Matrix::row_count() const noexcept - { - return m_rows; - } - - float& Matrix::operator[](const size_t row, const size_t column) - { - return at(row, column); - } - - Matrix::Matrix(Matrix&& other) noexcept - { - m_rows = other.m_rows; - m_columns = other.m_columns; - m_data = std::move(other.m_data); - - other.m_rows = 0; - other.m_columns = 0; - - other.m_data = nullptr; - } - - size_t Matrix::columns_count() const noexcept - { - return m_columns; - } - - std::pair Matrix::size() const noexcept - { - return {row_count(), columns_count()}; - } - - float& Matrix::at(const size_t row, const size_t col) - { - return const_cast(std::as_const(*this).at(row, col)); - } - - float Matrix::sum() - { - float sum = 0; - - for (size_t i = 0; i < row_count(); i++) - for (size_t j = 0; j < columns_count(); j++) - sum += at(i, j); - - return sum; - } - - const float& Matrix::at(const size_t row, const size_t col) const - { - return m_data[row * m_columns + col]; - } - - Matrix Matrix::operator*(const Matrix& other) const - { - if (m_columns != other.m_rows) - throw std::runtime_error("n != m"); - - auto out_mat = Matrix(m_rows, other.m_columns); - - for (size_t d = 0; d < m_rows; ++d) - for (size_t i = 0; i < other.m_columns; ++i) - for (size_t j = 0; j < other.m_rows; ++j) - out_mat.at(d, i) += at(d, j) * other.at(j, i); - - return out_mat; - } - - Matrix& Matrix::operator*=(const Matrix& other) - { - *this = *this * other; - return *this; - } - - Matrix Matrix::operator*(const float f) const - { - auto out = *this; - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - out.at(i, j) *= f; - - return out; - } - - Matrix& Matrix::operator*=(const float f) - { - for (size_t i = 0; i < row_count(); i++) - for (size_t j = 0; j < columns_count(); j++) - at(i, j) *= f; - return *this; - } - - void Matrix::clear() - { - set(0.f); - } - - Matrix& Matrix::operator=(const Matrix& other) - { - if (this == &other) - return *this; - - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - at(i, j) = other.at(i, j); - - return *this; - } - - Matrix& Matrix::operator=(Matrix&& other) noexcept - { - if (this == &other) - return *this; - - m_rows = other.m_rows; - m_columns = other.m_columns; - m_data = std::move(other.m_data); - - other.m_rows = 0; - other.m_columns = 0; - - return *this; - } - - Matrix& Matrix::operator/=(const float f) - { - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - at(i, j) /= f; - - return *this; - } - - Matrix Matrix::operator/(const float f) const - { - auto out = *this; - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - out.at(i, j) /= f; - - return out; - } - - std::string Matrix::to_string() const - { - std::string str; - - for (size_t i = 0; i < m_rows; i++) - { - for (size_t j = 0; j < m_columns; ++j) - { - str += std::format("{:.1f}", at(i, j)); - - if (j == m_columns - 1) - str += '\n'; - else - str += ' '; - } - } - return str; - } - - float Matrix::determinant() const // NOLINT(*-no-recursion) - { - if (m_rows + m_columns == 2) - return at(0, 0); - - if (m_rows == 2 and m_columns == 2) - return at(0, 0) * at(1, 1) - at(0, 1) * at(1, 0); - - float det = 0; - for (size_t i = 0; i < m_columns; i++) - det += alg_complement(0, i) * at(0, i); - - return det; - } - - float Matrix::alg_complement(const size_t i, const size_t j) const // NOLINT(*-no-recursion) - { - const auto tmp = minor(i, j); - return ((i + j + 2) % 2 == 0) ? tmp : -tmp; - } - - Matrix Matrix::transpose() const - { - Matrix transposed = {m_columns, m_rows}; - - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - transposed.at(j, i) = at(i, j); - - return transposed; - } - - Matrix::~Matrix() = default; - - void Matrix::set(const float val) - { - for (size_t i = 0; i < m_rows; ++i) - for (size_t j = 0; j < m_columns; ++j) - at(i, j) = val; - } - - Matrix Matrix::strip(const size_t row, const size_t column) const - { - Matrix stripped = {m_rows - 1, m_columns - 1}; - size_t strip_row_index = 0; - - for (size_t i = 0; i < m_rows; i++) - { - if (i == row) - continue; - - size_t strip_column_index = 0; - for (size_t j = 0; j < m_columns; ++j) - { - if (j == column) - continue; - - stripped.at(strip_row_index, strip_column_index) = at(i, j); - strip_column_index++; - } - - strip_row_index++; - } - - return stripped; - } - - float Matrix::minor(const size_t i, const size_t j) const // NOLINT(*-no-recursion) - { - return strip(i, j).determinant(); - } - - Matrix Matrix::to_screen_matrix(const float screen_width, const float screen_height) - { - return { - {screen_width / 2.f, 0.f, 0.f, 0.f}, - {0.f, -screen_height / 2.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {screen_width / 2.f, screen_height / 2.f, 0.f, 1.f}, - }; - } - - Matrix Matrix::translation_matrix(const Vector3& diff) - { - return { - {1.f, 0.f, 0.f, 0.f}, - {0.f, 1.f, 0.f, 0.f}, - {0.f, 0.f, 1.f, 0.f}, - {diff.x, diff.y, diff.z, 1.f}, - }; - } - - Matrix Matrix::orientation_matrix(const Vector3& forward, const Vector3& right, - const Vector3& up) - { - return { - {right.x, up.x, forward.x, 0.f}, - {right.y, up.y, forward.y, 0.f}, - {right.z, up.z, forward.z, 0.f}, - {0.f, 0.f, 0.f, 1.f}, - }; - } - - Matrix Matrix::projection_matrix(const float field_of_view, const float aspect_ratio, const float near, - const float far) - { - const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); - - return {{1.f / (aspect_ratio * fov_half_tan), 0.f, 0.f, 0.f}, - {0.f, 1.f / fov_half_tan, 0.f, 0.f}, - {0.f, 0.f, (far + near) / (far - near), 2.f * near * far / (far - near)}, - {0.f, 0.f, -1.f, 0.f}}; - } - - const float* Matrix::raw() const - { - return m_data.get(); - } - - void Matrix::set_data_from_raw(const float* raw_matrix) - { - for (size_t i = 0; i < m_columns * m_rows; ++i) - at(i / m_rows, i % m_columns) = raw_matrix[i]; - } - - Matrix::Matrix() - { - m_columns = 0; - m_rows = 0; - m_data = nullptr; - } -} // namespace omath -#endif diff --git a/source/pathfinding/a_star.cpp b/source/pathfinding/a_star.cpp deleted file mode 100644 index bcd73484..00000000 --- a/source/pathfinding/a_star.cpp +++ /dev/null @@ -1,99 +0,0 @@ -// -// Created by Vlad on 28.07.2024. -// -#include "omath/pathfinding/a_star.hpp" -#include -#include -#include -#include - -namespace omath::pathfinding -{ - struct PathNode final - { - std::optional> came_from; - float g_cost = 0.f; - }; - - std::vector> - Astar::reconstruct_final_path(const std::unordered_map, PathNode>& closed_list, - const Vector3& current) noexcept - { - std::vector> path; - std::optional current_opt = current; - - while (current_opt) - { - path.push_back(*current_opt); - - auto it = closed_list.find(*current_opt); - - if (it == closed_list.end()) - break; - - current_opt = it->second.came_from; - } - - std::ranges::reverse(path); - return path; - } - auto Astar::get_perfect_node(const std::unordered_map, PathNode>& open_list, - const Vector3& end_vertex) noexcept - { - return std::ranges::min_element(open_list, - [&end_vertex](const auto& a, const auto& b) - { - const float fa = a.second.g_cost + a.first.distance_to(end_vertex); - const float fb = b.second.g_cost + b.first.distance_to(end_vertex); - return fa < fb; - }); - } - - std::vector> Astar::find_path(const Vector3& start, const Vector3& end, - const NavigationMesh& nav_mesh) noexcept - { - std::unordered_map, PathNode> closed_list; - std::unordered_map, PathNode> open_list; - - auto maybe_start_vertex = nav_mesh.get_closest_vertex(start); - auto maybe_end_vertex = nav_mesh.get_closest_vertex(end); - - if (!maybe_start_vertex || !maybe_end_vertex) - return {}; - - const auto start_vertex = maybe_start_vertex.value(); - const auto end_vertex = maybe_end_vertex.value(); - - open_list.emplace(start_vertex, PathNode{std::nullopt, 0.f}); - - while (!open_list.empty()) - { - auto current_it = get_perfect_node(open_list, end_vertex); - - const auto current = current_it->first; - const auto current_node = current_it->second; - - if (current == end_vertex) - return reconstruct_final_path(closed_list, current); - - closed_list.emplace(current, current_node); - open_list.erase(current_it); - - for (const auto& neighbor: nav_mesh.get_neighbors(current)) - { - if (closed_list.contains(neighbor)) - continue; - - const float tentative_g_cost = current_node.g_cost + neighbor.distance_to(current); - - // ReSharper disable once CppTooWideScopeInitStatement - const auto open_it = open_list.find(neighbor); - - if (open_it == open_list.end() || tentative_g_cost < open_it->second.g_cost) - open_list[neighbor] = PathNode{current, tentative_g_cost}; - } - } - - return {}; - } -} // namespace omath::pathfinding diff --git a/source/pathfinding/navigation_mesh.cpp b/source/pathfinding/navigation_mesh.cpp deleted file mode 100644 index b2a831b0..00000000 --- a/source/pathfinding/navigation_mesh.cpp +++ /dev/null @@ -1,91 +0,0 @@ -// -// Created by Vlad on 28.07.2024. -// -#include "omath/pathfinding/navigation_mesh.hpp" -#include -#include -namespace omath::pathfinding -{ - std::expected, std::string> - NavigationMesh::get_closest_vertex(const Vector3& point) const noexcept - { - const auto res = std::ranges::min_element(m_vertex_map, [&point](const auto& a, const auto& b) - { return a.first.distance_to(point) < b.first.distance_to(point); }); - - if (res == m_vertex_map.cend()) - return std::unexpected("Failed to get clossest point"); - - return res->first; - } - - const std::vector>& NavigationMesh::get_neighbors(const Vector3& vertex) const noexcept - { - return m_vertex_map.at(vertex); - } - - bool NavigationMesh::empty() const - { - return m_vertex_map.empty(); - } - - std::vector NavigationMesh::serialize() const noexcept - { - auto dump_to_vector = [](const T& t, std::vector& vec) - { - for (size_t i = 0; i < sizeof(t); i++) - vec.push_back(*(reinterpret_cast(&t) + i)); - }; - - std::vector raw; - - for (const auto& [vertex, neighbors]: m_vertex_map) - { - const auto neighbors_count = neighbors.size(); - - dump_to_vector(vertex, raw); - dump_to_vector(neighbors_count, raw); - - for (const auto& neighbor: neighbors) - dump_to_vector(neighbor, raw); - } - return raw; - } - - void NavigationMesh::deserialize(const std::vector& raw) noexcept - { - auto load_from_vector = [](const std::vector& vec, size_t& offset, auto& value) - { - if (offset + sizeof(value) > vec.size()) - { - throw std::runtime_error("Deserialize: Invalid input data size."); - } - std::copy_n(vec.data() + offset, sizeof(value), reinterpret_cast(&value)); - offset += sizeof(value); - }; - - m_vertex_map.clear(); - - size_t offset = 0; - - while (offset < raw.size()) - { - Vector3 vertex; - load_from_vector(raw, offset, vertex); - - uint16_t neighbors_count; - load_from_vector(raw, offset, neighbors_count); - - std::vector> neighbors; - neighbors.reserve(neighbors_count); - - for (size_t i = 0; i < neighbors_count; ++i) - { - Vector3 neighbor; - load_from_vector(raw, offset, neighbor); - neighbors.push_back(neighbor); - } - - m_vertex_map.emplace(vertex, std::move(neighbors)); - } - } -} // namespace omath::pathfinding diff --git a/source/projectile_prediction/proj_pred_engine_avx2.cpp b/source/projectile_prediction/proj_pred_engine_avx2.cpp deleted file mode 100644 index 9a26c435..00000000 --- a/source/projectile_prediction/proj_pred_engine_avx2.cpp +++ /dev/null @@ -1,158 +0,0 @@ -// -// Created by Vlad on 2/23/2025. -// -#include "omath/projectile_prediction/proj_pred_engine_avx2.hpp" -#include -#include - -#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) -#include -#else -#include -#endif - -namespace omath::projectile_prediction -{ - std::optional> - ProjPredEngineAvx2::maybe_calculate_aim_point([[maybe_unused]] const Projectile& projectile, - [[maybe_unused]] const Target& target) const - { -#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) - const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; - const float v0 = projectile.m_launchSpeed; - const float v0Sqr = v0 * v0; - const Vector3 projOrigin = projectile.m_origin; - - constexpr int SIMD_FACTOR = 8; - float currentTime = m_simulationTimeStep; - - for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) - { - const __m256 times - = _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, - currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, - currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, - currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); - - const __m256 targetX - = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); - const __m256 targetY - = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); - const __m256 timesSq = _mm256_mul_ps(times, times); - const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, - _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq, - _mm256_set1_ps(target.m_origin.z))); - - const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); - const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); - const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); - - const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); - - const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); - const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); - const __m256 termSq = _mm256_mul_ps(term, term); - const __m256 numerator = _mm256_add_ps(dSqr, termSq); - const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero - const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); - - const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); - const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); - - const unsigned validMask = _mm256_movemask_ps(mask); - - if (!validMask) - continue; - - alignas(32) float validTimes[SIMD_FACTOR]; - _mm256_store_ps(validTimes, times); - - for (int i = 0; i < SIMD_FACTOR; ++i) - { - if (!(validMask & (1 << i))) - continue; - - const float candidateTime = validTimes[i]; - - if (candidateTime > m_maximumSimulationTime) - continue; - - // Fine search around candidate time - for (float fineTime = candidateTime - m_simulationTimeStep * 2; - fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) - { - if (fineTime < 0) - continue; - - const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); - const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); - if (!pitch) - continue; - - const Vector3 delta = targetPos - projOrigin; - const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); - const float height = d * std::tan(angles::DegreesToRadians(*pitch)); - return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); - } - } - } - - // Fallback scalar processing for remaining times - for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) - { - const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); - const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); - if (!pitch) - continue; - - const Vector3 delta = targetPos - projOrigin; - const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); - const float height = d * std::tan(angles::DegreesToRadians(*pitch)); - return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); - } - - return std::nullopt; -#else - throw std::runtime_error( - std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); -#endif - } - ProjPredEngineAvx2::ProjPredEngineAvx2(const float gravity_constant, const float simulation_time_step, - const float maximum_simulation_time) - : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), - m_maximum_simulation_time(maximum_simulation_time) - { - } - std::optional ProjPredEngineAvx2::calculate_pitch([[maybe_unused]] const Vector3& proj_origin, - [[maybe_unused]] const Vector3& target_pos, - [[maybe_unused]] const float bullet_gravity, - [[maybe_unused]] const float v0, - [[maybe_unused]] const float time) - { -#if defined(OMATH_USE_AVX2) && defined(__i386__) && defined(__x86_64__) - if (time <= 0.0f) - return std::nullopt; - - const Vector3 delta = target_pos - proj_origin; - const float dSqr = delta.x * delta.x + delta.y * delta.y; - const float h = delta.z; - - const float term = h + 0.5f * bullet_gravity * time * time; - const float requiredV0Sqr = (dSqr + term * term) / (time * time); - const float v0Sqr = v0 * v0; - - if (requiredV0Sqr > v0Sqr + 1e-3f) - return std::nullopt; - - if (dSqr == 0.0f) - return term >= 0.0f ? 90.0f : -90.0f; - - const float d = std::sqrt(dSqr); - const float tanTheta = term / d; - return angles::RadiansToDegrees(std::atan(tanTheta)); -#else - throw std::runtime_error( - std::format("{} AVX2 feature is not enabled!", std::source_location::current().function_name())); -#endif - } -} // namespace omath::projectile_prediction diff --git a/tests/engines/unit_test_open_gl.cpp b/tests/engines/unit_test_open_gl.cpp index 89506e61..be3238c5 100644 --- a/tests/engines/unit_test_open_gl.cpp +++ b/tests/engines/unit_test_open_gl.cpp @@ -2,9 +2,10 @@ // Created by Orange on 11/23/2024. // #include -#include -#include -#include + +import omath.opengl_engine.camera; +import omath.opengl_engine.constants; +import omath.opengl_engine.formulas; TEST(unit_test_opengl, ForwardVector) diff --git a/tests/engines/unit_test_source_engine.cpp b/tests/engines/unit_test_source_engine.cpp index 82dcc918..53f4a78d 100644 --- a/tests/engines/unit_test_source_engine.cpp +++ b/tests/engines/unit_test_source_engine.cpp @@ -2,9 +2,11 @@ // Created by Orange on 11/23/2024. // #include -#include -#include -#include + +import omath.source_engine.formulas; +import omath.source_engine.constants; +import omath.source_engine.camera; +import omath.camera; TEST(unit_test_source_engine, ForwardVector) diff --git a/tests/engines/unit_test_unity_engine.cpp b/tests/engines/unit_test_unity_engine.cpp index 47ce136f..00e1fac4 100644 --- a/tests/engines/unit_test_unity_engine.cpp +++ b/tests/engines/unit_test_unity_engine.cpp @@ -2,11 +2,13 @@ // Created by Orange on 11/27/2024. // #include -#include -#include -#include #include +import omath.unity_engine.camera; +import omath.unity_engine.formulas; +import omath.unity_engine.constants; +import omath.camera; + TEST(unit_test_unity_engine, ForwardVector) { const auto forward = omath::unity_engine::forward_vector({}); diff --git a/tests/engines/unit_test_unreal_engine.cpp b/tests/engines/unit_test_unreal_engine.cpp index 09912096..74e7d2c5 100644 --- a/tests/engines/unit_test_unreal_engine.cpp +++ b/tests/engines/unit_test_unreal_engine.cpp @@ -2,10 +2,10 @@ // Created by Orange on 11/27/2024. // #include -#include -#include -#include #include +import omath.unreal_engine.formulas; +import omath.unreal_engine.constants; +import omath.unreal_engine.camera; TEST(unit_test_unreal_engine, ForwardVector) { diff --git a/tests/general/unit_test_a_star.cpp b/tests/general/unit_test_a_star.cpp index 6c341c4f..bcdd0b54 100644 --- a/tests/general/unit_test_a_star.cpp +++ b/tests/general/unit_test_a_star.cpp @@ -2,8 +2,11 @@ // Created by Vlad on 18.08.2024. // #include -#include - +#include +#include +#include +#include +import omath.pathfinding.a_star; TEST(unit_test_a_star, finding_right_path) { omath::pathfinding::NavigationMesh mesh; diff --git a/tests/general/unit_test_angles.cpp b/tests/general/unit_test_angles.cpp index 94ba0eff..9395e441 100644 --- a/tests/general/unit_test_angles.cpp +++ b/tests/general/unit_test_angles.cpp @@ -2,27 +2,26 @@ // Created by Orange on 11/30/2024. // #include -#include - +import omath.angle; TEST(unit_test_angles, radians_to_deg) { constexpr float rad = 67; - EXPECT_NEAR(omath::angles::radians_to_degrees(rad), 3838.82f, 0.01f); + EXPECT_NEAR(omath::radians_to_degrees(rad), 3838.82f, 0.01f); } TEST(unit_test_angles, degrees_to_radians) { constexpr float degree = 90; - EXPECT_NEAR(omath::angles::degrees_to_radians(degree), 1.5708f, 0.01f); + EXPECT_NEAR(omath::degrees_to_radians(degree), 1.5708f, 0.01f); } TEST(unit_test_angles, horizontal_fov_to_verical) { constexpr float h_fov = 90; constexpr float aspect_ration = 16.0f / 9.0f; - const auto vertical_fov = omath::angles::horizontal_fov_to_vertical(h_fov, aspect_ration); + const auto vertical_fov = omath::horizontal_fov_to_vertical(h_fov, aspect_ration); EXPECT_NEAR(vertical_fov, 58.71f, 0.01f); } @@ -31,19 +30,19 @@ TEST(unit_test_angles, vertical_to_horizontal) { constexpr float v_fov = 58.71; constexpr float aspect_ration = 16.0f / 9.0f; - const auto horizontal_fov = omath::angles::vertical_fov_to_horizontal(v_fov, aspect_ration); + const auto horizontal_fov = omath::vertical_fov_to_horizontal(v_fov, aspect_ration); EXPECT_NEAR(horizontal_fov, 89.99f, 0.01f); } TEST(unit_test_angles, wrap_angle) { - const float wrapped = omath::angles::wrap_angle(361.f, 0.f, 360.f); + const float wrapped = omath::wrap_angle(361.f, 0.f, 360.f); EXPECT_NEAR(wrapped, 1.f, 0.01f); } TEST(unit_test_angles, wrap_angle_negative_range) { - const float wrapped = omath::angles::wrap_angle(-90.f, 0.f, 360.f); + const float wrapped = omath::wrap_angle(-90.f, 0.f, 360.f); EXPECT_NEAR(wrapped, 270.f, 0.01f); } \ No newline at end of file diff --git a/tests/general/unit_test_mat.cpp b/tests/general/unit_test_mat.cpp index 3cbf3de4..4bc23c16 100644 --- a/tests/general/unit_test_mat.cpp +++ b/tests/general/unit_test_mat.cpp @@ -1,8 +1,9 @@ // UnitTestMat.cpp -#include "omath/linear_algebra/mat.hpp" -#include "omath/linear_algebra/vector3.hpp" + #include +import omath.mat; +import omath.vector3; using namespace omath; class UnitTestMat : public ::testing::Test diff --git a/tests/general/unit_test_prediction.cpp b/tests/general/unit_test_prediction.cpp index 6fb054a5..4cad9254 100644 --- a/tests/general/unit_test_prediction.cpp +++ b/tests/general/unit_test_prediction.cpp @@ -1,14 +1,13 @@ #include -#include - +import omath.projectile_prediction.proj_pred_legacy; TEST(UnitTestPrediction, PredictionTest) { constexpr omath::projectile_prediction::Target target{ .m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_is_airborne = false}; constexpr omath::projectile_prediction::Projectile proj = { .m_origin = {3, 2, 1}, .m_launch_speed = 5000, .m_gravity_scale = 0.4}; - const auto viewPoint = - omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).maybe_calculate_aim_point(proj, target); + const auto viewPoint = omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f) + .maybe_calculate_aim_point(proj, target); const auto [pitch, yaw, _] = proj.m_origin.view_angle_to(viewPoint.value()).as_tuple(); diff --git a/tests/general/unit_test_projection.cpp b/tests/general/unit_test_projection.cpp index a5ca9a74..9e2541c2 100644 --- a/tests/general/unit_test_projection.cpp +++ b/tests/general/unit_test_projection.cpp @@ -3,10 +3,12 @@ // #include #include -#include -#include #include +import omath.source_engine.camera; +import omath.angle; +import omath.view_angles; + TEST(UnitTestProjection, Projection) { constexpr auto fov = omath::Angle::from_degrees(90.f); diff --git a/tests/general/unit_test_view_angles.cpp b/tests/general/unit_test_view_angles.cpp index 28605456..66bc0d12 100644 --- a/tests/general/unit_test_view_angles.cpp +++ b/tests/general/unit_test_view_angles.cpp @@ -1,4 +1,3 @@ // // Created by Orange on 11/30/2024. // -#include \ No newline at end of file From fcbdefb7be488ac16ad4decfa8fe4bf2f1d0ffad Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 3 Sep 2025 01:23:52 +0300 Subject: [PATCH 8/8] patch --- .idea/editor.xml | 4 ++-- modules/linear_algebra/vector2.ixx | 7 ++++--- modules/linear_algebra/vector3.ixx | 1 + modules/linear_algebra/vector4.ixx | 1 + 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/.idea/editor.xml b/.idea/editor.xml index fde53486..373c50fc 100644 --- a/.idea/editor.xml +++ b/.idea/editor.xml @@ -201,7 +201,7 @@