omath::projectile_prediction::ProjPredEngineAvx2 — AVX2-accelerated ballistic aim solver
Header: your project’s
projectile_prediction/proj_pred_engine_avx2.hppNamespace:omath::projectile_predictionInherits:ProjPredEngineInterfaceDepends on:Vector3<float>,Projectile,TargetCPU: Uses AVX2 when available; falls back to scalar elsewhere (fields are marked[[maybe_unused]]for non-x86/AVX2 builds).
This engine computes a world-space aim point (and implicitly the firing yaw/pitch) to intersect a moving target under a constant gravity model and constant muzzle speed. It typically scans candidate times of flight and solves for the elevation (pitch) that makes the vertical and horizontal kinematics meet at the same time.
API
class ProjPredEngineAvx2 final : public ProjPredEngineInterface {
public:
[[nodiscard]]
std::optional<Vector3<float>>
maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const override;
ProjPredEngineAvx2(float gravity_constant,
float simulation_time_step,
float maximum_simulation_time);
~ProjPredEngineAvx2() override = default;
private:
// Solve for pitch at a fixed time-of-flight t.
[[nodiscard]]
static std::optional<float>
calculate_pitch(const Vector3<float>& proj_origin,
const Vector3<float>& target_pos,
float bullet_gravity, float v0, float time);
// Tunables (may be unused on non-AVX2 builds)
[[maybe_unused]] const float m_gravity_constant; // |g| (e.g., 9.81)
[[maybe_unused]] const float m_simulation_time_step; // Δt (e.g., 1/240 s)
[[maybe_unused]] const float m_maximum_simulation_time; // Tmax (e.g., 3 s)
};
Parameters (constructor)
gravity_constant— magnitude of gravity (units consistent with your world, e.g., m/s²).simulation_time_step— Δt used to scan candidate intercept times.maximum_simulation_time— cap on time of flight; larger allows longer-range solutions but increases cost.
Return (solver)
-
maybe_calculate_aim_point(...)Vector3<float>: a world-space aim point yielding an intercept under the model.std::nullopt: no feasible solution (e.g., target receding too fast, out of range, or kinematics inconsistent).
How it solves (expected flow)
- Predict target at time
t(constant-velocity model unless yourTargetcarries more):
T(t) = target.position + target.velocity * t
2. Horizontal/vertical kinematics at fixed t with muzzle speed v0 and gravity g:
* Let `Δ = T(t) - proj_origin`, `d = length(Δ.xz)`, `h = Δ.y`.
* Required initial components:
```
cosθ = d / (v0 * t)
sinθ = (h + 0.5 * g * t^2) / (v0 * t)
```
* If `cosθ` ∈ [−1,1] and `sinθ` ∈ [−1,1] and `sin²θ + cos²θ ≈ 1`, then
```
θ = atan2(sinθ, cosθ)
```
That is what `calculate_pitch(...)` returns on success.
- Yaw is the azimuth toward
Δ.xz. - Pick the earliest feasible
tin[Δt, Tmax](scanned in steps ofΔt; AVX2 batches severaltat once). -
Return the aim point. Common choices:
- The impact point
T(t*)(useful as a HUD marker), or - A point along the initial firing ray at some convenient range using
(yaw, pitch); both are consistent—pick the convention your caller expects.
- The impact point
The private
calculate_pitch(...)matches step 2 and returnsnulloptif the trigonometric constraints are violated for thatt.
AVX2 notes
- On x86/x64 with AVX2, candidate times
tcan be evaluated 8 at a time using FMA (great for dense scans). - On ARM/ARM64 (no AVX2), code falls back to scalar math; the
[[maybe_unused]]members acknowledge compilation without SIMD.
Usage example
using namespace omath::projectile_prediction;
ProjPredEngineAvx2 solver(
/*gravity*/ 9.81f,
/*dt*/ 1.0f/240.0f,
/*Tmax*/ 3.0f
);
Projectile proj; // fill: origin, muzzle_speed, etc.
Target tgt; // fill: position, velocity
if (auto aim = solver.maybe_calculate_aim_point(proj, tgt)) {
// Aim your weapon at *aim and fire with muzzle speed proj.v0
// If you need yaw/pitch explicitly, replicate the pitch solve and azimuth.
} else {
// No solution (out of envelope) — pick a fallback
}
Edge cases & failure modes
- Zero or tiny
v0→ no solution. - Target collinear & receding faster than
v0→ no solution. tconstraints: if viable solutions exist only beyondTmax, you’ll getnullopt.- Geometric infeasibility at a given
t(e.g.,d > v0*t) causescalculate_pitchto fail that sample. - Numerical tolerance: check
sin²θ + cos²θagainst 1 with a small epsilon (e.g.,1e-3).
Performance & tuning
- Work is roughly
O(Nt)whereNt ≈ Tmax / Δt. - Smaller
Δt→ better accuracy, higher cost. With AVX2 you can afford smaller steps. -
If you frequently miss solutions between steps, consider:
- Coarse-to-fine: coarse scan, then local refine around the best
t. - Newton on time: root-find
‖horizontal‖ − v0 t cosθ(t) = 0shaped from the kinematics.
- Coarse-to-fine: coarse scan, then local refine around the best
Testing checklist
- Stationary target at same height → θ ≈ 0, aim point ≈ target.
- Higher target → positive pitch; lower target → negative pitch.
- Perpendicular moving target → feasible at moderate speeds.
- Very fast receding target →
nullopt. - Boundary:
d ≈ v0*Tmaxandhlarge → verify pass/fail around thresholds.
See also
ProjPredEngineInterface— base interface and general contractProjectile,Target— data carriers for solver inputs (speed, origin, position, velocity, etc.)
Last updated: 1 Nov 2025