diff --git a/include/omath/engines/frostbite_engine/formulas.hpp b/include/omath/engines/frostbite_engine/formulas.hpp index 2328cfe..7f7b115 100644 --- a/include/omath/engines/frostbite_engine/formulas.hpp +++ b/include/omath/engines/frostbite_engine/formulas.hpp @@ -8,31 +8,87 @@ namespace omath::frostbite_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline 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; + inline 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; + inline 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; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.roll) + * mat_rotation_axis_y(angles.yaw) + * mat_rotation_axis_x(angles.pitch); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far, - NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } + + [[nodiscard]] + inline 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.y), + RollAngle::from_degrees(angles.z), + }; + } + + [[nodiscard]] + inline 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::NEGATIVE_ONE_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/frostbite_engine/traits/camera_trait.hpp b/include/omath/engines/frostbite_engine/traits/camera_trait.hpp index 99a46ae..9156a76 100644 --- a/include/omath/engines/frostbite_engine/traits/camera_trait.hpp +++ b/include/omath/engines/frostbite_engine/traits/camera_trait.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/engines/frostbite_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::frostbite_engine @@ -12,13 +13,29 @@ namespace omath::frostbite_engine { 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(); + + return {PitchAngle::from_radians(-internal::asin(direction.y)), + YawAngle::from_radians(internal::atan2(direction.x, direction.z)), RollAngle::from_radians(0.f)}; + } [[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 frostbite_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::unreal_engine \ No newline at end of file +} // namespace omath::unreal_engine diff --git a/include/omath/engines/iw_engine/formulas.hpp b/include/omath/engines/iw_engine/formulas.hpp index a79895e..6cb962f 100644 --- a/include/omath/engines/iw_engine/formulas.hpp +++ b/include/omath/engines/iw_engine/formulas.hpp @@ -8,31 +8,85 @@ namespace omath::iw_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline OMATH_CONSTEXPR 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]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; + inline 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; + inline OMATH_CONSTEXPR 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 extract_scale(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; - - [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; + inline 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::NEGATIVE_ONE_TO_ONE) noexcept; + inline OMATH_CONSTEXPR ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept + { + const auto angles = mat_extract_rotation_zyx(mat); + return { + PitchAngle::from_degrees(angles.y), + YawAngle::from_degrees(angles.z), + RollAngle::from_degrees(angles.x), + }; + } + + [[nodiscard]] + inline 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]] + inline 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::NEGATIVE_ONE_TO_ONE) noexcept + { + constexpr float k_source_reference_aspect = 4.f / 3.f; + const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect); + + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + vertical_fov, aspect_ratio, near, far); + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + vertical_fov, aspect_ratio, near, far); + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/iw_engine/traits/camera_trait.hpp b/include/omath/engines/iw_engine/traits/camera_trait.hpp index 964b8c4..a50a87d 100644 --- a/include/omath/engines/iw_engine/traits/camera_trait.hpp +++ b/include/omath/engines/iw_engine/traits/camera_trait.hpp @@ -3,7 +3,8 @@ // #pragma once -#include "omath/engines/iw_engine/constants.hpp" +#include "omath/engines/iw_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::iw_engine @@ -12,13 +13,29 @@ namespace omath::iw_engine { 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(); + + return {PitchAngle::from_radians(-internal::asin(direction.z)), + YawAngle::from_radians(internal::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; + } [[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 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, 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::iw_engine \ No newline at end of file +} // namespace omath::iw_engine diff --git a/include/omath/engines/opengl_engine/formulas.hpp b/include/omath/engines/opengl_engine/formulas.hpp index dd07f07..c74c0a4 100644 --- a/include/omath/engines/opengl_engine/formulas.hpp +++ b/include/omath/engines/opengl_engine/formulas.hpp @@ -7,31 +7,89 @@ namespace omath::opengl_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline 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; + inline 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; + inline OMATH_CONSTEXPR Mat4X4 calc_view_matrix(const ViewAngles& angles, + const Vector3& cam_origin) noexcept + { + return mat_look_at_right_handed(cam_origin, cam_origin + forward_vector(angles), up_vector(angles)); + } [[nodiscard]] - Vector3 extract_scale(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.roll) + * mat_rotation_axis_y(angles.yaw) + * mat_rotation_axis_x(angles.pitch); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far, - NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } + + [[nodiscard]] + inline 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.y), + RollAngle::from_degrees(angles.z), + }; + } + + [[nodiscard]] + inline 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::NEGATIVE_ONE_TO_ONE) noexcept + { + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return mat_perspective_right_handed_vertical_fov< + float, MatStoreType::COLUMN_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( + field_of_view, aspect_ratio, near, far); + + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return mat_perspective_right_handed_vertical_fov< + float, MatStoreType::COLUMN_MAJOR, NDCDepthRange::ZERO_TO_ONE>( + field_of_view, aspect_ratio, near, far); + + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/opengl_engine/traits/camera_trait.hpp b/include/omath/engines/opengl_engine/traits/camera_trait.hpp index e7c1f4b..f16457c 100644 --- a/include/omath/engines/opengl_engine/traits/camera_trait.hpp +++ b/include/omath/engines/opengl_engine/traits/camera_trait.hpp @@ -3,7 +3,8 @@ // #pragma once -#include "omath/engines/opengl_engine/constants.hpp" +#include "omath/engines/opengl_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::opengl_engine @@ -12,13 +13,30 @@ namespace omath::opengl_engine { 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(); + + return {PitchAngle::from_radians(internal::asin(direction.y)), + YawAngle::from_radians(-internal::atan2(direction.x, -direction.z)), + RollAngle::from_radians(0.f)}; + } [[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 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, 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::opengl_engine \ No newline at end of file +} // namespace omath::opengl_engine diff --git a/include/omath/engines/rage_engine/formulas.hpp b/include/omath/engines/rage_engine/formulas.hpp index 53c3066..f2bc04f 100644 --- a/include/omath/engines/rage_engine/formulas.hpp +++ b/include/omath/engines/rage_engine/formulas.hpp @@ -9,31 +9,86 @@ namespace omath::rage_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline 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; + inline 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; + inline 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; + inline 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]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(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; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } + + [[nodiscard]] + inline 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]] + inline 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/rage_engine/traits/camera_trait.hpp b/include/omath/engines/rage_engine/traits/camera_trait.hpp index 5e6c420..8395af8 100644 --- a/include/omath/engines/rage_engine/traits/camera_trait.hpp +++ b/include/omath/engines/rage_engine/traits/camera_trait.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/engines/rage_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::rage_engine @@ -12,13 +13,29 @@ namespace omath::rage_engine { 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(); + + return {PitchAngle::from_radians(internal::asin(direction.z)), + YawAngle::from_radians(-internal::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)}; + } [[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 rage_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::rage_engine diff --git a/include/omath/engines/source_engine/formulas.hpp b/include/omath/engines/source_engine/formulas.hpp index 4a8bd93..60e3d7c 100644 --- a/include/omath/engines/source_engine/formulas.hpp +++ b/include/omath/engines/source_engine/formulas.hpp @@ -7,31 +7,85 @@ namespace omath::source_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; + inline 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 extract_origin(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR 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 extract_scale(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept + { + const auto angles = mat_extract_rotation_zyx(mat); + return { + PitchAngle::from_degrees(angles.y), + YawAngle::from_degrees(angles.z), + RollAngle::from_degrees(angles.x), + }; + } [[nodiscard]] - Vector3 up_vector(const ViewAngles& angles) noexcept; + inline 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 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far, - NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept; + inline 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]] + inline 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]] + inline 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::NEGATIVE_ONE_TO_ONE) noexcept + { + constexpr float k_source_reference_aspect = 4.f / 3.f; + const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect); + + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + vertical_fov, aspect_ratio, near, far); + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return mat_perspective_left_handed_vertical_fov( + vertical_fov, aspect_ratio, near, far); + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/source_engine/traits/camera_trait.hpp b/include/omath/engines/source_engine/traits/camera_trait.hpp index 5ecb192..09d9ed9 100644 --- a/include/omath/engines/source_engine/traits/camera_trait.hpp +++ b/include/omath/engines/source_engine/traits/camera_trait.hpp @@ -3,7 +3,8 @@ // #pragma once -#include "omath/engines/source_engine/constants.hpp" +#include "omath/engines/source_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::source_engine @@ -12,13 +13,29 @@ namespace omath::source_engine { 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(); + + return {PitchAngle::from_radians(-internal::asin(direction.z)), + YawAngle::from_radians(internal::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; + } [[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 source_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::source_engine \ No newline at end of file +} // namespace omath::source_engine diff --git a/include/omath/engines/unity_engine/formulas.hpp b/include/omath/engines/unity_engine/formulas.hpp index 7d5535b..65e8356 100644 --- a/include/omath/engines/unity_engine/formulas.hpp +++ b/include/omath/engines/unity_engine/formulas.hpp @@ -8,31 +8,86 @@ namespace omath::unity_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline 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; + inline 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; + inline 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; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept + { + return mat_rotation_axis_z(angles.roll) + * mat_rotation_axis_y(angles.yaw) + * mat_rotation_axis_x(angles.pitch); + } [[nodiscard]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far, - NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } + + [[nodiscard]] + inline 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.y), + RollAngle::from_degrees(angles.z), + }; + } + + [[nodiscard]] + inline 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::NEGATIVE_ONE_TO_ONE) noexcept + { + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return omath::mat_perspective_right_handed_vertical_fov< + float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>( + field_of_view, aspect_ratio, near, far); + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return omath::mat_perspective_right_handed_vertical_fov< + float, MatStoreType::ROW_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( + field_of_view, aspect_ratio, near, far); + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/unity_engine/traits/camera_trait.hpp b/include/omath/engines/unity_engine/traits/camera_trait.hpp index 1f531af..c7b89ed 100644 --- a/include/omath/engines/unity_engine/traits/camera_trait.hpp +++ b/include/omath/engines/unity_engine/traits/camera_trait.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/engines/unity_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::unity_engine @@ -12,13 +13,29 @@ namespace omath::unity_engine { 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(); + + return {PitchAngle::from_radians(-internal::asin(direction.y)), + YawAngle::from_radians(internal::atan2(direction.x, direction.z)), RollAngle::from_radians(0.f)}; + } [[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 unity_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::unity_engine \ No newline at end of file +} // namespace omath::unity_engine diff --git a/include/omath/engines/unreal_engine/formulas.hpp b/include/omath/engines/unreal_engine/formulas.hpp index 47b6dfe..4df684d 100644 --- a/include/omath/engines/unreal_engine/formulas.hpp +++ b/include/omath/engines/unreal_engine/formulas.hpp @@ -8,31 +8,86 @@ namespace omath::unreal_engine { [[nodiscard]] - Vector3 forward_vector(const ViewAngles& angles) noexcept; + inline OMATH_CONSTEXPR Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; [[nodiscard]] - Vector3 right_vector(const ViewAngles& angles) noexcept; + inline 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; + inline 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; + inline 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; + inline 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; + inline OMATH_CONSTEXPR 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]] - ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_origin(const Mat4X4& mat) noexcept + { + return mat_extract_origin(mat); + } [[nodiscard]] - Mat4X4 calc_perspective_projection_matrix(double field_of_view, double aspect_ratio, double near, double far, - NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept; + inline OMATH_CONSTEXPR Vector3 extract_scale(const Mat4X4& mat) noexcept + { + return mat_extract_scale(mat); + } + + [[nodiscard]] + inline OMATH_CONSTEXPR ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept + { + const auto angles = mat_extract_rotation_zyx(mat); + return { + PitchAngle::from_degrees(-angles.y), + YawAngle::from_degrees(angles.z), + RollAngle::from_degrees(-angles.x), + }; + } + + [[nodiscard]] + inline OMATH_CONSTEXPR Mat4X4 calc_perspective_projection_matrix( + const double field_of_view, const double aspect_ratio, const double near, const double far, + const NDCDepthRange ndc_depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE) noexcept + { + if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) + return mat_perspective_left_handed_horizontal_fov< + double, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>( + field_of_view, aspect_ratio, near, far); + if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) + return mat_perspective_left_handed_horizontal_fov< + double, MatStoreType::ROW_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( + field_of_view, aspect_ratio, near, far); + std::unreachable(); + } template requires std::is_floating_point_v diff --git a/include/omath/engines/unreal_engine/traits/camera_trait.hpp b/include/omath/engines/unreal_engine/traits/camera_trait.hpp index 4b0fe77..826e17d 100644 --- a/include/omath/engines/unreal_engine/traits/camera_trait.hpp +++ b/include/omath/engines/unreal_engine/traits/camera_trait.hpp @@ -4,6 +4,7 @@ #pragma once #include "omath/engines/unreal_engine/formulas.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/projection/camera.hpp" namespace omath::unreal_engine @@ -12,13 +13,29 @@ namespace omath::unreal_engine { 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(); + + return {PitchAngle::from_radians(internal::asin(direction.z)), + YawAngle::from_radians(internal::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; + } [[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 unreal_engine::calc_view_matrix(angles, cam_origin); + } [[nodiscard]] - static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, - double near, double far, NDCDepthRange ndc_depth_range) noexcept; + OMATH_CONSTEXPR static Mat4X4 + calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + const double near, const double 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::unreal_engine \ No newline at end of file +} // namespace omath::unreal_engine diff --git a/include/omath/internal/optional_constexpr_math.hpp b/include/omath/internal/optional_constexpr_math.hpp index ebd3fa9..04c1266 100644 --- a/include/omath/internal/optional_constexpr_math.hpp +++ b/include/omath/internal/optional_constexpr_math.hpp @@ -16,133 +16,169 @@ namespace omath::internal { template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type sin(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::sin(value); + } #endif return std::sin(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type cos(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::cos(value); + } #endif return std::cos(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type tan(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::tan(value); + } #endif return std::tan(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type atan(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::atan(value); + } #endif return std::atan(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type atan2(const Type& y, const Type& x) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::atan2(y, x); + } #endif return std::atan2(y, x); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type asin(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::asin(value); + } #endif return std::asin(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type acos(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::acos(value); + } #endif return std::acos(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type sqrt(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::sqrt(value); + } #endif return std::sqrt(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type hypot(const Type& x, const Type& y) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::hypot(x, y); + } #endif return std::hypot(x, y); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type hypot(const Type& x, const Type& y, const Type& z) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::sqrt(x * x + y * y + z * z); + } #endif return std::hypot(x, y, z); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type abs(const Type& value) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::abs(value); + } #endif return std::abs(value); } template + requires std::is_floating_point_v [[nodiscard]] OMATH_CONSTEXPR Type fmod(const Type& dividend, const Type& divisor) noexcept { #ifdef OMATH_USE_GCEM - if (std::is_constant_evaluated()) + if consteval + { return gcem::fmod(dividend, divisor); + } #endif return std::fmod(dividend, divisor); } diff --git a/include/omath/projection/camera.hpp b/include/omath/projection/camera.hpp index 42baf73..a753341 100644 --- a/include/omath/projection/camera.hpp +++ b/include/omath/projection/camera.hpp @@ -6,6 +6,7 @@ #include "omath/3d_primitives/aabb.hpp" #include "omath/3d_primitives/obb.hpp" +#include "omath/internal/optional_constexpr_math.hpp" #include "omath/linear_algebra/mat.hpp" #include "omath/linear_algebra/triangle.hpp" #include "omath/linear_algebra/vector3.hpp" @@ -84,8 +85,9 @@ namespace omath::projection }; ~Camera() = default; - Camera(const Vector3& position, const ViewAnglesType& view_angles, const ViewPort& view_port, - const FieldOfView& fov, const NumericType near, const NumericType far) noexcept + constexpr Camera(const Vector3& position, const ViewAnglesType& view_angles, + const ViewPort& view_port, const FieldOfView& fov, const NumericType near, + const NumericType 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) { @@ -102,18 +104,18 @@ namespace omath::projection // NEGATIVE_ONE_TO_ONE) share the same m[0,0]/m[1,1] layout, so this works // regardless of the NDC depth range. [[nodiscard("You must use extracted projection params")]] - static ProjectionParams extract_projection_params(const Mat4X4Type& proj_matrix) noexcept + OMATH_CONSTEXPR static ProjectionParams extract_projection_params(const Mat4X4Type& proj_matrix) noexcept { // m[1,1] == 1 / tan(fov/2) => fov = 2 * atan(1 / m[1,1]) const auto f = proj_matrix.at(1, 1); // m[0,0] == m[1,1] / aspect_ratio => aspect = m[1,1] / m[0,0] - const auto fov_radians = NumericType{2} * std::atan(NumericType{1} / f); + const auto fov_radians = NumericType{2} * internal::atan(NumericType{1} / f); return {FieldOfView::from_radians(static_cast(fov_radians)), f / proj_matrix.at(0, 0)}; } [[nodiscard("You must use calculated view angles")]] - static ViewAnglesType calc_view_angles_from_view_matrix(const Mat4X4Type& view_matrix) noexcept + OMATH_CONSTEXPR static ViewAnglesType calc_view_angles_from_view_matrix(const Mat4X4Type& view_matrix) noexcept { Vector3 forward_vector = {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]}; if constexpr (axes.inverted_forward) @@ -122,7 +124,7 @@ namespace omath::projection } [[nodiscard("You must use calculated origin")]] - static Vector3 calc_origin_from_view_matrix(const Mat4X4Type& view_matrix) noexcept + constexpr static Vector3 calc_origin_from_view_matrix(const Mat4X4Type& view_matrix) noexcept { // The view matrix is R * T(-origin), so the last column stores t = -R * origin. // Recovering origin: origin = -R^T * t @@ -136,40 +138,58 @@ namespace omath::projection }; } - void look_at(const Vector3& target) + OMATH_CONSTEXPR void look_at(const Vector3& target) { m_view_angles = TraitClass::calc_look_at_angle(m_origin, target); m_view_projection_matrix = std::nullopt; m_view_matrix = std::nullopt; } [[nodiscard("You must use calculated look-at angles")]] - ViewAnglesType calc_look_at_angles(const Vector3& look_to) const + OMATH_CONSTEXPR ViewAnglesType calc_look_at_angles(const Vector3& look_to) const { return TraitClass::calc_look_at_angle(m_origin, look_to); } [[nodiscard("You must use forward vector")]] - Vector3 get_forward() const noexcept + OMATH_CONSTEXPR Vector3 get_forward() const noexcept { + if consteval + { + const auto view_matrix = calc_view_matrix(); + return {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]}; + } + const auto& view_matrix = get_view_matrix(); return {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]}; } [[nodiscard("You must use right vector")]] - Vector3 get_right() const noexcept + OMATH_CONSTEXPR Vector3 get_right() const noexcept { + if consteval + { + const auto view_matrix = calc_view_matrix(); + return {view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]}; + } + const auto& view_matrix = get_view_matrix(); return {view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]}; } [[nodiscard("You must use up vector")]] - Vector3 get_up() const noexcept + OMATH_CONSTEXPR Vector3 get_up() const noexcept { + if consteval + { + const auto view_matrix = calc_view_matrix(); + return {view_matrix[1, 0], view_matrix[1, 1], view_matrix[1, 2]}; + } + const auto& view_matrix = get_view_matrix(); return {view_matrix[1, 0], view_matrix[1, 1], view_matrix[1, 2]}; } [[nodiscard("You must use absolute forward vector")]] - Vector3 get_abs_forward() const noexcept + OMATH_CONSTEXPR Vector3 get_abs_forward() const noexcept { if constexpr (axes.inverted_forward) return -get_forward(); @@ -177,7 +197,7 @@ namespace omath::projection } [[nodiscard("You must use absolute right vector")]] - Vector3 get_abs_right() const noexcept + OMATH_CONSTEXPR Vector3 get_abs_right() const noexcept { if constexpr (axes.inverted_right) return -get_right(); @@ -185,13 +205,32 @@ namespace omath::projection } [[nodiscard("You must use absolute up vector")]] - Vector3 get_abs_up() const noexcept + OMATH_CONSTEXPR Vector3 get_abs_up() const noexcept { return get_up(); } + [[nodiscard("You must use calculated view-projection matrix")]] + OMATH_CONSTEXPR Mat4X4Type calc_view_projection_matrix() const noexcept + { + return calc_projection_matrix() * calc_view_matrix(); + } + + [[nodiscard("You must use calculated view matrix")]] + OMATH_CONSTEXPR Mat4X4Type calc_view_matrix() const noexcept + { + return TraitClass::calc_view_matrix(m_view_angles, m_origin); + } + + [[nodiscard("You must use calculated projection matrix")]] + OMATH_CONSTEXPR Mat4X4Type calc_projection_matrix() const noexcept + { + return TraitClass::calc_projection_matrix( + m_field_of_view, m_view_port, m_near_plane_distance, m_far_plane_distance, depth_range); + } + [[nodiscard("You must use view-projection matrix")]] - const Mat4X4Type& get_view_projection_matrix() const noexcept + OMATH_CONSTEXPR const Mat4X4Type& get_view_projection_matrix() const noexcept { if (!m_view_projection_matrix.has_value()) m_view_projection_matrix = get_projection_matrix() * get_view_matrix(); @@ -199,90 +238,89 @@ namespace omath::projection return m_view_projection_matrix.value(); } - [[nodiscard("You must use view matrix")]] const Mat4X4Type& get_view_matrix() const noexcept + [[nodiscard("You must use view matrix")]] OMATH_CONSTEXPR const Mat4X4Type& get_view_matrix() const noexcept { if (!m_view_matrix.has_value()) - m_view_matrix = TraitClass::calc_view_matrix(m_view_angles, m_origin); + m_view_matrix = calc_view_matrix(); return m_view_matrix.value(); } - [[nodiscard("You must use projection matrix")]] const Mat4X4Type& get_projection_matrix() const noexcept + [[nodiscard("You must use projection matrix")]] OMATH_CONSTEXPR const Mat4X4Type& get_projection_matrix() const noexcept { if (!m_projection_matrix.has_value()) - m_projection_matrix = TraitClass::calc_projection_matrix( - m_field_of_view, m_view_port, m_near_plane_distance, m_far_plane_distance, depth_range); + m_projection_matrix = calc_projection_matrix(); return m_projection_matrix.value(); } - void set_field_of_view(const FieldOfView& fov) noexcept + constexpr void set_field_of_view(const FieldOfView& fov) noexcept { m_field_of_view = fov; m_view_projection_matrix = std::nullopt; m_projection_matrix = std::nullopt; } - void set_near_plane(const NumericType near_plane) noexcept + constexpr void set_near_plane(const NumericType near_plane) noexcept { m_near_plane_distance = near_plane; m_view_projection_matrix = std::nullopt; m_projection_matrix = std::nullopt; } - void set_far_plane(const NumericType far_plane) noexcept + constexpr void set_far_plane(const NumericType far_plane) noexcept { m_far_plane_distance = far_plane; m_view_projection_matrix = std::nullopt; m_projection_matrix = std::nullopt; } - void set_view_angles(const ViewAnglesType& view_angles) noexcept + constexpr void set_view_angles(const ViewAnglesType& view_angles) noexcept { m_view_angles = view_angles; m_view_projection_matrix = std::nullopt; m_view_matrix = std::nullopt; } - void set_origin(const Vector3& origin) noexcept + constexpr void set_origin(const Vector3& origin) noexcept { m_origin = origin; m_view_projection_matrix = std::nullopt; m_view_matrix = std::nullopt; } - void set_view_port(const ViewPort& view_port) noexcept + constexpr void set_view_port(const ViewPort& view_port) noexcept { m_view_port = view_port; m_view_projection_matrix = std::nullopt; m_projection_matrix = std::nullopt; } - [[nodiscard("You must use field of view")]] const FieldOfView& get_field_of_view() const noexcept + [[nodiscard("You must use field of view")]] constexpr const FieldOfView& get_field_of_view() const noexcept { return m_field_of_view; } - [[nodiscard("You must use near plane")]] const NumericType& get_near_plane() const noexcept + [[nodiscard("You must use near plane")]] constexpr const NumericType& get_near_plane() const noexcept { return m_near_plane_distance; } - [[nodiscard("You must use far plane")]] const NumericType& get_far_plane() const noexcept + [[nodiscard("You must use far plane")]] constexpr const NumericType& get_far_plane() const noexcept { return m_far_plane_distance; } - [[nodiscard("You must use view angles")]] const ViewAnglesType& get_view_angles() const noexcept + [[nodiscard("You must use view angles")]] constexpr const ViewAnglesType& get_view_angles() const noexcept { return m_view_angles; } - [[nodiscard("You must use origin")]] const Vector3& get_origin() const noexcept + [[nodiscard("You must use origin")]] constexpr const Vector3& get_origin() const noexcept { return m_origin; } template - [[nodiscard("You must use screen position")]] std::expected, Error> + [[nodiscard("You must use screen position")]] OMATH_CONSTEXPR std::expected, Error> world_to_screen(const Vector3& world_position) const noexcept { const auto normalized_cords = world_to_view_port(world_position); @@ -298,7 +336,7 @@ namespace omath::projection std::unreachable(); } template - [[nodiscard("You must use unclipped screen position")]] std::expected, Error> + [[nodiscard("You must use unclipped screen position")]] OMATH_CONSTEXPR std::expected, Error> world_to_screen_unclipped(const Vector3& world_position) const noexcept { const auto normalized_cords = world_to_view_port(world_position, ViewPortClipping::MANUAL); @@ -314,7 +352,7 @@ namespace omath::projection std::unreachable(); } - [[nodiscard("You must use frustum culling result")]] bool + [[nodiscard("You must use frustum culling result")]] OMATH_CONSTEXPR bool is_culled_by_frustum(const Triangle>& triangle) const noexcept { // Transform to clip space (before perspective divide) @@ -382,7 +420,7 @@ namespace omath::projection return false; } - [[nodiscard("You must use AABB frustum culling result")]] bool + [[nodiscard("You must use AABB frustum culling result")]] OMATH_CONSTEXPR bool is_aabb_culled_by_frustum(const primitives::Aabb& aabb) const noexcept { // For each plane, find the AABB corner most in the direction of the plane normal @@ -400,7 +438,7 @@ namespace omath::projection return false; } - [[nodiscard("You must use OBB frustum culling result")]] bool + [[nodiscard("You must use OBB frustum culling result")]] OMATH_CONSTEXPR bool is_obb_culled_by_frustum(const primitives::Obb& obb) const noexcept { // For each plane, project the OBB extents onto the plane normal to get the @@ -410,9 +448,9 @@ namespace omath::projection const Vector3 normal{a, b, c}; const auto center_distance = normal.dot(obb.center) + d; - const auto radius = obb.half_extents.x * std::abs(normal.dot(obb.axis_x)) - + obb.half_extents.y * std::abs(normal.dot(obb.axis_y)) - + obb.half_extents.z * std::abs(normal.dot(obb.axis_z)); + const auto radius = obb.half_extents.x * internal::abs(normal.dot(obb.axis_x)) + + obb.half_extents.y * internal::abs(normal.dot(obb.axis_y)) + + obb.half_extents.z * internal::abs(normal.dot(obb.axis_z)); if (center_distance + radius < NumericType{0}) return true; @@ -421,60 +459,84 @@ namespace omath::projection return false; } - [[nodiscard("You must use view port position")]] std::expected, Error> + [[nodiscard("You must use view port position")]] OMATH_CONSTEXPR std::expected, Error> world_to_view_port(const Vector3& world_position, const ViewPortClipping& clipping = ViewPortClipping::AUTO) const noexcept { + auto project_to_view_port = [this, &clipping](auto projected) -> std::expected, Error> + { + const auto& w = projected.at(3, 0); + constexpr auto eps = std::numeric_limits::epsilon(); + if (w <= eps) + return std::unexpected(Error::PERSPECTIVE_DIVIDER_LESS_EQ_ZERO); + + projected /= w; + + // ReSharper disable once CppTooWideScope + const auto clipped_automatically = + clipping == ViewPortClipping::AUTO && is_ndc_out_of_bounds(projected); + if (clipped_automatically) + return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + + // ReSharper disable once CppTooWideScope + constexpr auto z_min = depth_range == NDCDepthRange::ZERO_TO_ONE ? NumericType{0} : -NumericType{1}; + const auto clipped_manually = + clipping == ViewPortClipping::MANUAL + && (projected.at(2, 0) < z_min - eps || projected.at(2, 0) > NumericType{1} + eps); + if (clipped_manually) + 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)}; + }; + + if consteval + { + auto projected = + calc_view_projection_matrix() + * mat_column_from_vector(world_position); + return project_to_view_port(projected); + } + auto projected = get_view_projection_matrix() * mat_column_from_vector(world_position); - - const auto& w = projected.at(3, 0); - constexpr auto eps = std::numeric_limits::epsilon(); - if (w <= eps) - return std::unexpected(Error::PERSPECTIVE_DIVIDER_LESS_EQ_ZERO); - - projected /= w; - - // ReSharper disable once CppTooWideScope - const auto clipped_automatically = clipping == ViewPortClipping::AUTO && is_ndc_out_of_bounds(projected); - if (clipped_automatically) - return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); - - // ReSharper disable once CppTooWideScope - constexpr auto z_min = depth_range == NDCDepthRange::ZERO_TO_ONE ? NumericType{0} : -NumericType{1}; - const auto clipped_manually = - clipping == ViewPortClipping::MANUAL - && (projected.at(2, 0) < z_min - eps || projected.at(2, 0) > NumericType{1} + eps); - if (clipped_manually) - 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)}; + return project_to_view_port(projected); } [[nodiscard("You must use world position")]] - std::expected, Error> view_port_to_world(const Vector3& ndc) const noexcept + OMATH_CONSTEXPR std::expected, Error> + view_port_to_world(const Vector3& ndc) const noexcept { - const auto inv_view_proj = get_view_projection_matrix().inverted(); + auto view_port_to_world = [&ndc](const Mat4X4Type& view_projection) -> std::expected, Error> + { + const auto inv_view_proj = view_projection.inverted(); - if (!inv_view_proj) - return std::unexpected(Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO); + if (!inv_view_proj) + return std::unexpected(Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO); - auto inverted_projection = - inv_view_proj.value() * mat_column_from_vector(ndc); + auto inverted_projection = inv_view_proj.value() + * mat_column_from_vector(ndc); - const auto& w = inverted_projection.at(3, 0); + const auto& w = inverted_projection.at(3, 0); - if (std::abs(w) < std::numeric_limits::epsilon()) - return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); + if (internal::abs(w) < std::numeric_limits::epsilon()) + return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS); - inverted_projection /= w; + inverted_projection /= w; - return Vector3{inverted_projection.at(0, 0), inverted_projection.at(1, 0), - inverted_projection.at(2, 0)}; + return Vector3{inverted_projection.at(0, 0), inverted_projection.at(1, 0), + inverted_projection.at(2, 0)}; + }; + + if consteval + { + return view_port_to_world(calc_view_projection_matrix()); + } + + return view_port_to_world(get_view_projection_matrix()); } template [[nodiscard("You must use world position")]] - std::expected, Error> + OMATH_CONSTEXPR std::expected, Error> screen_to_world(const Vector3& screen_pos) const noexcept { return view_port_to_world(screen_to_ndc(screen_pos)); @@ -482,7 +544,7 @@ namespace omath::projection template [[nodiscard("You must use world position")]] - std::expected, Error> + OMATH_CONSTEXPR std::expected, Error> screen_to_world(const Vector2& screen_pos) const noexcept { const auto& [x, y] = screen_pos; @@ -517,7 +579,8 @@ namespace omath::projection // Top = r3 - r1 // Near = r3 + r2 ([-1,1]) or r2 ([0,1]) // Far = r3 - r2 - [[nodiscard("You must use frustum planes")]] std::array extract_frustum_planes() const noexcept + [[nodiscard("You must use frustum planes")]] OMATH_CONSTEXPR std::array + extract_frustum_planes() const noexcept { const auto& m = get_view_projection_matrix(); @@ -589,7 +652,7 @@ namespace omath::projection v */ - [[nodiscard("You must use screen position")]] Vector3 + [[nodiscard("You must use screen position")]] constexpr Vector3 ndc_to_screen_position_from_top_left_corner(const Vector3& ndc) const noexcept { /* @@ -607,7 +670,7 @@ namespace omath::projection (ndc.y / -NumericType{2} + NumericType{0.5}) * m_view_port.m_height, ndc.z}; } - [[nodiscard("You must use screen position")]] Vector3 + [[nodiscard("You must use screen position")]] constexpr Vector3 ndc_to_screen_position_from_bottom_left_corner(const Vector3& ndc) const noexcept { /* @@ -626,7 +689,7 @@ namespace omath::projection } template - [[nodiscard("You must use NDC position")]] Vector3 + [[nodiscard("You must use NDC position")]] constexpr Vector3 screen_to_ndc(const Vector3& screen_pos) const noexcept { if constexpr (screen_start == ScreenStart::TOP_LEFT_CORNER) diff --git a/source/engines/frostbite_engine/formulas.cpp b/source/engines/frostbite_engine/formulas.cpp index 3c18648..f0a73c4 100644 --- a/source/engines/frostbite_engine/formulas.cpp +++ b/source/engines/frostbite_engine/formulas.cpp @@ -2,70 +2,3 @@ // Created by Vlad on 3/22/2025. // #include "omath/engines/frostbite_engine/formulas.hpp" - -namespace omath::frostbite_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.roll) - * mat_rotation_axis_y(angles.yaw) - * 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.y), - RollAngle::from_degrees(angles.z), - }; - } - - 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::unity_engine diff --git a/source/engines/frostbite_engine/traits/camera_trait.cpp b/source/engines/frostbite_engine/traits/camera_trait.cpp index 1d4ac21..cf7a896 100644 --- a/source/engines/frostbite_engine/traits/camera_trait.cpp +++ b/source/engines/frostbite_engine/traits/camera_trait.cpp @@ -2,26 +2,3 @@ // Created by Vlad on 8/11/2025. // #include "omath/engines/frostbite_engine/traits/camera_trait.hpp" - -namespace omath::frostbite_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.y)), - YawAngle::from_radians(std::atan2(direction.x, direction.z)), RollAngle::from_radians(0.f)}; - } - Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept - { - return frostbite_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::unity_engine \ No newline at end of file diff --git a/source/engines/iw_engine/formulas.cpp b/source/engines/iw_engine/formulas.cpp index 2437065..6b1cc8e 100644 --- a/source/engines/iw_engine/formulas.cpp +++ b/source/engines/iw_engine/formulas.cpp @@ -2,78 +2,3 @@ // 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); - } - - 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.y), - YawAngle::from_degrees(angles.z), - RollAngle::from_degrees(angles.x), - }; - } - - 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, const NDCDepthRange ndc_depth_range) noexcept - { - // InfinityWard Engine (inherited from Quake) stores FOV as horizontal FOV at a 4:3 - // reference aspect. Convert to true vertical FOV, then delegate to the - // standard vertical-FOV left-handed builder with the caller's actual - // aspect ratio. - // vfov = 2 · atan( tan(hfov_4:3 / 2) / (4/3) ) - constexpr float k_source_reference_aspect = 4.f / 3.f; - const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect); - - if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) - return mat_perspective_left_handed_vertical_fov< - float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>( - vertical_fov, aspect_ratio, near, far); - if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) - return mat_perspective_left_handed_vertical_fov< - float, MatStoreType::ROW_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( - vertical_fov, aspect_ratio, near, far); - std::unreachable(); - }; -} // namespace omath::iw_engine diff --git a/source/engines/iw_engine/traits/camera_trait.cpp b/source/engines/iw_engine/traits/camera_trait.cpp index 14fbd7b..70794f1 100644 --- a/source/engines/iw_engine/traits/camera_trait.cpp +++ b/source/engines/iw_engine/traits/camera_trait.cpp @@ -2,26 +2,3 @@ // 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 direction = (look_at - cam_origin).normalized(); - - return {PitchAngle::from_radians(-std::asin(direction.z)), - YawAngle::from_radians(std::atan2(direction.y, direction.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, 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::iw_engine \ No newline at end of file diff --git a/source/engines/opengl_engine/formulas.cpp b/source/engines/opengl_engine/formulas.cpp index 5a1a7e9..6b3c8b5 100644 --- a/source/engines/opengl_engine/formulas.cpp +++ b/source/engines/opengl_engine/formulas.cpp @@ -2,72 +2,3 @@ // Created by Vlad on 3/19/2025. // #include "omath/engines/opengl_engine/formulas.hpp" - -namespace omath::opengl_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_look_at_right_handed(cam_origin, cam_origin + forward_vector(angles), up_vector(angles)); - } - Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept - { - return mat_rotation_axis_z(angles.roll) - * mat_rotation_axis_y(angles.yaw) - * 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.y), - RollAngle::from_degrees(angles.z), - }; - } - - 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::NEGATIVE_ONE_TO_ONE) - return mat_perspective_right_handed_vertical_fov( - field_of_view, aspect_ratio, near, far); - - if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) - return mat_perspective_right_handed_vertical_fov( - field_of_view, aspect_ratio, near, far); - - std::unreachable(); - } -} // namespace omath::opengl_engine diff --git a/source/engines/opengl_engine/traits/camera_trait.cpp b/source/engines/opengl_engine/traits/camera_trait.cpp index 6addb71..1797702 100644 --- a/source/engines/opengl_engine/traits/camera_trait.cpp +++ b/source/engines/opengl_engine/traits/camera_trait.cpp @@ -2,27 +2,3 @@ // 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 direction = (look_at - cam_origin).normalized(); - - return {PitchAngle::from_radians(std::asin(direction.y)), - YawAngle::from_radians(-std::atan2(direction.x, -direction.z)), 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, 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::opengl_engine \ No newline at end of file diff --git a/source/engines/rage_engine/formulas.cpp b/source/engines/rage_engine/formulas.cpp index 3ef1f38..99b070a 100644 --- a/source/engines/rage_engine/formulas.cpp +++ b/source/engines/rage_engine/formulas.cpp @@ -2,70 +2,3 @@ // Created by Orange on 6/3/2026. // #include "omath/engines/rage_engine/formulas.hpp" - -namespace omath::rage_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::rage_engine diff --git a/source/engines/rage_engine/traits/camera_trait.cpp b/source/engines/rage_engine/traits/camera_trait.cpp index b9e7b28..df4f739 100644 --- a/source/engines/rage_engine/traits/camera_trait.cpp +++ b/source/engines/rage_engine/traits/camera_trait.cpp @@ -2,26 +2,3 @@ // Created by Orange on 6/3/2026. // #include "omath/engines/rage_engine/traits/camera_trait.hpp" - -namespace omath::rage_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 rage_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::rage_engine diff --git a/source/engines/source_engine/formulas.cpp b/source/engines/source_engine/formulas.cpp index c3c762b..6fcb5e1 100644 --- a/source/engines/source_engine/formulas.cpp +++ b/source/engines/source_engine/formulas.cpp @@ -2,78 +2,3 @@ // Created by Vlad on 3/19/2025. // #include - -namespace omath::source_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)}; - } - - 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); - } - - 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.y), - YawAngle::from_degrees(angles.z), - RollAngle::from_degrees(angles.x), - }; - } - - 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 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 - { - // Source (inherited from Quake) stores FOV as horizontal FOV at a 4:3 - // reference aspect. Convert to true vertical FOV, then delegate to the - // standard vertical-FOV left-handed builder with the caller's actual - // aspect ratio. - // vfov = 2 · atan( tan(hfov_4:3 / 2) / (4/3) ) - constexpr float k_source_reference_aspect = 4.f / 3.f; - const auto vertical_fov = angles::horizontal_fov_to_vertical(field_of_view, k_source_reference_aspect); - - if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) - return mat_perspective_left_handed_vertical_fov< - float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>( - vertical_fov, aspect_ratio, near, far); - if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) - return mat_perspective_left_handed_vertical_fov< - float, MatStoreType::ROW_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( - vertical_fov, aspect_ratio, near, far); - std::unreachable(); - } -} // namespace omath::source_engine diff --git a/source/engines/source_engine/traits/camera_trait.cpp b/source/engines/source_engine/traits/camera_trait.cpp index 2663d34..d365696 100644 --- a/source/engines/source_engine/traits/camera_trait.cpp +++ b/source/engines/source_engine/traits/camera_trait.cpp @@ -2,27 +2,3 @@ // 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 direction = (look_at - cam_origin).normalized(); - - - return {PitchAngle::from_radians(-std::asin(direction.z)), - YawAngle::from_radians(std::atan2(direction.y, direction.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, 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::source_engine \ No newline at end of file diff --git a/source/engines/unity_engine/formulas.cpp b/source/engines/unity_engine/formulas.cpp index 370191e..719f189 100644 --- a/source/engines/unity_engine/formulas.cpp +++ b/source/engines/unity_engine/formulas.cpp @@ -2,69 +2,3 @@ // Created by Vlad on 3/22/2025. // #include "omath/engines/unity_engine/formulas.hpp" - -namespace omath::unity_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.roll) - * mat_rotation_axis_y(angles.yaw) - * 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.y), - RollAngle::from_degrees(angles.z), - }; - } - - 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 omath::mat_perspective_right_handed_vertical_fov( - field_of_view, aspect_ratio, near, far); - if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) - return omath::mat_perspective_right_handed_vertical_fov(field_of_view, aspect_ratio, - near, far); - std::unreachable(); - } -} // namespace omath::unity_engine diff --git a/source/engines/unity_engine/traits/camera_trait.cpp b/source/engines/unity_engine/traits/camera_trait.cpp index 1c88cb2..52fb651 100644 --- a/source/engines/unity_engine/traits/camera_trait.cpp +++ b/source/engines/unity_engine/traits/camera_trait.cpp @@ -2,26 +2,3 @@ // 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 direction = (look_at - cam_origin).normalized(); - - return {PitchAngle::from_radians(-std::asin(direction.y)), - YawAngle::from_radians(std::atan2(direction.x, direction.z)), 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, 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::unity_engine \ No newline at end of file diff --git a/source/engines/unreal_engine/formulas.cpp b/source/engines/unreal_engine/formulas.cpp index d972118..5820e54 100644 --- a/source/engines/unreal_engine/formulas.cpp +++ b/source/engines/unreal_engine/formulas.cpp @@ -2,76 +2,3 @@ // Created by Vlad on 3/22/2025. // #include "omath/engines/unreal_engine/formulas.hpp" -namespace omath::unreal_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 - { - // UE FRotator is intrinsic Z-Y-X (Yaw → Pitch → Roll applied in local - // frame), which for column-vector composition is Rz·Ry·Rx. - // Pitch and roll axes in omath spin opposite to UE's convention, so - // both carry a sign flip. - return mat_rotation_axis_z(angles.yaw) - * mat_rotation_axis_y(-angles.pitch) - * mat_rotation_axis_x(-angles.roll); - } - - - 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.y), - YawAngle::from_degrees(angles.z), - RollAngle::from_degrees(-angles.x), - }; - } - - Mat4X4 calc_perspective_projection_matrix(const double field_of_view, const double aspect_ratio, const double near, - const double far, const NDCDepthRange ndc_depth_range) noexcept - { - // UE stores horizontal FOV in FMinimalViewInfo — use the left-handed - // horizontal-FOV builder directly. - if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE) - return mat_perspective_left_handed_horizontal_fov< - double, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>( - field_of_view, aspect_ratio, near, far); - if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE) - return mat_perspective_left_handed_horizontal_fov< - double, MatStoreType::ROW_MAJOR, NDCDepthRange::NEGATIVE_ONE_TO_ONE>( - field_of_view, aspect_ratio, near, far); - std::unreachable(); - } -} // namespace omath::unreal_engine diff --git a/source/engines/unreal_engine/traits/camera_trait.cpp b/source/engines/unreal_engine/traits/camera_trait.cpp index f652d3e..24d8a38 100644 --- a/source/engines/unreal_engine/traits/camera_trait.cpp +++ b/source/engines/unreal_engine/traits/camera_trait.cpp @@ -2,26 +2,3 @@ // 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 direction = (look_at - cam_origin).normalized(); - - return {PitchAngle::from_radians(std::asin(direction.z)), - YawAngle::from_radians(std::atan2(direction.y, direction.x)), 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 double near, - const double 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::unreal_engine \ No newline at end of file diff --git a/tests/engines/unit_test_traits_engines.cpp b/tests/engines/unit_test_traits_engines.cpp index d01efc0..6d1a113 100644 --- a/tests/engines/unit_test_traits_engines.cpp +++ b/tests/engines/unit_test_traits_engines.cpp @@ -1,30 +1,40 @@ // Tests for engine trait headers to improve header coverage #include +#include #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include #include +#include #include #include +#include #include +#include +#include + #include #include #include @@ -41,6 +51,43 @@ static void expect_matrix_near(const MatT& a, const MatT& b, float eps = 1e-5f) } // ── Launch offset tests for all engines ────────────────────────────────────── +#ifdef OMATH_USE_GCEM +template +constexpr bool matrix_has_non_zero_element(const MatType& mat) +{ + for (std::size_t r = 0; r < 4; ++r) + for (std::size_t c = 0; c < 4; ++c) + if (mat.at(r, c) != NumericType{0}) + return true; + + return false; +} + +template +constexpr bool camera_can_calculate_matrices_at_compile_time() +{ + using FieldOfView = projection::FieldOfView; + + CameraType camera{{}, {}, {1280.f, 720.f}, FieldOfView::from_degrees(90.f), NumericType{0.01}, NumericType{1000}}; + const auto view = camera.get_view_matrix(); + const auto projection = camera.get_projection_matrix(); + const auto view_projection = camera.get_view_projection_matrix(); + + return matrix_has_non_zero_element(view) + && matrix_has_non_zero_element(projection) + && matrix_has_non_zero_element(view_projection); +} + +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +static_assert(camera_can_calculate_matrices_at_compile_time()); +#endif + #include // Helper: verify that zero offset matches default-initialized offset behavior diff --git a/tests/general/unit_test_projection.cpp b/tests/general/unit_test_projection.cpp index 8f3b79c..0c5cebd 100644 --- a/tests/general/unit_test_projection.cpp +++ b/tests/general/unit_test_projection.cpp @@ -60,15 +60,37 @@ static void verify_random_look_at_targets_project_to_screen_center(const omath:: } } +#ifdef OMATH_USE_GCEM +constexpr bool source_camera_constexpr_projection_round_trip() +{ + constexpr auto fov = omath::Angle::from_degrees(90.f); + constexpr auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, + fov, 0.01f, 1000.f); + + constexpr auto projected = cam.world_to_screen({1000.f, 0, 50.f}); + constexpr auto result = cam.screen_to_world(projected.value()); + constexpr auto result2 = cam.world_to_screen(result.value()); + + return projected.has_value() && result.has_value() && result2.has_value() + && static_cast>(projected.value()) + == static_cast>(result2.value()) + && omath::internal::abs(projected->x - 960.f) < 0.001f + && omath::internal::abs(projected->y - 504.f) < 0.001f + && omath::internal::abs(projected->z - 1.f) < 0.001f; +} + +static_assert(source_camera_constexpr_projection_round_trip()); +#endif + TEST(UnitTestProjection, Projection) { constexpr auto fov = omath::Angle::from_degrees(90.f); - const auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov, + constexpr auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - const auto projected = cam.world_to_screen({1000.f, 0, 50.f}); - const auto result = cam.screen_to_world(projected.value()); - const auto result2 = cam.world_to_screen(result.value()); + constexpr auto projected = cam.world_to_screen({1000.f, 0, 50.f}); + constexpr auto result = cam.screen_to_world(projected.value()); + constexpr auto result2 = cam.world_to_screen(result.value()); EXPECT_EQ(static_cast>(projected.value()), static_cast>(result2.value()));