Compare commits

..

14 Commits

Author SHA1 Message Date
ea8f3d8d51 Adds contributing guidelines
Introduces a CONTRIBUTING.MD file to provide guidelines for
contributing to the project, including prerequisites, setup
instructions, pull request workflow, code style, and building
instructions.

xd

returned back

patch
2025-08-06 06:23:06 +03:00
08d2ccc03a Refactors Vector operations for type safety
Ensures type safety in Vector2, Vector3, and Vector4 operations by using static_cast(0) instead of relying on implicit conversions.
This prevents potential issues with different numeric types.

Adds from_im_vec2 and from_im_vec4 methods for creating vectors from ImVec2/ImVec4 types.
2025-08-06 06:06:42 +03:00
21ec23d77b patch 2025-08-06 05:56:09 +03:00
2c4ff37062 Merge pull request #52 from orange-cpp/feature/more_traits
Adds engine traits for projectile prediction
2025-08-06 05:49:25 +03:00
695a8035b5 Adds engine traits for projectile prediction
Implements engine-specific traits for projectile and target position prediction.
Each trait encapsulates logic tailored to a specific game engine (IW, OpenGL, Unity),
accounting for engine-specific coordinate systems and calculations.
This allows for accurate projectile prediction across different game environments.
2025-08-06 05:45:37 +03:00
d12b236e56 Refactors target position prediction
Moves target prediction logic into engine traits, improving modularity.

This change consolidates target position prediction within the engine traits,
allowing for a more flexible and maintainable design.

This eliminates redundant code and simplifies the core prediction engine by
delegating target movement calculations to the appropriate trait.
2025-08-04 03:16:04 +03:00
7a5090d9f6 Marks legacy engine class as final
Prevents further inheritance from the legacy projectile prediction engine class.
2025-08-04 01:12:22 +03:00
ec76a7239c Adds direct pitch angle calculation
Implements a direct pitch angle calculation for scenarios with zero gravity, ensuring accurate projectile trajectory predictions in such conditions.

Also marks several methods as noexcept for better performance and exception safety.
2025-08-04 01:11:11 +03:00
2758f549a3 Updates project version and removes legacy code
Updates the project version to prepare for a new release.

Removes the legacy projectile prediction engine, which is no longer needed.
2025-08-03 18:35:52 +03:00
493931ef0f Ignores vcpkg directory
Excludes the vcpkg directory from being tracked by Git.

This prevents accidental commits of external library files
managed by vcpkg, keeping the repository cleaner.
2025-08-03 18:31:02 +03:00
9e1990942b 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.
2025-08-03 18:28:47 +03:00
f1984fbe46 Merge pull request #51 from orange-cpp/feature/projectile_pred_custom
Refactors projectile prediction engine
2025-08-03 17:38:08 +03:00
f1fbea21a7 Refactors projectile prediction engine
Refactors the projectile prediction engine by introducing an interface
and making the legacy implementation more flexible.

The legacy engine is updated to allow for coordinate system customization
through virtual methods, enabling usage in different game environments.

Also introduces vcpkg support for easier dependency management and adds boost-asio as a dependency.
2025-08-03 17:33:22 +03:00
4b44ce0667 Documents projectile launch angle formula
Adds a comment documenting the formula used for calculating the projectile launch pitch angle.

The comment includes a link to the Stack Overflow discussion where the formula was found and the LaTeX representation of the formula for clarity.
2025-07-31 21:52:16 +03:00
17 changed files with 451 additions and 115 deletions

3
.gitignore vendored
View File

@@ -1,4 +1,5 @@
/cmake-build/
/.idea
/out
*.DS_Store
*.DS_Store
/extlibs/vcpkg

View File

@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.26)
project(omath VERSION 3.0.2 LANGUAGES CXX)
project(omath VERSION 3.0.4.1 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)

