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