Files
omath/docs/projectile_prediction/proj_pred_engine_avx2.md
Orange 95c0873b8c Documents view angle struct and related API
Adds documentation for the `omath::ViewAngles` struct,
clarifying its purpose, common usage patterns,
and the definition of the types of pitch, yaw and roll.

Also, adds short explanations of how to use ViewAngles and what tradeoffs exist
between using raw float types and strongly typed Angle<> types.
2025-11-01 09:12:04 +03:00

162 lines
5.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# `omath::projectile_prediction::ProjPredEngineAvx2` — AVX2-accelerated ballistic aim solver
> Header: your projects `projectile_prediction/proj_pred_engine_avx2.hpp`
> Namespace: `omath::projectile_prediction`
> Inherits: `ProjPredEngineInterface`
> Depends on: `Vector3<float>`, `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<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)
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`, youll 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*