Compare commits

...

31 Commits
v3.9.0 ... 3.10

Author SHA1 Message Date
5a4026a4fc Merge pull request #91 from orange-cpp/feature/frostbite_engine
Defines constants for Frostbite engine
2025-10-23 00:18:07 +03:00
404c7de594 added frostbite sources 2025-10-23 00:08:17 +03:00
f26f48cc53 Defines constants for Frostbite engine
Introduces a constants header file for the Frostbite engine,
including common vectors, matrices, and angle types.
This provides a centralized location for defining and accessing
essential mathematical constants used throughout the engine.
2025-10-21 04:56:49 +03:00
ed8afb02a1 Merge pull request #90 from orange-cpp/feature/ndc_invalid_calc_fix
Fixes NDC calculation and updates formulas
2025-10-21 04:45:07 +03:00
d86695fad7 nuked not needed test 2025-10-21 04:38:43 +03:00
570c035f27 added second method of w2s 2025-10-21 04:38:43 +03:00
5657282577 Fixes incorrect NDC calculation and updates formulas
Corrects the NDC calculation in `world_to_screen` to improve accuracy.
Replaces custom perspective projection matrix calculation with `omath::mat_perspective_right_handed` for correctness and consistency.
Updates test cases and provides debugging information by printing view and projection matrices.
Addresses an issue in the feature/ndc_invalid_calc_fix branch.
2025-10-21 04:38:43 +03:00
551ac62075 Added TF2 into gallery 2025-10-19 04:00:20 +03:00
1b1be48ee6 Merge pull request #89 from luadebug/wasm
Fix pattern scan range check to use end() instead of cend()
2025-10-17 15:27:03 +03:00
Saikari
1f9ea136b0 Fix pattern scan range check to use end() instead of cend() 2025-10-16 19:47:27 +03:00
8dadb22e75 Removes ReSharper disable comments
Removes the `ReSharper disable CppInconsistentNaming` comments
from the header file.
2025-10-14 13:23:09 +03:00
8dd9860aa1 Adds PE pattern scan utility
Includes a new utility for scanning PE patterns.
2025-10-14 13:20:14 +03:00
9a0470f781 Merge pull request #88 from orange-cpp/feaute/more_support_for_hash
Adds hash support for Vector2, Vector3, and Vector4
2025-10-14 13:08:28 +03:00
eb8688c90c Unifies orthographic matrix generation
Consolidates the generation of left-handed and right-handed orthographic matrices into a shared implementation to reduce code duplication.
2025-10-14 13:06:23 +03:00
9f2f619a21 Adds hash support for Vector2, Vector3, and Vector4
Implements `std::hash` specialization for `omath::Vector2`, `omath::Vector3`, and `omath::Vector4` with float components.

This enables usage of these vector types as keys in hash-based containers, such as `std::unordered_map` and `std::unordered_set`.
2025-10-14 13:00:28 +03:00
568883ff1c Merge pull request #87 from orange-cpp/feature/no_win_api
Removes Windows-specific API dependencies for pattern scanning
2025-10-13 14:25:03 +03:00
97003b953a Update README.md 2025-10-13 14:24:53 +03:00
5646654317 Removes Windows-specific API dependencies for pattern scanning
Replaces calls to Windows API functions (GetModuleHandleA) with a void pointer parameter.

Simplifies pattern scanning for loaded modules by removing Windows-specific code and replacing it with a generic approach.
2025-10-13 14:17:30 +03:00
ee54e3de34 Merge pull request #86 from orange-cpp/feature/improved_pe_scanner_interface
Feature/improved pe scanner interface
2025-10-12 20:20:28 +03:00
7b0af9cf66 Removes unnecessary code in PE scanner
Simplifies the PePatternScanner class by removing extraneous code.
This cleans up the codebase and improves readability.
2025-10-12 20:00:52 +03:00
563ae0a656 Moves namespace declaration to end of file
Refactors the source file layout to improve readability and consistency.
Moves the namespace declaration to the end of the file.
2025-10-12 19:59:47 +03:00
b9e2307d7a Organizes PE scanner code into namespaces
Refactors the PE scanner implementation to group related definitions and functions within namespaces.

This improves code organization and readability, particularly for internal PE handling and scanning logic.

The included link to the `linux-pe` repository served as a reference during this refactoring.
2025-10-12 19:57:47 +03:00
cd0a864e7c Refactors PE scanner to eliminate redundant section extraction
Simplifies the PE pattern scanner by removing the redundant `extract_section_from_pe_file` function.

The extracted data is now returned directly from a new `ExtractedSection` struct within the main scanning function, streamlining the code and reducing duplication. This improves readability and maintainability of the PE scanner.
2025-10-12 19:53:41 +03:00
5c30f57c4c Merge pull request #85 from orange-cpp/feature/hide_pe_defs
Feature/hide pe defs
2025-10-12 19:46:22 +03:00
3dcd033616 Removes redundant NtHeaderVariant field declarations
Simplifies NtHeaderVariant structure by removing redundant field declarations.
This improves code readability and reduces unnecessary code duplication.
2025-10-12 19:43:29 +03:00
f10dc60772 Removes redundant NtHeaderVariant declaration
Moves the `NtHeaderVariant` definition outside the unnamed namespace,
to avoid repetition and improve code consistency.
This change is part of the ongoing work on feature/hide_pe_defs.
2025-10-12 19:42:53 +03:00
37f624956b Removes PE definition files
Removes the PE definition files and related functions as they are no longer needed.
2025-10-12 19:41:18 +03:00
20dc4e6730 Updates omath version to 3.9.2 2025-10-11 17:22:16 +03:00
f6c607d84c Uses source engine camera trait for view angle calculation
Replaces the custom `view_angle_to` function with `omath::source_engine::CameraTrait::calc_look_at_angle` for vector3 view angle calculations.
This change aligns with source engine conventions and improves code consistency.
2025-10-11 15:46:52 +03:00
81ed5f80d7 Merge pull request #84 from luadebug/patch-1
internal_rev_object.hpp: do not use __fastcall for non-Windows platforms
2025-10-10 07:47:25 +03:00
Saikari
e385686323 Update internal_rev_object.hpp 2025-10-09 21:34:19 +03:00
33 changed files with 880 additions and 422 deletions

BIN
.github/images/showcase/tf2.jpg vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

View File

