Compare commits

...

45 Commits

Author SHA1 Message Date
orange 8e7e1dfc42 Includes missing formula headers
Includes the necessary formula headers in the camera trait files for different rendering engines.
This ensures that the required mathematical functions and definitions are available when using camera traits.
2025-08-12 19:08:30 +03:00
orange 46ae5668ab Fixes Vector3 conversion from ImVec2
Corrects the static function signature for creating a Vector3 from an ImVec2.

It was mistakenly returning a Vector3 instead of Vector3.

patch
2025-08-12 09:38:43 +03:00
orange cdde09c70c feat(omath): Add NDC to screen position coordinate diagram 2025-08-12 09:29:14 +03:00
orange 5ccd3a454a Updates project version to 3.2.1
Increments the project version number in CMakeLists.txt
from 3.0.4.1 to 3.2.1, indicating a release or significant
changes in the project.
2025-08-12 09:25:40 +03:00
orange 330e1cbfb4 Adds concept for prediction engine traits
Introduces a concept `PredEngineConcept` to ensure that classes used as projectile prediction engine traits conform to a specific interface.
This enforces the presence and return types of required methods and ensures that these methods are `noexcept`, improving type safety and predictability.
2025-08-12 09:21:34 +03:00
orange 62c86fdc09 Introduces CameraEngine concept
Adds a concept `CameraEngineConcept` to ensure that camera
engine implementations provide the necessary functions
with the correct signatures and `noexcept` specifications.

This enables compile-time checks for valid camera engine
implementations, improving code reliability and preventing
runtime errors.
2025-08-12 09:13:02 +03:00
orange 0bda75e346 Merge pull request #54 from orange-cpp/feature/vec_fix_improvement
Adds comparison operators to Vector types
2025-08-12 08:57:31 +03:00
orange 24a80c4f14 Adds comparison operators to Vector types
Adds less than, greater than, less than or equal, and greater than or equal operators to the Vector2, Vector3 and Vector4 classes.
The comparison is based on the lengths of the vectors.

Adds corresponding unit tests.
2025-08-12 08:54:33 +03:00
orange 94bc18ba18 Removes virtual destructor from Camera
Removes the virtual destructor from the Camera class as it is not required,
as the class does not act as a base class. This simplifies the class
definition and avoids potential vtable overhead.
2025-08-11 01:43:22 +03:00
orange 7f2084969f Update issue templates 2025-08-11 01:30:26 +03:00
orange b4fbcba8f2 Marks Camera class as final
Prevents inheritance from the Camera class.
2025-08-11 01:18:39 +03:00
orange c792c92192 Merge pull request #53 from orange-cpp/u/camera_type_traits
Refactors camera and prediction using traits
2025-08-11 00:47:25 +03:00
orange 1f4b10f336 Refactors camera and prediction engine traits.
Moves camera and prediction engine implementations into traits for each engine,
decoupling the engine-specific logic from the core classes, promoting code reuse
and maintainability. This change allows for easier addition of new engines and
customization of existing ones.
2025-08-11 00:32:09 +03:00
orange 741c4e5396 Updates googletest submodule
Updates the googletest submodule to the latest commit.

This brings in the latest fixes and improvements from the
googletest project.
2025-08-07 23:33:30 +03:00
orange 7556e9134b Removes projectile.cpp
Removes the projectile.cpp file. The removal indicates that the associated projectile prediction functionality is no longer needed or has been moved elsewhere.
2025-08-06 18:37:28 +03:00
orange db6159f8f1 Adds community badges to README
Enhances the README by adding Discord and Telegram badges, providing users with direct access to community channels for support and discussions.
2025-08-06 06:39:57 +03:00
orange 7a9fa626e4 Adds contributing guidelines
Introduces a CONTRIBUTING.MD file to provide guidelines for
contributing to the project, including prerequisites, setup
instructions, pull request workflow, code style, and building
instructions.

xd

returned back

patch
2025-08-06 06:23:06 +03:00
orange 5be521ad2c Refactors Vector operations for type safety
Ensures type safety in Vector2, Vector3, and Vector4 operations by using static_cast(0) instead of relying on implicit conversions.
This prevents potential issues with different numeric types.

Adds from_im_vec2 and from_im_vec4 methods for creating vectors from ImVec2/ImVec4 types.
2025-08-06 06:06:42 +03:00
orange 9ad71be0da patch 2025-08-06 05:56:09 +03:00
orange a3b6435bac Merge pull request #52 from orange-cpp/feature/more_traits
Adds engine traits for projectile prediction
2025-08-06 05:49:25 +03:00
orange 0abfeba109 Adds engine traits for projectile prediction
Implements engine-specific traits for projectile and target position prediction.
Each trait encapsulates logic tailored to a specific game engine (IW, OpenGL, Unity),
accounting for engine-specific coordinate systems and calculations.
This allows for accurate projectile prediction across different game environments.
2025-08-06 05:45:37 +03:00
orange 5c9b36a03c Refactors target position prediction
Moves target prediction logic into engine traits, improving modularity.

This change consolidates target position prediction within the engine traits,
allowing for a more flexible and maintainable design.

This eliminates redundant code and simplifies the core prediction engine by
delegating target movement calculations to the appropriate trait.
2025-08-04 03:16:04 +03:00
orange 628361aff8 Marks legacy engine class as final
Prevents further inheritance from the legacy projectile prediction engine class.
2025-08-04 01:12:22 +03:00
orange 0e403f916d Adds direct pitch angle calculation
Implements a direct pitch angle calculation for scenarios with zero gravity, ensuring accurate projectile trajectory predictions in such conditions.

Also marks several methods as noexcept for better performance and exception safety.
2025-08-04 01:11:11 +03:00
orange a6fc92dd3a Updates project version and removes legacy code
Updates the project version to prepare for a new release.

Removes the legacy projectile prediction engine, which is no longer needed.
2025-08-03 18:35:52 +03:00
orange b16157e976 Ignores vcpkg directory
Excludes the vcpkg directory from being tracked by Git.

This prevents accidental commits of external library files
managed by vcpkg, keeping the repository cleaner.
2025-08-03 18:31:02 +03:00
orange d66ccf2f74 Refactors projectile prediction engine
Migrates projectile prediction logic to leverage engine traits for improved flexibility and testability.