32
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,32 @@
## 🤝 Contributing to OMath or other Orange's Projects
### ❕ Prerequisites
- A working up-to-date OMath installation
- C++ knowledge
- Git knowledge
- Ability to ask for help (Feel free to create empty pull-request or PM a maintainer
in [Telegram](https://t.me/orange_cpp))
### ⏬ Setting up OMath
Please read INSTALL.md file in repository
### 🔀 Pull requests and Branches
In order to send code back to the official OMath repository, you must first create a copy of OMath on your github
account ([fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)) and
then [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) back to OMath.
OMath developement is performed on multiple branches. Changes are then pull requested into master. By default, changes
merged into master will not roll out to stable build users unless the `stable` tag is updated.
### 📜 Code-Style
The orange code-style can be found in `.clang-format`.
### 📦 Building
OMath has already created the `cmake-build` and `out` directories where cmake/bin files are located. By default, you
can build OMath by running `cmake --build cmake-build/build/windows-release --target omath -j 6` in the source
directory.

View File

@@ -0,0 +1,80 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/iw_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::projectile_prediction::traits
{
class IwEngineTrait final
{
public:
constexpr static Vector3<float> 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
+ iw_engine::forward_vector({iw_engine::PitchAngle::from_degrees(-pitch),
iw_engine::YawAngle::from_degrees(yaw),
iw_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;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const 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<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> 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};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.z / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.y, delta.x));
};
};
} // namespace omath::projectile_prediction::traits

View File

@@ -0,0 +1,79 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/opengl_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::projectile_prediction::traits
{
class OpenGlEngineTrait final
{
public:
constexpr static Vector3<float> 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
+ opengl_engine::forward_vector({opengl_engine::PitchAngle::from_degrees(-pitch),
opengl_engine::YawAngle::from_degrees(yaw),
opengl_engine::RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const Target& target, const float time,
const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.y;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> 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 + height, projectile.m_origin.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.y / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
};
};
} // namespace omath::projectile_prediction::traits

View File

@@ -0,0 +1,80 @@
//
// Created by Vlad on 8/3/2025.
//
#pragma once
#include "omath/engines/source_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::projectile_prediction::traits
{
class SourceEngineTrait final
{
public:
constexpr static Vector3<float> 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;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const 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<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> 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};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.z / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.y, delta.x));
};
};
} // namespace omath::projectile_prediction::traits

View File

@@ -0,0 +1,79 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/unity_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::projectile_prediction::traits
{
class UnityEngineTrait final
{
public:
constexpr static Vector3<float> 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
+ unity_engine::forward_vector({unity_engine::PitchAngle::from_degrees(-pitch),
unity_engine::YawAngle::from_degrees(yaw),
unity_engine::RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const Target& target, const float time,
const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.y;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> 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 + height, projectile.m_origin.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.y / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
};
};
} // namespace omath::projectile_prediction::traits

View File

@@ -8,12 +8,12 @@
namespace omath::projectile_prediction
{
class ProjPredEngine
class ProjPredEngineInterface
{
public:
[[nodiscard]]
virtual std::optional<Vector3<float>> maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const = 0;
virtual ~ProjPredEngine() = default;
virtual ~ProjPredEngineInterface() = default;
};
} // namespace omath::projectile_prediction

View File

@@ -6,7 +6,7 @@
namespace omath::projectile_prediction
{
class ProjPredEngineAvx2 final : public ProjPredEngine
class ProjPredEngineAvx2 final : public ProjPredEngineInterface
{
public:
[[nodiscard]] std::optional<Vector3<float>>

View File

@@ -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"
@@ -12,15 +13,40 @@
namespace omath::projectile_prediction
{
class ProjPredEngineLegacy final : public ProjPredEngine
template<class EngineTrait = traits::SourceEngineTrait>
class ProjPredEngineLegacy final : 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<Vector3<float>> 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 =
EngineTrait::predict_target_position(target, 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 EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch);
}
return std::nullopt;
}
private:
const float m_gravity_constant;
@@ -28,13 +54,56 @@ namespace omath::projectile_prediction
const float m_maximum_simulation_time;
const float m_distance_tolerance;
// Realization of this formula:
// https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target
/*
\[
\theta \;=\; \arctan\!\Biggl(
\frac{%
v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,}
}{%
gx
}\Biggr)
\]
*/
[[nodiscard]]
std::optional<float>
maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile,
const Vector3<float>& target_position) const noexcept;
const Vector3<float>& target_position) const noexcept
{
const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale;
if (bullet_gravity == 0.f)
return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position);
const auto delta = target_position - projectile.m_origin;
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;
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);
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);
}
[[nodiscard]]
bool is_projectile_reached_target(const Vector3<float>& target_position, const Projectile& projectile,
float pitch, float time) const noexcept;
const float pitch, const float time) const noexcept
{
const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position);
const auto projectile_position =
EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant);
return projectile_position.distance_to(target_position) <= m_distance_tolerance;
}
};
} // namespace omath::projectile_prediction

View File