@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.26) cmake_minimum_required(VERSION 3.26)
project(omath VERSION 3.8.2 LANGUAGES CXX) project(omath VERSION 3.9.2 LANGUAGES CXX)
include(CMakePackageConfigHelpers) include(CMakePackageConfigHelpers)
include(CheckCXXCompilerFlag) include(CheckCXXCompilerFlag)
@@ -28,22 +28,23 @@ if (OMATH_USE_AVX2 AND NOT COMPILER_SUPPORTS_AVX2)
set(OMATH_USE_AVX2 OFF CACHE BOOL "Omath will use AVX2 to boost performance" FORCE) set(OMATH_USE_AVX2 OFF CACHE BOOL "Omath will use AVX2 to boost performance" FORCE)
endif () endif ()
message(STATUS "[${PROJECT_NAME}]: Building on ${CMAKE_HOST_SYSTEM_NAME}, compiler ${CMAKE_CXX_COMPILER_ID}") if (${PROJECT_IS_TOP_LEVEL})
message(STATUS "[${PROJECT_NAME}]: Warnings as errors ${OMATH_THREAT_WARNING_AS_ERROR}") message(STATUS "[${PROJECT_NAME}]: Building on ${CMAKE_HOST_SYSTEM_NAME}, compiler ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "[${PROJECT_NAME}]: Build unit tests ${OMATH_BUILD_TESTS}") message(STATUS "[${PROJECT_NAME}]: Warnings as errors ${OMATH_THREAT_WARNING_AS_ERROR}")
message(STATUS "[${PROJECT_NAME}]: Build benchmark ${OMATH_BUILD_BENCHMARK}") message(STATUS "[${PROJECT_NAME}]: Build unit tests ${OMATH_BUILD_TESTS}")
message(STATUS "[${PROJECT_NAME}]: As dynamic library ${OMATH_BUILD_AS_SHARED_LIBRARY}") message(STATUS "[${PROJECT_NAME}]: Build benchmark ${OMATH_BUILD_BENCHMARK}")
message(STATUS "[${PROJECT_NAME}]: Static C++ runtime ${OMATH_STATIC_MSVC_RUNTIME_LIBRARY}") message(STATUS "[${PROJECT_NAME}]: As dynamic library ${OMATH_BUILD_AS_SHARED_LIBRARY}")
message(STATUS "[${PROJECT_NAME}]: CMake unity build ${OMATH_USE_UNITY_BUILD}") message(STATUS "[${PROJECT_NAME}]: Static C++ runtime ${OMATH_STATIC_MSVC_RUNTIME_LIBRARY}")
message(STATUS "[${PROJECT_NAME}]: Example projects ${OMATH_BUILD_EXAMPLES}") message(STATUS "[${PROJECT_NAME}]: CMake unity build ${OMATH_USE_UNITY_BUILD}")
message(STATUS "[${PROJECT_NAME}]: AVX2 feature status ${OMATH_USE_AVX2}") message(STATUS "[${PROJECT_NAME}]: Example projects ${OMATH_BUILD_EXAMPLES}")
message(STATUS "[${PROJECT_NAME}]: ImGUI integration feature status ${OMATH_IMGUI_INTEGRATION}") message(STATUS "[${PROJECT_NAME}]: AVX2 feature status ${OMATH_USE_AVX2}")
message(STATUS "[${PROJECT_NAME}]: Legacy features support ${OMATH_ENABLE_LEGACY}") message(STATUS "[${PROJECT_NAME}]: ImGUI integration feature status ${OMATH_IMGUI_INTEGRATION}")
message(STATUS "[${PROJECT_NAME}]: Legacy features support ${OMATH_ENABLE_LEGACY}")
endif ()
file(GLOB_RECURSE OMATH_SOURCES CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp") 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") file(GLOB_RECURSE OMATH_HEADERS CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/include/*.hpp")
if (OMATH_BUILD_AS_SHARED_LIBRARY) if (OMATH_BUILD_AS_SHARED_LIBRARY)
add_library(${PROJECT_NAME} SHARED ${OMATH_SOURCES} ${OMATH_HEADERS}) add_library(${PROJECT_NAME} SHARED ${OMATH_SOURCES} ${OMATH_HEADERS})
else () else ()
@@ -111,8 +112,8 @@ if (OMATH_USE_AVX2)
target_compile_options(${PROJECT_NAME} PUBLIC -msimd128 -mavx2) target_compile_options(${PROJECT_NAME} PUBLIC -msimd128 -mavx2)
elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang") elseif (CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang|AppleClang")
target_compile_options(${PROJECT_NAME} PUBLIC -mfma -mavx2) target_compile_options(${PROJECT_NAME} PUBLIC -mfma -mavx2)
endif() endif ()
endif() endif ()
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23) target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)

View File

@@ -54,6 +54,7 @@ It provides the latest features, is highly customizable, has all for cheat devel
- **Ready for meta-programming**: Omath use templates for common types like Vectors, Matrixes etc, to handle all types! - **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, IWEngine and canonical OpenGL. - **Engine support**: Supports coordinate systems of Source, Unity, Unreal, IWEngine and canonical OpenGL.
- **Cross platform**: Supports Windows, MacOS and Linux. - **Cross platform**: Supports Windows, MacOS and Linux.
- **Algorithms**: Has ability to scan for byte pattern with wildcards in PE files/modules, binary slices, works even with Wine apps.
<div align = center> <div align = center>
# Gallery # Gallery
@@ -74,6 +75,10 @@ It provides the latest features, is highly customizable, has all for cheat devel
![CS2 Preview] ![CS2 Preview]
<br>
![TF2 Preview]
<br> <br>
<br> <br>
@@ -86,7 +91,7 @@ It provides the latest features, is highly customizable, has all for cheat devel
[APEX Preview]: .github/images/showcase/apex.png [APEX Preview]: .github/images/showcase/apex.png
[BO2 Preview]: .github/images/showcase/cod_bo2.png [BO2 Preview]: .github/images/showcase/cod_bo2.png
[CS2 Preview]: .github/images/showcase/cs2.jpeg [CS2 Preview]: .github/images/showcase/cs2.jpeg
[TF2 Preview]: .github/images/showcase/tf2.jpg
<!----------------------------------{ Buttons }---------------------------------> <!----------------------------------{ Buttons }--------------------------------->
[INSTALL]: INSTALL.md [INSTALL]: INSTALL.md
[DOCUMENTATION]: http://libomath.org [DOCUMENTATION]: http://libomath.org

View File

@@ -1 +1 @@
3.8.2 3.9.2

View File

@@ -0,0 +1,13 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/frostbite_engine/constants.hpp"
#include "omath/projection/camera.hpp"
#include "traits/camera_trait.hpp"
namespace omath::frostbite_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait>;
} // namespace omath::unity_engine

View File

@@ -0,0 +1,25 @@
//
// Created by Vlad on 10/21/2025.
//
#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::frostbite_engine
{
constexpr Vector3<float> k_abs_up = {0, 1, 0};
constexpr Vector3<float> k_abs_right = {1, 0, 0};
constexpr Vector3<float> k_abs_forward = {0, 0, 1};
using Mat4X4 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat3X3 = Mat<4, 4, float, MatStoreType::ROW_MAJOR>;
using Mat1X3 = Mat<1, 3, float, MatStoreType::ROW_MAJOR>;
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::frostbite_engine

View File

@@ -0,0 +1,26 @@
//
// Created by Vlad on 3/22/2025.
//
#pragma once
#include "omath/engines/frostbite_engine/constants.hpp"
namespace omath::frostbite_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]]
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far) noexcept;
} // namespace omath::unity_engine

View File

@@ -0,0 +1,24 @@
//
// Created by Vlad on 8/10/2025.
//
#pragma once
#include "omath/engines/frostbite_engine/formulas.hpp"
#include "omath/projection/camera.hpp"
namespace omath::frostbite_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::unreal_engine

View File

@@ -0,0 +1,76 @@
//
// Created by Vlad on 8/6/2025.
//
#pragma once
#include "omath/engines/frostbite_engine/formulas.hpp"
#include "omath/projectile_prediction/projectile.hpp"
#include "omath/projectile_prediction/target.hpp"
#include <optional>
namespace omath::frostbite_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 direction = (view_to - origin).normalized();
return angles::radians_to_degrees(std::asin(direction.y));
}
[[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.z));
};
};
} // namespace omath::unity_engine

View File

@@ -666,8 +666,7 @@ namespace omath
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, const Type bottom, const Type top,
const Type bottom, const Type top,
const Type near, const Type far) noexcept const Type near, const Type far) noexcept
{ {
return return
@@ -680,9 +679,8 @@ namespace omath
} }
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR> template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
[[nodiscard]] [[nodiscard]]
Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, const Type bottom, const Type top,
const Type bottom, const Type top, const Type near, const Type far) noexcept
const Type near, const Type far) noexcept
{ {
return return
{ {

View File

@@ -234,6 +234,21 @@ namespace omath
}; };
} // namespace omath } // namespace omath
template<> struct std::hash<omath::Vector2<float>>
{
[[nodiscard]]
std::size_t operator()(const omath::Vector2<float>& vec) const noexcept
{
std::size_t hash = 0;
constexpr std::hash<float> hasher;
hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
return hash;
}
};
template<class Type> template<class Type>
struct std::formatter<omath::Vector2<Type>> // NOLINT(*-dcl58-cpp) struct std::formatter<omath::Vector2<Type>> // NOLINT(*-dcl58-cpp)
{ {

View File

@@ -245,15 +245,6 @@ namespace omath
return std::make_tuple(this->x, this->y, z); return std::make_tuple(this->x, this->y, z);
} }
[[nodiscard]] Vector3 view_angle_to(const Vector3& other) const noexcept
{
const auto distance = distance_to(other);
const auto delta = other - *this;
return {angles::radians_to_degrees(std::asin(delta.z / distance)),
angles::radians_to_degrees(std::atan2(delta.y, delta.x)), 0};
}
[[nodiscard]] [[nodiscard]]
bool operator<(const Vector3& other) const noexcept bool operator<(const Vector3& other) const noexcept
{ {
@@ -282,6 +273,7 @@ namespace omath
template<> struct std::hash<omath::Vector3<float>> template<> struct std::hash<omath::Vector3<float>>
{ {
[[nodiscard]]
std::size_t operator()(const omath::Vector3<float>& vec) const noexcept std::size_t operator()(const omath::Vector3<float>& vec) const noexcept
{ {
std::size_t hash = 0; std::size_t hash = 0;

View File

@@ -202,6 +202,22 @@ namespace omath
}; };
} // namespace omath } // namespace omath
template<> struct std::hash<omath::Vector4<float>>
{
[[nodiscard]]
std::size_t operator()(const omath::Vector4<float>& vec) const noexcept
{
std::size_t hash = 0;
constexpr std::hash<float> hasher;
hash ^= hasher(vec.x) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
hash ^= hasher(vec.y) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
hash ^= hasher(vec.z) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
hash ^= hasher(vec.w) + 0x9e3779b9 + (hash << 6) + (hash >> 2);
return hash;
}
};
template<class Type> template<class Type>
struct std::formatter<omath::Vector4<Type>> // NOLINT(*-dcl58-cpp) struct std::formatter<omath::Vector4<Type>> // NOLINT(*-dcl58-cpp)
{ {

View File

@@ -89,3 +89,4 @@
// Utility // Utility
#include "omath/utility/pattern_scan.hpp" #include "omath/utility/pattern_scan.hpp"
#include "omath/utility/pe_pattern_scan.hpp"

View File

@@ -54,6 +54,12 @@ namespace omath::projection
friend UnitTestProjection_Projection_Test; friend UnitTestProjection_Projection_Test;
#endif #endif
public: public:
enum class ScreenStart
{
TOP_LEFT_CORNER,
BOTTOM_LEFT_CORNER,
};
~Camera() = default; ~Camera() = default;
Camera(const Vector3<float>& position, const ViewAnglesType& view_angles, const ViewPort& view_port, Camera(const Vector3<float>& position, const ViewAnglesType& view_angles, const ViewPort& view_port,
const FieldOfView& fov, const float near, const float far) noexcept const FieldOfView& fov, const float near, const float far) noexcept
@@ -146,15 +152,22 @@ namespace omath::projection
return m_origin; return m_origin;
} }
template<ScreenStart screen_start = ScreenStart::TOP_LEFT_CORNER>
[[nodiscard]] std::expected<Vector3<float>, Error> [[nodiscard]] std::expected<Vector3<float>, Error>
world_to_screen(const Vector3<float>& world_position) const noexcept world_to_screen(const Vector3<float>& world_position) const noexcept
{ {
auto normalized_cords = world_to_view_port(world_position); const auto normalized_cords = world_to_view_port(world_position);
if (!normalized_cords.has_value()) if (!normalized_cords.has_value())
return std::unexpected{normalized_cords.error()}; return std::unexpected{normalized_cords.error()};
return ndc_to_screen_position(*normalized_cords); if constexpr (screen_start == ScreenStart::TOP_LEFT_CORNER)
return ndc_to_screen_position_from_top_left_corner(*normalized_cords);
else if constexpr (screen_start == ScreenStart::BOTTOM_LEFT_CORNER)
return ndc_to_screen_position_from_bottom_left_corner(*normalized_cords);
else
std::unreachable();
} }
[[nodiscard]] std::expected<Vector3<float>, Error> [[nodiscard]] std::expected<Vector3<float>, Error>
@@ -225,7 +238,8 @@ namespace omath::projection
return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; }); return std::ranges::any_of(ndc.raw_array(), [](const auto& val) { return val < -1 || val > 1; });
} }
[[nodiscard]] Vector3<float> ndc_to_screen_position(const Vector3<float>& ndc) const noexcept [[nodiscard]] Vector3<float>
ndc_to_screen_position_from_top_left_corner(const Vector3<float>& ndc) const noexcept
{ {
/* /*
^ ^
@@ -239,7 +253,27 @@ namespace omath::projection
-1 | -1 |
v 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};
return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (ndc.y / -2.f + 0.5f) * m_view_port.m_height, ndc.z};
}
[[nodiscard]] Vector3<float>
ndc_to_screen_position_from_bottom_left_corner(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, (ndc.y / 2.f + 0.5f) * m_view_port.m_height, ndc.z};
} }
[[nodiscard]] Vector3<float> screen_to_ndc(const Vector3<float>& screen_pos) const noexcept [[nodiscard]] Vector3<float> screen_to_ndc(const Vector3<float>& screen_pos) const noexcept

View File

@@ -29,7 +29,7 @@ namespace omath::rev_eng
#ifdef _MSC_VER #ifdef _MSC_VER
using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...); using VirtualMethodType = ReturnType(__thiscall*)(void*, decltype(arg_list)...);
#else #else
using VirtualMethodType = ReturnType(__fastcall*)(void*, decltype(arg_list)...); using VirtualMethodType = ReturnType(*)(void*, decltype(arg_list)...);
#endif #endif
return (*reinterpret_cast<VirtualMethodType**>(this))[id](this, arg_list...); return (*reinterpret_cast<VirtualMethodType**>(this))[id](this, arg_list...);
} }

View File

@@ -1,5 +0,0 @@
//
// Created by Vlad on 10/8/2025.
//
#pragma once

View File

@@ -1,32 +0,0 @@
//
// Created by Vlad on 10/7/2025.
//
#pragma once
#include <cstdint>
namespace omath::system::pe
{
struct DosHeader final
{
std::uint16_t e_magic;
std::uint16_t e_cblp;
std::uint16_t e_cp;
std::uint16_t e_crlc;
std::uint16_t e_cparhdr;
std::uint16_t e_minalloc;
std::uint16_t e_maxalloc;
std::uint16_t e_ss;
std::uint16_t e_sp;
std::uint16_t e_csum;
std::uint16_t e_ip;
std::uint16_t e_cs;
std::uint16_t e_lfarlc;
std::uint16_t e_ovno;
std::uint16_t e_res[4];
std::uint16_t e_oemid;
std::uint16_t e_oeminfo;
std::uint16_t e_res2[10];
std::uint32_t e_lfanew;
};
}

View File

@@ -1,56 +0,0 @@
//
// Created by Vlad on 10/7/2025.
//
#pragma once
#include <cstdint>
namespace omath::system::pe
{
enum class MachineId : std::uint16_t
{
UNKNOWN = 0x0000,
TARGET_HOST = 0x0001, // Useful for indicating we want to interact with the host and not a WoW guest.
I386 = 0x014C, // Intel 386.
R3000 = 0x0162, // MIPS little-endian, 0x160 big-endian
R4000 = 0x0166, // MIPS little-endian
R10000 = 0x0168, // MIPS little-endian
WCEMIPSV2 = 0x0169, // MIPS little-endian WCE v2
ALPHA = 0x0184, // Alpha_AXP
SH3 = 0x01A2, // SH3 little-endian
SH3DSP = 0x01A3,
SH3E = 0x01A4, // SH3E little-endian
SH4 = 0x01A6, // SH4 little-endian
SH5 = 0x01A8, // SH5
ARM = 0x01C0, // ARM Little-Endian
THUMB = 0x01C2, // ARM Thumb/Thumb-2 Little-Endian
ARMNT = 0x01C4, // ARM Thumb-2 Little-Endian
AM33 = 0x01D3,
POWERPC = 0x01F0, // IBM PowerPC Little-Endian
POWERPCP = 0x01F1,
IA64 = 0x0200, // Intel 64
MIPS16 = 0x0266, // MIPS
ALPHA64 = 0x0284, // ALPHA64
MIPSFPU = 0x0366, // MIPS
MIPSFPU16 = 0x0466, // MIPS
AXP64 = 0x0284,
TRICORE = 0x0520, // Infineon
CEF = 0x0CEF,
EBC = 0x0EBC, // EFI Byte Code
AMD64 = 0x8664, // AMD64 (K8)
M32R = 0x9041, // M32R little-endian
ARM64 = 0xAA64, // ARM64 Little-Endian
CEE = 0xC0EE,
};
struct FileHeader final
{
MachineId machine;
uint16_t num_sections;
uint32_t timedate_stamp;
uint32_t ptr_symbols;
uint32_t num_symbols;
uint16_t size_optional_header;
std::uint16_t characteristics;
};
}

View File

@@ -1,23 +0,0 @@
//
// Created by Vlad on 10/9/2025.
//
#pragma once
#include "file_header.hpp"
#include "optional_header.hpp"
namespace omath::system::pe
{
enum class NtArchitecture
{
x32_bit,
x64_bit,
};
template<NtArchitecture architecture>
struct ImageNtHeaders final
{
std::uint32_t signature;
FileHeader file_header;
OptionalHeader<architecture == NtArchitecture::x64_bit> optional_header;
};
} // namespace omath::system::pe

View File

@@ -1,118 +0,0 @@
//
// Created by Vlad on 10/8/2025.
//
#pragma once
#include <cstdint>
namespace omath::system::pe
{
static constexpr std::uint16_t opt_hdr32_magic = 0x010B;
static constexpr std::uint16_t opt_hdr64_magic = 0x020B;
enum class SubsystemId : std::uint16_t
{
unknown = 0x0000,
native = 0x0001,
windows_gui = 0x0002,
windows_cui = 0x0003,
os2_cui = 0x0005,
posix_cui = 0x0007,
native_windows = 0x0008,
windows_ce_gui = 0x0009,
efi_application = 0x000A,
efi_boot_service_driver = 0x000B,
efi_runtime_driver = 0x000C,
efi_rom = 0x000D,
xbox = 0x000E,
windows_boot_application = 0x0010,
xbox_code_catalog = 0x0011,
};
struct DataDirectory final
{
std::uint32_t rva;
std::uint32_t size;
};
struct OptionalHeaderX64 final
{
// Standard fields.
std::uint16_t magic;
std::uint16_t linker_version;
std::uint32_t size_code;
std::uint32_t size_init_data;
std::uint32_t size_uninit_data;
std::uint32_t entry_point;
std::uint32_t base_of_code;
// NT additional fields.
std::uint64_t image_base;
std::uint32_t section_alignment;
std::uint32_t file_alignment;
std::uint32_t os_version;
std::uint32_t img_version;
std::uint32_t subsystem_version;
std::uint32_t win32_version_value;
std::uint32_t size_image;
std::uint32_t size_headers;
std::uint32_t checksum;
SubsystemId subsystem;
std::uint16_t characteristics;
std::uint64_t size_stack_reserve;
std::uint64_t size_stack_commit;
std::uint64_t size_heap_reserve;
std::uint64_t size_heap_commit;
std::uint32_t ldr_flags;
std::uint32_t num_data_directories;
DataDirectory data_directories[16];
};
struct OptionalHeaderX86 final
{
// Standard fields.
uint16_t magic;
uint16_t linker_version;
uint32_t size_code;
uint32_t size_init_data;
uint32_t size_uninit_data;
uint32_t entry_point;
uint32_t base_of_code;
uint32_t base_of_data;
// NT additional fields.
uint32_t image_base;
uint32_t section_alignment;
uint32_t file_alignment;
std::uint32_t os_version;
std::uint32_t img_version;
std::uint32_t subsystem_version;
uint32_t win32_version_value;
uint32_t size_image;
uint32_t size_headers;
uint32_t checksum;
SubsystemId subsystem;
std::uint16_t characteristics;
uint32_t size_stack_reserve;
uint32_t size_stack_commit;
uint32_t size_heap_reserve;
uint32_t size_heap_commit;
uint32_t ldr_flags;
uint32_t num_data_directories;
DataDirectory data_directories[16];
};
template<bool x64 = true>
using OptionalHeader = std::conditional_t<x64, OptionalHeaderX64, OptionalHeaderX86>;
} // namespace omath::system::pe

View File

@@ -1,32 +0,0 @@
//
// Created by Vlad on 10/8/2025.
//
#pragma once
#include <cstdint>
// Section header
//
namespace omath::system::pe
{
struct SectionHeader final
{
char name[8];
union
{
std::uint32_t physical_address;
std::uint32_t virtual_size;
};
std::uint32_t virtual_address;
std::uint32_t size_raw_data;
std::uint32_t ptr_raw_data;
std::uint32_t ptr_relocs;
std::uint32_t ptr_line_numbers;
std::uint32_t num_relocs;
std::uint32_t num_line_numbers;
std::uint32_t characteristics;
};
}

View File

@@ -9,17 +9,13 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
// ReSharper disable once CppInconsistentNaming // ReSharper disable CppInconsistentNaming
class unit_test_pattern_scan_read_test_Test; class unit_test_pattern_scan_read_test_Test;
// ReSharper disable once CppInconsistentNaming
class unit_test_pattern_scan_corner_case_1_Test; class unit_test_pattern_scan_corner_case_1_Test;
// ReSharper disable once CppInconsistentNaming
class unit_test_pattern_scan_corner_case_2_Test; class unit_test_pattern_scan_corner_case_2_Test;
// ReSharper disable once CppInconsistentNaming
class unit_test_pattern_scan_corner_case_3_Test; class unit_test_pattern_scan_corner_case_3_Test;
// ReSharper disable once CppInconsistentNaming
class unit_test_pattern_scan_corner_case_4_Test; class unit_test_pattern_scan_corner_case_4_Test;
// ReSharper restore CppInconsistentNaming
namespace omath namespace omath
{ {
enum class PatternScanError enum class PatternScanError
@@ -37,11 +33,11 @@ namespace omath
public: public:
[[nodiscard]] [[nodiscard]]
static std::span<std::byte>::iterator scan_for_pattern(const std::span<std::byte>& range, static std::span<std::byte>::iterator scan_for_pattern(const std::span<std::byte>& range,
const std::string_view& pattern); const std::string_view& pattern);
[[nodiscard]] [[nodiscard]]
static std::span<std::byte>::iterator scan_for_pattern(std::span<std::byte>&& range, static std::span<std::byte>::iterator scan_for_pattern(std::span<std::byte>&& range,
const std::string_view& pattern) = delete; const std::string_view& pattern) = delete;
template<class IteratorType> template<class IteratorType>
requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>> requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>>

View File

@@ -7,7 +7,6 @@
#include <filesystem> #include <filesystem>
#include <optional> #include <optional>
#include <string_view> #include <string_view>
#include <vector>
namespace omath namespace omath
{ {
struct PeSectionScanResult struct PeSectionScanResult
@@ -18,26 +17,14 @@ namespace omath
}; };
class PePatternScanner final class PePatternScanner final
{ {
private:
struct Section
{
std::uint64_t virtual_base_addr;
std::uint64_t raw_base_addr;
std::vector<std::byte> data;
};
public: public:
[[nodiscard]] [[nodiscard]]
static std::optional<std::uintptr_t> scan_for_pattern_in_loaded_module(const std::string_view& module_name, static std::optional<std::uintptr_t> scan_for_pattern_in_loaded_module(const void* module_base_address,
const std::string_view& pattern); const std::string_view& pattern);
[[nodiscard]] [[nodiscard]]
static std::optional<PeSectionScanResult> static std::optional<PeSectionScanResult>
scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern, scan_for_pattern_in_file(const std::filesystem::path& path_to_file, const std::string_view& pattern,
const std::string_view& target_section_name = ".text"); const std::string_view& target_section_name = ".text");
[[nodiscard]]
static std::optional<Section> extract_section_from_pe_file(const std::filesystem::path& path_to_file,
const std::string_view& section_name);
}; };
} // namespace omath } // namespace omath

View File

@@ -0,0 +1,42 @@
//
// Created by Vlad on 3/22/2025.
//
#include "omath/engines/frostbite_engine/formulas.hpp"
namespace omath::frostbite_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.roll)
* mat_rotation_axis_y<float, MatStoreType::ROW_MAJOR>(angles.yaw)
* mat_rotation_axis_x<float, MatStoreType::ROW_MAJOR>(angles.pitch);
}
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
const float far) noexcept
{
return mat_perspective_left_handed(field_of_view, aspect_ratio, near, far);
}
} // namespace omath::unity_engine

View File

@@ -0,0 +1,26 @@
//
// Created by Vlad on 8/11/2025.
//
#include "omath/engines/frostbite_engine/traits/camera_trait.hpp"
namespace omath::frostbite_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.y)),
YawAngle::from_radians(std::atan2(direction.x, direction.z)), RollAngle::from_radians(0.f)};
}
Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept
{
return frostbite_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

View File

@@ -25,7 +25,7 @@ namespace omath::unity_engine
} }
Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept 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), return mat_camera_view<float, MatStoreType::ROW_MAJOR>(-forward_vector(angles), right_vector(angles),
up_vector(angles), cam_origin); up_vector(angles), cam_origin);
} }
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept
@@ -37,13 +37,6 @@ namespace omath::unity_engine
Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near,
const float far) noexcept const float far) noexcept
{ {
const float fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / 2.f); return omath::mat_perspective_right_handed(field_of_view, aspect_ratio, near, far);
return {
{1.f / (aspect_ratio * fov_half_tan), 0, 0, 0},
{0, 1.f / (fov_half_tan), 0, 0},
{0, 0, (far + near) / (far - near), -(2.f * far * near) / (far - near)},
{0, 0, -1.f, 0},
};
} }
} // namespace omath::unity_engine } // namespace omath::unity_engine

View File

@@ -2,40 +2,227 @@
// Created by Vlad on 10/7/2025. // Created by Vlad on 10/7/2025.
// //
#include "omath/utility/pe_pattern_scan.hpp" #include "omath/utility/pe_pattern_scan.hpp"
#include "omath/system/pe/image_nt_headers.hpp"
#include "omath/system/pe/section_header.hpp"
#include "omath/utility/pattern_scan.hpp" #include "omath/utility/pattern_scan.hpp"
#include <fstream> #include <fstream>
#include <omath/system/pe/dos_header.hpp>
#include <span> #include <span>
#include <stdexcept> #include <stdexcept>
#include <variant> #include <variant>
#ifdef _WIN32
#include <Windows.h>
#endif
using namespace omath::system::pe; // Internal PE shit defines
using NtHeaderVariant = std::variant<ImageNtHeaders<NtArchitecture::x64_bit>, ImageNtHeaders<NtArchitecture::x32_bit>>; // Big thx for linuxpe sources as ref
// Link: https://github.com/can1357/linux-pe
namespace
{
constexpr std::uint16_t opt_hdr32_magic = 0x010B;
constexpr std::uint16_t opt_hdr64_magic = 0x020B;
// Standard fields.
// ReSharper disable CppDeclaratorNeverUsed
struct DataDirectory final
{
std::uint32_t rva;
std::uint32_t size;
};
struct OptionalHeaderX64 final
{
std::uint16_t magic;
std::uint16_t linker_version;
std::uint32_t size_code;
std::uint32_t size_init_data;
std::uint32_t size_uninit_data;
std::uint32_t entry_point;
std::uint32_t base_of_code;
// NT additional fields.
std::uint64_t image_base;
std::uint32_t section_alignment;
std::uint32_t file_alignment;
std::uint32_t os_version;
std::uint32_t img_version;
std::uint32_t subsystem_version;
std::uint32_t win32_version_value;
std::uint32_t size_image;
std::uint32_t size_headers;
std::uint32_t checksum;
std::uint16_t subsystem;
std::uint16_t characteristics;
std::uint64_t size_stack_reserve;
std::uint64_t size_stack_commit;
std::uint64_t size_heap_reserve;
std::uint64_t size_heap_commit;
std::uint32_t ldr_flags;
std::uint32_t num_data_directories;
DataDirectory data_directories[16];
};
struct OptionalHeaderX86 final
{
// Standard fields.
std::uint16_t magic{};
std::uint16_t linker_version{};
std::uint32_t size_code{};
std::uint32_t size_init_data{};
std::uint32_t size_uninit_data{};
std::uint32_t entry_point{};
std::uint32_t base_of_code{};
std::uint32_t base_of_data{};
// NT additional fields.
std::uint32_t image_base{};
std::uint32_t section_alignment{};
std::uint32_t file_alignment{};
std::uint32_t os_version{};
std::uint32_t img_version{};
std::uint32_t subsystem_version{};
std::uint32_t win32_version_value{};
std::uint32_t size_image{};
std::uint32_t size_headers{};
std::uint32_t checksum{};
std::uint16_t subsystem{};
std::uint16_t characteristics{};
std::uint32_t size_stack_reserve{};
std::uint32_t size_stack_commit{};
std::uint32_t size_heap_reserve{};
std::uint32_t size_heap_commit{};
std::uint32_t ldr_flags{};
std::uint32_t num_data_directories{};
DataDirectory data_directories[16]{};
};
template<bool x64 = true>
using OptionalHeader = std::conditional_t<x64, OptionalHeaderX64, OptionalHeaderX86>;
struct FileHeader final
{
std::uint16_t machine;
std::uint16_t num_sections;
std::uint32_t timedate_stamp;
std::uint32_t ptr_symbols;
std::uint32_t num_symbols;
std::uint16_t size_optional_header;
std::uint16_t characteristics;
};
struct DosHeader final
{
std::uint16_t e_magic;
std::uint16_t e_cblp;
std::uint16_t e_cp;
std::uint16_t e_crlc;
std::uint16_t e_cparhdr;
std::uint16_t e_minalloc;
std::uint16_t e_maxalloc;
std::uint16_t e_ss;
std::uint16_t e_sp;
std::uint16_t e_csum;
std::uint16_t e_ip;
std::uint16_t e_cs;
std::uint16_t e_lfarlc;
std::uint16_t e_ovno;
std::uint16_t e_res[4];
std::uint16_t e_oemid;
std::uint16_t e_oeminfo;
std::uint16_t e_res2[10];
std::uint32_t e_lfanew;
};
enum class NtArchitecture
{
x32_bit,
x64_bit,
};
template<NtArchitecture architecture>
struct ImageNtHeaders final
{
std::uint32_t signature;
FileHeader file_header;
OptionalHeader<architecture == NtArchitecture::x64_bit> optional_header;
};
struct SectionHeader final
{
char name[8];
union
{
std::uint32_t physical_address;
std::uint32_t virtual_size;
};
std::uint32_t virtual_address;
std::uint32_t size_raw_data;
std::uint32_t ptr_raw_data;
std::uint32_t ptr_relocs;
std::uint32_t ptr_line_numbers;
std::uint32_t num_relocs;
std::uint32_t num_line_numbers;
std::uint32_t characteristics;
};
// ReSharper restore CppDeclaratorNeverUsed
using NtHeaderVariant =
std::variant<ImageNtHeaders<NtArchitecture::x64_bit>, ImageNtHeaders<NtArchitecture::x32_bit>>;
} // namespace
// Internal PE scanner functions
namespace namespace
{ {
[[nodiscard]] [[nodiscard]]
NtHeaderVariant get_nt_header_from_file(std::fstream& file, const DosHeader& dos_header) std::optional<NtHeaderVariant> get_nt_header_from_file(std::fstream& file, const DosHeader& dos_header)
{ {
ImageNtHeaders<NtArchitecture::x32_bit> x86_headers; ImageNtHeaders<NtArchitecture::x32_bit> x86_headers{};
file.seekg(dos_header.e_lfanew, std::ios::beg); file.seekg(dos_header.e_lfanew, std::ios::beg);
file.read(reinterpret_cast<char*>(&x86_headers), sizeof(x86_headers)); file.read(reinterpret_cast<char*>(&x86_headers), sizeof(x86_headers));
if (x86_headers.optional_header.magic == opt_hdr32_magic) if (x86_headers.optional_header.magic == opt_hdr32_magic)
return x86_headers; return x86_headers;
ImageNtHeaders<NtArchitecture::x64_bit> x64_headers; if (x86_headers.optional_header.magic != opt_hdr64_magic)
return std::nullopt;
ImageNtHeaders<NtArchitecture::x64_bit> x64_headers{};
file.seekg(dos_header.e_lfanew, std::ios::beg); file.seekg(dos_header.e_lfanew, std::ios::beg);
file.read(reinterpret_cast<char*>(&x64_headers), sizeof(x64_headers)); file.read(reinterpret_cast<char*>(&x64_headers), sizeof(x64_headers));
return x64_headers; return x64_headers;
} }
[[nodiscard]]
std::optional<NtHeaderVariant> get_nt_header_from_loaded_module(const void* module_base_address)
{
const auto module_byte_ptr = static_cast<const std::byte*>(module_base_address);
ImageNtHeaders<NtArchitecture::x32_bit> x86_headers{};
const auto dos_header = static_cast<const DosHeader*>(module_base_address);
x86_headers = *reinterpret_cast<const ImageNtHeaders<NtArchitecture::x32_bit>*>(module_byte_ptr
+ dos_header->e_lfanew);
if (x86_headers.optional_header.magic == opt_hdr32_magic)
return x86_headers;
if (x86_headers.optional_header.magic != opt_hdr64_magic)
return std::nullopt;
return *reinterpret_cast<const ImageNtHeaders<NtArchitecture::x64_bit>*>(module_byte_ptr
+ dos_header->e_lfanew);
}
[[nodiscard]] [[nodiscard]]
constexpr bool invalid_dos_header_file(const DosHeader& dos_header) constexpr bool invalid_dos_header_file(const DosHeader& dos_header)
{ {
@@ -48,39 +235,105 @@ namespace
constexpr std::uint32_t nt_hdr_magic = 0x4550; constexpr std::uint32_t nt_hdr_magic = 0x4550;
return std::visit([](const auto& header) -> bool { return header.signature != nt_hdr_magic; }, variant); return std::visit([](const auto& header) -> bool { return header.signature != nt_hdr_magic; }, variant);
} }
struct ExtractedSection
{
std::uint64_t virtual_base_addr;
std::uint64_t raw_base_addr;
std::vector<std::byte> data;
};
[[nodiscard]]
std::optional<ExtractedSection> extract_section_from_pe_file(const std::filesystem::path& path_to_file,
const std::string_view& section_name)
{
std::fstream file(path_to_file, std::ios::binary | std::ios::in);
if (!file.is_open()) [[unlikely]]
return std::nullopt;
DosHeader dos_header{};
file.read(reinterpret_cast<char*>(&dos_header), sizeof(dos_header));
if (invalid_dos_header_file(dos_header)) [[unlikely]]
return std::nullopt;
const auto nt_headers = get_nt_header_from_file(file, dos_header);
if (!nt_headers)
return std::nullopt;
if (invalid_nt_header_file(nt_headers.value())) [[unlikely]]
return std::nullopt;
return std::visit(
[&file, &dos_header, &section_name](auto& concrete_headers) -> std::optional<ExtractedSection>
{
constexpr std::size_t size_of_signature = sizeof(concrete_headers.signature);
const auto offset_to_segment_table = dos_header.e_lfanew
+ concrete_headers.file_header.size_optional_header
+ sizeof(FileHeader) + size_of_signature;
file.seekg(static_cast<std::fstream::off_type>(offset_to_segment_table), std::ios::beg);
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; i++)
{
SectionHeader current_section{};
file.read(reinterpret_cast<char*>(&current_section), sizeof(current_section));
if (std::string_view(current_section.name) != section_name)
continue;
std::vector<std::byte> section_data(current_section.size_raw_data);
file.seekg(current_section.ptr_raw_data, std::ios::beg);
file.read(reinterpret_cast<char*>(section_data.data()),
static_cast<std::streamsize>(section_data.size()));
return ExtractedSection{.virtual_base_addr = current_section.virtual_address
+ concrete_headers.optional_header.image_base,
.raw_base_addr = current_section.ptr_raw_data,
.data = std::move(section_data)};
}
return std::nullopt;
},
nt_headers.value());
}
} // namespace } // namespace
namespace omath namespace omath
{ {
std::optional<std::uintptr_t> std::optional<std::uintptr_t> PePatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
PePatternScanner::scan_for_pattern_in_loaded_module([[maybe_unused]] const std::string_view& module_name, const std::string_view& pattern)
[[maybe_unused]] const std::string_view& pattern)
{ {
#ifdef _WIN32 const auto base_address = reinterpret_cast<std::uintptr_t>(module_base_address);
const auto base_address = reinterpret_cast<std::uintptr_t>(GetModuleHandleA(module_name.data()));
if (!base_address) if (!base_address)
return std::nullopt; return std::nullopt;
const auto dos_headers = reinterpret_cast<PIMAGE_DOS_HEADER>(base_address); auto nt_header_variant = get_nt_header_from_loaded_module(module_base_address);
const auto image_nt_headers = reinterpret_cast<PIMAGE_NT_HEADERS>(base_address + dos_headers->e_lfanew);
// Define .code segment as scan area if (!nt_header_variant)
const auto start = image_nt_headers->OptionalHeader.BaseOfCode; return std::nullopt;
const auto scan_size = image_nt_headers->OptionalHeader.SizeOfCode;
const auto scan_range = std::span{reinterpret_cast<std::byte*>(base_address) + start, scan_size}; return std::visit(
[base_address, &pattern](const auto& nt_header) -> std::optional<std::uintptr_t>
{
// Define .code segment as scan area
const auto start = nt_header.optional_header.base_of_code;
const auto scan_size = nt_header.optional_header.size_code;
const auto result = PatternScanner::scan_for_pattern(scan_range, pattern); const auto scan_range = std::span{reinterpret_cast<std::byte*>(base_address) + start, scan_size};
if (result != scan_range.cend()) // ReSharper disable once CppTooWideScopeInitStatement
return reinterpret_cast<std::uintptr_t>(&*result); const auto result = PatternScanner::scan_for_pattern(scan_range, pattern);
return std::nullopt; if (result != scan_range.end())
#else return reinterpret_cast<std::uintptr_t>(&*result);
throw std::runtime_error("Pattern scan for loaded modules is only for windows platform");
#endif return std::nullopt;
},
nt_header_variant.value());
} }
std::optional<PeSectionScanResult> std::optional<PeSectionScanResult>
PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file, PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
@@ -103,56 +356,4 @@ namespace omath
.raw_base_addr = pe_section->raw_base_addr, .raw_base_addr = pe_section->raw_base_addr,
.target_offset = offset}; .target_offset = offset};
} }
std::optional<PePatternScanner::Section>
PePatternScanner::extract_section_from_pe_file(const std::filesystem::path& path_to_file,
const std::string_view& section_name)
{
std::fstream file(path_to_file, std::ios::binary | std::ios::in);
if (!file.is_open()) [[unlikely]]
return std::nullopt;
DosHeader dos_header{};
file.read(reinterpret_cast<char*>(&dos_header), sizeof(dos_header));
if (invalid_dos_header_file(dos_header)) [[unlikely]]
return std::nullopt;
const auto nt_headers = get_nt_header_from_file(file, dos_header);
if (invalid_nt_header_file(nt_headers)) [[unlikely]]
return std::nullopt;
return std::visit(
[&file, &dos_header, &section_name](auto& concrete_headers) -> std::optional<Section>
{
constexpr std::size_t size_of_signature = sizeof(concrete_headers.signature);
const auto offset_to_segment_table = dos_header.e_lfanew
+ concrete_headers.file_header.size_optional_header
+ sizeof(FileHeader) + size_of_signature;
file.seekg(static_cast<std::fstream::off_type>(offset_to_segment_table), std::ios::beg);
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; i++)
{
SectionHeader current_section{};
file.read(reinterpret_cast<char*>(&current_section), sizeof(current_section));
if (std::string_view(current_section.name) != section_name)
continue;
std::vector<std::byte> section_data(current_section.size_raw_data);
file.seekg(current_section.ptr_raw_data, std::ios::beg);
file.read(reinterpret_cast<char*>(section_data.data()),
static_cast<std::streamsize>(section_data.size()));
return Section{.virtual_base_addr = current_section.virtual_address
+ concrete_headers.optional_header.image_base,
.raw_base_addr = current_section.ptr_raw_data,
.data = std::move(section_data)};
}
return std::nullopt;
},
nt_headers);
}
} // namespace omath } // namespace omath

View File

@@ -16,5 +16,4 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
target_link_libraries(${PROJECT_NAME} PRIVATE gtest gtest_main omath::omath) target_link_libraries(${PROJECT_NAME} PRIVATE gtest gtest_main omath::omath)
gtest_discover_tests(${PROJECT_NAME}) gtest_discover_tests(${PROJECT_NAME})

View File

@@ -0,0 +1,236 @@
//
// Created by Vlad on 10/23/2025.
//
#include <gtest/gtest.h>
#include <omath/engines/frostbite_engine/camera.hpp>
#include <omath/engines/frostbite_engine/constants.hpp>
#include <omath/engines/frostbite_engine/formulas.hpp>
#include <print>
#include <random>
TEST(unit_test_frostbite_engine, ForwardVector)
{
const auto forward = omath::frostbite_engine::forward_vector({});
EXPECT_EQ(forward, omath::frostbite_engine::k_abs_forward);
}
TEST(unit_test_frostbite_engine, ForwardVectorRotationYaw)
{
omath::frostbite_engine::ViewAngles angles;
angles.yaw = omath::frostbite_engine::YawAngle::from_degrees(90.f);
const auto forward = omath::frostbite_engine::forward_vector(angles);
EXPECT_NEAR(forward.x, omath::frostbite_engine::k_abs_right.x, 0.00001f);
EXPECT_NEAR(forward.y, omath::frostbite_engine::k_abs_right.y, 0.00001f);
EXPECT_NEAR(forward.z, omath::frostbite_engine::k_abs_right.z, 0.00001f);
}
TEST(unit_test_frostbite_engine, ForwardVectorRotationPitch)
{
omath::frostbite_engine::ViewAngles angles;
angles.pitch = omath::frostbite_engine::PitchAngle::from_degrees(-90.f);
const auto forward = omath::frostbite_engine::forward_vector(angles);
EXPECT_NEAR(forward.x, omath::frostbite_engine::k_abs_up.x, 0.00001f);
EXPECT_NEAR(forward.y, omath::frostbite_engine::k_abs_up.y, 0.00001f);
EXPECT_NEAR(forward.z, omath::frostbite_engine::k_abs_up.z, 0.00001f);
}
TEST(unit_test_frostbite_engine, ForwardVectorRotationRoll)
{
omath::frostbite_engine::ViewAngles angles;
angles.roll = omath::frostbite_engine::RollAngle::from_degrees(-90.f);
const auto forward = omath::frostbite_engine::up_vector(angles);
EXPECT_NEAR(forward.x, omath::frostbite_engine::k_abs_right.x, 0.00001f);
EXPECT_NEAR(forward.y, omath::frostbite_engine::k_abs_right.y, 0.00001f);
EXPECT_NEAR(forward.z, omath::frostbite_engine::k_abs_right.z, 0.00001f);
}
TEST(unit_test_frostbite_engine, RightVector)
{
const auto right = omath::frostbite_engine::right_vector({});
EXPECT_EQ(right, omath::frostbite_engine::k_abs_right);
}
TEST(unit_test_frostbite_engine, UpVector)
{
const auto up = omath::frostbite_engine::up_vector({});
EXPECT_EQ(up, omath::frostbite_engine::k_abs_up);
}
TEST(unit_test_frostbite_engine, ProjectTargetMovedFromCamera)
{
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
const auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f);
for (float distance = 0.02f; distance < 100.f; distance += 0.01f)
{
const auto projected = cam.world_to_screen({0, 0, distance});
EXPECT_TRUE(projected.has_value());
if (!projected.has_value())
continue;
EXPECT_NEAR(projected->x, 640, 0.00001f);
EXPECT_NEAR(projected->y, 360, 0.00001f);
}
}
TEST(unit_test_frostbite_engine, Project)
{
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
const auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.03f, 1000.f);
const auto proj = cam.world_to_screen<omath::frostbite_engine::Camera::ScreenStart::BOTTOM_LEFT_CORNER>({10.f, 3, 10.f});
EXPECT_NEAR(proj->x, 1263.538, 0.001f);
EXPECT_NEAR(proj->y, 547.061f, 0.001f);
}
TEST(unit_test_frostbite_engine, CameraSetAndGetFov)
{
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f);
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 90.f);
cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f));
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f);
}
TEST(unit_test_frostbite_engine, CameraSetAndGetOrigin)
{
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, {}, 0.01f, 1000.f);
EXPECT_EQ(cam.get_origin(), omath::Vector3<float>{});
cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f));
EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f);
}
TEST(unit_test_frostbite_engine, loook_at_random_all_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{dist(gen), dist(gen), dist(gen)};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_frostbite_engine, loook_at_random_x_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{dist(gen), 0.f, 0.f};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.001f || std::abs(projected_pos->y - 0.f) >= 0.001f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_frostbite_engine, loook_at_random_y_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{0.f, dist(gen), 0.f};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}
TEST(unit_test_frostbite_engine, loook_at_random_z_axis)
{
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
std::uniform_real_distribution<float> dist(-1000.f, 1000.f);
constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f);
auto cam = omath::frostbite_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f);
std::size_t failed_points = 0;
for (int i = 0; i < 1000; i++)
{
const auto position_to_look = omath::Vector3<float>{0.f, 0.f, dist(gen)};
if (cam.get_origin().distance_to(position_to_look) < 10)
continue;
cam.look_at(position_to_look);
auto projected_pos = cam.world_to_view_port(position_to_look);
EXPECT_TRUE(projected_pos.has_value());
if (!projected_pos)
continue;
if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f)
failed_points++;
}
EXPECT_LE(failed_points, 100);
}

View File

@@ -87,9 +87,9 @@ TEST(unit_test_unity_engine, Project)
constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f); constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f);
const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.03f, 1000.f); const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.03f, 1000.f);
const auto proj = cam.world_to_screen({5.f, 3, 10.f}); const auto proj = cam.world_to_screen<omath::unity_engine::Camera::ScreenStart::BOTTOM_LEFT_CORNER>({10.f, 3, 10.f});
EXPECT_NEAR(proj->x, 951.769f, 0.001f); EXPECT_NEAR(proj->x, 1263.538, 0.001f);
EXPECT_NEAR(proj->y, 547.061f, 0.001f); EXPECT_NEAR(proj->y, 547.061f, 0.001f);
} }

View File

@@ -214,3 +214,20 @@ TEST(UnitTestMatStandalone, Enverse)
EXPECT_EQ(mv, m.inverted()); EXPECT_EQ(mv, m.inverted());
} }
TEST(UnitTestMatStandalone, Equanity)
{
constexpr omath::Vector3<float> left_handed = {0, 2, 10};
constexpr omath::Vector3<float> right_handed = {0, 2, -10};
auto proj_left_handed = omath::mat_perspective_left_handed(90.f, 16.f / 9.f, 0.1, 1000);
auto proj_right_handed = omath::mat_perspective_right_handed(90.f, 16.f / 9.f, 0.1, 1000);
auto ndc_left_handed = proj_left_handed * omath::mat_column_from_vector(left_handed);
auto ndc_right_handed = proj_right_handed * omath::mat_column_from_vector(right_handed);
ndc_left_handed /= ndc_left_handed.at(3, 0);
ndc_right_handed /= ndc_right_handed.at(3, 0);
EXPECT_EQ(ndc_left_handed, ndc_right_handed);
}

View File

@@ -1,6 +1,6 @@
#include <gtest/gtest.h> #include <gtest/gtest.h>
#include <omath/projectile_prediction/proj_pred_engine_legacy.hpp> #include <omath/projectile_prediction/proj_pred_engine_legacy.hpp>
#include <omath/engines/source_engine/traits/camera_trait.hpp>
TEST(UnitTestPrediction, PredictionTest) TEST(UnitTestPrediction, PredictionTest)
{ {
constexpr omath::projectile_prediction::Target target{ constexpr omath::projectile_prediction::Target target{
@@ -10,8 +10,9 @@ TEST(UnitTestPrediction, PredictionTest)
const auto viewPoint = const auto viewPoint =
omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).maybe_calculate_aim_point(proj, target); omath::projectile_prediction::ProjPredEngineLegacy(400, 1.f / 1000.f, 50, 5.f).maybe_calculate_aim_point(proj, target);
const auto [pitch, yaw, _] = proj.m_origin.view_angle_to(viewPoint.value()).as_tuple();
EXPECT_NEAR(42.547142, pitch, 0.01f); const auto [pitch, yaw, _] =omath::source_engine::CameraTrait::calc_look_at_angle(proj.m_origin, viewPoint.value());
EXPECT_NEAR(-1.181189, yaw, 0.01f);
EXPECT_NEAR(-42.547142, pitch.as_degrees(), 0.01f);
EXPECT_NEAR(-1.181189, yaw.as_degrees(), 0.01f);
} }