This change decouples core prediction algorithms from specific engine implementations, allowing for easier adaptation to different game engines or simulation environments.
2025-08-03 18:28:47 +03:00
orange 5b66a53f08 Merge pull request #51 from orange-cpp/feature/projectile_pred_custom
Refactors projectile prediction engine
2025-08-03 17:38:08 +03:00
orange 6c5c86b850 Refactors projectile prediction engine
Refactors the projectile prediction engine by introducing an interface
and making the legacy implementation more flexible.

The legacy engine is updated to allow for coordinate system customization
through virtual methods, enabling usage in different game environments.

Also introduces vcpkg support for easier dependency management and adds boost-asio as a dependency.
2025-08-03 17:33:22 +03:00
orange da528d2884 Documents projectile launch angle formula
Adds a comment documenting the formula used for calculating the projectile launch pitch angle.

The comment includes a link to the Stack Overflow discussion where the formula was found and the LaTeX representation of the formula for clarity.
2025-07-31 21:52:16 +03:00
orange 095519e035 Merge pull request #49 from orange-cpp/feature/legacy_option
Adds option to enable legacy classes
2025-07-15 11:52:36 +03:00
orange 0fe5b6b7a1 Enables legacy code compilation
The changes encapsulate the matrix tests within an `#ifdef` block, allowing conditional compilation based on whether `OMATH_ENABLE_LEGACY` is defined. This enables the legacy code to be compiled only when needed.
2025-07-15 11:51:14 +03:00
orange b82106a565 Adds option to enable legacy classes
Introduces a CMake option to enable legacy classes,
allowing for backward compatibility with older code.
This ensures that older codebases can still function
while new development can utilize updated classes.
2025-07-15 11:48:33 +03:00
orange 0cae66b9c7 Update README.md 2025-07-14 23:11:46 +03:00
orange b621308346 Merge pull request #48 from luadebug/patch-1
Update INSTALL.md
2025-07-14 23:10:39 +03:00
Saikari bd2b14c047 Update INSTALL.md 2025-07-14 18:53:41 +03:00
orange 1ff591ff48 Update README.md
fixed ordering
2025-07-14 17:39:16 +03:00
orange f8f746bfbb Update README.md
made badge in read me smaller
2025-07-14 17:38:50 +03:00
orange cadb2b6285 Merge pull request #47 from luadebug/patch-1
Update README.md

