From ce9758c86b398f5ab43fce76dda10445a5fd561a Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 31 Oct 2025 16:05:44 +0300 Subject: [PATCH 01/14] Adds dark theme support to the OM documentation Applies a dark theme to the documentation site using the `darkly` theme. Provides a visually appealing dark mode option. Configures colors to ensure readability and aesthetic consistency. --- docs/index.md | 75 +++++- docs/linear_algebra/mat.md | 428 +++++++++++++++++++++++++++++++++ docs/styles/dark-overrides.css | 67 ++++++ mkdocs.yml | 4 +- 4 files changed, 561 insertions(+), 13 deletions(-) create mode 100644 docs/linear_algebra/mat.md create mode 100644 docs/styles/dark-overrides.css diff --git a/docs/index.md b/docs/index.md index 000ea345..70faad52 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,17 +1,68 @@ -# Welcome to MkDocs +# Installation -For full documentation visit [mkdocs.org](https://www.mkdocs.org). +## Using vcpkg +**Note**: Support vcpkg for package management +1. Install [vcpkg](https://github.com/microsoft/vcpkg) +2. Run the following command to install the orange-math package: +``` +vcpkg install orange-math +``` +CMakeLists.txt +```cmake +find_package(omath CONFIG REQUIRED) +target_link_libraries(main PRIVATE omath::omath) +``` +For detailed commands on installing different versions and more information, please refer to Microsoft's [official instructions](https://learn.microsoft.com/en-us/vcpkg/get_started/overview). -## Commands +## Using xrepo +**Note**: Support xrepo for package management +1. Install [xmake](https://xmake.io/) +2. Run the following command to install the omath package: +``` +xrepo install omath +``` +xmake.lua +```xmake +add_requires("omath") +target("...") + add_packages("omath") +``` -* `mkdocs new [dir-name]` - Create a new project. -* `mkdocs serve` - Start the live-reloading docs server. -* `mkdocs build` - Build the documentation site. -* `mkdocs -h` - Print help message and exit. +## Build from source using CMake +1. **Preparation** -## Project layout + Install needed tools: cmake, clang, git, msvc (windows only). - mkdocs.yml # The configuration file. - docs/ - index.md # The documentation homepage. - ... # Other markdown pages, images and other files. + 1. **Linux:** + ```bash + sudo pacman -Sy cmake ninja clang git + ``` + 2. **MacOS:** + ```bash + brew install llvm git cmake ninja + ``` + 3. **Windows:** + + Install Visual Studio from [here](https://visualstudio.microsoft.com/downloads/) and Git from [here](https://git-scm.com/downloads). + + Use x64 Native Tools shell to execute needed commands down below. +2. **Clone the repository:** + ```bash + git clone https://github.com/orange-cpp/omath.git + ``` +3. **Navigate to the project directory:** + ```bash + cd omath + ``` +4. **Build the project using CMake:** + ```bash + cmake --preset windows-release -S . + cmake --build cmake-build/build/windows-release --target omath -j 6 + ``` + Use **\-\** preset to build suitable version for yourself. Like **windows-release** or **linux-release**. + + | Platform Name | Build Config | + |---------------|---------------| + | windows | release/debug | + | linux | release/debug | + | darwin | release/debug | diff --git a/docs/linear_algebra/mat.md b/docs/linear_algebra/mat.md new file mode 100644 index 00000000..b54cc3be --- /dev/null +++ b/docs/linear_algebra/mat.md @@ -0,0 +1,428 @@ +# `omath::Mat` + +> Header: your project’s `mat.hpp` (requires `vector3.hpp`) +> Namespace: `omath` +> Requires: **C++23** (uses multi-parameter `operator[]`) +> SIMD (optional): define **`OMATH_USE_AVX2`** to enable AVX2-accelerated multiplication for `float`/`double`. + +--- + +## Overview + +`omath::Mat` is a compile-time, fixed-size matrix with: + +* **Row/column counts** as template parameters (no heap allocations). +* **Row-major** or **column-major** storage (compile-time via `MatStoreType`). +* **Arithmetic** and **linear algebra**: matrix × matrix, scalar ops, transpose, determinant, inverse (optional), etc. +* **Transform helpers**: translation, axis rotations, look-at, perspective & orthographic projections. +* **I/O helpers**: `to_string`/`to_wstring`/`to_u8string` and `std::formatter` specializations. + +--- + +## Template parameters + +| Parameter | Description | Default | +| ----------- | ------------------------------------------------------------------------ | ----------- | +| `Rows` | Number of rows (size_t, compile-time) | — | +| `Columns` | Number of columns (size_t, compile-time) | — | +| `Type` | Element type (arithmetic) | `float` | +| `StoreType` | Storage order: `MatStoreType::ROW_MAJOR` or `MatStoreType::COLUMN_MAJOR` | `ROW_MAJOR` | + +```cpp +enum class MatStoreType : uint8_t { ROW_MAJOR = 0, COLUMN_MAJOR }; +``` + +--- + +## Quick start + +```cpp +#include "mat.hpp" +using omath::Mat; + +// 4x4 float, row-major +Mat<4,4> I = { + {1,0,0,0}, + {0,1,0,0}, + {0,0,1,0}, + {0,0,0,1}, +}; + +// Multiply 4x4 transforms +Mat<4,4> A = { {1,2,3,0},{0,1,4,0},{5,6,0,0},{0,0,0,1} }; +Mat<4,4> B = { {2,0,0,0},{0,2,0,0},{0,0,2,0},{0,0,0,1} }; +Mat<4,4> C = A * B; // matrix × matrix + +// Scalar ops +auto D = C * 0.5f; // scale all entries + +// Indexing (C++23 multi-parameter operator[]) +float a03 = A[0,3]; // same as A.at(0,3) +A[1,2] = 42.0f; + +// Transpose, determinant, inverse +auto AT = A.transposed(); +float det = A.determinant(); // only for square matrices +auto inv = A.inverted(); // std::optional; std::nullopt if non-invertible +``` + +> **Note** +> Multiplication requires the **same** `StoreType` and `Type` on both operands, and dimensions must match at compile time. + +--- + +## Construction + +```cpp +Mat(); // zero-initialized +Mat(std::initializer_list> rows); +explicit Mat(const Type* raw_data); // copies Rows*Columns elements +Mat(const Mat&); Mat(Mat&&); +``` + +* **Zeroing/setting** + + ```cpp + m.clear(); // set all entries to 0 + m.set(3.14f); // set all entries to a value + ``` + +* **Shape & metadata** + + ```cpp + Mat<>::row_count(); // constexpr size_t + Mat<>::columns_count(); // constexpr size_t + Mat<>::size(); // constexpr MatSize {rows, columns} + Mat<>::get_store_ordering(); // constexpr MatStoreType + using ContainedType = Type; // alias + ``` + +--- + +## Element access + +```cpp +T& at(size_t r, size_t c); +T const& at(size_t r, size_t c) const; + +T& operator[](size_t r, size_t c); // C++23 +T const& operator[](size_t r, size_t c) const; // C++23 +``` + +> **Bounds checking** +> In debug builds you may enable/disable range checks via your compile-time macros (see the source guard around `at()`). + +--- + +## Arithmetic + +* **Matrix × matrix** + + ```cpp + // (Rows x Columns) * (Columns x OtherColumns) -> (Rows x OtherColumns) + template + Mat + operator*(const Mat&) const; + ``` + + * Complexity: `O(Rows * Columns * OtherColumns)`. + * AVX2-accelerated when `OMATH_USE_AVX2` is defined and `Type` is `float` or `double`. + +* **Scalars** + + ```cpp + Mat operator*(const Type& s) const; Mat& operator*=(const Type& s); + Mat operator/(const Type& s) const; Mat& operator/=(const Type& s); + ``` + +* **Transpose** + + ```cpp + Mat transposed() const noexcept; + ``` + +* **Determinant (square only)** + + ```cpp + Type determinant() const; // 1x1, 2x2 fast path; larger uses Laplace expansion + ``` + +* **Inverse (square only)** + + ```cpp + std::optional inverted() const; // nullopt if det == 0 + ``` + +* **Minors & cofactors (square only)** + + ```cpp + Mat strip(size_t r, size_t c) const; + Type minor(size_t r, size_t c) const; + Type alg_complement(size_t r, size_t c) const; // cofactor + ``` + +* **Utilities** + + ```cpp + Type sum() const noexcept; + auto& raw_array(); // std::array& + auto const& raw_array() const; + ``` + +* **Comparison / formatting** + + ```cpp + bool operator==(const Mat&) const; + bool operator!=(const Mat&) const; + + std::string to_string() const noexcept; + std::wstring to_wstring() const noexcept; + std::u8string to_u8string() const noexcept; + ``` + +// std::formatter specialization provided for char, wchar_t, char8_t + +```` + +--- + +## Storage order notes + +- **Row-major**: `index = row * Columns + column` +- **Column-major**: `index = row + column * Rows` + +Choose one **consistently** across your math types and shader conventions. Mixed orders are supported by the type system but not for cross-multiplying (store types must match). + +--- + +## Transform helpers + +### From vectors + +```cpp +template +Mat<1,4,T,St> mat_row_from_vector(const Vector3& v); + +template +Mat<4,1,T,St> mat_column_from_vector(const Vector3& v); +```` + +### Translation + +```cpp +template +Mat<4,4,T,St> mat_translation(const Vector3& d) noexcept; +``` + +### Axis rotations + +```cpp +// Angle type must provide angle.cos() and angle.sin() +template +Mat<4,4,T,St> mat_rotation_axis_x(const Angle& a) noexcept; + +template +Mat<4,4,T,St> mat_rotation_axis_y(const Angle& a) noexcept; + +template +Mat<4,4,T,St> mat_rotation_axis_z(const Angle& a) noexcept; +``` + +### Camera/view + +```cpp +template +Mat<4,4,T,St> mat_camera_view(const Vector3& forward, + const Vector3& right, + const Vector3& up, + const Vector3& camera_origin) noexcept; +``` + +### Perspective projections + +```cpp +template +Mat<4,4,T,St> mat_perspective_left_handed (float fov_deg, float aspect, float near, float far) noexcept; + +template +Mat<4,4,T,St> mat_perspective_right_handed(float fov_deg, float aspect, float near, float far) noexcept; +``` + +### Orthographic projections + +```cpp +template +Mat<4,4,T,St> mat_ortho_left_handed (T left, T right, T bottom, T top, T near, T far) noexcept; + +template +Mat<4,4,T,St> mat_ortho_right_handed(T left, T right, T bottom, T top, T near, T far) noexcept; +``` + +### Look-at matrices + +```cpp +template +Mat<4,4,T,St> mat_look_at_left_handed (const Vector3& eye, + const Vector3& center, + const Vector3& up); + +template +Mat<4,4,T,St> mat_look_at_right_handed(const Vector3& eye, + const Vector3& center, + const Vector3& up); +``` + +--- + +## Screen-space helper + +```cpp +template +static constexpr Mat<4,4> to_screen_mat(const Type& screen_w, const Type& screen_h) noexcept; +// Maps NDC to screen space (origin top-left, y down) +``` + +--- + +## Examples + +### 1) Building a left-handed camera and perspective + +```cpp +using V3 = omath::Vector3; +using M4 = omath::Mat<4,4,float, omath::MatStoreType::COLUMN_MAJOR>; + +V3 eye{0, 1, -5}, center{0, 0, 0}, up{0, 1, 0}; +M4 view = omath::mat_look_at_left_handed(eye, center, up); + +float fov = 60.f, aspect = 16.f/9.f, n = 0.1f, f = 100.f; +M4 proj = omath::mat_perspective_left_handed(fov, aspect, n, f); + +// final VP +M4 vp = proj * view; +``` + +### 2) Inverting a transform safely + +```cpp +omath::Mat<4,4> T = omath::mat_translation(omath::Vector3{2,3,4}); +if (auto inv = T.inverted()) { + // use *inv +} else { + // handle non-invertible +} +``` + +### 3) Formatting for logs + +```cpp +omath::Mat<2,2> A = { {1,2},{3,4} }; +std::string s = A.to_string(); // "[[ 1.000, 2.000]\n [ 3.000, 4.000]]" +std::string f = std::format("A = {}", A); // uses std::formatter +``` + +--- + +## Performance + +* **Cache-friendly kernels** per storage order when AVX2 is not enabled. +* **AVX2 path** (`OMATH_USE_AVX2`) for `float`/`double` implements FMAs with 256-bit vectors for both row-major and column-major multiplication. +* Complexity for `A(R×K) * B(K×C)`: **O(RKC)** regardless of storage order. + +--- + +## Constraints & concepts + +```cpp +template +concept MatTemplateEqual = + (M1::rows == M2::rows) && + (M1::columns == M2::columns) && + std::is_same_v && + (M1::store_type == M2::store_type); +``` + +> Use this concept to constrain generic functions that operate on like-shaped matrices. + +--- + +## Exceptions + +* `std::invalid_argument` — initializer list dimensions mismatch. +* `std::out_of_range` — out-of-bounds in `at()` when bounds checking is active (see source guard). +* `inverted()` does **not** throw; returns `std::nullopt` if `determinant() == 0`. + +--- + +## Build switches + +* **`OMATH_USE_AVX2`** — enable AVX2 vectorized multiplication paths (`` required). +* **Debug checks** — the `at()` method contains a conditional range check; refer to the preprocessor guard in the code to enable/disable in your configuration. + +--- + +## Known requirements & interoperability + +* **C++23** is required for multi-parameter `operator[]`. If you target pre-C++23, use `at(r,c)` instead. +* All binary operations require matching `Type` and `StoreType`. Convert explicitly if needed. + +--- + +## See also + +* `omath::Vector3` +* Projection helpers: `mat_perspective_*`, `mat_ortho_*` +* View helpers: `mat_look_at_*`, `mat_camera_view` +* Construction helpers: `mat_row_from_vector`, `mat_column_from_vector`, `mat_translation`, `mat_rotation_axis_*` + +--- + +## Appendix: API summary (signatures) + +```cpp +// Core +Mat(); Mat(const Mat&); Mat(Mat&&); +Mat(std::initializer_list>); +explicit Mat(const Type* raw); +Mat& operator=(const Mat&); Mat& operator=(Mat&&); + +static constexpr size_t row_count(); +static constexpr size_t columns_count(); +static consteval MatSize size(); +static constexpr MatStoreType get_store_ordering(); + +T& at(size_t r, size_t c); +T const& at(size_t r, size_t c) const; +T& operator[](size_t r, size_t c); +T const& operator[](size_t r, size_t c) const; + +void clear(); +void set(const Type& v); +Type sum() const noexcept; + +template Mat operator*(const Mat&) const; +Mat& operator*=(const Type&); Mat operator*(const Type&) const; +Mat& operator/=(const Type&); Mat operator/(const Type&) const; + +Mat transposed() const noexcept; +Type determinant() const; // square only +std::optional inverted() const; // square only + +Mat strip(size_t r, size_t c) const; +Type minor(size_t r, size_t c) const; +Type alg_complement(size_t r, size_t c) const; + +auto& raw_array(); auto const& raw_array() const; +std::string to_string() const noexcept; +std::wstring to_wstring() const noexcept; +std::u8string to_u8string() const noexcept; + +bool operator==(const Mat&) const; +bool operator!=(const Mat&) const; + +// Helpers (see sections above) +``` + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/styles/dark-overrides.css b/docs/styles/dark-overrides.css new file mode 100644 index 00000000..eef24c46 --- /dev/null +++ b/docs/styles/dark-overrides.css @@ -0,0 +1,67 @@ +/* styles/dark-overrides.css */ +:root[data-md-color-scheme="mydark"] { + color-scheme: dark; /* important for native UI */ + --md-default-bg-color: #0d0f12; + --md-code-bg-color: #12151a; + --md-footer-bg-color: #0b0d10; + + --md-default-fg-color: #dfe5ef; + --md-typeset-color: #dfe5ef; + --md-default-fg-color--light: #aab2bf; + --md-code-fg-color: #e6edf3; + + --md-primary-fg-color: #7c4dff; + --md-accent-fg-color: #c6ff00; + + --md-default-fg-color--lighter: #2a2f36; + --md-shadow-z1: 0 2px 6px rgba(0,0,0,.35); +} + +/* Ensure the page chrome actually uses the bg (prevents white flashes/areas) */ +html[data-md-color-scheme="mydark"], +body[data-md-color-scheme="mydark"], +.md-header, .md-main, .md-footer, .md-sidebar { + background-color: #0d0f12 !important; +} + +/* Optional: code tokens */ +:root[data-md-color-scheme="mydark"] .md-typeset code, +:root[data-md-color-scheme="mydark"] .md-typeset pre { + --md-code-hl-keyword-color: #c678dd; + --md-code-hl-string-color: #98c379; + --md-code-hl-name-color: #61afef; + --md-code-hl-number-color: #d19a66; +} +/* Make default/body text white-ish in dark mode */ +:root[data-md-color-scheme="mydark"] { + --md-default-fg-color: #e6edf3; /* global text */ + --md-default-fg-color--light: #c5cfdb; + --md-default-fg-color--lighter: #9aa4b2; + --md-typeset-color: #e6edf3; /* content text */ +} + +/* Ensure it actually applies (beats theme selectors) */ +:root[data-md-color-scheme="mydark"] body, +:root[data-md-color-scheme="mydark"] .md-content, +:root[data-md-color-scheme="mydark"] .md-typeset { + color: var(--md-typeset-color) !important; +} + +/* Nav, header, footer text */ +:root[data-md-color-scheme="mydark"] .md-header, +:root[data-md-color-scheme="mydark"] .md-footer, +:root[data-md-color-scheme="mydark"] .md-nav, +:root[data-md-color-scheme="mydark"] .md-nav__link { + color: var(--md-default-fg-color) !important; +} + +/* Link color in articles (optional) */ +:root[data-md-color-scheme="mydark"] .md-typeset a { + color: var(--md-accent-fg-color); +} + +/* Inline code text (optional) */ +:root[data-md-color-scheme="mydark"] code, +:root[data-md-color-scheme="mydark"] pre { + color: var(--md-code-fg-color) !important; +} diff --git a/mkdocs.yml b/mkdocs.yml index c97182f5..91c36ada 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1 +1,3 @@ -site_name: My Docs +site_name: OM Docs +theme: + name: darkly \ No newline at end of file From 0510dd8328ae6b677f822b003882398338eae642 Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 31 Oct 2025 16:14:20 +0300 Subject: [PATCH 02/14] Adds documentation for Vector4 and incorporates ImGui integration Adds comprehensive documentation for the `Vector4` class, outlining constructors, operators, and utility functions. Includes detailed notes on `clamp` functionality and potential ImGui integration caveats. Incorporates optional ImGui integration with explanations for correct usage and potential improvements. --- docs/linear_algebra/triangle.md | 173 +++++++++++++++++++ docs/linear_algebra/vector2.md | 291 +++++++++++++++++++++++++++++++ docs/linear_algebra/vector3.md | 297 ++++++++++++++++++++++++++++++++ docs/linear_algebra/vector4.md | 253 +++++++++++++++++++++++++++ mkdocs.yml | 2 +- 5 files changed, 1015 insertions(+), 1 deletion(-) create mode 100644 docs/linear_algebra/triangle.md create mode 100644 docs/linear_algebra/vector2.md create mode 100644 docs/linear_algebra/vector3.md create mode 100644 docs/linear_algebra/vector4.md diff --git a/docs/linear_algebra/triangle.md b/docs/linear_algebra/triangle.md new file mode 100644 index 00000000..fc80f886 --- /dev/null +++ b/docs/linear_algebra/triangle.md @@ -0,0 +1,173 @@ +# `omath::Triangle` — Simple 3D triangle utility + +> Header: your project’s `triangle.hpp` +> Namespace: `omath` +> Depends on: `omath::Vector3` (from `vector3.hpp`) + +A tiny helper around three `Vector3` vertices with convenience methods for normals, edge vectors/lengths, a right-angle test (at **`v2`**), and the triangle centroid. + +> **Note on the template parameter** +> +> The class is declared as `template class Triangle`, but the stored vertices are concretely `Vector3`. In practice this type is currently **fixed to `Vector3`**. You can ignore the template parameter or refactor to store `Vector` if you intend true genericity. + +--- + +## Vertex layout & naming + +``` +v1 +|\ +| \ +a | \ hypot = |v1 - v3| +| \ +v2 -- v3 + b + +a = |v1 - v2| (side_a_length) +b = |v3 - v2| (side_b_length) +``` + +* **`side_a_vector()`** = `v1 - v2` (points from v2 → v1) +* **`side_b_vector()`** = `v3 - v2` (points from v2 → v3) +* **Right-angle check** uses `a² + b² ≈ hypot²` with an epsilon of `1e-4`. + +--- + +## Quick start + +```cpp +#include "triangle.hpp" +using omath::Vector3; +using omath::Triangle; + +Triangle tri( // template arg unused; any placeholder ok + Vector3{0,0,0}, // v1 + Vector3{0,0,1}, // v2 (right angle is tested at v2) + Vector3{1,0,1} // v3 +); + +auto n = tri.calculate_normal(); // unit normal (right-handed: (v3-v2) × (v1-v2)) +float a = tri.side_a_length(); // |v1 - v2| +float b = tri.side_b_length(); // |v3 - v2| +float hyp = tri.hypot(); // |v1 - v3| +bool rect = tri.is_rectangular(); // true if ~right triangle at v2 +auto C = tri.mid_point(); // centroid (average of v1,v2,v3) +``` + +--- + +## Data members + +```cpp +Vector3 m_vertex1; // v1 +Vector3 m_vertex2; // v2 (the corner tested by is_rectangular) +Vector3 m_vertex3; // v3 +``` + +--- + +## Constructors + +```cpp +constexpr Triangle() = default; +constexpr Triangle(const Vector3& v1, + const Vector3& v2, + const Vector3& v3); +``` + +--- + +## Methods + +```cpp +// Normal (unit) using right-handed cross product: +// n = (v3 - v2) × (v1 - v2), then normalized() +[[nodiscard]] constexpr Vector3 calculate_normal() const; + +// Edge lengths with the naming from the diagram +[[nodiscard]] float side_a_length() const; // |v1 - v2| +[[nodiscard]] float side_b_length() const; // |v3 - v2| + +// Edge vectors (from v2 to the other vertex) +[[nodiscard]] constexpr Vector3 side_a_vector() const; // v1 - v2 +[[nodiscard]] constexpr Vector3 side_b_vector() const; // v3 - v2 + +// Hypotenuse length between v1 and v3 +[[nodiscard]] constexpr float hypot() const; // |v1 - v3| + +// Right-triangle check at vertex v2 (Pythagoras with epsilon 1e-4) +[[nodiscard]] constexpr bool is_rectangular() const; + +// Centroid of the triangle (average of the 3 vertices) +[[nodiscard]] constexpr Vector3 mid_point() const; // actually the centroid +``` + +### Notes & edge cases + +* **Normal direction** follows the right-hand rule for the ordered vertices `{v2 → v3} × {v2 → v1}`. + Swap vertex order to flip the normal. +* **Degenerate triangles** (collinear or overlapping vertices) yield a **zero vector** normal (since `normalized()` of the zero vector returns the zero vector in your math types). +* **`mid_point()` is the centroid**, not the midpoint of any single edge. If you need the midpoint of edge `v1–v2`, use `(m_vertex1 + m_vertex2) * 0.5f`. + +--- + +## Examples + +### Area and plane from existing API + +```cpp +const auto a = tri.side_a_vector(); +const auto b = tri.side_b_vector(); +const auto n = b.cross(a); // unnormalized normal +float area = 0.5f * n.length(); // triangle area + +// Plane equation n̂·(x - v2) = 0 +auto nhat = n.length() > 0 ? n / n.length() : n; +float d = -nhat.dot(tri.m_vertex2); +``` + +### Project a point onto the triangle’s plane + +```cpp +Vector3 p{0.3f, 1.0f, 0.7f}; +auto n = tri.calculate_normal(); +float t = n.dot(tri.m_vertex2 - p); // signed distance along normal +auto projected = p + n * t; // on-plane projection +``` + +--- + +## API summary (signatures) + +```cpp +class Triangle final { +public: + constexpr Triangle(); + constexpr Triangle(const Vector3& v1, + const Vector3& v2, + const Vector3& v3); + + Vector3 m_vertex1, m_vertex2, m_vertex3; + + [[nodiscard]] constexpr Vector3 calculate_normal() const; + [[nodiscard]] float side_a_length() const; + [[nodiscard]] float side_b_length() const; + [[nodiscard]] constexpr Vector3 side_a_vector() const; + [[nodiscard]] constexpr Vector3 side_b_vector() const; + [[nodiscard]] constexpr float hypot() const; + [[nodiscard]] constexpr bool is_rectangular() const; + [[nodiscard]] constexpr Vector3 mid_point() const; +}; +``` + +--- + +## Suggestions (optional improvements) + +* If generic vectors are intended, store `Vector m_vertex*;` and constrain `Vector` to the required ops (`-`, `cross`, `normalized`, `distance_to`, `+`, `/`). +* Consider renaming `mid_point()` → `centroid()` to avoid ambiguity. +* Expose an `area()` helper and (optionally) a barycentric coordinate routine if you plan to use this in rasterization or intersection tests. + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/linear_algebra/vector2.md b/docs/linear_algebra/vector2.md new file mode 100644 index 00000000..483b1444 --- /dev/null +++ b/docs/linear_algebra/vector2.md @@ -0,0 +1,291 @@ +# `omath::Vector2` — 2D vector (C++20/23) + +> Header: your project’s `vector2.hpp` +> Namespace: `omath` +> Template: `template requires std::is_arithmetic_v` + +`Vector2` is a lightweight, POD-like 2D math type with arithmetic, geometry helpers, comparisons, hashing (for `float`), optional ImGui interop, and `std::formatter` support. + +--- + +## Quick start + +```cpp +#include "vector2.hpp" +using omath::Vector2; + +using Vec2f = Vector2; + +Vec2f a{3.f, 4.f}; +Vec2f b{1.f, 2.f}; + +auto d = a.distance_to(b); // ≈ 3.1623 +auto dot = a.dot(b); // 11 +auto len = a.length(); // 5 +auto unit_a = a.normalized(); // (0.6, 0.8) + +// Component-wise mutate +Vec2f c{2, 3}; +c *= b; // c -> (2*1, 3*2) = (2, 6) + +// Scalar ops (non-mutating + mutating) +auto scaled = a * 0.5f; // (1.5, 2) +a *= 2.f; // (6, 8) + +// Ordering by length() +bool shorter = (b < a); + +// Formatted printing +std::string s = std::format("a = {}", a); // "a = [6, 8]" +``` + +--- + +## Members + +```cpp +Type x{0}; +Type y{0}; +``` + +--- + +## Constructors + +```cpp +constexpr Vector2(); // (0,0) +constexpr Vector2(const Type& x, const Type& y) noexcept; +``` + +--- + +## Equality & ordering + +```cpp +constexpr bool operator==(const Vector2&) const noexcept; // component-wise equality +constexpr bool operator!=(const Vector2&) const noexcept; + +bool operator< (const Vector2&) const noexcept; // compares by length() +bool operator> (const Vector2&) const noexcept; +bool operator<=(const Vector2&) const noexcept; +bool operator>=(const Vector2&) const noexcept; +``` + +> **Note:** `<`, `>`, `<=`, `>=` order vectors by **magnitude** (not lexicographically). + +--- + +## Arithmetic + +### With another vector (component-wise, **mutating**) + +```cpp +Vector2& operator+=(const Vector2&) noexcept; +Vector2& operator-=(const Vector2&) noexcept; +Vector2& operator*=(const Vector2&) noexcept; // Hadamard product (x*=x, y*=y) +Vector2& operator/=(const Vector2&) noexcept; +``` + +> Non-mutating `v * u` / `v / u` (vector × vector) are **not** provided. +> Use `v *= u` (mutating) or build a new vector explicitly. + +### With a scalar + +```cpp +Vector2& operator*=(const Type& v) noexcept; +Vector2& operator/=(const Type& v) noexcept; +Vector2& operator+=(const Type& v) noexcept; +Vector2& operator-=(const Type& v) noexcept; + +constexpr Vector2 operator*(const Type& v) const noexcept; +constexpr Vector2 operator/(const Type& v) const noexcept; +``` + +### Binary (+/−) with another vector (non-mutating) + +```cpp +constexpr Vector2 operator+(const Vector2&) const noexcept; +constexpr Vector2 operator-(const Vector2&) const noexcept; +``` + +### Unary + +```cpp +constexpr Vector2 operator-() const noexcept; // negation +``` + +--- + +## Geometry & helpers + +```cpp +Type distance_to (const Vector2&) const noexcept; // sqrt of squared distance +constexpr Type distance_to_sqr(const Vector2&) const noexcept; + +constexpr Type dot(const Vector2&) const noexcept; + +#ifndef _MSC_VER +constexpr Type length() const noexcept; // uses std::hypot; constexpr on non-MSVC +constexpr Vector2 normalized() const noexcept; // returns *this if length==0 +#else +Type length() const noexcept; +Vector2 normalized() const noexcept; +#endif + +constexpr Type length_sqr() const noexcept; // x*x + y*y +Vector2& abs() noexcept; // component-wise absolute (constexpr-friendly impl) + +constexpr Type sum() const noexcept; // x + y +constexpr std::tuple as_tuple() const noexcept; +``` + +--- + +## ImGui integration (optional) + +Define `OMATH_IMGUI_INTEGRATION` **before** including the header. + +```cpp +#ifdef OMATH_IMGUI_INTEGRATION +constexpr ImVec2 to_im_vec2() const noexcept; // {float(x), float(y)} +static Vector2 from_im_vec2(const ImVec2&) noexcept; +#endif +``` + +--- + +## Hashing & formatting + +* **Hash (for `Vector2`)** + + ```cpp + template<> struct std::hash> { + std::size_t operator()(const omath::Vector2&) const noexcept; + }; + ``` + + Example: + + ```cpp + std::unordered_set> set; + set.insert({1.f, 2.f}); + ``` + +* **`std::formatter`** (for any `Type`) + + ```cpp + // prints "[x, y]" for char / wchar_t / char8_t + template + struct std::formatter>; + ``` + +--- + +## Notes & invariants + +* `Type` must be arithmetic (e.g., `float`, `double`, `int`, …). +* `normalized()` returns the input unchanged if `length() == 0`. +* `abs()` uses a constexpr-friendly implementation (not `std::abs`) to allow compile-time evaluation. +* On MSVC, `length()`/`normalized()` are not `constexpr` due to library constraints; they’re still `noexcept`. + +--- + +## Examples + +### Component-wise operations and scalar scaling + +```cpp +omath::Vector2 u{2, 3}, v{4, 5}; + +u += v; // (6, 8) +u -= v; // (2, 3) +u *= v; // (8, 15) Hadamard product (mutates u) +auto w = v * 2.0f; // (8, 10) non-mutating scalar multiply +``` + +### Geometry helpers + +```cpp +omath::Vector2 p{0.0, 0.0}, q{3.0, 4.0}; + +auto dsq = p.distance_to_sqr(q); // 25 +auto d = p.distance_to(q); // 5 +auto dot = p.dot(q); // 0 +auto uq = q.normalized(); // (0.6, 0.8) +``` + +### Using as a key in unordered containers (`float`) + +```cpp +std::unordered_map, int> counts; +counts[{1.f, 2.f}] = 42; +``` + +### ImGui interop + +```cpp +#define OMATH_IMGUI_INTEGRATION +#include "vector2.hpp" + +omath::Vector2 v{10, 20}; +ImVec2 iv = v.to_im_vec2(); +v = omath::Vector2::from_im_vec2(iv); +``` + +--- + +## API summary (signatures) + +```cpp +// Constructors +constexpr Vector2(); +constexpr Vector2(const Type& x, const Type& y) noexcept; + +// Equality & ordering +constexpr bool operator==(const Vector2&) const noexcept; +constexpr bool operator!=(const Vector2&) const noexcept; +bool operator< (const Vector2&) const noexcept; +bool operator> (const Vector2&) const noexcept; +bool operator<=(const Vector2&) const noexcept; +bool operator>=(const Vector2&) const noexcept; + +// Compound (vector/vector and scalar) +Vector2& operator+=(const Vector2&) noexcept; +Vector2& operator-=(const Vector2&) noexcept; +Vector2& operator*=(const Vector2&) noexcept; +Vector2& operator/=(const Vector2&) noexcept; +Vector2& operator*=(const Type&) noexcept; +Vector2& operator/=(const Type&) noexcept; +Vector2& operator+=(const Type&) noexcept; +Vector2& operator-=(const Type&) noexcept; + +// Non-mutating arithmetic +constexpr Vector2 operator+(const Vector2&) const noexcept; +constexpr Vector2 operator-(const Vector2&) const noexcept; +constexpr Vector2 operator*(const Type&) const noexcept; +constexpr Vector2 operator/(const Type&) const noexcept; +constexpr Vector2 operator-() const noexcept; + +// Geometry +Type distance_to(const Vector2&) const noexcept; +constexpr Type distance_to_sqr(const Vector2&) const noexcept; +constexpr Type dot(const Vector2&) const noexcept; +Type length() const noexcept; // constexpr on non-MSVC +Vector2 normalized() const noexcept; // constexpr on non-MSVC +constexpr Type length_sqr() const noexcept; +Vector2& abs() noexcept; +constexpr Type sum() const noexcept; +constexpr std::tuple as_tuple() const noexcept; + +// ImGui (optional) +#ifdef OMATH_IMGUI_INTEGRATION +constexpr ImVec2 to_im_vec2() const noexcept; +static Vector2 from_im_vec2(const ImVec2&) noexcept; +#endif + +// Hash (float) and formatter are specialized in the header +``` + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/linear_algebra/vector3.md b/docs/linear_algebra/vector3.md new file mode 100644 index 00000000..63479252 --- /dev/null +++ b/docs/linear_algebra/vector3.md @@ -0,0 +1,297 @@ +# `omath::Vector3` — 3D vector (C++20/23) + +> Header: your project’s `vector3.hpp` +> Namespace: `omath` +> Template: `template requires std::is_arithmetic_v` +> Depends on: `omath::Vector2` (base class), `omath::Angle` (for `angle_between`) +> C++: uses `std::expected` ⇒ **C++23** recommended (or a backport) + +`Vector3` extends `Vector2` with a `z` component and 3D operations: arithmetic, geometry (dot, cross, distance), normalization, angle-between with robust error signaling, hashing (for `float`) and `std::formatter` support. + +--- + +## Quick start + +```cpp +#include "vector3.hpp" +using omath::Vector3; + +using Vec3f = Vector3; + +Vec3f a{3, 4, 0}; +Vec3f b{1, 2, 2}; + +auto d = a.distance_to(b); // Euclidean distance +auto dot = a.dot(b); // 3*1 + 4*2 + 0*2 = 11 +auto cr = a.cross(b); // (8, -6, 2) +auto len = a.length(); // hypot(x,y,z) +auto unit = a.normalized(); // safe normalize (returns a if length==0) + +if (auto ang = a.angle_between(b)) { + float deg = ang->as_degrees(); // [0, 180], clamped +} else { + // vectors have zero length -> no defined angle +} +``` + +--- + +## Data members + +```cpp +Type x{0}; // inherited from Vector2 +Type y{0}; // inherited from Vector2 +Type z{0}; +``` + +--- + +## Constructors + +```cpp +constexpr Vector3() noexcept; +constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept; +``` + +--- + +## Equality & ordering + +```cpp +constexpr bool operator==(const Vector3&) const noexcept; // component-wise +constexpr bool operator!=(const Vector3&) const noexcept; + +bool operator< (const Vector3&) const noexcept; // compare by length() +bool operator> (const Vector3&) const noexcept; +bool operator<=(const Vector3&) const noexcept; +bool operator>=(const Vector3&) const noexcept; +``` + +> **Note:** Ordering uses **magnitude**, not lexicographic order. + +--- + +## Arithmetic (mutating) + +Component-wise with another vector: + +```cpp +Vector3& operator+=(const Vector3&) noexcept; +Vector3& operator-=(const Vector3&) noexcept; +Vector3& operator*=(const Vector3&) noexcept; // Hadamard product +Vector3& operator/=(const Vector3&) noexcept; +``` + +With a scalar: + +```cpp +Vector3& operator*=(const Type& v) noexcept; +Vector3& operator/=(const Type& v) noexcept; +Vector3& operator+=(const Type& v) noexcept; +Vector3& operator-=(const Type& v) noexcept; +``` + +--- + +## Arithmetic (non-mutating) + +```cpp +constexpr Vector3 operator-() const noexcept; +constexpr Vector3 operator+(const Vector3&) const noexcept; +constexpr Vector3 operator-(const Vector3&) const noexcept; +constexpr Vector3 operator*(const Vector3&) const noexcept; // Hadamard +constexpr Vector3 operator/(const Vector3&) const noexcept; // Hadamard +constexpr Vector3 operator*(const Type& scalar) const noexcept; +constexpr Vector3 operator/(const Type& scalar) const noexcept; +``` + +--- + +## Geometry & helpers + +```cpp +// Distances & lengths +Type distance_to(const Vector3&) const; // sqrt of squared distance +constexpr Type distance_to_sqr(const Vector3&) const noexcept; +#ifndef _MSC_VER +constexpr Type length() const; // hypot(x,y,z) +constexpr Type length_2d() const; // 2D length from base +constexpr Vector3 normalized() const; // returns *this if length==0 +#else +Type length() const noexcept; +Type length_2d() const noexcept; +Vector3 normalized() const noexcept; +#endif +constexpr Type length_sqr() const noexcept; + +// Products +constexpr Type dot(const Vector3&) const noexcept; +constexpr Vector3 cross(const Vector3&) const noexcept; // right-handed + +// Sums & tuples +constexpr Type sum() const noexcept; // x + y + z +constexpr Type sum_2d() const noexcept; // x + y +constexpr auto as_tuple() const noexcept -> std::tuple; + +// Utilities +Vector3& abs() noexcept; // component-wise absolute +``` + +--- + +## Angles & orthogonality + +```cpp +enum class Vector3Error { IMPOSSIBLE_BETWEEN_ANGLE }; + +// Angle in degrees, clamped to [0,180]. Error if any vector has zero length. +std::expected< + omath::Angle, + Vector3Error +> angle_between(const Vector3& other) const noexcept; + +bool is_perpendicular(const Vector3& other) const noexcept; // true if angle == 90° +``` + +--- + +## Hashing & formatting + +* **Hash (for `Vector3`)** + + ```cpp + template<> struct std::hash> { + std::size_t operator()(const omath::Vector3&) const noexcept; + }; + ``` + + Example: + + ```cpp + std::unordered_map, int> counts; + counts[{1.f, 2.f, 3.f}] = 7; + ``` + +* **`std::formatter`** (all character types) + + ```cpp + template + struct std::formatter>; // prints "[x, y, z]" + ``` + +--- + +## Error handling + +* `angle_between()` returns `std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE)` if either vector length is zero. +* Other operations are total for arithmetic `Type` (no throwing behavior in this class). + +--- + +## Examples + +### Cross product & perpendicular check + +```cpp +omath::Vector3 x{1,0,0}, y{0,1,0}; +auto z = x.cross(y); // (0,0,1) +bool perp = x.is_perpendicular(y); // true +``` + +### Safe normalization and angle + +```cpp +omath::Vector3 u{0,0,0}, v{1,1,0}; +auto nu = u.normalized(); // returns {0,0,0} +if (auto ang = u.angle_between(v)) { + // won't happen: u has zero length → error +} else { + // handle degenerate case +} +``` + +### Hadamard vs scalar multiply + +```cpp +omath::Vector3 a{2,3,4}, b{5,6,7}; +auto h = a * b; // (10, 18, 28) component-wise +auto s = a * 2.f; // (4, 6, 8) scalar +``` + +--- + +## API summary (signatures) + +```cpp +// Ctors +constexpr Vector3() noexcept; +constexpr Vector3(const Type& x, const Type& y, const Type& z) noexcept; + +// Equality & ordering +constexpr bool operator==(const Vector3&) const noexcept; +constexpr bool operator!=(const Vector3&) const noexcept; +bool operator< (const Vector3&) const noexcept; +bool operator> (const Vector3&) const noexcept; +bool operator<=(const Vector3&) const noexcept; +bool operator>=(const Vector3&) const noexcept; + +// Mutating arithmetic +Vector3& operator+=(const Vector3&) noexcept; +Vector3& operator-=(const Vector3&) noexcept; +Vector3& operator*=(const Vector3&) noexcept; +Vector3& operator/=(const Vector3&) noexcept; +Vector3& operator*=(const Type&) noexcept; +Vector3& operator/=(const Type&) noexcept; +Vector3& operator+=(const Type&) noexcept; +Vector3& operator-=(const Type&) noexcept; + +// Non-mutating arithmetic +constexpr Vector3 operator-() const noexcept; +constexpr Vector3 operator+(const Vector3&) const noexcept; +constexpr Vector3 operator-(const Vector3&) const noexcept; +constexpr Vector3 operator*(const Vector3&) const noexcept; +constexpr Vector3 operator/(const Vector3&) const noexcept; +constexpr Vector3 operator*(const Type&) const noexcept; +constexpr Vector3 operator/(const Type&) const noexcept; + +// Geometry +Type distance_to(const Vector3&) const; +constexpr Type distance_to_sqr(const Vector3&) const noexcept; +#ifndef _MSC_VER +constexpr Type length() const; +constexpr Type length_2d() const; +constexpr Vector3 normalized() const; +#else +Type length() const noexcept; +Type length_2d() const noexcept; +Vector3 normalized() const noexcept; +#endif +constexpr Type length_sqr() const noexcept; +constexpr Type dot(const Vector3&) const noexcept; +constexpr Vector3 cross(const Vector3&) const noexcept; + +Vector3& abs() noexcept; +constexpr Type sum() const noexcept; +constexpr Type sum_2d() const noexcept; +constexpr auto as_tuple() const noexcept -> std::tuple; + +// Angles +std::expected, omath::Vector3Error> +angle_between(const Vector3&) const noexcept; +bool is_perpendicular(const Vector3&) const noexcept; + +// Hash (float) and formatter specializations provided below the class +``` + +--- + +## Notes + +* Inherits all public API of `Vector2` (including `x`, `y`, many operators, and helpers used internally). +* `normalized()` returns the original vector if its length is zero (no NaNs). +* `cross()` uses the standard right-handed definition. +* `length()`/`normalized()` are `constexpr` on non-MSVC; MSVC builds provide `noexcept` runtime versions. + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/linear_algebra/vector4.md b/docs/linear_algebra/vector4.md new file mode 100644 index 00000000..833fa736 --- /dev/null +++ b/docs/linear_algebra/vector4.md @@ -0,0 +1,253 @@ +# `omath::Vector4` — 4D vector (C++20/23) + +> Header: your project’s `vector4.hpp` +> Namespace: `omath` +> Template: `template requires std::is_arithmetic_v` +> Inherits: `omath::Vector3` (brings `x`, `y`, `z` and most scalar ops) + +`Vector4` extends `Vector3` with a `w` component and 4D operations: component-wise arithmetic, scalar ops, dot/length helpers, clamping, hashing (for `float`) and `std::formatter` support. Optional ImGui interop is available behind a macro. + +--- + +## Quick start + +```cpp +#include "vector4.hpp" +using omath::Vector4; + +using Vec4f = Vector4; + +Vec4f a{1, 2, 3, 1}; +Vec4f b{4, 5, 6, 2}; + +// Component-wise & scalar ops +auto c = a + b; // (5, 7, 9, 3) +c *= 0.5f; // (2.5, 3.5, 4.5, 1.5) +auto h = a * b; // Hadamard: (4, 10, 18, 2) + +// Dot / length +float d = a.dot(b); // 1*4 + 2*5 + 3*6 + 1*2 = 32 +float L = a.length(); // sqrt(x²+y²+z²+w²) + +// Clamp (x,y,z only; see notes) +Vec4f col{1.4f, -0.2f, 0.7f, 42.f}; +col.clamp(0.f, 1.f); // -> (1, 0, 0.7, w unchanged) +``` + +--- + +## Data members + +```cpp +// Inherited from Vector3: +Type x{0}; +Type y{0}; +Type z{0}; + +// Added in Vector4: +Type w{0}; +``` + +--- + +## Constructors + +```cpp +constexpr Vector4() noexcept; // (0,0,0,0) +constexpr Vector4(const Type& x, const Type& y, + const Type& z, const Type& w); // value-init +``` + +--- + +## Equality & ordering + +```cpp +constexpr bool operator==(const Vector4&) const noexcept; // component-wise +constexpr bool operator!=(const Vector4&) const noexcept; + +bool operator< (const Vector4&) const noexcept; // compare by length() +bool operator> (const Vector4&) const noexcept; +bool operator<=(const Vector4&) const noexcept; +bool operator>=(const Vector4&) const noexcept; +``` + +> **Note:** Ordering uses **magnitude** (Euclidean norm), not lexicographic order. + +--- + +## Arithmetic (mutating) + +With another vector (component-wise): + +```cpp +Vector4& operator+=(const Vector4&) noexcept; +Vector4& operator-=(const Vector4&) noexcept; +Vector4& operator*=(const Vector4&) noexcept; // Hadamard +Vector4& operator/=(const Vector4&) noexcept; +``` + +With a scalar: + +```cpp +Vector4& operator*=(const Type& v) noexcept; +Vector4& operator/=(const Type& v) noexcept; + +// From base class (inherited): +Vector4& operator+=(const Type& v) noexcept; // adds v to x,y,z (and w via base? see notes) +Vector4& operator-=(const Type& v) noexcept; +``` + +--- + +## Arithmetic (non-mutating) + +```cpp +constexpr Vector4 operator-() const noexcept; +constexpr Vector4 operator+(const Vector4&) const noexcept; +constexpr Vector4 operator-(const Vector4&) const noexcept; +constexpr Vector4 operator*(const Vector4&) const noexcept; // Hadamard +constexpr Vector4 operator/(const Vector4&) const noexcept; // Hadamard +constexpr Vector4 operator*(const Type& scalar) const noexcept; +constexpr Vector4 operator/(const Type& scalar) const noexcept; +``` + +--- + +## Geometry & helpers + +```cpp +constexpr Type length_sqr() const noexcept; // x² + y² + z² + w² +Type length() const noexcept; // std::sqrt(length_sqr()) +constexpr Type dot(const Vector4& rhs) const noexcept; + +Vector4& abs() noexcept; // component-wise absolute +Vector4& clamp(const Type& min, const Type& max) noexcept; + // clamps x,y,z; leaves w unchanged (see notes) +constexpr Type sum() const noexcept; // x + y + z + w +``` + +--- + +## ImGui integration (optional) + +Guarded by `OMATH_IMGUI_INTEGRATION`: + +```cpp +#ifdef OMATH_IMGUI_INTEGRATION +constexpr ImVec4 to_im_vec4() const noexcept; +// NOTE: Provided signature returns Vector4 and (in current code) sets only x,y,z. +// See "Notes & caveats" for a corrected version you may prefer. +static Vector4 from_im_vec4(const ImVec4& other) noexcept; +#endif +``` + +--- + +## Hashing & formatting + +* **Hash specialization** (only for `Vector4`): + + ```cpp + template<> struct std::hash> { + std::size_t operator()(const omath::Vector4&) const noexcept; + }; + ``` + + Example: + + ```cpp + std::unordered_map, int> counts; + counts[{1.f, 2.f, 3.f, 1.f}] = 7; + ``` + +* **`std::formatter`** (for any `Type`, all character kinds): + + ```cpp + template + struct std::formatter>; // -> "[x, y, z, w]" + ``` + +--- + +## Notes & caveats (as implemented) + +* `clamp(min,max)` **clamps only `x`, `y`, `z`** and **does not clamp `w`**. This may be intentional (e.g., when `w` is a homogeneous coordinate) — document your intent in your codebase. + If you want to clamp `w` too: + + ```cpp + w = std::clamp(w, min, max); + ``` + +* **ImGui interop**: + + * The header references `ImVec4` but does not include `` itself. Ensure it’s included **before** this header whenever `OMATH_IMGUI_INTEGRATION` is defined. + * `from_im_vec4` currently returns `Vector4` and (in the snippet shown) initializes **only x,y,z**. A more consistent version would be: + + ```cpp + #ifdef OMATH_IMGUI_INTEGRATION + static Vector4 from_im_vec4(const ImVec4& v) noexcept { + return {static_cast(v.x), static_cast(v.y), + static_cast(v.z), static_cast(v.w)}; + } + #endif + ``` + +* Many scalar compound operators (`+= Type`, `-= Type`) are inherited from `Vector3`. + +--- + +## API summary (signatures) + +```cpp +// Ctors +constexpr Vector4() noexcept; +constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w); + +// Equality & ordering +constexpr bool operator==(const Vector4&) const noexcept; +constexpr bool operator!=(const Vector4&) const noexcept; +bool operator< (const Vector4&) const noexcept; +bool operator> (const Vector4&) const noexcept; +bool operator<=(const Vector4&) const noexcept; +bool operator>=(const Vector4&) const noexcept; + +// Mutating arithmetic +Vector4& operator+=(const Vector4&) noexcept; +Vector4& operator-=(const Vector4&) noexcept; +Vector4& operator*=(const Vector4&) noexcept; +Vector4& operator/=(const Vector4&) noexcept; +Vector4& operator*=(const Type&) noexcept; +Vector4& operator/=(const Type&) noexcept; +// (inherited) Vector4& operator+=(const Type&) noexcept; +// (inherited) Vector4& operator-=(const Type&) noexcept; + +// Non-mutating arithmetic +constexpr Vector4 operator-() const noexcept; +constexpr Vector4 operator+(const Vector4&) const noexcept; +constexpr Vector4 operator-(const Vector4&) const noexcept; +constexpr Vector4 operator*(const Vector4&) const noexcept; +constexpr Vector4 operator/(const Vector4&) const noexcept; +constexpr Vector4 operator*(const Type&) const noexcept; +constexpr Vector4 operator/(const Type&) const noexcept; + +// Geometry & helpers +constexpr Type length_sqr() const noexcept; +Type length() const noexcept; +constexpr Type dot(const Vector4&) const noexcept; +Vector4& abs() noexcept; +Vector4& clamp(const Type& min, const Type& max) noexcept; +constexpr Type sum() const noexcept; + +// ImGui (optional) +#ifdef OMATH_IMGUI_INTEGRATION +constexpr ImVec4 to_im_vec4() const noexcept; +static Vector4 from_im_vec4(const ImVec4&) noexcept; // see note for preferred template version +#endif + +// Hash (float) and formatter specializations provided below the class +``` + +--- + +*Last updated: 31 Oct 2025* diff --git a/mkdocs.yml b/mkdocs.yml index 91c36ada..08deb725 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,3 +1,3 @@ -site_name: OM Docs +site_name: OMATH Docs theme: name: darkly \ No newline at end of file From 56505cf3e13c851af204999fb21f1e73a5c071af Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 31 Oct 2025 16:17:32 +0300 Subject: [PATCH 03/14] Adds documentation for ray-triangle intersection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents the `omath::collision::Ray` and `LineTracer` types, detailing their purpose, usage, and API. Includes a description of the Möller–Trumbore algorithm and provides usage examples for segment-triangle and infinite ray intersection tests. Relates to feature/docs --- docs/collision/line_tracer.md | 171 ++++++++++++++++++++++++++++++++++ 1 file changed, 171 insertions(+) create mode 100644 docs/collision/line_tracer.md diff --git a/docs/collision/line_tracer.md b/docs/collision/line_tracer.md new file mode 100644 index 00000000..23cefccc --- /dev/null +++ b/docs/collision/line_tracer.md @@ -0,0 +1,171 @@ +# `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). + +--- + +*Last updated: 31 Oct 2025* From 9212b1f51f86ab06779926ebd7cf49f8ab025b0a Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 31 Oct 2025 16:25:56 +0300 Subject: [PATCH 04/14] Documents pattern scanning API Adds comprehensive documentation for the pattern scanning API. Details parsing behavior, complexity, and usage examples. Includes troubleshooting tips and minimal test sketches. Clarifies edge-case handling and implementation notes. --- docs/3d_primitives/box.md | 118 +++++++++++++++++++++ docs/3d_primitives/plane.md | 98 ++++++++++++++++++ docs/utility/color.md | 190 ++++++++++++++++++++++++++++++++++ docs/utility/pattern_scan.md | 194 +++++++++++++++++++++++++++++++++++ 4 files changed, 600 insertions(+) create mode 100644 docs/3d_primitives/box.md create mode 100644 docs/3d_primitives/plane.md create mode 100644 docs/utility/color.md create mode 100644 docs/utility/pattern_scan.md 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/utility/color.md b/docs/utility/color.md new file mode 100644 index 00000000..e4b864b0 --- /dev/null +++ b/docs/utility/color.md @@ -0,0 +1,190 @@ +# `omath::Color` — RGBA color with HSV helpers (C++20/23) + +> Header: your project’s `color.hpp` +> Namespace: `omath` +> Inherits: `Vector4` (`x=r`, `y=g`, `z=b`, `w=a`) +> Depends on: ``, `Vector4`, optionally ImGui (`OMATH_IMGUI_INTEGRATION`) +> Formatting: provides `std::formatter` + +`Color` is a tiny RGBA utility on top of `Vector4`. It offers sRGB-style channel construction, HSV↔RGB conversion, in-place HSV setters, linear blending, and string/formatter helpers. + +--- + +## Quick start + +```cpp +#include "color.hpp" +using omath::Color; + +// RGBA in [0,1] (r,g,b clamped to [0,1] on construction) +Color c{0.2f, 0.4f, 0.8f, 0.5f}; + +// From 8-bit channels +auto red = Color::from_rgba(255, 0, 0, 255); +auto green = Color::from_rgba(0, 255, 0, 160); + +// From HSV (h ∈ [0,1], s ∈ [0,1], v ∈ [0,1]) +auto cyan = Color::from_hsv(0.5f, 1.0f, 1.0f); // a = 1 + +// Read/modify via HSV +auto hsv = cyan.to_hsv(); // hue ∈ [0,1], saturation ∈ [0,1], value ∈ [0,1] +cyan.set_value(0.6f); // converts back to RGB (alpha becomes 1) + +// Blend linearly (lerp) +auto mid = red.blend(green, 0.5f); + +// Printable (0–255 per channel) +std::string s = std::format("{}", mid); // "[r:128, g:128, b:0, a:207]" for example +``` + +--- + +## Data model + +* Inherits `Vector4`: + + * `x` = **red**, `y` = **green**, `z` = **blue**, `w` = **alpha**. +* Construction clamps **RGB** to `[0,1]` (via `Vector4::clamp(0,1)`), **alpha is not clamped** by that call (see notes). + +--- + +## Construction & factories + +```cpp +// RGBA in [0,1] (RGB clamped to [0,1]; alpha untouched by clamp) +constexpr Color(float r, float g, float b, float a) noexcept; + +// Default +constexpr Color() noexcept; + +// From 8-bit RGBA (0–255) → normalized to [0,1] +constexpr static Color from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept; + +// From HSV where hue ∈ [0,1], saturation ∈ [0,1], value ∈ [0,1] +struct Hsv { float hue{}, saturation{}, value{}; }; + +constexpr static Color from_hsv(float hue, float saturation, float value) noexcept; +constexpr static Color from_hsv(const Hsv& hsv) noexcept; // delegates to the above + +// Construct from a Vector4 (RGB clamped, alpha not clamped) +constexpr explicit Color(const Vector4& vec) noexcept; +``` + +**HSV details** + +* `from_hsv(h, s, v)`: `h` is **normalized** (`[0,1]`); it is clamped, then mapped to the 6 hue sectors; **alpha = 1.0**. +* `to_hsv()`: returns `Hsv{h,s,v}` with **`h ∈ [0,1]`** (internally computes degrees and divides by 360), `s,v ∈ [0,1]`. + +--- + +## Mutators + +```cpp +constexpr void set_hue(float h) noexcept; // h ∈ [0,1] recommended +constexpr void set_saturation(float s) noexcept; // s ∈ [0,1] +constexpr void set_value(float v) noexcept; // v ∈ [0,1] + +// Linear blend: (1-ratio)*this + ratio*other, ratio clamped to [0,1] +constexpr Color blend(const Color& other, float ratio) const noexcept; +``` + +> ⚠️ **Alpha reset on HSV setters:** each `set_*` converts HSV→RGB using `from_hsv(...)`, which **sets alpha to 1.0** (overwriting previous `w`). If you need to preserve alpha: +> +> ```cpp +> float a = col.w; +> col.set_value(0.5f); +> col.w = a; +> ``` + +--- + +## Constants + +```cpp +static constexpr Color red(); // (1,0,0,1) +static constexpr Color green(); // (0,1,0,1) +static constexpr Color blue(); // (0,0,1,1) +``` + +--- + +## String & formatting + +```cpp +// "[r:R, g:G, b:B, a:A]" with each channel shown as 0–255 integer +std::string to_string() const noexcept; +std::wstring to_wstring() const noexcept; +std::u8string to_u8string() const noexcept; + +// Formatter forwards to the above (char/wchar_t/char8_t) +template<> struct std::formatter; +``` + +--- + +## ImGui (optional) + +```cpp +#ifdef OMATH_IMGUI_INTEGRATION +ImColor to_im_color() const noexcept; // constructs from Vector4's to_im_vec4() +#endif +``` + +Ensure `` is included somewhere before this header when the macro is enabled. + +--- + +## Notes & caveats + +* **Alpha clamping:** `Vector4::clamp(min,max)` (called by `Color` ctors) clamps **x,y,z** only in the provided `Vector4` implementation; `w` is **left unchanged**. If you require strict `[0,1]` alpha, clamp it yourself: + + ```cpp + col.w = std::clamp(col.w, 0.0f, 1.0f); + ``` +* **HSV range:** The API consistently uses **normalized hue** (`[0,1]`). Convert degrees ↔ normalized as `h_norm = h_deg / 360.f`. +* **Blend space:** `blend` is a **linear** interpolation in RGBA; it is not perceptually uniform. + +--- + +## API summary + +```cpp +struct Hsv { float hue{}, saturation{}, value{}; }; + +class Color final : public Vector4 { +public: + constexpr Color(float r, float g, float b, float a) noexcept; + constexpr Color() noexcept; + constexpr explicit Color(const Vector4& vec) noexcept; + + static constexpr Color from_rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) noexcept; + static constexpr Color from_hsv(float hue, float saturation, float value) noexcept; + static constexpr Color from_hsv(const Hsv& hsv) noexcept; + + constexpr Hsv to_hsv() const noexcept; + + constexpr void set_hue(float h) noexcept; + constexpr void set_saturation(float s) noexcept; + constexpr void set_value(float v) noexcept; + + constexpr Color blend(const Color& other, float ratio) const noexcept; + + static constexpr Color red(); + static constexpr Color green(); + static constexpr Color blue(); + +#ifdef OMATH_IMGUI_INTEGRATION + ImColor to_im_color() const noexcept; +#endif + + std::string to_string() const noexcept; + std::wstring to_wstring() const noexcept; + std::u8string to_u8string() const noexcept; +}; + +// formatter provided +``` + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/utility/pattern_scan.md b/docs/utility/pattern_scan.md new file mode 100644 index 00000000..c1bc8833 --- /dev/null +++ b/docs/utility/pattern_scan.md @@ -0,0 +1,194 @@ +# `omath::PatternScanner` — Fast byte-pattern search with wildcards + +> Header: your project’s `pattern_scanner.hpp` +> Namespace: `omath` +> Core API: `scan_for_pattern(...)` (span or iterators) +> Errors: `PatternScanError::INVALID_PATTERN_STRING` (from `parse_pattern`) +> C++: uses `std::span`, `std::expected`, `std::byte` + +`PatternScanner` scans a contiguous byte range for a **hex pattern** that may include **wildcards**. It returns an iterator to the **first match** or the end iterator if not found (or if the pattern string is invalid). + +--- + +## Quick start + +```cpp +#include "pattern_scanner.hpp" +using omath::PatternScanner; + +std::vector buf = /* ... bytes ... */; +std::span s{buf.data(), buf.size()}; + +// Example pattern: "48 8B ?? ?? 89" (hex bytes with '?' as any byte) +auto it = PatternScanner::scan_for_pattern(s, "48 8B ?? ?? 89"); +if (it != s.end()) { + // Found at offset: + auto offset = static_cast(it - s.begin()); +} +``` + +Or with iterators: + +```cpp +auto it = PatternScanner::scan_for_pattern(buf.begin(), buf.end(), "DE AD BE EF"); +if (it != buf.end()) { /* ... */ } +``` + +--- + +## Pattern string grammar + +The pattern string is parsed into a sequence of byte **tokens**: + +* **Hex byte**: two hexadecimal digits form one byte. + Examples: `90`, `4F`, `00`, `ff` +* **Wildcard byte**: `?` or `??` matches **any single byte**. +* **Separators**: any ASCII whitespace (space, tab, newline) is **ignored** and may be used to group tokens. + +> ✔️ Valid: `"48 8B ?? 05 00"`, `"90 90 90"`, `"??"`, `"00??FF"` (no spaces) +> ❌ Invalid: odd number of hex digits in a token, non-hex characters (besides `?` and whitespace) + +If the string cannot be parsed into a clean sequence of tokens, `parse_pattern()` returns `std::unexpected(PatternScanError::INVALID_PATTERN_STRING)`, and the public scan function returns **end**. + +--- + +## API + +```cpp +namespace omath { + +enum class PatternScanError { INVALID_PATTERN_STRING }; + +class PatternScanner final { +public: + // Contiguous range (span) overload + [[nodiscard]] + static std::span::iterator + scan_for_pattern(const std::span& range, + const std::string_view& pattern); + + // Deleted rvalue-span overload (prevents dangling) + static std::span::iterator + scan_for_pattern(std::span&&, + const std::string_view&) = delete; + + // Iterator overload + template + requires std::input_or_output_iterator> + static IteratorType + scan_for_pattern(const IteratorType& begin, + const IteratorType& end, + const std::string_view& pattern); +private: + [[nodiscard]] + static std::expected>, PatternScanError> + parse_pattern(const std::string_view& pattern_string); +}; + +} // namespace omath +``` + +### Return value + +* On success: iterator to the **first** matching position. +* On failure / not found / invalid pattern: **`end`**. + +--- + +## Complexity + +Let `N` be the number of bytes in the range and `M` the number of **pattern tokens**. + +* Time: **O(N × M)** (simple sliding window with early break). +* Space: **O(M)** for the parsed pattern vector. + +--- + +## Examples + +### Find a function prologue + +```cpp +// x86-64: push rbp; mov rbp, rsp +auto it = PatternScanner::scan_for_pattern(s, "55 48 89 E5"); +if (it != s.end()) { + // ... process +} +``` + +### Skip variable bytes with wildcards + +```cpp +// mov rax, ; call +auto it = PatternScanner::scan_for_pattern(s, "48 B8 ?? ?? ?? ?? ?? ?? ?? ?? E8 ?? ?? ?? ??"); +``` + +### Iterator-based scan (subrange) + +```cpp +auto sub_begin = buf.begin() + 1024; +auto sub_end = buf.begin() + 4096; +auto it = PatternScanner::scan_for_pattern(sub_begin, sub_end, "DE AD ?? BE EF"); +``` + +--- + +## Behavior & edge cases + +* **Empty or too-short**: If the effective pattern token count `M` is `0` or `M > N`, the function returns `end`. +* **Wildcards**: Each `?`/`??` token matches **exactly one** byte. +* **No exceptions**: Invalid pattern → parsed as `unexpected`, public API returns `end`. +* **No ownership**: The span overload takes `const std::span&` and returns a **mutable iterator** into that span. You must ensure the underlying memory stays alive. The rvalue-span overload is **deleted** to prevent dangling. + +--- + +## Notes & caveats (implementation-sensitive) + +* Although the iterator overload is constrained with `std::input_or_output_iterator`, the implementation uses `*(begin + i + j)` and arithmetic on iterators. In practice this means you should pass **random-access / contiguous iterators** (e.g., from `std::vector` or `std::span`). Using non-random-access iterators would be ill-formed. +* The inner matching loop compares a parsed token (`std::optional`) to the candidate byte; `std::nullopt` is treated as a **wildcard** (always matches). +* **Implementation note:** the outer scan currently derives its scan bound from the **pattern string length**; conceptually it should use the **number of parsed tokens** (hex/wildcard bytes). If you plan to accept spaces (or other non-byte characters) in the pattern string, ensure the scan window uses the parsed token count to avoid false negatives near the end of the buffer. + +--- + +## Testing hooks + +The class befriends several unit tests: + +``` +unit_test_pattern_scan_read_test_Test +unit_test_pattern_scan_corner_case_1_Test +unit_test_pattern_scan_corner_case_2_Test +unit_test_pattern_scan_corner_case_3_Test +unit_test_pattern_scan_corner_case_4_Test +``` + +Use these to validate parsing correctness, wildcard handling, and boundary conditions. + +--- + +## Troubleshooting + +* **Always returns end**: verify the pattern string is valid hex/wildcards and that you’re scanning the intended subrange. Try a simpler pattern (e.g., a single known byte) to sanity-check. +* **Crashes or compile errors with iterator overload**: use iterators that support random access (e.g., from `std::vector`), or prefer the `std::span` overload. +* **Ambiguous wildcards**: this scanner treats `?` and `??` as **byte-wide** wildcards (not per-nibble). If you need nibble-level masks, extend `parse_pattern` to support patterns like `A?`/`?F` with bitmask matching. + +--- + +## Minimal unit test sketch + +```cpp +TEST(pattern, basic) { + std::array data{ + std::byte{0x48}, std::byte{0x8B}, std::byte{0x01}, std::byte{0x02}, + std::byte{0x89}, std::byte{0x50}, std::byte{0x90}, std::byte{0xCC} + }; + std::span s{data.data(), data.size()}; + auto it = omath::PatternScanner::scan_for_pattern(s, "48 8B ?? ?? 89"); + ASSERT_NE(it, s.end()); + EXPECT_EQ(static_cast(it - s.begin()), 0U); +} +``` + +--- + +*Last updated: 31 Oct 2025* From d12a2611b8ccbd7cb6979de004a6bbf2b513ba4c Mon Sep 17 00:00:00 2001 From: Orange Date: Fri, 31 Oct 2025 16:47:28 +0300 Subject: [PATCH 05/14] Moves images to improved documentation structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Moves various image files demonstrating libomath’s usage and featuring artwork from showcases to the improved `docs/images` structure to reorganize the project's documentation in a logical and maintainable fashion. This change ensures consistency and simplifies updating documentation assets. --- README.md | 12 +- {.github => docs}/images/banner.png | Bin .../images/logos/omath_logo_macro.png | Bin .../images/logos/omath_logo_mega.png | Bin .../images/logos/omath_logo_micro.png | Bin .../images/logos/omath_logo_nano.png | Bin {.github => docs}/images/showcase/apex.png | Bin {.github => docs}/images/showcase/cod_bo2.png | Bin {.github => docs}/images/showcase/cs2.jpeg | Bin {.github => docs}/images/showcase/tf2.jpg | Bin {.github => docs}/images/yt_previews/img.png | Bin docs/index.md | 143 ++++++++-------- docs/install.md | 68 ++++++++ docs/styles/center.css | 4 + docs/utility/pattern_scan.md | 2 +- docs/utility/pe_pattern_scan.md | 155 ++++++++++++++++++ mkdocs.yml | 4 +- 17 files changed, 312 insertions(+), 76 deletions(-) rename {.github => docs}/images/banner.png (100%) rename {.github => docs}/images/logos/omath_logo_macro.png (100%) rename {.github => docs}/images/logos/omath_logo_mega.png (100%) rename {.github => docs}/images/logos/omath_logo_micro.png (100%) rename {.github => docs}/images/logos/omath_logo_nano.png (100%) rename {.github => docs}/images/showcase/apex.png (100%) rename {.github => docs}/images/showcase/cod_bo2.png (100%) rename {.github => docs}/images/showcase/cs2.jpeg (100%) rename {.github => docs}/images/showcase/tf2.jpg (100%) rename {.github => docs}/images/yt_previews/img.png (100%) create mode 100644 docs/install.md create mode 100644 docs/styles/center.css create mode 100644 docs/utility/pe_pattern_scan.md diff --git a/README.md b/README.md index e8591b2d..11183887 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@
-![banner](.github/images/logos/omath_logo_macro.png) +![banner](docs/images/logos/omath_logo_macro.png) ![Static Badge](https://img.shields.io/badge/license-libomath-orange) ![GitHub contributors](https://img.shields.io/github/contributors/orange-cpp/omath) @@ -61,7 +61,7 @@ It provides the latest features, is highly customizable, has all for cheat devel
-[![Youtube Video](.github/images/yt_previews/img.png)](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ) +[![Youtube Video](docs/images/yt_previews/img.png)](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ)
@@ -88,10 +88,10 @@ It provides the latest features, is highly customizable, has all for cheat devel - [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 [INSTALL]: INSTALL.md [DOCUMENTATION]: http://libomath.org diff --git a/.github/images/banner.png b/docs/images/banner.png similarity index 100% rename from .github/images/banner.png rename to docs/images/banner.png diff --git a/.github/images/logos/omath_logo_macro.png b/docs/images/logos/omath_logo_macro.png similarity index 100% rename from .github/images/logos/omath_logo_macro.png rename to docs/images/logos/omath_logo_macro.png diff --git a/.github/images/logos/omath_logo_mega.png b/docs/images/logos/omath_logo_mega.png similarity index 100% rename from .github/images/logos/omath_logo_mega.png rename to docs/images/logos/omath_logo_mega.png diff --git a/.github/images/logos/omath_logo_micro.png b/docs/images/logos/omath_logo_micro.png similarity index 100% rename from .github/images/logos/omath_logo_micro.png rename to docs/images/logos/omath_logo_micro.png diff --git a/.github/images/logos/omath_logo_nano.png b/docs/images/logos/omath_logo_nano.png similarity index 100% rename from .github/images/logos/omath_logo_nano.png rename to docs/images/logos/omath_logo_nano.png diff --git a/.github/images/showcase/apex.png b/docs/images/showcase/apex.png similarity index 100% rename from .github/images/showcase/apex.png rename to docs/images/showcase/apex.png diff --git a/.github/images/showcase/cod_bo2.png b/docs/images/showcase/cod_bo2.png similarity index 100% rename from .github/images/showcase/cod_bo2.png rename to docs/images/showcase/cod_bo2.png diff --git a/.github/images/showcase/cs2.jpeg b/docs/images/showcase/cs2.jpeg similarity index 100% rename from .github/images/showcase/cs2.jpeg rename to docs/images/showcase/cs2.jpeg diff --git a/.github/images/showcase/tf2.jpg b/docs/images/showcase/tf2.jpg similarity index 100% rename from .github/images/showcase/tf2.jpg rename to docs/images/showcase/tf2.jpg diff --git a/.github/images/yt_previews/img.png b/docs/images/yt_previews/img.png similarity index 100% rename from .github/images/yt_previews/img.png rename to docs/images/yt_previews/img.png diff --git a/docs/index.md b/docs/index.md index 70faad52..61872d1c 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,68 +1,75 @@ -# Installation - -## Using vcpkg -**Note**: Support vcpkg for package management -1. Install [vcpkg](https://github.com/microsoft/vcpkg) -2. Run the following command to install the orange-math package: -``` -vcpkg install orange-math -``` -CMakeLists.txt -```cmake -find_package(omath CONFIG REQUIRED) -target_link_libraries(main PRIVATE omath::omath) -``` -For detailed commands on installing different versions and more information, please refer to Microsoft's [official instructions](https://learn.microsoft.com/en-us/vcpkg/get_started/overview). - -## Using xrepo -**Note**: Support xrepo for package management -1. Install [xmake](https://xmake.io/) -2. Run the following command to install the omath package: -``` -xrepo install omath -``` -xmake.lua -```xmake -add_requires("omath") -target("...") - add_packages("omath") -``` - -## Build from source using CMake -1. **Preparation** - - Install needed tools: cmake, clang, git, msvc (windows only). - - 1. **Linux:** - ```bash - sudo pacman -Sy cmake ninja clang git - ``` - 2. **MacOS:** - ```bash - brew install llvm git cmake ninja - ``` - 3. **Windows:** - - Install Visual Studio from [here](https://visualstudio.microsoft.com/downloads/) and Git from [here](https://git-scm.com/downloads). - - Use x64 Native Tools shell to execute needed commands down below. -2. **Clone the repository:** - ```bash - git clone https://github.com/orange-cpp/omath.git - ``` -3. **Navigate to the project directory:** - ```bash - cd omath - ``` -4. **Build the project using CMake:** - ```bash - cmake --preset windows-release -S . - cmake --build cmake-build/build/windows-release --target omath -j 6 - ``` - Use **\-\** preset to build suitable version for yourself. Like **windows-release** or **linux-release**. - - | Platform Name | Build Config | - |---------------|---------------| - | windows | release/debug | - | linux | release/debug | - | darwin | release/debug | +
+ +

+ omath banner +

+ + +

+ license: libomath + GitHub contributors + Top language + + CodeFactor + + GitHub Actions Workflow Status + + Vcpkg package + + GitHub forks + + Join us on Discord + + + Telegram + +

+
+OMath is a 100% independent, constexpr template blazingly fast math library that doesn't have legacy C++ code. + +It provides the latest features, is highly customizable, has all for cheat development, DirectX/OpenGL/Vulkan support, premade support for different game engines, much more constexpr stuff than in other libraries and more... + + +# 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 + +
+ +[![Youtube Video](images/yt_previews/img.png)](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ) + +
+ +![APEX Preview] + +
+ +![BO2 Preview] + +
+ +![CS2 Preview] + +
+ +![TF2 Preview] + +
+
+ + +[APEX Preview]: images/showcase/apex.png +[BO2 Preview]: images/showcase/cod_bo2.png +[CS2 Preview]: images/showcase/cs2.jpeg +[TF2 Preview]: images/showcase/tf2.jpg \ No newline at end of file diff --git a/docs/install.md b/docs/install.md new file mode 100644 index 00000000..4a3cd311 --- /dev/null +++ b/docs/install.md @@ -0,0 +1,68 @@ +# Installation + +## Using vcpkg +**Note**: Support vcpkg for package management +1. Install [vcpkg](https://github.com/microsoft/vcpkg) +2. Run the following command to install the orange-math package: +``` +vcpkg install orange-math +``` +CMakeLists.txt +```cmake +find_package(omath CONFIG REQUIRED) +target_link_libraries(main PRIVATE omath::omath) +``` +For detailed commands on installing different versions and more information, please refer to Microsoft's [official instructions](https://learn.microsoft.com/en-us/vcpkg/get_started/overview). + +## Using xrepo +**Note**: Support xrepo for package management +1. Install [xmake](https://xmake.io/) +2. Run the following command to install the omath package: +``` +xrepo install omath +``` +xmake.lua +```xmake +add_requires("omath") +target("...") + add_packages("omath") +``` + +## Build from source using CMake +1. **Preparation** + + Install needed tools: cmake, clang, git, msvc (windows only). + + 1. **Linux:** + ```bash + sudo pacman -Sy cmake ninja clang git + ``` + 2. **MacOS:** + ```bash + brew install llvm git cmake ninja + ``` + 3. **Windows:** + + Install Visual Studio from [here](https://visualstudio.microsoft.com/downloads/) and Git from [here](https://git-scm.com/downloads). + + Use x64 Native Tools shell to execute needed commands down below. +2. **Clone the repository:** + ```bash + git clone https://github.com/orange-cpp/omath.git + ``` +3. **Navigate to the project directory:** + ```bash + cd omath + ``` +4. **Build the project using CMake:** + ```bash + cmake --preset windows-release -S . + cmake --build cmake-build/build/windows-release --target omath -j 6 + ``` + Use **\-\** preset to build suitable version for yourself. Like **windows-release** or **linux-release**. + + | Platform Name | Build Config | + |---------------|---------------| + | windows | release/debug | + | linux | release/debug | + | darwin | release/debug | diff --git a/docs/styles/center.css b/docs/styles/center.css new file mode 100644 index 00000000..c1b0e2a3 --- /dev/null +++ b/docs/styles/center.css @@ -0,0 +1,4 @@ +/* docs/css/custom.css */ +.center-text { + text-align: center; +} \ No newline at end of file diff --git a/docs/utility/pattern_scan.md b/docs/utility/pattern_scan.md index c1bc8833..182c5b52 100644 --- a/docs/utility/pattern_scan.md +++ b/docs/utility/pattern_scan.md @@ -45,7 +45,7 @@ The pattern string is parsed into a sequence of byte **tokens**: * **Wildcard byte**: `?` or `??` matches **any single byte**. * **Separators**: any ASCII whitespace (space, tab, newline) is **ignored** and may be used to group tokens. -> ✔️ Valid: `"48 8B ?? 05 00"`, `"90 90 90"`, `"??"`, `"00??FF"` (no spaces) +> ✔️ Valid: `"48 8B ?? 05 00"`, `"90 90 90"`, `"??"` > ❌ Invalid: odd number of hex digits in a token, non-hex characters (besides `?` and whitespace) If the string cannot be parsed into a clean sequence of tokens, `parse_pattern()` returns `std::unexpected(PatternScanError::INVALID_PATTERN_STRING)`, and the public scan function returns **end**. diff --git a/docs/utility/pe_pattern_scan.md b/docs/utility/pe_pattern_scan.md new file mode 100644 index 00000000..026afa7a --- /dev/null +++ b/docs/utility/pe_pattern_scan.md @@ -0,0 +1,155 @@ +# `omath::PePatternScanner` — Scan PE images for byte patterns + +> Header: your project’s `pe_pattern_scanner.hpp` +> Namespace: `omath` +> Platform: **Windows / PE (Portable Executable) images** +> Depends on: ``, ``, `` +> Companion: works well with `omath::PatternScanner` (same pattern grammar) + +`PePatternScanner` searches **Portable Executable (PE)** binaries for a hex pattern (with wildcards). You can scan: + +* a **loaded module** in the current process, or +* a **PE file on disk** (by section name; defaults to **`.text`**). + +--- + +## Pattern string grammar (same as `PatternScanner`) + +* **Hex byte**: two hex digits → one byte (`90`, `4F`, `00`, `ff`). +* **Wildcard byte**: `?` or `??` matches **any byte**. +* **Whitespace**: ignored (use to group tokens). + +✔️ `"48 8B ?? ?? 89"`, `"55 8B EC"`, `"??"` +❌ odd digit counts, non-hex characters (besides `?` and whitespace) + +--- + +## API + +```cpp +namespace omath { + +struct PeSectionScanResult { + std::uint64_t virtual_base_addr; // RVA base of the scanned section (ImageBase + SectionRVA) + std::uint64_t raw_base_addr; // file offset (start of section in the file) + std::ptrdiff_t target_offset; // offset from section base to the first matched byte +}; + +class PePatternScanner final { +public: + // Scan a module already loaded in *this* process. + // module_base_address: HMODULE / ImageBase (e.g., from GetModuleHandle) + // Returns absolute address (process VA) of the first match, or nullopt. + static std::optional + scan_for_pattern_in_loaded_module(const void* module_base_address, + const std::string_view& pattern); + + // Scan a PE file on disk, by section name (default ".text"). + // Returns section bases (virtual + raw) and match offset within the section, or nullopt. + static std::optional + scan_for_pattern_in_file(const std::filesystem::path& path_to_file, + const std::string_view& pattern, + const std::string_view& target_section_name = ".text"); +}; + +} // namespace omath +``` + +--- + +## Return values + +* **Loaded module**: `std::optional` + + * `value()` = **process virtual address** (ImageBase + SectionRVA + match offset). + * `nullopt` = no match or parse/PE error. + +* **File scan**: `std::optional` + + * `virtual_base_addr` = **ImageBase + SectionRVA** of the scanned section (as if mapped). + * `raw_base_addr` = **file offset** of section start. + * `target_offset` = offset from the section base to the **first matched byte**. + * To get addresses: + + * **Virtual (RVA)** of hit = `virtual_base_addr + target_offset` + * **Raw file offset** of hit = `raw_base_addr + target_offset` + +--- + +## Usage examples + +### Scan a loaded module (current process) + +```cpp +#include +#include "pe_pattern_scanner.hpp" + +using omath::PePatternScanner; + +auto hMod = ::GetModuleHandleW(L"kernel32.dll"); +if (hMod) { + auto addr = PePatternScanner::scan_for_pattern_in_loaded_module( + hMod, "48 8B ?? ?? 89" // hex + wildcards + ); + if (addr) { + // Use the absolute process VA: + std::uintptr_t hit_va = *addr; + // ... + } +} +``` + +### Scan a PE file on disk (default section “.text”) + +```cpp +#include "pe_pattern_scanner.hpp" +using omath::PePatternScanner; + +auto res = PePatternScanner::scan_for_pattern_in_file( + R"(C:\Windows\System32\kernel32.dll)", "55 8B EC" +); +if (res) { + auto rva_hit = res->virtual_base_addr + res->target_offset; + auto raw_hit = res->raw_base_addr + res->target_offset; + // rva_hit: where it would be in memory; raw_hit: file offset +} +``` + +### Scan another section (e.g., “.rdata”) + +```cpp +auto res = PePatternScanner::scan_for_pattern_in_file( + "foo.dll", "48 8D 0D ?? ?? ?? ??", ".rdata" +); +``` + +--- + +## Notes & edge cases + +* **PE only**: these functions assume a valid **PE/COFF** layout. Non-PE files or corrupted headers yield `nullopt`. +* **Section name**: `scan_for_pattern_in_file` defaults to **`.text`**; pass a different name to target other sections. +* **Alignment & RVAs**: `virtual_base_addr` is computed from section headers (RVA aligned per section alignment). The returned “virtual” base is suitable for RVA math; the **process VA** returned by the “loaded module” API already includes the image base. +* **Architecture**: works for 32-bit and 64-bit PEs; `std::uintptr_t` size matches the build architecture. +* **Performance**: Pattern matching is **O(N × M)** (sliding window with wildcards). For large images, prefer scanning only necessary sections. +* **Wildcards**: Each `?` matches **one byte** (no nibble masks). If you need `A?`-style nibble wildcards, extend the parser (see `PatternScanner`). +* **Safety**: For loaded modules, you must have access to the memory; scanning read-only sections is fine, but never write. For file scans, ensure the file path is accessible. + +--- + +## Troubleshooting + +* **`nullopt`**: Verify the pattern (valid tokens), correct **section**, and that you’re scanning the intended module/file (check bitness and version). +* **“Loaded module” address math**: If you need an **offset from the module base**, compute `offset = hit_va - reinterpret_cast(module_base_address)`. +* **Multiple matches**: Only the **first** match is returned. If you need all matches, extend the implementation to continue scanning from `target_offset + 1`. + +--- + +## See also + +* `omath::PatternScanner` — raw buffer/iterator scanning with the same pattern grammar. +* `omath::Triangle`, `omath::Vector3` — math types used elsewhere in the library. + +--- + +*Last updated: 31 Oct 2025* diff --git a/mkdocs.yml b/mkdocs.yml index 08deb725..25349244 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,3 +1,5 @@ site_name: OMATH Docs theme: - name: darkly \ No newline at end of file + name: darkly +extra_css: + - styles/center.css \ No newline at end of file From 95c0873b8cdfd1fe7d384ff1c3f7ea2983a95f90 Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 1 Nov 2025 09:12:04 +0300 Subject: [PATCH 06/14] Documents view angle struct and related API Adds documentation for the `omath::ViewAngles` struct, clarifying its purpose, common usage patterns, and the definition of the types of pitch, yaw and roll. Also, adds short explanations of how to use ViewAngles and what tradeoffs exist between using raw float types and strongly typed Angle<> types. --- docs/linear_algebra/mat.md | 2 +- docs/pathfinding/a_star.md | 188 +++++++++++++ docs/pathfinding/navigation_mesh.md | 113 ++++++++ .../proj_pred_engine_avx2.md | 161 +++++++++++ .../proj_pred_engine_legacy.md | 184 ++++++++++++ docs/projectile_prediction/projectile.md | 96 +++++++ .../projectile_engine.md | 152 ++++++++++ docs/projectile_prediction/target.md | 70 +++++ docs/projection/camera.md | 261 ++++++++++++++++++ docs/projection/error_codes.md | 79 ++++++ docs/rev_eng/external_rev_object.md | 164 +++++++++++ docs/rev_eng/internal_rev_object.md | 142 ++++++++++ docs/trigonometry/angle.md | 165 +++++++++++ docs/trigonometry/angles.md | 107 +++++++ docs/trigonometry/view_angles.md | 87 ++++++ 15 files changed, 1970 insertions(+), 1 deletion(-) create mode 100644 docs/pathfinding/a_star.md create mode 100644 docs/pathfinding/navigation_mesh.md create mode 100644 docs/projectile_prediction/proj_pred_engine_avx2.md create mode 100644 docs/projectile_prediction/proj_pred_engine_legacy.md create mode 100644 docs/projectile_prediction/projectile.md create mode 100644 docs/projectile_prediction/projectile_engine.md create mode 100644 docs/projectile_prediction/target.md create mode 100644 docs/projection/camera.md create mode 100644 docs/projection/error_codes.md create mode 100644 docs/rev_eng/external_rev_object.md create mode 100644 docs/rev_eng/internal_rev_object.md create mode 100644 docs/trigonometry/angle.md create mode 100644 docs/trigonometry/angles.md create mode 100644 docs/trigonometry/view_angles.md diff --git a/docs/linear_algebra/mat.md b/docs/linear_algebra/mat.md index b54cc3be..8d1efd68 100644 --- a/docs/linear_algebra/mat.md +++ b/docs/linear_algebra/mat.md @@ -1,4 +1,4 @@ -# `omath::Mat` +# `omath::Mat` — Matrix class (C++20/23) > Header: your project’s `mat.hpp` (requires `vector3.hpp`) > Namespace: `omath` diff --git a/docs/pathfinding/a_star.md b/docs/pathfinding/a_star.md new file mode 100644 index 00000000..df44820c --- /dev/null +++ b/docs/pathfinding/a_star.md @@ -0,0 +1,188 @@ +# `omath::pathfinding::Astar` — Pathfinding over a navigation mesh + +> Header: your project’s `pathfinding/astar.hpp` +> Namespace: `omath::pathfinding` +> Inputs: start/end as `Vector3`, a `NavigationMesh` +> Output: ordered list of waypoints `std::vector>` + +`Astar` exposes a single public function, `find_path`, that computes a path of 3D waypoints on a navigation mesh. Internally it reconstructs the result with `reconstruct_final_path` from a closed set keyed by `Vector3`. + +--- + +## API + +```cpp +namespace omath::pathfinding { + +struct PathNode; // holds per-node search data (see "Expected PathNode fields") + +class Astar final { +public: + [[nodiscard]] + static std::vector> + find_path(const Vector3& start, + const Vector3& end, + const NavigationMesh& nav_mesh) noexcept; + +private: + [[nodiscard]] + static std::vector> + reconstruct_final_path( + const std::unordered_map, PathNode>& closed_list, + const Vector3& current) noexcept; +}; + +} // namespace omath::pathfinding +``` + +### Semantics + +* Returns a **polyline** of 3D points from `start` to `end`. +* If no path exists, the function typically returns an **empty vector** (behavior depends on implementation details; keep this contract in unit tests). + +--- + +## What `NavigationMesh` is expected to provide + +The header doesn’t constrain `NavigationMesh`, but for A* it commonly needs: + +* **Neighborhood queries**: given a position or node key → iterable neighbors. +* **Traversal cost**: `g(u,v)` (often Euclidean distance or edge weight). +* **Heuristic**: `h(x,end)` (commonly straight-line distance on the mesh). +* **Projection / snap**: the ability to map `start`/`end` to valid nodes/points on the mesh (if they are off-mesh). + +> If your `NavigationMesh` doesn’t directly expose these, `Astar::find_path` likely does the adapter work (snapping to the nearest convex polygon/portal nodes and expanding across adjacency). + +--- + +## Expected `PathNode` fields + +Although not visible here, `PathNode` typically carries: + +* `Vector3 parent;` — predecessor position or key for backtracking +* `float g;` — cost from `start` +* `float h;` — heuristic to `end` +* `float f;` — `g + h` + +`reconstruct_final_path(closed_list, current)` walks `parent` links from `current` back to the start, **reverses** the chain, and returns the path. + +--- + +## Heuristic & optimality + +* Use an **admissible** heuristic (never overestimates true cost) to keep A* optimal. + The usual choice is **Euclidean distance** in 3D: + + ```cpp + h(x, goal) = (goal - x).length(); + ``` +* For best performance, make it **consistent** (triangle inequality holds). Euclidean distance is consistent on standard navmeshes. + +--- + +## Complexity + +Let `V` be explored vertices (or portal nodes) and `E` the traversed edges. + +* With a binary heap open list: **O(E log V)** time, **O(V)** memory. +* With a d-ary heap or pairing heap you may reduce practical constants. + +--- + +## Typical usage + +```cpp +#include "omath/pathfinding/astar.hpp" +#include "omath/pathfinding/navigation_mesh.hpp" + +using omath::Vector3; +using omath::pathfinding::Astar; + +NavigationMesh nav = /* ... load/build mesh ... */; + +Vector3 start{2.5f, 0.0f, -1.0f}; +Vector3 goal {40.0f, 0.0f, 12.0f}; + +auto path = Astar::find_path(start, goal, nav); + +if (!path.empty()) { + // feed to your agent/renderer + for (const auto& p : path) { + // draw waypoint p or push to steering + } +} else { + // handle "no path" (e.g., unreachable or disconnected mesh) +} +``` + +--- + +## Notes & recommendations + +* **Start/end snapping**: If `start` or `end` are outside the mesh, decide whether to snap to the nearest polygon/portal or fail early. Keep this behavior consistent and document it where `NavigationMesh` is defined. +* **Numerical stability**: Prefer squared distances when only comparing (`dist2`) to avoid unnecessary `sqrt`. +* **Tie-breaking**: When `f` ties are common (grid-like graphs), bias toward larger `g` or smaller `h` to reduce zig-zagging. +* **Smoothing**: A* returns a polyline that may hug polygon edges. Consider: + + * **String pulling / Funnel algorithm** over the corridor of polygons to get a straightened path. + * **Raycast smoothing** (visibility checks) to remove redundant interior points. +* **Hashing `Vector3`**: Your repo defines `std::hash>`. Ensure equality/precision rules for using float keys are acceptable (or use discrete node IDs instead). + +--- + +## Testing checklist + +* Start/end on the **same polygon** → direct path of 2 points. +* **Disconnected components** → empty result. +* **Narrow corridors** → path stays inside. +* **Obstacles blocking** → no path. +* **Floating-point noise** → still reconstructs a valid chain from parents. + +--- + +## Minimal pseudo-implementation outline (for reference) + +```cpp +// Pseudocode only — matches the header’s intent +std::vector find_path(start, goal, mesh) { + auto [snode, gnode] = mesh.snap_to_nodes(start, goal); + OpenSet open; // min-heap by f + std::unordered_map closed; + + open.push({snode, g=0, h=heuristic(snode, gnode)}); + parents.clear(); + + while (!open.empty()) { + auto current = open.pop_min(); // node with lowest f + + if (current.pos == gnode.pos) + return reconstruct_final_path(closed, current.pos); + + for (auto [nbr, cost] : mesh.neighbors(current.pos)) { + float tentative_g = current.g + cost; + if (auto it = closed.find(nbr); it == closed.end() || tentative_g < it->second.g) { + closed[nbr] = { .parent = current.pos, + .g = tentative_g, + .h = heuristic(nbr, gnode.pos), + .f = tentative_g + heuristic(nbr, gnode.pos) }; + open.push(closed[nbr]); + } + } + } + return {}; // no path +} +``` + +--- + +## FAQ + +* **Why return `std::vector>` and not polygon IDs?** + Waypoints are directly usable by agents/steering and for rendering. If you also need the corridor (polygon chain), extend the API or `PathNode` to store it. + +* **Does `find_path` modify the mesh?** + No; it should be a read-only search over `NavigationMesh`. + +--- + +*Last updated: 31 Oct 2025* diff --git a/docs/pathfinding/navigation_mesh.md b/docs/pathfinding/navigation_mesh.md new file mode 100644 index 00000000..a03f6c59 --- /dev/null +++ b/docs/pathfinding/navigation_mesh.md @@ -0,0 +1,113 @@ +# `omath::pathfinding::NavigationMesh` — Lightweight vertex graph for A* + +> Header: your project’s `pathfinding/navigation_mesh.hpp` +> Namespace: `omath::pathfinding` +> Nodes: `Vector3` (3D points) +> Storage: adjacency map `unordered_map, std::vector>>` + +A minimal navigation mesh represented as a **vertex/edge graph**. Each vertex is a `Vector3` and neighbors are stored in an adjacency list. Designed to pair with `Astar::find_path`. + +--- + +## API + +```cpp +class NavigationMesh final { +public: + // Nearest graph vertex to an arbitrary 3D point. + // On success -> closest vertex; on failure -> std::string error (e.g., empty mesh). + [[nodiscard]] + std::expected, std::string> + get_closest_vertex(const Vector3& point) const noexcept; + + // Read-only neighbor list for a vertex key. + // If vertex is absent, implementation should return an empty list (see notes). + [[nodiscard]] + const std::vector>& + get_neighbors(const Vector3& vertex) const noexcept; + + // True if the graph has no vertices/edges. + [[nodiscard]] + bool empty() const; + + // Serialize/deserialize the graph (opaque binary). + [[nodiscard]] std::vector serialize() const noexcept; + void deserialize(const std::vector& raw) noexcept; + + // Public adjacency (vertex -> neighbors) + std::unordered_map, std::vector>> m_vertex_map; +}; +``` + +--- + +## Quick start + +```cpp +using omath::Vector3; +using omath::pathfinding::NavigationMesh; + +// Build a tiny mesh (triangle) +NavigationMesh nav; +nav.m_vertex_map[ {0,0,0} ] = { {1,0,0}, {0,0,1} }; +nav.m_vertex_map[ {1,0,0} ] = { {0,0,0}, {0,0,1} }; +nav.m_vertex_map[ {0,0,1} ] = { {0,0,0}, {1,0,0} }; + +// Query the closest node to an arbitrary point +auto q = nav.get_closest_vertex({0.3f, 0.0f, 0.2f}); +if (q) { + const auto& v = *q; + const auto& nbrs = nav.get_neighbors(v); + (void)nbrs; +} +``` + +--- + +## Semantics & expectations + +* **Nearest vertex** + `get_closest_vertex(p)` should scan known vertices and return the one with minimal Euclidean distance to `p`. If the mesh is empty, expect an error (`unexpected` with a message). + +* **Neighbors** + `get_neighbors(v)` returns the adjacency list for `v`. If `v` is not present, a conventional behavior is to return a **reference to a static empty vector** (since the API is `noexcept` and returns by reference). Verify in your implementation. + +* **Graph invariants** (recommended) + + * Neighbor links are **symmetric** for undirected navigation (if `u` has `v`, then `v` has `u`). + * No self-loops unless explicitly desired. + * Vertices are unique keys; hashing uses `std::hash>` (be mindful of floating-point equality). + +--- + +## Serialization + +* `serialize()` → opaque, implementation-defined binary of the current `m_vertex_map`. +* `deserialize(raw)` → restores the internal map from `raw`. + Keep versioning in mind if you evolve the format (e.g., add a header/magic/version). + +--- + +## Performance + +Let `V = m_vertex_map.size()` and `E = Σ|neighbors(v)|`. + +* `get_closest_vertex`: **O(V)** (linear scan) unless you back it with a spatial index (KD-tree, grid, etc.). +* `get_neighbors`: **O(1)** average (hash lookup). +* Memory: **O(V + E)**. + +--- + +## Usage notes + +* **Floating-point keys**: Using `Vector3` as an unordered_map key relies on your `std::hash>` and exact `operator==`. Avoid building meshes with numerically “close but not equal” duplicates; consider canonicalizing or using integer IDs if needed. +* **Pathfinding**: Pair with `Astar::find_path(start, end, nav)`; the A* heuristic can use straight-line distance between vertex positions. + +--- + +## Minimal test ideas + +* Empty mesh → `get_closest_vertex` returns error; `empty() == true`. +* Single vertex → nearest always that vertex; neighbors empty. +* Symmetric edges → `get_neighbors(a)` contains `b` and vice versa. +* Serialization round-trip preserves vertex/edge counts and neighbor lists. diff --git a/docs/projectile_prediction/proj_pred_engine_avx2.md b/docs/projectile_prediction/proj_pred_engine_avx2.md new file mode 100644 index 00000000..56969f71 --- /dev/null +++ b/docs/projectile_prediction/proj_pred_engine_avx2.md @@ -0,0 +1,161 @@ +# `omath::projectile_prediction::ProjPredEngineAvx2` — AVX2-accelerated ballistic aim solver + +> Header: your project’s `projectile_prediction/proj_pred_engine_avx2.hpp` +> Namespace: `omath::projectile_prediction` +> Inherits: `ProjPredEngineInterface` +> Depends on: `Vector3`, `Projectile`, `Target` +> CPU: Uses AVX2 when available; falls back to scalar elsewhere (fields are marked `[[maybe_unused]]` for non-x86/AVX2 builds). + +This engine computes a **world-space aim point** (and implicitly the firing **yaw/pitch**) to intersect a moving target under a **constant gravity** model and **constant muzzle speed**. It typically scans candidate times of flight and solves for the elevation (`pitch`) that makes the vertical and horizontal kinematics meet at the same time. + +--- + +## API + +```cpp +class ProjPredEngineAvx2 final : public ProjPredEngineInterface { +public: + [[nodiscard]] + std::optional> + maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const override; + + ProjPredEngineAvx2(float gravity_constant, + float simulation_time_step, + float maximum_simulation_time); + ~ProjPredEngineAvx2() override = default; + +private: + // Solve for pitch at a fixed time-of-flight t. + [[nodiscard]] + static std::optional + calculate_pitch(const Vector3& proj_origin, + const Vector3& target_pos, + float bullet_gravity, float v0, float time); + + // Tunables (may be unused on non-AVX2 builds) + [[maybe_unused]] const float m_gravity_constant; // |g| (e.g., 9.81) + [[maybe_unused]] const float m_simulation_time_step; // Δt (e.g., 1/240 s) + [[maybe_unused]] const float m_maximum_simulation_time; // Tmax (e.g., 3 s) +}; +``` + +### Parameters (constructor) + +* `gravity_constant` — magnitude of gravity (units consistent with your world, e.g., **m/s²**). +* `simulation_time_step` — Δt used to scan candidate intercept times. +* `maximum_simulation_time` — cap on time of flight; larger allows longer-range solutions but increases cost. + +### Return (solver) + +* `maybe_calculate_aim_point(...)` + + * **`Vector3`**: a world-space **aim point** yielding an intercept under the model. + * **`std::nullopt`**: no feasible solution (e.g., target receding too fast, out of range, or kinematics inconsistent). + +--- + +## How it solves (expected flow) + +1. **Predict target at time `t`** (constant-velocity model unless your `Target` carries more): + + ``` + T(t) = target.position + target.velocity * t + ``` +2. **Horizontal/vertical kinematics at fixed `t`** with muzzle speed `v0` and gravity `g`: + + * Let `Δ = T(t) - proj_origin`, `d = length(Δ.xz)`, `h = Δ.y`. + * Required initial components: + + ``` + cosθ = d / (v0 * t) + sinθ = (h + 0.5 * g * t^2) / (v0 * t) + ``` + * If `cosθ` ∈ [−1,1] and `sinθ` ∈ [−1,1] and `sin²θ + cos²θ ≈ 1`, then + + ``` + θ = atan2(sinθ, cosθ) + ``` + + That is what `calculate_pitch(...)` returns on success. +3. **Yaw** is the azimuth toward `Δ.xz`. +4. **Pick the earliest feasible `t`** in `[Δt, Tmax]` (scanned in steps of `Δt`; AVX2 batches several `t` at once). +5. **Return the aim point.** Common choices: + + * The **impact point** `T(t*)` (useful as a HUD marker), or + * A point along the **initial firing ray** at some convenient range using `(yaw, pitch)`; both are consistent—pick the convention your caller expects. + +> The private `calculate_pitch(...)` matches step **2** and returns `nullopt` if the trigonometric constraints are violated for that `t`. + +--- + +## AVX2 notes + +* On x86/x64 with AVX2, candidate times `t` can be evaluated **8 at a time** using FMA (great for dense scans). +* On ARM/ARM64 (no AVX2), code falls back to scalar math; the `[[maybe_unused]]` members acknowledge compilation without SIMD. + +--- + +## Usage example + +```cpp +using namespace omath::projectile_prediction; + +ProjPredEngineAvx2 solver( + /*gravity*/ 9.81f, + /*dt*/ 1.0f/240.0f, + /*Tmax*/ 3.0f +); + +Projectile proj; // fill: origin, muzzle_speed, etc. +Target tgt; // fill: position, velocity + +if (auto aim = solver.maybe_calculate_aim_point(proj, tgt)) { + // Aim your weapon at *aim and fire with muzzle speed proj.v0 + // If you need yaw/pitch explicitly, replicate the pitch solve and azimuth. +} else { + // No solution (out of envelope) — pick a fallback +} +``` + +--- + +## Edge cases & failure modes + +* **Zero or tiny `v0`** → no solution. +* **Target collinear & receding faster than `v0`** → no solution. +* **`t` constraints**: if viable solutions exist only beyond `Tmax`, you’ll get `nullopt`. +* **Geometric infeasibility** at a given `t` (e.g., `d > v0*t`) causes `calculate_pitch` to fail that sample. +* **Numerical tolerance**: check `sin²θ + cos²θ` against 1 with a small epsilon (e.g., `1e-3`). + +--- + +## Performance & tuning + +* Work is roughly `O(Nt)` where `Nt ≈ Tmax / Δt`. +* Smaller `Δt` → better accuracy, higher cost. With AVX2 you can afford smaller steps. +* If you frequently miss solutions **between** steps, consider: + + * **Coarse-to-fine**: coarse scan, then local refine around the best `t`. + * **Newton on time**: root-find `‖horizontal‖ − v0 t cosθ(t) = 0` shaped from the kinematics. + +--- + +## Testing checklist + +* **Stationary target** at same height → θ ≈ 0, aim point ≈ target. +* **Higher target** → positive pitch; **lower target** → negative pitch. +* **Perpendicular moving target** → feasible at moderate speeds. +* **Very fast receding target** → `nullopt`. +* **Boundary**: `d ≈ v0*Tmax` and `h` large → verify pass/fail around thresholds. + +--- + +## See also + +* `ProjPredEngineInterface` — base interface and general contract +* `Projectile`, `Target` — data carriers for solver inputs (speed, origin, position, velocity, etc.) + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projectile_prediction/proj_pred_engine_legacy.md b/docs/projectile_prediction/proj_pred_engine_legacy.md new file mode 100644 index 00000000..fca8b174 --- /dev/null +++ b/docs/projectile_prediction/proj_pred_engine_legacy.md @@ -0,0 +1,184 @@ +# `omath::projectile_prediction::ProjPredEngineLegacy` — Legacy trait-based aim solver + +> Header: `omath/projectile_prediction/proj_pred_engine_legacy.hpp` +> Namespace: `omath::projectile_prediction` +> Inherits: `ProjPredEngineInterface` +> Template param (default): `EngineTrait = source_engine::PredEngineTrait` +> Purpose: compute a world-space **aim point** to hit a (possibly moving) target using a **discrete time scan** and a **closed-form ballistic pitch** under constant gravity. + +--- + +## Overview + +`ProjPredEngineLegacy` is a portable, trait-driven projectile lead solver. At each simulation time step `t` it: + +1. **Predicts target position** with `EngineTrait::predict_target_position(target, t, g)`. +2. **Computes launch pitch** via a gravity-aware closed form (or a direct angle if gravity is zero). +3. **Validates** that a projectile fired with that pitch (and direct yaw) actually reaches the predicted target within a **distance tolerance** at time `t`. +4. On success, **returns an aim point** computed by `EngineTrait::calc_viewpoint_from_angles(...)`. + +If no time step yields a feasible solution up to `maximum_simulation_time`, returns `std::nullopt`. + +--- + +## API + +```cpp +template +requires PredEngineConcept +class ProjPredEngineLegacy final : public ProjPredEngineInterface { +public: + ProjPredEngineLegacy(float gravity_constant, + float simulation_time_step, + float maximum_simulation_time, + float distance_tolerance); + + [[nodiscard]] + std::optional> + maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const override; + +private: + // Closed-form ballistic pitch solver (internal) + std::optional + maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile, + const Vector3& target_position) const noexcept; + + bool is_projectile_reached_target(const Vector3& target_position, + const Projectile& projectile, + float pitch, float time) const noexcept; + + const float m_gravity_constant; + const float m_simulation_time_step; + const float m_maximum_simulation_time; + const float m_distance_tolerance; +}; +``` + +### Constructor parameters + +* `gravity_constant` — magnitude of gravity (e.g., `9.81f`), world units/s². +* `simulation_time_step` — Δt for the scan (e.g., `1/240.f`). +* `maximum_simulation_time` — search horizon in seconds. +* `distance_tolerance` — max allowed miss distance at time `t` to accept a solution. + +--- + +## Trait requirements (`PredEngineConcept`) + +Your `EngineTrait` must expose **noexcept** static methods with these signatures: + +```cpp +Vector3 predict_projectile_position(const Projectile&, float pitch_deg, float yaw_deg, + float time, float gravity) noexcept; + +Vector3 predict_target_position(const Target&, float time, float gravity) noexcept; + +float calc_vector_2d_distance(const Vector3& v) noexcept; // typically length in XZ plane +float get_vector_height_coordinate(const Vector3& v) noexcept; // typically Y + +Vector3 calc_viewpoint_from_angles(const Projectile&, Vector3 target, + std::optional maybe_pitch_deg) noexcept; + +float calc_direct_pitch_angle(const Vector3& from, const Vector3& to) noexcept; +float calc_direct_yaw_angle (const Vector3& from, const Vector3& to) noexcept; +``` + +> This design lets you adapt different game/physics conventions (axes, units, handedness) without changing the solver. + +--- + +## Algorithm details + +### Time scan + +For `t = 0 .. maximum_simulation_time` in steps of `simulation_time_step`: + +1. `T = EngineTrait::predict_target_position(target, t, g)` +2. `pitch = maybe_calculate_projectile_launch_pitch_angle(projectile, T)` + + * If `std::nullopt`: continue +3. `yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, T)` +4. `P = EngineTrait::predict_projectile_position(projectile, pitch, yaw, t, g)` +5. Accept if `|P - T| <= distance_tolerance` +6. Return `EngineTrait::calc_viewpoint_from_angles(projectile, T, pitch)` + +### Closed-form pitch (gravity on) + +Implements the classic ballistic formula (low-arc branch), where: + +* `v` = muzzle speed, +* `g` = `gravity_constant * projectile.m_gravity_scale`, +* `x` = horizontal (2D) distance to target, +* `y` = vertical offset to target. + +[ +\theta ;=; \arctan!\left(\frac{v^{2} ;-; \sqrt{v^{4}-g!\left(gx^{2}+2yv^{2}\right)}}{gx}\right) +] + +* If the **discriminant** ( v^{4}-g(gx^{2}+2yv^{2}) < 0 ) ⇒ **no real solution**. +* If `g == 0`, falls back to `EngineTrait::calc_direct_pitch_angle(...)`. +* Returns **degrees** (internally converts from radians). + +--- + +## Usage example + +```cpp +using namespace omath::projectile_prediction; + +ProjPredEngineLegacy solver( + /*gravity*/ 9.81f, + /*dt*/ 1.f / 240.f, + /*Tmax*/ 3.0f, + /*tol*/ 0.05f +); + +Projectile proj; // fill: m_origin, m_launch_speed, m_gravity_scale, etc. +Target tgt; // fill: position/velocity as required by your trait + +if (auto aim = solver.maybe_calculate_aim_point(proj, tgt)) { + // Drive your turret/reticle toward *aim +} else { + // No feasible intercept in the given horizon +} +``` + +--- + +## Behavior & edge cases + +* **Zero gravity or zero distance**: uses direct pitch toward the target. +* **Negative discriminant** in the pitch formula: returns `std::nullopt` for that time step. +* **Very small `x`** (horizontal distance): the formula’s denominator `gx` approaches zero; your trait’s direct pitch helper provides a stable fallback. +* **Tolerance**: `distance_tolerance` controls acceptance; tighten for accuracy, loosen for robustness. + +--- + +## Complexity & tuning + +* Time: **O(T)** where ( T \approx \frac{\text{maximum_simulation_time}}{\text{simulation_time_step}} ) + plus trait costs for prediction and angle math per step. +* Smaller `simulation_time_step` improves precision but increases runtime. +* If needed, do a **coarse-to-fine** search: coarse Δt scan, then refine around the best hit time. + +--- + +## Testing checklist + +* Stationary, level target → pitch ≈ 0 for short ranges; accepted within tolerance. +* Elevated/depressed targets → pitch positive/negative as expected. +* Receding fast target → unsolved within horizon ⇒ `nullopt`. +* Gravity scale = 0 → identical to straight-line solution. +* Near-horizon shots (large range, small arc) → discriminant near zero; verify stability. + +--- + +## Notes + +* All angles produced/consumed by the trait in this implementation are **degrees**. +* `calc_viewpoint_from_angles` defines what “aim point” means in your engine (e.g., a point along the initial ray or the predicted impact point). Keep this consistent with your HUD/reticle. + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projectile_prediction/projectile.md b/docs/projectile_prediction/projectile.md new file mode 100644 index 00000000..a741a88d --- /dev/null +++ b/docs/projectile_prediction/projectile.md @@ -0,0 +1,96 @@ +# `omath::projectile_prediction::Projectile` — Projectile parameters for aim solvers + +> Header: `omath/projectile_prediction/projectile.hpp` +> Namespace: `omath::projectile_prediction` +> Used by: `ProjPredEngineInterface` implementations (e.g., `ProjPredEngineLegacy`, `ProjPredEngineAvx2`) + +`Projectile` is a tiny data holder that describes how a projectile is launched: **origin** (world position), **launch speed**, and a **gravity scale** (multiplier applied to the engine’s gravity constant). + +--- + +## API + +```cpp +namespace omath::projectile_prediction { + +class Projectile final { +public: + Vector3 m_origin; // Launch position (world space) + float m_launch_speed{}; // Initial speed magnitude (units/sec) + float m_gravity_scale{}; // Multiplier for global gravity (dimensionless) +}; + +} // namespace omath::projectile_prediction +``` + +--- + +## Field semantics + +* **`m_origin`** + World-space position where the projectile is spawned (e.g., muzzle or emitter point). + +* **`m_launch_speed`** + Initial speed **magnitude** in your world units per second. Direction is determined by the solver (from yaw/pitch). + + * Must be **non-negative**. Zero disables meaningful ballistic solutions. + +* **`m_gravity_scale`** + Multiplies the engine’s gravity constant provided to the solver (e.g., `g = gravity_constant * m_gravity_scale`). + + * Use `1.0f` for normal gravity, `0.0f` for no-drop projectiles, other values to simulate heavier/lighter rounds. + +> Units must be consistent across your project (e.g., meters & seconds). If `gravity_constant = 9.81f`, then `m_launch_speed` is in m/s and positions are in meters. + +--- + +## Typical usage + +```cpp +using namespace omath::projectile_prediction; + +Projectile proj; +proj.m_origin = { 0.0f, 1.6f, 0.0f }; // player eye / muzzle height +proj.m_launch_speed = 850.0f; // e.g., 850 m/s +proj.m_gravity_scale = 1.0f; // normal gravity + +// With an aim solver: +auto aim = engine->maybe_calculate_aim_point(proj, target); +if (aim) { + // rotate/aim toward *aim and fire +} +``` + +--- + +## With gravity-aware solver (outline) + +Engines typically compute the firing angles to reach a predicted target position: + +* Horizontal distance `x` and vertical offset `y` are derived from `target - m_origin`. +* Gravity used is `g = gravity_constant * m_gravity_scale`. +* Launch direction has speed `m_launch_speed` and angles solved by the engine. + +If `m_gravity_scale == 0`, engines usually fall back to straight-line (no-drop) solutions. + +--- + +## Validation & tips + +* Keep `m_launch_speed ≥ 0`. Negative values are nonsensical. +* If your weapon can vary muzzle speed (charge-up, attachments), update `m_launch_speed` per shot. +* For different ammo types (tracers, grenades), prefer tweaking **`m_gravity_scale`** (and possibly the engine’s gravity constant) to match observed arc. + +--- + +## See also + +* `ProjPredEngineInterface` — common interface for aim solvers +* `ProjPredEngineLegacy` — trait-based, time-stepped ballistic solver +* `ProjPredEngineAvx2` — AVX2-accelerated solver with fixed-time pitch solve +* `Target` — target state consumed by the solvers +* `Vector3` — math type used for positions and directions + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projectile_prediction/projectile_engine.md b/docs/projectile_prediction/projectile_engine.md new file mode 100644 index 00000000..db0cf331 --- /dev/null +++ b/docs/projectile_prediction/projectile_engine.md @@ -0,0 +1,152 @@ +# `omath::projectile_prediction::ProjPredEngineInterface` — Aim-point solver interface + +> Header: your project’s `projectile_prediction/proj_pred_engine_interface.hpp` +> Namespace: `omath::projectile_prediction` +> Depends on: `Vector3`, `Projectile`, `Target` +> Purpose: **contract** for engines that compute a lead/aim point to hit a moving target. + +--- + +## Overview + +`ProjPredEngineInterface` defines a single pure-virtual method that attempts to compute the **world-space aim point** where a projectile should be launched to intersect a target under the engine’s physical model (e.g., constant projectile speed, gravity, drag, max flight time, etc.). + +If a valid solution exists, the engine returns the 3D aim point. Otherwise, it returns `std::nullopt` (no feasible intercept). + +--- + +## API + +```cpp +namespace omath::projectile_prediction { + +class ProjPredEngineInterface { +public: + [[nodiscard]] + virtual std::optional> + maybe_calculate_aim_point(const Projectile& projectile, + const Target& target) const = 0; + + virtual ~ProjPredEngineInterface() = default; +}; + +} // namespace omath::projectile_prediction +``` + +### Semantics + +* **Input** + + * `Projectile` — engine-specific projectile properties (typical: muzzle speed, gravity vector, drag flag/coeff, max range / flight time). + * `Target` — target state (typical: position, velocity, possibly acceleration). + +* **Output** + + * `std::optional>` + + * `value()` — world-space point to aim at **now** so that the projectile intersects the target under the model. + * `std::nullopt` — no solution (e.g., target outruns projectile, blocked by constraints, numerical failure). + +* **No side effects**: method is `const` and should not modify inputs. + +--- + +## Typical usage + +```cpp +using namespace omath::projectile_prediction; + +std::unique_ptr engine = /* your implementation */; +Projectile proj = /* fill from weapon config */; +Target tgt = /* read from tracking system */; + +if (auto aim = engine->maybe_calculate_aim_point(proj, tgt)) { + // Rotate/steer to (*aim) +} else { + // Fall back: no-lead, predictive UI, or do not fire +} +``` + +--- + +## Implementation guidance (for engine authors) + +**Common models:** + +1. **No gravity, constant speed** + Closed form intersect time `t` solves `‖p_t + v_t t − p_0‖ = v_p t`. + Choose the smallest non-negative real root; aim point = `p_t + v_t t`. + +2. **Gravity (constant g), constant speed** + Solve ballistics with vertical drop: either numerical (Newton–Raphson on time) or 2D elevation + azimuth decomposition. Ensure convergence caps and time bounds. + +3. **Drag** + Typically requires numeric integration (e.g., RK4) wrapped in a root find on time-of-flight. + +**Robustness tips:** + +* **Feasibility checks:** return `nullopt` when: + + * projectile speed ≤ 0; target too fast in receding direction; solution time outside `[0, t_max]`. +* **Bounds:** clamp search time to reasonable `[t_min, t_max]` (e.g., `[0, max_flight_time]` or by range). +* **Tolerances:** use epsilons for convergence (e.g., `|f(t)| < 1e-4`, `|Δt| < 1e-4 s`). +* **Determinism:** fix iteration counts or seeds if needed for replayability. + +--- + +## Example: constant-speed, no-gravity intercept (closed form) + +```cpp +// Solve ||p + v t|| = s t where p = target_pos - shooter_pos, v = target_vel, s = projectile_speed +// Quadratic: (v·v - s^2) t^2 + 2 (p·v) t + (p·p) = 0 +inline std::optional intercept_time_no_gravity(const Vector3& p, + const Vector3& v, + float s) { + const float a = v.dot(v) - s*s; + const float b = 2.f * p.dot(v); + const float c = p.dot(p); + if (std::abs(a) < 1e-6f) { // near linear + if (std::abs(b) < 1e-6f) return std::nullopt; + float t = -c / b; + return t >= 0.f ? std::optional{t} : std::nullopt; + } + const float disc = b*b - 4.f*a*c; + if (disc < 0.f) return std::nullopt; + const float sqrtD = std::sqrt(disc); + float t1 = (-b - sqrtD) / (2.f*a); + float t2 = (-b + sqrtD) / (2.f*a); + float t = (t1 >= 0.f ? t1 : t2); + return t >= 0.f ? std::optional{t} : std::nullopt; +} +``` + +Aim point (given shooter origin `S`, target pos `T`, vel `V`): + +``` +p = T - S +t* = intercept_time_no_gravity(p, V, speed) +aim = T + V * t* +``` + +Return `nullopt` if `t*` is absent. + +--- + +## Testing checklist + +* **Stationary target**: aim point equals target position when `s > 0`. +* **Target perpendicular motion**: lead equals lateral displacement `V⊥ * t`. +* **Receding too fast**: expect `nullopt`. +* **Gravity model**: verify arc solutions exist for short & long trajectories (if implemented). +* **Numerics**: convergence within max iterations; monotonic improvement of residuals. + +--- + +## Notes + +* This is an **interface** only; concrete engines (e.g., `SimpleNoGravityEngine`, `BallisticGravityEngine`) should document their assumptions (gravity, drag, wind, bounds) and units (meters, seconds). +* The coordinate system and handedness should be consistent with `Vector3` and the rest of your math stack. + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projectile_prediction/target.md b/docs/projectile_prediction/target.md new file mode 100644 index 00000000..45944635 --- /dev/null +++ b/docs/projectile_prediction/target.md @@ -0,0 +1,70 @@ +# `omath::projectile_prediction::Target` — Target state for aim solvers + +> Header: `omath/projectile_prediction/target.hpp` +> Namespace: `omath::projectile_prediction` +> Used by: `ProjPredEngineInterface` implementations (e.g., Legacy/AVX2 engines) + +A small POD-style container describing a target’s **current pose** and **motion** for projectile lead/aim computations. + +--- + +## API + +```cpp +namespace omath::projectile_prediction { + +class Target final { +public: + Vector3 m_origin; // Current world-space position of the target + Vector3 m_velocity; // World-space linear velocity (units/sec) + bool m_is_airborne{}; // Domain hint (e.g., ignore ground snapping) +}; + +} // namespace omath::projectile_prediction +``` + +--- + +## Field semantics + +* **`m_origin`** — target position in world coordinates (same units as your `Vector3` grid). +* **`m_velocity`** — instantaneous linear velocity. Solvers commonly assume **constant velocity** between “now” and impact unless your trait injects gravity/accel. +* **`m_is_airborne`** — optional hint for engine/trait logic (e.g., apply gravity to the target, skip ground friction/snap). Exact meaning is engine-dependent. + +> Keep units consistent with your projectile model (e.g., meters & seconds). If projectiles use `g = 9.81 m/s²`, velocity should be in m/s and positions in meters. + +--- + +## Typical usage + +```cpp +using namespace omath::projectile_prediction; + +Target tgt; +tgt.m_origin = { 42.0f, 1.8f, -7.5f }; +tgt.m_velocity = { 3.0f, 0.0f, 0.0f }; // moving +X at 3 units/s +tgt.m_is_airborne = false; + +// Feed into an aim solver with a Projectile +auto aim = engine->maybe_calculate_aim_point(projectile, tgt); +``` + +--- + +## Notes & tips + +* If you track acceleration (e.g., gravity on ragdolls), your **EngineTrait** may derive it from `m_is_airborne` and world gravity; otherwise most solvers treat the target’s motion as linear. +* For highly agile targets, refresh `m_origin`/`m_velocity` every tick and re-solve; don’t reuse stale aim points. +* Precision: `Vector3` is typically enough; if you need sub-millimeter accuracy over long ranges, consider double-precision internally in your trait. + +--- + +## See also + +* `Projectile` — shooter origin, muzzle speed, gravity scale +* `ProjPredEngineInterface` — common interface for aim solvers +* `ProjPredEngineLegacy`, `ProjPredEngineAvx2` — concrete solvers using this data + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projection/camera.md b/docs/projection/camera.md new file mode 100644 index 00000000..b9cf47ba --- /dev/null +++ b/docs/projection/camera.md @@ -0,0 +1,261 @@ +# `omath::projection::Camera` — Generic, trait-driven camera with screen/world conversion + +> Header: `omath/projection/camera.hpp` (this header) +> Namespace: `omath::projection` +> Template: `Camera` +> Requires: `CameraEngineConcept` +> Key features: **lazy view-projection caching**, world↔screen helpers, pluggable math via a **Trait** + +--- + +## Overview + +`Camera` is a small, zero-allocation camera wrapper. It delegates the math for **view**, **projection**, and **look-at** to a **Trait** (`TraitClass`), which lets you plug in different coordinate systems or conventions without changing the camera code. The class caches the **View×Projection** matrix and invalidates it when any parameter changes. + +Alongside the camera, the header defines: + +* `struct ViewPort { float m_width, m_height; float aspect_ratio() const; }` +* `using FieldOfView = Angle;` + +--- + +## Template & trait requirements + +```cpp +template +concept CameraEngineConcept = requires( + const omath::Vector3& cam_origin, + const omath::Vector3& look_at, + const ViewAnglesType& angles, + const omath::projection::FieldOfView& fov, + const omath::projection::ViewPort& viewport, + float znear, float zfar +) { + { T::calc_look_at_angle(cam_origin, look_at) } noexcept -> std::same_as; + { T::calc_view_matrix(angles, cam_origin) } noexcept -> std::same_as; + { T::calc_projection_matrix(fov, viewport, znear, zfar)}noexcept -> std::same_as; +}; +``` + +Your `Mat4X4Type` must behave like the library’s `Mat<4,4,...>` (supports `*`, `/`, `inverted()`, `.at(r,c)`, `.raw_array()`, and `static constexpr get_store_ordering()`). + +--- + +## Quick start + +```cpp +using Mat4 = omath::Mat<4,4,float, omath::MatStoreType::COLUMN_MAJOR>; + +// Example trait (sketch): assumes Y-up, column-major, left-handed +struct MyCamTrait { + static ViewAnglesType calc_look_at_angle(const Vector3& eye, + const Vector3& at) noexcept; + static Mat4 calc_view_matrix(const ViewAnglesType& ang, + const Vector3& eye) noexcept; + static Mat4 calc_projection_matrix(const FieldOfView& fov, + const ViewPort& vp, + float znear, float zfar) noexcept; +}; + +using Camera = omath::projection::Camera; + +omath::projection::ViewPort vp{1920, 1080}; +omath::projection::FieldOfView fov = omath::angles::degrees(70.f); + +Camera cam(/*position*/ {0,1.7f, -3}, + /*angles*/ MyViewAngles{/*...*/}, + /*viewport*/ vp, fov, + /*near*/ 0.1f, + /*far*/ 1000.f); + +// Project world → screen (origin top-left) +auto s = cam.world_to_screen({1, 1, 0}); +if (s) { + // s->x, s->y in pixels; s->z in NDC depth +} +``` + +--- + +## API + +```cpp +enum class ScreenStart { TOP_LEFT_CORNER, BOTTOM_LEFT_CORNER }; + +class Camera final { +public: + ~Camera() = default; + + Camera(const Vector3& position, + const ViewAnglesType& view_angles, + const ViewPort& view_port, + const FieldOfView& fov, + float near, float far) noexcept; + + void look_at(const Vector3& target); // recomputes view angles; invalidates cache + + // Lazily computed and cached: + const Mat4X4Type& get_view_projection_matrix() const noexcept; + + // Setters (all invalidate cached VP): + void set_field_of_view(const FieldOfView&) noexcept; + void set_near_plane(float) noexcept; + void set_far_plane(float) noexcept; + void set_view_angles(const ViewAnglesType&) noexcept; + void set_origin(const Vector3&) noexcept; + void set_view_port(const ViewPort&) noexcept; + + // Getters: + const FieldOfView& get_field_of_view() const noexcept; + const float& get_near_plane() const noexcept; + const float& get_far_plane() const noexcept; + const ViewAnglesType& get_view_angles() const noexcept; + const Vector3& get_origin() const noexcept; + + // World → Screen (pixels) via NDC; choose screen origin: + template + std::expected, Error> + world_to_screen(const Vector3& world) const noexcept; + + // World → NDC (aka “viewport” in this code) ∈ [-1,1]^3 + std::expected, Error> + world_to_view_port(const Vector3& world) const noexcept; + + // NDC → World (uses inverse VP) + std::expected, Error> + view_port_to_screen(const Vector3& ndc) const noexcept; + + // Screen (pixels) → World + std::expected, Error> + screen_to_world(const Vector3& screen) const noexcept; + + // 2D overload (z defaults to 1, i.e., far plane ray-end in NDC) + std::expected, Error> + screen_to_world(const Vector2& screen) const noexcept; + +protected: + ViewPort m_view_port{}; + FieldOfView m_field_of_view; + mutable std::optional m_view_projection_matrix; + float m_far_plane_distance{}; + float m_near_plane_distance{}; + ViewAnglesType m_view_angles; + Vector3 m_origin; + +private: + static constexpr bool is_ndc_out_of_bounds(const Mat4X4Type& ndc) noexcept; + Vector3 ndc_to_screen_position_from_top_left_corner(const Vector3& ndc) const noexcept; + Vector3 ndc_to_screen_position_from_bottom_left_corner(const Vector3& ndc) const noexcept; + Vector3 screen_to_ndc(const Vector3& screen) const noexcept; +}; +``` + +### Error handling + +All conversions return `std::expected<..., Error>` with errors from `error_codes.hpp`, notably: + +* `Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS` — clip space W=0 or NDC outside `[-1,1]`. +* `Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO` — non-invertible View×Projection matrix. + +--- + +## Coordinate spaces & conversions + +### World → NDC (`world_to_view_port`) + +1. Build (or reuse cached) `VP = P * V` (projection * view). +2. Multiply by homogeneous column from the world point. +3. Reject if `w == 0`. +4. Perspective divide → NDC in `[-1,1]^3`. +5. Reject if any component is out of range. + +Returns `{x_ndc, y_ndc, z_ndc}`. + +### NDC → Screen (pixels) + +The class offers two origins: + +* **Top-left (default)** + + ``` + x_px = (x_ndc + 1)/2 * width + y_px = ( -y_ndc/2 + 0.5) * height // flips Y + ``` +* **Bottom-left** + + ``` + x_px = (x_ndc + 1)/2 * width + y_px = ( y_ndc/2 + 0.5) * height + ``` + +### Screen (pixels) → NDC + +``` +x_ndc = screen_x / width * 2 - 1 +y_ndc = 1 - screen_y / height * 2 // Top-left screen origin assumed here +z_ndc = screen_z // Caller-provided (e.g., 0..1 depth) +``` + +### NDC → World (`view_port_to_screen`) + +Despite the method name, this function **unprojects** an NDC point back to world space: + +1. Compute `VP^{-1}`; if not invertible → error. +2. Multiply by NDC (homogeneous 4D) and divide by `w`. +3. Return world point. + +> Tip: to build a **world-space ray** from a screen pixel, unproject at `z=0` (near) and `z=1` (far). + +--- + +## Caching & invalidation + +* `get_view_projection_matrix()` computes `P*V` once and caches it. +* Any setter (`set_*`) or `look_at()` clears the cache (`m_view_projection_matrix = std::nullopt`). + +--- + +## Notes & gotchas + +* **Matrix order**: The camera multiplies `P * V`. Make sure your **Trait** matches this convention. +* **Store ordering**: The `Mat4X4Type::get_store_ordering()` is used when building homogeneous columns; ensure it’s consistent with your matrix implementation. +* **Naming quirk**: `view_port_to_screen()` returns a **world** point from **NDC** (it’s an unproject). Consider renaming to `ndc_to_world()` in your codebase for clarity. +* **FOV units**: `FieldOfView` uses the project’s `Angle` type; pass degrees via `angles::degrees(...)`. + +--- + +## Minimal trait sketch (column-major, left-handed) + +```cpp +struct LHCTrait { + static MyAngles calc_look_at_angle(const Vector3& eye, + const Vector3& at) noexcept { /* ... */ } + + static Mat4 calc_view_matrix(const MyAngles& ang, + const Vector3& eye) noexcept { + // Build from forward/right/up and translation + } + + static Mat4 calc_projection_matrix(const FieldOfView& fov, + const ViewPort& vp, + float zn, float zf) noexcept { + return omath::mat_perspective_left_handed( + fov.as_degrees(), vp.aspect_ratio(), zn, zf + ); + } +}; +``` + +--- + +## Testing checklist + +* World point centered in view projects to **screen center**. +* Points outside frustum → `WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS`. +* Inverting `VP` fails gracefully for singular matrices. +* `ScreenStart` switch flips Y as expected. +* Screen→World ray: unproject `(x,y,0)` and `(x,y,1)` and verify direction passes through the camera frustum. + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projection/error_codes.md b/docs/projection/error_codes.md new file mode 100644 index 00000000..435751bb --- /dev/null +++ b/docs/projection/error_codes.md @@ -0,0 +1,79 @@ +# `omath::projection::Error` — Error codes for world/screen projection + +> Header: `omath/projection/error_codes.hpp` +> Namespace: `omath::projection` +> Type: `enum class Error : uint16_t` + +These error codes are returned by camera/projection helpers (e.g., `Camera::world_to_screen`, `Camera::screen_to_world`) wrapped in `std::expected<..., Error>`. Use them to distinguish **clipping/visibility** problems from **matrix/math** failures. + +--- + +## Enum values + +```cpp +enum class Error : uint16_t { + WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS, + INV_VIEW_PROJ_MAT_DET_EQ_ZERO, +}; +``` + +* **`WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS`** + The input point cannot produce a valid on-screen coordinate: + + * Clip-space `w == 0` (point at/infinite or behind camera plane), or + * After projection, any NDC component is outside `[-1, 1]`. + +* **`INV_VIEW_PROJ_MAT_DET_EQ_ZERO`** + The **View × Projection** matrix is not invertible (determinant ≈ 0). + Unprojection (`screen_to_world` / `view_port_to_screen`) requires an invertible matrix. + +--- + +## Typical usage + +```cpp +using omath::projection::Error; + +auto pix = cam.world_to_screen(point); +if (!pix) { + switch (pix.error()) { + case Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS: + // Cull label/marker; point is off-screen or behind camera. + break; + case Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO: + // Investigate camera/projection setup; near/far/FOV or trait bug. + break; + } +} + +// Unproject a screen pixel (top-left origin) at depth 1.0 +if (auto world = cam.screen_to_world({sx, sy, 1.0f})) { + // use *world +} else if (world.error() == Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO) { + // handle singular VP matrix +} +``` + +--- + +## When you might see these errors + +* **Out-of-bounds** + + * The world point lies outside the camera frustum. + * The point is behind the camera (clip `w <= 0`). + * Extremely large coordinates cause overflow and fail NDC bounds. + +* **Non-invertible VP** + + * Degenerate projection settings (e.g., `near == far`, zero FOV). + * Trait builds `P` or `V` incorrectly (wrong handedness/order). + * Numerical issues from nearly singular configurations. + +--- + +## Recommendations + +* Validate camera setup: `near > 0`, `far > near`, sensible FOV (e.g., 30°–120°). +* For UI markers: treat `WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS` as a simple **cull** signal. +* Log `INV_VIEW_PROJ_MAT_DET_EQ_ZERO` — it usually indicates a configuration or math bug worth fixing rather than hiding. diff --git a/docs/rev_eng/external_rev_object.md b/docs/rev_eng/external_rev_object.md new file mode 100644 index 00000000..3a983fe1 --- /dev/null +++ b/docs/rev_eng/external_rev_object.md @@ -0,0 +1,164 @@ +# `omath::rev_eng::ExternalReverseEngineeredObject` — typed offsets over external memory + +> Header: `omath/rev_eng/external_reverse_engineered_object.hpp` +> Namespace: `omath::rev_eng` +> Pattern: **CRTP-style wrapper** around a user-provided *ExternalMemoryManagementTrait* that actually reads/writes another process or device’s memory. + +A tiny base class for reverse-engineered objects that live **outside** your address space. You pass an absolute base address and a trait with `read_memory` / `write_memory`. Your derived types then expose strongly-typed getters/setters that delegate into the trait using **byte offsets**. + +--- + +## Quick look + +```cpp +template +class ExternalReverseEngineeredObject { +public: + explicit ExternalReverseEngineeredObject(std::uintptr_t addr) + : m_object_address(addr) {} + +protected: + template + [[nodiscard]] Type get_by_offset(std::ptrdiff_t offset) const { + return ExternalMemoryManagementTrait::read_memory(m_object_address + offset); + } + + template + void set_by_offset(std::ptrdiff_t offset, const Type& value) const { + ExternalMemoryManagementTrait::write_memory(m_object_address + offset, value); + } + +private: + std::uintptr_t m_object_address{}; +}; +``` + +--- + +## Trait requirements + +Your `ExternalMemoryManagementTrait` must provide: + +```cpp +// Reads sizeof(T) bytes starting at absolute address and returns T. +template +static T read_memory(std::uintptr_t absolute_address); + +// Writes sizeof(T) bytes to absolute address. +template +static void write_memory(std::uintptr_t absolute_address, const T& value); +``` + +> Tip: If your implementation prefers returning `bool`/`expected<>` for writes, either: +> +> * make `write_memory` `void` and throw/log internally, or +> * adjust `set_by_offset` in your fork to surface the status. + +### Common implementations + +* **Windows**: wrap `ReadProcessMemory` / `WriteProcessMemory` with a stored `HANDLE` (often captured via a singleton or embedded in the trait). +* **Linux**: `/proc//mem`, `process_vm_readv/writev`, `ptrace`. +* **Device/FPGA**: custom MMIO/driver APIs. + +--- + +## How to use (derive and map fields) + +Create a concrete type for your target structure and map known offsets: + +```cpp +struct WinRPMTrait { + template + static T read_memory(std::uintptr_t addr) { + T out{}; + SIZE_T n{}; + if (!ReadProcessMemory(g_handle, reinterpret_cast(addr), &out, sizeof(T), &n) || n != sizeof(T)) + throw std::runtime_error("RPM failed"); + return out; + } + template + static void write_memory(std::uintptr_t addr, const T& val) { + SIZE_T n{}; + if (!WriteProcessMemory(g_handle, reinterpret_cast(addr), &val, sizeof(T), &n) || n != sizeof(T)) + throw std::runtime_error("WPM failed"); + } +}; + +class Player final : public omath::rev_eng::ExternalReverseEngineeredObject { + using Base = omath::rev_eng::ExternalReverseEngineeredObject; +public: + using Base::Base; // inherit ctor (takes base address) + + // Offsets taken from your RE notes (in bytes) + Vector3 position() const { return get_by_offset>(0x30); } + void set_position(const Vector3& p) const { set_by_offset(0x30, p); } + + float health() const { return get_by_offset(0x100); } + void set_health(float h) const { set_by_offset(0x100, h); } +}; +``` + +Then: + +```cpp +Player p{ /* base address you discovered */ 0x7FF6'1234'0000ull }; +auto pos = p.position(); +p.set_health(100.f); +``` + +--- + +## Design notes & constraints + +* **Offsets are byte offsets** from the object’s **base address** passed to the constructor. +* **Type safety is on you**: `Type` must match the external layout at that offset (endian, packing, alignment). +* **No lifetime tracking**: if the target object relocates/frees, you must update/recreate the wrapper with the new base address. +* **Thread safety**: the class itself is stateless; thread safety depends on your trait implementation. +* **Endianness**: assumes the host and target endianness agree, or your trait handles conversion. +* **Error handling**: this header doesn’t prescribe it; adopt exceptions/expected/logging inside the trait. + +--- + +## Best practices + +* Centralize offsets in one place (constexprs or a small struct) and **comment source/version** (e.g., *game v1.2.3*). +* Wrap fragile multi-field writes in a trait-level **transaction** if your platform supports it. +* Validate pointers/guards (e.g., vtable signature, canary) before trusting offsets. +* Prefer **plain old data** (`struct` without virtuals) for `Type` to ensure trivial byte copies. + +--- + +## Minimal trait sketch (POSIX, `process_vm_readv`) + +```cpp +struct LinuxPvmTrait { + static pid_t pid; + + template static T read_memory(std::uintptr_t addr) { + T out{}; + iovec local{ &out, sizeof(out) }, remote{ reinterpret_cast(addr), sizeof(out) }; + if (process_vm_readv(pid, &local, 1, &remote, 1, 0) != ssize_t(sizeof(out))) + throw std::runtime_error("pvm_readv failed"); + return out; + } + + template static void write_memory(std::uintptr_t addr, const T& val) { + iovec local{ const_cast(&val), sizeof(val) }, remote{ reinterpret_cast(addr), sizeof(val) }; + if (process_vm_writev(pid, &local, 1, &remote, 1, 0) != ssize_t(sizeof(val))) + throw std::runtime_error("pvm_writev failed"); + } +}; +``` + +--- + +## Troubleshooting + +* **Garbled values** → wrong offset/Type, or target’s structure changed between versions. +* **Access denied** → missing privileges (admin/root), wrong process handle, or page protections. +* **Crashes in trait** → add bounds/sanity checks; many APIs fail on unmapped pages. +* **Writes “stick” only briefly** → the target may constantly overwrite (server authority / anti-cheat / replication). + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/rev_eng/internal_rev_object.md b/docs/rev_eng/internal_rev_object.md new file mode 100644 index 00000000..3fce7be7 --- /dev/null +++ b/docs/rev_eng/internal_rev_object.md @@ -0,0 +1,142 @@ +# `omath::rev_eng::InternalReverseEngineeredObject` — raw in-process offset/VTABLE access + +> Header: `omath/rev_eng/internal_reverse_engineered_object.hpp` +> Namespace: `omath::rev_eng` +> Purpose: Convenience base for **internal** (same-process) RE wrappers that: +> +> * read/write fields by **byte offset** from `this` +> * call **virtual methods** by **vtable index** + +--- + +## At a glance + +```cpp +class InternalReverseEngineeredObject { +protected: + template + [[nodiscard]] Type& get_by_offset(std::ptrdiff_t offset); + + template + [[nodiscard]] const Type& get_by_offset(std::ptrdiff_t offset) const; + + template + ReturnType call_virtual_method(auto... arg_list); +}; +``` + +* `get_by_offset(off)` — returns a **reference** to `T` located at `reinterpret_cast(this) + off`. +* `call_virtual_method(args...)` — fetches the function pointer from `(*reinterpret_cast(this))[id]` and invokes it as a free function with implicit `this` passed explicitly. + +On MSVC builds the function pointer type uses `__thiscall`; on non-MSVC it uses a plain function pointer taking `void*` as the first parameter (the typical Itanium ABI shape). + +--- + +## Example: wrapping a reverse-engineered class + +```cpp +struct Player : omath::rev_eng::InternalReverseEngineeredObject { + // Field offsets (document game/app version!) + static constexpr std::ptrdiff_t kHealth = 0x100; + static constexpr std::ptrdiff_t kPosition = 0x30; + + // Accessors + float& health() { return get_by_offset(kHealth); } + const float& health() const { return get_by_offset(kHealth); } + + Vector3& position() { return get_by_offset>(kPosition); } + const Vector3& position() const { return get_by_offset>(kPosition); } + + // Virtuals (vtable indices discovered via RE) + int getTeam() { return call_virtual_method<27, int>(); } + void setArmor(float val) { call_virtual_method<42, void>(val); } // signature must match! +}; +``` + +Usage: + +```cpp +auto* p = /* pointer to live Player instance within the same process */; +p->health() = 100.f; +int team = p->getTeam(); +``` + +--- + +## How `call_virtual_method` resolves the signature + +```cpp +template +ReturnType call_virtual_method(auto... arg_list) { +#ifdef _MSC_VER + using Fn = ReturnType(__thiscall*)(void*, decltype(arg_list)...); +#else + using Fn = ReturnType(*)(void*, decltype(arg_list)...); +#endif + return (*reinterpret_cast(this))[id](this, arg_list...); +} +``` + +* The **first parameter** is always `this` (`void*`). +* Remaining parameter types are deduced from the **actual arguments** (`decltype(arg_list)...`). + Ensure you pass arguments with the correct types (e.g., `int32_t` vs `int`, pointer/ref qualifiers), or define thin wrappers that cast to the exact signature you recovered. + +> ⚠ On 32-bit MSVC the `__thiscall` distinction matters; on 64-bit MSVC it’s ignored (all member funcs use the common x64 calling convention). + +--- + +## Safety notes (read before using!) + +Working at this level is inherently unsafe; be deliberate: + +1. **Correct offsets & alignment** + + * `get_by_offset` assumes `this + offset` is **properly aligned** for `T` and points to an object of type `T`. + * Wrong offsets or misalignment ⇒ **undefined behavior** (UB), crashes, silent corruption. + +2. **Object layout assumptions** + + * The vtable pointer is assumed to be at the **start of the most-derived subobject at `this`**. + * With **multiple/virtual inheritance**, the desired subobject’s vptr may be at a non-zero offset. If so, adjust `this` to that subobject before calling, e.g.: + + ```cpp + auto* sub = reinterpret_cast(reinterpret_cast(this) + kSubobjectOffset); + // … then reinterpret sub instead of this inside a custom helper + ``` + +3. **ABI & calling convention** + + * Indices and signatures are **compiler/ABI-specific**. Recheck after updates or different builds (MSVC vs Clang/LLVM-MSVC vs MinGW). + +4. **Strict aliasing** + + * Reinterpreting memory as unrelated `T` can violate aliasing rules. Prefer **trivially copyable** PODs and exact original types where possible. + +5. **Const-correctness** + + * The `const` overload returns `const T&` but still reinterprets memory; do not write through it. Use the non-const overload to mutate. + +6. **Thread safety** + + * No synchronization is provided. Ensure the underlying object isn’t concurrently mutated in incompatible ways. + +--- + +## Tips & patterns + +* **Centralize offsets** in `constexpr` with comments (`// game v1.2.3, sig XYZ`). +* **Guard reads**: if you have a canary or RTTI/vtable hash, check it before relying on offsets. +* **Prefer accessors** returning references**:** lets you both read and write with natural syntax. +* **Wrap tricky virtuals**: if a method takes complex/reference params, wrap `call_virtual_method` in a strongly typed member that casts exactly as needed. + +--- + +## Troubleshooting + +* **Crash on virtual call** → wrong index or wrong `this` (subobject), or mismatched signature (args/ret or calling conv). +* **Weird field values** → wrong offset, wrong type size/packing, stale layout after an update. +* **Only in 32-bit** → double-check `__thiscall` and parameter passing (register vs stack). + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/trigonometry/angle.md b/docs/trigonometry/angle.md new file mode 100644 index 00000000..b4b46701 --- /dev/null +++ b/docs/trigonometry/angle.md @@ -0,0 +1,165 @@ +# `omath::Angle` — templated angle with normalize/clamper + trig + +> Header: `omath/trigonometry/angle.hpp` +> Namespace: `omath` +> Template: `Angle` +> Requires: `std::is_arithmetic_v` +> Formatters: `std::formatter` for `char`, `wchar_t`, `char8_t` → `"{}deg"` + +--- + +## Overview + +`Angle` is a tiny value-type that stores an angle in **degrees** and automatically **normalizes** or **clamps** it into a compile-time range. It exposes conversions to/from radians, common trig (`sin/cos/tan/cot`), arithmetic with wrap/clamp semantics, and lightweight formatting. + +Two behaviors via `AngleFlags`: + +* `AngleFlags::Normalized` (default): values are wrapped into `[min, max]` using `angles::wrap_angle`. +* `AngleFlags::Clamped`: values are clamped to `[min, max]` using `std::clamp`. + +--- + +## API + +```cpp +namespace omath { + +enum class AngleFlags { Normalized = 0, Clamped = 1 }; + +template +requires std::is_arithmetic_v +class Angle { +public: + // Construction + static constexpr Angle from_degrees(const Type& deg) noexcept; + static constexpr Angle from_radians(const Type& rad) noexcept; + constexpr Angle() noexcept; // 0 deg, adjusted by flags/range + + // Accessors / conversions (degrees stored internally) + constexpr const Type& operator*() const noexcept; // raw degrees reference + constexpr Type as_degrees() const noexcept; + constexpr Type as_radians() const noexcept; + + // Trig (computed from radians) + Type sin() const noexcept; + Type cos() const noexcept; + Type tan() const noexcept; + Type atan() const noexcept; // atan(as_radians()) (rarely used) + Type cot() const noexcept; // cos()/sin() (watch sin≈0) + + // Arithmetic (wraps or clamps per flags and [min,max]) + constexpr Angle& operator+=(const Angle&) noexcept; + constexpr Angle& operator-=(const Angle&) noexcept; + constexpr Angle operator+(const Angle&) noexcept; + constexpr Angle operator-(const Angle&) noexcept; + constexpr Angle operator-() const noexcept; + + // Comparison (partial ordering) + constexpr std::partial_ordering operator<=>(const Angle&) const noexcept = default; +}; + +} // namespace omath +``` + +### Formatting + +```cpp +std::format("{}", Angle::from_degrees(45)); // "45deg" +``` + +Formatters exist for `char`, `wchar_t`, and `char8_t`. + +--- + +## Usage examples + +### Defaults (0–360, normalized) + +```cpp +using Deg = omath::Angle<>; // float, [0,360], Normalized + +auto a = Deg::from_degrees(370); // -> 10deg +auto b = Deg::from_radians(omath::angles::pi); // -> 180deg + +a += Deg::from_degrees(355); // 10 + 355 -> 365 -> wraps -> 5deg + +float s = a.sin(); // sin(5°) +``` + +### Clamped range + +```cpp +using Fov = omath::Angle; +auto fov = Fov::from_degrees(200.f); // -> 179deg (clamped) +``` + +### Signed, normalized range + +```cpp +using SignedDeg = omath::Angle; + +auto x = SignedDeg::from_degrees(190.f); // -> -170deg +auto y = SignedDeg::from_degrees(-200.f); // -> 160deg +auto z = x + y; // -170 + 160 = -10deg (wrapped if needed) +``` + +### Get/set raw degrees + +```cpp +auto yaw = SignedDeg::from_degrees(-45.f); +float deg = *yaw; // same as yaw.as_degrees() +``` + +--- + +## Semantics & notes + +* **Storage & units:** Internally stores **degrees** (`Type m_angle`). `as_radians()`/`from_radians()` use the project helpers in `omath::angles`. +* **Arithmetic honors policy:** `operator+=`/`-=` and the binary `+`/`-` apply **wrap** or **clamp** in `[min,max]`, mirroring construction behavior. +* **`atan()`**: returns `std::atan(as_radians())` (the arctangent of the *radian value*). This is mathematically unusual for an angle type and is rarely useful; prefer `tan()`/`atan2` in client code when solving geometry problems. +* **`cot()` / `tan()` singularities:** Near multiples where `sin() ≈ 0` or `cos() ≈ 0`, results blow up. Guard in your usage if inputs can approach these points. +* **Comparison:** `operator<=>` is defaulted. With normalization, distinct representatives can compare as expected (e.g., `-180` vs `180` in signed ranges are distinct endpoints). +* **No implicit numeric conversion:** There’s **no `operator Type()`**. Use `as_degrees()`/`as_radians()` (or `*angle`) explicitly—this intentional friction avoids unit mistakes. + +--- + +## Customization patterns + +* **Radians workflow:** Keep angles in degrees internally but wrap helper creators: + + ```cpp + inline Deg degf(float d) { return Deg::from_degrees(d); } + inline Deg radf(float r) { return Deg::from_radians(r); } + ``` +* **Compile-time policy:** Pick ranges/flags at the type level to enforce invariants (e.g., `YawDeg = Angle`; `FovDeg = Angle`). + +--- + +## Pitfalls & gotchas + +* Ensure `min < max` at compile time for meaningful wrap/clamp behavior. +* For normalized signed ranges, decide whether your `wrap_angle(min,max)` treats endpoints half-open (e.g., `[-180,180)`) to avoid duplicate representations; the formatter will print the stored value verbatim. +* If you need **sum of many angles**, accumulating in radians then converting back can improve numeric stability at extreme values. + +--- + +## Minimal tests + +```cpp +using A = omath::Angle<>; +REQUIRE(A::from_degrees(360).as_degrees() == 0.f); +REQUIRE(A::from_degrees(-1).as_degrees() == 359.f); + +using S = omath::Angle; +REQUIRE(S::from_degrees( 181).as_degrees() == -179.f); +REQUIRE(S::from_degrees(-181).as_degrees() == 179.f); + +using C = omath::Angle; +REQUIRE(C::from_degrees(5).as_degrees() == 10.f); +REQUIRE(C::from_degrees(25).as_degrees() == 20.f); +``` + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/trigonometry/angles.md b/docs/trigonometry/angles.md new file mode 100644 index 00000000..afef6cbe --- /dev/null +++ b/docs/trigonometry/angles.md @@ -0,0 +1,107 @@ +# `omath::angles` — angle conversions, FOV helpers, and wrapping + +> Header: `omath/trigonometry/angles.hpp` +> Namespace: `omath::angles` +> All functions are `[[nodiscard]]` and `noexcept` where applicable. + +A small set of constexpr-friendly utilities for converting between degrees/radians, converting horizontal/vertical field of view, and wrapping angles into a closed interval. + +--- + +## API + +```cpp +// Degrees ↔ Radians (Type must be floating-point) +template +requires std::is_floating_point_v +constexpr Type radians_to_degrees(const Type& radians) noexcept; + +template +requires std::is_floating_point_v +constexpr Type degrees_to_radians(const Type& degrees) noexcept; + +// FOV conversion (inputs/outputs in degrees, aspect = width/height) +template +requires std::is_floating_point_v +Type horizontal_fov_to_vertical(const Type& horizontal_fov, const Type& aspect) noexcept; + +template +requires std::is_floating_point_v +Type vertical_fov_to_horizontal(const Type& vertical_fov, const Type& aspect) noexcept; + +// Wrap angle into [min, max] (any arithmetic type) +template +requires std::is_arithmetic_v +Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept; +``` + +--- + +## Usage + +### Degrees ↔ Radians + +```cpp +float rad = omath::angles::degrees_to_radians(180.0f); // π +double deg = omath::angles::radians_to_degrees(std::numbers::pi); // 180 +``` + +### Horizontal ↔ Vertical FOV + +* `aspect` = **width / height**. +* Inputs/outputs are **degrees**. + +```cpp +float hdeg = 90.0f; +float aspect = 16.0f / 9.0f; + +float vdeg = omath::angles::horizontal_fov_to_vertical(hdeg, aspect); // ~58.0° +float hdeg2 = omath::angles::vertical_fov_to_horizontal(vdeg, aspect); // ≈ 90.0° +``` + +Formulas (in radians): + +* `v = 2 * atan( tan(h/2) / aspect )` +* `h = 2 * atan( tan(v/2) * aspect )` + +### Wrapping angles (or any periodic value) + +Wrap any numeric `angle` into `[min, max]`: + +```cpp +// Wrap degrees into [0, 360] +float a = omath::angles::wrap_angle( 370.0f, 0.0f, 360.0f); // 10 +float b = omath::angles::wrap_angle( -15.0f, 0.0f, 360.0f); // 345 +// Signed range [-180,180] +float c = omath::angles::wrap_angle( 200.0f, -180.0f, 180.0f); // -160 +``` + +--- + +## Notes & edge cases + +* **Type requirements** + + * Converters & FOV helpers require **floating-point** `Type`. + * `wrap_angle` accepts any arithmetic `Type` (floats or integers). +* **Aspect ratio** must be **positive** and finite. For `aspect == 0` the FOV helpers are undefined. +* **Units**: FOV functions accept/return **degrees** but compute internally in radians. +* **Wrapping interval**: Behavior assumes `max > min`. The result lies in the **closed interval** `[min, max]` with modulo arithmetic; if you need half-open behavior (e.g., `[min,max)`), adjust your range or post-process endpoint cases. +* **constexpr**: Converters are `constexpr`; FOV helpers are runtime constexpr-compatible except for `std::atan/std::tan` constraints on some standard libraries. + +--- + +## Quick tests + +```cpp +using namespace omath::angles; + +static_assert(degrees_to_radians(180.0) == std::numbers::pi); +static_assert(radians_to_degrees(std::numbers::pi_v) == 180.0f); + +float v = horizontal_fov_to_vertical(90.0f, 16.0f/9.0f); +float h = vertical_fov_to_horizontal(v, 16.0f/9.0f); +assert(std::abs(h - 90.0f) < 1e-5f); + +assert(wrap_angle(360.0f, 0.0f, 360.0f) == 0.0f || wrap_angle(360.0f, 0.0f, 360.0f) == 360.0f); +``` diff --git a/docs/trigonometry/view_angles.md b/docs/trigonometry/view_angles.md new file mode 100644 index 00000000..5f6b12b9 --- /dev/null +++ b/docs/trigonometry/view_angles.md @@ -0,0 +1,87 @@ +# `omath::ViewAngles` — tiny POD for pitch/yaw/roll + +> Header: your project’s `view_angles.hpp` +> Namespace: `omath` +> Kind: **aggregate struct** (POD), no methods, no allocation + +A minimal container for Euler angles. You choose the types for each component (e.g., raw `float` or the strong `omath::Angle<>` type), and plug it into systems like `projection::Camera`. + +--- + +## API + +```cpp +namespace omath { + template + struct ViewAngles { + PitchType pitch; + YawType yaw; + RollType roll; + }; +} +``` + +* Aggregate: supports brace-init, aggregate copying, and `constexpr` usage when the component types do. +* Semantics (units/handedness/ranges) are **entirely defined by your chosen types**. + +--- + +## Common aliases + +```cpp +// Simple, raw degrees as floats (be careful with wrapping!) +using ViewAnglesF = omath::ViewAngles; + +// Safer, policy-based angles (recommended) +using PitchDeg = omath::Angle; +using YawDeg = omath::Angle; +using RollDeg = omath::Angle; +using ViewAnglesDeg = omath::ViewAngles; +``` + +--- + +## Examples + +### Basic construction + +```cpp +omath::ViewAngles a{ 10.f, 45.f, 0.f }; // pitch, yaw, roll in degrees +``` + +### With `omath::Angle<>` (automatic wrap/clamper) + +```cpp +ViewAnglesDeg v{ + PitchDeg::from_degrees( 95.f), // -> 89deg (clamped) + YawDeg::from_degrees (-190.f), // -> 170deg (wrapped) + RollDeg::from_degrees ( 30.f) +}; +``` + +### Using with `projection::Camera` + +```cpp +using Mat4 = omath::Mat<4,4,float, omath::MatStoreType::COLUMN_MAJOR>; +using Cam = omath::projection::Camera; + +omath::projection::ViewPort vp{1920,1080}; +auto fov = omath::angles::degrees_to_radians(70.f); // or your Angle type + +Cam cam(/*position*/ {0,1.7f,-3}, + /*angles*/ ViewAnglesDeg{ PitchDeg::from_degrees(0), + YawDeg::from_degrees(0), + RollDeg::from_degrees(0) }, + /*viewport*/ vp, + /*fov*/ omath::Angle::from_degrees(70.f), + /*near*/ 0.1f, + /*far*/ 1000.f); +``` + +--- + +## Notes & tips + +* **Ranges/units**: pick types that encode your policy (e.g., signed yaw in `[-180,180]`, pitch clamped to avoid gimbal flips). +* **Handedness & order**: this struct doesn’t impose rotation order. Your math/trait layer (e.g., `MyCameraTrait`) must define how `(pitch, yaw, roll)` map to a view matrix (common orders: ZYX or XYZ). +* **Zero-cost**: with plain `float`s this is as cheap as three scalars; with `Angle<>` you gain safety at the cost of tiny wrap/clamp logic on construction/arithmetic. From 8142d800c70cba3c50f2ce99671ee7feba86258d Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 1 Nov 2025 12:14:36 +0300 Subject: [PATCH 07/14] Styles navbar for wider display and consistent spacing Widens the navbar container to accommodate more content. Adjusts padding of navbar items for tighter spacing. Prevents line wrapping and enables horizontal scrolling on smaller screens. --- docs/engines/frostbite/camera_trait.md | 108 ++++++++++++++++++++ docs/engines/frostbite/constants.md | 59 +++++++++++ docs/engines/frostbite/formulas.md | 0 docs/engines/frostbite/pred_engine_trait.md | 105 +++++++++++++++++++ docs/styles/custom-header.css | 11 ++ mkdocs.yml | 3 +- 6 files changed, 285 insertions(+), 1 deletion(-) create mode 100644 docs/engines/frostbite/camera_trait.md create mode 100644 docs/engines/frostbite/constants.md create mode 100644 docs/engines/frostbite/formulas.md create mode 100644 docs/engines/frostbite/pred_engine_trait.md create mode 100644 docs/styles/custom-header.css 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/styles/custom-header.css b/docs/styles/custom-header.css new file mode 100644 index 00000000..f3b69319 --- /dev/null +++ b/docs/styles/custom-header.css @@ -0,0 +1,11 @@ +/* Widen the navbar container */ +.navbar .container { + max-width: 100%; /* adjust to your target width */ + width: 95%; +} + +/* Tighter spacing between navbar items */ +.navbar-nav > li > a { + padding-left: 8px; + padding-right: 8px; +} diff --git a/mkdocs.yml b/mkdocs.yml index 25349244..cabaeb5b 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -2,4 +2,5 @@ site_name: OMATH Docs theme: name: darkly extra_css: - - styles/center.css \ No newline at end of file + - styles/center.css + - styles/custom-header.css \ No newline at end of file From f4df88bb7a7467cf959443f5b896412bcd272261 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 09:22:33 +0000 Subject: [PATCH 08/14] Initial plan From e351b64355ff8ea08a67260f0b44f0e06fbc712b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 09:37:11 +0000 Subject: [PATCH 09/14] Add comprehensive documentation for all game engines Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com> --- docs/engines/iw_engine/camera_trait.md | 109 ++++++++++ docs/engines/iw_engine/constants.md | 77 +++++++ docs/engines/iw_engine/formulas.md | 135 ++++++++++++ docs/engines/iw_engine/pred_engine_trait.md | 198 +++++++++++++++++ docs/engines/opengl_engine/camera_trait.md | 110 ++++++++++ docs/engines/opengl_engine/constants.md | 78 +++++++ docs/engines/opengl_engine/formulas.md | 140 ++++++++++++ .../opengl_engine/pred_engine_trait.md | 199 +++++++++++++++++ docs/engines/source_engine/camera_trait.md | 109 ++++++++++ docs/engines/source_engine/constants.md | 77 +++++++ docs/engines/source_engine/formulas.md | 135 ++++++++++++ .../source_engine/pred_engine_trait.md | 198 +++++++++++++++++ docs/engines/unity_engine/camera_trait.md | 109 ++++++++++ docs/engines/unity_engine/constants.md | 77 +++++++ docs/engines/unity_engine/formulas.md | 135 ++++++++++++ .../engines/unity_engine/pred_engine_trait.md | 198 +++++++++++++++++ docs/engines/unreal_engine/camera_trait.md | 109 ++++++++++ docs/engines/unreal_engine/constants.md | 77 +++++++ docs/engines/unreal_engine/formulas.md | 135 ++++++++++++ .../unreal_engine/pred_engine_trait.md | 200 ++++++++++++++++++ 20 files changed, 2605 insertions(+) create mode 100644 docs/engines/iw_engine/camera_trait.md create mode 100644 docs/engines/iw_engine/constants.md create mode 100644 docs/engines/iw_engine/formulas.md create mode 100644 docs/engines/iw_engine/pred_engine_trait.md create mode 100644 docs/engines/opengl_engine/camera_trait.md create mode 100644 docs/engines/opengl_engine/constants.md create mode 100644 docs/engines/opengl_engine/formulas.md create mode 100644 docs/engines/opengl_engine/pred_engine_trait.md create mode 100644 docs/engines/source_engine/camera_trait.md create mode 100644 docs/engines/source_engine/constants.md create mode 100644 docs/engines/source_engine/formulas.md create mode 100644 docs/engines/source_engine/pred_engine_trait.md create mode 100644 docs/engines/unity_engine/camera_trait.md create mode 100644 docs/engines/unity_engine/constants.md create mode 100644 docs/engines/unity_engine/formulas.md create mode 100644 docs/engines/unity_engine/pred_engine_trait.md create mode 100644 docs/engines/unreal_engine/camera_trait.md create mode 100644 docs/engines/unreal_engine/constants.md create mode 100644 docs/engines/unreal_engine/formulas.md create mode 100644 docs/engines/unreal_engine/pred_engine_trait.md 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..b4c1c655 --- /dev/null +++ b/docs/engines/source_engine/camera_trait.md @@ -0,0 +1,109 @@ +# `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 math helpers in `omath/engines/source_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/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; + +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 + +* **Z-up**: gravity points along `-Z`, height increases with `+Z` +* **Left-handed**: cross product `forward × right = up` with left-hand rule +* This matches **Unreal Engine** conventions for 3D games and simulations + +--- + +## See also + +* `omath/engines/unreal_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/unreal_engine/formulas.md b/docs/engines/unreal_engine/formulas.md new file mode 100644 index 00000000..3f4ef3cb --- /dev/null +++ b/docs/engines/unreal_engine/formulas.md @@ -0,0 +1,135 @@ +# `omath::unreal_engine` — formulas & matrix helpers + +> Header: `omath/engines/unreal_engine/formulas.hpp` +> Namespace: `omath::unreal_engine` +> Purpose: compute direction vectors, rotation matrices, view matrices, and perspective projections for Unreal Engine + +--- + +## Summary + +This header provides **Unreal 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 Unreal Engine's **Z-up, left-handed** coordinate system. + +--- + +## API + +```cpp +namespace omath::unreal_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::unreal_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., 10.0 + far_plane // e.g., 100000.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::unreal_engine; + +// Camera setup +ViewAngles angles = { + PitchAngle::from_degrees(-20.0f), + YawAngle::from_degrees(45.0f), + RollAngle::from_degrees(0.0f) +}; +Vector3 cam_pos{1000.0f, 500.0f, 200.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, 10.0f, 100000.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 (left-handed) + +--- + +## See also + +* `omath/engines/unreal_engine/constants.hpp` — coordinate frame & angle types +* `omath/engines/unreal_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/unreal_engine/pred_engine_trait.md b/docs/engines/unreal_engine/pred_engine_trait.md new file mode 100644 index 00000000..dd337db2 --- /dev/null +++ b/docs/engines/unreal_engine/pred_engine_trait.md @@ -0,0 +1,200 @@ +# `omath::unreal_engine::PredEngineTrait` — projectile prediction trait + +> Header: `omath/engines/unreal_engine/traits/pred_engine_trait.hpp` +> Namespace: `omath::unreal_engine` +> Purpose: provide Unreal 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 (Y in Unreal Engine, note: code uses Z) +* `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::unreal_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 (implementation returns Y, but UE is Z-up) + 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::unreal_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., 980 cm/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 per implementation, though UE is Z-up) + +**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² // Note: implementation uses Y +``` + +--- + +## Distance & height helpers + +* `calc_vector_2d_distance(delta)` → `sqrt(delta.x² + delta.z²)` (horizontal distance in X/Z plane) +* `get_vector_height_coordinate(vec)` → `vec.y` (implementation returns Y; UE convention is Z-up) + +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 (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(Δy, Δx)` converted to degrees (direction normalized first) + - 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 [-90°, +90°], yaw in [-180°, +180°] +* **Gravity**: applied downward (implementation uses Y component, but UE is Z-up) +* **Pitch convention**: +90° = straight up, -90° = straight down + +**Note**: Some implementation details (gravity application to Y coordinate) may need adjustment for full Unreal Engine Z-up consistency. + +--- + +## Usage example + +```cpp +using namespace omath::unreal_engine; +using namespace omath::projectile_prediction; + +Projectile proj{ + .m_origin = {0, 0, 200}, + .m_launch_speed = 5000.0f, + .m_gravity_scale = 1.0f +}; + +Target tgt{ + .m_origin = {2000, 1000, 200}, + .m_velocity = {50, 20, 0}, + .m_is_airborne = false +}; + +float gravity = 980.0f; // cm/s² in Unreal units +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/unreal_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` From b8ed0bd5a5cdccb4f07f642c6284d8ca774167fd Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 09:40:43 +0000 Subject: [PATCH 10/14] Initial plan From 0b89c1d36d570248af09709af70fb41ef192178a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 09:51:38 +0000 Subject: [PATCH 11/14] Add comprehensive documentation improvements Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com> --- docs/api_overview.md | 527 ++++++++++++++++++++++++++++++++++ docs/best_practices.md | 532 ++++++++++++++++++++++++++++++++++ docs/faq.md | 406 ++++++++++++++++++++++++++ docs/getting_started.md | 306 ++++++++++++++++++++ docs/index.md | 168 ++++++++++- docs/troubleshooting.md | 525 ++++++++++++++++++++++++++++++++++ docs/tutorials.md | 616 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 3078 insertions(+), 2 deletions(-) create mode 100644 docs/api_overview.md create mode 100644 docs/best_practices.md create mode 100644 docs/faq.md create mode 100644 docs/getting_started.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/tutorials.md 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/faq.md b/docs/faq.md new file mode 100644 index 00000000..9f50e14d --- /dev/null +++ b/docs/faq.md @@ -0,0 +1,406 @@ +# Frequently Asked Questions (FAQ) + +Common questions and answers about OMath. + +--- + +## General Questions + +### What is OMath? + +OMath is a modern C++ math library designed for game development, graphics programming, and high-performance computing. It provides: +- Vector and matrix operations +- 3D projection and camera systems +- Projectile prediction +- Collision detection +- Support for multiple game engines (Source, Unity, Unreal, etc.) +- Pattern scanning utilities + +### Why choose OMath over other math libraries? + +- **Modern C++**: Uses C++20/23 features (concepts, `constexpr`, `std::expected`) +- **No legacy code**: Built from scratch without legacy baggage +- **Game engine support**: Pre-configured for Source, Unity, Unreal, Frostbite, etc. +- **Zero dependencies**: No external dependencies needed (except for testing) +- **Performance**: AVX2 optimizations available +- **Type safety**: Strong typing prevents common errors +- **Cross-platform**: Works on Windows, Linux, and macOS + +### Is OMath suitable for production use? + +Yes! OMath is production-ready and used in various projects. It has: +- Comprehensive test coverage +- Clear error handling +- Well-documented API +- Active maintenance and community support + +--- + +## Installation & Setup + +### How do I install OMath? + +Three main methods: + +**vcpkg (recommended):** +```bash +vcpkg install orange-math +``` + +**xrepo:** +```bash +xrepo install omath +``` + +**From source:** +See [Installation Guide](install.md) + +### What are the minimum requirements? + +- **Compiler**: C++20 support required + - GCC 10+ + - Clang 11+ + - MSVC 2019 16.10+ +- **CMake**: 3.15+ (if building from source) +- **Platform**: Windows, Linux, or macOS + +### Do I need C++23? + +C++23 is **recommended** but not required. Some features like `std::expected` work better with C++23, but fallbacks are available for C++20. + +### Can I use OMath in a C++17 project? + +No, OMath requires C++20 minimum due to use of concepts, `constexpr` enhancements, and other C++20 features. + +--- + +## Usage Questions + +### How do I include OMath in my project? + +**Full library:** +```cpp +#include +``` + +**Specific components:** +```cpp +#include +#include +``` + +### Which game engine should I use? + +Choose based on your target game or application: + +| Engine | Use For | +|--------|---------| +| **Source Engine** | CS:GO, TF2, CS2, Half-Life, Portal, L4D | +| **Unity Engine** | Unity games (many indie and mobile games) | +| **Unreal Engine** | Fortnite, Unreal games | +| **Frostbite** | Battlefield, Star Wars games (EA titles) | +| **IW Engine** | Call of Duty series | +| **OpenGL** | Custom OpenGL applications, generic 3D | + +### How do I switch between engines? + +Just change the namespace: + +```cpp +// Source Engine +using namespace omath::source_engine; +Camera cam = /* ... */; + +// Unity Engine +using namespace omath::unity_engine; +Camera cam = /* ... */; +``` + +Each engine has the same API but different coordinate system handling. + +### What if my game isn't listed? + +Use the **OpenGL engine** as a starting point - it uses canonical OpenGL conventions. You may need to adjust coordinate transformations based on your specific game. + +--- + +## Performance Questions + +### Should I use the AVX2 or Legacy engine? + +**Use AVX2 if:** +- Target modern CPUs (2013+) +- Need maximum performance +- Can accept reduced compatibility + +**Use Legacy if:** +- Need broad compatibility +- Target older CPUs or ARM +- Unsure about target hardware + +The API is identical - just change the class: +```cpp +// Legacy (compatible) +ProjPredEngineLegacy engine; + +// AVX2 (faster) +ProjPredEngineAVX2 engine; +``` + +### How much faster is AVX2? + +Typically 2-4x faster for projectile prediction calculations, depending on the CPU and specific use case. + +### Are vector operations constexpr? + +Yes! Most operations are `constexpr` and can be evaluated at compile-time: + +```cpp +constexpr Vector3 v{1, 2, 3}; +constexpr auto len_sq = v.length_sqr(); // Computed at compile time +``` + +### Is OMath thread-safe? + +- **Immutable operations** (vector math, etc.) are thread-safe +- **Mutable state** (Camera updates) is NOT thread-safe +- Use separate instances per thread or synchronize access + +--- + +## Troubleshooting + +### `world_to_screen()` always returns `nullopt` + +Check: +1. **Is the point behind the camera?** Points behind the camera cannot be projected. +2. **Are near/far planes correct?** Ensure `near < far` and both are positive. +3. **Is FOV valid?** FOV should be between 1° and 179°. +4. **Are camera angles normalized?** Use engine-provided angle types. + +### Angles are wrapping incorrectly + +Use the correct angle type: +```cpp +// Good: uses proper angle type +PitchAngle pitch = PitchAngle::from_degrees(45.0f); + +// Bad: raw float loses normalization +float pitch = 45.0f; +``` + +### Projection seems mirrored or inverted + +You may be using the wrong engine trait. Each engine has different coordinate conventions: +- **Source/Unity**: Z-up +- **Unreal**: Z-up, different handedness +- **OpenGL**: Y-up + +Ensure you're using the trait matching your game. + +### Pattern scanning finds multiple matches + +This is normal! Patterns may appear multiple times. Solutions: +1. Make the pattern more specific (more bytes, fewer wildcards) +2. Use additional context (nearby code patterns) +3. Verify each match programmatically + +### Projectile prediction returns `nullopt` + +Common reasons: +1. **Target too fast**: Target velocity exceeds projectile speed +2. **Out of range**: Distance exceeds max flight time +3. **Invalid input**: Check projectile speed > 0 +4. **Gravity too strong**: Projectile can't reach target height + +### Compilation errors about `std::expected` + +If using C++20 (not C++23), you may need a backport library like `tl::expected`: + +```cmake +# CMakeLists.txt +find_package(tl-expected CONFIG REQUIRED) +target_link_libraries(your_target PRIVATE tl::expected) +``` + +Or upgrade to C++23 if possible. + +--- + +## Feature Questions + +### Can I use OMath with DirectX/OpenGL/Vulkan? + +Yes! OMath matrices and vectors work with all graphics APIs. Use: +- **OpenGL**: `opengl_engine` traits +- **DirectX**: Use appropriate engine trait or OpenGL as base +- **Vulkan**: Use OpenGL traits as starting point + +### Does OMath support quaternions? + +Not currently. Quaternion support may be added in future versions. For now, use euler angles (ViewAngles) or convert manually. + +### Can I extend OMath with custom engine traits? + +Yes! Implement the `CameraEngineConcept`: + +```cpp +class MyEngineTrait { +public: + static ViewAngles calc_look_at_angle( + const Vector3& origin, + const Vector3& target + ); + + static Mat4X4 calc_view_matrix( + const ViewAngles& angles, + const Vector3& origin + ); + + static Mat4X4 calc_projection_matrix( + const FieldOfView& fov, + const ViewPort& viewport, + float near, float far + ); +}; + +// Use with Camera +using MyCamera = Camera; +``` + +### Does OMath support SIMD for vector operations? + +AVX2 support is available for projectile prediction. General vector SIMD may be added in future versions. The library already compiles to efficient code with compiler optimizations enabled. + +### Can I use OMath for machine learning? + +OMath is optimized for game development and graphics, not ML. For machine learning, consider libraries like Eigen or xtensor which are designed for that domain. + +--- + +## Debugging Questions + +### How do I print vectors? + +OMath provides `std::formatter` support: + +```cpp +#include +#include + +Vector3 v{1, 2, 3}; +std::cout << std::format("{}", v) << "\n"; // Prints: [1, 2, 3] +``` + +### How do I visualize projection problems? + +1. Check if `world_to_screen()` succeeds +2. Print camera matrices: + ```cpp + auto view = camera.get_view_matrix(); + auto proj = camera.get_projection_matrix(); + // Print matrix values + ``` +3. Test with known good points (e.g., origin, simple positions) +4. Verify viewport and FOV values + +### How can I debug pattern scanning? + +```cpp +PatternView pattern{"48 8B 05 ?? ?? ?? ??"}; + +// Print pattern details +std::cout << "Pattern length: " << pattern.size() << "\n"; +std::cout << "Pattern bytes: "; +for (auto byte : pattern) { + if (byte.has_value()) { + std::cout << std::hex << (int)*byte << " "; + } else { + std::cout << "?? "; + } +} +std::cout << "\n"; +``` + +--- + +## Contributing + +### How can I contribute to OMath? + +See [CONTRIBUTING.md](https://github.com/orange-cpp/omath/blob/master/CONTRIBUTING.md) for guidelines. Contributions welcome: +- Bug fixes +- New features +- Documentation improvements +- Test coverage +- Examples + +### Where do I report bugs? + +[GitHub Issues](https://github.com/orange-cpp/omath/issues) + +Please include: +- OMath version +- Compiler and version +- Minimal reproducible example +- Expected vs actual behavior + +### How do I request a feature? + +Open a GitHub issue with: +- Use case description +- Proposed API (if applicable) +- Why existing features don't meet your needs + +--- + +## License & Legal + +### What license does OMath use? + +OMath uses a custom "libomath" license. See [LICENSE](https://github.com/orange-cpp/omath/blob/master/LICENSE) for full details. + +### Can I use OMath in commercial projects? + +Check the LICENSE file for commercial use terms. + +### Can I use OMath for game cheating/hacking? + +OMath is a math library and can be used for various purposes. However: +- Using it to cheat in online games may violate game ToS +- Creating cheats may be illegal in your jurisdiction +- The developers do not condone cheating in online games + +Use responsibly and ethically. + +--- + +## Getting Help + +### Where can I get help? + +- **Documentation**: [http://libomath.org](http://libomath.org) +- **Discord**: [Join community](https://discord.gg/eDgdaWbqwZ) +- **Telegram**: [@orangennotes](https://t.me/orangennotes) +- **GitHub Issues**: [Report bugs/ask questions](https://github.com/orange-cpp/omath/issues) + +### Is there a Discord/community? + +Yes! Join our Discord: [https://discord.gg/eDgdaWbqwZ](https://discord.gg/eDgdaWbqwZ) + +### Are there video tutorials? + +Check our [YouTube channel](https://youtu.be/lM_NJ1yCunw?si=-Qf5yzDcWbaxAXGQ) for demonstrations and tutorials. + +--- + +## Didn't find your answer? + +- Search the [documentation](index.md) +- Check [tutorials](tutorials.md) +- Ask on [Discord](https://discord.gg/eDgdaWbqwZ) +- Open a [GitHub issue](https://github.com/orange-cpp/omath/issues) + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/getting_started.md b/docs/getting_started.md new file mode 100644 index 00000000..eaf04ec5 --- /dev/null +++ b/docs/getting_started.md @@ -0,0 +1,306 @@ +# Getting Started with OMath + +Welcome to OMath! This guide will help you get up and running with the library quickly. + +## What is OMath? + +OMath is a modern, blazingly fast C++ math library designed for: +- **Game development** and cheat development +- **Graphics programming** (DirectX/OpenGL/Vulkan) +- **3D applications** with support for multiple game engines +- **High-performance computing** with AVX2 optimizations + +Key features: +- 100% independent, no legacy C++ code +- Fully `constexpr` template-based design +- Zero additional dependencies (except for unit tests) +- Cross-platform (Windows, macOS, Linux) +- Built-in support for Source, Unity, Unreal, Frostbite, IWEngine, and OpenGL coordinate systems + +--- + +## Installation + +Choose one of the following methods to install OMath: + +### Using vcpkg (Recommended) + +```bash +vcpkg install orange-math +``` + +Then in your CMakeLists.txt: +```cmake +find_package(omath CONFIG REQUIRED) +target_link_libraries(your_target PRIVATE omath::omath) +``` + +### Using xrepo + +```bash +xrepo install omath +``` + +Then in your xmake.lua: +```lua +add_requires("omath") +target("your_target") + add_packages("omath") +``` + +### Building from Source + +See the detailed [Installation Guide](install.md) for complete instructions. + +--- + +## Quick Example + +Here's a simple example to get you started: + +```cpp +#include +#include + +int main() { + using namespace omath; + + // Create 3D vectors + Vector3 a{1.0f, 2.0f, 3.0f}; + Vector3 b{4.0f, 5.0f, 6.0f}; + + // Vector operations + auto sum = a + b; // Vector addition + auto dot_product = a.dot(b); // Dot product: 32.0 + auto cross_product = a.cross(b); // Cross product: (-3, 6, -3) + auto length = a.length(); // Length: ~3.74 + auto normalized = a.normalized(); // Unit vector + + std::cout << "Sum: [" << sum.x << ", " << sum.y << ", " << sum.z << "]\n"; + std::cout << "Dot product: " << dot_product << "\n"; + std::cout << "Length: " << length << "\n"; + + return 0; +} +``` + +--- + +## Core Concepts + +### 1. Vectors + +OMath provides 2D, 3D, and 4D vector types: + +```cpp +using namespace omath; + +Vector2 vec2{1.0f, 2.0f}; +Vector3 vec3{1.0f, 2.0f, 3.0f}; +Vector4 vec4{1.0f, 2.0f, 3.0f, 4.0f}; +``` + +All vector types support: +- Arithmetic operations (+, -, *, /) +- Dot and cross products (where applicable) +- Length and distance calculations +- Normalization +- Component-wise operations + +See: [Vector2](linear_algebra/vector2.md), [Vector3](linear_algebra/vector3.md), [Vector4](linear_algebra/vector4.md) + +### 2. Matrices + +4x4 matrices for transformations: + +```cpp +using namespace omath; + +Mat4X4 matrix = Mat4X4::identity(); +// Use for transformations, projections, etc. +``` + +See: [Matrix Documentation](linear_algebra/mat.md) + +### 3. Angles + +Strong-typed angle system with automatic range management: + +```cpp +using namespace omath; + +auto angle = Angle::from_degrees(45.0f); +auto radians = angle.as_radians(); + +// View angles for camera systems +ViewAngles view{ + PitchAngle::from_degrees(-10.0f), + YawAngle::from_degrees(90.0f), + RollAngle::from_degrees(0.0f) +}; +``` + +See: [Angle](trigonometry/angle.md), [View Angles](trigonometry/view_angles.md) + +### 4. 3D Projection + +Built-in camera and projection systems: + +```cpp +using namespace omath; +using namespace omath::projection; + +ViewPort viewport{1920.0f, 1080.0f}; +auto fov = FieldOfView::from_degrees(90.0f); + +// Example using Source Engine +using namespace omath::source_engine; +Camera cam( + Vector3{0, 0, 100}, // Position + ViewAngles{}, // Angles + viewport, + fov, + 0.1f, // near plane + 1000.0f // far plane +); + +// Project 3D point to 2D screen +Vector3 world_pos{100, 50, 75}; +if (auto screen_pos = cam.world_to_screen(world_pos)) { + std::cout << "Screen: " << screen_pos->x << ", " << screen_pos->y << "\n"; +} +``` + +See: [Camera](projection/camera.md) + +### 5. Game Engine Support + +OMath provides pre-configured traits for major game engines: + +```cpp +// Source Engine +#include +using SourceCamera = omath::source_engine::Camera; + +// Unity Engine +#include +using UnityCamera = omath::unity_engine::Camera; + +// Unreal Engine +#include +using UnrealCamera = omath::unreal_engine::Camera; + +// And more: OpenGL, Frostbite, IWEngine +``` + +Each engine has its own coordinate system conventions automatically handled. + +See: Engine-specific docs in [engines/](engines/) folder + +--- + +## Common Use Cases + +### World-to-Screen Projection + +```cpp +using namespace omath; +using namespace omath::source_engine; + +Camera cam = /* initialize camera */; +Vector3 enemy_position{100, 200, 50}; + +if (auto screen = cam.world_to_screen(enemy_position)) { + // Draw ESP box at screen->x, screen->y + std::cout << "Enemy on screen at: " << screen->x << ", " << screen->y << "\n"; +} else { + // Enemy not visible (behind camera or outside frustum) +} +``` + +### Projectile Prediction + +```cpp +using namespace omath::projectile_prediction; + +Projectile bullet{ + Vector3{0, 0, 0}, // shooter position + 1000.0f, // muzzle velocity (m/s) + Vector3{0, 0, -9.81f} // gravity +}; + +Target enemy{ + Vector3{100, 200, 50}, // position + Vector3{10, 0, 0} // velocity +}; + +// Calculate where to aim +ProjPredEngineLegacy engine; +if (auto aim_point = engine.maybe_calculate_aim_point(bullet, enemy)) { + // Aim at *aim_point to hit moving target +} +``` + +See: [Projectile Prediction](projectile_prediction/projectile_engine.md) + +### Collision Detection + +```cpp +using namespace omath; + +// Ray-plane intersection +Plane ground{ + Vector3{0, 0, 0}, // point on plane + Vector3{0, 0, 1} // normal (pointing up) +}; + +Vector3 ray_origin{0, 0, 100}; +Vector3 ray_direction{0, 0, -1}; + +if (auto hit = ground.intersects_ray(ray_origin, ray_direction)) { + std::cout << "Hit ground at: " << hit->x << ", " << hit->y << ", " << hit->z << "\n"; +} +``` + +See: [Collision Detection](collision/line_tracer.md) + +### Pattern Scanning + +```cpp +#include + +using namespace omath; + +std::vector memory = /* ... */; +PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0"}; + +if (auto result = pattern_scan(memory, pattern)) { + std::cout << "Pattern found at offset: " << result->offset << "\n"; +} +``` + +See: [Pattern Scanning](utility/pattern_scan.md) + +--- + +## Next Steps + +Now that you have the basics, explore these topics: + +1. **[API Reference](index.md)** - Complete API documentation +2. **[Examples](../examples/)** - Working code examples +3. **[Engine-Specific Features](engines/)** - Deep dive into game engine support +4. **[Advanced Topics](#)** - Performance optimization, custom traits, etc. + +--- + +## Getting Help + +- **Documentation**: [http://libomath.org](http://libomath.org) +- **Discord**: [Join our community](https://discord.gg/eDgdaWbqwZ) +- **Telegram**: [@orangennotes](https://t.me/orangennotes) +- **Issues**: [GitHub Issues](https://github.com/orange-cpp/omath/issues) + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/index.md b/docs/index.md index 61872d1c..36921807 100644 --- a/docs/index.md +++ b/docs/index.md @@ -25,12 +25,135 @@

+ OMath is a 100% independent, constexpr template blazingly fast math library that doesn't have legacy C++ code. It provides the latest features, is highly customizable, has all for cheat development, DirectX/OpenGL/Vulkan support, premade support for different game engines, much more constexpr stuff than in other libraries and more... +--- + +## 🚀 Quick Start + +**New to OMath?** Start here: + +- **[Getting Started Guide](getting_started.md)** - Installation and first steps +- **[API Overview](api_overview.md)** - High-level API reference +- **[Installation Instructions](install.md)** - Detailed setup guide + +**Quick example:** + +```cpp +#include + +using namespace omath; + +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 +``` + +--- + +## 📚 Documentation Structure + +### Core Mathematics + +**Linear Algebra** +- [Vector2](linear_algebra/vector2.md) - 2D vectors with full operator support +- [Vector3](linear_algebra/vector3.md) - 3D vectors, dot/cross products, angles +- [Vector4](linear_algebra/vector4.md) - 4D vectors (homogeneous coordinates) +- [Mat4X4](linear_algebra/mat.md) - 4×4 matrices for transformations +- [Triangle](linear_algebra/triangle.md) - Triangle primitive and utilities + +**Trigonometry** +- [Angle](trigonometry/angle.md) - Strong-typed angle system with range enforcement +- [Angles](trigonometry/angles.md) - Angle utilities and conversions +- [View Angles](trigonometry/view_angles.md) - Pitch/Yaw/Roll for camera systems + +**3D Primitives** +- [Box](3d_primitives/box.md) - Axis-aligned bounding boxes +- [Plane](3d_primitives/plane.md) - Infinite planes and intersections + +### Game Development Features + +**Projection & Camera** +- [Camera](projection/camera.md) - Generic camera system with engine traits +- [Error Codes](projection/error_codes.md) - Projection error handling + +**Collision Detection** +- [Line Tracer](collision/line_tracer.md) - Ray-triangle, ray-plane intersections + +**Projectile Prediction** +- [Projectile Engine Interface](projectile_prediction/projectile_engine.md) - Base interface +- [Projectile](projectile_prediction/projectile.md) - Projectile properties +- [Target](projectile_prediction/target.md) - Target state representation +- [Legacy Engine](projectile_prediction/proj_pred_engine_legacy.md) - Standard implementation +- [AVX2 Engine](projectile_prediction/proj_pred_engine_avx2.md) - Optimized implementation + +**Pathfinding** +- [A* Algorithm](pathfinding/a_star.md) - A* pathfinding implementation +- [Navigation Mesh](pathfinding/navigation_mesh.md) - Triangle-based navigation + +### Game Engine Support + +OMath provides built-in support for multiple game engines with proper coordinate system handling: + +**Source Engine** (Valve - CS:GO, TF2, etc.) +- [Camera Trait](engines/source_engine/camera_trait.md) +- [Pred Engine Trait](engines/source_engine/pred_engine_trait.md) +- [Constants](engines/source_engine/constants.md) +- [Formulas](engines/source_engine/formulas.md) + +**Unity Engine** +- [Camera Trait](engines/unity_engine/camera_trait.md) +- [Pred Engine Trait](engines/unity_engine/pred_engine_trait.md) +- [Constants](engines/unity_engine/constants.md) +- [Formulas](engines/unity_engine/formulas.md) + +**Unreal Engine** (Epic Games) +- [Camera Trait](engines/unreal_engine/camera_trait.md) +- [Pred Engine Trait](engines/unreal_engine/pred_engine_trait.md) +- [Constants](engines/unreal_engine/constants.md) +- [Formulas](engines/unreal_engine/formulas.md) + +**Frostbite Engine** (EA - Battlefield, etc.) +- [Camera Trait](engines/frostbite/camera_trait.md) +- [Pred Engine Trait](engines/frostbite/pred_engine_trait.md) +- [Constants](engines/frostbite/constants.md) +- [Formulas](engines/frostbite/formulas.md) + +**IW Engine** (Infinity Ward - Call of Duty) +- [Camera Trait](engines/iw_engine/camera_trait.md) +- [Pred Engine Trait](engines/iw_engine/pred_engine_trait.md) +- [Constants](engines/iw_engine/constants.md) +- [Formulas](engines/iw_engine/formulas.md) + +**OpenGL Engine** (Canonical OpenGL) +- [Camera Trait](engines/opengl_engine/camera_trait.md) +- [Pred Engine Trait](engines/opengl_engine/pred_engine_trait.md) +- [Constants](engines/opengl_engine/constants.md) +- [Formulas](engines/opengl_engine/formulas.md) + +### Utilities + +**Color** +- [Color](utility/color.md) - RGBA color with conversions + +**Pattern Scanning & Memory** +- [Pattern Scan](utility/pattern_scan.md) - Binary pattern search with wildcards +- [PE Pattern Scan](utility/pe_pattern_scan.md) - PE file pattern scanning + +**Reverse Engineering** +- [External Rev Object](rev_eng/external_rev_object.md) - External process memory access +- [Internal Rev Object](rev_eng/internal_rev_object.md) - Internal memory access + +--- + +## ✨ Key Features -# 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. @@ -43,7 +166,28 @@ It provides the latest features, is highly customizable, has all for cheat devel - **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 +--- + +## 📖 Common Use Cases + +### World-to-Screen Projection +Project 3D world coordinates to 2D screen space for ESP overlays, UI elements, or visualization. + +### Projectile Prediction +Calculate aim points for moving targets considering projectile speed, gravity, and target velocity. + +### Collision Detection +Perform ray-casting, line tracing, and intersection tests for hit detection and physics. + +### Pattern Scanning +Search for byte patterns in memory for reverse engineering, modding, or tool development. + +### Pathfinding +Find optimal paths through 3D spaces using A* algorithm and navigation meshes. + +--- + +## 🎮 Gallery
@@ -68,6 +212,26 @@ It provides the latest features, is highly customizable, has all for cheat devel

+--- + +## 🤝 Community & Support + +- **Documentation**: [http://libomath.org](http://libomath.org) +- **GitHub**: [orange-cpp/omath](https://github.com/orange-cpp/omath) +- **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 + +OMath is open source and welcomes contributions! See [CONTRIBUTING.md](https://github.com/orange-cpp/omath/blob/master/CONTRIBUTING.md) for guidelines. + +--- + +*Last updated: 1 Nov 2025* + [APEX Preview]: images/showcase/apex.png [BO2 Preview]: images/showcase/cod_bo2.png diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 00000000..a0cdf22b --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,525 @@ +# Troubleshooting Guide + +Solutions to common problems when using OMath. + +--- + +## Build & Compilation Issues + +### Error: C++20 features not available + +**Problem:** Compiler doesn't support C++20. + +**Solution:** +Upgrade your compiler: +- **GCC**: Version 10 or newer +- **Clang**: Version 11 or newer +- **MSVC**: Visual Studio 2019 16.10 or newer + +Set C++20 in CMakeLists.txt: +```cmake +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +``` + +### Error: `std::expected` not found + +**Problem:** Using C++20 without C++23's `std::expected`. + +**Solutions:** + +1. **Upgrade to C++23** (recommended): + ```cmake + set(CMAKE_CXX_STANDARD 23) + ``` + +2. **Use a backport library**: + ```cmake + find_package(tl-expected CONFIG REQUIRED) + target_link_libraries(your_target PRIVATE tl::expected) + ``` + +### Error: `omath/omath.hpp` not found + +**Problem:** OMath not installed or not in include path. + +**Solution:** + +Check installation: +```bash +# vcpkg +vcpkg list | grep omath + +# Check if files exist +ls /path/to/vcpkg/installed/x64-linux/include/omath +``` + +In CMakeLists.txt: +```cmake +find_package(omath CONFIG REQUIRED) +target_link_libraries(your_target PRIVATE omath::omath) +``` + +### Linker errors with AVX2 engine + +**Problem:** Undefined references to AVX2 functions. + +**Solution:** + +Enable AVX2 in your build: +```cmake +if(MSVC) + target_compile_options(your_target PRIVATE /arch:AVX2) +else() + target_compile_options(your_target PRIVATE -mavx2) +endif() +``` + +Or use the legacy engine instead: +```cpp +// Use this instead of ProjPredEngineAVX2 +ProjPredEngineLegacy engine; +``` + +--- + +## Runtime Issues + +### `world_to_screen()` always returns `nullopt` + +**Common causes:** + +1. **Point behind camera** + ```cpp + // Point is behind the camera + Vector3 behind = camera_pos - Vector3{0, 0, 100}; + auto result = camera.world_to_screen(behind); // Returns nullopt + ``` + + **Fix:** Only project points in front of camera. Check Z-coordinate in view space. + +2. **Invalid near/far planes** + ```cpp + // Bad: near >= far + Camera camera(pos, angles, viewport, fov, 100.0f, 1.0f); + + // Good: near < far + Camera camera(pos, angles, viewport, fov, 0.1f, 1000.0f); + ``` + +3. **Invalid FOV** + ```cpp + // Bad: FOV out of range + auto fov = FieldOfView::from_degrees(0.0f); // Too small + auto fov = FieldOfView::from_degrees(180.0f); // Too large + + // Good: FOV in valid range + auto fov = FieldOfView::from_degrees(90.0f); + ``` + +4. **Uninitialized camera** + ```cpp + // Make sure camera is properly initialized + camera.update(current_position, current_angles); + ``` + +**Debugging:** +```cpp +Vector3 world_pos{100, 100, 100}; + +// Check projection step by step +std::cout << "World pos: " << world_pos.x << ", " + << world_pos.y << ", " << world_pos.z << "\n"; + +auto view_matrix = camera.get_view_matrix(); +// Transform to view space manually and check if Z > 0 + +if (auto screen = camera.world_to_screen(world_pos)) { + std::cout << "Success: " << screen->x << ", " << screen->y << "\n"; +} else { + std::cout << "Failed - check if point is behind camera\n"; +} +``` + +### Angles wrapping incorrectly + +**Problem:** Angles not normalizing to expected ranges. + +**Solution:** + +Use proper angle types: +```cpp +// Wrong: using raw floats +float pitch = 95.0f; // Out of valid range! + +// Right: using typed angles +auto pitch = PitchAngle::from_degrees(89.0f); // Clamped to valid range +``` + +For custom ranges: +```cpp +// Define custom angle with wrapping +auto angle = Angle::from_degrees(270.0f); +// Result: -90° (wrapped) +``` + +### Projection appears mirrored or inverted + +**Problem:** Using wrong engine trait for your game. + +**Solution:** + +Different engines have different coordinate systems: + +| Symptom | Likely Issue | Fix | +|---------|-------------|-----| +| Upside down | Y-axis inverted | Try different engine or negate Y | +| Left-right flipped | Wrong handedness | Check engine documentation | +| Rotated 90° | Axis swap | Verify engine coordinate system | + +```cpp +// Try different engine traits +using namespace omath::source_engine; // Z-up, left-handed +using namespace omath::unity_engine; // Y-up, left-handed +using namespace omath::unreal_engine; // Z-up, left-handed (different conventions) +using namespace omath::opengl_engine; // Y-up, right-handed +``` + +If still wrong, manually transform coordinates: +```cpp +// Example: swap Y and Z for Y-up to Z-up conversion +Vector3 convert_y_up_to_z_up(const Vector3& pos) { + return Vector3{pos.x, pos.z, pos.y}; +} +``` + +--- + +## Projectile Prediction Issues + +### `maybe_calculate_aim_point()` returns `nullopt` + +**Common causes:** + +1. **Target moving too fast** + ```cpp + Target target; + target.velocity = Vector3{1000, 0, 0}; // Very fast! + + Projectile proj; + proj.speed = 500.0f; // Too slow to catch target + + // Returns nullopt - projectile can't catch target + ``` + + **Fix:** Check if projectile speed > target speed in the direction of motion. + +2. **Zero projectile speed** + ```cpp + Projectile proj; + proj.speed = 0.0f; // Invalid! + + // Returns nullopt + ``` + + **Fix:** Ensure `proj.speed > 0`. + +3. **Invalid positions** + ```cpp + // NaN or infinite values + target.position = Vector3{NAN, 0, 0}; + + // Returns nullopt + ``` + + **Fix:** Validate all input values are finite. + +4. **Target out of range** + ```cpp + // Target very far away + float distance = shooter_pos.distance_to(target.position); + float max_range = proj.speed * max_flight_time; + + if (distance > max_range) { + // Will return nullopt + } + ``` + +**Debugging:** +```cpp +Projectile proj{/* ... */}; +Target target{/* ... */}; + +// Check inputs +assert(proj.speed > 0); +assert(std::isfinite(target.position.length())); +assert(std::isfinite(target.velocity.length())); + +// Check if target is reachable +float distance = proj.origin.distance_to(target.position); +float target_speed = target.velocity.length(); + +std::cout << "Distance: " << distance << "\n"; +std::cout << "Projectile speed: " << proj.speed << "\n"; +std::cout << "Target speed: " << target_speed << "\n"; + +if (target_speed >= proj.speed) { + std::cout << "Target may be too fast!\n"; +} +``` + +### Aim point is inaccurate + +**Problem:** Calculated aim point doesn't hit target. + +**Possible causes:** + +1. **Unit mismatch** + ```cpp + // All units must match! + proj.speed = 800.0f; // meters per second + target.velocity = Vector3{2, 1, 0}; // Must also be m/s! + + // If using different units (e.g., game units vs meters), convert: + float game_units_to_meters = 0.01905f; // Example for Source + target.velocity = game_velocity * game_units_to_meters; + ``` + +2. **Wrong gravity vector** + ```cpp + // Source Engine: Z-up + proj.gravity = Vector3{0, 0, -9.81f}; + + // Unity: Y-up + proj.gravity = Vector3{0, -9.81f, 0}; + ``` + +3. **Target velocity not updated** + ```cpp + // Update target velocity each frame + target.velocity = current_velocity; // Not last frame's velocity! + ``` + +--- + +## Pattern Scanning Issues + +### Pattern not found when it should be + +**Problem:** `pattern_scan()` returns `nullopt` but pattern exists. + +**Solutions:** + +1. **Pattern syntax error** + ```cpp + // Wrong: missing spaces + PatternView pattern{"488B05????????"}; + + // Right: spaces between bytes + PatternView pattern{"48 8B 05 ?? ?? ?? ??"}; + ``` + +2. **Pattern too specific** + ```cpp + // May fail if any byte is different + PatternView pattern{"48 8B 05 01 02 03 04 48 85 C0"}; + + // Better: use wildcards for variable bytes + PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0"}; + ``` + +3. **Searching wrong memory region** + ```cpp + // Make sure you're scanning the right memory + std::vector code_section = get_code_section(); + auto result = pattern_scan(code_section, pattern); + ``` + +4. **Pattern might have multiple matches** + ```cpp + // Find all matches instead of just first + size_t offset = 0; + while (offset < memory.size()) { + auto result = pattern_scan( + std::span(memory.begin() + offset, memory.end()), + pattern + ); + if (result) { + std::cout << "Match at: " << offset + result->offset << "\n"; + offset += result->offset + 1; + } else { + break; + } + } + ``` + +### Pattern found at wrong location + +**Problem:** Pattern matches unintended code. + +**Solutions:** + +1. **Make pattern more specific** + ```cpp + // Too generic + PatternView pattern{"48 8B"}; + + // More specific - include more context + PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0 74 ??"}; + ``` + +2. **Verify found address** + ```cpp + if (auto result = pattern_scan(memory, pattern)) { + // Verify by checking nearby bytes + size_t offset = result->offset; + + // Check if instruction makes sense + if (memory[offset] == 0x48 && memory[offset + 1] == 0x8B) { + // Looks good + } + } + ``` + +3. **Use multiple patterns** + ```cpp + // Find reference function first + auto ref_pattern = PatternView{"E8 ?? ?? ?? ?? 85 C0"}; + auto ref_result = pattern_scan(memory, ref_pattern); + + // Then search near that location + // This provides context validation + ``` + +--- + +## Vector & Math Issues + +### `normalized()` returns zero vector + +**Problem:** Normalizing a zero-length vector. + +**Behavior:** +```cpp +Vector3 zero{0, 0, 0}; +auto result = zero.normalized(); // Returns {0, 0, 0} +``` + +This is **intentional** to avoid NaN. Check vector length first: +```cpp +if (v.length() > 0.001f) { + auto normalized = v.normalized(); + // Use normalized vector +} else { + // Handle zero-length case +} +``` + +### `angle_between()` returns error + +**Problem:** One or both vectors have zero length. + +**Solution:** +```cpp +auto angle_result = v1.angle_between(v2); + +if (angle_result) { + float degrees = angle_result->as_degrees(); +} else { + // Handle error - one or both vectors have zero length + std::cerr << "Cannot compute angle between zero-length vectors\n"; +} +``` + +### Cross product seems wrong + +**Problem:** Unexpected cross product result. + +**Check:** +1. **Right-handed system** + ```cpp + Vector3 x{1, 0, 0}; + Vector3 y{0, 1, 0}; + auto z = x.cross(y); // Should be {0, 0, 1} in right-handed system + ``` + +2. **Order matters** + ```cpp + auto cross1 = a.cross(b); // {x1, y1, z1} + auto cross2 = b.cross(a); // {-x1, -y1, -z1} (opposite direction!) + ``` + +--- + +## Performance Issues + +### Code is slower than expected + +**Solutions:** + +1. **Enable optimizations** + ```cmake + # CMakeLists.txt + target_compile_options(your_target PRIVATE + $<$:-O3> + $<$:-march=native> + ) + ``` + +2. **Use AVX2 engine** + ```cpp + // Instead of + ProjPredEngineLegacy engine; + + // Use + ProjPredEngineAVX2 engine; + ``` + +3. **Avoid unnecessary operations** + ```cpp + // Bad: recompute every frame + for (auto& entity : entities) { + float dist = entity.pos.distance_to(player_pos); // Expensive sqrt! + if (dist < 100.0f) { /* ... */ } + } + + // Good: use squared distance + constexpr float max_dist_sq = 100.0f * 100.0f; + for (auto& entity : entities) { + float dist_sq = entity.pos.distance_to_sqr(player_pos); // No sqrt! + if (dist_sq < max_dist_sq) { /* ... */ } + } + ``` + +4. **Cache matrices** + ```cpp + // Bad: recompute matrix every call + for (auto& pos : positions) { + auto screen = camera.world_to_screen(pos); // Recomputes matrices! + } + + // Good: matrices are cached in camera automatically + camera.update(pos, angles); // Updates matrices once + for (auto& pos : positions) { + auto screen = camera.world_to_screen(pos); // Uses cached matrices + } + ``` + +--- + +## Getting More Help + +If your issue isn't covered here: + +1. **Check the docs**: [API Overview](api_overview.md), [Tutorials](tutorials.md) +2. **Search GitHub issues**: [Issues page](https://github.com/orange-cpp/omath/issues) +3. **Ask on Discord**: [Join community](https://discord.gg/eDgdaWbqwZ) +4. **Open a new issue**: Include: + - OMath version + - Compiler and version + - Minimal reproducible example + - What you expected vs what happened + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/tutorials.md b/docs/tutorials.md new file mode 100644 index 00000000..05f14806 --- /dev/null +++ b/docs/tutorials.md @@ -0,0 +1,616 @@ +# OMath Tutorials + +This page provides step-by-step tutorials for common OMath use cases. + +--- + +## Tutorial 1: Basic Vector Math + +Learn the fundamentals of vector operations in OMath. + +### Step 1: Include OMath + +```cpp +#include +#include + +using namespace omath; +``` + +### Step 2: Create Vectors + +```cpp +// 2D vectors +Vector2 v2a{3.0f, 4.0f}; +Vector2 v2b{1.0f, 2.0f}; + +// 3D vectors +Vector3 v3a{1.0f, 2.0f, 3.0f}; +Vector3 v3b{4.0f, 5.0f, 6.0f}; + +// 4D vectors (often used for homogeneous coordinates) +Vector4 v4{1.0f, 2.0f, 3.0f, 1.0f}; +``` + +### Step 3: Perform Operations + +```cpp +// Addition +auto sum = v3a + v3b; // {5, 7, 9} + +// Subtraction +auto diff = v3a - v3b; // {-3, -3, -3} + +// Scalar multiplication +auto scaled = v3a * 2.0f; // {2, 4, 6} + +// Dot product +float dot = v3a.dot(v3b); // 32.0 + +// Cross product (3D only) +auto cross = v3a.cross(v3b); // {-3, 6, -3} + +// Length +float len = v3a.length(); // ~3.74 + +// Normalization (safe - returns original if length is zero) +auto normalized = v3a.normalized(); + +// Distance between vectors +float dist = v3a.distance_to(v3b); // ~5.196 +``` + +### Step 4: Angle Calculations + +```cpp +if (auto angle = v3a.angle_between(v3b)) { + std::cout << "Angle in degrees: " << angle->as_degrees() << "\n"; + std::cout << "Angle in radians: " << angle->as_radians() << "\n"; +} else { + std::cout << "Cannot compute angle (zero-length vector)\n"; +} + +// Check if perpendicular +if (v3a.is_perpendicular(v3b)) { + std::cout << "Vectors are perpendicular\n"; +} +``` + +**Key takeaways:** +- All vector operations are type-safe and constexpr-friendly +- Safe normalization never produces NaN +- Angle calculations use `std::expected` for error handling + +--- + +## Tutorial 2: World-to-Screen Projection + +Project 3D coordinates to 2D screen space for overlays and ESP. + +### Step 1: Choose Your Game Engine + +```cpp +#include + +// For Source Engine games (CS:GO, TF2, etc.) +using namespace omath::source_engine; + +// Or for other engines: +// using namespace omath::unity_engine; +// using namespace omath::unreal_engine; +// using namespace omath::frostbite_engine; +``` + +### Step 2: Set Up the Camera + +```cpp +using namespace omath; +using namespace omath::projection; + +// Define viewport (screen dimensions) +ViewPort viewport{1920.0f, 1080.0f}; + +// Define field of view +auto fov = FieldOfView::from_degrees(90.0f); + +// Camera position and angles +Vector3 camera_pos{0.0f, 0.0f, 100.0f}; +ViewAngles camera_angles{ + PitchAngle::from_degrees(0.0f), + YawAngle::from_degrees(0.0f), + RollAngle::from_degrees(0.0f) +}; + +// Create camera (using Source Engine in this example) +Camera camera( + camera_pos, + camera_angles, + viewport, + fov, + 0.1f, // near plane + 1000.0f // far plane +); +``` + +### Step 3: Project 3D Points + +```cpp +// 3D world position (e.g., enemy player position) +Vector3 enemy_pos{150.0f, 200.0f, 75.0f}; + +// Project to screen +if (auto screen = camera.world_to_screen(enemy_pos)) { + std::cout << "Enemy on screen at: " + << screen->x << ", " << screen->y << "\n"; + + // Draw ESP box or marker at screen->x, screen->y + // Note: screen coordinates are in viewport space (0-width, 0-height) +} else { + // Enemy is not visible (behind camera or outside frustum) + std::cout << "Enemy not visible\n"; +} +``` + +### Step 4: Update Camera for Each Frame + +```cpp +void render_frame() { + // Read current camera data from game + Vector3 new_pos = read_camera_position(); + ViewAngles new_angles = read_camera_angles(); + + // Update camera + camera.update(new_pos, new_angles); + + // Project all entities + for (const auto& entity : entities) { + if (auto screen = camera.world_to_screen(entity.position)) { + draw_esp_box(screen->x, screen->y); + } + } +} +``` + +**Key takeaways:** +- Choose the engine trait that matches your target game +- `world_to_screen()` returns `std::optional` - always check the result +- Update camera each frame for accurate projections +- Screen coordinates are in the viewport space you defined + +--- + +## Tutorial 3: Projectile Prediction (Aim-Bot) + +Calculate where to aim to hit a moving target. + +### Step 1: Define Projectile Properties + +```cpp +#include +#include + +using namespace omath; +using namespace omath::projectile_prediction; + +// Define your weapon's projectile +Projectile bullet; +bullet.origin = Vector3{0, 0, 0}; // Shooter position +bullet.speed = 800.0f; // Muzzle velocity (m/s or game units/s) +bullet.gravity = Vector3{0, 0, -9.81f}; // Gravity vector +``` + +### Step 2: Define Target State + +```cpp +// Target information (enemy player) +Target enemy; +enemy.position = Vector3{100, 200, 50}; // Current position +enemy.velocity = Vector3{10, 5, 0}; // Current velocity +``` + +### Step 3: Calculate Aim Point + +```cpp +// Create prediction engine +// Use AVX2 version if available for better performance: +// ProjPredEngineAVX2 engine; +ProjPredEngineLegacy engine; + +// Calculate where to aim +if (auto aim_point = engine.maybe_calculate_aim_point(bullet, enemy)) { + std::cout << "Aim at: " + << aim_point->x << ", " + << aim_point->y << ", " + << aim_point->z << "\n"; + + // Calculate angles to aim_point + Vector3 aim_direction = (*aim_point - bullet.origin).normalized(); + + // Convert to view angles (engine-specific) + // ViewAngles angles = calculate_angles_to_direction(aim_direction); + // set_aim_angles(angles); +} else { + // Cannot hit target (too fast, out of range, etc.) + std::cout << "Target cannot be hit\n"; +} +``` + +### Step 4: Handle Different Scenarios + +```cpp +// Stationary target +Target stationary; +stationary.position = Vector3{100, 100, 100}; +stationary.velocity = Vector3{0, 0, 0}; +// aim_point will equal position for stationary targets + +// Fast-moving target +Target fast; +fast.position = Vector3{100, 100, 100}; +fast.velocity = Vector3{50, 0, 0}; // Moving very fast +// May return nullopt if target is too fast + +// Target at different heights +Target aerial; +aerial.position = Vector3{100, 100, 200}; // High up +aerial.velocity = Vector3{5, 5, -10}; // Falling +// Gravity will be factored into the calculation +``` + +### Step 5: Performance Optimization + +```cpp +// For better performance on modern CPUs, use AVX2: +#include + +ProjPredEngineAVX2 fast_engine; // 2-4x faster than legacy + +// Use the same way as legacy engine +if (auto aim = fast_engine.maybe_calculate_aim_point(bullet, enemy)) { + // Process aim point +} +``` + +**Key takeaways:** +- Always check if aim point exists before using +- Velocity must be in same units as position/speed +- Gravity vector points down (typically negative Z or Y depending on engine) +- Use AVX2 engine when possible for better performance +- Returns `nullopt` when target is unreachable + +--- + +## Tutorial 4: Collision Detection + +Perform ray-casting and intersection tests. + +### Step 1: Ray-Plane Intersection + +```cpp +#include + +using namespace omath; + +// Define a ground plane (Z=0, normal pointing up) +Plane ground{ + Vector3{0, 0, 0}, // Point on plane + Vector3{0, 0, 1} // Normal vector (Z-up) +}; + +// Define a ray (e.g., looking downward from above) +Vector3 ray_origin{10, 20, 100}; +Vector3 ray_direction{0, 0, -1}; // Pointing down + +// Test intersection +if (auto hit = ground.intersects_ray(ray_origin, ray_direction)) { + std::cout << "Hit ground at: " + << hit->x << ", " << hit->y << ", " << hit->z << "\n"; + // Expected: (10, 20, 0) +} else { + std::cout << "Ray does not intersect plane\n"; +} +``` + +### Step 2: Distance to Plane + +```cpp +// Calculate signed distance from point to plane +Vector3 point{10, 20, 50}; +float distance = ground.distance_to_point(point); + +std::cout << "Distance to ground: " << distance << "\n"; +// Expected: 50.0 (50 units above ground) + +// Negative distance means point is below the plane +Vector3 below{10, 20, -5}; +float dist_below = ground.distance_to_point(below); +// Expected: -5.0 +``` + +### Step 3: Axis-Aligned Bounding Box + +```cpp +#include + +// Create a bounding box +Box bbox{ + Vector3{0, 0, 0}, // Min corner + Vector3{100, 100, 100} // Max corner +}; + +// Test if point is inside +Vector3 inside{50, 50, 50}; +if (bbox.contains(inside)) { + std::cout << "Point is inside box\n"; +} + +Vector3 outside{150, 50, 50}; +if (!bbox.contains(outside)) { + std::cout << "Point is outside box\n"; +} + +// Box-box intersection +Box other{ + Vector3{50, 50, 50}, + Vector3{150, 150, 150} +}; + +if (bbox.intersects(other)) { + std::cout << "Boxes overlap\n"; +} +``` + +### Step 4: Line Tracing + +```cpp +#include + +using namespace omath::collision; + +// Ray-triangle intersection +Vector3 v0{0, 0, 0}; +Vector3 v1{100, 0, 0}; +Vector3 v2{0, 100, 0}; + +Vector3 ray_start{25, 25, 100}; +Vector3 ray_dir{0, 0, -1}; + +LineTracer tracer; +if (auto hit = tracer.ray_triangle_intersect(ray_start, ray_dir, v0, v1, v2)) { + std::cout << "Hit triangle at: " + << hit->point.x << ", " + << hit->point.y << ", " + << hit->point.z << "\n"; + std::cout << "Hit distance: " << hit->distance << "\n"; + std::cout << "Surface normal: " + << hit->normal.x << ", " + << hit->normal.y << ", " + << hit->normal.z << "\n"; +} +``` + +**Key takeaways:** +- Plane normals should be unit vectors +- Ray direction should typically be normalized +- Signed distance indicates which side of plane a point is on +- AABB tests are very fast for broad-phase collision detection +- Line tracer provides hit point, distance, and surface normal + +--- + +## Tutorial 5: Pattern Scanning + +Search for byte patterns in memory. + +### Step 1: Basic Pattern Scanning + +```cpp +#include +#include + +using namespace omath; + +// Memory to search (e.g., from a loaded module) +std::vector memory = { + 0x48, 0x8B, 0x05, 0xAA, 0xBB, 0xCC, 0xDD, + 0x48, 0x85, 0xC0, 0x74, 0x10, + // ... more bytes +}; + +// Pattern with wildcards (?? = match any byte) +PatternView pattern{"48 8B 05 ?? ?? ?? ?? 48 85 C0"}; + +// Scan for pattern +if (auto result = pattern_scan(memory, pattern)) { + std::cout << "Pattern found at offset: " << result->offset << "\n"; + + // Extract wildcard values if needed + // result->wildcards contains the matched bytes at ?? positions +} else { + std::cout << "Pattern not found\n"; +} +``` + +### Step 2: PE File Scanning + +```cpp +#include + +// Scan a PE file (EXE or DLL) +PEPatternScanner scanner("game.exe"); + +PatternView pattern{"E8 ?? ?? ?? ?? 85 C0 75 ??"}; + +if (auto rva = scanner.scan_pattern(pattern)) { + std::cout << "Pattern found at RVA: 0x" + << std::hex << *rva << std::dec << "\n"; + + // Convert RVA to absolute address if needed + uintptr_t base_address = get_module_base("game.exe"); + uintptr_t absolute = base_address + *rva; +} else { + std::cout << "Pattern not found in PE file\n"; +} +``` + +### Step 3: Multiple Patterns + +```cpp +// Search for multiple patterns +std::vector patterns{ + PatternView{"48 8B 05 ?? ?? ?? ??"}, + PatternView{"E8 ?? ?? ?? ?? 85 C0"}, + PatternView{"FF 15 ?? ?? ?? ?? 48 8B"} +}; + +for (size_t i = 0; i < patterns.size(); ++i) { + if (auto result = pattern_scan(memory, patterns[i])) { + std::cout << "Pattern " << i << " found at: " + << result->offset << "\n"; + } +} +``` + +### Step 4: Pattern with Masks + +```cpp +// Alternative: use mask-based patterns +// Pattern: bytes to match +std::vector pattern_bytes{0x48, 0x8B, 0x05, 0x00, 0x00, 0x00, 0x00}; + +// Mask: 'x' = must match, '?' = wildcard +std::string mask{"xxx????"}; + +// Custom scan function +auto scan_with_mask = [&](const std::vector& data) { + for (size_t i = 0; i < data.size() - pattern_bytes.size(); ++i) { + bool match = true; + for (size_t j = 0; j < pattern_bytes.size(); ++j) { + if (mask[j] == 'x' && data[i + j] != pattern_bytes[j]) { + match = false; + break; + } + } + if (match) return i; + } + return size_t(-1); +}; +``` + +**Key takeaways:** +- Use `??` in pattern strings for wildcards +- PE scanner works with files and modules +- Pattern scanning is useful for finding functions, vtables, or data +- Always validate found addresses before use +- Patterns may have multiple matches - consider context + +--- + +## Tutorial 6: Angles and View Angles + +Work with game camera angles properly. + +### Step 1: Understanding Angle Types + +```cpp +#include + +using namespace omath; + +// Generic angle with custom range +auto angle1 = Angle::from_degrees(45.0f); +auto angle2 = Angle::from_degrees(270.0f); + +// Specialized camera angles +auto pitch = PitchAngle::from_degrees(-10.0f); // Looking down +auto yaw = YawAngle::from_degrees(90.0f); // Looking right +auto roll = RollAngle::from_degrees(0.0f); // No tilt +``` + +### Step 2: Angle Conversions + +```cpp +// Create from degrees +auto deg_angle = PitchAngle::from_degrees(45.0f); + +// Get as radians +float radians = deg_angle.as_radians(); +std::cout << "45° = " << radians << " radians\n"; + +// Get as degrees +float degrees = deg_angle.as_degrees(); +std::cout << "Value: " << degrees << "°\n"; +``` + +### Step 3: View Angles (Camera) + +```cpp +// Pitch: vertical rotation (-89° to 89°) +// Yaw: horizontal rotation (-180° to 180°) +// Roll: camera tilt (-180° to 180°) + +ViewAngles camera_angles{ + PitchAngle::from_degrees(-15.0f), // Looking slightly down + YawAngle::from_degrees(45.0f), // Facing northeast + RollAngle::from_degrees(0.0f) // No tilt +}; + +// Access individual components +float pitch_val = camera_angles.pitch.as_degrees(); +float yaw_val = camera_angles.yaw.as_degrees(); +float roll_val = camera_angles.roll.as_degrees(); +``` + +### Step 4: Calculating Look-At Angles + +```cpp +using namespace omath::source_engine; // Or your game's engine + +Vector3 camera_pos{0, 0, 100}; +Vector3 target_pos{100, 100, 100}; + +// Calculate angles to look at target +ViewAngles look_at = CameraTrait::calc_look_at_angle(camera_pos, target_pos); + +std::cout << "Pitch: " << look_at.pitch.as_degrees() << "°\n"; +std::cout << "Yaw: " << look_at.yaw.as_degrees() << "°\n"; +std::cout << "Roll: " << look_at.roll.as_degrees() << "°\n"; +``` + +### Step 5: Angle Arithmetic + +```cpp +// Angles support arithmetic with automatic normalization +auto angle1 = YawAngle::from_degrees(170.0f); +auto angle2 = YawAngle::from_degrees(20.0f); + +// Addition (wraps around) +auto sum = angle1 + angle2; // 190° → normalized to -170° + +// Subtraction +auto diff = angle2 - angle1; // -150° + +// Scaling +auto scaled = angle1 * 2.0f; +``` + +**Key takeaways:** +- Use specialized angle types for camera angles (PitchAngle, YawAngle, RollAngle) +- Angles automatically normalize to their valid ranges +- Each game engine may have different angle conventions +- Use engine traits to calculate look-at angles correctly + +--- + +## Next Steps + +Now that you've completed these tutorials, explore: + +- **[API Overview](api_overview.md)** - Complete API reference +- **[Engine Documentation](engines/)** - Engine-specific features +- **[Examples](../examples/)** - More code examples +- **[Getting Started](getting_started.md)** - Quick start guide + +--- + +*Last updated: 1 Nov 2025* From f3d5f84d2c476a851b19d979ff906ec48017c431 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 09:54:08 +0000 Subject: [PATCH 12/14] Improve documentation with cross-references and README enhancements Co-authored-by: orange-cpp <59374393+orange-cpp@users.noreply.github.com> --- README.md | 69 +++++++++++++++---- docs/collision/line_tracer.md | 12 +++- docs/engines/source_engine/camera_trait.md | 8 ++- docs/linear_algebra/vector2.md | 11 ++- docs/linear_algebra/vector3.md | 12 +++- .../projectile_engine.md | 10 +++ docs/projection/camera.md | 9 +++ 7 files changed, 114 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 11183887..0bf77770 100644 --- a/README.md +++ b/README.md @@ -43,18 +43,45 @@ 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 @@ -84,6 +111,22 @@ 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) @@ -93,8 +136,10 @@ It provides the latest features, is highly customizable, has all for cheat devel [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/collision/line_tracer.md b/docs/collision/line_tracer.md index 23cefccc..9e9e1f8a 100644 --- a/docs/collision/line_tracer.md +++ b/docs/collision/line_tracer.md @@ -168,4 +168,14 @@ public: --- -*Last updated: 31 Oct 2025* +## 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/source_engine/camera_trait.md b/docs/engines/source_engine/camera_trait.md index b4c1c655..610fc610 100644 --- a/docs/engines/source_engine/camera_trait.md +++ b/docs/engines/source_engine/camera_trait.md @@ -105,5 +105,9 @@ This satisfies `CameraEngineConcept` expected by `projection::Camera` (look-at, ## See also -* Source Engine math helpers in `omath/engines/source_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). +* [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/linear_algebra/vector2.md b/docs/linear_algebra/vector2.md index 483b1444..7f044754 100644 --- a/docs/linear_algebra/vector2.md +++ b/docs/linear_algebra/vector2.md @@ -288,4 +288,13 @@ static Vector2 from_im_vec2(const ImVec2&) noexcept; --- -*Last updated: 31 Oct 2025* +## See Also + +- [Vector3 Documentation](vector3.md) - 3D vector operations +- [Vector4 Documentation](vector4.md) - 4D vector operations +- [Getting Started Guide](../getting_started.md) - Quick start with OMath +- [Tutorials](../tutorials.md) - Step-by-step examples + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/linear_algebra/vector3.md b/docs/linear_algebra/vector3.md index 63479252..15dda160 100644 --- a/docs/linear_algebra/vector3.md +++ b/docs/linear_algebra/vector3.md @@ -294,4 +294,14 @@ bool is_perpendicular(const Vector3&) const noexcept; --- -*Last updated: 31 Oct 2025* +## See Also + +- [Vector2 Documentation](vector2.md) - 2D vector operations +- [Vector4 Documentation](vector4.md) - 4D vector operations +- [Angle Documentation](../trigonometry/angle.md) - Working with angles +- [Getting Started Guide](../getting_started.md) - Quick start with OMath +- [Tutorials](../tutorials.md) - Practical examples including vector math + +--- + +*Last updated: 1 Nov 2025* diff --git a/docs/projectile_prediction/projectile_engine.md b/docs/projectile_prediction/projectile_engine.md index db0cf331..a47ff637 100644 --- a/docs/projectile_prediction/projectile_engine.md +++ b/docs/projectile_prediction/projectile_engine.md @@ -149,4 +149,14 @@ Return `nullopt` if `t*` is absent. --- +## See Also + +- [Projectile Documentation](projectile.md) - Projectile properties +- [Target Documentation](target.md) - Target state representation +- [Legacy Implementation](proj_pred_engine_legacy.md) - Standard projectile prediction engine +- [AVX2 Implementation](proj_pred_engine_avx2.md) - Optimized AVX2 engine +- [Tutorials - Projectile Prediction](../tutorials.md#tutorial-3-projectile-prediction-aim-bot) - Complete aim-bot tutorial + +--- + *Last updated: 1 Nov 2025* diff --git a/docs/projection/camera.md b/docs/projection/camera.md index b9cf47ba..42f7ebd1 100644 --- a/docs/projection/camera.md +++ b/docs/projection/camera.md @@ -258,4 +258,13 @@ struct LHCTrait { --- +## See Also + +- [Engine-Specific Camera Traits](../engines/) - Camera implementations for different game engines +- [View Angles Documentation](../trigonometry/view_angles.md) - Understanding pitch/yaw/roll +- [Getting Started Guide](../getting_started.md) - Quick start with projection +- [Tutorials - World-to-Screen](../tutorials.md#tutorial-2-world-to-screen-projection) - Complete projection tutorial + +--- + *Last updated: 1 Nov 2025* From d3e379e71b31c6b34a598bbdc81474da5ae7a480 Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 1 Nov 2025 12:57:33 +0300 Subject: [PATCH 13/14] Removes dark overrides CSS file Deletes the `dark-overrides.css` file. This file appears to have been superseded by other theming mechanisms and is no longer needed. Its presence was causing potential conflicts. --- docs/styles/dark-overrides.css | 67 ---------------------------------- 1 file changed, 67 deletions(-) delete mode 100644 docs/styles/dark-overrides.css diff --git a/docs/styles/dark-overrides.css b/docs/styles/dark-overrides.css deleted file mode 100644 index eef24c46..00000000 --- a/docs/styles/dark-overrides.css +++ /dev/null @@ -1,67 +0,0 @@ -/* styles/dark-overrides.css */ -:root[data-md-color-scheme="mydark"] { - color-scheme: dark; /* important for native UI */ - --md-default-bg-color: #0d0f12; - --md-code-bg-color: #12151a; - --md-footer-bg-color: #0b0d10; - - --md-default-fg-color: #dfe5ef; - --md-typeset-color: #dfe5ef; - --md-default-fg-color--light: #aab2bf; - --md-code-fg-color: #e6edf3; - - --md-primary-fg-color: #7c4dff; - --md-accent-fg-color: #c6ff00; - - --md-default-fg-color--lighter: #2a2f36; - --md-shadow-z1: 0 2px 6px rgba(0,0,0,.35); -} - -/* Ensure the page chrome actually uses the bg (prevents white flashes/areas) */ -html[data-md-color-scheme="mydark"], -body[data-md-color-scheme="mydark"], -.md-header, .md-main, .md-footer, .md-sidebar { - background-color: #0d0f12 !important; -} - -/* Optional: code tokens */ -:root[data-md-color-scheme="mydark"] .md-typeset code, -:root[data-md-color-scheme="mydark"] .md-typeset pre { - --md-code-hl-keyword-color: #c678dd; - --md-code-hl-string-color: #98c379; - --md-code-hl-name-color: #61afef; - --md-code-hl-number-color: #d19a66; -} -/* Make default/body text white-ish in dark mode */ -:root[data-md-color-scheme="mydark"] { - --md-default-fg-color: #e6edf3; /* global text */ - --md-default-fg-color--light: #c5cfdb; - --md-default-fg-color--lighter: #9aa4b2; - --md-typeset-color: #e6edf3; /* content text */ -} - -/* Ensure it actually applies (beats theme selectors) */ -:root[data-md-color-scheme="mydark"] body, -:root[data-md-color-scheme="mydark"] .md-content, -:root[data-md-color-scheme="mydark"] .md-typeset { - color: var(--md-typeset-color) !important; -} - -/* Nav, header, footer text */ -:root[data-md-color-scheme="mydark"] .md-header, -:root[data-md-color-scheme="mydark"] .md-footer, -:root[data-md-color-scheme="mydark"] .md-nav, -:root[data-md-color-scheme="mydark"] .md-nav__link { - color: var(--md-default-fg-color) !important; -} - -/* Link color in articles (optional) */ -:root[data-md-color-scheme="mydark"] .md-typeset a { - color: var(--md-accent-fg-color); -} - -/* Inline code text (optional) */ -:root[data-md-color-scheme="mydark"] code, -:root[data-md-color-scheme="mydark"] pre { - color: var(--md-code-fg-color) !important; -} From 754b370d8bb10ca33238001dcddf96937acf0386 Mon Sep 17 00:00:00 2001 From: Orange Date: Sat, 1 Nov 2025 13:00:32 +0300 Subject: [PATCH 14/14] Renames documentation titles for consistency Updates several documentation titles to a more consistent format. Changes include renaming "Frequently Asked Questions (FAQ)" to "FAQ", "Getting Started with OMath" to "Getting Started", "Troubleshooting Guide" to "Troubleshooting", and "OMath Tutorials" to "Tutorials". ``` --- docs/faq.md | 2 +- docs/getting_started.md | 3 +-- docs/troubleshooting.md | 2 +- docs/tutorials.md | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/docs/faq.md b/docs/faq.md index 9f50e14d..60f4ef93 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -1,4 +1,4 @@ -# Frequently Asked Questions (FAQ) +# FAQ Common questions and answers about OMath. diff --git a/docs/getting_started.md b/docs/getting_started.md index eaf04ec5..2d05f738 100644 --- a/docs/getting_started.md +++ b/docs/getting_started.md @@ -1,5 +1,4 @@ -# Getting Started with OMath - +# Getting Started Welcome to OMath! This guide will help you get up and running with the library quickly. ## What is OMath? diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md index a0cdf22b..7fc8574d 100644 --- a/docs/troubleshooting.md +++ b/docs/troubleshooting.md @@ -1,4 +1,4 @@ -# Troubleshooting Guide +# Troubleshooting Solutions to common problems when using OMath. diff --git a/docs/tutorials.md b/docs/tutorials.md index 05f14806..d911973f 100644 --- a/docs/tutorials.md +++ b/docs/tutorials.md @@ -1,4 +1,4 @@ -# OMath Tutorials +# Tutorials This page provides step-by-step tutorials for common OMath use cases.