Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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)

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 **\<platform\>-\<build configuration\>** preset to build siutable version for yourself. Like **windows-release** or **linux-release**.
## ❔ Usage
Expand Down
20 changes: 20 additions & 0 deletions include/omath/projectile_prediction/ProjPredEngine.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//
// Created by Vlad on 2/23/2025.
//
#pragma once
#include "Projectile.hpp"
#include "Target.hpp"
#include "omath/Vector3.hpp"


namespace omath::projectile_prediction
{
class ProjPredEngine
{
public:
[[nodiscard]]
virtual std::optional<Vector3> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const = 0;
virtual ~ProjPredEngine() = default;
};
} // namespace omath::projectile_prediction
26 changes: 26 additions & 0 deletions include/omath/projectile_prediction/ProjPredEngineAVX2.hpp
Original file line number Diff line number Diff line change
@@ -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<Vector3> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const override;


ProjPredEngineAVX2(float gravityConstant, float simulationTimeStep, float maximumSimulationTime);
~ProjPredEngineAVX2() override = default;

private:
[[nodiscard]] static std::optional<float> 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
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@

#include <optional>
#include "omath/Vector3.hpp"
#include "omath/prediction/Projectile.hpp"
#include "omath/prediction/Target.hpp"
#include "omath/projectile_prediction/ProjPredEngine.hpp"
#include "omath/projectile_prediction/Projectile.hpp"
#include "omath/projectile_prediction/Target.hpp"

namespace omath::prediction

namespace omath::projectile_prediction
{
class Engine final
class ProjPredEngineLegacy final : public ProjPredEngine
{
public:
explicit Engine(float gravityConstant, float simulationTimeStep,
float maximumSimulationTime, float distanceTolerance);
explicit ProjPredEngineLegacy(float gravityConstant, float simulationTimeStep, float maximumSimulationTime,
float distanceTolerance);

[[nodiscard]]
std::optional<Vector3> MaybeCalculateAimPoint(const Projectile& projectile, const Target& target) const;
std::optional<Vector3> MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const override;

private:
const float m_gravityConstant;
Expand All @@ -32,7 +35,7 @@ namespace omath::prediction


[[nodiscard]]
bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch, float time) const;

bool IsProjectileReachedTarget(const Vector3& targetPosition, const Projectile& projectile, float pitch,
float time) const;
};
}
} // namespace omath::projectile_prediction
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
#pragma once
#include "omath/Vector3.hpp"

namespace omath::prediction
namespace omath::projectile_prediction
{
class Projectile final
{
public:

Check notice on line 13 in include/omath/projectile_prediction/Projectile.hpp

View check run for this annotation

codefactor.io / CodeFactor

include/omath/projectile_prediction/Projectile.hpp#L13

Do not leave a blank line after "public:". (whitespace/blank_line)
[[nodiscard]]
Vector3 PredictPosition(float pitch, float yaw, float time, float gravity) const;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
#pragma once
#include "omath/Vector3.hpp"

namespace omath::prediction
namespace omath::projectile_prediction
{
class Target final
{
public:

Check notice on line 13 in include/omath/projectile_prediction/Target.hpp

View check run for this annotation

codefactor.io / CodeFactor

include/omath/projectile_prediction/Target.hpp#L13

Do not leave a blank line after "public:". (whitespace/blank_line)
[[nodiscard]]
constexpr Vector3 PredictPosition(const float time, const float gravity) const
{
Expand Down
2 changes: 1 addition & 1 deletion source/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
1 change: 0 additions & 1 deletion source/prediction/CMakeLists.txt

This file was deleted.

1 change: 1 addition & 0 deletions source/projectile_prediction/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target_sources(omath PRIVATE ProjPredEngineLegacy.cpp Projectile.cpp Target.cpp ProjPredEngineAVX2.cpp ProjPredEngine.cpp)
10 changes: 10 additions & 0 deletions source/projectile_prediction/ProjPredEngine.cpp
Original file line number Diff line number Diff line change
@@ -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
138 changes: 138 additions & 0 deletions source/projectile_prediction/ProjPredEngineAVX2.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
//
// Created by Vlad on 2/23/2025.
//
#include "omath/projectile_prediction/ProjPredEngineAVX2.hpp"


namespace omath::projectile_prediction
{
std::optional<Vector3> 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<float> 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
Original file line number Diff line number Diff line change
@@ -1,25 +1,18 @@
//
// Created by Vlad on 6/9/2024.
//


#include "omath/prediction/Engine.hpp"
#include "omath/projectile_prediction/ProjPredEngineLegacy.hpp"
#include <cmath>
#include <omath/Angles.hpp>


namespace omath::prediction
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)
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<Vector3> Engine::MaybeCalculateAimPoint(const Projectile &projectile, const Target &target) const
std::optional<Vector3> ProjPredEngineLegacy::MaybeCalculateAimPoint(const Projectile& projectile,
const Target& target) const
{
for (float time = 0.f; time < m_maximumSimulationTime; time += m_simulationTimeStep)
{
Expand All @@ -28,7 +21,7 @@ namespace omath::prediction
const auto projectilePitch = MaybeCalculateProjectileLaunchPitchAngle(projectile, predictedTargetPosition);

if (!projectilePitch.has_value()) [[unlikely]]
continue;
continue;

if (!IsProjectileReachedTarget(predictedTargetPosition, projectile, projectilePitch.value(), time))
continue;
Expand All @@ -41,8 +34,9 @@ namespace omath::prediction
return std::nullopt;
}

std::optional<float> Engine::MaybeCalculateProjectileLaunchPitchAngle(const Projectile &projectile,
const Vector3 &targetPosition) const
std::optional<float>
ProjPredEngineLegacy::MaybeCalculateProjectileLaunchPitchAngle(const Projectile& projectile,
const Vector3& targetPosition) const
{
const auto bulletGravity = m_gravityConstant * projectile.m_gravityScale;
const auto delta = targetPosition - projectile.m_origin;
Expand All @@ -51,24 +45,24 @@ namespace omath::prediction
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);
float root = launchSpeedSqr * launchSpeedSqr -
bulletGravity * (bulletGravity * distance2dSqr + 2.0f * delta.z * launchSpeedSqr);

if (root < 0.0f) [[unlikely]]
return std::nullopt;
return std::nullopt;

root = std::sqrt(root);
const float angle = std::atan((launchSpeedSqr - root) / (bulletGravity * distance2d));

return angles::RadiansToDegrees(angle);
}

bool Engine::IsProjectileReachedTarget(const Vector3 &targetPosition, const Projectile &projectile,
const float pitch, const float time) const
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Created by Vlad on 6/9/2024.
//

#include "omath/prediction/Projectile.hpp"
#include <cmath>
#include "omath/projectile_prediction/Projectile.hpp"

#include <omath/engines/Source/Formulas.hpp>

namespace omath::prediction
namespace omath::projectile_prediction
{
Vector3 Projectile::PredictPosition(const float pitch, const float yaw, const float time, const float gravity) const
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading