fixed projectile prediction for double

This commit is contained in:
2026-04-25 21:05:00 +03:00
parent 4c65781c6f
commit 9234704010
17 changed files with 260 additions and 236 deletions

View File

@@ -12,7 +12,7 @@ namespace omath::cry_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -26,7 +26,7 @@ namespace omath::cry_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -49,7 +49,7 @@ namespace omath::cry_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -12,7 +12,7 @@ namespace omath::frostbite_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -26,7 +26,7 @@ namespace omath::frostbite_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -49,7 +49,7 @@ namespace omath::frostbite_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -13,7 +13,7 @@ namespace omath::iw_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -27,7 +27,7 @@ namespace omath::iw_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -50,7 +50,7 @@ namespace omath::iw_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -12,7 +12,7 @@ namespace omath::opengl_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -26,7 +26,7 @@ namespace omath::opengl_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -49,7 +49,7 @@ namespace omath::opengl_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -13,7 +13,7 @@ namespace omath::source_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -27,7 +27,7 @@ namespace omath::source_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -50,7 +50,7 @@ namespace omath::source_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -12,7 +12,7 @@ namespace omath::unity_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile<float>& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
@@ -26,7 +26,7 @@ namespace omath::unity_engine
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target<float>& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
@@ -49,7 +49,7 @@ namespace omath::unity_engine
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile<float>& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{

View File

@@ -12,69 +12,72 @@ namespace omath::unreal_engine
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
static Vector3<double> predict_projectile_position(const projectile_prediction::Projectile<double>& projectile,
const double pitch, const double yaw,
const double time, const double gravity) noexcept
{
const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
const auto fwd_d = forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)});
auto current_pos = launch_pos
+ Vector3<float>{static_cast<float>(fwd_d.x), static_cast<float>(fwd_d.y),
static_cast<float>(fwd_d.z)}
+ Vector3<double>{fwd_d.x, fwd_d.y, fwd_d.z}
* projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
static Vector3<double> predict_target_position(const projectile_prediction::Target<double>& target,
const double time, const double gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f;
predicted.y -= gravity * (time * time) * 0.5;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
static double calc_vector_2d_distance(const Vector3<double>& 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
static double get_vector_height_coordinate(const Vector3<double>& vec) noexcept
{
return vec.y;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
static Vector3<double> calc_viewpoint_from_angles(const projectile_prediction::Projectile<double>& projectile,
Vector3<double> predicted_target_position,
const std::optional<double> 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
static double calc_direct_pitch_angle(const Vector3<double>& origin, const Vector3<double>& 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<float>& origin, const Vector3<float>& view_to) noexcept
static double calc_direct_yaw_angle(const Vector3<double>& origin, const Vector3<double>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::atan2(direction.y, direction.x));
};
}
};
} // namespace omath::unreal_engine

View File

