From 5f14510e5b5d2c097ac3d705c3764fd7c03fcb53 Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 12 Jun 2026 00:52:22 +0300 Subject: [PATCH] made constexpr cryengine camera & fromulas --- include/omath/engines/cry_engine/formulas.hpp | 71 +++++++++-- .../cry_engine/traits/camera_trait.hpp | 32 ++++- include/omath/trigonometry/angles.hpp | 7 +- source/engines/cry_engine/formulas.cpp | 70 ----------- .../cry_engine/traits/camera_trait.cpp | 27 ---- tests/engines/unit_test_cry_engine.cpp | 118 +++++++++++++++--- 6 files changed, 195 insertions(+), 130 deletions(-) delete mode 100644 source/engines/cry_engine/formulas.cpp delete mode 100644 source/engines/cry_engine/traits/camera_trait.cpp diff --git a/include/omath/engines/cry_engine/formulas.hpp b/include/omath/engines/cry_engine/formulas.hpp index 4014b40..3739b67 100644 --- a/include/omath/engines/cry_engine/formulas.hpp +++ b/include/omath/engines/cry_engine/formulas.hpp @@ -8,31 +8,82 @@ namespace omath::cry_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.yaw) + * mat_rotation_axis_y(angles.roll) + * mat_rotation_axis_x(angles.pitch); + } [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + OMATH_CONSTEXPR 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 up_vector(const ViewAngles& angles) noexcept; + OMATH_CONSTEXPR Vector3 right_vector(const ViewAngles& angles) noexcept + { + const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right); - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; + return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)}; + } [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; + OMATH_CONSTEXPR 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]] - Vector3 extract_origin(const Mat4X4& mat) noexcept; + OMATH_CONSTEXPR 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]] - Vector3 extract_scale(const Mat4X4& mat) noexcept; + OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far, - NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept; + OMATH_CONSTEXPR ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept + { + const auto angles = mat_extract_rotation_zyx(mat); + return { + PitchAngle::from_degrees(angles.x), + YawAngle::from_degrees(angles.z), + RollAngle::from_degrees(angles.y), + }; + } + [[nodiscard]] + OMATH_CONSTEXPR Mat4X4 + calc_perspective_projection_matrix( + const float field_of_view, const float aspect_ratio, const float near, const float far, + const NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept + { + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + field_of_view, aspect_ratio, near, far); + + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + field_of_view, aspect_ratio, near, far); + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/cry_engine/traits/camera_trait.hpp b/include/omath/engines/cry_engine/traits/camera_trait.hpp index b7770af..0aee139 100644 --- a/include/omath/engines/cry_engine/traits/camera_trait.hpp +++ b/include/omath/engines/cry_engine/traits/camera_trait.hpp @@ -4,21 +4,41 @@ #pragma once #include "omath/engines/cry_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" - namespace omath::cry_engine { class CameraTrait final { public: [[nodiscard]] - static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; + OMATH_CONSTEXPR static ViewAngles calc_look_at_angle(const Vector3& cam_origin, + const Vector3& look_at) noexcept + { + const auto direction = (look_at - cam_origin).normalized(); +#ifdef OMATH_USE_GCEM + return {PitchAngle::from_radians(gcem::asin(direction.z)), + YawAngle::from_radians(-gcem::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)}; +#else + return {PitchAngle::from_radians(std::asin(direction.z)), + YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)}; +#endif + } [[nodiscard]] - static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; + OMATH_CONSTEXPR static Mat4X4 calc_view_matrix(const ViewAngles& angles, + const Vector3& cam_origin) noexcept + { + return cry_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, NDCDepthRange ndc_depth_range) noexcept; + OMATH_CONSTEXPR static Mat4X4 + calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + const float near, const float far, const NDCDepthRange ndc_depth_range) noexcept + { + return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far, + ndc_depth_range); + } }; -} // namespace omath::cry_engine \ No newline at end of file +} // namespace omath::cry_engine diff --git a/include/omath/trigonometry/angles.hpp b/include/omath/trigonometry/angles.hpp index ce07dd8..10c2789 100644 --- a/include/omath/trigonometry/angles.hpp +++ b/include/omath/trigonometry/angles.hpp @@ -3,6 +3,7 @@ // #pragma once +#include "omath/internal/optional_constexpr_math.hpp" #include #include @@ -47,14 +48,18 @@ namespace omath::angles template requires std::is_arithmetic_v - [[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept + [[nodiscard]] OMATH_CONSTEXPR 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; +#ifdef OMATH_USE_GCEM + Type wrapped_angle = gcem::fmod(angle - min, range); +#else Type wrapped_angle = std::fmod(angle - min, range); +#endif if (wrapped_angle < 0) wrapped_angle += range; diff --git a/source/engines/cry_engine/formulas.cpp b/source/engines/cry_engine/formulas.cpp deleted file mode 100644 index bc7d6ce..0000000 --- a/source/engines/cry_engine/formulas.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// -// Created by Vlad on 3/22/2025. -// -#include "omath/engines/cry_engine/formulas.hpp" - -namespace omath::cry_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 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_z(angles.yaw) - * mat_rotation_axis_y(angles.roll) - * mat_rotation_axis_x(angles.pitch); - } - - Vector3 extract_origin(const Mat4X4& mat) noexcept - { - return mat_extract_origin(mat); - } - - Vector3 extract_scale(const Mat4X4& mat) noexcept - { - return mat_extract_scale(mat); - } - - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept - { - const auto angles = mat_extract_rotation_zyx(mat); - return { - PitchAngle::from_degrees(angles.x), - YawAngle::from_degrees(angles.z), - RollAngle::from_degrees(angles.y), - }; - } - - Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, - const float far, const NDCDepthRange ndc_depth_range) noexcept - { - if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) - return mat_perspective_left_handed_vertical_fov( - field_of_view, aspect_ratio, near, far); - - if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) - return mat_perspective_left_handed_vertical_fov( - field_of_view, aspect_ratio, near, far); - std::unreachable(); - } -} // namespace omath::cry_engine diff --git a/source/engines/cry_engine/traits/camera_trait.cpp b/source/engines/cry_engine/traits/camera_trait.cpp deleted file mode 100644 index da260f5..0000000 --- a/source/engines/cry_engine/traits/camera_trait.cpp +++ /dev/null @@ -1,27 +0,0 @@ -// -// Created by Vlad on 8/11/2025. -// -#include "omath/engines/cry_engine/traits/camera_trait.hpp" - -namespace omath::cry_engine -{ - - ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept - { - const auto direction = (look_at - cam_origin).normalized(); - - return {PitchAngle::from_radians(std::asin(direction.z)), - YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return cry_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, const NDCDepthRange ndc_depth_range) noexcept - { - return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far, - ndc_depth_range); - } -} // namespace omath::cry_engine \ No newline at end of file diff --git a/tests/engines/unit_test_cry_engine.cpp b/tests/engines/unit_test_cry_engine.cpp index 1888c27..3392772 100644 --- a/tests/engines/unit_test_cry_engine.cpp +++ b/tests/engines/unit_test_cry_engine.cpp @@ -9,6 +9,98 @@ #include using namespace omath; + +#ifdef OMATH_USE_GCEM +namespace +{ + constexpr bool close_to(const float actual, const float expected, const float epsilon) + { + const float diff = actual - expected; + return (diff < 0.0f ? -diff : diff) <= epsilon; + } + + constexpr bool vec_close_to(const Vector3& actual, const Vector3& expected, const float epsilon) + { + return close_to(actual.x, expected.x, epsilon) && close_to(actual.y, expected.y, epsilon) + && close_to(actual.z, expected.z, epsilon); + } +} // namespace + +static_assert( + [] + { + constexpr omath::cry_engine::ViewAngles angles{}; + constexpr auto forward = omath::cry_engine::forward_vector(angles); + constexpr auto right = omath::cry_engine::right_vector(angles); + constexpr auto up = omath::cry_engine::up_vector(angles); + constexpr auto rotation = omath::cry_engine::rotation_matrix(angles); + + return vec_close_to(forward, omath::cry_engine::k_abs_forward, 1e-5f) + && vec_close_to(right, omath::cry_engine::k_abs_right, 1e-5f) + && vec_close_to(up, omath::cry_engine::k_abs_up, 1e-5f) && close_to(rotation.at(0, 0), 1.0f, 1e-5f) + && close_to(rotation.at(1, 1), 1.0f, 1e-5f) && close_to(rotation.at(2, 2), 1.0f, 1e-5f) + && close_to(rotation.at(3, 3), 1.0f, 1e-5f); + }(), + "CryEngine basis vectors should be constexpr with gcem"); + +static_assert( + [] + { + constexpr auto view = omath::cry_engine::calc_view_matrix({}, {}); + return close_to(view.at(0, 0), 1.0f, 1e-5f) && close_to(view.at(1, 2), 1.0f, 1e-5f) + && close_to(view.at(2, 1), 1.0f, 1e-5f) && close_to(view.at(3, 3), 1.0f, 1e-5f); + }(), + "CryEngine view matrix should be constexpr with gcem"); + +static_assert( + [] + { + constexpr auto transform = mat_translation({1.0f, 2.0f, 3.0f}) + * mat_scale({2.0f, 3.0f, 4.0f}); + constexpr auto origin = omath::cry_engine::extract_origin(transform); + constexpr auto scale = omath::cry_engine::extract_scale(transform); + + return vec_close_to(origin, {1.0f, 2.0f, 3.0f}, 1e-5f) && vec_close_to(scale, {2.0f, 3.0f, 4.0f}, 1e-5f); + }(), + "CryEngine transform extraction should be constexpr with gcem"); + +static_assert( + [] + { + constexpr auto projection = omath::cry_engine::calc_perspective_projection_matrix( + 90.0f, 1.0f, 1.0f, 11.0f, NDCDepthRange::ZERO_TO_ONE); + return close_to(projection.at(0, 0), 1.0f, 1e-5f) && close_to(projection.at(1, 1), 1.0f, 1e-5f) + && close_to(projection.at(2, 2), 1.1f, 1e-5f) && close_to(projection.at(2, 3), -1.1f, 1e-5f) + && close_to(projection.at(3, 2), 1.0f, 1e-5f); + }(), + "CryEngine projection matrix should be constexpr with gcem"); + +static_assert( + [] + { + constexpr auto angles = + omath::cry_engine::CameraTrait::calc_look_at_angle({}, omath::cry_engine::k_abs_forward); + constexpr auto view = omath::cry_engine::CameraTrait::calc_view_matrix(angles, {}); + constexpr auto projection = omath::cry_engine::CameraTrait::calc_projection_matrix( + projection::FieldOfView::from_degrees(90.0f), {1.0f, 1.0f}, 1.0f, 11.0f, + NDCDepthRange::ZERO_TO_ONE); + + return close_to(angles.pitch.as_degrees(), 0.0f, 1e-5f) && close_to(angles.yaw.as_degrees(), 0.0f, 1e-5f) + && close_to(angles.roll.as_degrees(), 0.0f, 1e-5f) && close_to(view.at(0, 0), 1.0f, 1e-5f) + && close_to(view.at(2, 1), 1.0f, 1e-5f) && close_to(projection.at(0, 0), 1.0f, 1e-5f) + && close_to(projection.at(1, 1), 1.0f, 1e-5f) && close_to(projection.at(2, 2), 1.1f, 1e-5f) + && close_to(projection.at(2, 3), -1.1f, 1e-5f) && close_to(projection.at(3, 2), 1.0f, 1e-5f); + }(), + "CryEngine CameraTrait should be constexpr with gcem"); + +static_assert(omath::cry_engine::units_to_centimeters(100.0f) == 1.0f); +static_assert(omath::cry_engine::units_to_meters(2.0f) == 2.0f); +static_assert(omath::cry_engine::units_to_kilometers(2000.0f) == 2.0f); +static_assert(omath::cry_engine::centimeters_to_units(1.0f) == 100.0f); +static_assert(omath::cry_engine::meters_to_units(2.0f) == 2.0f); +static_assert(omath::cry_engine::kilometers_to_units(2.0f) == 2000.0f); +#endif + TEST(unit_test_cry_engine, look_at_forward) { const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_forward); @@ -251,11 +343,9 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3Zero) TEST(unit_test_cry_engine, ViewAnglesAsVector3Values) { - const omath::cry_engine::ViewAngles angles{ - omath::cry_engine::PitchAngle::from_degrees(45.f), - omath::cry_engine::YawAngle::from_degrees(-90.f), - omath::cry_engine::RollAngle::from_degrees(30.f) - }; + const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(45.f), + omath::cry_engine::YawAngle::from_degrees(-90.f), + omath::cry_engine::RollAngle::from_degrees(30.f)}; const auto vec = angles.as_vector3(); EXPECT_FLOAT_EQ(vec.x, 45.f); @@ -266,11 +356,9 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3Values) TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch) { // Pitch is clamped to [-90, 90] - const omath::cry_engine::ViewAngles angles{ - omath::cry_engine::PitchAngle::from_degrees(120.f), - omath::cry_engine::YawAngle::from_degrees(0.f), - omath::cry_engine::RollAngle::from_degrees(0.f) - }; + const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(120.f), + omath::cry_engine::YawAngle::from_degrees(0.f), + omath::cry_engine::RollAngle::from_degrees(0.f)}; const auto vec = angles.as_vector3(); EXPECT_FLOAT_EQ(vec.x, 90.f); @@ -279,12 +367,10 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch) TEST(unit_test_cry_engine, ViewAnglesAsVector3NormalizedYaw) { // Yaw is normalized to [-180, 180], 270 wraps to -90 - const omath::cry_engine::ViewAngles angles{ - omath::cry_engine::PitchAngle::from_degrees(0.f), - omath::cry_engine::YawAngle::from_degrees(270.f), - omath::cry_engine::RollAngle::from_degrees(0.f) - }; + const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(0.f), + omath::cry_engine::YawAngle::from_degrees(270.f), + omath::cry_engine::RollAngle::from_degrees(0.f)}; const auto vec = angles.as_vector3(); EXPECT_NEAR(vec.y, -90.f, 0.01f); -} \ No newline at end of file +}