From b7210dec4cbed152cc503e8014b70100edcfbd5c Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 3 Jun 2026 13:42:13 +0300 Subject: [PATCH 1/2] added rage engine support fix patch --- include/omath/engines/rage_engine/camera.hpp | 13 +++ .../omath/engines/rage_engine/constants.hpp | 25 +++++ .../omath/engines/rage_engine/formulas.hpp | 85 +++++++++++++++ include/omath/engines/rage_engine/mesh.hpp | 13 +++ .../rage_engine/traits/camera_trait.hpp | 24 +++++ .../engines/rage_engine/traits/mesh_trait.hpp | 20 ++++ .../rage_engine/traits/pred_engine_trait.hpp | 76 +++++++++++++ .../renderer_realizations/imgui_renderer.hpp | 2 +- include/omath/omath.hpp | 8 +- source/engines/rage_engine/formulas.cpp | 71 ++++++++++++ .../rage_engine/traits/camera_trait.cpp | 27 +++++ source/lua/lua_engines.cpp | 8 ++ tests/engines/unit_test_rage_engine.cpp | 101 ++++++++++++++++++ 13 files changed, 471 insertions(+), 2 deletions(-) create mode 100644 include/omath/engines/rage_engine/camera.hpp create mode 100644 include/omath/engines/rage_engine/constants.hpp create mode 100644 include/omath/engines/rage_engine/formulas.hpp create mode 100644 include/omath/engines/rage_engine/mesh.hpp create mode 100644 include/omath/engines/rage_engine/traits/camera_trait.hpp create mode 100644 include/omath/engines/rage_engine/traits/mesh_trait.hpp create mode 100644 include/omath/engines/rage_engine/traits/pred_engine_trait.hpp create mode 100644 source/engines/rage_engine/formulas.cpp create mode 100644 source/engines/rage_engine/traits/camera_trait.cpp create mode 100644 tests/engines/unit_test_rage_engine.cpp diff --git a/include/omath/engines/rage_engine/camera.hpp b/include/omath/engines/rage_engine/camera.hpp new file mode 100644 index 00000000..2000bdc3 --- /dev/null +++ b/include/omath/engines/rage_engine/camera.hpp @@ -0,0 +1,13 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "omath/engines/rage_engine/constants.hpp" +#include "omath/projection/camera.hpp" +#include "traits/camera_trait.hpp" + +namespace omath::rage_engine +{ + using Camera = projection::Camera; +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/constants.hpp b/include/omath/engines/rage_engine/constants.hpp new file mode 100644 index 00000000..b60860bf --- /dev/null +++ b/include/omath/engines/rage_engine/constants.hpp @@ -0,0 +1,25 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "omath/linear_algebra/mat.hpp" +#include "omath/linear_algebra/vector3.hpp" +#include +#include + +namespace omath::rage_engine +{ + constexpr Vector3 k_abs_up = {0, 0, 1}; + constexpr Vector3 k_abs_right = {1, 0, 0}; + constexpr Vector3 k_abs_forward = {0, 1, 0}; + + using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>; + using Mat3X3 = Mat<3, 3, float, MatStoreType::ROW_MAJOR>; + using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>; + using PitchAngle = Angle; + using YawAngle = Angle; + using RollAngle = Angle; + + using ViewAngles = omath::ViewAngles; +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/formulas.hpp b/include/omath/engines/rage_engine/formulas.hpp new file mode 100644 index 00000000..a0a888c7 --- /dev/null +++ b/include/omath/engines/rage_engine/formulas.hpp @@ -0,0 +1,85 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "omath/engines/rage_engine/constants.hpp" +#include + +namespace omath::rage_engine +{ + [[nodiscard]] + Vector3 forward_vector(const ViewAngles& angles) noexcept; + + [[nodiscard]] + Vector3 right_vector(const ViewAngles& angles) noexcept; + + [[nodiscard]] + Vector3 up_vector(const ViewAngles& angles) noexcept; + + [[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; + + [[nodiscard]] + Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept; + + [[nodiscard]] + Vector3 extract_origin(const Mat4X4& mat) noexcept; + + [[nodiscard]] + Vector3 extract_scale(const Mat4X4& mat) noexcept; + + [[nodiscard]] + ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept; + + [[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; + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType units_to_centimeters(const FloatingType& units) + { + return units / static_cast(100); + } + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType units_to_meters(const FloatingType& units) + { + return units; + } + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType units_to_kilometers(const FloatingType& units) + { + return units_to_meters(units) / static_cast(1000); + } + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType centimeters_to_units(const FloatingType& centimeters) + { + return centimeters * static_cast(100); + } + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType meters_to_units(const FloatingType& meters) + { + return meters; + } + + template + requires std::is_floating_point_v + [[nodiscard]] + constexpr FloatingType kilometers_to_units(const FloatingType& kilometers) + { + return meters_to_units(kilometers * static_cast(1000)); + } +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/mesh.hpp b/include/omath/engines/rage_engine/mesh.hpp new file mode 100644 index 00000000..27ad1baa --- /dev/null +++ b/include/omath/engines/rage_engine/mesh.hpp @@ -0,0 +1,13 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "constants.hpp" +#include "omath/3d_primitives/mesh.hpp" +#include "traits/mesh_trait.hpp" + +namespace omath::rage_engine +{ + using Mesh = primitives::Mesh; +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/traits/camera_trait.hpp b/include/omath/engines/rage_engine/traits/camera_trait.hpp new file mode 100644 index 00000000..773c38e7 --- /dev/null +++ b/include/omath/engines/rage_engine/traits/camera_trait.hpp @@ -0,0 +1,24 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "omath/engines/rage_engine/formulas.hpp" +#include "omath/projection/camera.hpp" + +namespace omath::rage_engine +{ + class CameraTrait final + { + public: + [[nodiscard]] + static ViewAngles calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept; + + [[nodiscard]] + static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept; + [[nodiscard]] + static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port, + float near, float far, NDCDepthRange ndc_depth_range) noexcept; + }; + +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/traits/mesh_trait.hpp b/include/omath/engines/rage_engine/traits/mesh_trait.hpp new file mode 100644 index 00000000..5cc57e9d --- /dev/null +++ b/include/omath/engines/rage_engine/traits/mesh_trait.hpp @@ -0,0 +1,20 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include +#include + +namespace omath::rage_engine +{ + class MeshTrait final + { + public: + [[nodiscard]] + static Mat4X4 rotation_matrix(const ViewAngles& rotation) + { + return rage_engine::rotation_matrix(rotation); + } + }; +} // namespace omath::rage_engine diff --git a/include/omath/engines/rage_engine/traits/pred_engine_trait.hpp b/include/omath/engines/rage_engine/traits/pred_engine_trait.hpp new file mode 100644 index 00000000..8fb2e712 --- /dev/null +++ b/include/omath/engines/rage_engine/traits/pred_engine_trait.hpp @@ -0,0 +1,76 @@ +// +// Created by Codex on 6/3/2026. +// + +#pragma once +#include "omath/engines/rage_engine/formulas.hpp" +#include "omath/projectile_prediction/projectile.hpp" +#include "omath/projectile_prediction/target.hpp" +#include + +namespace omath::rage_engine +{ + class PredEngineTrait final + { + public: + constexpr static Vector3 + predict_projectile_position(const projectile_prediction::Projectile& projectile, const float pitch, + const float yaw, const float time, const float gravity) noexcept + { + const auto launch_pos = projectile.m_origin + projectile.m_launch_offset; + auto current_pos = launch_pos + + forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw), + RollAngle::from_degrees(0)}) + * projectile.m_launch_speed * time; + current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; + + return current_pos; + } + [[nodiscard]] + static constexpr Vector3 predict_target_position(const projectile_prediction::Target& target, + const float time, const float gravity) noexcept + { + auto predicted = target.m_origin + target.m_velocity * time; + + if (target.m_is_airborne) + predicted.z -= gravity * (time * time) * 0.5f; + + return predicted; + } + [[nodiscard]] + static float calc_vector_2d_distance(const Vector3& delta) noexcept + { + return std::sqrt(delta.x * delta.x + delta.y * delta.y); + } + + [[nodiscard]] + constexpr static float get_vector_height_coordinate(const Vector3& vec) noexcept + { + return vec.z; + } + + [[nodiscard]] + static Vector3 calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile, + Vector3 predicted_target_position, + const std::optional projectile_pitch) noexcept + { + const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin); + const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value())); + + return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height}; + } + [[nodiscard]] + static float calc_direct_pitch_angle(const Vector3& origin, const Vector3& view_to) noexcept + { + const auto direction = (view_to - origin).normalized(); + return angles::radians_to_degrees(std::asin(direction.z)); + } + [[nodiscard]] + static float calc_direct_yaw_angle(const Vector3& origin, const Vector3& view_to) noexcept + { + const auto direction = (view_to - origin).normalized(); + + return angles::radians_to_degrees(-std::atan2(direction.x, direction.y)); + }; + }; +} // namespace omath::rage_engine diff --git a/include/omath/hud/renderer_realizations/imgui_renderer.hpp b/include/omath/hud/renderer_realizations/imgui_renderer.hpp index 8b8190b4..a7e13647 100644 --- a/include/omath/hud/renderer_realizations/imgui_renderer.hpp +++ b/include/omath/hud/renderer_realizations/imgui_renderer.hpp @@ -27,7 +27,7 @@ namespace omath::hud const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) override; void add_text(const Vector2& position, const Color& color, const std::string_view& text) override; [[nodiscard]] - virtual Vector2 calc_text_size(const std::string_view& text) override; + Vector2 calc_text_size(const std::string_view& text) override; }; } // namespace omath::hud #endif // OMATH_IMGUI_INTEGRATION \ No newline at end of file diff --git a/include/omath/omath.hpp b/include/omath/omath.hpp index bcf503a3..f249eacb 100644 --- a/include/omath/omath.hpp +++ b/include/omath/omath.hpp @@ -87,6 +87,12 @@ #include "omath/engines/frostbite_engine/traits/camera_trait.hpp" #include "omath/engines/frostbite_engine/traits/pred_engine_trait.hpp" +// RAGE Engine +#include "omath/engines/rage_engine/constants.hpp" +#include "omath/engines/rage_engine/formulas.hpp" +#include "omath/engines/rage_engine/camera.hpp" +#include "omath/engines/rage_engine/traits/camera_trait.hpp" +#include "omath/engines/rage_engine/traits/pred_engine_trait.hpp" // Unreal Engine #include "omath/engines/unreal_engine/constants.hpp" @@ -101,4 +107,4 @@ // Utility #include "omath/utility/pattern_scan.hpp" -#include "omath/utility/pe_pattern_scan.hpp" \ No newline at end of file +#include "omath/utility/pe_pattern_scan.hpp" diff --git a/source/engines/rage_engine/formulas.cpp b/source/engines/rage_engine/formulas.cpp new file mode 100644 index 00000000..ff2b3ced --- /dev/null +++ b/source/engines/rage_engine/formulas.cpp @@ -0,0 +1,71 @@ +// +// Created by Codex 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 new file mode 100644 index 00000000..8e1f5cd5 --- /dev/null +++ b/source/engines/rage_engine/traits/camera_trait.cpp @@ -0,0 +1,27 @@ +// +// Created by Codex 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/lua/lua_engines.cpp b/source/lua/lua_engines.cpp index 31a49994..f64cae29 100644 --- a/source/lua/lua_engines.cpp +++ b/source/lua/lua_engines.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -181,6 +182,12 @@ namespace using ViewAngles = omath::source_engine::ViewAngles; using Camera = omath::source_engine::Camera; }; + struct RageEngineTraits + { + using PitchAngle = omath::rage_engine::PitchAngle; + using ViewAngles = omath::rage_engine::ViewAngles; + using Camera = omath::rage_engine::Camera; + }; struct UnityEngineTraits { using PitchAngle = omath::unity_engine::PitchAngle; @@ -254,6 +261,7 @@ namespace omath::lua register_engine(omath_table, "frostbite"); register_engine(omath_table, "iw"); register_engine(omath_table, "source"); + register_engine(omath_table, "rage"); register_engine(omath_table, "unity"); register_engine(omath_table, "unreal"); register_engine(omath_table, "cry"); diff --git a/tests/engines/unit_test_rage_engine.cpp b/tests/engines/unit_test_rage_engine.cpp new file mode 100644 index 00000000..59cbaa61 --- /dev/null +++ b/tests/engines/unit_test_rage_engine.cpp @@ -0,0 +1,101 @@ +// +// Created by Codex on 6/3/2026. +// +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace omath; + +static_assert(std::is_same_v); + +static void expect_rage_vector_near(const Vector3& actual, const Vector3& expected) +{ + for (const auto& [result, etalon] : std::views::zip(actual.as_array(), expected.as_array())) + EXPECT_NEAR(result, etalon, 0.0001f); +} + +TEST(unit_test_rage_engine, ForwardVector) +{ + const auto forward = rage_engine::forward_vector({}); + + EXPECT_EQ(forward, rage_engine::k_abs_forward); +} + +TEST(unit_test_rage_engine, RightVector) +{ + const auto right = rage_engine::right_vector({}); + + EXPECT_EQ(right, rage_engine::k_abs_right); +} + +TEST(unit_test_rage_engine, UpVector) +{ + const auto up = rage_engine::up_vector({}); + EXPECT_EQ(up, rage_engine::k_abs_up); +} + +TEST(unit_test_rage_engine, LookAtForward) +{ + const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_forward); + + expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_forward); +} + +TEST(unit_test_rage_engine, LookAtRight) +{ + const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_right); + + expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_right); +} + +TEST(unit_test_rage_engine, LookAtUp) +{ + const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_up); + + expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_up); +} + +TEST(unit_test_rage_engine, ProjectTargetMovedFromCamera) +{ + constexpr auto fov = projection::FieldOfView::from_degrees(60.f); + const auto cam = rage_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f); + + const auto projected = cam.world_to_screen({0, 10.f, 0}); + + ASSERT_TRUE(projected.has_value()); + EXPECT_NEAR(projected->x, 640.f, 0.0001f); + EXPECT_NEAR(projected->y, 360.f, 0.0001f); +} + +TEST(unit_test_rage_engine, PredEngineTraitUsesZAsHeight) +{ + projectile_prediction::Projectile projectile; + projectile.m_origin = {0.f, 0.f, 0.f}; + projectile.m_launch_speed = 10.f; + projectile.m_gravity_scale = 1.f; + + const auto pos = rage_engine::PredEngineTrait::predict_projectile_position(projectile, 0.f, 0.f, 1.f, 9.81f); + + EXPECT_NEAR(pos.x, 0.f, 0.0001f); + EXPECT_NEAR(pos.y, 10.f, 0.0001f); + EXPECT_NEAR(pos.z, -9.81f * 0.5f, 0.0001f); + EXPECT_NEAR(rage_engine::PredEngineTrait::get_vector_height_coordinate({1.f, 2.f, 3.f}), 3.f, 0.0001f); +} + +TEST(unit_test_rage_engine, MeshTraitForwardsRotationMatrix) +{ + const rage_engine::ViewAngles angles{ + rage_engine::PitchAngle::from_degrees(20.f), + rage_engine::YawAngle::from_degrees(-35.f), + rage_engine::RollAngle::from_degrees(15.f), + }; + + EXPECT_EQ(rage_engine::MeshTrait::rotation_matrix(angles), rage_engine::rotation_matrix(angles)); +} From 8810ec20a527436d1e30636d35025d2a0026639f Mon Sep 17 00:00:00 2001 From: Orange Date: Wed, 3 Jun 2026 16:14:13 +0300 Subject: [PATCH 2/2] added rage into read me --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 9b7cf852..695344d8 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ if (auto screen = camera.world_to_screen(world_position)) { - **Collision Detection**: Production ready code to handle collision detection by using simple interfaces. - **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution - **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types! -- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**. +- **Engine support**: Supports coordinate systems of **Source, Rage, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**. - **Cross platform**: Supports Windows, MacOS and Linux. - **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps. - **Scripting**: Supports to make scripts in Lua out of box. @@ -113,6 +113,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
+![GTA5 Preview] + +
+ ![OpenGL Preview]
@@ -144,6 +148,7 @@ if (auto screen = camera.world_to_screen(world_position)) { [BO2 Preview]: docs/images/showcase/cod_bo2.png [CS2 Preview]: docs/images/showcase/cs2.jpeg [TF2 Preview]: docs/images/showcase/tf2.jpg +[GTA5 Preview]: https://i.imgur.com/W7T8RhZ.png [OpenGL Preview]: docs/images/showcase/opengl.png [QUICKSTART]: docs/getting_started.md