# `omath::projectile_prediction::ProjPredEngineAvx2` — AVX2-accelerated ballistic aim solver > Header: your project’s `projectile_prediction/proj_pred_engine_avx2.hpp` > Namespace: `omath::projectile_prediction` > Inherits: `ProjPredEngineInterface` > Depends on: `Vector3`, `Projectile`, `Target` > CPU: 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 ```cpp class ProjPredEngineAvx2 final : public ProjPredEngineInterface { public: [[nodiscard]] std::optional> 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 calculate_pitch(const Vector3& proj_origin, const Vector3& 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`**: 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) 1. **Predict target at time `t`** (constant-velocity model unless your `Target` carries 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. 3. **Yaw** is the azimuth toward `Δ.xz`. 4. **Pick the earliest feasible `t`** in `[Δt, Tmax]` (scanned in steps of `Δt`; AVX2 batches several `t` at once). 5. **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 private `calculate_pitch(...)` matches step **2** and returns `nullopt` if the trigonometric constraints are violated for that `t`. --- ## AVX2 notes * On x86/x64 with AVX2, candidate times `t` can 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 ```cpp 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. * **`t` constraints**: if viable solutions exist only beyond `Tmax`, you’ll get `nullopt`. * **Geometric infeasibility** at a given `t` (e.g., `d > v0*t`) causes `calculate_pitch` to 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)` where `Nt ≈ 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) = 0` shaped from the kinematics. --- ## 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*Tmax` and `h` large → verify pass/fail around thresholds. --- ## See also * `ProjPredEngineInterface` — base interface and general contract * `Projectile`, `Target` — data carriers for solver inputs (speed, origin, position, velocity, etc.) --- *Last updated: 1 Nov 2025*