@@ -8,22 +8,24 @@
namespace omath::projectile_prediction
{
template<class ArithmeticType = float>
struct AimAngles
{
float pitch{};
float yaw{};
ArithmeticType pitch{};
ArithmeticType yaw{};
};
template<class ArithmeticType = float>
class ProjPredEngineInterface
{
public:
[[nodiscard]]
virtual std::optional<Vector3<float>> maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const = 0;
virtual std::optional<Vector3<ArithmeticType>> maybe_calculate_aim_point(
const Projectile<ArithmeticType>& projectile, const Target<ArithmeticType>& target) const = 0;
[[nodiscard]]
virtual std::optional<AimAngles> maybe_calculate_aim_angles(const Projectile& projectile,
const Target& target) const = 0;
virtual std::optional<AimAngles<ArithmeticType>> maybe_calculate_aim_angles(
const Projectile<ArithmeticType>& projectile, const Target<ArithmeticType>& target) const = 0;
virtual ~ProjPredEngineInterface() = default;
};

View File

@@ -6,14 +6,14 @@
namespace omath::projectile_prediction
{
class ProjPredEngineAvx2 final : public ProjPredEngineInterface
class ProjPredEngineAvx2 final : public ProjPredEngineInterface<float>
{
public:
[[nodiscard]] std::optional<Vector3<float>>
maybe_calculate_aim_point(const Projectile& projectile, const Target& target) const override;
maybe_calculate_aim_point(const Projectile<float>& projectile, const Target<float>& target) const override;
[[nodiscard]] std::optional<AimAngles>
maybe_calculate_aim_angles(const Projectile& projectile, const Target& target) const override;
[[nodiscard]] std::optional<AimAngles<float>>
maybe_calculate_aim_angles(const Projectile<float>& projectile, const Target<float>& target) const override;
ProjPredEngineAvx2(float gravity_constant, float simulation_time_step, float maximum_simulation_time);
~ProjPredEngineAvx2() override = default;
@@ -21,7 +21,7 @@ namespace omath::projectile_prediction
private:
[[nodiscard]] static std::optional<float> calculate_pitch(const Vector3<float>& proj_origin,
const Vector3<float>& target_pos,
float bullet_gravity, float v0, float time) ;
float bullet_gravity, float v0, float time);
// We use [[maybe_unused]] here since AVX2 is not available for ARM and ARM64 CPU
[[maybe_unused]] const float m_gravity_constant;

View File

@@ -13,24 +13,23 @@
namespace omath::projectile_prediction
{
template<class T>
template<class T, class ArithmeticType>
concept PredEngineConcept =
requires(const Projectile& projectile, const Target& target, const Vector3<float>& vec_a,
const Vector3<float>& vec_b,
Vector3<float> v3, // by-value for calc_viewpoint_from_angles
float pitch, float yaw, float time, float gravity, std::optional<float> maybe_pitch) {
// Presence + return types
requires(const Projectile<ArithmeticType>& projectile, const Target<ArithmeticType>& target,
const Vector3<ArithmeticType>& vec_a, const Vector3<ArithmeticType>& vec_b,
Vector3<ArithmeticType> v3,
ArithmeticType pitch, ArithmeticType yaw, ArithmeticType time, ArithmeticType gravity,
std::optional<ArithmeticType> maybe_pitch) {
{
T::predict_projectile_position(projectile, pitch, yaw, time, gravity)
} -> std::same_as<Vector3<float>>;
{ T::predict_target_position(target, time, gravity) } -> std::same_as<Vector3<float>>;
{ T::calc_vector_2d_distance(vec_a) } -> std::same_as<float>;
{ T::get_vector_height_coordinate(vec_b) } -> std::same_as<float>;
{ T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as<Vector3<float>>;
{ T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as<float>;
{ T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as<float>;
} -> std::same_as<Vector3<ArithmeticType>>;
{ T::predict_target_position(target, time, gravity) } -> std::same_as<Vector3<ArithmeticType>>;
{ T::calc_vector_2d_distance(vec_a) } -> std::same_as<ArithmeticType>;
{ T::get_vector_height_coordinate(vec_b) } -> std::same_as<ArithmeticType>;
{ T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as<Vector3<ArithmeticType>>;
{ T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as<ArithmeticType>;
{ T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as<ArithmeticType>;
// Enforce noexcept as in PredEngineTrait
requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity));
requires noexcept(T::predict_target_position(target, time, gravity));
requires noexcept(T::calc_vector_2d_distance(vec_a));
@@ -39,21 +38,24 @@ namespace omath::projectile_prediction
requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b));
requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b));
};
template<class EngineTrait = source_engine::PredEngineTrait>
requires PredEngineConcept<EngineTrait>
class ProjPredEngineLegacy final : public ProjPredEngineInterface
template<class EngineTrait = source_engine::PredEngineTrait, class ArithmeticType = float>
requires PredEngineConcept<EngineTrait, ArithmeticType>
class ProjPredEngineLegacy final : public ProjPredEngineInterface<ArithmeticType>
{
public:
explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step,
const float maximum_simulation_time, const float distance_tolerance)
explicit ProjPredEngineLegacy(const ArithmeticType gravity_constant,
const ArithmeticType simulation_time_step,
const ArithmeticType maximum_simulation_time,
const ArithmeticType 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
std::optional<Vector3<ArithmeticType>> maybe_calculate_aim_point(
const Projectile<ArithmeticType>& projectile, const Target<ArithmeticType>& target) const override
{
const auto solution = find_solution(projectile, target);
if (!solution)
@@ -64,28 +66,31 @@ namespace omath::projectile_prediction
}
[[nodiscard]]
std::optional<AimAngles> maybe_calculate_aim_angles(const Projectile& projectile,
const Target& target) const override
std::optional<AimAngles<ArithmeticType>> maybe_calculate_aim_angles(
const Projectile<ArithmeticType>& projectile, const Target<ArithmeticType>& target) const override
{
const auto solution = find_solution(projectile, target);
if (!solution)
return std::nullopt;
const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin + projectile.m_launch_offset, solution->predicted_target_position);
return AimAngles{solution->pitch, yaw};
const auto yaw = EngineTrait::calc_direct_yaw_angle(
projectile.m_origin + projectile.m_launch_offset, solution->predicted_target_position);
return AimAngles<ArithmeticType>{solution->pitch, yaw};
}
private:
struct Solution
{
Vector3<float> predicted_target_position;
float pitch;
Vector3<ArithmeticType> predicted_target_position;
ArithmeticType pitch;
};
[[nodiscard]]
std::optional<Solution> find_solution(const Projectile& projectile, const Target& target) const
std::optional<Solution> find_solution(const Projectile<ArithmeticType>& projectile,
const Target<ArithmeticType>& target) const
{
for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step)
for (ArithmeticType time = ArithmeticType{0}; time < m_maximum_simulation_time;
time += m_simulation_time_step)
{
const auto predicted_target_position =
EngineTrait::predict_target_position(target, time, m_gravity_constant);
@@ -105,10 +110,10 @@ namespace omath::projectile_prediction
return std::nullopt;
}
const float m_gravity_constant;
const float m_simulation_time_step;
const float m_maximum_simulation_time;
const float m_distance_tolerance;
const ArithmeticType m_gravity_constant;
const ArithmeticType m_simulation_time_step;
const ArithmeticType m_maximum_simulation_time;
const ArithmeticType 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
@@ -123,15 +128,15 @@ namespace omath::projectile_prediction
\]
*/
[[nodiscard]]
std::optional<float>
maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile,
const Vector3<float>& target_position) const noexcept
std::optional<ArithmeticType>
maybe_calculate_projectile_launch_pitch_angle(const Projectile<ArithmeticType>& projectile,
const Vector3<ArithmeticType>& target_position) const noexcept
{
const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale;
const auto launch_origin = projectile.m_origin + projectile.m_launch_offset;
if (bullet_gravity == 0.f)
if (bullet_gravity == ArithmeticType{0})
return EngineTrait::calc_direct_pitch_angle(launch_origin, target_position);
const auto delta = target_position - launch_origin;
@@ -140,24 +145,28 @@ namespace omath::projectile_prediction
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);
ArithmeticType root = launch_speed_sqr * launch_speed_sqr
- bullet_gravity
* (bullet_gravity * distance2d_sqr
+ ArithmeticType{2} * EngineTrait::get_vector_height_coordinate(delta)
* launch_speed_sqr);
if (root < 0.0f) [[unlikely]]
if (root < ArithmeticType{0}) [[unlikely]]
return std::nullopt;
root = std::sqrt(root);
const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d));
const ArithmeticType 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,
const float pitch, const float time) const noexcept
bool is_projectile_reached_target(const Vector3<ArithmeticType>& target_position,
const Projectile<ArithmeticType>& projectile,
const ArithmeticType pitch, const ArithmeticType time) const noexcept
{
const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin + projectile.m_launch_offset, target_position);
const auto yaw = EngineTrait::calc_direct_yaw_angle(
projectile.m_origin + projectile.m_launch_offset, target_position);
const auto projectile_position =
EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant);

View File

@@ -7,12 +7,13 @@
namespace omath::projectile_prediction
{
template <class ArithmeticType = float>
class Projectile final
{
public:
Vector3<float> m_origin;
Vector3<float> m_launch_offset{0.f, 0.f, 0.f};
float m_launch_speed{};
float m_gravity_scale{};
Vector3<ArithmeticType> m_origin;
Vector3<ArithmeticType> m_launch_offset{};
ArithmeticType m_launch_speed{};
ArithmeticType m_gravity_scale{};
};
} // namespace omath::projectile_prediction

View File

@@ -7,11 +7,12 @@
namespace omath::projectile_prediction
{
template <class ArithmeticType = float>
class Target final
{
public:
Vector3<float> m_origin;
Vector3<float> m_velocity;
Vector3<ArithmeticType> m_origin;
Vector3<ArithmeticType> m_velocity;
bool m_is_airborne{};
};
} // namespace omath::projectile_prediction
} // namespace omath::projectile_prediction