Thx for vcpkg port update <3 u r already in CREDITS.md
2025-07-14 17:37:12 +03:00
Saikari 0ee4db4c5b Update README.md 2025-07-14 16:25:16 +03:00
orange 5425b60f6e Update README.md 2025-07-08 17:14:52 +03:00
orange 30d8db8b36 Update README.md 2025-07-08 17:13:24 +03:00
orange 2bc23d42c0 Update README.md 2025-07-08 17:01:56 +03:00
orange e68ca673c9 Renames test fixture for clarity.
Updates the name of the test fixture from "lline_tracer_fixture" to "line_tracer_fixture" for improved readability and consistency in the test code.
2025-07-07 08:28:29 +03:00
orange 77bf19da2d Fixes potential compile error
Corrects the usage of `At` method within the unit tests to `at` to resolve a potential compile error due to incorrect case sensitivity.
2025-07-07 08:18:00 +03:00
48 changed files with 980 additions and 316 deletions
+38
View File
@@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.
+10
View File
@@ -0,0 +1,10 @@
---
name: Custom issue template
about: Describe this issue template's purpose here.
title: ''
labels: ''
assignees: ''
---
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+2 -1
View File
@@ -1,4 +1,5 @@
/cmake-build/
/.idea
/out
*.DS_Store
*.DS_Store
/extlibs/vcpkg
+8 -2
View File
@@ -1,11 +1,11 @@
cmake_minimum_required(VERSION 3.26)
project(omath VERSION 3.0.2 LANGUAGES CXX)
project(omath VERSION 3.2.1 LANGUAGES CXX)
include(CMakePackageConfigHelpers)
option(OMATH_BUILD_TESTS "Build unit tests" OFF)
option(OMATH_BUILD_TESTS "Build unit tests" ${PROJECT_IS_TOP_LEVEL})
option(OMATH_THREAT_WARNING_AS_ERROR "Set highest level of warnings and force compiler to treat them as errors" ON)
option(OMATH_BUILD_AS_SHARED_LIBRARY "Build Omath as .so or .dll" OFF)
option(OMATH_USE_AVX2 "Omath will use AVX2 to boost performance" ON)
@@ -14,6 +14,8 @@ option(OMATH_BUILD_EXAMPLES "Build example projects with you can learn & play" O
option(OMATH_STATIC_MSVC_RUNTIME_LIBRARY "Force Omath to link static runtime" OFF)
option(OMATH_SUPRESS_SAFETY_CHECKS "Supress some safety checks in release build to improve general performance" ON)
option(OMATH_USE_UNITY_BUILD "Will enable unity build to speed up compilation" ON)
option(OMATH_ENABLE_LEGACY "Will enable legacy classes that MUST be used ONLY for backward compatibility" OFF)
file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp")
file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
@@ -56,6 +58,10 @@ if (OMATH_SUPRESS_SAFETY_CHECKS)
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_SUPRESS_SAFETY_CHECKS)
endif ()
if (OMATH_ENABLE_LEGACY)
target_compile_options(${PROJECT_NAME} PUBLIC OMATH_ENABLE_LEGACY)
endif ()
set_target_properties(${PROJECT_NAME} PROPERTIES
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}"
+32
View File
@@ -0,0 +1,32 @@
## 🤝 Contributing to OMath or other Orange's Projects
### ❕ Prerequisites
- A working up-to-date OMath installation
- C++ knowledge
- Git knowledge
- Ability to ask for help (Feel free to create empty pull-request or PM a maintainer
in [Telegram](https://t.me/orange_cpp))
### ⏬ Setting up OMath
Please read INSTALL.md file in repository
### 🔀 Pull requests and Branches
In order to send code back to the official OMath repository, you must first create a copy of OMath on your github
account ([fork](https://help.github.com/articles/creating-a-pull-request-from-a-fork/)) and
then [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork/) back to OMath.
OMath developement is performed on multiple branches. Changes are then pull requested into master. By default, changes
merged into master will not roll out to stable build users unless the `stable` tag is updated.
### 📜 Code-Style
The orange code-style can be found in `.clang-format`.
### 📦 Building
OMath has already created the `cmake-build` and `out` directories where cmake/bin files are located. By default, you
can build OMath by running `cmake --build cmake-build/build/windows-release --target omath -j 6` in the source
directory.
+14
View File
@@ -14,6 +14,20 @@ 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).
## <img width="28px" src="https://xmake.io/assets/img/logo.svg" /> 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")
```
## <img width="28px" src="https://upload.wikimedia.org/wikipedia/commons/e/ef/CMake_logo.svg?" /> Build from source using CMake
1. **Preparation**
+15 -1
View File
@@ -7,11 +7,25 @@
![GitHub top language](https://img.shields.io/github/languages/top/orange-cpp/omath)
[![CodeFactor](https://www.codefactor.io/repository/github/orange-cpp/omath/badge)](https://www.codefactor.io/repository/github/orange-cpp/omath)
![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/orange-cpp/omath/cmake-multi-platform.yml)
[![Vcpkg package](https://repology.org/badge/version-for-repo/vcpkg/orange-math.svg)](https://repology.org/project/orange-math/versions)
![GitHub forks](https://img.shields.io/github/forks/orange-cpp/omath)
[![discord badge](https://dcbadge.limes.pink/api/server/https://discord.gg/eDgdaWbqwZ?style=flat)](https://discord.gg/eDgdaWbqwZ)
[![telegram badge](https://img.shields.io/badge/Telegram-2CA5E0?style=flat-squeare&logo=telegram&logoColor=white)](https://t.me/orangennotes)
</div>
Oranges's Math Library (omath) is a comprehensive, open-source library aimed at providing efficient, reliable, and versatile mathematical functions and algorithms. Developed primarily in C++, this library is designed to cater to a wide range of mathematical operations essential in scientific computing, engineering, and academic research.
<div align = center>
<a href="https://www.star-history.com/#orange-cpp/omath&Date">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=orange-cpp/omath&type=Date&theme=dark" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=orange-cpp/omath&type=Date" />
<img alt="Star History Chart" src="https://api.star-history.com/svg?repos=orange-cpp/omath&type=Date" />
</picture>
</a>
</div>
## 👁‍🗨 Features
- **Efficiency**: Optimized for performance, ensuring quick computations using AVX2.
- **Versatility**: Includes a wide array of mathematical functions and algorithms.
@@ -71,7 +85,7 @@ Or even advanced projectile aimbot
Contributions to `omath` are welcome! Please read `CONTRIBUTING.md` for details on our code of conduct and the process for submitting pull requests.
## 📜 License
This project is licensed under the MIT - see the `LICENSE` file for details.
This project is licensed under the ZLIB - see the `LICENSE` file for details.
## 💘 Acknowledgments
- [All contributors](https://github.com/orange-cpp/omath/graphs/contributors)
+2 -11
View File
@@ -5,18 +5,9 @@
#pragma once
#include "omath/engines/iw_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::iw_engine
{
class Camera final : public projection::Camera<Mat4X4, ViewAngles>
{
public:
Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void look_at(const Vector3<float>& target) override;
protected:
[[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override;
[[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override;
};
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::iw_engine
@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/iw_engine/constants.hpp"
#include "omath/projection/camera.hpp"
namespace omath::iw_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::iw_engine
@@ -0,0 +1,79 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/iw_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::iw_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.z -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.z / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.y, delta.x));
};
};
} // namespace omath::iw_engine
@@ -4,16 +4,9 @@
#pragma once
#include "omath/engines/opengl_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::opengl_engine
{
class Camera final : public projection::Camera<Mat4X4, ViewAngles>
{
public:
Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void look_at(const Vector3<float>& target) override;
[[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override;
[[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override;
};
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::opengl_engine
@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/opengl_engine/constants.hpp"
#include "omath/projection/camera.hpp"
namespace omath::opengl_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::opengl_engine
@@ -0,0 +1,78 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/opengl_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::opengl_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.y;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.y / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
};
};
} // namespace omath::opengl_engine
+2 -12
View File
@@ -4,18 +4,8 @@
#pragma once
#include "omath/engines/source_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::source_engine
{
class Camera final : public projection::Camera<Mat4X4, ViewAngles>
{
public:
Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void look_at(const Vector3<float>& target) override;
protected:
[[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override;
[[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override;
};
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::source_engine
@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/source_engine/constants.hpp"
#include "omath/projection/camera.hpp"
namespace omath::source_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::source_engine
@@ -0,0 +1,79 @@
//
// Created by Vlad on 8/3/2025.
//
#pragma once
#include "omath/engines/source_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::source_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.z -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.z -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.y * delta.y);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.z;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.z / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.y, delta.x));
};
};
} // namespace omath::source_engine
+2 -11
View File
@@ -5,18 +5,9 @@
#pragma once
#include "omath/engines/unity_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::unity_engine
{
class Camera final : public projection::Camera<Mat4X4, ViewAngles>
{
public:
Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, float near, float far);
void look_at(const Vector3<float>& target) override;
protected:
[[nodiscard]] Mat4X4 calc_view_matrix() const noexcept override;
[[nodiscard]] Mat4X4 calc_projection_matrix() const noexcept override;
};
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::unity_engine
@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/unity_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::unity_engine
{
class CameraTrait final
{
public:
[[nodiscard]]
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
[[nodiscard]]
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
float near, float far) noexcept;
};
} // namespace omath::unity_engine
@@ -0,0 +1,78 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/unity_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::unity_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float> predict_projectile_position(const projectile_prediction::Projectile& projectile,
const float pitch, const float yaw,
const float time, const float gravity) noexcept
{
auto current_pos = projectile.m_origin
+ forward_vector({PitchAngle::from_degrees(-pitch), YawAngle::from_degrees(yaw),
RollAngle::from_degrees(0)})
* projectile.m_launch_speed * time;
current_pos.y -= (gravity * projectile.m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
[[nodiscard]]
static constexpr Vector3<float> predict_target_position(const projectile_prediction::Target& target,
const float time, const float gravity) noexcept
{
auto predicted = target.m_origin + target.m_velocity * time;
if (target.m_is_airborne)
predicted.y -= gravity * (time * time) * 0.5f;
return predicted;
}
[[nodiscard]]
static float calc_vector_2d_distance(const Vector3<float>& delta) noexcept
{
return std::sqrt(delta.x * delta.x + delta.z * delta.z);
}
[[nodiscard]]
constexpr static float get_vector_height_coordinate(const Vector3<float>& vec) noexcept
{
return vec.y;
}
[[nodiscard]]
static Vector3<float> calc_viewpoint_from_angles(const projectile_prediction::Projectile& projectile,
Vector3<float> predicted_target_position,
const std::optional<float> projectile_pitch) noexcept
{
const auto delta2d = calc_vector_2d_distance(predicted_target_position - projectile.m_origin);
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return {predicted_target_position.x, predicted_target_position.y + height, projectile.m_origin.z};
}
// Due to specification of maybe_calculate_projectile_launch_pitch_angle, pitch angle must be:
// 89 look up, -89 look down
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto distance = origin.distance_to(view_to);
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::asin(delta.y / distance));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto delta = view_to - origin;
return angles::radians_to_degrees(std::atan2(delta.z, delta.x));
};
};
} // namespace omath::unity_engine
+4
View File
@@ -1,4 +1,7 @@
#pragma once
#ifdef OMATH_ENABLE_LEGACY
#include "omath/vector3.hpp"
#include <initializer_list>
#include <memory>
@@ -106,3 +109,4 @@ namespace omath
std::unique_ptr<float[]> m_data;
};
} // namespace omath
#endif
@@ -8,12 +8,12 @@
namespace omath::projectile_prediction
{
class ProjPredEngine
class ProjPredEngineInterface
{
public:
[[nodiscard]]
virtual std::optional<Vector3<float>> maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const = 0;
virtual ~ProjPredEngine() = default;
virtual ~ProjPredEngineInterface() = default;
};
} // namespace omath::projectile_prediction
@@ -6,7 +6,7 @@
namespace omath::projectile_prediction
{
class ProjPredEngineAvx2 final : public ProjPredEngine
class ProjPredEngineAvx2 final : public ProjPredEngineInterface
{
public:
[[nodiscard]] std::optional<Vector3<float>>
@@ -4,6 +4,7 @@
#pragma once
#include "omath/engines/source_engine/traits/pred_engine_trait.hpp"
#include "omath/projectile_prediction/proj_pred_engine.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
@@ -12,15 +13,65 @@
namespace omath::projectile_prediction
{
class ProjPredEngineLegacy final : public ProjPredEngine
template<class T>
concept PredEngineConcept =
requires(const Projectile& projectile, const Target& target, const Vector3<float>& vec_a,
const Vector3<float>& vec_b,
Vector3<float> v3, // by-value for calc_viewpoint_from_angles
float pitch, float yaw, float time, float gravity, std::optional<float> maybe_pitch) {
// Presence + return types
{ T::predict_projectile_position(projectile, pitch, yaw, time, gravity) } -> std::same_as<Vec3>;
{ T::predict_target_position(target, time, gravity) } -> std::same_as<Vec3>;
{ T::calc_vector_2d_distance(vec_a) } -> std::same_as<float>;
{ T::get_vector_height_coordinate(vec_b) } -> std::same_as<float>;
{ T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch) } -> std::same_as<Vec3>;
{ T::calc_direct_pitch_angle(vec_a, vec_b) } -> std::same_as<float>;
{ T::calc_direct_yaw_angle(vec_a, vec_b) } -> std::same_as<float>;
// Enforce noexcept as in PredEngineTrait
requires noexcept(T::predict_projectile_position(projectile, pitch, yaw, time, gravity));
requires noexcept(T::predict_target_position(target, time, gravity));
requires noexcept(T::calc_vector_2d_distance(vec_a));
requires noexcept(T::get_vector_height_coordinate(vec_b));
requires noexcept(T::calc_viewpoint_from_angles(projectile, v3, maybe_pitch));
requires noexcept(T::calc_direct_pitch_angle(vec_a, vec_b));
requires noexcept(T::calc_direct_yaw_angle(vec_a, vec_b));
};
template<class EngineTrait = source_engine::PredEngineTrait>
requires PredEngineConcept<EngineTrait>
class ProjPredEngineLegacy final : public ProjPredEngineInterface
{
public:
explicit ProjPredEngineLegacy(float gravity_constant, float simulation_time_step, float maximum_simulation_time,
float distance_tolerance);
explicit ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step,
const float maximum_simulation_time, const float distance_tolerance)
: m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step),
m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance)
{
}
[[nodiscard]]
std::optional<Vector3<float>> maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const override;
const Target& target) const override
{
for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step)
{
const auto predicted_target_position =
EngineTrait::predict_target_position(target, time, m_gravity_constant);
const auto projectile_pitch =
maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position);
if (!projectile_pitch.has_value()) [[unlikely]]
continue;
if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(),
time))
continue;
return EngineTrait::calc_viewpoint_from_angles(projectile, predicted_target_position, projectile_pitch);
}
return std::nullopt;
}
private:
const float m_gravity_constant;
@@ -28,13 +79,56 @@ namespace omath::projectile_prediction
const float m_maximum_simulation_time;
const float m_distance_tolerance;
// Realization of this formula:
// https://stackoverflow.com/questions/54917375/how-to-calculate-the-angle-to-shoot-a-bullet-in-order-to-hit-a-moving-target
/*
\[
\theta \;=\; \arctan\!\Biggl(
\frac{%
v^{2}\;\pm\;\sqrt{\,v^{4}-g\!\left(gx^{2}+2yv^{2}\right)\,}
}{%
gx
}\Biggr)
\]
*/
[[nodiscard]]
std::optional<float>
maybe_calculate_projectile_launch_pitch_angle(const Projectile& projectile,
const Vector3<float>& target_position) const noexcept;
const Vector3<float>& target_position) const noexcept
{
const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale;
if (bullet_gravity == 0.f)
return EngineTrait::calc_direct_pitch_angle(projectile.m_origin, target_position);
const auto delta = target_position - projectile.m_origin;
const auto distance2d = EngineTrait::calc_vector_2d_distance(delta);
const auto distance2d_sqr = distance2d * distance2d;
const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed;
float root = launch_speed_sqr * launch_speed_sqr
- bullet_gravity
* (bullet_gravity * distance2d_sqr
+ 2.0f * EngineTrait::get_vector_height_coordinate(delta) * launch_speed_sqr);
if (root < 0.0f) [[unlikely]]
return std::nullopt;
root = std::sqrt(root);
const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d));
return angles::radians_to_degrees(angle);
}
[[nodiscard]]
bool is_projectile_reached_target(const Vector3<float>& target_position, const Projectile& projectile,
float pitch, float time) const noexcept;
const float pitch, const float time) const noexcept
{
const auto yaw = EngineTrait::calc_direct_yaw_angle(projectile.m_origin, target_position);
const auto projectile_position =
EngineTrait::predict_projectile_position(projectile, pitch, yaw, time, m_gravity_constant);
return projectile_position.distance_to(target_position) <= m_distance_tolerance;
}
};
} // namespace omath::projectile_prediction
@@ -10,9 +10,6 @@ namespace omath::projectile_prediction
class Projectile final
{
public:
[[nodiscard]]
Vector3<float> predict_position(float pitch, float yaw, float time, float gravity) const noexcept;
Vector3<float> m_origin;
float m_launch_speed{};
float m_gravity_scale{};
@@ -10,17 +10,6 @@ namespace omath::projectile_prediction
class Target final
{
public:
[[nodiscard]]
constexpr Vector3<float> predict_position(const float time, const float gravity) const noexcept
{
auto predicted = m_origin + m_velocity * time;
if (m_is_airborne)
predicted.z -= gravity * (time*time) * 0.5f;
return predicted;
}
Vector3<float> m_origin;
Vector3<float> m_velocity;
bool m_is_airborne{};
+38 -9
View File
@@ -26,11 +26,27 @@ namespace omath::projection
};
using FieldOfView = Angle<float, 0.f, 180.f, AngleFlags::Clamped>;
template<class Mat4X4Type, class ViewAnglesType>
class Camera
template<class T, class MatType, class ViewAnglesType>
concept CameraEngineConcept =
requires(const Vector3<float>& cam_origin, const Vector3<float>& look_at, const ViewAnglesType& angles,
const FieldOfView& fov, const ViewPort& viewport, float znear, float zfar) {
// Presence + return types
{ T::calc_look_at_angle(cam_origin, look_at) } -> std::same_as<ViewAnglesType>;
{ T::calc_view_matrix(angles, cam_origin) } -> std::same_as<MatType>;
{ T::calc_projection_matrix(fov, viewport, znear, zfar) } -> std::same_as<MatType>;
// Enforce noexcept as in the trait declaration
requires noexcept(T::calc_look_at_angle(cam_origin, look_at));
requires noexcept(T::calc_view_matrix(angles, cam_origin));
requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar));
};
template<class Mat4X4Type, class ViewAnglesType, class TraitClass>
requires CameraEngineConcept<TraitClass, Mat4X4Type, ViewAnglesType>
class Camera final
{
public:
virtual ~Camera() = default;
~Camera() = default;
Camera(const Vector3<float>& position, const ViewAnglesType& view_angles, const ViewPort& view_port,
const FieldOfView& fov, const float near, const float far) noexcept
: m_view_port(view_port), m_field_of_view(fov), m_far_plane_distance(far), m_near_plane_distance(near),
@@ -39,15 +55,16 @@ namespace omath::projection
}
protected:
virtual void look_at(const Vector3<float>& target) = 0;
[[nodiscard]] virtual Mat4X4Type calc_view_matrix() const noexcept = 0;
[[nodiscard]] virtual Mat4X4Type calc_projection_matrix() const noexcept = 0;
void look_at(const Vector3<float>& target)
{
m_view_angles = TraitClass::calc_look_at_angle(m_origin, target);
}
[[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept
{
return calc_projection_matrix() * calc_view_matrix();
return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance,
m_far_plane_distance)
* TraitClass::calc_view_matrix(m_view_angles, m_origin);
}
public:
@@ -169,6 +186,18 @@ namespace omath::projection
[[nodiscard]] Vector3<float> ndc_to_screen_position(const Vector3<float>& ndc) const noexcept
{
/*
^
| y
1 |
|
|
-1 ---------0--------- 1 --> x
|
|
-1 |
v
*/
return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (1.f - ndc.y) / 2.f * m_view_port.m_height, ndc.z};
}
};
+31 -3
View File
@@ -142,7 +142,7 @@ namespace omath
[[nodiscard]] Vector2 normalized() const noexcept
{
const Type len = length();
return len > 0.f ? *this / len : *this;
return len > static_cast<Type>(0) ? *this / len : *this;
}
#endif
[[nodiscard]] constexpr Type length_sqr() const noexcept
@@ -153,8 +153,8 @@ namespace omath
constexpr Vector2& abs() noexcept
{
// FIXME: Replace with std::abs, if it will become constexprable
x = x < 0 ? -x : x;
y = y < 0 ? -y : y;
x = x < static_cast<Type>(0) ? -x : x;
y = y < static_cast<Type>(0) ? -y : y;
return *this;
}
@@ -190,6 +190,29 @@ namespace omath
return x + y;
}
[[nodiscard]]
bool operator<(const Vector2& other) const noexcept
{
return length() < other.length();
}
[[nodiscard]]
bool operator>(const Vector2& other) const noexcept
{
return length() > other.length();
}
[[nodiscard]]
bool operator<=(const Vector2& other) const noexcept
{
return length() <= other.length();
}
[[nodiscard]]
bool operator>=(const Vector2& other) const noexcept
{
return length() >= other.length();
}
[[nodiscard]]
constexpr std::tuple<Type, Type> as_tuple() const noexcept
{
@@ -202,6 +225,11 @@ namespace omath
{
return {static_cast<float>(this->x), static_cast<float>(this->y)};
}
[[nodiscard]]
static Vector2 from_im_vec2(const ImVec2& other) noexcept
{
return {static_cast<Type>(other.x), static_cast<Type>(other.y)};
}
#endif
};
} // namespace omath
+27 -3
View File
@@ -151,7 +151,7 @@ namespace omath
{
const Type len = this->length();
return len != 0 ? *this / len : *this;
return len != static_cast<Type>(0) ? *this / len : *this;
}
[[nodiscard]] Type length_2d() const noexcept
@@ -221,7 +221,7 @@ namespace omath
{
const auto bottom = length() * other.length();
if (bottom == 0.f)
if (bottom == static_cast<Type>(0))
return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE);
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
@@ -230,7 +230,7 @@ namespace omath
[[nodiscard]] bool is_perpendicular(const Vector3& other) const noexcept
{
if (const auto angle = angle_between(other))
return angle->as_degrees() == 90.f;
return angle->as_degrees() == static_cast<Type>(90);
return false;
}
@@ -253,6 +253,30 @@ namespace omath
return {angles::radians_to_degrees(std::asin(delta.z / distance)),
angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0};
}
[[nodiscard]]
bool operator<(const Vector3& other) const noexcept
{
return length() < other.length();
}
[[nodiscard]]
bool operator>(const Vector3& other) const noexcept
{
return length() > other.length();
}
[[nodiscard]]
bool operator<=(const Vector3& other) const noexcept
{
return length() <= other.length();
}
[[nodiscard]]
bool operator>=(const Vector3& other) const noexcept
{
return length() >= other.length();
}
};
} // namespace omath
// ReSharper disable once CppRedundantNamespaceDefinition
+32 -3
View File
@@ -18,7 +18,7 @@ namespace omath
constexpr Vector4(const Type& x, const Type& y, const Type& z, const Type& w): Vector3<Type>(x, y, z), w(w)
{
}
constexpr Vector4() noexcept : Vector3<Type>(), w(0) {};
constexpr Vector4() noexcept: Vector3<Type>(), w(static_cast<Type>(0)) {};
[[nodiscard]]
constexpr bool operator==(const Vector4& other) const noexcept
@@ -89,7 +89,7 @@ namespace omath
return Vector3<Type>::dot(other) + w * other.w;
}
[[nodiscard]] Vector3<Type> length() const noexcept
[[nodiscard]] Type length() const noexcept
{
return std::sqrt(length_sqr());
}
@@ -158,6 +158,30 @@ namespace omath
return Vector3<Type>::sum() + w;
}
[[nodiscard]]
bool operator<(const Vector4& other) const noexcept
{
return length() < other.length();
}
[[nodiscard]]
bool operator>(const Vector4& other) const noexcept
{
return length() > other.length();
}
[[nodiscard]]
bool operator<=(const Vector4& other) const noexcept
{
return length() <= other.length();
}
[[nodiscard]]
bool operator>=(const Vector4& other) const noexcept
{
return length() >= other.length();
}
#ifdef OMATH_IMGUI_INTEGRATION
[[nodiscard]]
ImVec4 to_im_vec4() const noexcept
@@ -169,6 +193,11 @@ namespace omath
static_cast<float>(w),
};
}
[[nodiscard]]
static Vector4<float> from_im_vec4(const ImVec4& other) noexcept
{
return {static_cast<Type>(other.x), static_cast<Type>(other.y), static_cast<Type>(other.z)};
}
#endif
};
};
} // namespace omath
-33
View File
@@ -1,33 +0,0 @@
//
// Created by Vlad on 3/17/2025.
//
#include "omath/engines/iw_engine/camera.hpp"
#include "omath/engines/iw_engine/formulas.hpp"
namespace omath::iw_engine
{
Camera::Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, const float near, const float far)
: projection::Camera<Mat4X4, ViewAngles>(position, view_angles, view_port, fov, near, far)
{
}
void Camera::look_at([[maybe_unused]] const Vector3<float>& target)
{
const float distance = m_origin.distance_to(target);
const auto delta = target - m_origin;
m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance));
m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x));
m_view_angles.roll = RollAngle::from_radians(0.f);
}
Mat4X4 Camera::calc_view_matrix() const noexcept
{
return iw_engine::calc_view_matrix(m_view_angles, m_origin);
}
Mat4X4 Camera::calc_projection_matrix() const noexcept
{
return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(),
m_near_plane_distance, m_far_plane_distance);
}
} // namespace omath::iw_engine
@@ -0,0 +1,27 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/iw_engine/traits/camera_trait.hpp"
#include "omath/engines/iw_engine/formulas.hpp"
namespace omath::iw_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto distance = cam_origin.distance_to(look_at);
const auto delta = cam_origin - look_at;
return {PitchAngle::from_radians(-std::asin(delta.z / distance)),
YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return iw_engine::calc_view_matrix(angles, cam_origin);
}
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near,
const float far) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far);
}
} // namespace omath::iw_engine
-33
View File
@@ -1,33 +0,0 @@
//
// Created by Orange on 12/23/2024.
//
#include "omath/engines/opengl_engine/camera.hpp"
#include "omath/engines/opengl_engine/formulas.hpp"
namespace omath::opengl_engine
{
Camera::Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const Angle<float, 0.f, 180.f, AngleFlags::Clamped>& fov, const float near, const float far)
: projection::Camera<Mat4X4, ViewAngles>(position, view_angles, view_port, fov, near, far)
{
}
void Camera::look_at([[maybe_unused]] const Vector3<float>& target)
{
const float distance = m_origin.distance_to(target);
const auto delta = target - m_origin;
m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance));
m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x));
m_view_angles.roll = RollAngle::from_radians(0.f);
}
Mat4X4 Camera::calc_view_matrix() const noexcept
{
return opengl_engine::calc_view_matrix(m_view_angles, m_origin);
}
Mat4X4 Camera::calc_projection_matrix() const noexcept
{
return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(),
m_near_plane_distance, m_far_plane_distance);
}
} // namespace omath::opengl_engine
@@ -0,0 +1,28 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/opengl_engine/traits/camera_trait.hpp"
#include "omath/engines/opengl_engine/formulas.hpp"
namespace omath::opengl_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto distance = cam_origin.distance_to(look_at);
const auto delta = cam_origin - look_at;
return {PitchAngle::from_radians(-std::asin(delta.y / distance)),
YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return opengl_engine::calc_view_matrix(angles, cam_origin);
}
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near,
const float far) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far);
}
} // namespace omath::opengl_engine
-35
View File
@@ -1,35 +0,0 @@
//
// Created by Orange on 12/4/2024.
//
#include "omath/engines/source_engine/camera.hpp"
#include "omath/engines/source_engine/formulas.hpp"
namespace omath::source_engine
{
Camera::Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const projection::FieldOfView& fov, const float near, const float far)
: projection::Camera<Mat4X4, ViewAngles>(position, view_angles, view_port, fov, near, far)
{
}
void Camera::look_at(const Vector3<float>& target)
{
const float distance = m_origin.distance_to(target);
const auto delta = target - m_origin;
m_view_angles.pitch = PitchAngle::from_radians(std::asin(delta.z / distance));
m_view_angles.yaw = -YawAngle::from_radians(std::atan2(delta.y, delta.x));
m_view_angles.roll = RollAngle::from_radians(0.f);
}
Mat4X4 Camera::calc_view_matrix() const noexcept
{
return source_engine::calc_view_matrix(m_view_angles, m_origin);
}
Mat4X4 Camera::calc_projection_matrix() const noexcept
{
return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(),
m_near_plane_distance, m_far_plane_distance);
}
} // namespace omath::source_engine
@@ -0,0 +1,27 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/source_engine/traits/camera_trait.hpp"
#include "omath/engines/source_engine/formulas.hpp"
namespace omath::source_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto distance = cam_origin.distance_to(look_at);
const auto delta = cam_origin - look_at;
return {PitchAngle::from_radians(-std::asin(delta.z / distance)),
YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return source_engine::calc_view_matrix(angles, cam_origin);
}
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near,
const float far) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far);
}
} // namespace omath::source_engine
-27
View File
@@ -1,27 +0,0 @@
//
// Created by Vlad on 3/22/2025.
//
#include <omath/engines/unity_engine/camera.hpp>
#include <omath/engines/unity_engine/formulas.hpp>
namespace omath::unity_engine
{
Camera::Camera(const Vector3<float>& position, const ViewAngles& view_angles, const projection::ViewPort& view_port,
const projection::FieldOfView& fov, const float near, const float far)
: projection::Camera<Mat4X4, ViewAngles>(position, view_angles, view_port, fov, near, far)
{
}
void Camera::look_at([[maybe_unused]] const Vector3<float>& target)
{
throw std::runtime_error("Not implemented");
}
Mat4X4 Camera::calc_view_matrix() const noexcept
{
return unity_engine::calc_view_matrix(m_view_angles, m_origin);
}
Mat4X4 Camera::calc_projection_matrix() const noexcept
{
return calc_perspective_projection_matrix(m_field_of_view.as_degrees(), m_view_port.aspect_ratio(),
m_near_plane_distance, m_far_plane_distance);
}
} // namespace omath::unity_engine
@@ -0,0 +1,27 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/unity_engine/traits/camera_trait.hpp"
namespace omath::unity_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto distance = cam_origin.distance_to(look_at);
const auto delta = cam_origin - look_at;
return {PitchAngle::from_radians(-std::asin(delta.y / distance)),
YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return unity_engine::calc_view_matrix(angles, cam_origin);
}
Mat4X4 CameraTrait::calc_projection_matrix(const projection::FieldOfView& fov,
const projection::ViewPort& view_port, const float near,
const float far) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far);
}
} // namespace omath::unity_engine
+3
View File
@@ -1,3 +1,5 @@
#ifdef OMATH_ENABLE_LEGACY
#include "omath/matrix.hpp"
#include "omath/angles.hpp"
#include "omath/vector3.hpp"
@@ -359,3 +361,4 @@ namespace omath
m_data = nullptr;
}
} // namespace omath
#endif
@@ -1,69 +0,0 @@
#include "omath/projectile_prediction/proj_pred_engine_legacy.hpp"
#include <cmath>
#include <omath/angles.hpp>
namespace omath::projectile_prediction
{
ProjPredEngineLegacy::ProjPredEngineLegacy(const float gravity_constant, const float simulation_time_step,
const float maximum_simulation_time, const float distance_tolerance)
: m_gravity_constant(gravity_constant), m_simulation_time_step(simulation_time_step),
m_maximum_simulation_time(maximum_simulation_time), m_distance_tolerance(distance_tolerance)
{
}
std::optional<Vector3<float>> ProjPredEngineLegacy::maybe_calculate_aim_point(const Projectile& projectile,
const Target& target) const
{
for (float time = 0.f; time < m_maximum_simulation_time; time += m_simulation_time_step)
{
const auto predicted_target_position = target.predict_position(time, m_gravity_constant);
const auto projectile_pitch =
maybe_calculate_projectile_launch_pitch_angle(projectile, predicted_target_position);
if (!projectile_pitch.has_value()) [[unlikely]]
continue;
if (!is_projectile_reached_target(predicted_target_position, projectile, projectile_pitch.value(), time))
continue;
const auto delta2d = (predicted_target_position - projectile.m_origin).length_2d();
const auto height = delta2d * std::tan(angles::degrees_to_radians(projectile_pitch.value()));
return Vector3(predicted_target_position.x, predicted_target_position.y, projectile.m_origin.z + height);
}
return std::nullopt;
}
std::optional<float> ProjPredEngineLegacy::maybe_calculate_projectile_launch_pitch_angle(
const Projectile& projectile, const Vector3<float>& target_position) const noexcept
{
const auto bullet_gravity = m_gravity_constant * projectile.m_gravity_scale;
const auto delta = target_position - projectile.m_origin;
const auto distance2d = delta.length_2d();
const auto distance2d_sqr = distance2d * distance2d;
const auto launch_speed_sqr = projectile.m_launch_speed * projectile.m_launch_speed;
float root = launch_speed_sqr * launch_speed_sqr
- bullet_gravity * (bullet_gravity * distance2d_sqr + 2.0f * delta.z * launch_speed_sqr);
if (root < 0.0f) [[unlikely]]
return std::nullopt;
root = std::sqrt(root);
const float angle = std::atan((launch_speed_sqr - root) / (bullet_gravity * distance2d));
return angles::radians_to_degrees(angle);
}
bool ProjPredEngineLegacy::is_projectile_reached_target(const Vector3<float>& target_position,
const Projectile& projectile, const float pitch,
const float time) const noexcept
{
const auto yaw = projectile.m_origin.view_angle_to(target_position).y;
const auto projectile_position = projectile.predict_position(pitch, yaw, time, m_gravity_constant);
return projectile_position.distance_to(target_position) <= m_distance_tolerance;
}
} // namespace omath::projectile_prediction
@@ -1,22 +0,0 @@
//
// Created by Vlad on 6/9/2024.
//
#include "omath/projectile_prediction/projectile.hpp"
#include <omath/engines/source_engine/formulas.hpp>
namespace omath::projectile_prediction
{
Vector3<float> Projectile::predict_position(const float pitch, const float yaw, const float time,
const float gravity) const noexcept
{
auto current_pos = m_origin
+ source_engine::forward_vector({source_engine::PitchAngle::from_degrees(-pitch),
source_engine::YawAngle::from_degrees(yaw),
source_engine::RollAngle::from_degrees(0)})
* m_launch_speed * time;
current_pos.z -= (gravity * m_gravity_scale) * (time * time) * 0.5f;
return current_pos;
}
} // namespace omath::projectile_prediction
+5 -5
View File
@@ -31,10 +31,10 @@ namespace
// -----------------------------------------------------------------------------
// Fixture with one canonical rightangled triangle in the XY plane.
// -----------------------------------------------------------------------------
class lline_tracer_fixture : public ::testing::Test
class line_tracer_fixture : public ::testing::Test
{
protected:
lline_tracer_fixture() :
line_tracer_fixture() :
triangle({0.f, 0.f, 0.f}, {1.f, 0.f, 0.f}, {0.f, 1.f, 0.f})
{
}
@@ -51,7 +51,7 @@ namespace
bool expected_clear; // true => segment does NOT hit the triangle
};
class CanTraceLineParam : public lline_tracer_fixture,
class CanTraceLineParam : public line_tracer_fixture,
public ::testing::WithParamInterface<TraceCase>
{
};
@@ -79,7 +79,7 @@ namespace
// -----------------------------------------------------------------------------
// Validate that the reported hit point is correct for a genuine intersection.
// -----------------------------------------------------------------------------
TEST_F(lline_tracer_fixture, HitPointCorrect)
TEST_F(line_tracer_fixture, HitPointCorrect)
{
constexpr Ray ray{{0.3f, 0.3f, -1.f}, {0.3f, 0.3f, 1.f}};
constexpr Vec3 expected{0.3f, 0.3f, 0.f};
@@ -92,7 +92,7 @@ namespace
// -----------------------------------------------------------------------------
// Triangle far beyond the ray should not block.
// -----------------------------------------------------------------------------
TEST_F(lline_tracer_fixture, DistantTriangleClear)
TEST_F(line_tracer_fixture, DistantTriangleClear)
{
constexpr Ray short_ray{{0.f, 0.f, 0.f}, {0.f, 0.f, 1.f}};
constexpr Triangle<Vec3> distant{{1000.f, 1000.f, 1000.f},
+2 -2
View File
@@ -167,8 +167,8 @@ TEST_F(unit_test_mat, StaticMethod_ToScreenMat)
TEST_F(unit_test_mat, Method_At_OutOfRange)
{
#if !defined(NDEBUG) && defined(OMATH_SUPRESS_SAFETY_CHECKS)
EXPECT_THROW(std::ignore = m2.At(2, 0), std::out_of_range);
EXPECT_THROW(std::ignore = m2.At(0, 2), std::out_of_range);
EXPECT_THROW(std::ignore = m2.at(2, 0), std::out_of_range);
EXPECT_THROW(std::ignore = m2.at(0, 2), std::out_of_range);
#endif
}
+5 -1
View File
@@ -1,6 +1,9 @@
//
// Created by vlad on 5/18/2024.
//
#ifdef OMATH_ENABLE_LEGACY
#include <gtest/gtest.h>
#include <omath/matrix.hpp>
#include "omath/vector3.hpp"
@@ -177,4 +180,5 @@ TEST_F(UnitTestMatrix, AssignmentOperator_Move)
EXPECT_FLOAT_EQ(m3.at(0, 0), 1.0f);
EXPECT_EQ(m2.row_count(), 0); // m2 should be empty after the move
EXPECT_EQ(m2.columns_count(), 0);
}
}
#endif
+22
View File
@@ -346,6 +346,28 @@ TEST_F(UnitTestVector2, NegationOperator_ZeroVector)
EXPECT_FLOAT_EQ(result.y, -0.0f);
}
TEST_F(UnitTestVector2, LessOperator)
{
EXPECT_TRUE(v1 < v2);
}
TEST_F(UnitTestVector2, GreaterOperator)
{
EXPECT_TRUE(v2 > v1);
}
TEST_F(UnitTestVector2, LessEqualOperator)
{
EXPECT_TRUE(omath::Vector2<float>{} <= omath::Vector2<float>{});
EXPECT_TRUE(omath::Vector2<float>{} <= omath::Vector2(1.f, 1.f));
}
TEST_F(UnitTestVector2, GreaterEqualOperator)
{
EXPECT_TRUE(omath::Vector2<float>{} >= omath::Vector2<float>{});
EXPECT_TRUE(omath::Vector2(1.f, 1.f) >= omath::Vector2<float>{});
}
// Static assertions (compile-time checks)
static_assert(Vector2(1.0f, 2.0f).length_sqr() == 5.0f, "LengthSqr should be 5");
static_assert(Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)) == 14.0f, "Dot product should be 14");
+21
View File
@@ -402,6 +402,27 @@ TEST_F(UnitTestVector3, IsPerpendicular)
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).is_perpendicular({0.0f, 0.0f, 1.0f}));
}
TEST_F(UnitTestVector3, LessOperator)
{
EXPECT_TRUE(v1 < v2);
}
TEST_F(UnitTestVector3, GreaterOperator)
{
EXPECT_TRUE(v2 > v1);
}
TEST_F(UnitTestVector3, LessEqualOperator)
{
EXPECT_TRUE(omath::Vector3<float>{} <= omath::Vector3<float>{});
EXPECT_TRUE(omath::Vector3<float>{} <= omath::Vector3(1.f, 1.f, 1.f));
}
TEST_F(UnitTestVector3, GreaterEqualOperator)
{
EXPECT_TRUE(omath::Vector3<float>{} >= omath::Vector3<float>{});
EXPECT_TRUE(omath::Vector3(1.f, 1.f, 1.f) >= omath::Vector3<float>{});
}
// Static assertions (compile-time checks)
static_assert(Vector3(1.0f, 2.0f, 3.0f).length_sqr() == 14.0f, "LengthSqr should be 14");
static_assert(Vector3(1.0f, 2.0f, 3.0f).dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32");
+20
View File
@@ -215,3 +215,23 @@ TEST_F(UnitTestVector4, Clamp)
EXPECT_FLOAT_EQ(v3.z, 2.5f);
EXPECT_FLOAT_EQ(v3.w, 4.0f); // w is not clamped in this method
}
TEST_F(UnitTestVector4, LessOperator)
{
EXPECT_TRUE(v1 < v2);
}
TEST_F(UnitTestVector4, GreaterOperator)
{
EXPECT_TRUE(v2 > v1);
}
TEST_F(UnitTestVector4, LessEqualOperator)
{
EXPECT_TRUE(omath::Vector4<float>{} <= omath::Vector4<float>{});
EXPECT_TRUE(omath::Vector4<float>{} <= omath::Vector4(1.f, 1.f, 1.f, 1.f));
}
TEST_F(UnitTestVector4, GreaterEqualOperator)
{
EXPECT_TRUE(omath::Vector4<float>{} >= omath::Vector4<float>{});
EXPECT_TRUE(omath::Vector4(1.f, 1.f, 1.f, 1.f) >= omath::Vector4<float>{});
}