@@ -10,9 +10,6 @@ namespace omath::projectile_prediction
class Projectile final
{
public:
[[nodiscard]]
Vector3<float> predict_position(float pitch, float yaw, float time, float gravity) const noexcept;
Vector3<float> m_origin;
float m_launch_speed{};
float m_gravity_scale{};

View File

@@ -10,17 +10,6 @@ namespace omath::projectile_prediction
class Target final
{
public:
[[nodiscard]]
constexpr Vector3<float> predict_position(const float time, const float gravity) const noexcept
{
auto predicted = m_origin + m_velocity * time;
if (m_is_airborne)
predicted.z -= gravity * (time*time) * 0.5f;
return predicted;
}
Vector3<float> m_origin;
Vector3<float> m_velocity;
bool m_is_airborne{};

View File

@@ -142,7 +142,7 @@ namespace omath
[[nodiscard]] Vector2 normalized() const noexcept
{
const Type len = length();
return len > 0.f ? *this / len : *this;
return len > static_cast<Type>(0) ? *this / len : *this;
}
#endif
[[nodiscard]] constexpr Type length_sqr() const noexcept
@@ -153,8 +153,8 @@ namespace omath
constexpr Vector2& abs() noexcept
{
// FIXME: Replace with std::abs, if it will become constexprable
x = x < 0 ? -x : x;
y = y < 0 ? -y : y;
x = x < static_cast<Type>(0) ? -x : x;
y = y < static_cast<Type>(0) ? -y : y;
return *this;
}
@@ -202,6 +202,11 @@ namespace omath
{
return {static_cast<float>(this->x), static_cast<float>(this->y)};
}
[[nodiscard]]
static Vector3<float> from_im_vec2(const ImVec2& other) noexcept
{
return {static_cast<Type>(other.x), static_cast<Type>(other.y)};
}
#endif
};
} // namespace omath

View File

@@ -151,7 +151,7 @@ namespace omath
{
const Type len = this->length();
return len != 0 ? *this / len : *this;
return len != static_cast<Type>(0) ? *this / len : *this;
}
[[nodiscard]] Type length_2d() const noexcept
@@ -221,7 +221,7 @@ namespace omath
{
const auto bottom = length() * other.length();
if (bottom == 0.f)
if (bottom == static_cast<Type>(0))
return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE);
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
@@ -230,7 +230,7 @@ namespace omath
[[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept
{
if (const auto angle = angle_between(other))
return angle->as_degrees() == 90.f;
return angle->as_degrees() == static_cast<Type>(90);
return false;
}

View File

@@ -18,7 +18,7 @@ namespace omath
constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3<Type>(x, y, z), w(w)
{
}
constexpr Vector4() noexcept : Vector3<Type>(), w(0) {};
constexpr Vector4() noexcept: Vector3<Type>(), w(static_cast<Type>(0)) {};
[[nodiscard]]
constexpr bool operator==(const Vector4& other) const noexcept
@@ -169,6 +169,12 @@ namespace omath
static_cast<float>(w),
};
}
[[nodiscard]]
static Vector4<float> from_im_vec4(const ImVec4& other) noexcept
{
return {static_cast<Type>(other.x), static_cast<Type>(other.y), static_cast<Type>(other.z)};
}
}
#endif
};
};
} // namespace omath

View File

@@ -1,69 +0,0 @@
#include "omath/projectile_prediction/proj_pred_engine_legacy.hpp"
#include <cmath>
#include <omath/angles.hpp>
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<Vector3<float>> 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;
const auto delta2d = (predicted_target_position - projectile.m_origin).length_2d();
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return Vector3(predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height);
}
return std::nullopt;
}
std::optional<float> ProjPredEngineLegacy::maybe_calculate_projectile_launch_pitch_angle(
const Projectile& projectile, const Vector3<float>& 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 = delta.length_2d();
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 * delta.z * 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<float>& 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 = projectile.predict_position(pitch, yaw, time, m_gravity_constant);
return projectile_position.distance_to(target_position) <= m_distance_tolerance;
}
} // namespace omath::projectile_prediction

View File

@@ -7,16 +7,4 @@
namespace omath::projectile_prediction
{
Vector3<float> Projectile::predict_position(const float pitch, const float yaw, const float time,
const float gravity) const noexcept
{
auto current_pos = m_origin
+ source_engine::forward_vector({source_engine::PitchAngle::from_degrees(-pitch),
source_engine::YawAngle::from_degrees(yaw),
source_engine::RollAngle::from_degrees(0)})
* m_launch_speed * time;
current_pos.z -= (gravity * m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
} // namespace omath::projectile_prediction