diff --git a/README.md b/README.md
index e8591b2d..0bf77770 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
-
+


@@ -43,25 +43,52 @@ It provides the latest features, is highly customizable, has all for cheat devel
-# Features
-- **Efficiency**: Optimized for performance, ensuring quick computations using AVX2.
-- **Versatility**: Includes a wide array of mathematical functions and algorithms.
-- **Ease of Use**: Simplified interface for convenient integration into various projects.
-- **Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot.
-- **3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline.
-- **Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
-- **No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
-- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
-- **Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine and canonical OpenGL**.
-- **Cross platform**: Supports Windows, MacOS and Linux.
-- **Algorithms**: Has ability to scan for byte pattern with wildcards in PE files/modules, binary slices, works even with Wine apps.
+## 🚀 Quick Example
+
+```cpp
+#include
+
+using namespace omath;
+
+// 3D vector operations
+Vector3 a{1, 2, 3};
+Vector3 b{4, 5, 6};
+
+auto dot = a.dot(b); // 32.0
+auto cross = a.cross(b); // (-3, 6, -3)
+auto distance = a.distance_to(b); // ~5.196
+auto normalized = a.normalized(); // Unit vector
+
+// World-to-screen projection (Source Engine example)
+using namespace omath::source_engine;
+Camera camera(position, angles, viewport, fov, near_plane, far_plane);
+
+if (auto screen = camera.world_to_screen(world_position)) {
+ // Draw at screen->x, screen->y
+}
+```
+
+**[➡️ See more examples and tutorials][TUTORIALS]**
+
+# ✨ Features
+- **🚀 Efficiency**: Optimized for performance, ensuring quick computations using AVX2.
+- **🎯 Versatility**: Includes a wide array of mathematical functions and algorithms.
+- **✅ Ease of Use**: Simplified interface for convenient integration into various projects.
+- **🎮 Projectile Prediction**: Projectile prediction engine with O(N) algo complexity, that can power you projectile aim-bot.
+- **📐 3D Projection**: No need to find view-projection matrix anymore you can make your own projection pipeline.
+- **💥 Collision Detection**: Production ready code to handle collision detection by using simple interfaces.
+- **📦 No Additional Dependencies**: No additional dependencies need to use OMath except unit test execution
+- **🔧 Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types!
+- **🎯 Engine support**: Supports coordinate systems of **Source, Unity, Unreal, Frostbite, IWEngine and canonical OpenGL**.
+- **🌍 Cross platform**: Supports Windows, MacOS and Linux.
+- **🔍 Algorithms**: Has ability to scan for byte pattern with wildcards in PE files/modules, binary slices, works even with Wine apps.
# Gallery
-[](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ)
+[](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ)
@@ -84,17 +111,35 @@ It provides the latest features, is highly customizable, has all for cheat devel
+## 📚 Documentation
+
+- **[Getting Started Guide](https://libomath.org/getting_started/)** - Installation and first steps
+- **[API Overview](https://libomath.org/api_overview/)** - Complete API reference
+- **[Tutorials](https://libomath.org/tutorials/)** - Step-by-step guides
+- **[FAQ](https://libomath.org/faq/)** - Common questions and answers
+- **[Troubleshooting](https://libomath.org/troubleshooting/)** - Solutions to common issues
+- **[Best Practices](https://libomath.org/best_practices/)** - Guidelines for effective usage
+
+## 🤝 Community & Support
+
+- **Discord**: [Join our community](https://discord.gg/eDgdaWbqwZ)
+- **Telegram**: [@orangennotes](https://t.me/orangennotes)
+- **Issues**: [Report bugs or request features](https://github.com/orange-cpp/omath/issues)
+- **Contributing**: See [CONTRIBUTING.md](CONTRIBUTING.md) for guidelines
+
# 💘 Acknowledgments
- [All contributors](https://github.com/orange-cpp/omath/graphs/contributors)
-[APEX Preview]: .github/images/showcase/apex.png
-[BO2 Preview]: .github/images/showcase/cod_bo2.png
-[CS2 Preview]: .github/images/showcase/cs2.jpeg
-[TF2 Preview]: .github/images/showcase/tf2.jpg
+[APEX Preview]: docs/images/showcase/apex.png
+[BO2 Preview]: docs/images/showcase/cod_bo2.png
+[CS2 Preview]: docs/images/showcase/cs2.jpeg
+[TF2 Preview]: docs/images/showcase/tf2.jpg
+[QUICKSTART]: docs/getting_started.md
[INSTALL]: INSTALL.md
[DOCUMENTATION]: http://libomath.org
+[TUTORIALS]: docs/tutorials.md
[CONTRIBUTING]: CONTRIBUTING.md
[EXAMPLES]: examples
[SPONSOR]: https://boosty.to/orangecpp/purchase/3568644?ssource=DIRECT&share=subscription_link
diff --git a/docs/3d_primitives/box.md b/docs/3d_primitives/box.md
new file mode 100644
index 00000000..84e392d6
--- /dev/null
+++ b/docs/3d_primitives/box.md
@@ -0,0 +1,118 @@
+# `omath::primitives::create_box` — Build an oriented box as 12 triangles
+
+> Header: your project’s `primitives/box.hpp` (declares `create_box`)
+> Namespace: `omath::primitives`
+> Depends on: `omath::Triangle>`, `omath::Vector3`
+
+```cpp
+[[nodiscard]]
+std::array>, 12>
+create_box(const Vector3& top,
+ const Vector3& bottom,
+ const Vector3& dir_forward,
+ const Vector3& dir_right,
+ float ratio = 4.f) noexcept;
+```
+
+---
+
+## What it does
+
+Constructs a **rectangular cuboid (“box”)** oriented in 3D space and returns its surface as **12 triangles** (2 per face × 6 faces). The box’s central axis runs from `bottom` → `top`. The **up** direction is inferred from that segment; the **forward** and **right** directions define the box’s orientation around that axis.
+
+The lateral half-extents are derived from the axis length and `ratio`:
+
+> Let `H = |top - bottom|`. Lateral half-size ≈ `H / ratio` along both `dir_forward` and `dir_right`
+> (i.e., the cross-section is a square of side `2H/ratio`).
+
+> **Note:** This describes the intended behavior from the interface. If you rely on a different sizing rule, document it next to your implementation.
+
+---
+
+## Parameters
+
+* `top`
+ Center of the **top face**.
+
+* `bottom`
+ Center of the **bottom face**.
+
+* `dir_forward`
+ A direction that orients the box around its up axis. Should be **non-zero** and **not collinear** with `top - bottom`.
+
+* `dir_right`
+ A direction roughly orthogonal to both `dir_forward` and `top - bottom`. Used to fully fix orientation.
+
+* `ratio` (default `4.0f`)
+ Controls thickness relative to height. Larger values → thinner box.
+ With the default rule above, half-extent = `|top-bottom|/ratio`.
+
+---
+
+## Return value
+
+`std::array>, 12>` — the six faces of the box, triangulated.
+Winding is intended to be **outward-facing** (right-handed coordinates). Do not rely on a specific **face ordering**; treat the array as opaque unless your implementation guarantees an order.
+
+---
+
+## Expected math & robustness
+
+* Define `u = normalize(top - bottom)`.
+* Re-orthonormalize the basis to avoid skew:
+
+ ```cpp
+ f = normalize(dir_forward - u * u.dot(dir_forward)); // drop any up component
+ r = normalize(u.cross(f)); // right-handed basis
+ // (Optionally recompute f = r.cross(u) for orthogonality)
+ ```
+* Half-extents: `h = length(top - bottom) / ratio; hf = h * f; hr = h * r`.
+* Corners (top): `t±r±f = top ± hr ± hf`; (bottom): `b±r±f = bottom ± hr ± hf`.
+* Triangulate each face with consistent CCW winding when viewed from outside.
+
+---
+
+## Example
+
+```cpp
+using omath::Vector3;
+using omath::Triangle;
+using omath::primitives::create_box;
+
+// Axis from bottom to top (height 2)
+Vector3 bottom{0, 0, 0};
+Vector3 top {0, 2, 0};
+
+// Orientation around the axis
+Vector3 forward{0, 0, 1};
+Vector3 right {1, 0, 0};
+
+// Ratio 4 → lateral half-size = height/4 = 0.5
+auto tris = create_box(top, bottom, forward, right, 4.0f);
+
+// Use the triangles (normals, rendering, collision, etc.)
+for (const auto& tri : tris) {
+ auto n = tri.calculate_normal();
+ (void)n;
+}
+```
+
+---
+
+## Usage notes & pitfalls
+
+* **Degenerate axis**: If `top == bottom`, the box is undefined (zero height). Guard against this.
+* **Directions**: Provide **non-zero**, **reasonably orthogonal** `dir_forward`/`dir_right`. A robust implementation should project/normalize internally, but callers should still pass sensible inputs.
+* **Winding**: If your renderer or collision expects a specific winding, verify with a unit test and flip vertex order per face if necessary.
+* **Thickness policy**: This doc assumes both lateral half-extents equal `|top-bottom|/ratio`. If your implementation diverges (e.g., separate forward/right ratios), document it.
+
+---
+
+## See also
+
+* `omath::Triangle` (vertex utilities: normals, centroid, etc.)
+* `omath::Vector3` (geometry operations used by the construction)
+
+---
+
+*Last updated: 31 Oct 2025*
diff --git a/docs/3d_primitives/plane.md b/docs/3d_primitives/plane.md
new file mode 100644
index 00000000..c4619a3a
--- /dev/null
+++ b/docs/3d_primitives/plane.md
@@ -0,0 +1,98 @@
+# `omath::primitives::create_plane` — Build an oriented quad (2 triangles)
+
+> Header: your project’s `primitives/plane.hpp`
+> Namespace: `omath::primitives`
+> Depends on: `omath::Triangle>`, `omath::Vector3`
+
+```cpp
+[[nodiscard]]
+std::array>, 2>
+create_plane(const Vector3& vertex_a,
+ const Vector3& vertex_b,
+ const Vector3& direction,
+ float size) noexcept;
+```
+
+---
+
+## What it does
+
+Creates a **rectangle (quad)** in 3D oriented by the edge **A→B** and a second in-plane **direction**. The quad is returned as **two triangles** suitable for rendering or collision.
+
+* Edge axis: `e = vertex_b - vertex_a`
+* Width axis: “direction”, **projected to be perpendicular to `e`** so the quad is planar and well-formed.
+* Normal (by right-hand rule): `n ∝ e × width`.
+
+> **Sizing convention**
+> Typical construction uses **half-width = `size`** along the (normalized, orthogonalized) *direction*, i.e. the total width is `2*size`.
+> If your implementation interprets `size` as full width, adjust your expectations accordingly.
+
+---
+
+## Parameters
+
+* `vertex_a`, `vertex_b` — two adjacent quad vertices defining the **long edge** of the plane.
+* `direction` — a vector indicating the **cross-edge direction** within the plane (does not need to be orthogonal or normalized).
+* `size` — **half-width** of the quad along the (processed) `direction`.
+
+---
+
+## Return
+
+`std::array>, 2>` — the quad triangulated (consistent CCW winding, outward normal per `e × width`).
+
+---
+
+## Robust construction (expected math)
+
+1. `e = vertex_b - vertex_a`
+2. Make `d` perpendicular to `e`:
+
+ ```
+ d = direction - e * (e.dot(direction) / e.length_sqr());
+ if (d.length_sqr() == 0) pick an arbitrary perpendicular to e
+ d = d.normalized();
+ ```
+3. Offsets: `w = d * size`
+4. Four corners:
+
+ ```
+ A0 = vertex_a - w; A1 = vertex_a + w;
+ B0 = vertex_b - w; B1 = vertex_b + w;
+ ```
+5. Triangles (CCW when viewed from +normal):
+
+ ```
+ T0 = Triangle{ A0, A1, B1 }
+ T1 = Triangle{ A0, B1, B0 }
+ ```
+
+---
+
+## Example
+
+```cpp
+using omath::Vector3;
+using omath::Triangle;
+using omath::primitives::create_plane;
+
+Vector3 a{ -1, 0, -1 }; // edge start
+Vector3 b{ 1, 0, -1 }; // edge end
+Vector3 dir{ 0, 0, 1 }; // cross-edge direction within the plane (roughly +Z)
+float half_width = 2.0f;
+
+auto quad = create_plane(a, b, dir, half_width);
+
+// e.g., compute normals
+for (const auto& tri : quad) {
+ auto n = tri.calculate_normal(); (void)n;
+}
+```
+
+---
+
+## Notes & edge cases
+
+* **Degenerate edge**: if `vertex_a == vertex_b`, the plane is undefined.
+* **Collinearity**: if `direction` is parallel to `vertex_b - vertex_a`, the function must choose an alternate perpendicular; expect a fallback.
+* **Winding**: If your renderer expects a specific face order, verify and swap the two vertices in each triangle as needed.
diff --git a/docs/api_overview.md b/docs/api_overview.md
new file mode 100644
index 00000000..6dc0ad3b
--- /dev/null
+++ b/docs/api_overview.md
@@ -0,0 +1,527 @@
+# API Overview
+
+This document provides a high-level overview of OMath's API, organized by functionality area.
+
+---
+
+## Module Organization
+
+OMath is organized into several logical modules:
+
+### Core Mathematics
+- **Linear Algebra** - Vectors, matrices, triangles
+- **Trigonometry** - Angles, view angles, trigonometric functions
+- **3D Primitives** - Boxes, planes, geometric shapes
+
+### Game Development
+- **Collision Detection** - Ray tracing, intersection tests
+- **Projectile Prediction** - Ballistics and aim-assist calculations
+- **Projection** - Camera systems and world-to-screen transformations
+- **Pathfinding** - A* algorithm, navigation meshes
+
+### Engine Support
+- **Source Engine** - Valve's Source Engine (CS:GO, TF2, etc.)
+- **Unity Engine** - Unity game engine
+- **Unreal Engine** - Epic's Unreal Engine
+- **Frostbite Engine** - EA's Frostbite Engine
+- **IW Engine** - Infinity Ward's engine (Call of Duty)
+- **OpenGL Engine** - Canonical OpenGL coordinate system
+
+### Utilities
+- **Color** - RGBA color representation
+- **Pattern Scanning** - Memory pattern search (wildcards, PE files)
+- **Reverse Engineering** - Internal/external memory manipulation
+
+---
+
+## Core Types
+
+### Vectors
+
+All vector types are template-based and support arithmetic types.
+
+| Type | Description | Key Methods |
+|------|-------------|-------------|
+| `Vector2` | 2D vector | `length()`, `normalized()`, `dot()`, `distance_to()` |
+| `Vector3` | 3D vector | `length()`, `normalized()`, `dot()`, `cross()`, `angle_between()` |
+| `Vector4` | 4D vector | Extends Vector3 with `w` component |
+
+**Common aliases:**
+```cpp
+using Vec2f = Vector2;
+using Vec3f = Vector3;
+using Vec4f = Vector4;
+```
+
+**Key features:**
+- Component-wise arithmetic (+, -, *, /)
+- Scalar multiplication/division
+- Dot and cross products
+- Safe normalization (returns original if length is zero)
+- Distance calculations
+- Angle calculations with error handling
+- Hash support for `float` variants
+- `std::formatter` support
+
+### Matrices
+
+| Type | Description | Key Methods |
+|------|-------------|-------------|
+| `Mat4X4` | 4×4 matrix | `identity()`, `transpose()`, `determinant()`, `inverse()` |
+
+**Use cases:**
+- Transformation matrices
+- View matrices
+- Projection matrices
+- Model-view-projection pipelines
+
+### Angles
+
+Strong-typed angle system with compile-time range enforcement:
+
+| Type | Range | Description |
+|------|-------|-------------|
+| `Angle` | Custom | Generic angle type with bounds |
+| `PitchAngle` | [-89°, 89°] | Vertical camera rotation |
+| `YawAngle` | [-180°, 180°] | Horizontal camera rotation |
+| `RollAngle` | [-180°, 180°] | Camera roll |
+| `ViewAngles` | - | Composite pitch/yaw/roll |
+
+**Features:**
+- Automatic normalization/clamping based on flags
+- Conversions between degrees and radians
+- Type-safe arithmetic
+- Prevents common angle bugs
+
+---
+
+## Projection System
+
+### Camera
+
+Generic camera template that works with any engine trait:
+
+```cpp
+template
+class Camera;
+```
+
+**Engine-specific cameras:**
+```cpp
+omath::source_engine::Camera // Source Engine
+omath::unity_engine::Camera // Unity
+omath::unreal_engine::Camera // Unreal
+omath::frostbite_engine::Camera // Frostbite
+omath::iw_engine::Camera // IW Engine
+omath::opengl_engine::Camera // OpenGL
+```
+
+**Core methods:**
+- `world_to_screen(Vector3)` - Project 3D point to 2D screen
+- `get_view_matrix()` - Get current view matrix
+- `get_projection_matrix()` - Get current projection matrix
+- `update(position, angles)` - Update camera state
+
+**Supporting types:**
+- `ViewPort` - Screen dimensions and aspect ratio
+- `FieldOfView` - FOV in degrees with validation
+- `ProjectionError` - Error codes for projection failures
+
+---
+
+## Collision Detection
+
+### LineTracer
+
+Ray-casting and line tracing utilities:
+
+```cpp
+namespace omath::collision {
+ class LineTracer;
+}
+```
+
+**Features:**
+- Ray-triangle intersection
+- Ray-plane intersection
+- Ray-box intersection
+- Distance calculations
+- Normal calculations at hit points
+
+### 3D Primitives
+
+| Type | Description | Key Methods |
+|------|-------------|-------------|
+| `Plane` | Infinite plane | `intersects_ray()`, `distance_to_point()` |
+| `Box` | Axis-aligned bounding box | `contains()`, `intersects()` |
+
+---
+
+## Projectile Prediction
+
+### Interfaces
+
+**`ProjPredEngineInterface`** - Base interface for all prediction engines
+
+```cpp
+virtual std::optional>
+maybe_calculate_aim_point(const Projectile&, const Target&) const = 0;
+```
+
+### Implementations
+
+| Engine | Description | Optimizations |
+|--------|-------------|---------------|
+| `ProjPredEngineLegacy` | Standard implementation | Portable, works everywhere |
+| `ProjPredEngineAVX2` | AVX2 optimized | 2-4x faster on modern CPUs |
+
+### Supporting Types
+
+**`Projectile`** - Defines projectile properties:
+```cpp
+struct Projectile {
+ Vector3 origin;
+ float speed;
+ Vector3 gravity;
+ // ... additional properties
+};
+```
+
+**`Target`** - Defines target state:
+```cpp
+struct Target {
+ Vector3 position;
+ Vector3 velocity;
+ // ... additional properties
+};
+```
+
+---
+
+## Pathfinding
+
+### A* Algorithm
+
+```cpp
+namespace omath::pathfinding {
+ template
+ class AStar;
+}
+```
+
+**Features:**
+- Generic node type support
+- Customizable heuristics
+- Efficient priority queue implementation
+- Path reconstruction
+
+### Navigation Mesh
+
+```cpp
+namespace omath::pathfinding {
+ class NavigationMesh;
+}
+```
+
+**Features:**
+- Triangle-based navigation
+- Neighbor connectivity
+- Walkable area definitions
+
+---
+
+## Engine Traits
+
+Each game engine has a trait system providing engine-specific math:
+
+### CameraTrait
+
+Implements camera math for an engine:
+- `calc_look_at_angle()` - Calculate angles to look at a point
+- `calc_view_matrix()` - Build view matrix from angles and position
+- `calc_projection_matrix()` - Build projection matrix from FOV and viewport
+
+### PredEngineTrait
+
+Provides physics/ballistics specific to an engine:
+- Gravity vectors
+- Coordinate system conventions
+- Unit conversions
+- Physics parameters
+
+### Available Traits
+
+| Engine | Camera Trait | Pred Engine Trait | Constants | Formulas |
+|--------|--------------|-------------------|-----------|----------|
+| Source Engine | ✓ | ✓ | ✓ | ✓ |
+| Unity Engine | ✓ | ✓ | ✓ | ✓ |
+| Unreal Engine | ✓ | ✓ | ✓ | ✓ |
+| Frostbite | ✓ | ✓ | ✓ | ✓ |
+| IW Engine | ✓ | ✓ | ✓ | ✓ |
+| OpenGL | ✓ | ✓ | ✓ | ✓ |
+
+**Documentation:**
+- See `docs/engines//` for detailed per-engine docs
+- Each engine has separate docs for camera_trait, pred_engine_trait, constants, and formulas
+
+---
+
+## Utility Functions
+
+### Color
+
+```cpp
+struct Color {
+ uint8_t r, g, b, a;
+
+ // Conversions
+ static Color from_hsv(float h, float s, float v);
+ static Color from_hex(uint32_t hex);
+ uint32_t to_hex() const;
+
+ // Blending
+ Color blend(const Color& other, float t) const;
+};
+```
+
+### Pattern Scanning
+
+**Binary pattern search with wildcards:**
+
+```cpp
+// Pattern with wildcards (?? = any byte)
+PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0"};
+
+// Scan memory
+auto result = pattern_scan(memory_buffer, pattern);
+if (result) {
+ std::cout << "Found at offset: " << result->offset << "\n";
+}
+```
+
+**PE file scanning:**
+
+```cpp
+PEPatternScanner scanner("target.exe");
+if (auto addr = scanner.scan_pattern(pattern)) {
+ std::cout << "Found at RVA: " << *addr << "\n";
+}
+```
+
+### Reverse Engineering
+
+**External memory access:**
+```cpp
+ExternalRevObject process("game.exe");
+Vector3 position = process.read>(address);
+process.write(address, new_position);
+```
+
+**Internal memory access:**
+```cpp
+InternalRevObject memory;
+auto value = memory.read(address);
+memory.write(address, new_value);
+```
+
+---
+
+## Concepts and Constraints
+
+OMath uses C++20 concepts for type safety:
+
+```cpp
+template
+concept Arithmetic = std::is_arithmetic_v;
+
+template
+concept CameraEngineConcept = requires(EngineTrait t) {
+ { t.calc_look_at_angle(...) } -> /* returns angles */;
+ { t.calc_view_matrix(...) } -> /* returns matrix */;
+ { t.calc_projection_matrix(...) } -> /* returns matrix */;
+};
+```
+
+---
+
+## Error Handling
+
+OMath uses modern C++ error handling:
+
+### std::expected (C++23)
+
+```cpp
+std::expected, Vector3Error>
+angle_between(const Vector3& other) const;
+
+if (auto angle = v1.angle_between(v2)) {
+ // Success: use *angle
+} else {
+ // Error: angle.error() gives Vector3Error
+}
+```
+
+### std::optional
+
+```cpp
+std::optional>
+world_to_screen(const Vector3& world);
+
+if (auto screen = camera.world_to_screen(pos)) {
+ // Success: use screen->x, screen->y
+} else {
+ // Point not visible
+}
+```
+
+### Error Codes
+
+```cpp
+enum class ProjectionError {
+ SUCCESS = 0,
+ POINT_BEHIND_CAMERA,
+ INVALID_VIEWPORT,
+ // ...
+};
+```
+
+---
+
+## Performance Considerations
+
+### constexpr Support
+
+Most operations are `constexpr` where possible:
+
+```cpp
+constexpr Vector3 v{1, 2, 3};
+constexpr auto len_sq = v.length_sqr(); // Computed at compile time
+```
+
+### AVX2 Optimizations
+
+Use AVX2 variants when available:
+
+```cpp
+// Standard: portable but slower
+ProjPredEngineLegacy legacy_engine;
+
+// AVX2: 2-4x faster on modern CPUs
+ProjPredEngineAVX2 fast_engine;
+```
+
+**When to use AVX2:**
+- Modern Intel/AMD processors (2013+)
+- Performance-critical paths
+- Batch operations
+
+**When to use Legacy:**
+- Older processors
+- ARM platforms
+- Guaranteed compatibility
+
+### Cache Efficiency
+
+```cpp
+// Good: contiguous storage
+std::vector> positions;
+
+// Good: structure of arrays for SIMD
+struct Particles {
+ std::vector x, y, z;
+};
+```
+
+---
+
+## Platform Support
+
+| Platform | Support | Notes |
+|----------|---------|-------|
+| Windows | ✓ | MSVC, Clang, GCC |
+| Linux | ✓ | GCC, Clang |
+| macOS | ✓ | Clang |
+
+**Minimum requirements:**
+- C++20 compiler
+- C++23 recommended for `std::expected`
+
+---
+
+## Thread Safety
+
+- **Vector/Matrix types**: Thread-safe (immutable operations)
+- **Camera**: Not thread-safe (mutable state)
+- **Pattern scanning**: Thread-safe (read-only operations)
+- **Memory access**: Depends on OS/process synchronization
+
+**Thread-safe example:**
+```cpp
+// Safe: each thread gets its own camera
+std::vector threads;
+for (int i = 0; i < num_threads; ++i) {
+ threads.emplace_back([i]() {
+ Camera camera = /* create camera */;
+ // Use camera in this thread
+ });
+}
+```
+
+---
+
+## Best Practices
+
+### 1. Use Type Aliases
+
+```cpp
+using Vec3f = omath::Vector3;
+using Mat4 = omath::Mat4X4;
+```
+
+### 2. Prefer constexpr When Possible
+
+```cpp
+constexpr auto compute_at_compile_time() {
+ Vector3 v{1, 2, 3};
+ return v.length_sqr();
+}
+```
+
+### 3. Check Optional/Expected Results
+
+```cpp
+// Good
+if (auto result = camera.world_to_screen(pos)) {
+ use(*result);
+}
+
+// Bad - may crash
+auto result = camera.world_to_screen(pos);
+use(result->x); // Undefined behavior if nullopt
+```
+
+### 4. Use Engine-Specific Types
+
+```cpp
+// Good: uses correct coordinate system
+using namespace omath::source_engine;
+Camera camera = /* ... */;
+
+// Bad: mixing engine types
+using UnityCamera = omath::unity_engine::Camera;
+using SourceAngles = omath::source_engine::ViewAngles;
+UnityCamera camera{pos, SourceAngles{}}; // Wrong!
+```
+
+---
+
+## See Also
+
+- [Getting Started Guide](getting_started.md)
+- [Installation Instructions](install.md)
+- [Examples Directory](../examples/)
+- Individual module documentation in respective folders
+
+---
+
+*Last updated: 1 Nov 2025*
diff --git a/docs/best_practices.md b/docs/best_practices.md
new file mode 100644
index 00000000..d38584ff
--- /dev/null
+++ b/docs/best_practices.md
@@ -0,0 +1,532 @@
+# Best Practices
+
+Guidelines for using OMath effectively and avoiding common pitfalls.
+
+---
+
+## Code Organization
+
+### Use Type Aliases
+
+Define clear type aliases for commonly used types:
+
+```cpp
+// Good: Clear and concise
+using Vec3f = omath::Vector3;
+using Vec2f = omath::Vector2;
+using Mat4 = omath::Mat4X4;
+
+Vec3f position{1.0f, 2.0f, 3.0f};
+```
+
+```cpp
+// Avoid: Verbose and repetitive
+omath::Vector3 position{1.0f, 2.0f, 3.0f};
+omath::Vector3 velocity{0.0f, 0.0f, 0.0f};
+```
+
+### Namespace Usage
+
+Be selective with `using namespace`:
+
+```cpp
+// Good: Specific namespace for your engine
+using namespace omath::source_engine;
+
+// Good: Import specific types
+using omath::Vector3;
+using omath::Vector2;
+
+// Avoid: Too broad
+using namespace omath; // Imports everything
+```
+
+### Include What You Use
+
+```cpp
+// Good: Include specific headers
+#include
+#include
+
+// Okay for development
+#include
+
+// Production: Include only what you need
+// to reduce compile times
+```
+
+---
+
+## Error Handling
+
+### Always Check Optional Results
+
+```cpp
+// Good: Check before using
+if (auto screen = camera.world_to_screen(world_pos)) {
+ draw_at(screen->x, screen->y);
+} else {
+ // Handle point not visible
+}
+
+// Bad: Unchecked access can crash
+auto screen = camera.world_to_screen(world_pos);
+draw_at(screen->x, screen->y); // Undefined behavior if nullopt!
+```
+
+### Handle Expected Errors
+
+```cpp
+// Good: Handle error case
+if (auto angle = v1.angle_between(v2)) {
+ use_angle(*angle);
+} else {
+ switch (angle.error()) {
+ case Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE:
+ // Handle zero-length vector
+ break;
+ }
+}
+
+// Bad: Assume success
+auto angle = v1.angle_between(v2);
+use_angle(*angle); // Throws if error!
+```
+
+### Validate Inputs
+
+```cpp
+// Good: Validate before expensive operations
+bool is_valid_projectile(const Projectile& proj) {
+ return proj.speed > 0.0f &&
+ std::isfinite(proj.speed) &&
+ std::isfinite(proj.origin.length());
+}
+
+if (is_valid_projectile(proj) && is_valid_target(target)) {
+ auto aim = engine.maybe_calculate_aim_point(proj, target);
+}
+```
+
+---
+
+## Performance
+
+### Use constexpr When Possible
+
+```cpp
+// Good: Computed at compile time
+constexpr Vector3 gravity{0.0f, 0.0f, -9.81f};
+constexpr float max_range = 1000.0f;
+constexpr float max_range_sq = max_range * max_range;
+
+// Use in runtime calculations
+if (position.length_sqr() < max_range_sq) {
+ // ...
+}
+```
+
+### Prefer Squared Distance
+
+```cpp
+// Good: Avoids expensive sqrt
+constexpr float max_dist_sq = 100.0f * 100.0f;
+for (const auto& entity : entities) {
+ if (entity.pos.distance_to_sqr(player_pos) < max_dist_sq) {
+ // Process nearby entity
+ }
+}
+
+// Avoid: Unnecessary sqrt calls
+constexpr float max_dist = 100.0f;
+for (const auto& entity : entities) {
+ if (entity.pos.distance_to(player_pos) < max_dist) {
+ // More expensive
+ }
+}
+```
+
+### Cache Expensive Calculations
+
+```cpp
+// Good: Update camera once per frame
+void update_frame() {
+ camera.update(current_position, current_angles);
+
+ // All projections use cached matrices
+ for (const auto& entity : entities) {
+ if (auto screen = camera.world_to_screen(entity.pos)) {
+ draw_entity(screen->x, screen->y);
+ }
+ }
+}
+
+// Bad: Camera recreated each call
+for (const auto& entity : entities) {
+ Camera cam(pos, angles, viewport, fov, near, far); // Expensive!
+ auto screen = cam.world_to_screen(entity.pos);
+}
+```
+
+### Choose the Right Engine
+
+```cpp
+// Good: Use AVX2 when available
+#ifdef __AVX2__
+ using Engine = ProjPredEngineAVX2;
+#else
+ using Engine = ProjPredEngineLegacy;
+#endif
+
+Engine prediction_engine;
+
+// Or runtime detection
+Engine* create_best_engine() {
+ if (cpu_supports_avx2()) {
+ return new ProjPredEngineAVX2();
+ }
+ return new ProjPredEngineLegacy();
+}
+```
+
+### Minimize Allocations
+
+```cpp
+// Good: Reuse vectors
+std::vector> positions;
+positions.reserve(expected_count);
+
+// In loop
+positions.clear(); // Doesn't deallocate
+for (...) {
+ positions.push_back(compute_position());
+}
+
+// Bad: Allocate every time
+for (...) {
+ std::vector> positions; // Allocates each iteration
+ // ...
+}
+```
+
+---
+
+## Type Safety
+
+### Use Strong Angle Types
+
+```cpp
+// Good: Type-safe angles
+PitchAngle pitch = PitchAngle::from_degrees(45.0f);
+YawAngle yaw = YawAngle::from_degrees(90.0f);
+ViewAngles angles{pitch, yaw, RollAngle::from_degrees(0.0f)};
+
+// Bad: Raw floats lose safety
+float pitch = 45.0f; // No range checking
+float yaw = 90.0f; // Can go out of bounds
+```
+
+### Match Engine Types
+
+```cpp
+// Good: Use matching types from same engine
+using namespace omath::source_engine;
+Camera camera = /* ... */;
+ViewAngles angles = /* ... */;
+
+// Bad: Mixing engine types
+using UnityCamera = omath::unity_engine::Camera;
+using SourceAngles = omath::source_engine::ViewAngles;
+UnityCamera camera{pos, SourceAngles{}, ...}; // May cause issues!
+```
+
+### Template Type Parameters
+
+```cpp
+// Good: Explicit and clear
+Vector3 position;
+Vector3 high_precision_pos;
+
+// Okay: Use default float
+Vector3<> position; // Defaults to float
+
+// Avoid: Mixing types unintentionally
+Vector3 a;
+Vector3 b;
+auto result = a + b; // Type mismatch!
+```
+
+---
+
+## Testing & Validation
+
+### Test Edge Cases
+
+```cpp
+void test_projection() {
+ Camera camera = setup_camera();
+
+ // Test normal case
+ assert(camera.world_to_screen({100, 100, 100}).has_value());
+
+ // Test edge cases
+ assert(!camera.world_to_screen({0, 0, -100}).has_value()); // Behind
+ assert(!camera.world_to_screen({1e10, 0, 0}).has_value()); // Too far
+
+ // Test boundaries
+ Vector3 at_near{0, 0, camera.near_plane() + 0.1f};
+ assert(camera.world_to_screen(at_near).has_value());
+}
+```
+
+### Validate Assumptions
+
+```cpp
+void validate_game_data() {
+ // Validate FOV
+ float fov = read_game_fov();
+ assert(fov > 1.0f && fov < 179.0f);
+
+ // Validate positions
+ Vector3 pos = read_player_position();
+ assert(std::isfinite(pos.x));
+ assert(std::isfinite(pos.y));
+ assert(std::isfinite(pos.z));
+
+ // Validate viewport
+ ViewPort vp = read_viewport();
+ assert(vp.width > 0 && vp.height > 0);
+}
+```
+
+### Use Assertions
+
+```cpp
+// Good: Catch errors early in development
+void shoot_projectile(const Projectile& proj) {
+ assert(proj.speed > 0.0f && "Projectile speed must be positive");
+ assert(std::isfinite(proj.origin.length()) && "Invalid projectile origin");
+
+ // Continue with logic
+}
+
+// Add debug-only checks
+#ifndef NDEBUG
+ if (!is_valid_input(data)) {
+ std::cerr << "Warning: Invalid input detected\n";
+ }
+#endif
+```
+
+---
+
+## Memory & Resources
+
+### RAII for Resources
+
+```cpp
+// Good: Automatic cleanup
+class GameOverlay {
+ Camera camera_;
+ std::vector entities_;
+
+public:
+ GameOverlay(/* ... */) : camera_(/* ... */) {
+ entities_.reserve(1000);
+ }
+
+ // Resources cleaned up automatically
+ ~GameOverlay() = default;
+};
+```
+
+### Avoid Unnecessary Copies
+
+```cpp
+// Good: Pass by const reference
+void draw_entities(const std::vector>& positions) {
+ for (const auto& pos : positions) {
+ // Process position
+ }
+}
+
+// Bad: Copies entire vector
+void draw_entities(std::vector> positions) {
+ // Expensive copy!
+}
+
+// Good: Move when transferring ownership
+std::vector> compute_positions();
+auto positions = compute_positions(); // Move, not copy
+```
+
+### Use Structured Bindings
+
+```cpp
+// Good: Clear and concise
+if (auto [success, screen_pos] = try_project(world_pos); success) {
+ draw_at(screen_pos.x, screen_pos.y);
+}
+
+// Good: Decompose results
+auto [x, y, z] = position.as_tuple();
+```
+
+---
+
+## Documentation
+
+### Document Assumptions
+
+```cpp
+// Good: Clear documentation
+/**
+ * Projects world position to screen space.
+ *
+ * @param world_pos Position in world coordinates (meters)
+ * @return Screen position if visible, nullopt if behind camera or out of view
+ *
+ * @note Assumes camera.update() was called this frame
+ * @note Screen coordinates are in viewport space [0, width] x [0, height]
+ */
+std::optional> project(const Vector3& world_pos);
+```
+
+### Explain Non-Obvious Code
+
+```cpp
+// Good: Explain the math
+// Use squared distance to avoid expensive sqrt
+// max_range = 100.0 → max_range_sq = 10000.0
+constexpr float max_range_sq = 100.0f * 100.0f;
+if (dist_sq < max_range_sq) {
+ // Entity is in range
+}
+
+// Explain engine-specific quirks
+// Source Engine uses Z-up coordinates, but angles are in degrees
+// Pitch: [-89, 89], Yaw: [-180, 180], Roll: [-180, 180]
+ViewAngles angles{pitch, yaw, roll};
+```
+
+---
+
+## Debugging
+
+### Add Debug Visualization
+
+```cpp
+#ifndef NDEBUG
+void debug_draw_projection() {
+ // Draw camera frustum
+ draw_frustum(camera);
+
+ // Draw world axes
+ draw_line({0,0,0}, {100,0,0}, Color::Red); // X
+ draw_line({0,0,0}, {0,100,0}, Color::Green); // Y
+ draw_line({0,0,0}, {0,0,100}, Color::Blue); // Z
+
+ // Draw projected points
+ for (const auto& entity : entities) {
+ if (auto screen = camera.world_to_screen(entity.pos)) {
+ draw_cross(screen->x, screen->y);
+ }
+ }
+}
+#endif
+```
+
+### Log Important Values
+
+```cpp
+void debug_projection_failure(const Vector3& pos) {
+ std::cerr << "Projection failed for position: "
+ << pos.x << ", " << pos.y << ", " << pos.z << "\n";
+
+ auto view_matrix = camera.get_view_matrix();
+ std::cerr << "View matrix:\n";
+ // Print matrix...
+
+ std::cerr << "Camera position: "
+ << camera.position().x << ", "
+ << camera.position().y << ", "
+ << camera.position().z << "\n";
+}
+```
+
+### Use Debug Builds
+
+```cmake
+# CMakeLists.txt
+if(CMAKE_BUILD_TYPE STREQUAL "Debug")
+ target_compile_definitions(your_target PRIVATE
+ DEBUG_PROJECTION=1
+ VALIDATE_INPUTS=1
+ )
+endif()
+```
+
+```cpp
+#ifdef DEBUG_PROJECTION
+ std::cout << "Projecting: " << world_pos << "\n";
+#endif
+
+#ifdef VALIDATE_INPUTS
+ assert(std::isfinite(world_pos.length()));
+#endif
+```
+
+---
+
+## Platform Considerations
+
+### Cross-Platform Code
+
+```cpp
+// Good: Platform-agnostic
+constexpr float PI = 3.14159265359f;
+
+// Avoid: Platform-specific
+#ifdef _WIN32
+ // Windows-only code
+#endif
+```
+
+### Handle Different Compilers
+
+```cpp
+// Good: Compiler-agnostic
+#if defined(_MSC_VER)
+ // MSVC-specific
+#elif defined(__GNUC__)
+ // GCC/Clang-specific
+#endif
+
+// Use OMath's built-in compatibility
+// It handles compiler differences automatically
+```
+
+---
+
+## Summary
+
+**Key principles:**
+1. **Safety first**: Always check optional/expected results
+2. **Performance matters**: Use constexpr, avoid allocations, cache results
+3. **Type safety**: Use strong types, match engine types
+4. **Clear code**: Use aliases, document assumptions, explain non-obvious logic
+5. **Test thoroughly**: Validate inputs, test edge cases, add assertions
+6. **Debug effectively**: Add visualization, log values, use debug builds
+
+---
+
+## See Also
+
+- [Troubleshooting Guide](troubleshooting.md)
+- [FAQ](faq.md)
+- [API Overview](api_overview.md)
+- [Tutorials](tutorials.md)
+
+---
+
+*Last updated: 1 Nov 2025*
diff --git a/docs/collision/line_tracer.md b/docs/collision/line_tracer.md
new file mode 100644
index 00000000..9e9e1f8a
--- /dev/null
+++ b/docs/collision/line_tracer.md
@@ -0,0 +1,181 @@
+# `omath::collision::Ray` & `LineTracer` — Ray–Triangle intersection (Möller–Trumbore)
+
+> Headers: your project’s `ray.hpp` (includes `omath/linear_algebra/triangle.hpp`, `omath/linear_algebra/vector3.hpp`)
+> Namespace: `omath::collision`
+> Depends on: `omath::Vector3`, `omath::Triangle>`
+> Algorithm: **Möller–Trumbore** ray–triangle intersection (no allocation)
+
+---
+
+## Overview
+
+These types provide a minimal, fast path to test and compute intersections between a **ray or line segment** and a **single triangle**:
+
+* `Ray` — start/end points plus a flag to treat the ray as **infinite** (half-line) or a **finite segment**.
+* `LineTracer` — static helpers:
+
+ * `can_trace_line(ray, triangle)` → `true` if they intersect.
+ * `get_ray_hit_point(ray, triangle)` → the hit point (precondition: intersection exists).
+
+---
+
+## `Ray`
+
+```cpp
+class Ray {
+public:
+ omath::Vector3 start; // ray origin
+ omath::Vector3 end; // end point (for finite segment) or a point along the direction
+ bool infinite_length = false;
+
+ [[nodiscard]] omath::Vector3 direction_vector() const noexcept;
+ [[nodiscard]] omath::Vector3 direction_vector_normalized() const noexcept;
+};
+```
+
+### Semantics
+
+* **Direction**: `direction_vector() == end - start`.
+ The normalized variant returns a unit vector (or `{0,0,0}` if the direction length is zero).
+* **Extent**:
+
+ * `infinite_length == true` → treat as a **semi-infinite ray** from `start` along `direction`.
+ * `infinite_length == false` → treat as a **closed segment** from `start` to `end`.
+
+> Tip: For an infinite ray that points along some vector `d`, set `end = start + d`.
+
+---
+
+## `LineTracer`
+
+```cpp
+class LineTracer {
+public:
+ LineTracer() = delete;
+
+ [[nodiscard]]
+ static bool can_trace_line(
+ const Ray& ray,
+ const omath::Triangle>& triangle
+ ) noexcept;
+
+ // Möller–Trumbore intersection
+ [[nodiscard]]
+ static omath::Vector3 get_ray_hit_point(
+ const Ray& ray,
+ const omath::Triangle>& triangle
+ ) noexcept;
+};
+```
+
+### Behavior & contract
+
+* **Intersection test**: `can_trace_line` returns `true` iff the ray/segment intersects the triangle (within the ray’s extent).
+* **Hit point**: `get_ray_hit_point` **assumes** there is an intersection.
+ Call **only after** `can_trace_line(...) == true`. Otherwise the result is unspecified.
+* **Triangle winding**: Standard Möller–Trumbore works with either winding; no backface culling is implied here.
+* **Degenerate inputs**: A zero-length ray or degenerate triangle yields **no hit** under typical Möller–Trumbore tolerances.
+
+---
+
+## Quick examples
+
+### 1) Segment vs triangle
+
+```cpp
+using omath::Vector3;
+using omath::Triangle;
+using omath::collision::Ray;
+using omath::collision::LineTracer;
+
+Triangle> tri(
+ Vector3{0, 0, 0},
+ Vector3{1, 0, 0},
+ Vector3{0, 1, 0}
+);
+
+Ray seg;
+seg.start = {0.25f, 0.25f, 1.0f};
+seg.end = {0.25f, 0.25f,-1.0f};
+seg.infinite_length = false; // finite segment
+
+if (LineTracer::can_trace_line(seg, tri)) {
+ Vector3 hit = LineTracer::get_ray_hit_point(seg, tri);
+ // use hit
+}
+```
+
+### 2) Infinite ray
+
+```cpp
+Ray ray;
+ray.start = {0.5f, 0.5f, 1.0f};
+ray.end = ray.start + Vector3{0, 0, -1}; // direction only
+ray.infinite_length = true;
+
+bool hit = LineTracer::can_trace_line(ray, tri);
+```
+
+---
+
+## Notes & edge cases
+
+* **Normalization**: `direction_vector_normalized()` returns `{0,0,0}` for a zero-length direction (safe, but unusable for tracing).
+* **Precision**: The underlying algorithm uses EPS thresholds to reject nearly parallel cases; results near edges can be sensitive to floating-point error. If you need robust edge inclusion/exclusion, document and enforce a policy (e.g., inclusive barycentric range with small epsilon).
+* **Hit location**: The point returned by `get_ray_hit_point` lies **on the triangle plane** and within its area by construction (when `can_trace_line` is `true`).
+
+---
+
+## API summary
+
+```cpp
+namespace omath::collision {
+
+class Ray {
+public:
+ Vector3 start, end;
+ bool infinite_length = false;
+
+ [[nodiscard]] Vector3 direction_vector() const noexcept;
+ [[nodiscard]] Vector3 direction_vector_normalized() const noexcept;
+};
+
+class LineTracer {
+public:
+ LineTracer() = delete;
+
+ [[nodiscard]] static bool can_trace_line(
+ const Ray&,
+ const omath::Triangle>&
+ ) noexcept;
+
+ [[nodiscard]] static Vector3 get_ray_hit_point(
+ const Ray&,
+ const omath::Triangle>&
+ ) noexcept; // precondition: can_trace_line(...) == true
+};
+
+} // namespace omath::collision
+```
+
+---
+
+## Implementation hints (if you extend it)
+
+* Expose a variant that returns **barycentric coordinates** `(u, v, w)` alongside the hit point to support texture lookup or edge tests.
+* Provide an overload returning `std::optional>` (or `expected`) for safer one-shot queries without a separate test call.
+* If you need backface culling, add a flag or dedicated function (reject hits where the signed distance is negative with respect to triangle normal).
+
+---
+
+## See Also
+
+- [Plane Documentation](../3d_primitives/plane.md) - Ray-plane intersection
+- [Box Documentation](../3d_primitives/box.md) - AABB collision detection
+- [Triangle Documentation](../linear_algebra/triangle.md) - Triangle primitives
+- [Tutorials - Collision Detection](../tutorials.md#tutorial-4-collision-detection) - Complete collision tutorial
+- [Getting Started Guide](../getting_started.md) - Quick start with OMath
+
+---
+
+*Last updated: 1 Nov 2025*
diff --git a/docs/engines/frostbite/camera_trait.md b/docs/engines/frostbite/camera_trait.md
new file mode 100644
index 00000000..bb03cd02
--- /dev/null
+++ b/docs/engines/frostbite/camera_trait.md
@@ -0,0 +1,108 @@
+# `omath::frostbite_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/frostbite_engine/traits/camera_trait.hpp` • Impl: `omath/engines/frostbite_engine/traits/camera_trait.cpp`
+> Namespace: `omath::frostbite_engine`
+> Purpose: provide Frostbite-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `-asin(dir.y)` and **yaw** as `atan2(dir.x, dir.z)`; **roll** is `0`. Pitch/yaw are returned using the project’s strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to Frostbite formulas `frostbite_engine::calc_view_matrix`, producing a `Mat4X4` view matrix for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait’s types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the Frostbite engine math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::frostbite_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin.
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far.
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::frostbite_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at**:
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = -asin(dir.y) // +Y is up
+ yaw = atan2(dir.x, dir.z)
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the Frostbite engine helper `frostbite_engine::calc_view_matrix(angles, origin)` to match the engine’s handedness and axis conventions.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix.
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from Frostbite math headers
+using Angs = ViewAngles; // pitch/yaw/roll type
+using FBcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(70.f);
+
+FBcam cam(
+ /*position*/ {0.f, 1.7f, -3.f},
+ /*angles*/ omath::frostbite_engine::CameraTrait::calc_look_at_angle({0,1.7f,-3},{0,1.7f,0}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 0.1f,
+ /*far*/ 1000.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project’s angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+
+---
+
+## See also
+
+* Frostbite math helpers in `omath/engines/frostbite_engine/formulas.hpp` (view/projection builders used above).
+* Generic camera wrapper `omath::projection::Camera` and its `CameraEngineConcept` (this trait is designed to plug straight into it).
diff --git a/docs/engines/frostbite/constants.md b/docs/engines/frostbite/constants.md
new file mode 100644
index 00000000..61d1fbe4
--- /dev/null
+++ b/docs/engines/frostbite/constants.md
@@ -0,0 +1,59 @@
+Nice! A clean little “types + constants” header. A few quick fixes and polish:
+
+## Issues / suggestions
+
+1. **Mat3X3 alias is wrong**
+
+* You wrote `using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;`
+* That should be `Mat<3, 3, ...>`.
+
+2. **`constexpr` globals in a header → make them `inline constexpr`**
+
+* Since this is in a header included by multiple TUs, use `inline constexpr` to avoid ODR/link issues (C++17+).
+
+3. **Consider column-major vs row-major**
+
+* Most game/graphics stacks (GLSL/HLSL, many engines) lean column-major and column vectors. If the rest of your math lib or shaders assume column-major, align these typedefs now to avoid silent transposes later. If row-major is intentional, all good—just be consistent.
+
+4. **Naming consistency**
+
+* If you prefer `k_` prefix, keep it; otherwise consider `kAbsUp`/`ABS_UP` to match your codebase’s conventions.
+
+5. **`Mat1X3` as a “row vector”**
+
+* If you actually use it as a 3-component vector, consider just `Vector3` (clearer) and reserve `Mat1X3` for real row-vector math.
+
+---
+
+## Tidied version
+
+```cpp
+// Created by Vlad on 10/21/2025.
+#pragma once
+
+#include "omath/linear_algebra/mat.hpp"
+#include "omath/linear_algebra/vector3.hpp"
+#include
+#include
+
+namespace omath::frostbite_engine
+{
+ // Inline to avoid ODR across translation units
+ inline constexpr Vector3 k_abs_up = {0.0f, 1.0f, 0.0f};
+ inline constexpr Vector3 k_abs_right = {1.0f, 0.0f, 0.0f};
+ inline constexpr Vector3 k_abs_forward = {0.0f, 0.0f, 1.0f};
+
+ // NOTE: verify row/column major matches the rest of your engine
+ using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+ using Mat3X3 = Mat<3, 3, float, MatStoreType::ROW_MAJOR>;
+ using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
+
+ using PitchAngle = Angle;
+ using YawAngle = Angle;
+ using RollAngle = Angle;
+
+ using ViewAngles = omath::ViewAngles;
+} // namespace omath::frostbite_engine
+```
+
+If you share how your matrices multiply vectors (row vs column) and your world handedness, I can double-check the axis constants and angle normalization to make sure yaw/pitch signs line up with your camera and `atan2` usage.
diff --git a/docs/engines/frostbite/formulas.md b/docs/engines/frostbite/formulas.md
new file mode 100644
index 00000000..e69de29b
diff --git a/docs/engines/frostbite/pred_engine_trait.md b/docs/engines/frostbite/pred_engine_trait.md
new file mode 100644
index 00000000..a5b77518
--- /dev/null
+++ b/docs/engines/frostbite/pred_engine_trait.md
@@ -0,0 +1,105 @@
+// Created by Vlad on 8/6/2025.
+#pragma once
+
+#include // sqrt, hypot, tan, asin, atan2
+#include
+
+#include "omath/engines/frostbite_engine/formulas.hpp"
+#include "omath/projectile_prediction/projectile.hpp"
+#include "omath/projectile_prediction/target.hpp"
+
+namespace omath::frostbite_engine
+{
+class PredEngineTrait final
+{
+public:
+// Predict projectile position given launch angles (degrees), time (s), and world gravity (m/s^2).
+// Note: kept runtime function; remove constexpr to avoid CTAD surprises across toolchains.
+static Vector3 predict_projectile_position(
+const projectile_prediction::Projectile& projectile,
+float pitch_deg, float yaw_deg,
+float time, float gravity) noexcept
+{
+// Engine convention: negative pitch looks up (your original used -pitch).
+const auto fwd = forward_vector({
+PitchAngle::from_degrees(-pitch_deg),
+YawAngle::from_degrees(yaw_deg),
+RollAngle::from_degrees(0.0f)
+});
+
+ Vector3 pos =
+ projectile.m_origin +
+ fwd * (projectile.m_launch_speed * time);
+
+ // s = 1/2 a t^2 downward
+ pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
+ return pos;
+ }
+
+ [[nodiscard]]
+ static Vector3 predict_target_position(
+ const projectile_prediction::Target& target,
+ float time, float gravity) noexcept
+ {
+ Vector3 predicted = target.m_origin + target.m_velocity * time;
+
+ if (target.m_is_airborne) {
+ // If targets also have a gravity scale in your model, multiply here.
+ predicted.y -= gravity * (time * time) * 0.5f;
+ }
+ return predicted;
+ }
+
+ [[nodiscard]]
+ static float calc_vector_2d_distance(const Vector3& delta) noexcept
+ {
+ // More stable than sqrt(x*x + z*z)
+ return std::hypot(delta.x, delta.z);
+ }
+
+ [[nodiscard]]
+ static float get_vector_height_coordinate(const Vector3& vec) noexcept
+ {
+ return vec.y;
+ }
+
+ // Computes a viewpoint above the predicted target, using an optional projectile pitch.
+ // If pitch is absent, we leave Y unchanged (or you can choose a sensible default).
+ [[nodiscard]]
+ static Vector3 calc_viewpoint_from_angles(
+ const projectile_prediction::Projectile& projectile,
+ const Vector3& predicted_target_position,
+ const std::optional projectile_pitch_deg) noexcept
+ {
+ // Lateral separation from projectile to target (X/Z plane).
+ const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
+
+ float y = predicted_target_position.y;
+ if (projectile_pitch_deg.has_value()) {
+ const float pitch_rad = angles::degrees_to_radians(*projectile_pitch_deg);
+ const float height = delta2d * std::tan(pitch_rad);
+ y += height;
+ }
+
+ // Use the target's Z, not the projectile's Z (likely bugfix).
+ return { predicted_target_position.x, y, predicted_target_position.z };
+ }
+
+ // Due to maybe_calculate_projectile_launch_pitch_angle spec: +89° up, -89° down.
+ [[nodiscard]]
+ static float calc_direct_pitch_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept
+ {
+ const auto direction = (view_to - origin).normalized();
+ return angles::radians_to_degrees(std::asin(direction.y));
+ }
+
+ [[nodiscard]]
+ static float calc_direct_yaw_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept
+ {
+ const auto direction = (view_to - origin).normalized();
+ return angles::radians_to_degrees(std::atan2(direction.x, direction.z));
+ }
+ };
+} // namespace omath::frostbite_engine
diff --git a/docs/engines/iw_engine/camera_trait.md b/docs/engines/iw_engine/camera_trait.md
new file mode 100644
index 00000000..d693abd4
--- /dev/null
+++ b/docs/engines/iw_engine/camera_trait.md
@@ -0,0 +1,109 @@
+# `omath::iw_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/iw_engine/traits/camera_trait.hpp` • Impl: `omath/engines/iw_engine/traits/camera_trait.cpp`
+> Namespace: `omath::iw_engine`
+> Purpose: provide IW Engine (Call of Duty)-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `asin(dir.z)` and **yaw** as `atan2(dir.y, dir.x)`; **roll** is `0`. Pitch/yaw are returned using the project's strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to IW Engine formulas `iw_engine::calc_view_matrix`, producing a `Mat4X4` view matrix for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait's types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the IW Engine math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::iw_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin.
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far.
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::iw_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at** (Z-up coordinate system):
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = asin(dir.z) // +Z is up
+ yaw = atan2(dir.y, dir.x) // horizontal rotation
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the IW Engine helper `iw_engine::calc_view_matrix(angles, origin)` to match the engine's handedness and axis conventions.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix.
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from IW Engine math headers
+using Angs = ViewAngles; // pitch/yaw/roll type
+using IWcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(65.f);
+
+IWcam cam(
+ /*position*/ {500.f, 200.f, 100.f},
+ /*angles*/ omath::iw_engine::CameraTrait::calc_look_at_angle({500,200,100},{0,0,100}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 0.1f,
+ /*far*/ 5000.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project's angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+* IW Engine uses **Z-up**: pitch angles control vertical look, positive = up.
+
+---
+
+## See also
+
+* IW Engine math helpers in `omath/engines/iw_engine/formulas.hpp` (view/projection builders used above).
+* Generic camera wrapper `omath::projection::Camera` and its `CameraEngineConcept` (this trait is designed to plug straight into it).
diff --git a/docs/engines/iw_engine/constants.md b/docs/engines/iw_engine/constants.md
new file mode 100644
index 00000000..cb9d9d98
--- /dev/null
+++ b/docs/engines/iw_engine/constants.md
@@ -0,0 +1,77 @@
+# `omath::iw_engine` — types & constants
+
+> Header: `omath/engines/iw_engine/constants.hpp`
+> Namespace: `omath::iw_engine`
+> Purpose: define IW Engine (Call of Duty) coordinate system, matrix types, and angle ranges
+
+---
+
+## Summary
+
+The **IW Engine** (Infinity Ward Engine, used in Call of Duty series) uses a **Z-up, right-handed** coordinate system:
+
+* **Up** = `{0, 0, 1}` (Z-axis)
+* **Right** = `{0, -1, 0}` (negative Y-axis)
+* **Forward** = `{1, 0, 0}` (X-axis)
+
+Matrices are **row-major**. Angles are **clamped pitch** (±89°) and **normalized yaw/roll** (±180°).
+
+---
+
+## Constants
+
+```cpp
+namespace omath::iw_engine {
+ constexpr Vector3 k_abs_up = {0, 0, 1};
+ constexpr Vector3 k_abs_right = {0, -1, 0};
+ constexpr Vector3 k_abs_forward = {1, 0, 0};
+}
+```
+
+These basis vectors define the engine's **world coordinate frame**.
+
+---
+
+## Matrix types
+
+```cpp
+using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
+```
+
+**Row-major** storage means rows are contiguous in memory. Suitable for CPU-side transforms and typical C++ math libraries.
+
+---
+
+## Angle types
+
+```cpp
+using PitchAngle = Angle;
+using YawAngle = Angle;
+using RollAngle = Angle;
+
+using ViewAngles = omath::ViewAngles;
+```
+
+* **PitchAngle**: clamped to **[-89°, +89°]** (looking down vs. up)
+* **YawAngle**: normalized to **[-180°, +180°]** (horizontal rotation)
+* **RollAngle**: normalized to **[-180°, +180°]** (camera roll)
+
+`ViewAngles` bundles all three into a single type for camera/view transforms.
+
+---
+
+## Coordinate system notes
+
+* **Z-up**: gravity points along `-Z`, height increases with `+Z`
+* **Right-handed**: cross product `forward × right = up` holds
+* This matches **IW Engine** (Call of Duty series: Modern Warfare, Black Ops, etc.) conventions
+
+---
+
+## See also
+
+* `omath/engines/iw_engine/formulas.hpp` — view/projection matrix builders
+* `omath/trigonometry/angle.hpp` — angle normalization & clamping helpers
+* `omath/trigonometry/view_angles.hpp` — generic pitch/yaw/roll wrapper
diff --git a/docs/engines/iw_engine/formulas.md b/docs/engines/iw_engine/formulas.md
new file mode 100644
index 00000000..283b4baa
--- /dev/null
+++ b/docs/engines/iw_engine/formulas.md
@@ -0,0 +1,135 @@
+# `omath::iw_engine` — formulas & matrix helpers
+
+> Header: `omath/engines/iw_engine/formulas.hpp`
+> Namespace: `omath::iw_engine`
+> Purpose: compute direction vectors, rotation matrices, view matrices, and perspective projections for IW Engine (Call of Duty)
+
+---
+
+## Summary
+
+This header provides **IW Engine**-specific math for:
+
+* **Direction vectors** (`forward`, `right`, `up`) from `ViewAngles`
+* **Rotation matrices** from Euler angles
+* **View matrices** (camera transforms)
+* **Perspective projection** matrices
+
+All functions respect IW Engine's **Z-up, right-handed** coordinate system.
+
+---
+
+## API
+
+```cpp
+namespace omath::iw_engine {
+
+ // Compute forward direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 forward_vector(const ViewAngles& angles) noexcept;
+
+ // Compute right direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 right_vector(const ViewAngles& angles) noexcept;
+
+ // Compute up direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 up_vector(const ViewAngles& angles) noexcept;
+
+ // Build 3x3 rotation matrix from angles
+ [[nodiscard]]
+ Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
+
+ // Build view matrix (camera space transform)
+ [[nodiscard]]
+ Mat4X4 calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection matrix
+ [[nodiscard]]
+ Mat4X4 calc_perspective_projection_matrix(float field_of_view,
+ float aspect_ratio,
+ float near, float far) noexcept;
+
+} // namespace omath::iw_engine
+```
+
+---
+
+## Direction vectors
+
+Given camera angles (pitch/yaw/roll):
+
+* `forward_vector(angles)` → unit vector pointing where the camera looks
+* `right_vector(angles)` → unit vector pointing to the camera's right
+* `up_vector(angles)` → unit vector pointing upward relative to the camera
+
+These are used for movement, aim direction, and building coordinate frames.
+
+---
+
+## Rotation & view matrices
+
+* `rotation_matrix(angles)` → 3×3 (or 4×4) rotation matrix from Euler angles
+* `calc_view_matrix(angles, origin)` → camera view matrix
+
+The view matrix transforms world coordinates into camera space (origin at camera, axes aligned with camera orientation).
+
+---
+
+## Perspective projection
+
+```cpp
+Mat4X4 proj = calc_perspective_projection_matrix(
+ fov_degrees, // vertical field of view (e.g., 65)
+ aspect_ratio, // width / height (e.g., 16/9)
+ near_plane, // e.g., 0.1
+ far_plane // e.g., 5000.0
+);
+```
+
+Produces a **perspective projection matrix** suitable for 3D rendering pipelines. Combined with the view matrix, this implements the standard camera transform chain.
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::iw_engine;
+
+// Camera setup
+ViewAngles angles = {
+ PitchAngle::from_degrees(-10.0f),
+ YawAngle::from_degrees(90.0f),
+ RollAngle::from_degrees(0.0f)
+};
+Vector3 cam_pos{500.0f, 200.0f, 100.0f};
+
+// Compute direction
+auto forward = forward_vector(angles);
+auto right = right_vector(angles);
+auto up = up_vector(angles);
+
+// Build matrices
+auto view_mat = calc_view_matrix(angles, cam_pos);
+auto proj_mat = calc_perspective_projection_matrix(65.0f, 16.0f/9.0f, 0.1f, 5000.0f);
+
+// Use view_mat and proj_mat for rendering...
+```
+
+---
+
+## Conventions
+
+* **Angles**: pitch (up/down), yaw (left/right), roll (tilt)
+* **Pitch**: positive = looking up, negative = looking down
+* **Yaw**: increases counter-clockwise from the +X axis
+* **Coordinate system**: Z-up, X-forward, Y-right (negative in code convention)
+
+---
+
+## See also
+
+* `omath/engines/iw_engine/constants.hpp` — coordinate frame & angle types
+* `omath/engines/iw_engine/traits/camera_trait.hpp` — plug-in for generic `Camera`
+* `omath/projection/camera.hpp` — generic camera wrapper using these formulas
diff --git a/docs/engines/iw_engine/pred_engine_trait.md b/docs/engines/iw_engine/pred_engine_trait.md
new file mode 100644
index 00000000..85af5c13
--- /dev/null
+++ b/docs/engines/iw_engine/pred_engine_trait.md
@@ -0,0 +1,198 @@
+# `omath::iw_engine::PredEngineTrait` — projectile prediction trait
+
+> Header: `omath/engines/iw_engine/traits/pred_engine_trait.hpp`
+> Namespace: `omath::iw_engine`
+> Purpose: provide IW Engine (Call of Duty)-specific projectile and target prediction for ballistic calculations
+
+---
+
+## Summary
+
+`PredEngineTrait` implements engine-specific helpers for **projectile prediction**:
+
+* `predict_projectile_position` – computes where a projectile will be after `time` seconds
+* `predict_target_position` – computes where a moving target will be after `time` seconds
+* `calc_vector_2d_distance` – horizontal distance (X/Y plane, ignoring Z)
+* `get_vector_height_coordinate` – extracts vertical coordinate (Z in IW Engine)
+* `calc_viewpoint_from_angles` – computes aim point given pitch angle
+* `calc_direct_pitch_angle` – pitch angle to look from origin to target
+* `calc_direct_yaw_angle` – yaw angle to look from origin to target
+
+These methods satisfy the `PredEngineTraitConcept` required by generic projectile prediction algorithms.
+
+---
+
+## API
+
+```cpp
+namespace omath::iw_engine {
+
+class PredEngineTrait final {
+public:
+ // Predict projectile position after `time` seconds
+ static constexpr Vector3
+ predict_projectile_position(const projectile_prediction::Projectile& projectile,
+ float pitch, float yaw, float time,
+ float gravity) noexcept;
+
+ // Predict target position after `time` seconds
+ static constexpr Vector3
+ predict_target_position(const projectile_prediction::Target& target,
+ float time, float gravity) noexcept;
+
+ // Compute horizontal (2D) distance
+ static float
+ calc_vector_2d_distance(const Vector3& delta) noexcept;
+
+ // Get vertical coordinate (Z in IW Engine)
+ static constexpr float
+ get_vector_height_coordinate(const Vector3& vec) noexcept;
+
+ // Compute aim point from angles
+ static Vector3
+ calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
+ Vector3 predicted_target_position,
+ std::optional projectile_pitch) noexcept;
+
+ // Compute pitch angle to look at target
+ static float
+ calc_direct_pitch_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+
+ // Compute yaw angle to look at target
+ static float
+ calc_direct_yaw_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+};
+
+} // namespace omath::iw_engine
+```
+
+---
+
+## Projectile prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_projectile_position(
+ projectile, // initial position, speed, gravity scale
+ pitch_deg, // launch pitch (positive = up)
+ yaw_deg, // launch yaw
+ time, // time in seconds
+ gravity // gravity constant (e.g., 800 units/s²)
+);
+```
+
+Computes:
+
+1. Forward vector from pitch/yaw (using `forward_vector`)
+2. Initial velocity: `forward * launch_speed`
+3. Position after `time`: `origin + velocity*time - 0.5*gravity*gravityScale*time²` (Z component only)
+
+**Note**: Negative pitch in `forward_vector` convention → positive pitch looks up.
+
+---
+
+## Target prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_target_position(
+ target, // position, velocity, airborne flag
+ time, // time in seconds
+ gravity // gravity constant
+);
+```
+
+Simple linear extrapolation plus gravity if target is airborne:
+
+```
+predicted = origin + velocity * time
+if (airborne)
+ predicted.z -= 0.5 * gravity * time²
+```
+
+---
+
+## Distance & height helpers
+
+* `calc_vector_2d_distance(delta)` → `sqrt(delta.x² + delta.y²)` (horizontal distance)
+* `get_vector_height_coordinate(vec)` → `vec.z` (vertical coordinate in IW Engine)
+
+Used to compute ballistic arc parameters.
+
+---
+
+## Aim angle calculation
+
+* `calc_direct_pitch_angle(origin, target)` → pitch in degrees to look from `origin` to `target`
+ - Formula: `asin(Δz / distance)` converted to degrees
+ - Positive = looking up, negative = looking down
+
+* `calc_direct_yaw_angle(origin, target)` → yaw in degrees to look from `origin` to `target`
+ - Formula: `atan2(Δy, Δx)` converted to degrees
+ - Horizontal rotation around Z-axis
+
+---
+
+## Viewpoint from angles
+
+```cpp
+auto aim_point = PredEngineTrait::calc_viewpoint_from_angles(
+ projectile,
+ predicted_target_pos,
+ optional_pitch_deg
+);
+```
+
+Computes where to aim in 3D space given a desired pitch angle. Uses horizontal distance and `tan(pitch)` to compute height offset.
+
+---
+
+## Conventions
+
+* **Coordinate system**: Z-up (height increases with Z)
+* **Angles**: pitch in [-89°, +89°], yaw in [-180°, +180°]
+* **Gravity**: applied downward along -Z axis
+* **Pitch convention**: +89° = straight up, -89° = straight down
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::iw_engine;
+using namespace omath::projectile_prediction;
+
+Projectile proj{
+ .m_origin = {0, 0, 100},
+ .m_launch_speed = 1200.0f,
+ .m_gravity_scale = 1.0f
+};
+
+Target tgt{
+ .m_origin = {800, 300, 100},
+ .m_velocity = {15, 8, 0},
+ .m_is_airborne = false
+};
+
+float gravity = 800.0f;
+float time = 0.5f;
+
+// Predict where target will be
+auto target_pos = PredEngineTrait::predict_target_position(tgt, time, gravity);
+
+// Compute aim angles
+float pitch = PredEngineTrait::calc_direct_pitch_angle(proj.m_origin, target_pos);
+float yaw = PredEngineTrait::calc_direct_yaw_angle(proj.m_origin, target_pos);
+
+// Predict projectile position with those angles
+auto proj_pos = PredEngineTrait::predict_projectile_position(proj, pitch, yaw, time, gravity);
+```
+
+---
+
+## See also
+
+* `omath/engines/iw_engine/formulas.hpp` — direction vectors and matrix builders
+* `omath/projectile_prediction/projectile.hpp` — `Projectile` struct
+* `omath/projectile_prediction/target.hpp` — `Target` struct
+* Generic projectile prediction algorithms that use `PredEngineTraitConcept`
diff --git a/docs/engines/opengl_engine/camera_trait.md b/docs/engines/opengl_engine/camera_trait.md
new file mode 100644
index 00000000..a7e288a8
--- /dev/null
+++ b/docs/engines/opengl_engine/camera_trait.md
@@ -0,0 +1,110 @@
+# `omath::opengl_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/opengl_engine/traits/camera_trait.hpp` • Impl: `omath/engines/opengl_engine/traits/camera_trait.cpp`
+> Namespace: `omath::opengl_engine`
+> Purpose: provide OpenGL-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `asin(dir.y)` and **yaw** as `-atan2(dir.x, -dir.z)`; **roll** is `0`. Pitch/yaw are returned using the project's strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to OpenGL formulas `opengl_engine::calc_view_matrix`, producing a `Mat4X4` view matrix (column-major) for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait's types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the OpenGL math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::opengl_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin (column-major).
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far (column-major).
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::opengl_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at** (Y-up, -Z forward coordinate system):
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = asin(dir.y) // +Y is up
+ yaw = -atan2(dir.x, -dir.z) // horizontal rotation
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the OpenGL helper `opengl_engine::calc_view_matrix(angles, origin)` to match OpenGL's right-handed, Y-up, -Z forward conventions. Matrix is **column-major**.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix (column-major).
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from OpenGL math headers (column-major)
+using Angs = ViewAngles; // pitch/yaw/roll type
+using GLcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(45.f);
+
+GLcam cam(
+ /*position*/ {5.f, 3.f, 5.f},
+ /*angles*/ omath::opengl_engine::CameraTrait::calc_look_at_angle({5,3,5},{0,0,0}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 0.1f,
+ /*far*/ 100.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project's angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+* OpenGL uses **Y-up, -Z forward**: pitch angles control vertical look (positive = up), yaw controls horizontal rotation.
+* Matrices are **column-major** (no transpose needed for OpenGL shaders).
+
+---
+
+## See also
+
+* OpenGL math helpers in `omath/engines/opengl_engine/formulas.hpp` (view/projection builders used above).
+* Generic camera wrapper `omath::projection::Camera` and its `CameraEngineConcept` (this trait is designed to plug straight into it).
diff --git a/docs/engines/opengl_engine/constants.md b/docs/engines/opengl_engine/constants.md
new file mode 100644
index 00000000..8bcf9ce8
--- /dev/null
+++ b/docs/engines/opengl_engine/constants.md
@@ -0,0 +1,78 @@
+# `omath::opengl_engine` — types & constants
+
+> Header: `omath/engines/opengl_engine/constants.hpp`
+> Namespace: `omath::opengl_engine`
+> Purpose: define OpenGL coordinate system, matrix types, and angle ranges
+
+---
+
+## Summary
+
+The **OpenGL Engine** uses a **Y-up, right-handed** coordinate system:
+
+* **Up** = `{0, 1, 0}` (Y-axis)
+* **Right** = `{1, 0, 0}` (X-axis)
+* **Forward** = `{0, 0, -1}` (negative Z-axis)
+
+Matrices are **column-major**. Angles are **clamped pitch** (±90°) and **normalized yaw/roll** (±180°).
+
+---
+
+## Constants
+
+```cpp
+namespace omath::opengl_engine {
+ constexpr Vector3 k_abs_up = {0, 1, 0};
+ constexpr Vector3 k_abs_right = {1, 0, 0};
+ constexpr Vector3 k_abs_forward = {0, 0, -1};
+}
+```
+
+These basis vectors define the engine's **world coordinate frame**.
+
+---
+
+## Matrix types
+
+```cpp
+using Mat4X4 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>;
+using Mat3X3 = Mat<4, 4, float, MatStoreType::COLUMN_MAJOR>;
+using Mat1X3 = Mat<1, 3, float, MatStoreType::COLUMN_MAJOR>;
+```
+
+**Column-major** storage means columns are contiguous in memory. This matches OpenGL's native matrix layout and shader expectations (GLSL).
+
+---
+
+## Angle types
+
+```cpp
+using PitchAngle = Angle;
+using YawAngle = Angle;
+using RollAngle = Angle;
+
+using ViewAngles = omath::ViewAngles;
+```
+
+* **PitchAngle**: clamped to **[-90°, +90°]** (looking down vs. up)
+* **YawAngle**: normalized to **[-180°, +180°]** (horizontal rotation)
+* **RollAngle**: normalized to **[-180°, +180°]** (camera roll)
+
+`ViewAngles` bundles all three into a single type for camera/view transforms.
+
+---
+
+## Coordinate system notes
+
+* **Y-up**: gravity points along `-Y`, height increases with `+Y`
+* **Right-handed**: cross product `right × up = forward` (forward is `-Z`)
+* **Forward = -Z**: the camera looks down the negative Z-axis (OpenGL convention)
+* This matches **OpenGL** conventions for 3D graphics pipelines
+
+---
+
+## See also
+
+* `omath/engines/opengl_engine/formulas.hpp` — view/projection matrix builders
+* `omath/trigonometry/angle.hpp` — angle normalization & clamping helpers
+* `omath/trigonometry/view_angles.hpp` — generic pitch/yaw/roll wrapper
diff --git a/docs/engines/opengl_engine/formulas.md b/docs/engines/opengl_engine/formulas.md
new file mode 100644
index 00000000..c2515d55
--- /dev/null
+++ b/docs/engines/opengl_engine/formulas.md
@@ -0,0 +1,140 @@
+# `omath::opengl_engine` — formulas & matrix helpers
+
+> Header: `omath/engines/opengl_engine/formulas.hpp`
+> Namespace: `omath::opengl_engine`
+> Purpose: compute direction vectors, rotation matrices, view matrices, and perspective projections for OpenGL
+
+---
+
+## Summary
+
+This header provides **OpenGL**-specific math for:
+
+* **Direction vectors** (`forward`, `right`, `up`) from `ViewAngles`
+* **Rotation matrices** from Euler angles
+* **View matrices** (camera transforms)
+* **Perspective projection** matrices
+
+All functions respect OpenGL's **Y-up, right-handed** coordinate system with **forward = -Z**.
+
+---
+
+## API
+
+```cpp
+namespace omath::opengl_engine {
+
+ // Compute forward direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 forward_vector(const ViewAngles& angles) noexcept;
+
+ // Compute right direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 right_vector(const ViewAngles& angles) noexcept;
+
+ // Compute up direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 up_vector(const ViewAngles& angles) noexcept;
+
+ // Build 3x3 rotation matrix from angles
+ [[nodiscard]]
+ Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
+
+ // Build view matrix (camera space transform)
+ [[nodiscard]]
+ Mat4X4 calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection matrix
+ [[nodiscard]]
+ Mat4X4 calc_perspective_projection_matrix(float field_of_view,
+ float aspect_ratio,
+ float near, float far) noexcept;
+
+} // namespace omath::opengl_engine
+```
+
+---
+
+## Direction vectors
+
+Given camera angles (pitch/yaw/roll):
+
+* `forward_vector(angles)` → unit vector pointing where the camera looks (typically `-Z` direction)
+* `right_vector(angles)` → unit vector pointing to the camera's right (`+X` direction)
+* `up_vector(angles)` → unit vector pointing upward relative to the camera (`+Y` direction)
+
+These are used for movement, aim direction, and building coordinate frames.
+
+---
+
+## Rotation & view matrices
+
+* `rotation_matrix(angles)` → 3×3 (or 4×4) rotation matrix from Euler angles (column-major)
+* `calc_view_matrix(angles, origin)` → camera view matrix (column-major)
+
+The view matrix transforms world coordinates into camera space (origin at camera, axes aligned with camera orientation).
+
+**Note**: Matrices are **column-major** to match OpenGL/GLSL conventions. No transpose needed when uploading to shaders.
+
+---
+
+## Perspective projection
+
+```cpp
+Mat4X4 proj = calc_perspective_projection_matrix(
+ fov_degrees, // vertical field of view (e.g., 45)
+ aspect_ratio, // width / height (e.g., 16/9)
+ near_plane, // e.g., 0.1
+ far_plane // e.g., 100.0
+);
+```
+
+Produces a **perspective projection matrix** suitable for OpenGL rendering. Combined with the view matrix, this implements the standard camera transform chain.
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::opengl_engine;
+
+// Camera setup
+ViewAngles angles = {
+ PitchAngle::from_degrees(-20.0f),
+ YawAngle::from_degrees(135.0f),
+ RollAngle::from_degrees(0.0f)
+};
+Vector3 cam_pos{5.0f, 3.0f, 5.0f};
+
+// Compute direction
+auto forward = forward_vector(angles);
+auto right = right_vector(angles);
+auto up = up_vector(angles);
+
+// Build matrices (column-major for OpenGL)
+auto view_mat = calc_view_matrix(angles, cam_pos);
+auto proj_mat = calc_perspective_projection_matrix(45.0f, 16.0f/9.0f, 0.1f, 100.0f);
+
+// Upload to OpenGL shaders (no transpose needed)
+glUniformMatrix4fv(view_loc, 1, GL_FALSE, view_mat.data());
+glUniformMatrix4fv(proj_loc, 1, GL_FALSE, proj_mat.data());
+```
+
+---
+
+## Conventions
+
+* **Angles**: pitch (up/down), yaw (left/right), roll (tilt)
+* **Pitch**: positive = looking up, negative = looking down
+* **Yaw**: increases counter-clockwise from the -Z axis
+* **Coordinate system**: Y-up, -Z-forward, X-right (right-handed)
+* **Matrix storage**: column-major (matches OpenGL/GLSL)
+
+---
+
+## See also
+
+* `omath/engines/opengl_engine/constants.hpp` — coordinate frame & angle types
+* `omath/engines/opengl_engine/traits/camera_trait.hpp` — plug-in for generic `Camera`
+* `omath/projection/camera.hpp` — generic camera wrapper using these formulas
diff --git a/docs/engines/opengl_engine/pred_engine_trait.md b/docs/engines/opengl_engine/pred_engine_trait.md
new file mode 100644
index 00000000..84cccc38
--- /dev/null
+++ b/docs/engines/opengl_engine/pred_engine_trait.md
@@ -0,0 +1,199 @@
+# `omath::opengl_engine::PredEngineTrait` — projectile prediction trait
+
+> Header: `omath/engines/opengl_engine/traits/pred_engine_trait.hpp`
+> Namespace: `omath::opengl_engine`
+> Purpose: provide OpenGL-specific projectile and target prediction for ballistic calculations
+
+---
+
+## Summary
+
+`PredEngineTrait` implements engine-specific helpers for **projectile prediction**:
+
+* `predict_projectile_position` – computes where a projectile will be after `time` seconds
+* `predict_target_position` – computes where a moving target will be after `time` seconds
+* `calc_vector_2d_distance` – horizontal distance (X/Z plane, ignoring Y)
+* `get_vector_height_coordinate` – extracts vertical coordinate (Y in OpenGL)
+* `calc_viewpoint_from_angles` – computes aim point given pitch angle
+* `calc_direct_pitch_angle` – pitch angle to look from origin to target
+* `calc_direct_yaw_angle` – yaw angle to look from origin to target
+
+These methods satisfy the `PredEngineTraitConcept` required by generic projectile prediction algorithms.
+
+---
+
+## API
+
+```cpp
+namespace omath::opengl_engine {
+
+class PredEngineTrait final {
+public:
+ // Predict projectile position after `time` seconds
+ static constexpr Vector3
+ predict_projectile_position(const projectile_prediction::Projectile& projectile,
+ float pitch, float yaw, float time,
+ float gravity) noexcept;
+
+ // Predict target position after `time` seconds
+ static constexpr Vector3
+ predict_target_position(const projectile_prediction::Target& target,
+ float time, float gravity) noexcept;
+
+ // Compute horizontal (2D) distance
+ static float
+ calc_vector_2d_distance(const Vector3& delta) noexcept;
+
+ // Get vertical coordinate (Y in OpenGL)
+ static constexpr float
+ get_vector_height_coordinate(const Vector3& vec) noexcept;
+
+ // Compute aim point from angles
+ static Vector3
+ calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
+ Vector3 predicted_target_position,
+ std::optional projectile_pitch) noexcept;
+
+ // Compute pitch angle to look at target
+ static float
+ calc_direct_pitch_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+
+ // Compute yaw angle to look at target
+ static float
+ calc_direct_yaw_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+};
+
+} // namespace omath::opengl_engine
+```
+
+---
+
+## Projectile prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_projectile_position(
+ projectile, // initial position, speed, gravity scale
+ pitch_deg, // launch pitch (positive = up)
+ yaw_deg, // launch yaw
+ time, // time in seconds
+ gravity // gravity constant (e.g., 9.81 m/s²)
+);
+```
+
+Computes:
+
+1. Forward vector from pitch/yaw (using `forward_vector`)
+2. Initial velocity: `forward * launch_speed`
+3. Position after `time`: `origin + velocity*time - 0.5*gravity*gravityScale*time²` (Y component only)
+
+**Note**: Negative pitch in `forward_vector` convention → positive pitch looks up.
+
+---
+
+## Target prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_target_position(
+ target, // position, velocity, airborne flag
+ time, // time in seconds
+ gravity // gravity constant
+);
+```
+
+Simple linear extrapolation plus gravity if target is airborne:
+
+```
+predicted = origin + velocity * time
+if (airborne)
+ predicted.y -= 0.5 * gravity * time²
+```
+
+---
+
+## Distance & height helpers
+
+* `calc_vector_2d_distance(delta)` → `sqrt(delta.x² + delta.z²)` (horizontal distance)
+* `get_vector_height_coordinate(vec)` → `vec.y` (vertical coordinate in OpenGL)
+
+Used to compute ballistic arc parameters.
+
+---
+
+## Aim angle calculation
+
+* `calc_direct_pitch_angle(origin, target)` → pitch in degrees to look from `origin` to `target`
+ - Formula: `asin(Δy / distance)` converted to degrees (direction normalized first)
+ - Positive = looking up, negative = looking down
+
+* `calc_direct_yaw_angle(origin, target)` → yaw in degrees to look from `origin` to `target`
+ - Formula: `-atan2(Δx, -Δz)` converted to degrees (direction normalized first)
+ - Horizontal rotation around Y-axis (accounts for -Z forward convention)
+
+---
+
+## Viewpoint from angles
+
+```cpp
+auto aim_point = PredEngineTrait::calc_viewpoint_from_angles(
+ projectile,
+ predicted_target_pos,
+ optional_pitch_deg
+);
+```
+
+Computes where to aim in 3D space given a desired pitch angle. Uses horizontal distance and `tan(pitch)` to compute height offset. Result has adjusted Y coordinate.
+
+---
+
+## Conventions
+
+* **Coordinate system**: Y-up, -Z forward (height increases with Y)
+* **Angles**: pitch in [-90°, +90°], yaw in [-180°, +180°]
+* **Gravity**: applied downward along -Y axis
+* **Pitch convention**: +90° = straight up, -90° = straight down
+* **Forward direction**: negative Z-axis
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::opengl_engine;
+using namespace omath::projectile_prediction;
+
+Projectile proj{
+ .m_origin = {0, 2, 0},
+ .m_launch_speed = 30.0f,
+ .m_gravity_scale = 1.0f
+};
+
+Target tgt{
+ .m_origin = {10, 2, -15},
+ .m_velocity = {0.5f, 0, -1.0f},
+ .m_is_airborne = false
+};
+
+float gravity = 9.81f;
+float time = 0.5f;
+
+// Predict where target will be
+auto target_pos = PredEngineTrait::predict_target_position(tgt, time, gravity);
+
+// Compute aim angles
+float pitch = PredEngineTrait::calc_direct_pitch_angle(proj.m_origin, target_pos);
+float yaw = PredEngineTrait::calc_direct_yaw_angle(proj.m_origin, target_pos);
+
+// Predict projectile position with those angles
+auto proj_pos = PredEngineTrait::predict_projectile_position(proj, pitch, yaw, time, gravity);
+```
+
+---
+
+## See also
+
+* `omath/engines/opengl_engine/formulas.hpp` — direction vectors and matrix builders
+* `omath/projectile_prediction/projectile.hpp` — `Projectile` struct
+* `omath/projectile_prediction/target.hpp` — `Target` struct
+* Generic projectile prediction algorithms that use `PredEngineTraitConcept`
diff --git a/docs/engines/source_engine/camera_trait.md b/docs/engines/source_engine/camera_trait.md
new file mode 100644
index 00000000..610fc610
--- /dev/null
+++ b/docs/engines/source_engine/camera_trait.md
@@ -0,0 +1,113 @@
+# `omath::source_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/source_engine/traits/camera_trait.hpp` • Impl: `omath/engines/source_engine/traits/camera_trait.cpp`
+> Namespace: `omath::source_engine`
+> Purpose: provide Source Engine-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `asin(dir.z)` and **yaw** as `atan2(dir.y, dir.x)`; **roll** is `0`. Pitch/yaw are returned using the project's strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to Source Engine formulas `source_engine::calc_view_matrix`, producing a `Mat4X4` view matrix for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait's types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the Source Engine math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::source_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin.
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far.
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::source_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at** (Z-up coordinate system):
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = asin(dir.z) // +Z is up
+ yaw = atan2(dir.y, dir.x) // horizontal rotation
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the Source Engine helper `source_engine::calc_view_matrix(angles, origin)` to match the engine's handedness and axis conventions.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix.
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from Source Engine math headers
+using Angs = ViewAngles; // pitch/yaw/roll type
+using SEcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(90.f);
+
+SEcam cam(
+ /*position*/ {100.f, 50.f, 80.f},
+ /*angles*/ omath::source_engine::CameraTrait::calc_look_at_angle({100,50,80},{0,0,80}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 0.1f,
+ /*far*/ 1000.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project's angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+* Source Engine uses **Z-up**: pitch angles control vertical look, positive = up.
+
+---
+
+## See also
+
+* [Source Engine Formulas](formulas.md) - View/projection matrix builders
+* [Source Engine Constants](constants.md) - Engine-specific constants
+* [Source Engine Pred Engine Trait](pred_engine_trait.md) - Projectile prediction for Source Engine
+* [Generic Camera Documentation](../../projection/camera.md) - Camera base class
+* [Getting Started Guide](../../getting_started.md) - Quick start with OMath
+* [Tutorials - World-to-Screen](../../tutorials.md#tutorial-2-world-to-screen-projection) - Projection tutorial
diff --git a/docs/engines/source_engine/constants.md b/docs/engines/source_engine/constants.md
new file mode 100644
index 00000000..829a9341
--- /dev/null
+++ b/docs/engines/source_engine/constants.md
@@ -0,0 +1,77 @@
+# `omath::source_engine` — types & constants
+
+> Header: `omath/engines/source_engine/constants.hpp`
+> Namespace: `omath::source_engine`
+> Purpose: define Source Engine coordinate system, matrix types, and angle ranges
+
+---
+
+## Summary
+
+The **Source Engine** uses a **Z-up, right-handed** coordinate system:
+
+* **Up** = `{0, 0, 1}` (Z-axis)
+* **Right** = `{0, -1, 0}` (negative Y-axis)
+* **Forward** = `{1, 0, 0}` (X-axis)
+
+Matrices are **row-major**. Angles are **clamped pitch** (±89°) and **normalized yaw/roll** (±180°).
+
+---
+
+## Constants
+
+```cpp
+namespace omath::source_engine {
+ constexpr Vector3 k_abs_up = {0, 0, 1};
+ constexpr Vector3 k_abs_right = {0, -1, 0};
+ constexpr Vector3 k_abs_forward = {1, 0, 0};
+}
+```
+
+These basis vectors define the engine's **world coordinate frame**.
+
+---
+
+## Matrix types
+
+```cpp
+using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
+```
+
+**Row-major** storage means rows are contiguous in memory. Suitable for CPU-side transforms and typical C++ math libraries.
+
+---
+
+## Angle types
+
+```cpp
+using PitchAngle = Angle;
+using YawAngle = Angle;
+using RollAngle = Angle;
+
+using ViewAngles = omath::ViewAngles;
+```
+
+* **PitchAngle**: clamped to **[-89°, +89°]** (looking down vs. up)
+* **YawAngle**: normalized to **[-180°, +180°]** (horizontal rotation)
+* **RollAngle**: normalized to **[-180°, +180°]** (camera roll)
+
+`ViewAngles` bundles all three into a single type for camera/view transforms.
+
+---
+
+## Coordinate system notes
+
+* **Z-up**: gravity points along `-Z`, height increases with `+Z`
+* **Right-handed**: cross product `forward × right = up` holds
+* This matches **Source Engine** (Half-Life 2, TF2, CS:GO, etc.) conventions
+
+---
+
+## See also
+
+* `omath/engines/source_engine/formulas.hpp` — view/projection matrix builders
+* `omath/trigonometry/angle.hpp` — angle normalization & clamping helpers
+* `omath/trigonometry/view_angles.hpp` — generic pitch/yaw/roll wrapper
diff --git a/docs/engines/source_engine/formulas.md b/docs/engines/source_engine/formulas.md
new file mode 100644
index 00000000..eba0c907
--- /dev/null
+++ b/docs/engines/source_engine/formulas.md
@@ -0,0 +1,135 @@
+# `omath::source_engine` — formulas & matrix helpers
+
+> Header: `omath/engines/source_engine/formulas.hpp`
+> Namespace: `omath::source_engine`
+> Purpose: compute direction vectors, rotation matrices, view matrices, and perspective projections for Source Engine
+
+---
+
+## Summary
+
+This header provides **Source Engine**-specific math for:
+
+* **Direction vectors** (`forward`, `right`, `up`) from `ViewAngles`
+* **Rotation matrices** from Euler angles
+* **View matrices** (camera transforms)
+* **Perspective projection** matrices
+
+All functions respect Source Engine's **Z-up, right-handed** coordinate system.
+
+---
+
+## API
+
+```cpp
+namespace omath::source_engine {
+
+ // Compute forward direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 forward_vector(const ViewAngles& angles) noexcept;
+
+ // Compute right direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 right_vector(const ViewAngles& angles) noexcept;
+
+ // Compute up direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 up_vector(const ViewAngles& angles) noexcept;
+
+ // Build 3x3 rotation matrix from angles
+ [[nodiscard]]
+ Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
+
+ // Build view matrix (camera space transform)
+ [[nodiscard]]
+ Mat4X4 calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection matrix
+ [[nodiscard]]
+ Mat4X4 calc_perspective_projection_matrix(float field_of_view,
+ float aspect_ratio,
+ float near, float far) noexcept;
+
+} // namespace omath::source_engine
+```
+
+---
+
+## Direction vectors
+
+Given camera angles (pitch/yaw/roll):
+
+* `forward_vector(angles)` → unit vector pointing where the camera looks
+* `right_vector(angles)` → unit vector pointing to the camera's right
+* `up_vector(angles)` → unit vector pointing upward relative to the camera
+
+These are used for movement, aim direction, and building coordinate frames.
+
+---
+
+## Rotation & view matrices
+
+* `rotation_matrix(angles)` → 3×3 (or 4×4) rotation matrix from Euler angles
+* `calc_view_matrix(angles, origin)` → camera view matrix
+
+The view matrix transforms world coordinates into camera space (origin at camera, axes aligned with camera orientation).
+
+---
+
+## Perspective projection
+
+```cpp
+Mat4X4 proj = calc_perspective_projection_matrix(
+ fov_degrees, // vertical field of view (e.g., 90)
+ aspect_ratio, // width / height (e.g., 16/9)
+ near_plane, // e.g., 0.1
+ far_plane // e.g., 1000.0
+);
+```
+
+Produces a **perspective projection matrix** suitable for 3D rendering pipelines. Combined with the view matrix, this implements the standard camera transform chain.
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::source_engine;
+
+// Camera setup
+ViewAngles angles = {
+ PitchAngle::from_degrees(-15.0f),
+ YawAngle::from_degrees(45.0f),
+ RollAngle::from_degrees(0.0f)
+};
+Vector3 cam_pos{100.0f, 50.0f, 80.0f};
+
+// Compute direction
+auto forward = forward_vector(angles);
+auto right = right_vector(angles);
+auto up = up_vector(angles);
+
+// Build matrices
+auto view_mat = calc_view_matrix(angles, cam_pos);
+auto proj_mat = calc_perspective_projection_matrix(90.0f, 16.0f/9.0f, 0.1f, 1000.0f);
+
+// Use view_mat and proj_mat for rendering...
+```
+
+---
+
+## Conventions
+
+* **Angles**: pitch (up/down), yaw (left/right), roll (tilt)
+* **Pitch**: positive = looking up, negative = looking down
+* **Yaw**: increases counter-clockwise from the +X axis
+* **Coordinate system**: Z-up, X-forward, Y-right (negative in code convention)
+
+---
+
+## See also
+
+* `omath/engines/source_engine/constants.hpp` — coordinate frame & angle types
+* `omath/engines/source_engine/traits/camera_trait.hpp` — plug-in for generic `Camera`
+* `omath/projection/camera.hpp` — generic camera wrapper using these formulas
diff --git a/docs/engines/source_engine/pred_engine_trait.md b/docs/engines/source_engine/pred_engine_trait.md
new file mode 100644
index 00000000..63f50241
--- /dev/null
+++ b/docs/engines/source_engine/pred_engine_trait.md
@@ -0,0 +1,198 @@
+# `omath::source_engine::PredEngineTrait` — projectile prediction trait
+
+> Header: `omath/engines/source_engine/traits/pred_engine_trait.hpp`
+> Namespace: `omath::source_engine`
+> Purpose: provide Source Engine-specific projectile and target prediction for ballistic calculations
+
+---
+
+## Summary
+
+`PredEngineTrait` implements engine-specific helpers for **projectile prediction**:
+
+* `predict_projectile_position` – computes where a projectile will be after `time` seconds
+* `predict_target_position` – computes where a moving target will be after `time` seconds
+* `calc_vector_2d_distance` – horizontal distance (X/Y plane, ignoring Z)
+* `get_vector_height_coordinate` – extracts vertical coordinate (Z in Source Engine)
+* `calc_viewpoint_from_angles` – computes aim point given pitch angle
+* `calc_direct_pitch_angle` – pitch angle to look from origin to target
+* `calc_direct_yaw_angle` – yaw angle to look from origin to target
+
+These methods satisfy the `PredEngineTraitConcept` required by generic projectile prediction algorithms.
+
+---
+
+## API
+
+```cpp
+namespace omath::source_engine {
+
+class PredEngineTrait final {
+public:
+ // Predict projectile position after `time` seconds
+ static constexpr Vector3
+ predict_projectile_position(const projectile_prediction::Projectile& projectile,
+ float pitch, float yaw, float time,
+ float gravity) noexcept;
+
+ // Predict target position after `time` seconds
+ static constexpr Vector3
+ predict_target_position(const projectile_prediction::Target& target,
+ float time, float gravity) noexcept;
+
+ // Compute horizontal (2D) distance
+ static float
+ calc_vector_2d_distance(const Vector3& delta) noexcept;
+
+ // Get vertical coordinate (Z in Source Engine)
+ static constexpr float
+ get_vector_height_coordinate(const Vector3& vec) noexcept;
+
+ // Compute aim point from angles
+ static Vector3
+ calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
+ Vector3 predicted_target_position,
+ std::optional projectile_pitch) noexcept;
+
+ // Compute pitch angle to look at target
+ static float
+ calc_direct_pitch_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+
+ // Compute yaw angle to look at target
+ static float
+ calc_direct_yaw_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+};
+
+} // namespace omath::source_engine
+```
+
+---
+
+## Projectile prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_projectile_position(
+ projectile, // initial position, speed, gravity scale
+ pitch_deg, // launch pitch (positive = up)
+ yaw_deg, // launch yaw
+ time, // time in seconds
+ gravity // gravity constant (e.g., 800 units/s²)
+);
+```
+
+Computes:
+
+1. Forward vector from pitch/yaw (using `forward_vector`)
+2. Initial velocity: `forward * launch_speed`
+3. Position after `time`: `origin + velocity*time - 0.5*gravity*gravityScale*time²` (Z component only)
+
+**Note**: Negative pitch in `forward_vector` convention → positive pitch looks up.
+
+---
+
+## Target prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_target_position(
+ target, // position, velocity, airborne flag
+ time, // time in seconds
+ gravity // gravity constant
+);
+```
+
+Simple linear extrapolation plus gravity if target is airborne:
+
+```
+predicted = origin + velocity * time
+if (airborne)
+ predicted.z -= 0.5 * gravity * time²
+```
+
+---
+
+## Distance & height helpers
+
+* `calc_vector_2d_distance(delta)` → `sqrt(delta.x² + delta.y²)` (horizontal distance)
+* `get_vector_height_coordinate(vec)` → `vec.z` (vertical coordinate in Source Engine)
+
+Used to compute ballistic arc parameters.
+
+---
+
+## Aim angle calculation
+
+* `calc_direct_pitch_angle(origin, target)` → pitch in degrees to look from `origin` to `target`
+ - Formula: `asin(Δz / distance)` converted to degrees
+ - Positive = looking up, negative = looking down
+
+* `calc_direct_yaw_angle(origin, target)` → yaw in degrees to look from `origin` to `target`
+ - Formula: `atan2(Δy, Δx)` converted to degrees
+ - Horizontal rotation around Z-axis
+
+---
+
+## Viewpoint from angles
+
+```cpp
+auto aim_point = PredEngineTrait::calc_viewpoint_from_angles(
+ projectile,
+ predicted_target_pos,
+ optional_pitch_deg
+);
+```
+
+Computes where to aim in 3D space given a desired pitch angle. Uses horizontal distance and `tan(pitch)` to compute height offset.
+
+---
+
+## Conventions
+
+* **Coordinate system**: Z-up (height increases with Z)
+* **Angles**: pitch in [-89°, +89°], yaw in [-180°, +180°]
+* **Gravity**: applied downward along -Z axis
+* **Pitch convention**: +89° = straight up, -89° = straight down
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::source_engine;
+using namespace omath::projectile_prediction;
+
+Projectile proj{
+ .m_origin = {0, 0, 100},
+ .m_launch_speed = 1000.0f,
+ .m_gravity_scale = 1.0f
+};
+
+Target tgt{
+ .m_origin = {500, 200, 100},
+ .m_velocity = {10, 5, 0},
+ .m_is_airborne = false
+};
+
+float gravity = 800.0f;
+float time = 0.5f;
+
+// Predict where target will be
+auto target_pos = PredEngineTrait::predict_target_position(tgt, time, gravity);
+
+// Compute aim angles
+float pitch = PredEngineTrait::calc_direct_pitch_angle(proj.m_origin, target_pos);
+float yaw = PredEngineTrait::calc_direct_yaw_angle(proj.m_origin, target_pos);
+
+// Predict projectile position with those angles
+auto proj_pos = PredEngineTrait::predict_projectile_position(proj, pitch, yaw, time, gravity);
+```
+
+---
+
+## See also
+
+* `omath/engines/source_engine/formulas.hpp` — direction vectors and matrix builders
+* `omath/projectile_prediction/projectile.hpp` — `Projectile` struct
+* `omath/projectile_prediction/target.hpp` — `Target` struct
+* Generic projectile prediction algorithms that use `PredEngineTraitConcept`
diff --git a/docs/engines/unity_engine/camera_trait.md b/docs/engines/unity_engine/camera_trait.md
new file mode 100644
index 00000000..1611eef1
--- /dev/null
+++ b/docs/engines/unity_engine/camera_trait.md
@@ -0,0 +1,109 @@
+# `omath::unity_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/unity_engine/traits/camera_trait.hpp` • Impl: `omath/engines/unity_engine/traits/camera_trait.cpp`
+> Namespace: `omath::unity_engine`
+> Purpose: provide Unity Engine-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `asin(dir.y)` and **yaw** as `atan2(dir.x, dir.z)`; **roll** is `0`. Pitch/yaw are returned using the project's strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to Unity Engine formulas `unity_engine::calc_view_matrix`, producing a `Mat4X4` view matrix for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait's types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the Unity Engine math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::unity_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin.
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far.
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::unity_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at** (Y-up coordinate system):
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = asin(dir.y) // +Y is up
+ yaw = atan2(dir.x, dir.z) // horizontal rotation
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the Unity Engine helper `unity_engine::calc_view_matrix(angles, origin)` to match the engine's handedness and axis conventions.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix.
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from Unity Engine math headers
+using Angs = ViewAngles; // pitch/yaw/roll type
+using UEcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(60.f);
+
+UEcam cam(
+ /*position*/ {10.f, 5.f, -10.f},
+ /*angles*/ omath::unity_engine::CameraTrait::calc_look_at_angle({10,5,-10},{0,5,0}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 0.3f,
+ /*far*/ 1000.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project's angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+* Unity Engine uses **Y-up**: pitch angles control vertical look, positive = up.
+
+---
+
+## See also
+
+* Unity Engine math helpers in `omath/engines/unity_engine/formulas.hpp` (view/projection builders used above).
+* Generic camera wrapper `omath::projection::Camera` and its `CameraEngineConcept` (this trait is designed to plug straight into it).
diff --git a/docs/engines/unity_engine/constants.md b/docs/engines/unity_engine/constants.md
new file mode 100644
index 00000000..fc25d6a0
--- /dev/null
+++ b/docs/engines/unity_engine/constants.md
@@ -0,0 +1,77 @@
+# `omath::unity_engine` — types & constants
+
+> Header: `omath/engines/unity_engine/constants.hpp`
+> Namespace: `omath::unity_engine`
+> Purpose: define Unity Engine coordinate system, matrix types, and angle ranges
+
+---
+
+## Summary
+
+The **Unity Engine** uses a **Y-up, left-handed** coordinate system:
+
+* **Up** = `{0, 1, 0}` (Y-axis)
+* **Right** = `{1, 0, 0}` (X-axis)
+* **Forward** = `{0, 0, 1}` (Z-axis)
+
+Matrices are **row-major**. Angles are **clamped pitch** (±90°) and **normalized yaw/roll** (±180°).
+
+---
+
+## Constants
+
+```cpp
+namespace omath::unity_engine {
+ constexpr Vector3 k_abs_up = {0, 1, 0};
+ constexpr Vector3 k_abs_right = {1, 0, 0};
+ constexpr Vector3 k_abs_forward = {0, 0, 1};
+}
+```
+
+These basis vectors define the engine's **world coordinate frame**.
+
+---
+
+## Matrix types
+
+```cpp
+using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
+```
+
+**Row-major** storage means rows are contiguous in memory. Suitable for CPU-side transforms and typical C++ math libraries.
+
+---
+
+## Angle types
+
+```cpp
+using PitchAngle = Angle;
+using YawAngle = Angle;
+using RollAngle = Angle;
+
+using ViewAngles = omath::ViewAngles;
+```
+
+* **PitchAngle**: clamped to **[-90°, +90°]** (looking down vs. up)
+* **YawAngle**: normalized to **[-180°, +180°]** (horizontal rotation)
+* **RollAngle**: normalized to **[-180°, +180°]** (camera roll)
+
+`ViewAngles` bundles all three into a single type for camera/view transforms.
+
+---
+
+## Coordinate system notes
+
+* **Y-up**: gravity points along `-Y`, height increases with `+Y`
+* **Left-handed**: cross product `forward × right = up` with left-hand rule
+* This matches **Unity Engine** conventions for 3D games and simulations
+
+---
+
+## See also
+
+* `omath/engines/unity_engine/formulas.hpp` — view/projection matrix builders
+* `omath/trigonometry/angle.hpp` — angle normalization & clamping helpers
+* `omath/trigonometry/view_angles.hpp` — generic pitch/yaw/roll wrapper
diff --git a/docs/engines/unity_engine/formulas.md b/docs/engines/unity_engine/formulas.md
new file mode 100644
index 00000000..7ed0b90a
--- /dev/null
+++ b/docs/engines/unity_engine/formulas.md
@@ -0,0 +1,135 @@
+# `omath::unity_engine` — formulas & matrix helpers
+
+> Header: `omath/engines/unity_engine/formulas.hpp`
+> Namespace: `omath::unity_engine`
+> Purpose: compute direction vectors, rotation matrices, view matrices, and perspective projections for Unity Engine
+
+---
+
+## Summary
+
+This header provides **Unity Engine**-specific math for:
+
+* **Direction vectors** (`forward`, `right`, `up`) from `ViewAngles`
+* **Rotation matrices** from Euler angles
+* **View matrices** (camera transforms)
+* **Perspective projection** matrices
+
+All functions respect Unity Engine's **Y-up, left-handed** coordinate system.
+
+---
+
+## API
+
+```cpp
+namespace omath::unity_engine {
+
+ // Compute forward direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 forward_vector(const ViewAngles& angles) noexcept;
+
+ // Compute right direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 right_vector(const ViewAngles& angles) noexcept;
+
+ // Compute up direction from pitch/yaw/roll
+ [[nodiscard]]
+ Vector3 up_vector(const ViewAngles& angles) noexcept;
+
+ // Build 3x3 rotation matrix from angles
+ [[nodiscard]]
+ Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
+
+ // Build view matrix (camera space transform)
+ [[nodiscard]]
+ Mat4X4 calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection matrix
+ [[nodiscard]]
+ Mat4X4 calc_perspective_projection_matrix(float field_of_view,
+ float aspect_ratio,
+ float near, float far) noexcept;
+
+} // namespace omath::unity_engine
+```
+
+---
+
+## Direction vectors
+
+Given camera angles (pitch/yaw/roll):
+
+* `forward_vector(angles)` → unit vector pointing where the camera looks
+* `right_vector(angles)` → unit vector pointing to the camera's right
+* `up_vector(angles)` → unit vector pointing upward relative to the camera
+
+These are used for movement, aim direction, and building coordinate frames.
+
+---
+
+## Rotation & view matrices
+
+* `rotation_matrix(angles)` → 3×3 (or 4×4) rotation matrix from Euler angles
+* `calc_view_matrix(angles, origin)` → camera view matrix
+
+The view matrix transforms world coordinates into camera space (origin at camera, axes aligned with camera orientation).
+
+---
+
+## Perspective projection
+
+```cpp
+Mat4X4 proj = calc_perspective_projection_matrix(
+ fov_degrees, // vertical field of view (e.g., 60)
+ aspect_ratio, // width / height (e.g., 16/9)
+ near_plane, // e.g., 0.3
+ far_plane // e.g., 1000.0
+);
+```
+
+Produces a **perspective projection matrix** suitable for 3D rendering pipelines. Combined with the view matrix, this implements the standard camera transform chain.
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::unity_engine;
+
+// Camera setup
+ViewAngles angles = {
+ PitchAngle::from_degrees(-15.0f),
+ YawAngle::from_degrees(45.0f),
+ RollAngle::from_degrees(0.0f)
+};
+Vector3 cam_pos{10.0f, 5.0f, -10.0f};
+
+// Compute direction
+auto forward = forward_vector(angles);
+auto right = right_vector(angles);
+auto up = up_vector(angles);
+
+// Build matrices
+auto view_mat = calc_view_matrix(angles, cam_pos);
+auto proj_mat = calc_perspective_projection_matrix(60.0f, 16.0f/9.0f, 0.3f, 1000.0f);
+
+// Use view_mat and proj_mat for rendering...
+```
+
+---
+
+## Conventions
+
+* **Angles**: pitch (up/down), yaw (left/right), roll (tilt)
+* **Pitch**: positive = looking up, negative = looking down
+* **Yaw**: increases counter-clockwise from the +Z axis
+* **Coordinate system**: Y-up, Z-forward, X-right (left-handed)
+
+---
+
+## See also
+
+* `omath/engines/unity_engine/constants.hpp` — coordinate frame & angle types
+* `omath/engines/unity_engine/traits/camera_trait.hpp` — plug-in for generic `Camera`
+* `omath/projection/camera.hpp` — generic camera wrapper using these formulas
diff --git a/docs/engines/unity_engine/pred_engine_trait.md b/docs/engines/unity_engine/pred_engine_trait.md
new file mode 100644
index 00000000..13992def
--- /dev/null
+++ b/docs/engines/unity_engine/pred_engine_trait.md
@@ -0,0 +1,198 @@
+# `omath::unity_engine::PredEngineTrait` — projectile prediction trait
+
+> Header: `omath/engines/unity_engine/traits/pred_engine_trait.hpp`
+> Namespace: `omath::unity_engine`
+> Purpose: provide Unity Engine-specific projectile and target prediction for ballistic calculations
+
+---
+
+## Summary
+
+`PredEngineTrait` implements engine-specific helpers for **projectile prediction**:
+
+* `predict_projectile_position` – computes where a projectile will be after `time` seconds
+* `predict_target_position` – computes where a moving target will be after `time` seconds
+* `calc_vector_2d_distance` – horizontal distance (X/Z plane, ignoring Y)
+* `get_vector_height_coordinate` – extracts vertical coordinate (Y in Unity Engine)
+* `calc_viewpoint_from_angles` – computes aim point given pitch angle
+* `calc_direct_pitch_angle` – pitch angle to look from origin to target
+* `calc_direct_yaw_angle` – yaw angle to look from origin to target
+
+These methods satisfy the `PredEngineTraitConcept` required by generic projectile prediction algorithms.
+
+---
+
+## API
+
+```cpp
+namespace omath::unity_engine {
+
+class PredEngineTrait final {
+public:
+ // Predict projectile position after `time` seconds
+ static constexpr Vector3
+ predict_projectile_position(const projectile_prediction::Projectile& projectile,
+ float pitch, float yaw, float time,
+ float gravity) noexcept;
+
+ // Predict target position after `time` seconds
+ static constexpr Vector3
+ predict_target_position(const projectile_prediction::Target& target,
+ float time, float gravity) noexcept;
+
+ // Compute horizontal (2D) distance
+ static float
+ calc_vector_2d_distance(const Vector3& delta) noexcept;
+
+ // Get vertical coordinate (Y in Unity Engine)
+ static constexpr float
+ get_vector_height_coordinate(const Vector3& vec) noexcept;
+
+ // Compute aim point from angles
+ static Vector3
+ calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
+ Vector3 predicted_target_position,
+ std::optional projectile_pitch) noexcept;
+
+ // Compute pitch angle to look at target
+ static float
+ calc_direct_pitch_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+
+ // Compute yaw angle to look at target
+ static float
+ calc_direct_yaw_angle(const Vector3& origin,
+ const Vector3& view_to) noexcept;
+};
+
+} // namespace omath::unity_engine
+```
+
+---
+
+## Projectile prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_projectile_position(
+ projectile, // initial position, speed, gravity scale
+ pitch_deg, // launch pitch (positive = up)
+ yaw_deg, // launch yaw
+ time, // time in seconds
+ gravity // gravity constant (e.g., 9.81 m/s²)
+);
+```
+
+Computes:
+
+1. Forward vector from pitch/yaw (using `forward_vector`)
+2. Initial velocity: `forward * launch_speed`
+3. Position after `time`: `origin + velocity*time - 0.5*gravity*gravityScale*time²` (Y component only)
+
+**Note**: Negative pitch in `forward_vector` convention → positive pitch looks up.
+
+---
+
+## Target prediction
+
+```cpp
+auto pos = PredEngineTrait::predict_target_position(
+ target, // position, velocity, airborne flag
+ time, // time in seconds
+ gravity // gravity constant
+);
+```
+
+Simple linear extrapolation plus gravity if target is airborne:
+
+```
+predicted = origin + velocity * time
+if (airborne)
+ predicted.y -= 0.5 * gravity * time²
+```
+
+---
+
+## Distance & height helpers
+
+* `calc_vector_2d_distance(delta)` → `sqrt(delta.x² + delta.z²)` (horizontal distance)
+* `get_vector_height_coordinate(vec)` → `vec.y` (vertical coordinate in Unity Engine)
+
+Used to compute ballistic arc parameters.
+
+---
+
+## Aim angle calculation
+
+* `calc_direct_pitch_angle(origin, target)` → pitch in degrees to look from `origin` to `target`
+ - Formula: `asin(Δy / distance)` converted to degrees (direction normalized first)
+ - Positive = looking up, negative = looking down
+
+* `calc_direct_yaw_angle(origin, target)` → yaw in degrees to look from `origin` to `target`
+ - Formula: `atan2(Δx, Δz)` converted to degrees (direction normalized first)
+ - Horizontal rotation around Y-axis
+
+---
+
+## Viewpoint from angles
+
+```cpp
+auto aim_point = PredEngineTrait::calc_viewpoint_from_angles(
+ projectile,
+ predicted_target_pos,
+ optional_pitch_deg
+);
+```
+
+Computes where to aim in 3D space given a desired pitch angle. Uses horizontal distance and `tan(pitch)` to compute height offset. Result has adjusted Y coordinate.
+
+---
+
+## Conventions
+
+* **Coordinate system**: Y-up (height increases with Y)
+* **Angles**: pitch in [-90°, +90°], yaw in [-180°, +180°]
+* **Gravity**: applied downward along -Y axis
+* **Pitch convention**: +90° = straight up, -90° = straight down
+
+---
+
+## Usage example
+
+```cpp
+using namespace omath::unity_engine;
+using namespace omath::projectile_prediction;
+
+Projectile proj{
+ .m_origin = {0, 2, 0},
+ .m_launch_speed = 50.0f,
+ .m_gravity_scale = 1.0f
+};
+
+Target tgt{
+ .m_origin = {20, 2, 15},
+ .m_velocity = {1, 0, 0.5f},
+ .m_is_airborne = false
+};
+
+float gravity = 9.81f;
+float time = 0.5f;
+
+// Predict where target will be
+auto target_pos = PredEngineTrait::predict_target_position(tgt, time, gravity);
+
+// Compute aim angles
+float pitch = PredEngineTrait::calc_direct_pitch_angle(proj.m_origin, target_pos);
+float yaw = PredEngineTrait::calc_direct_yaw_angle(proj.m_origin, target_pos);
+
+// Predict projectile position with those angles
+auto proj_pos = PredEngineTrait::predict_projectile_position(proj, pitch, yaw, time, gravity);
+```
+
+---
+
+## See also
+
+* `omath/engines/unity_engine/formulas.hpp` — direction vectors and matrix builders
+* `omath/projectile_prediction/projectile.hpp` — `Projectile` struct
+* `omath/projectile_prediction/target.hpp` — `Target` struct
+* Generic projectile prediction algorithms that use `PredEngineTraitConcept`
diff --git a/docs/engines/unreal_engine/camera_trait.md b/docs/engines/unreal_engine/camera_trait.md
new file mode 100644
index 00000000..7a02fc69
--- /dev/null
+++ b/docs/engines/unreal_engine/camera_trait.md
@@ -0,0 +1,109 @@
+# `omath::unreal_engine::CameraTrait` — plug-in trait for `projection::Camera`
+
+> Header: `omath/engines/unreal_engine/traits/camera_trait.hpp` • Impl: `omath/engines/unreal_engine/traits/camera_trait.cpp`
+> Namespace: `omath::unreal_engine`
+> Purpose: provide Unreal Engine-style **look-at**, **view**, and **projection** math to the generic `omath::projection::Camera` (satisfies `CameraEngineConcept`).
+
+---
+
+## Summary
+
+`CameraTrait` exposes three `static` functions:
+
+* `calc_look_at_angle(origin, look_at)` – computes Euler angles so the camera at `origin` looks at `look_at`. Implementation normalizes the direction, computes **pitch** as `asin(dir.z)` and **yaw** as `atan2(dir.y, dir.x)`; **roll** is `0`. Pitch/yaw are returned using the project's strong angle types (`PitchAngle`, `YawAngle`, `RollAngle`).
+* `calc_view_matrix(angles, origin)` – delegates to Unreal Engine formulas `unreal_engine::calc_view_matrix`, producing a `Mat4X4` view matrix for the given angles and origin.
+* `calc_projection_matrix(fov, viewport, near, far)` – builds a perspective projection by calling `calc_perspective_projection_matrix(fov_degrees, aspect, near, far)`, where `aspect = viewport.aspect_ratio()`. Accepts `FieldOfView` (degrees).
+
+The trait's types (`ViewAngles`, `Mat4X4`, angle aliases) and helpers live in the Unreal Engine math headers included by the trait (`formulas.hpp`) and the shared projection header (`projection/camera.hpp`).
+
+---
+
+## API
+
+```cpp
+namespace omath::unreal_engine {
+
+class CameraTrait final {
+public:
+ // Compute Euler angles (pitch/yaw/roll) to look from cam_origin to look_at.
+ static ViewAngles
+ calc_look_at_angle(const Vector3& cam_origin,
+ const Vector3& look_at) noexcept;
+
+ // Build view matrix for given angles and origin.
+ static Mat4X4
+ calc_view_matrix(const ViewAngles& angles,
+ const Vector3& cam_origin) noexcept;
+
+ // Build perspective projection from FOV (deg), viewport, near/far.
+ static Mat4X4
+ calc_projection_matrix(const projection::FieldOfView& fov,
+ const projection::ViewPort& view_port,
+ float near, float far) noexcept;
+};
+
+} // namespace omath::unreal_engine
+```
+
+Uses: `Vector3`, `ViewAngles` (pitch/yaw/roll), `Mat4X4`, `projection::FieldOfView`, `projection::ViewPort`.
+
+---
+
+## Behavior & conventions
+
+* **Angles from look-at** (Z-up coordinate system):
+
+ ```
+ dir = normalize(look_at - origin)
+ pitch = asin(dir.z) // +Z is up
+ yaw = atan2(dir.y, dir.x) // horizontal rotation
+ roll = 0
+ ```
+
+ Returned as `PitchAngle::from_radians(...)`, `YawAngle::from_radians(...)`, etc.
+
+* **View matrix**: built by the Unreal Engine helper `unreal_engine::calc_view_matrix(angles, origin)` to match the engine's handedness and axis conventions.
+
+* **Projection**: uses `calc_perspective_projection_matrix(fov.as_degrees(), viewport.aspect_ratio(), near, far)`. Pass your **vertical FOV** in degrees via `FieldOfView`; the helper computes a standard perspective matrix.
+
+---
+
+## Using with `projection::Camera`
+
+Create a camera whose math is driven by this trait:
+
+```cpp
+using Mat4 = Mat4X4; // from Unreal Engine math headers
+using Angs = ViewAngles; // pitch/yaw/roll type
+using UEcam = omath::projection::Camera;
+
+omath::projection::ViewPort vp{1920.f, 1080.f};
+auto fov = omath::projection::FieldOfView::from_degrees(90.f);
+
+UEcam cam(
+ /*position*/ {1000.f, 500.f, 200.f},
+ /*angles*/ omath::unreal_engine::CameraTrait::calc_look_at_angle({1000,500,200},{0,0,200}),
+ /*viewport*/ vp,
+ /*fov*/ fov,
+ /*near*/ 10.f,
+ /*far*/ 100000.f
+);
+```
+
+This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, view, projection) as declared in the trait header.
+
+---
+
+## Notes & tips
+
+* Ensure your `ViewAngles` aliases (`PitchAngle`, `YawAngle`, `RollAngle`) match the project's angle policy (ranges/normalization). The implementation constructs them **from radians**.
+* `aspect_ratio()` is taken directly from `ViewPort` (`width / height`), so keep both positive and non-zero.
+* `near` must be > 0 and `< far` for a valid projection matrix (enforced by your math helpers).
+* Unreal Engine uses **Z-up**: pitch angles control vertical look, positive = up.
+
+---
+
+## See also
+
+* Unreal Engine math helpers in `omath/engines/unreal_engine/formulas.hpp` (view/projection builders used above).
+* Generic camera wrapper `omath::projection::Camera` and its `CameraEngineConcept` (this trait is designed to plug straight into it).
diff --git a/docs/engines/unreal_engine/constants.md b/docs/engines/unreal_engine/constants.md
new file mode 100644
index 00000000..5b66bd7e
--- /dev/null
+++ b/docs/engines/unreal_engine/constants.md
@@ -0,0 +1,77 @@
+# `omath::unreal_engine` — types & constants
+
+> Header: `omath/engines/unreal_engine/constants.hpp`
+> Namespace: `omath::unreal_engine`
+> Purpose: define Unreal Engine coordinate system, matrix types, and angle ranges
+
+---
+
+## Summary
+
+The **Unreal Engine** uses a **Z-up, left-handed** coordinate system:
+
+* **Up** = `{0, 0, 1}` (Z-axis)
+* **Right** = `{0, 1, 0}` (Y-axis)
+* **Forward** = `{1, 0, 0}` (X-axis)
+
+Matrices are **row-major**. Angles are **clamped pitch** (±90°) and **normalized yaw/roll** (±180°).
+
+---
+
+## Constants
+
+```cpp
+namespace omath::unreal_engine {
+ constexpr Vector3 k_abs_up = {0, 0, 1};
+ constexpr Vector3 k_abs_right = {0, 1, 0};
+ constexpr Vector3 k_abs_forward = {1, 0, 0};
+}
+```
+
+These basis vectors define the engine's **world coordinate frame**.
+
+---
+
+## Matrix types
+
+```cpp
+using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
+using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
+```
+
+**Row-major** storage means rows are contiguous in memory. Suitable for CPU-side transforms and typical C++ math libraries.
+
+---
+
+## Angle types
+
+```cpp
+using PitchAngle = Angle;
+using YawAngle = Angle;
+using RollAngle = Angle