From 9e1990942b89c39fc8db9cd4dc5eec1c39fafb84 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 3 Aug 2025 18:28:47 +0300 Subject: [PATCH] Refactors projectile prediction engine Migrates projectile prediction logic to leverage engine traits for improved flexibility and testability. This change decouples core prediction algorithms from specific engine implementations, allowing for easier adaptation to different game engines or simulation environments. --- CMakeLists.txt | 2 +- .../engine_traits/source_engine_trait.hpp | 62 +++++++++++++ .../proj_pred_engine_legacy.hpp | 69 +++++++++----- .../proj_pred_engine_legacy.cpp | 92 ------------------- 4 files changed, 109 insertions(+), 116 deletions(-) create mode 100644 include/omath/projectile_prediction/engine_traits/source_engine_trait.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index af67dd5..2bad1e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ project(omath VERSION 3.0.2 LANGUAGES CXX) include(CMakePackageConfigHelpers) -option(OMATH_BUILD_TESTS "Build unit tests" OFF) +option(OMATH_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL}) option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON) option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF) option(OMATH_USE_AVX2 "Omath will use AVX2 to boost performance" ON) diff --git a/include/omath/projectile_prediction/engine_traits/source_engine_trait.hpp b/include/omath/projectile_prediction/engine_traits/source_engine_trait.hpp new file mode 100644 index 0000000..367e93c --- /dev/null +++ b/include/omath/projectile_prediction/engine_traits/source_engine_trait.hpp @@ -0,0 +1,62 @@ +// +// Created by Vlad on 8/3/2025. +// + +#pragma once +#include "omath/engines/source_engine/formulas.hpp" +#include "omath/projectile_prediction/projectile.hpp" +#include + +namespace omath::projectile_prediction::traits +{ + class SourceEngineTrait final + { + public: + constexpr static Vector3 predict_projectile_position(const Projectile& projectile, const float pitch, + const float yaw, const float time, + const float gravity) noexcept + { + auto current_pos = projectile.m_origin + + source_engine::forward_vector({source_engine::PitchAngle::from_degrees(-pitch), + source_engine::YawAngle::from_degrees(yaw), + source_engine::RollAngle::from_degrees(0)}) + * projectile.m_launch_speed * time; + current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; + + return current_pos; + } + + static bool is_projectile_reached_target(const Vector3& target_position, + const Projectile& projectile, const float pitch, + const float time, const float gravity, + const float tolerance) noexcept + { + const auto yaw = projectile.m_origin.view_angle_to(target_position).y; + const auto projectile_position = predict_projectile_position(projectile, pitch, yaw, time, gravity); + + return projectile_position.distance_to(target_position) <= tolerance; + } + [[nodiscard]] + static float calc_vector_2d_distance(const Vector3& delta) + { + return std::sqrt(delta.x * delta.x + delta.y * delta.y); + } + + [[nodiscard]] + constexpr static float get_vector_height_coordinate(const Vector3& vec) + { + return vec.z; + } + + [[nodiscard]] + static Vector3 calc_viewpoint_from_angles(const Projectile& projectile, + Vector3 predicted_target_position, + const std::optional projectile_pitch) + { + 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}; + } + }; +} // namespace omath::projectile_prediction::traits \ No newline at end of file diff --git a/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp b/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp index 4de7a8c..f463f84 100644 --- a/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp +++ b/include/omath/projectile_prediction/proj_pred_engine_legacy.hpp @@ -4,6 +4,7 @@ #pragma once +#include "engine_traits/source_engine_trait.hpp" #include "omath/projectile_prediction/proj_pred_engine.hpp" #include "omath/projectile_prediction/projectile.hpp" #include "omath/projectile_prediction/target.hpp" @@ -13,15 +14,40 @@ namespace omath::projectile_prediction { // ReSharper disable once CppClassCanBeFinal + template class ProjPredEngineLegacy : public ProjPredEngineInterface { public: - explicit ProjPredEngineLegacy(float gravity_constant, float simulation_time_step, float maximum_simulation_time, - float distance_tolerance); + explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, + const float maximum_simulation_time, const float distance_tolerance) + : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), + m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) + { + } [[nodiscard]] std::optional> maybe_calculate_aim_point(const Projectile& projectile, - const Target& target) const override; + const Target& target) const override + { + for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) + { + const auto predicted_target_position = target.predict_position(time, m_gravity_constant); + + const auto projectile_pitch = + maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); + + if (!projectile_pitch.has_value()) [[unlikely]] + continue; + + if (!EngineTrait::is_projectile_reached_target(predicted_target_position, projectile, + projectile_pitch.value(), time, m_gravity_constant, + m_distance_tolerance)) + continue; + + return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); + } + return std::nullopt; + } private: const float m_gravity_constant; @@ -44,30 +70,27 @@ namespace omath::projectile_prediction [[nodiscard]] std::optional maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, - const Vector3& target_position) const noexcept; + const Vector3& target_position) const noexcept + { + const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; + const auto delta = target_position - projectile.m_origin; - [[nodiscard]] - bool is_projectile_reached_target(const Vector3& target_position, const Projectile& projectile, - float pitch, float time) const noexcept; + const auto distance2d = EngineTrait::calc_vector_2d_distance(delta); + const auto distance2d_sqr = distance2d * distance2d; + const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; - protected: - // NOTE: Override this if you need to use engine with different coordinate system - // Like where Z is not height coordinate - // =============================================================================================== - [[nodiscard]] - virtual float calc_vector_2d_distance(const Vector3& delta) const; + float root = launch_speed_sqr * launch_speed_sqr + - bullet_gravity + * (bullet_gravity * distance2d_sqr + + 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr); - [[nodiscard]] - virtual float get_vector_height_coordinate(const Vector3& vec) const; + if (root < 0.0f) [[unlikely]] + return std::nullopt; - [[nodiscard]] - virtual Vector3 calc_viewpoint_from_angles(const Projectile& projectile, - Vector3 predicted_target_position, - std::optional projectile_pitch) const; + root = std::sqrt(root); + const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); - [[nodiscard]] - virtual Vector3 predict_projectile_position(const Projectile& projectile, float pitch, float yaw, - float time, float gravity) const; - // =============================================================================================== + return angles::radians_to_degrees(angle); + } }; } // namespace omath::projectile_prediction diff --git a/source/projectile_prediction/proj_pred_engine_legacy.cpp b/source/projectile_prediction/proj_pred_engine_legacy.cpp index 96fd7a0..7ea7d15 100644 --- a/source/projectile_prediction/proj_pred_engine_legacy.cpp +++ b/source/projectile_prediction/proj_pred_engine_legacy.cpp @@ -4,97 +4,5 @@ namespace omath::projectile_prediction { - ProjPredEngineLegacy::ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step, - const float maximum_simulation_time, const float distance_tolerance) - : m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step), - m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance) - { - } - std::optional> ProjPredEngineLegacy::maybe_calculate_aim_point(const Projectile& projectile, - const Target& target) const - { - for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step) - { - const auto predicted_target_position = target.predict_position(time, m_gravity_constant); - - const auto projectile_pitch = - maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position); - - if (!projectile_pitch.has_value()) [[unlikely]] - continue; - - if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), time)) - continue; - - return calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch); - } - return std::nullopt; - } - - std::optional ProjPredEngineLegacy::maybe_calculate_projectile_launch_pitch_angle( - const Projectile& projectile, const Vector3& target_position) const noexcept - { - const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale; - const auto delta = target_position - projectile.m_origin; - - const auto distance2d = calc_vector_2d_distance(delta); - const auto distance2d_sqr = distance2d * distance2d; - const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed; - - float root = launch_speed_sqr * launch_speed_sqr - - bullet_gravity - * (bullet_gravity * distance2d_sqr - + 2.0f * get_vector_height_coordinate(delta) * launch_speed_sqr); - - if (root < 0.0f) [[unlikely]] - return std::nullopt; - - root = std::sqrt(root); - const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d)); - - return angles::radians_to_degrees(angle); - } - - bool ProjPredEngineLegacy::is_projectile_reached_target(const Vector3& target_position, - const Projectile& projectile, const float pitch, - const float time) const noexcept - { - const auto yaw = projectile.m_origin.view_angle_to(target_position).y; - const auto projectile_position = predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant); - - return projectile_position.distance_to(target_position) <= m_distance_tolerance; - } - - float ProjPredEngineLegacy::calc_vector_2d_distance(const Vector3& delta) const - { - return std::sqrt(delta.x * delta.x + delta.y * delta.y); - } - - float ProjPredEngineLegacy::get_vector_height_coordinate(const Vector3& vec) const - { - return vec.z; - } - Vector3 ProjPredEngineLegacy::calc_viewpoint_from_angles(const Projectile& projectile, - const Vector3 predicted_target_position, - const std::optional projectile_pitch) const - { - 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}; - } - Vector3 ProjPredEngineLegacy::predict_projectile_position(const Projectile& projectile, const float pitch, - const float yaw, const float time, - const float gravity) const - { - auto current_pos = projectile.m_origin - + source_engine::forward_vector({source_engine::PitchAngle::from_degrees(-pitch), - source_engine::YawAngle::from_degrees(yaw), - source_engine::RollAngle::from_degrees(0)}) - * projectile.m_launch_speed * time; - current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f; - - return current_pos; - } } // namespace omath::projectile_prediction