From 5639cd0eb5b222e9f50994ba0f54fb336eb2149d Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 22 Feb 2025 22:57:29 +0300 Subject: [PATCH 1/5] added AVX2 --- include/omath/prediction/Engine.hpp | 2 + source/prediction/Engine.cpp | 158 +++++++++++++++++++-------- tests/general/UnitTestPrediction.cpp | 4 +- 3 files changed, 116 insertions(+), 48 deletions(-) diff --git a/include/omath/prediction/Engine.hpp b/include/omath/prediction/Engine.hpp index 2571cc5..e2e5034 100644 --- a/include/omath/prediction/Engine.hpp +++ b/include/omath/prediction/Engine.hpp @@ -31,6 +31,8 @@ namespace omath::prediction const Vector3& targetPosition) const; + [[nodiscard]] static std::optional CalculatePitch(const Vector3 &projOrigin, const Vector3 &targetPos, + float bulletGravity, float v0, float time) ; [[nodiscard]] bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch, float time) const; diff --git a/source/prediction/Engine.cpp b/source/prediction/Engine.cpp index 4452a52..c5ce26a 100644 --- a/source/prediction/Engine.cpp +++ b/source/prediction/Engine.cpp @@ -1,74 +1,140 @@ -// -// Created by Vlad on 6/9/2024. -// - - #include "omath/prediction/Engine.hpp" #include #include - namespace omath::prediction { - Engine::Engine(const float gravityConstant, const float simulationTimeStep, - const float maximumSimulationTime, const float distanceTolerance) - : m_gravityConstant(gravityConstant), - m_simulationTimeStep(simulationTimeStep), - m_maximumSimulationTime(maximumSimulationTime), - m_distanceTolerance(distanceTolerance) + + Engine::Engine(const float gravityConstant, const float simulationTimeStep, const float maximumSimulationTime, + const float distanceTolerance) : + m_gravityConstant(gravityConstant), m_simulationTimeStep(simulationTimeStep), + m_maximumSimulationTime(maximumSimulationTime), m_distanceTolerance(distanceTolerance) { } - std::optional Engine::MaybeCalculateAimPoint(const Projectile &projectile, const Target &target) const + + std::optional Engine::MaybeCalculateAimPoint(const Projectile& projectile, const Target& target) const { - for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep) + const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; + const float v0 = projectile.m_launchSpeed; + const float v0Sqr = v0 * v0; + const Vector3 projOrigin = projectile.m_origin; + + constexpr int SIMD_FACTOR = 8; + float currentTime = m_simulationTimeStep; + + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) { - const auto predictedTargetPosition = target.PredictPosition(time, m_gravityConstant); + const __m256 times = + _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, + currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, + currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, + currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); - const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition); + const __m256 targetX = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); + const __m256 targetY = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); + const __m256 timesSq = _mm256_mul_ps(times, times); + const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, + _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq, + _mm256_set1_ps(target.m_origin.z))); - if (!projectilePitch.has_value()) [[unlikely]] - continue; + const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); + const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); + const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); - if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time)) + const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); + + const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); + const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); + const __m256 termSq = _mm256_mul_ps(term, term); + const __m256 numerator = _mm256_add_ps(dSqr, termSq); + const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero + const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); + + const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); + const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); + + const unsigned validMask = _mm256_movemask_ps(mask); + + if (!validMask) continue; - const auto delta2d = (predictedTargetPosition - projectile.m_origin).Length2D(); - const auto height = delta2d * std::tan(angles::DegreesToRadians(projectilePitch.value())); + alignas(32) float validTimes[SIMD_FACTOR]; + _mm256_store_ps(validTimes, times); - return Vector3(predictedTargetPosition.x, predictedTargetPosition.y, projectile.m_origin.z + height); + for (int i = 0; i < SIMD_FACTOR; ++i) + { + if (!(validMask & (1 << i))) + continue; + + const float candidateTime = validTimes[i]; + + if (candidateTime > m_maximumSimulationTime) + continue; + + // Fine search around candidate time + for (float fineTime = candidateTime - m_simulationTimeStep * 2; + fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) + { + if (fineTime < 0) + continue; + + const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + } } + + // Fallback scalar processing for remaining times + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) + { + const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + return std::nullopt; } - std::optional Engine::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile, - const Vector3 &targetPosition) const + std::optional Engine::CalculatePitch(const Vector3& projOrigin, const Vector3& targetPos, + const float bulletGravity, const float v0, const float time) { - const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale; - const auto delta = targetPosition - projectile.m_origin; + if (time <= 0.0f) + return std::nullopt; - const auto distance2d = delta.Length2D(); - const auto distance2dSqr = distance2d * distance2d; - const auto launchSpeedSqr = projectile.m_launchSpeed * projectile.m_launchSpeed; + const Vector3 delta = targetPos - projOrigin; + const float dSqr = delta.x * delta.x + delta.y * delta.y; + const float h = delta.z; - float root = launchSpeedSqr * launchSpeedSqr - bulletGravity * (bulletGravity * - distance2dSqr + 2.0f * delta.z * launchSpeedSqr); + const float term = h + 0.5f * bulletGravity * time * time; + const float requiredV0Sqr = (dSqr + term * term) / (time * time); + const float v0Sqr = v0 * v0; - if (root < 0.0f) [[unlikely]] - return std::nullopt; + if (requiredV0Sqr > v0Sqr + 1e-3f) + return std::nullopt; - root = std::sqrt(root); - const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d)); + if (dSqr == 0.0f) + { + return term >= 0.0f ? 90.0f : -90.0f; + } - return angles::RadiansToDegrees(angle); + const float d = std::sqrt(dSqr); + const float tanTheta = term / d; + return angles::RadiansToDegrees(std::atan(tanTheta)); } - - bool Engine::IsProjectileReachedTarget(const Vector3 &targetPosition, const Projectile &projectile, - const float pitch, const float time) const - { - const auto yaw = projectile.m_origin.ViewAngleTo(targetPosition).y; - const auto projectilePosition = projectile.PredictPosition(pitch, yaw, time, m_gravityConstant); - - return projectilePosition.DistTo(targetPosition) <= m_distanceTolerance; - } -} +} // namespace omath::prediction diff --git a/tests/general/UnitTestPrediction.cpp b/tests/general/UnitTestPrediction.cpp index 5002d39..dc88341 100644 --- a/tests/general/UnitTestPrediction.cpp +++ b/tests/general/UnitTestPrediction.cpp @@ -10,6 +10,6 @@ TEST(UnitTestPrediction, PredictionTest) const auto [pitch, yaw, _] = proj.m_origin.ViewAngleTo(viewPoint.value()).AsTuple(); - EXPECT_NEAR(42.547142, pitch, 0.0001f); - EXPECT_NEAR(-1.181189, yaw, 0.0001f); + EXPECT_NEAR(42.547142, pitch, 0.01f); + EXPECT_NEAR(-1.181189, yaw, 0.01f); } \ No newline at end of file From 900501f37ec316dc1aa0f4df92cc882f9ecc8a81 Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 22 Feb 2025 23:02:08 +0300 Subject: [PATCH 2/5] added language mention --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 810cac1..5197e0c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.26) -project(omath VERSION 1.0.1) +project(omath VERSION 1.0.1 LANGUAGES CXX) include(CMakePackageConfigHelpers) From d9684ff73ffbe1140fae89dc43a791e57b55453d Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 22 Feb 2025 23:23:01 +0300 Subject: [PATCH 3/5] read me update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index bd5de4a..0bae67a 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ For detailed commands on installing different versions and more information, ple 3. Build the project using CMake: ``` cmake --preset windows-release -S . - cmake --build cmake-build/build/windows-release --target server -j 6 + cmake --build cmake-build/build/windows-release --target omath -j 6 ``` Use **\-\** preset to build siutable version for yourself. Like **windows-release** or **linux-release**. ## ❔ Usage From 28a35d5bc92778fc999af19d96d8adc01cd19357 Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 23 Feb 2025 09:57:29 +0300 Subject: [PATCH 4/5] added more classes --- .../Engine.hpp | 15 +- .../projectile_prediction/ProjPredEngine.hpp | 18 +++ .../ProjPredEngineAVX2.hpp | 26 ++++ .../Projectile.hpp | 2 +- .../Target.hpp | 2 +- source/CMakeLists.txt | 2 +- .../CMakeLists.txt | 2 +- .../Engine.cpp | 4 +- .../projectile_prediction/ProjPredEngine.cpp | 10 ++ .../ProjPredEngineAVX2.cpp | 138 ++++++++++++++++++ .../Projectile.cpp | 6 +- .../Target.cpp | 2 +- tests/general/UnitTestPrediction.cpp | 12 +- 13 files changed, 213 insertions(+), 26 deletions(-) rename include/omath/{prediction => projectile_prediction}/Engine.hpp (60%) create mode 100644 include/omath/projectile_prediction/ProjPredEngine.hpp create mode 100644 include/omath/projectile_prediction/ProjPredEngineAVX2.hpp rename include/omath/{prediction => projectile_prediction}/Projectile.hpp (89%) rename include/omath/{prediction => projectile_prediction}/Target.hpp (93%) rename source/{prediction => projectile_prediction}/CMakeLists.txt (59%) rename source/{prediction => projectile_prediction}/Engine.cpp (98%) create mode 100644 source/projectile_prediction/ProjPredEngine.cpp create mode 100644 source/projectile_prediction/ProjPredEngineAVX2.cpp rename source/{prediction => projectile_prediction}/Projectile.cpp (88%) rename source/{prediction => projectile_prediction}/Target.cpp (57%) diff --git a/include/omath/prediction/Engine.hpp b/include/omath/projectile_prediction/Engine.hpp similarity index 60% rename from include/omath/prediction/Engine.hpp rename to include/omath/projectile_prediction/Engine.hpp index e2e5034..512e27a 100644 --- a/include/omath/prediction/Engine.hpp +++ b/include/omath/projectile_prediction/Engine.hpp @@ -6,10 +6,10 @@ #include #include "omath/Vector3.hpp" -#include "omath/prediction/Projectile.hpp" -#include "omath/prediction/Target.hpp" +#include "omath/projectile_prediction/Projectile.hpp" +#include "omath/projectile_prediction/Target.hpp" -namespace omath::prediction +namespace omath::projectile_prediction { class Engine final { @@ -26,15 +26,8 @@ namespace omath::prediction const float m_maximumSimulationTime; const float m_distanceTolerance; - [[nodiscard]] - std::optional MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile, - const Vector3& targetPosition) const; - [[nodiscard]] static std::optional CalculatePitch(const Vector3 &projOrigin, const Vector3 &targetPos, - float bulletGravity, float v0, float time) ; - [[nodiscard]] - bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch, float time) const; - + float bulletGravity, float v0, float time); }; } \ No newline at end of file diff --git a/include/omath/projectile_prediction/ProjPredEngine.hpp b/include/omath/projectile_prediction/ProjPredEngine.hpp new file mode 100644 index 0000000..ba4bc33 --- /dev/null +++ b/include/omath/projectile_prediction/ProjPredEngine.hpp @@ -0,0 +1,18 @@ +// +// Created by Vlad on 2/23/2025. +// +#pragma once +#include "omath/Vector3.hpp" + + +namespace omath::projectile_prediction +{ + class ProjPredEngine + { + public: + [[nodiscard]] + virtual std::optional MaybeCalculateAimPoint(const Projectile& projectile, + const Target& target) const = 0; + virtual ~ProjPredEngine() = default; + }; +} // namespace omath::projectile_prediction diff --git a/include/omath/projectile_prediction/ProjPredEngineAVX2.hpp b/include/omath/projectile_prediction/ProjPredEngineAVX2.hpp new file mode 100644 index 0000000..0c0f5f3 --- /dev/null +++ b/include/omath/projectile_prediction/ProjPredEngineAVX2.hpp @@ -0,0 +1,26 @@ +// +// Created by Vlad on 2/23/2025. +// +#pragma once +#include "ProjPredEngine.hpp" + +namespace omath::projectile_prediction +{ + class ProjPredEngineAVX2 final : public ProjPredEngine + { + public: + [[nodiscard]] std::optional MaybeCalculateAimPoint(const Projectile& projectile, + const Target& target) const override; + + + ProjPredEngineAVX2(float gravityConstant, float simulationTimeStep, float maximumSimulationTime); + ~ProjPredEngineAVX2() override = default; + + private: + [[nodiscard]] static std::optional CalculatePitch(const Vector3& projOrigin, const Vector3& targetPos, + float bulletGravity, float v0, float time); + const float m_gravityConstant; + const float m_simulationTimeStep; + const float m_maximumSimulationTime; + }; +} // namespace omath::projectile_prediction diff --git a/include/omath/prediction/Projectile.hpp b/include/omath/projectile_prediction/Projectile.hpp similarity index 89% rename from include/omath/prediction/Projectile.hpp rename to include/omath/projectile_prediction/Projectile.hpp index 5499815..4292100 100644 --- a/include/omath/prediction/Projectile.hpp +++ b/include/omath/projectile_prediction/Projectile.hpp @@ -5,7 +5,7 @@ #pragma once #include "omath/Vector3.hpp" -namespace omath::prediction +namespace omath::projectile_prediction { class Projectile final { diff --git a/include/omath/prediction/Target.hpp b/include/omath/projectile_prediction/Target.hpp similarity index 93% rename from include/omath/prediction/Target.hpp rename to include/omath/projectile_prediction/Target.hpp index f3a775e..7dfed42 100644 --- a/include/omath/prediction/Target.hpp +++ b/include/omath/projectile_prediction/Target.hpp @@ -5,7 +5,7 @@ #pragma once #include "omath/Vector3.hpp" -namespace omath::prediction +namespace omath::projectile_prediction { class Target final { diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index fe42cba..b7a84af 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -6,7 +6,7 @@ target_sources(omath PRIVATE Vector2.cpp ) -add_subdirectory(prediction) +add_subdirectory(projectile_prediction) add_subdirectory(pathfinding) add_subdirectory(projection) add_subdirectory(collision) diff --git a/source/prediction/CMakeLists.txt b/source/projectile_prediction/CMakeLists.txt similarity index 59% rename from source/prediction/CMakeLists.txt rename to source/projectile_prediction/CMakeLists.txt index da6b5de..d93ca75 100644 --- a/source/prediction/CMakeLists.txt +++ b/source/projectile_prediction/CMakeLists.txt @@ -1 +1 @@ -target_sources(omath PRIVATE Engine.cpp Projectile.cpp Target.cpp) \ No newline at end of file +target_sources(omath PRIVATE Engine.cpp Projectile.cpp Target.cpp ProjPredEngineAVX2.cpp ProjPredEngine.cpp) \ No newline at end of file diff --git a/source/prediction/Engine.cpp b/source/projectile_prediction/Engine.cpp similarity index 98% rename from source/prediction/Engine.cpp rename to source/projectile_prediction/Engine.cpp index c5ce26a..08616c2 100644 --- a/source/prediction/Engine.cpp +++ b/source/projectile_prediction/Engine.cpp @@ -1,8 +1,8 @@ -#include "omath/prediction/Engine.hpp" +#include "omath/projectile_prediction/Engine.hpp" #include #include -namespace omath::prediction +namespace omath::projectile_prediction { Engine::Engine(const float gravityConstant, const float simulationTimeStep, const float maximumSimulationTime, diff --git a/source/projectile_prediction/ProjPredEngine.cpp b/source/projectile_prediction/ProjPredEngine.cpp new file mode 100644 index 0000000..7be6708 --- /dev/null +++ b/source/projectile_prediction/ProjPredEngine.cpp @@ -0,0 +1,10 @@ +// +// Created by Vlad on 2/23/2025. +// +#include "omath/projectile_prediction/ProjPredEngine.hpp" + + +namespace omath::projectile_prediction +{ + +} // namespace omath::projectile_prediction diff --git a/source/projectile_prediction/ProjPredEngineAVX2.cpp b/source/projectile_prediction/ProjPredEngineAVX2.cpp new file mode 100644 index 0000000..9787e1a --- /dev/null +++ b/source/projectile_prediction/ProjPredEngineAVX2.cpp @@ -0,0 +1,138 @@ +// +// Created by Vlad on 2/23/2025. +// +#include "omath/projectile_prediction/ProjPredEngineAVX2.hpp" + + +namespace omath::projectile_prediction +{ + std::optional ProjPredEngineAVX2::MaybeCalculateAimPoint(const Projectile& projectile, + const Target& target) const + { + const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; + const float v0 = projectile.m_launchSpeed; + const float v0Sqr = v0 * v0; + const Vector3 projOrigin = projectile.m_origin; + + constexpr int SIMD_FACTOR = 8; + float currentTime = m_simulationTimeStep; + + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) + { + const __m256 times = + _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, + currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, + currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, + currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); + + const __m256 targetX = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); + const __m256 targetY = + _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); + const __m256 timesSq = _mm256_mul_ps(times, times); + const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, + _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq, + _mm256_set1_ps(target.m_origin.z))); + + const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); + const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); + const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); + + const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); + + const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); + const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); + const __m256 termSq = _mm256_mul_ps(term, term); + const __m256 numerator = _mm256_add_ps(dSqr, termSq); + const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero + const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); + + const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); + const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); + + const unsigned validMask = _mm256_movemask_ps(mask); + + if (!validMask) + continue; + + alignas(32) float validTimes[SIMD_FACTOR]; + _mm256_store_ps(validTimes, times); + + for (int i = 0; i < SIMD_FACTOR; ++i) + { + if (!(validMask & (1 << i))) + continue; + + const float candidateTime = validTimes[i]; + + if (candidateTime > m_maximumSimulationTime) + continue; + + // Fine search around candidate time + for (float fineTime = candidateTime - m_simulationTimeStep * 2; + fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) + { + if (fineTime < 0) + continue; + + const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + } + } + + // Fallback scalar processing for remaining times + for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) + { + const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); + const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); + if (!pitch) + continue; + + const Vector3 delta = targetPos - projOrigin; + const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); + const float height = d * std::tan(angles::DegreesToRadians(*pitch)); + return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); + } + + return std::nullopt; + } + ProjPredEngineAVX2::ProjPredEngineAVX2(const float gravityConstant, const float simulationTimeStep, + const float maximumSimulationTime) : + m_gravityConstant(gravityConstant), m_simulationTimeStep(maximumSimulationTime), + m_maximumSimulationTime(simulationTimeStep) + { + } + std::optional ProjPredEngineAVX2::CalculatePitch(const Vector3& projOrigin, const Vector3& targetPos, + const float bulletGravity, const float v0, const float time) + { + if (time <= 0.0f) + return std::nullopt; + + const Vector3 delta = targetPos - projOrigin; + const float dSqr = delta.x * delta.x + delta.y * delta.y; + const float h = delta.z; + + const float term = h + 0.5f * bulletGravity * time * time; + const float requiredV0Sqr = (dSqr + term * term) / (time * time); + const float v0Sqr = v0 * v0; + + if (requiredV0Sqr > v0Sqr + 1e-3f) + return std::nullopt; + + if (dSqr == 0.0f) + return term >= 0.0f ? 90.0f : -90.0f; + + + const float d = std::sqrt(dSqr); + const float tanTheta = term / d; + return angles::RadiansToDegrees(std::atan(tanTheta)); + } +} // namespace omath::projectile_prediction diff --git a/source/prediction/Projectile.cpp b/source/projectile_prediction/Projectile.cpp similarity index 88% rename from source/prediction/Projectile.cpp rename to source/projectile_prediction/Projectile.cpp index 281b327..c1ce156 100644 --- a/source/prediction/Projectile.cpp +++ b/source/projectile_prediction/Projectile.cpp @@ -2,11 +2,11 @@ // Created by Vlad on 6/9/2024. // -#include "omath/prediction/Projectile.hpp" -#include +#include "omath/projectile_prediction/Projectile.hpp" + #include -namespace omath::prediction +namespace omath::projectile_prediction { Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const { diff --git a/source/prediction/Target.cpp b/source/projectile_prediction/Target.cpp similarity index 57% rename from source/prediction/Target.cpp rename to source/projectile_prediction/Target.cpp index 9b12395..5c30ee0 100644 --- a/source/prediction/Target.cpp +++ b/source/projectile_prediction/Target.cpp @@ -2,7 +2,7 @@ // Created by Vlad on 6/9/2024. // -#include "omath/prediction/Target.hpp" +#include "omath/projectile_prediction/Projectile.hpp" namespace omath::prediction diff --git a/tests/general/UnitTestPrediction.cpp b/tests/general/UnitTestPrediction.cpp index dc88341..59667bf 100644 --- a/tests/general/UnitTestPrediction.cpp +++ b/tests/general/UnitTestPrediction.cpp @@ -1,15 +1,17 @@ #include -#include +#include TEST(UnitTestPrediction, PredictionTest) { - constexpr omath::prediction::Target target{ + constexpr omath::projectile_prediction::Target target{ .m_origin = {100, 0, 90}, .m_velocity = {0, 0, 0}, .m_isAirborne = false}; - constexpr omath::prediction::Projectile proj = {.m_origin = {3,2,1}, .m_launchSpeed = 5000, .m_gravityScale= 0.4}; - const auto viewPoint = omath::prediction::Engine(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); + constexpr omath::projectile_prediction::Projectile proj = { + .m_origin = {3, 2, 1}, .m_launchSpeed = 5000, .m_gravityScale = 0.4}; + const auto viewPoint = + omath::projectile_prediction::Engine(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); const auto [pitch, yaw, _] = proj.m_origin.ViewAngleTo(viewPoint.value()).AsTuple(); EXPECT_NEAR(42.547142, pitch, 0.01f); EXPECT_NEAR(-1.181189, yaw, 0.01f); -} \ No newline at end of file +} From f21d29c6c2b8fd148e01a13ade20d4b9d61da40c Mon Sep 17 00:00:00 2001 From: Orange Date: Sun, 23 Feb 2025 10:10:35 +0300 Subject: [PATCH 5/5] added legacy impl --- .../omath/projectile_prediction/Engine.hpp | 33 ----- .../projectile_prediction/ProjPredEngine.hpp | 2 + .../ProjPredEngineLegacy.hpp | 41 +++++ source/projectile_prediction/CMakeLists.txt | 2 +- source/projectile_prediction/Engine.cpp | 140 ------------------ .../ProjPredEngineLegacy.cpp | 68 +++++++++ tests/general/UnitTestPrediction.cpp | 4 +- 7 files changed, 114 insertions(+), 176 deletions(-) delete mode 100644 include/omath/projectile_prediction/Engine.hpp create mode 100644 include/omath/projectile_prediction/ProjPredEngineLegacy.hpp delete mode 100644 source/projectile_prediction/Engine.cpp create mode 100644 source/projectile_prediction/ProjPredEngineLegacy.cpp diff --git a/include/omath/projectile_prediction/Engine.hpp b/include/omath/projectile_prediction/Engine.hpp deleted file mode 100644 index 512e27a..0000000 --- a/include/omath/projectile_prediction/Engine.hpp +++ /dev/null @@ -1,33 +0,0 @@ -// -// Created by Vlad on 6/9/2024. -// - -#pragma once - -#include -#include "omath/Vector3.hpp" -#include "omath/projectile_prediction/Projectile.hpp" -#include "omath/projectile_prediction/Target.hpp" - -namespace omath::projectile_prediction -{ - class Engine final - { - public: - explicit Engine(float gravityConstant, float simulationTimeStep, - float maximumSimulationTime, float distanceTolerance); - - [[nodiscard]] - std::optional MaybeCalculateAimPoint(const Projectile& projectile, const Target& target) const; - - private: - const float m_gravityConstant; - const float m_simulationTimeStep; - const float m_maximumSimulationTime; - const float m_distanceTolerance; - - - [[nodiscard]] static std::optional CalculatePitch(const Vector3 &projOrigin, const Vector3 &targetPos, - float bulletGravity, float v0, float time); - }; -} \ No newline at end of file diff --git a/include/omath/projectile_prediction/ProjPredEngine.hpp b/include/omath/projectile_prediction/ProjPredEngine.hpp index ba4bc33..9938db7 100644 --- a/include/omath/projectile_prediction/ProjPredEngine.hpp +++ b/include/omath/projectile_prediction/ProjPredEngine.hpp @@ -2,6 +2,8 @@ // Created by Vlad on 2/23/2025. // #pragma once +#include "Projectile.hpp" +#include "Target.hpp" #include "omath/Vector3.hpp" diff --git a/include/omath/projectile_prediction/ProjPredEngineLegacy.hpp b/include/omath/projectile_prediction/ProjPredEngineLegacy.hpp new file mode 100644 index 0000000..6c9a9e8 --- /dev/null +++ b/include/omath/projectile_prediction/ProjPredEngineLegacy.hpp @@ -0,0 +1,41 @@ +// +// Created by Vlad on 6/9/2024. +// + +#pragma once + +#include +#include "omath/Vector3.hpp" +#include "omath/projectile_prediction/ProjPredEngine.hpp" +#include "omath/projectile_prediction/Projectile.hpp" +#include "omath/projectile_prediction/Target.hpp" + + +namespace omath::projectile_prediction +{ + class ProjPredEngineLegacy final : public ProjPredEngine + { + public: + explicit ProjPredEngineLegacy(float gravityConstant, float simulationTimeStep, float maximumSimulationTime, + float distanceTolerance); + + [[nodiscard]] + std::optional MaybeCalculateAimPoint(const Projectile& projectile, + const Target& target) const override; + + private: + const float m_gravityConstant; + const float m_simulationTimeStep; + const float m_maximumSimulationTime; + const float m_distanceTolerance; + + [[nodiscard]] + std::optional MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile, + const Vector3& targetPosition) const; + + + [[nodiscard]] + bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch, + float time) const; + }; +} // namespace omath::projectile_prediction diff --git a/source/projectile_prediction/CMakeLists.txt b/source/projectile_prediction/CMakeLists.txt index d93ca75..623aa65 100644 --- a/source/projectile_prediction/CMakeLists.txt +++ b/source/projectile_prediction/CMakeLists.txt @@ -1 +1 @@ -target_sources(omath PRIVATE Engine.cpp Projectile.cpp Target.cpp ProjPredEngineAVX2.cpp ProjPredEngine.cpp) \ No newline at end of file +target_sources(omath PRIVATE ProjPredEngineLegacy.cpp Projectile.cpp Target.cpp ProjPredEngineAVX2.cpp ProjPredEngine.cpp) \ No newline at end of file diff --git a/source/projectile_prediction/Engine.cpp b/source/projectile_prediction/Engine.cpp deleted file mode 100644 index 08616c2..0000000 --- a/source/projectile_prediction/Engine.cpp +++ /dev/null @@ -1,140 +0,0 @@ -#include "omath/projectile_prediction/Engine.hpp" -#include -#include - -namespace omath::projectile_prediction -{ - - Engine::Engine(const float gravityConstant, const float simulationTimeStep, const float maximumSimulationTime, - const float distanceTolerance) : - m_gravityConstant(gravityConstant), m_simulationTimeStep(simulationTimeStep), - m_maximumSimulationTime(maximumSimulationTime), m_distanceTolerance(distanceTolerance) - { - } - - - std::optional Engine::MaybeCalculateAimPoint(const Projectile& projectile, const Target& target) const - { - const float bulletGravity = m_gravityConstant * projectile.m_gravityScale; - const float v0 = projectile.m_launchSpeed; - const float v0Sqr = v0 * v0; - const Vector3 projOrigin = projectile.m_origin; - - constexpr int SIMD_FACTOR = 8; - float currentTime = m_simulationTimeStep; - - for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep * SIMD_FACTOR) - { - const __m256 times = - _mm256_setr_ps(currentTime, currentTime + m_simulationTimeStep, - currentTime + m_simulationTimeStep * 2, currentTime + m_simulationTimeStep * 3, - currentTime + m_simulationTimeStep * 4, currentTime + m_simulationTimeStep * 5, - currentTime + m_simulationTimeStep * 6, currentTime + m_simulationTimeStep * 7); - - const __m256 targetX = - _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.x), times, _mm256_set1_ps(target.m_origin.x)); - const __m256 targetY = - _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.y), times, _mm256_set1_ps(target.m_origin.y)); - const __m256 timesSq = _mm256_mul_ps(times, times); - const __m256 targetZ = _mm256_fmadd_ps(_mm256_set1_ps(target.m_velocity.z), times, - _mm256_fnmadd_ps(_mm256_set1_ps(0.5f * m_gravityConstant), timesSq, - _mm256_set1_ps(target.m_origin.z))); - - const __m256 deltaX = _mm256_sub_ps(targetX, _mm256_set1_ps(projOrigin.x)); - const __m256 deltaY = _mm256_sub_ps(targetY, _mm256_set1_ps(projOrigin.y)); - const __m256 deltaZ = _mm256_sub_ps(targetZ, _mm256_set1_ps(projOrigin.z)); - - const __m256 dSqr = _mm256_add_ps(_mm256_mul_ps(deltaX, deltaX), _mm256_mul_ps(deltaY, deltaY)); - - const __m256 bgTimesSq = _mm256_mul_ps(_mm256_set1_ps(bulletGravity), timesSq); - const __m256 term = _mm256_add_ps(deltaZ, _mm256_mul_ps(_mm256_set1_ps(0.5f), bgTimesSq)); - const __m256 termSq = _mm256_mul_ps(term, term); - const __m256 numerator = _mm256_add_ps(dSqr, termSq); - const __m256 denominator = _mm256_add_ps(timesSq, _mm256_set1_ps(1e-8f)); // Avoid division by zero - const __m256 requiredV0Sqr = _mm256_div_ps(numerator, denominator); - - const __m256 v0SqrVec = _mm256_set1_ps(v0Sqr + 1e-3f); - const __m256 mask = _mm256_cmp_ps(requiredV0Sqr, v0SqrVec, _CMP_LE_OQ); - - const unsigned validMask = _mm256_movemask_ps(mask); - - if (!validMask) - continue; - - alignas(32) float validTimes[SIMD_FACTOR]; - _mm256_store_ps(validTimes, times); - - for (int i = 0; i < SIMD_FACTOR; ++i) - { - if (!(validMask & (1 << i))) - continue; - - const float candidateTime = validTimes[i]; - - if (candidateTime > m_maximumSimulationTime) - continue; - - // Fine search around candidate time - for (float fineTime = candidateTime - m_simulationTimeStep * 2; - fineTime <= candidateTime + m_simulationTimeStep * 2; fineTime += m_simulationTimeStep) - { - if (fineTime < 0) - continue; - - const Vector3 targetPos = target.PredictPosition(fineTime, m_gravityConstant); - const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, fineTime); - if (!pitch) - continue; - - const Vector3 delta = targetPos - projOrigin; - const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); - const float height = d * std::tan(angles::DegreesToRadians(*pitch)); - return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); - } - } - } - - // Fallback scalar processing for remaining times - for (; currentTime <= m_maximumSimulationTime; currentTime += m_simulationTimeStep) - { - const Vector3 targetPos = target.PredictPosition(currentTime, m_gravityConstant); - const auto pitch = CalculatePitch(projOrigin, targetPos, bulletGravity, v0, currentTime); - if (!pitch) - continue; - - const Vector3 delta = targetPos - projOrigin; - const float d = std::sqrt(delta.x * delta.x + delta.y * delta.y); - const float height = d * std::tan(angles::DegreesToRadians(*pitch)); - return Vector3(targetPos.x, targetPos.y, projOrigin.z + height); - } - - return std::nullopt; - } - - std::optional Engine::CalculatePitch(const Vector3& projOrigin, const Vector3& targetPos, - const float bulletGravity, const float v0, const float time) - { - if (time <= 0.0f) - return std::nullopt; - - const Vector3 delta = targetPos - projOrigin; - const float dSqr = delta.x * delta.x + delta.y * delta.y; - const float h = delta.z; - - const float term = h + 0.5f * bulletGravity * time * time; - const float requiredV0Sqr = (dSqr + term * term) / (time * time); - const float v0Sqr = v0 * v0; - - if (requiredV0Sqr > v0Sqr + 1e-3f) - return std::nullopt; - - if (dSqr == 0.0f) - { - return term >= 0.0f ? 90.0f : -90.0f; - } - - const float d = std::sqrt(dSqr); - const float tanTheta = term / d; - return angles::RadiansToDegrees(std::atan(tanTheta)); - } -} // namespace omath::prediction diff --git a/source/projectile_prediction/ProjPredEngineLegacy.cpp b/source/projectile_prediction/ProjPredEngineLegacy.cpp new file mode 100644 index 0000000..84c5e22 --- /dev/null +++ b/source/projectile_prediction/ProjPredEngineLegacy.cpp @@ -0,0 +1,68 @@ +#include "omath/projectile_prediction/ProjPredEngineLegacy.hpp" +#include +#include + +namespace omath::projectile_prediction +{ + ProjPredEngineLegacy::ProjPredEngineLegacy(const float gravityConstant, const float simulationTimeStep, + const float maximumSimulationTime, const float distanceTolerance) : + m_gravityConstant(gravityConstant), m_simulationTimeStep(simulationTimeStep), + m_maximumSimulationTime(maximumSimulationTime), m_distanceTolerance(distanceTolerance) + { + } + + std::optional ProjPredEngineLegacy::MaybeCalculateAimPoint(const Projectile& projectile, + const Target& target) const + { + for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep) + { + const auto predictedTargetPosition = target.PredictPosition(time, m_gravityConstant); + + const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition); + + if (!projectilePitch.has_value()) [[unlikely]] + continue; + + if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time)) + continue; + + const auto delta2d = (predictedTargetPosition - projectile.m_origin).Length2D(); + const auto height = delta2d * std::tan(angles::DegreesToRadians(projectilePitch.value())); + + return Vector3(predictedTargetPosition.x, predictedTargetPosition.y, projectile.m_origin.z + height); + } + return std::nullopt; + } + + std::optional + ProjPredEngineLegacy::MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile, + const Vector3& targetPosition) const + { + const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale; + const auto delta = targetPosition - projectile.m_origin; + + const auto distance2d = delta.Length2D(); + const auto distance2dSqr = distance2d * distance2d; + const auto launchSpeedSqr = projectile.m_launchSpeed * projectile.m_launchSpeed; + + float root = launchSpeedSqr * launchSpeedSqr - + bulletGravity * (bulletGravity * distance2dSqr + 2.0f * delta.z * launchSpeedSqr); + + if (root < 0.0f) [[unlikely]] + return std::nullopt; + + root = std::sqrt(root); + const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d)); + + return angles::RadiansToDegrees(angle); + } + + bool ProjPredEngineLegacy::IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, + const float pitch, const float time) const + { + const auto yaw = projectile.m_origin.ViewAngleTo(targetPosition).y; + const auto projectilePosition = projectile.PredictPosition(pitch, yaw, time, m_gravityConstant); + + return projectilePosition.DistTo(targetPosition) <= m_distanceTolerance; + } +} // namespace omath::projectile_prediction diff --git a/tests/general/UnitTestPrediction.cpp b/tests/general/UnitTestPrediction.cpp index 59667bf..c77c438 100644 --- a/tests/general/UnitTestPrediction.cpp +++ b/tests/general/UnitTestPrediction.cpp @@ -1,5 +1,5 @@ #include -#include +#include TEST(UnitTestPrediction, PredictionTest) { @@ -8,7 +8,7 @@ TEST(UnitTestPrediction, PredictionTest) constexpr omath::projectile_prediction::Projectile proj = { .m_origin = {3, 2, 1}, .m_launchSpeed = 5000, .m_gravityScale = 0.4}; const auto viewPoint = - omath::projectile_prediction::Engine(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); + omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).MaybeCalculateAimPoint(proj, target); const auto [pitch, yaw, _] = proj.m_origin.ViewAngleTo(viewPoint.value()).AsTuple();