Compare commits

...

11 Commits

Author SHA1 Message Date
orange 73725b8365 mkdocs improvement 2026-06-08 19:49:29 +03:00
orange 8f6341e840 fix for lua 2026-06-08 19:22:04 +03:00
va_alpatov b9da58e138 added enity overlay aspect 2026-06-08 03:43:15 +03:00
orange 87896117d4 Merge pull request #196 from orange-cpp/feature/outline
improved outline arguments
2026-06-07 19:39:20 +03:00
va_alpatov de8a540426 applied fix for lua 2026-06-07 19:22:41 +03:00
va_alpatov d1eaee6e6e improved outline arguments 2026-06-07 19:08:01 +03:00
orange b9522fe191 nope 2026-06-03 17:34:06 +03:00
orange 2e1ff91156 added more tests 2026-06-03 16:26:31 +03:00
orange 40b2d2b332 Merge pull request #195 from orange-cpp/feature/rage_engine
added rage engine support
2026-06-03 16:15:43 +03:00
orange 8810ec20a5 added rage into read me 2026-06-03 16:14:13 +03:00
orange b7210dec4c added rage engine support
fix

patch
2026-06-03 15:35:41 +03:00
27 changed files with 731 additions and 128 deletions
+1 -1
View File
@@ -37,7 +37,7 @@ jobs:
python-version: '3.x'
- name: Install mkdocs and dependencies
run: pip install mkdocs mkdocs-bootswatch
run: pip install mkdocs mkdocs-material
- name: Build documentation
run: mkdocs build --strict
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
python-version: '3.x'
- name: Install mkdocs and dependencies
run: pip install mkdocs mkdocs-bootswatch
run: pip install mkdocs mkdocs-material
- name: Build documentation
run: mkdocs build --strict
+6 -1
View File
@@ -81,7 +81,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
- **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, CryEngine and canonical OpenGL**.
- **Engine support**: Supports coordinate systems of **Source, Rage, Unity, Unreal, Frostbite, IWEngine, CryEngine and canonical OpenGL**.
- **Cross platform**: Supports Windows, MacOS and Linux.
- **Algorithms**: Has ability to scan for byte pattern with wildcards in ELF/Mach-O/PE files/modules, binary slices, works even with Wine apps.
- **Scripting**: Supports to make scripts in Lua out of box.
@@ -113,6 +113,10 @@ if (auto screen = camera.world_to_screen(world_position)) {
<br>
![GTA5 Preview]
<br>
![OpenGL Preview]
<br>
@@ -144,6 +148,7 @@ if (auto screen = camera.world_to_screen(world_position)) {
[BO2 Preview]: docs/images/showcase/cod_bo2.png
[CS2 Preview]: docs/images/showcase/cs2.jpeg
[TF2 Preview]: docs/images/showcase/tf2.jpg
[GTA5 Preview]: https://i.imgur.com/W7T8RhZ.png
[OpenGL Preview]: docs/images/showcase/opengl.png
<!----------------------------------{ Buttons }--------------------------------->
[QUICKSTART]: docs/getting_started.md
+15 -8
View File
@@ -1,11 +1,18 @@
/* Widen the navbar container */
.navbar .container {
max-width: 100%; /* adjust to your target width */
width: 95%;
/* Material header tweaks */
.md-header,
.md-tabs {
background-color: #101014;
}
/* Tighter spacing between navbar items */
.navbar-nav > li > a {
padding-left: 8px;
padding-right: 8px;
.md-header {
border-bottom: 1px solid rgba(255, 152, 0, 0.28);
}
.md-tabs {
border-bottom: 1px solid rgba(255, 152, 0, 0.16);
}
.md-tabs__link--active,
.md-tabs__link:hover {
color: #ffb74d;
}
+33 -54
View File
@@ -1,65 +1,44 @@
/* Normal links */
a {
color: orange;
/* Material dark palette overrides */
[data-md-color-scheme="slate"] {
--md-default-bg-color: #0f1117;
--md-default-fg-color: #e8eaed;
--md-primary-fg-color: #101014;
--md-primary-fg-color--light: #1a1a21;
--md-primary-fg-color--dark: #09090d;
--md-accent-fg-color: #ff9800;
--md-accent-fg-color--transparent: rgba(255, 152, 0, 0.14);
--md-typeset-a-color: #ffb74d;
--md-code-bg-color: #171a22;
--md-code-fg-color: #f5f7fa;
}
/* On hover/focus */
a:hover,
a:focus {
color: #ff9900; /* a slightly different orange, optional */
}
/* Navbar background */
.navbar,
.navbar-default,
.navbar-inverse {
background-color: #a26228 !important; /* your orange */
border-color: #ff6600 !important;
.md-typeset a {
color: var(--md-typeset-a-color);
}
/* Navbar brand + links */
.navbar .navbar-brand,
.navbar .navbar-nav > li > a {
color: #ffffff !important;
.md-typeset a:hover,
.md-typeset a:focus {
color: #ffd180;
}
/* Active and hover states */
.navbar .navbar-nav > .active > a,
.navbar .navbar-nav > .active > a:focus,
.navbar .navbar-nav > .active > a:hover,
.navbar .navbar-nav > li > a:hover,
.navbar .navbar-nav > li > a:focus {
color: #ffffff !important;
}
/* === DROPDOWN MENU BACKGROUND === */
.navbar .dropdown-menu {
border-color: #ff6600 !important;
.md-tabs__item--active > .md-tabs__link,
.md-tabs__link:hover,
.md-nav__link:hover,
.md-nav__link:focus,
.md-nav__link--active,
.md-nav__item .md-nav__link--active {
color: #ffb74d;
}
/* Caret icon (the little triangle) */
.navbar .dropdown-toggle .caret {
border-top-color: #ffffff !important;
border-bottom-color: #ffffff !important;
.md-typeset .md-button {
border-color: #ff9800;
color: #ffb74d;
}
/* === BOOTSTRAP 3 STYLE ITEMS (mkdocs + bootswatch darkly often use this) === */
.navbar .dropdown-menu > li > a {
color: #ffffff !important;
}
.navbar .dropdown-menu > li > a:hover,
.navbar .dropdown-menu > li > a:focus {
background-color: #e65c00 !important; /* darker orange on hover */
color: #ffffff !important;
}
/* === BOOTSTRAP 4+ STYLE ITEMS (if your theme uses .dropdown-item) === */
.navbar .dropdown-item {
color: #ffffff !important;
}
.navbar .dropdown-item:hover,
.navbar .dropdown-item:focus {
background-color: #e65c00 !important;
color: #ffffff !important;
.md-typeset .md-button:hover,
.md-typeset .md-button:focus,
.md-typeset .md-button--primary {
background-color: #ff9800;
border-color: #ff9800;
color: #111111;
}
+40 -21
View File
@@ -71,6 +71,7 @@ namespace imgui_desktop::gui
ImGui::SliderFloat("X##ent", &m_entity_x, 100.f, vp->Size.x - 100.f);
ImGui::SliderFloat("Top Y", &m_entity_top_y, 20.f, m_entity_bottom_y - 20.f);
ImGui::SliderFloat("Bottom Y", &m_entity_bottom_y, m_entity_top_y + 20.f, vp->Size.y - 20.f);
ImGui::SliderFloat("Aspect", &m_entity_aspect, 1.f, 10.f);
}
if (ImGui::CollapsingHeader("Box", ImGuiTreeNodeFlags_DefaultOpen))
@@ -196,7 +197,11 @@ namespace imgui_desktop::gui
const DashedBar dbar{m_bar_color, m_bar_outline_color, m_bar_bg_color, m_bar_width,
m_bar_value, m_bar_dash_len, m_bar_dash_gap, m_bar_offset};
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y},
auto outline_helper = [](const bool is_outline) -> Outlined
{
return is_outline ? Outlined::On : Outlined::Off;
};
omath::hud::EntityOverlay({m_entity_x, m_entity_top_y}, {m_entity_x, m_entity_bottom_y}, m_entity_aspect,
std::make_shared<omath::hud::ImguiHudRenderer>())
.contents(
// ── Boxes ────────────────────────────────────────────────────
@@ -207,47 +212,61 @@ namespace imgui_desktop::gui
RightSide{
when(m_show_right_bar, bar),
when(m_show_right_dashed_bar, dbar),
when(m_show_right_labels,
Label{{0.f, 1.f, 0.f, 1.f}, m_label_offset, m_outlined, "Health: 100/100"}),
when(m_show_right_labels,
Label{{1.f, 0.f, 0.f, 1.f}, m_label_offset, m_outlined, "Shield: 125/125"}),
when(m_show_right_labels,
Label{{1.f, 0.f, 1.f, 1.f}, m_label_offset, m_outlined, "*LOCKED*"}),
when(m_show_right_labels, Label{{0.f, 1.f, 0.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"Health: 100/100"}),
when(m_show_right_labels, Label{{1.f, 0.f, 0.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"Shield: 125/125"}),
when(m_show_right_labels, Label{{1.f, 0.f, 1.f, 1.f},
m_label_offset,
outline_helper(m_outlined),
"*LOCKED*"}),
SpaceVertical{10},
SpaceVertical{10},
when(m_show_ring, ProgressRing{m_ring_color, m_ring_bg, m_ring_radius, m_ring_ratio,
m_ring_thickness, m_ring_offset}),
},
LeftSide{
when(m_show_left_bar, bar),
when(m_show_left_dashed_bar, dbar),
when(m_show_left_labels, Label{omath::Color::from_rgba(255, 128, 0, 255),
m_label_offset, m_outlined, "Armor: 75"}),
when(m_show_left_labels, Label{omath::Color::from_rgba(0, 200, 255, 255),
m_label_offset, m_outlined, "Level: 42"}),
when(m_show_left_labels,
Label{omath::Color::from_rgba(255, 128, 0, 255), m_label_offset,
outline_helper(m_outlined), "Armor: 75"}),
when(m_show_left_labels,
Label{omath::Color::from_rgba(0, 200, 255, 255), m_label_offset,
outline_helper(m_outlined), "Level: 42"}),
},
TopSide{
when(m_show_top_bar, bar),
when(m_show_top_dashed_bar, dbar),
when(m_show_centered_top, Centered{Label{omath::Color::from_rgba(0, 255, 255, 255),
m_label_offset, m_outlined, "*VISIBLE*"}}),
when(m_show_centered_top,
Centered{Label{omath::Color::from_rgba(0, 255, 255, 255), m_label_offset,
outline_helper(m_outlined), "*VISIBLE*"}}),
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 255, 0, 255), m_label_offset,
m_outlined, "*SCOPED*"}),
outline_helper(m_outlined), "*SCOPED*"}),
when(m_show_top_labels, Label{omath::Color::from_rgba(255, 0, 0, 255), m_label_offset,
m_outlined, "*BLEEDING*"}),
outline_helper(m_outlined), "*BLEEDING*"}),
},
BottomSide{
when(m_show_bottom_bar, bar),
when(m_show_bottom_dashed_bar, dbar),
when(m_show_centered_bottom, Centered{Label{omath::Color::from_rgba(255, 255, 255, 255),
m_label_offset, m_outlined, "PlayerName"}}),
when(m_show_centered_bottom,
Centered{Label{omath::Color::from_rgba(255, 255, 255, 255), m_label_offset,
outline_helper(m_outlined), "PlayerName"}}),
when(m_show_bottom_labels, Label{omath::Color::from_rgba(200, 200, 0, 255),
m_label_offset, m_outlined, "42m"}),
m_label_offset, outline_helper(m_outlined), "42m"}),
},
when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y+40.f}, m_aim_color, m_aim_radius}),
when(m_show_aim, AimDot{{m_entity_x, m_entity_top_y + 40.f}, m_aim_color, m_aim_radius}),
when(m_show_scan, ScanMarker{m_scan_color, m_scan_outline, m_scan_outline_thickness}),
when(m_show_skeleton, Skeleton{m_skel_color, m_skel_thickness}),
when(m_show_proj, ProjectileAim{{m_proj_pos_x, m_proj_pos_y}, m_proj_color, m_proj_size, m_proj_line_width, static_cast<ProjectileAim::Figure>(m_proj_figure)}),
when(m_show_proj, ProjectileAim{{m_proj_pos_x, m_proj_pos_y},
m_proj_color,
m_proj_size,
m_proj_line_width,
static_cast<ProjectileAim::Figure>(m_proj_figure)}),
when(m_show_snap, SnapLine{{vp->Size.x / 2.f, vp->Size.y}, m_snap_color, m_snap_width}));
}
+1 -2
View File
@@ -26,8 +26,7 @@ namespace imgui_desktop::gui
bool m_opened = true;
// Entity
float m_entity_x = 550.f, m_entity_top_y = 150.f, m_entity_bottom_y = 450.f;
float m_entity_x = 550.f, m_entity_top_y = 150.f, m_entity_bottom_y = 450.f, m_entity_aspect = 4.f;
// Box
omath::Color m_box_color{1.f, 1.f, 1.f, 1.f};
omath::Color m_box_fill{0.f, 0.f, 0.f, 0.f};
@@ -0,0 +1,13 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::rage_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::rage_engine
@@ -0,0 +1,25 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/linear_algebra/mat.hpp"
#include "omath/linear_algebra/vector3.hpp"
#include <omath/trigonometry/angle.hpp>
#include <omath/trigonometry/view_angles.hpp>
namespace omath::rage_engine
{
constexpr Vector3<float> k_abs_up = {0, 0, 1};
constexpr Vector3<float> k_abs_right = {1, 0, 0};
constexpr Vector3<float> k_abs_forward = {0, 1, 0};
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<float, -90.f, 90.f, AngleFlags::Clamped>;
using YawAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using RollAngle = Angle<float, -180.f, 180.f, AngleFlags::Normalized>;
using ViewAngles = omath::ViewAngles<PitchAngle, YawAngle, RollAngle>;
} // namespace omath::rage_engine
@@ -0,0 +1,85 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/constants.hpp"
#include <type_traits>
namespace omath::rage_engine
{
[[nodiscard]]
Vector3<float> forward_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> right_vector(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> up_vector(const ViewAngles& angles) noexcept;
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
[[nodiscard]]
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
[[nodiscard]]
Vector3<float> extract_origin(const Mat4X4& mat) noexcept;
[[nodiscard]]
Vector3<float> extract_scale(const Mat4X4& mat) noexcept;
[[nodiscard]]
ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept;
[[nodiscard]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far,
NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) noexcept;
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_centimeters(const FloatingType& units)
{
return units / static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_meters(const FloatingType& units)
{
return units;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType units_to_kilometers(const FloatingType& units)
{
return units_to_meters(units) / static_cast<FloatingType>(1000);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType centimeters_to_units(const FloatingType& centimeters)
{
return centimeters * static_cast<FloatingType>(100);
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType meters_to_units(const FloatingType& meters)
{
return meters;
}
template<class FloatingType>
requires std::is_floating_point_v<FloatingType>
[[nodiscard]]
constexpr FloatingType kilometers_to_units(const FloatingType& kilometers)
{
return meters_to_units(kilometers * static_cast<FloatingType>(1000));
}
} // namespace omath::rage_engine
@@ -0,0 +1,13 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "constants.hpp"
#include "omath/3d_primitives/mesh.hpp"
#include "traits/mesh_trait.hpp"
namespace omath::rage_engine
{
using Mesh = primitives::Mesh<Mat4X4, ViewAngles, MeshTrait>;
} // namespace omath::rage_engine
@@ -0,0 +1,24 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::rage_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, NDCDepthRange ndc_depth_range) noexcept;
};
} // namespace omath::rage_engine
@@ -0,0 +1,20 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include <omath/engines/rage_engine/constants.hpp>
#include <omath/engines/rage_engine/formulas.hpp>
namespace omath::rage_engine
{
class MeshTrait final
{
public:
[[nodiscard]]
static Mat4X4 rotation_matrix(const ViewAngles& rotation)
{
return rage_engine::rotation_matrix(rotation);
}
};
} // namespace omath::rage_engine
@@ -0,0 +1,76 @@
//
// Created by Orange on 6/3/2026.
//
#pragma once
#include "omath/engines/rage_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::rage_engine
{
class PredEngineTrait final
{
public:
constexpr static Vector3<float>
predict_projectile_position(const projectile_prediction::Projectile<float>& projectile, const float pitch,
const float yaw, const float time, const float gravity) noexcept
{
const auto launch_pos = projectile.m_origin + projectile.m_launch_offset;
auto current_pos = launch_pos
+ 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<float>& 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<float>& 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};
}
[[nodiscard]]
static float calc_direct_pitch_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.z));
}
[[nodiscard]]
static float calc_direct_yaw_angle(const Vector3<float>& origin, const Vector3<float>& view_to) noexcept
{
const auto direction = (view_to - origin).normalized();
return angles::radians_to_degrees(-std::atan2(direction.x, direction.y));
};
};
} // namespace omath::rage_engine
+13 -13
View File
@@ -15,7 +15,7 @@ namespace omath::hud
class EntityOverlay final
{
public:
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom, float aspect,
const std::shared_ptr<HudRendererInterface>& renderer);
// ── Boxes ────────────────────────────────────────────────────────
@@ -57,22 +57,22 @@ namespace omath::hud
float offset = 5.f);
// ── Labels ───────────────────────────────────────────────────────
EntityOverlay& add_right_label(const Color& color, float offset, bool outlined, const std::string_view& text);
EntityOverlay& add_right_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text);
EntityOverlay& add_left_label(const Color& color, float offset, bool outlined, const std::string_view& text);
EntityOverlay& add_left_label(const Color& color, float offset, widget::Outlined outlined, const std::string_view& text);
EntityOverlay& add_top_label(const Color& color, float offset, bool outlined, std::string_view text);
EntityOverlay& add_top_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text);
EntityOverlay& add_bottom_label(const Color& color, float offset, bool outlined, std::string_view text);
EntityOverlay& add_bottom_label(const Color& color, float offset, widget::Outlined outlined, std::string_view text);
EntityOverlay& add_centered_top_label(const Color& color, float offset, bool outlined,
EntityOverlay& add_centered_top_label(const Color& color, float offset, widget::Outlined outlined,
const std::string_view& text);
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, bool outlined,
EntityOverlay& add_centered_bottom_label(const Color& color, float offset, widget::Outlined outlined,
const std::string_view& text);
template<typename... Args>
EntityOverlay& add_right_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
EntityOverlay& add_right_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
Args&&... args)
{
return add_right_label(color, offset, outlined,
@@ -80,7 +80,7 @@ namespace omath::hud
}
template<typename... Args>
EntityOverlay& add_left_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
EntityOverlay& add_left_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
Args&&... args)
{
return add_left_label(color, offset, outlined,
@@ -88,7 +88,7 @@ namespace omath::hud
}
template<typename... Args>
EntityOverlay& add_top_label(const Color& color, const float offset, const bool outlined, std::format_string<Args...> fmt,
EntityOverlay& add_top_label(const Color& color, const float offset, const widget::Outlined outlined, std::format_string<Args...> fmt,
Args&&... args)
{
return add_top_label(color, offset, outlined,
@@ -96,7 +96,7 @@ namespace omath::hud
}
template<typename... Args>
EntityOverlay& add_bottom_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& add_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_bottom_label(color, offset, outlined,
@@ -104,7 +104,7 @@ namespace omath::hud
}
template<typename... Args>
EntityOverlay& add_centered_top_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& add_centered_top_label(const Color& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_centered_top_label(color, offset, outlined,
@@ -112,7 +112,7 @@ namespace omath::hud
}
template<typename... Args>
EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& add_centered_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
std::format_string<Args...> fmt, Args&&... args)
{
return add_centered_bottom_label(color, offset, outlined,
+6 -1
View File
@@ -30,6 +30,11 @@ namespace omath::hud::widget
float thickness = 1.f;
};
enum class Outlined
{
Off,
On,
};
struct CorneredBox
{
Color color;
@@ -118,7 +123,7 @@ namespace omath::hud::widget
{
Color color;
float offset;
bool outlined;
Outlined outlined;
std::string_view text;
};
@@ -27,7 +27,7 @@ namespace omath::hud
const Color& tint = Color{1.f, 1.f, 1.f, 1.f}) override;
void add_text(const Vector2<float>& position, const Color& color, const std::string_view& text) override;
[[nodiscard]]
virtual Vector2<float> calc_text_size(const std::string_view& text) override;
Vector2<float> calc_text_size(const std::string_view& text) override;
};
} // namespace omath::hud
#endif // OMATH_IMGUI_INTEGRATION
+7 -1
View File
@@ -87,6 +87,12 @@
#include "omath/engines/frostbite_engine/traits/camera_trait.hpp"
#include "omath/engines/frostbite_engine/traits/pred_engine_trait.hpp"
// RAGE Engine
#include "omath/engines/rage_engine/constants.hpp"
#include "omath/engines/rage_engine/formulas.hpp"
#include "omath/engines/rage_engine/camera.hpp"
#include "omath/engines/rage_engine/traits/camera_trait.hpp"
#include "omath/engines/rage_engine/traits/pred_engine_trait.hpp"
// Unreal Engine
#include "omath/engines/unreal_engine/constants.hpp"
@@ -101,4 +107,4 @@
// Utility
#include "omath/utility/pattern_scan.hpp"
#include "omath/utility/pe_pattern_scan.hpp"
#include "omath/utility/pe_pattern_scan.hpp"
+23 -2
View File
@@ -1,7 +1,28 @@
site_name: OMATH Docs
theme:
name: darkly
name: material
font:
text: Roboto
code: Roboto Mono
palette:
scheme: slate
primary: black
accent: orange
features:
- navigation.instant
- navigation.tracking
- navigation.sections
- navigation.top
- search.highlight
- content.code.copy
markdown_extensions:
- pymdownx.highlight:
anchor_linenums: true
line_spans: __span
pygments_lang_class: true
- pymdownx.inlinehilite
- pymdownx.superfences
extra_css:
- styles/center.css
- styles/custom-header.css
- styles/links.css
- styles/links.css
+71
View File
@@ -0,0 +1,71 @@
//
// Created by Orange on 6/3/2026.
//
#include "omath/engines/rage_engine/formulas.hpp"
namespace omath::rage_engine
{
Vector3<float> forward_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_forward);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Vector3<float> right_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Vector3<float> up_vector(const ViewAngles& angles) noexcept
{
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_up);
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
}
Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return mat_camera_view<float, MatStoreType::ROW_MAJOR>(forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin);
}
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
{
return mat_rotation_axis_z<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.roll)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
Vector3<float> extract_origin(const Mat4X4& mat) noexcept
{
return mat_extract_origin(mat);
}
Vector3<float> extract_scale(const Mat4X4& mat) noexcept
{
return mat_extract_scale(mat);
}
ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
{
const auto angles = mat_extract_rotation_zyx(mat);
return {
PitchAngle::from_degrees(angles.x),
YawAngle::from_degrees(angles.z),
RollAngle::from_degrees(angles.y),
};
}
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
const float far, const NDCDepthRange ndc_depth_range) noexcept
{
if (ndc_depth_range == NDCDepthRange::ZERO_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR, NDCDepthRange::ZERO_TO_ONE>(
field_of_view, aspect_ratio, near, far);
if (ndc_depth_range == NDCDepthRange::NEGATIVE_ONE_TO_ONE)
return mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
NDCDepthRange::NEGATIVE_ONE_TO_ONE>(
field_of_view, aspect_ratio, near, far);
std::unreachable();
}
} // namespace omath::rage_engine
@@ -0,0 +1,27 @@
//
// Created by Orange on 6/3/2026.
//
#include "omath/engines/rage_engine/traits/camera_trait.hpp"
namespace omath::rage_engine
{
ViewAngles CameraTrait::calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept
{
const auto direction = (look_at - cam_origin).normalized();
return {PitchAngle::from_radians(std::asin(direction.z)),
YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return rage_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,
const NDCDepthRange ndc_depth_range) noexcept
{
return calc_perspective_projection_matrix(fov.as_degrees(), view_port.aspect_ratio(), near, far,
ndc_depth_range);
}
} // namespace omath::rage_engine
+1 -1
View File
@@ -660,7 +660,7 @@ namespace omath::hooks
LRESULT __stdcall HooksManager::wnd_proc_detour(HWND hwnd, UINT msg, WPARAM w_param, LPARAM l_param)
{
auto& mgr = get();
const auto& mgr = get();
callback_ptr<wnd_proc_callback> cb;
WNDPROC original;
{
+15 -14
View File
@@ -98,10 +98,11 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_right_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_right_label(const Color& color, const float offset,
const widget::Outlined outlined,
const std::string_view& text)
{
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text);
else
m_renderer->add_text(m_text_cursor_right + Vector2<float>{offset, 0.f}, color, text.data());
@@ -110,12 +111,12 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_top_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_top_label(const Color& color, const float offset, const widget::Outlined outlined,
const std::string_view text)
{
m_text_cursor_top.y -= m_renderer->calc_text_size(text.data()).y;
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text);
else
m_renderer->add_text(m_text_cursor_top + Vector2<float>{0.f, -offset}, color, text.data());
@@ -392,12 +393,12 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_bottom_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
const std::string_view text)
{
const auto text_size = m_renderer->calc_text_size(text);
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
else
m_renderer->add_text(m_text_cursor_bottom + Vector2<float>{0.f, offset}, color, text);
@@ -407,13 +408,13 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_left_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_left_label(const Color& color, const float offset, const widget::Outlined outlined,
const std::string_view& text)
{
const auto text_size = m_renderer->calc_text_size(text);
const auto pos = m_text_cursor_left + Vector2<float>{-(offset + text_size.x), 0.f};
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(pos, color, text);
else
m_renderer->add_text(pos, color, text);
@@ -423,7 +424,7 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_centered_bottom_label(const Color& color, const float offset, const widget::Outlined outlined,
const std::string_view& text)
{
const auto text_size = m_renderer->calc_text_size(text);
@@ -431,7 +432,7 @@ namespace omath::hud
m_canvas.bottom_left_corner.x + (m_canvas.bottom_right_corner.x - m_canvas.bottom_left_corner.x) / 2.f;
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_bottom.y + offset};
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(pos, color, text);
else
m_renderer->add_text(pos, color, text);
@@ -441,7 +442,7 @@ namespace omath::hud
return *this;
}
EntityOverlay& EntityOverlay::add_centered_top_label(const Color& color, const float offset, const bool outlined,
EntityOverlay& EntityOverlay::add_centered_top_label(const Color& color, const float offset, const widget::Outlined outlined,
const std::string_view& text)
{
const auto text_size = m_renderer->calc_text_size(text);
@@ -451,7 +452,7 @@ namespace omath::hud
m_text_cursor_top.y -= text_size.y;
const auto pos = Vector2<float>{box_center_x - text_size.x / 2.f, m_text_cursor_top.y - offset};
if (outlined)
if (outlined == widget::Outlined::On)
draw_outlined_text(pos, color, text);
else
m_renderer->add_text(pos, color, text);
@@ -459,9 +460,9 @@ namespace omath::hud
return *this;
}
EntityOverlay::EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom,
EntityOverlay::EntityOverlay(const Vector2<float>& top, const Vector2<float>& bottom, const float aspect,
const std::shared_ptr<HudRendererInterface>& renderer)
: m_canvas(top, bottom), m_text_cursor_right(m_canvas.top_right_corner),
: m_canvas(top, bottom, aspect), m_text_cursor_right(m_canvas.top_right_corner),
m_text_cursor_top(m_canvas.top_left_corner), m_text_cursor_bottom(m_canvas.bottom_left_corner),
m_text_cursor_left(m_canvas.top_left_corner), m_renderer(renderer)
{
+8
View File
@@ -9,6 +9,7 @@
#include <omath/engines/frostbite_engine/camera.hpp>
#include <omath/engines/iw_engine/camera.hpp>
#include <omath/engines/opengl_engine/camera.hpp>
#include <omath/engines/rage_engine/camera.hpp>
#include <omath/engines/source_engine/camera.hpp>
#include <omath/engines/unity_engine/camera.hpp>
#include <omath/engines/unreal_engine/camera.hpp>
@@ -181,6 +182,12 @@ namespace
using ViewAngles = omath::source_engine::ViewAngles;
using Camera = omath::source_engine::Camera;
};
struct RageEngineTraits
{
using PitchAngle = omath::rage_engine::PitchAngle;
using ViewAngles = omath::rage_engine::ViewAngles;
using Camera = omath::rage_engine::Camera;
};
struct UnityEngineTraits
{
using PitchAngle = omath::unity_engine::PitchAngle;
@@ -254,6 +261,7 @@ namespace omath::lua
register_engine<FrostbiteEngineTraits>(omath_table, "frostbite");
register_engine<IWEngineTraits>(omath_table, "iw");
register_engine<SourceEngineTraits>(omath_table, "source");
register_engine<RageEngineTraits>(omath_table, "rage");
register_engine<UnityEngineTraits>(omath_table, "unity");
register_engine<UnrealEngineTraits, double>(omath_table, "unreal");
register_engine<CryEngineTraits>(omath_table, "cry");
+13 -7
View File
@@ -159,13 +159,19 @@ namespace
return {1.f, 1.f, 1.f, 1.f};
}
[[nodiscard]]
omath::hud::widget::Outlined outlined_from_bool(const bool outlined)
{
return outlined ? omath::hud::widget::Outlined::On : omath::hud::widget::Outlined::Off;
}
[[nodiscard]]
omath::hud::EntityOverlay make_overlay(const omath::Vector2<float>& top, const omath::Vector2<float>& bottom,
const std::shared_ptr<LuaHudRenderer>& renderer)
{
if (!renderer)
throw std::invalid_argument("hud renderer must not be nil");
return {top, bottom, renderer};
return {top, bottom, 4.f, renderer};
}
[[nodiscard]]
@@ -374,37 +380,37 @@ namespace omath::lua
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_right_label(color, offset, outlined, text);
return overlay.add_right_label(color, offset, outlined_from_bool(outlined), text);
},
"add_left_label",
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_left_label(color, offset, outlined, text);
return overlay.add_left_label(color, offset, outlined_from_bool(outlined), text);
},
"add_top_label",
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_top_label(color, offset, outlined, text);
return overlay.add_top_label(color, offset, outlined_from_bool(outlined), text);
},
"add_bottom_label",
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_bottom_label(color, offset, outlined, text);
return overlay.add_bottom_label(color, offset, outlined_from_bool(outlined), text);
},
"add_centered_top_label",
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_centered_top_label(color, offset, outlined, text);
return overlay.add_centered_top_label(color, offset, outlined_from_bool(outlined), text);
},
"add_centered_bottom_label",
[](omath::hud::EntityOverlay& overlay, const omath::Color& color, const float offset,
const bool outlined, const std::string& text) -> omath::hud::EntityOverlay&
{
return overlay.add_centered_bottom_label(color, offset, outlined, text);
return overlay.add_centered_bottom_label(color, offset, outlined_from_bool(outlined), text);
},
"add_right_space_vertical", &omath::hud::EntityOverlay::add_right_space_vertical,
+101
View File
@@ -0,0 +1,101 @@
//
// Created by Orange on 6/3/2026.
//
#include <gtest/gtest.h>
#include <omath/engines/rage_engine/camera.hpp>
#include <omath/engines/rage_engine/constants.hpp>
#include <omath/engines/rage_engine/formulas.hpp>
#include <omath/engines/rage_engine/traits/mesh_trait.hpp>
#include <omath/engines/rage_engine/traits/pred_engine_trait.hpp>
#include <omath/projectile_prediction/projectile.hpp>
#include <ranges>
#include <type_traits>
using namespace omath;
static_assert(std::is_same_v<rage_engine::Mat4X4::ContainedType, float>);
static void expect_rage_vector_near(const Vector3<float>& actual, const Vector3<float>& expected)
{
for (const auto& [result, etalon] : std::views::zip(actual.as_array(), expected.as_array()))
EXPECT_NEAR(result, etalon, 0.0001f);
}
TEST(unit_test_rage_engine, ForwardVector)
{
const auto forward = rage_engine::forward_vector({});
EXPECT_EQ(forward, rage_engine::k_abs_forward);
}
TEST(unit_test_rage_engine, RightVector)
{
const auto right = rage_engine::right_vector({});
EXPECT_EQ(right, rage_engine::k_abs_right);
}
TEST(unit_test_rage_engine, UpVector)
{
const auto up = rage_engine::up_vector({});
EXPECT_EQ(up, rage_engine::k_abs_up);
}
TEST(unit_test_rage_engine, LookAtForward)
{
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_forward);
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_forward);
}
TEST(unit_test_rage_engine, LookAtRight)
{
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_right);
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_right);
}
TEST(unit_test_rage_engine, LookAtUp)
{
const auto angles = rage_engine::CameraTrait::calc_look_at_angle({}, rage_engine::k_abs_up);
expect_rage_vector_near(rage_engine::forward_vector(angles), rage_engine::k_abs_up);
}
TEST(unit_test_rage_engine, ProjectTargetMovedFromCamera)
{
constexpr auto fov = projection::FieldOfView::from_degrees(60.f);
const auto cam = rage_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f);
const auto projected = cam.world_to_screen({0, 10.f, 0});
ASSERT_TRUE(projected.has_value());
EXPECT_NEAR(projected->x, 640.f, 0.0001f);
EXPECT_NEAR(projected->y, 360.f, 0.0001f);
}
TEST(unit_test_rage_engine, PredEngineTraitUsesZAsHeight)
{
projectile_prediction::Projectile<float> projectile;
projectile.m_origin = {0.f, 0.f, 0.f};
projectile.m_launch_speed = 10.f;
projectile.m_gravity_scale = 1.f;
const auto pos = rage_engine::PredEngineTrait::predict_projectile_position(projectile, 0.f, 0.f, 1.f, 9.81f);
EXPECT_NEAR(pos.x, 0.f, 0.0001f);
EXPECT_NEAR(pos.y, 10.f, 0.0001f);
EXPECT_NEAR(pos.z, -9.81f * 0.5f, 0.0001f);
EXPECT_NEAR(rage_engine::PredEngineTrait::get_vector_height_coordinate({1.f, 2.f, 3.f}), 3.f, 0.0001f);
}
TEST(unit_test_rage_engine, MeshTraitForwardsRotationMatrix)
{
const rage_engine::ViewAngles angles{
rage_engine::PitchAngle::from_degrees(20.f),
rage_engine::YawAngle::from_degrees(-35.f),
rage_engine::RollAngle::from_degrees(15.f),
};
EXPECT_EQ(rage_engine::MeshTrait::rotation_matrix(angles), rage_engine::rotation_matrix(angles));
}
+92
View File
@@ -9,6 +9,7 @@
#include <omath/engines/frostbite_engine/camera.hpp>
#include <omath/engines/iw_engine/camera.hpp>
#include <omath/engines/opengl_engine/camera.hpp>
#include <omath/engines/rage_engine/camera.hpp>
#include <omath/engines/source_engine/camera.hpp>
#include <omath/engines/unreal_engine/camera.hpp>
#include <omath/linear_algebra/triangle.hpp>
@@ -16,6 +17,49 @@
#include <print>
#include <random>
template<class NumericType>
static NumericType random_visible_component(std::mt19937& gen, std::uniform_real_distribution<NumericType>& dist)
{
auto value = dist(gen);
if (std::abs(value) < static_cast<NumericType>(50))
value += value < NumericType{} ? -static_cast<NumericType>(50) : static_cast<NumericType>(50);
return value;
}
template<class CameraType, class ViewAnglesType, class NumericType>
static void verify_random_look_at_targets_project_to_screen_center(const omath::Vector3<NumericType>& origin,
const ViewAnglesType& view_angles,
const omath::projection::ViewPort& view_port,
const NumericType near_plane,
const NumericType far_plane, const unsigned int seed)
{
std::mt19937 gen(seed);
std::uniform_real_distribution<NumericType> dist(static_cast<NumericType>(-500), static_cast<NumericType>(500));
constexpr auto fov = omath::projection::FieldOfView::from_degrees(75.f);
auto cam = CameraType(origin, view_angles, view_port, fov, near_plane, far_plane);
const auto screen_center_x = static_cast<NumericType>(view_port.m_width / 2.f);
const auto screen_center_y = static_cast<NumericType>(view_port.m_height / 2.f);
for (int i = 0; i < 100; i++)
{
const auto target = origin
+ omath::Vector3<NumericType>{
random_visible_component(gen, dist),
random_visible_component(gen, dist),
random_visible_component(gen, dist),
};
cam.look_at(target);
const auto projected = cam.world_to_screen(target);
ASSERT_TRUE(projected.has_value()) << "iteration: " << i;
EXPECT_NEAR(projected->x, screen_center_x, static_cast<NumericType>(0.1));
EXPECT_NEAR(projected->y, screen_center_y, static_cast<NumericType>(0.1));
}
}
TEST(UnitTestProjection, Projection)
{
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
@@ -1115,6 +1159,54 @@ TEST(UnitTestProjection, LookAt_ForwardVectorPointsAtTarget)
EXPECT_NEAR(fwd.z, 0.f, 1e-4f);
}
TEST(UnitTestProjection, SourceEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::source_engine::Camera>(
{10.f, -20.f, 30.f}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 101);
}
TEST(UnitTestProjection, UnityEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::unity_engine::Camera>(
{-15.f, 25.f, -35.f}, omath::unity_engine::ViewAngles{}, {1280.f, 720.f}, 0.03f, 5000.f, 102);
}
TEST(UnitTestProjection, UnrealEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::unreal_engine::Camera>(
{100.0, -50.0, 25.0}, omath::unreal_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01, 5000.0, 103);
}
TEST(UnitTestProjection, FrostbiteEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::frostbite_engine::Camera>(
{-4.f, 2.f, 8.f}, omath::frostbite_engine::ViewAngles{}, {1600.f, 900.f}, 0.01f, 5000.f, 104);
}
TEST(UnitTestProjection, IWEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::iw_engine::Camera>(
{40.f, -60.f, 20.f}, omath::iw_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 105);
}
TEST(UnitTestProjection, OpenGLEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::opengl_engine::Camera>(
{-30.f, 70.f, 15.f}, omath::opengl_engine::ViewAngles{}, {1920.f, 1080.f}, 0.01f, 5000.f, 106);
}
TEST(UnitTestProjection, CryEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::cry_engine::Camera>(
{5.f, 15.f, -25.f}, omath::cry_engine::ViewAngles{}, {1280.f, 720.f}, 0.01f, 5000.f, 107);
}
TEST(UnitTestProjection, RageEngine_LookAtRandomWorldPointsProjectToScreenCenter)
{
verify_random_look_at_targets_project_to_screen_center<omath::rage_engine::Camera>(
{-5.f, 45.f, 10.f}, omath::rage_engine::ViewAngles{}, {1280.f, 720.f}, 0.01f, 5000.f, 108);
}
// ---- is_culled_by_frustum (triangle) ----
TEST(UnitTestProjection, TriangleInsideFrustumNotCulled)