mirror of
https://github.com/orange-cpp/omath.git
synced 2026-08-08 14:02:06 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 792fb13851 | |||
| 2d38387da9 | |||
| 5f14510e5b | |||
| 4a9125549f | |||
| d9acf5eea8 | |||
| 589f440e70 | |||
| 854b50f317 | |||
| a741fc1485 | |||
| a2be99be50 | |||
| 00e7c564fd | |||
| 5d9dbec5b8 | |||
| a2c9084e5f | |||
| ad2523dc3a | |||
| 3599a7d592 | |||
| 00287c7a58 | |||
| ce589b4f17 |
@@ -107,7 +107,7 @@ jobs:
|
|||||||
-DOMATH_BUILD_TESTS=ON \
|
-DOMATH_BUILD_TESTS=ON \
|
||||||
-DOMATH_BUILD_BENCHMARK=OFF \
|
-DOMATH_BUILD_BENCHMARK=OFF \
|
||||||
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
|
-DOMATH_ENABLE_COVERAGE=${{ matrix.coverage == true && 'ON' || 'OFF' }} \
|
||||||
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua"
|
-DVCPKG_MANIFEST_FEATURES="imgui;avx2;tests;lua;gcem"
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
shell: bash
|
shell: bash
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ jobs:
|
|||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Install mkdocs and dependencies
|
- name: Install mkdocs and dependencies
|
||||||
run: pip install mkdocs mkdocs-material
|
run: pip install mkdocs mkdocs-bootswatch
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: mkdocs build --strict
|
run: mkdocs build --strict
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ jobs:
|
|||||||
python-version: '3.x'
|
python-version: '3.x'
|
||||||
|
|
||||||
- name: Install mkdocs and dependencies
|
- name: Install mkdocs and dependencies
|
||||||
run: pip install mkdocs mkdocs-material
|
run: pip install mkdocs mkdocs-bootswatch
|
||||||
|
|
||||||
- name: Build documentation
|
- name: Build documentation
|
||||||
run: mkdocs build --strict
|
run: mkdocs build --strict
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ option(OMATH_ENABLE_FORCE_INLINE
|
|||||||
option(OMATH_ENABLE_LUA
|
option(OMATH_ENABLE_LUA
|
||||||
"omath bindings for lua" OFF)
|
"omath bindings for lua" OFF)
|
||||||
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX/OpenGL automatically" OFF)
|
option(OMATH_ENABLE_HOOKING "omath will HooksManager that can hook DirectX/OpenGL automatically" OFF)
|
||||||
|
option(OMATH_USE_GCEM "omath will use gcem library to make more functions/methods constexpr" OFF)
|
||||||
|
|
||||||
if(VCPKG_MANIFEST_FEATURES)
|
if(VCPKG_MANIFEST_FEATURES)
|
||||||
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
|
foreach(omath_feature IN LISTS VCPKG_MANIFEST_FEATURES)
|
||||||
@@ -50,6 +51,8 @@ if(VCPKG_MANIFEST_FEATURES)
|
|||||||
set(OMATH_ENABLE_LUA ON)
|
set(OMATH_ENABLE_LUA ON)
|
||||||
elseif(omath_feature STREQUAL "hooking")
|
elseif(omath_feature STREQUAL "hooking")
|
||||||
set(OMATH_ENABLE_HOOKING ON)
|
set(OMATH_ENABLE_HOOKING ON)
|
||||||
|
elseif (omath_feature STREQUAL "gcem")
|
||||||
|
set(OMATH_USE_GCEM ON)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
endforeach()
|
endforeach()
|
||||||
@@ -80,6 +83,7 @@ if(${PROJECT_IS_TOP_LEVEL})
|
|||||||
message(STATUS "[${PROJECT_NAME}]: Coverage feature status ${OMATH_ENABLE_COVERAGE}")
|
message(STATUS "[${PROJECT_NAME}]: Coverage feature status ${OMATH_ENABLE_COVERAGE}")
|
||||||
message(STATUS "[${PROJECT_NAME}]: Valgrind feature status ${OMATH_ENABLE_VALGRIND}")
|
message(STATUS "[${PROJECT_NAME}]: Valgrind feature status ${OMATH_ENABLE_VALGRIND}")
|
||||||
message(STATUS "[${PROJECT_NAME}]: Lua feature status ${OMATH_ENABLE_LUA}")
|
message(STATUS "[${PROJECT_NAME}]: Lua feature status ${OMATH_ENABLE_LUA}")
|
||||||
|
message(STATUS "[${PROJECT_NAME}]: Gcem feature status ${OMATH_USE_GCEM}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
|
if(OMATH_STATIC_MSVC_RUNTIME_LIBRARY)
|
||||||
@@ -120,6 +124,12 @@ if (OMATH_ENABLE_HOOKING)
|
|||||||
endif ()
|
endif ()
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
|
||||||
|
if (OMATH_USE_GCEM)
|
||||||
|
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_USE_GCEM)
|
||||||
|
find_package(gcem CONFIG REQUIRED)
|
||||||
|
target_link_libraries(${PROJECT_NAME} PUBLIC gcem)
|
||||||
|
endif ()
|
||||||
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
add_library(${PROJECT_NAME}::${PROJECT_NAME} ALIAS ${PROJECT_NAME})
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}")
|
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_VERSION="${PROJECT_VERSION}")
|
||||||
|
|||||||
+2
-2
@@ -57,7 +57,7 @@
|
|||||||
"inherits": ["windows-base", "vcpkg-base"],
|
"inherits": ["windows-base", "vcpkg-base"],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"VCPKG_TARGET_TRIPLET": "x64-windows-static",
|
"VCPKG_TARGET_TRIPLET": "x64-windows-static",
|
||||||
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;hooking",
|
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;hooking;gcem",
|
||||||
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
|
"OMATH_STATIC_MSVC_RUNTIME_LIBRARY": "ON"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -239,7 +239,7 @@
|
|||||||
"hidden": true,
|
"hidden": true,
|
||||||
"inherits": ["darwin-base", "vcpkg-base"],
|
"inherits": ["darwin-base", "vcpkg-base"],
|
||||||
"cacheVariables": {
|
"cacheVariables": {
|
||||||
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;lua"
|
"VCPKG_MANIFEST_FEATURES": "tests;imgui;avx2;examples;lua;gcem"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +1,11 @@
|
|||||||
/* Material header tweaks */
|
/* Widen the navbar container */
|
||||||
.md-header,
|
.navbar .container {
|
||||||
.md-tabs {
|
max-width: 100%; /* adjust to your target width */
|
||||||
background-color: #101014;
|
width: 95%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-header {
|
/* Tighter spacing between navbar items */
|
||||||
border-bottom: 1px solid rgba(255, 152, 0, 0.28);
|
.navbar-nav > li > a {
|
||||||
}
|
padding-left: 8px;
|
||||||
|
padding-right: 8px;
|
||||||
.md-tabs {
|
|
||||||
border-bottom: 1px solid rgba(255, 152, 0, 0.16);
|
|
||||||
}
|
|
||||||
|
|
||||||
.md-tabs__link--active,
|
|
||||||
.md-tabs__link:hover {
|
|
||||||
color: #ffb74d;
|
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-33
@@ -1,44 +1,65 @@
|
|||||||
/* Material dark palette overrides */
|
/* Normal links */
|
||||||
[data-md-color-scheme="slate"] {
|
a {
|
||||||
--md-default-bg-color: #0f1117;
|
color: orange;
|
||||||
--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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-typeset a {
|
|
||||||
color: var(--md-typeset-a-color);
|
/* 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:hover,
|
/* Navbar brand + links */
|
||||||
.md-typeset a:focus {
|
.navbar .navbar-brand,
|
||||||
color: #ffd180;
|
.navbar .navbar-nav > li > a {
|
||||||
|
color: #ffffff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-tabs__item--active > .md-tabs__link,
|
/* Active and hover states */
|
||||||
.md-tabs__link:hover,
|
.navbar .navbar-nav > .active > a,
|
||||||
.md-nav__link:hover,
|
.navbar .navbar-nav > .active > a:focus,
|
||||||
.md-nav__link:focus,
|
.navbar .navbar-nav > .active > a:hover,
|
||||||
.md-nav__link--active,
|
.navbar .navbar-nav > li > a:hover,
|
||||||
.md-nav__item .md-nav__link--active {
|
.navbar .navbar-nav > li > a:focus {
|
||||||
color: #ffb74d;
|
color: #ffffff !important;
|
||||||
|
}
|
||||||
|
/* === DROPDOWN MENU BACKGROUND === */
|
||||||
|
.navbar .dropdown-menu {
|
||||||
|
border-color: #ff6600 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-typeset .md-button {
|
/* Caret icon (the little triangle) */
|
||||||
border-color: #ff9800;
|
.navbar .dropdown-toggle .caret {
|
||||||
color: #ffb74d;
|
border-top-color: #ffffff !important;
|
||||||
|
border-bottom-color: #ffffff !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.md-typeset .md-button:hover,
|
/* === BOOTSTRAP 3 STYLE ITEMS (mkdocs + bootswatch darkly often use this) === */
|
||||||
.md-typeset .md-button:focus,
|
.navbar .dropdown-menu > li > a {
|
||||||
.md-typeset .md-button--primary {
|
color: #ffffff !important;
|
||||||
background-color: #ff9800;
|
}
|
||||||
border-color: #ff9800;
|
|
||||||
color: #111111;
|
.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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,31 +8,81 @@
|
|||||||
namespace omath::cry_engine
|
namespace omath::cry_engine
|
||||||
{
|
{
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> forward_vector(const ViewAngles& angles) noexcept;
|
inline OMATH_CONSTEXPR 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);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> right_vector(const ViewAngles& angles) noexcept;
|
inline OMATH_CONSTEXPR 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)};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> up_vector(const ViewAngles& angles) noexcept;
|
inline OMATH_CONSTEXPR Vector3<float> right_vector(const ViewAngles& angles) noexcept
|
||||||
|
{
|
||||||
|
const auto vec = rotation_matrix(angles) * mat_column_from_vector(k_abs_right);
|
||||||
|
|
||||||
[[nodiscard]] Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
|
return {vec.at(0, 0), vec.at(1, 0), vec.at(2, 0)};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept;
|
inline OMATH_CONSTEXPR 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)};
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> extract_origin(const Mat4X4& mat) noexcept;
|
inline OMATH_CONSTEXPR 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);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector3<float> extract_scale(const Mat4X4& mat) noexcept;
|
inline OMATH_CONSTEXPR Vector3<float> extract_origin(const Mat4X4& mat) noexcept
|
||||||
|
{
|
||||||
|
return mat_extract_origin(mat);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept;
|
inline OMATH_CONSTEXPR Vector3<float> extract_scale(const Mat4X4& mat) noexcept
|
||||||
|
{
|
||||||
|
return mat_extract_scale(mat);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Mat4X4 calc_perspective_projection_matrix(float field_of_view, float aspect_ratio, float near, float far,
|
inline OMATH_CONSTEXPR ViewAngles extract_rotation_angles(const Mat4X4& mat) noexcept
|
||||||
NDCDepthRange ndc_depth_range = NDCDepthRange::ZERO_TO_ONE) 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),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
[[nodiscard]]
|
||||||
|
inline OMATH_CONSTEXPR 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 = NDCDepthRange::ZERO_TO_ONE) 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();
|
||||||
|
}
|
||||||
|
|
||||||
template<class FloatingType>
|
template<class FloatingType>
|
||||||
requires std::is_floating_point_v<FloatingType>
|
requires std::is_floating_point_v<FloatingType>
|
||||||
|
|||||||
@@ -4,21 +4,41 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "omath/engines/cry_engine/formulas.hpp"
|
#include "omath/engines/cry_engine/formulas.hpp"
|
||||||
|
#include "omath/internal/optional_constexpr_math.hpp"
|
||||||
#include "omath/projection/camera.hpp"
|
#include "omath/projection/camera.hpp"
|
||||||
|
|
||||||
namespace omath::cry_engine
|
namespace omath::cry_engine
|
||||||
{
|
{
|
||||||
class CameraTrait final
|
class CameraTrait final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin, const Vector3<float>& look_at) noexcept;
|
OMATH_CONSTEXPR static ViewAngles calc_look_at_angle(const Vector3<float>& cam_origin,
|
||||||
|
const Vector3<float>& look_at) noexcept
|
||||||
|
{
|
||||||
|
const auto direction = (look_at - cam_origin).normalized();
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return {PitchAngle::from_radians(gcem::asin(direction.z)),
|
||||||
|
YawAngle::from_radians(-gcem::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)};
|
||||||
|
#else
|
||||||
|
return {PitchAngle::from_radians(std::asin(direction.z)),
|
||||||
|
YawAngle::from_radians(-std::atan2(direction.x, direction.y)), RollAngle::from_radians(0.f)};
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3<float>& cam_origin) noexcept;
|
OMATH_CONSTEXPR static Mat4X4 calc_view_matrix(const ViewAngles& angles,
|
||||||
|
const Vector3<float>& cam_origin) noexcept
|
||||||
|
{
|
||||||
|
return cry_engine::calc_view_matrix(angles, cam_origin);
|
||||||
|
}
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static Mat4X4 calc_projection_matrix(const projection::FieldOfView& fov, const projection::ViewPort& view_port,
|
OMATH_CONSTEXPR static Mat4X4
|
||||||
float near, float far, NDCDepthRange ndc_depth_range) noexcept;
|
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::cry_engine
|
} // namespace omath::cry_engine
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// Created by orange on 6/11/2026.
|
||||||
|
//
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
#include <gcem.hpp>
|
||||||
|
#define OMATH_CONSTEXPR constexpr
|
||||||
|
#else
|
||||||
|
#define OMATH_CONSTEXPR
|
||||||
|
#endif
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#include <numeric>
|
#include <numeric>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#ifdef OMATH_USE_AVX2
|
#ifdef OMATH_USE_AVX2
|
||||||
@@ -149,7 +150,8 @@ namespace omath
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use element reference")]] constexpr Type& at(const size_t row_index, const size_t column_index)
|
[[nodiscard("You must use element reference")]] constexpr Type& at(const size_t row_index,
|
||||||
|
const size_t column_index)
|
||||||
{
|
{
|
||||||
return const_cast<Type&>(std::as_const(*this).at(row_index, column_index));
|
return const_cast<Type&>(std::as_const(*this).at(row_index, column_index));
|
||||||
}
|
}
|
||||||
@@ -176,6 +178,13 @@ namespace omath
|
|||||||
operator*(const Mat<Columns, OtherColumns, Type, StoreType>& other) const
|
operator*(const Mat<Columns, OtherColumns, Type, StoreType>& other) const
|
||||||
{
|
{
|
||||||
#ifdef OMATH_USE_AVX2
|
#ifdef OMATH_USE_AVX2
|
||||||
|
if (std::is_constant_evaluated())
|
||||||
|
{
|
||||||
|
if constexpr (StoreType == MatStoreType::ROW_MAJOR)
|
||||||
|
return cache_friendly_multiply_row_major(other);
|
||||||
|
else if constexpr (StoreType == MatStoreType::COLUMN_MAJOR)
|
||||||
|
return cache_friendly_multiply_col_major(other);
|
||||||
|
}
|
||||||
if constexpr (StoreType == MatStoreType::ROW_MAJOR)
|
if constexpr (StoreType == MatStoreType::ROW_MAJOR)
|
||||||
return avx_multiply_row_major(other);
|
return avx_multiply_row_major(other);
|
||||||
else if constexpr (StoreType == MatStoreType::COLUMN_MAJOR)
|
else if constexpr (StoreType == MatStoreType::COLUMN_MAJOR)
|
||||||
@@ -622,10 +631,14 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
|
||||||
[[nodiscard("You must use extracted scale")]]
|
[[nodiscard("You must use extracted scale")]]
|
||||||
Vector3<Type> mat_extract_scale(const Mat<4, 4, Type, St>& mat) noexcept
|
OMATH_CONSTEXPR Vector3<Type> mat_extract_scale(const Mat<4, 4, Type, St>& mat) noexcept
|
||||||
{
|
{
|
||||||
auto column_length = [](const Type x, const Type y, const Type z) {
|
auto column_length = [](const Type x, const Type y, const Type z) {
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return static_cast<Type>(gcem::sqrt(x * x + y * y + z * z));
|
||||||
|
#else
|
||||||
return static_cast<Type>(std::sqrt(x * x + y * y + z * z));
|
return static_cast<Type>(std::sqrt(x * x + y * y + z * z));
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto scale_x = column_length(mat.at(0, 0), mat.at(1, 0), mat.at(2, 0));
|
const auto scale_x = column_length(mat.at(0, 0), mat.at(1, 0), mat.at(2, 0));
|
||||||
@@ -635,9 +648,15 @@ namespace omath
|
|||||||
constexpr auto epsilon = std::numeric_limits<Type>::epsilon();
|
constexpr auto epsilon = std::numeric_limits<Type>::epsilon();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
gcem::abs(scale_x) < epsilon ? Type{1} : scale_x,
|
||||||
|
gcem::abs(scale_y) < epsilon ? Type{1} : scale_y,
|
||||||
|
gcem::abs(scale_z) < epsilon ? Type{1} : scale_z,
|
||||||
|
#else
|
||||||
std::abs(scale_x) < epsilon ? Type{1} : scale_x,
|
std::abs(scale_x) < epsilon ? Type{1} : scale_x,
|
||||||
std::abs(scale_y) < epsilon ? Type{1} : scale_y,
|
std::abs(scale_y) < epsilon ? Type{1} : scale_y,
|
||||||
std::abs(scale_z) < epsilon ? Type{1} : scale_z,
|
std::abs(scale_z) < epsilon ? Type{1} : scale_z,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -654,15 +673,21 @@ namespace omath
|
|||||||
const auto m22 = mat.at(2, 2) / scale.z;
|
const auto m22 = mat.at(2, 2) / scale.z;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
angles::radians_to_degrees(gcem::atan2(m21, m22)),
|
||||||
|
angles::radians_to_degrees(gcem::asin(std::clamp(-m20, Type{-1}, Type{1}))),
|
||||||
|
angles::radians_to_degrees(gcem::atan2(m10, m00)),
|
||||||
|
#else
|
||||||
angles::radians_to_degrees(std::atan2(m21, m22)),
|
angles::radians_to_degrees(std::atan2(m21, m22)),
|
||||||
angles::radians_to_degrees(std::asin(std::clamp(-m20, Type{-1}, Type{1}))),
|
angles::radians_to_degrees(std::asin(std::clamp(-m20, Type{-1}, Type{1}))),
|
||||||
angles::radians_to_degrees(std::atan2(m10, m00)),
|
angles::radians_to_degrees(std::atan2(m10, m00)),
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
||||||
[[nodiscard("You must use rotation matrix")]]
|
[[nodiscard("You must use rotation matrix")]]
|
||||||
Mat<4, 4, Type, St> mat_rotation_axis_x(const Angle& angle) noexcept
|
OMATH_CONSTEXPR Mat<4, 4, Type, St> mat_rotation_axis_x(const Angle& angle) noexcept
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
@@ -675,7 +700,7 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
||||||
[[nodiscard("You must use rotation matrix")]]
|
[[nodiscard("You must use rotation matrix")]]
|
||||||
Mat<4, 4, Type, St> mat_rotation_axis_y(const Angle& angle) noexcept
|
OMATH_CONSTEXPR Mat<4, 4, Type, St> mat_rotation_axis_y(const Angle& angle) noexcept
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
@@ -688,7 +713,7 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR, class Angle>
|
||||||
[[nodiscard("You must use rotation matrix")]]
|
[[nodiscard("You must use rotation matrix")]]
|
||||||
Mat<4, 4, Type, St> mat_rotation_axis_z(const Angle& angle) noexcept
|
OMATH_CONSTEXPR Mat<4, 4, Type, St> mat_rotation_axis_z(const Angle& angle) noexcept
|
||||||
{
|
{
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
@@ -701,7 +726,7 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR>
|
||||||
[[nodiscard("You must use camera view matrix")]]
|
[[nodiscard("You must use camera view matrix")]]
|
||||||
static Mat<4, 4, Type, St> mat_camera_view(const Vector3<Type>& forward, const Vector3<Type>& right,
|
OMATH_CONSTEXPR static Mat<4, 4, Type, St> mat_camera_view(const Vector3<Type>& forward, const Vector3<Type>& right,
|
||||||
const Vector3<Type>& up, const Vector3<Type>& camera_origin) noexcept
|
const Vector3<Type>& up, const Vector3<Type>& camera_origin) noexcept
|
||||||
{
|
{
|
||||||
return Mat<4, 4, Type, St>
|
return Mat<4, 4, Type, St>
|
||||||
@@ -715,12 +740,15 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use perspective matrix")]]
|
[[nodiscard("You must use perspective matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_perspective_left_handed_vertical_fov(const Type field_of_view, const Type aspect_ratio,
|
Mat<4, 4, Type, St> mat_perspective_left_handed_vertical_fov(const Type field_of_view, const Type aspect_ratio,
|
||||||
const Type near, const Type far) noexcept
|
const Type near, const Type far) noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
const auto fov_half_tan = gcem::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
||||||
|
#else
|
||||||
const auto fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
const auto fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
||||||
|
#endif
|
||||||
if constexpr (DepthRange == NDCDepthRange::ZERO_TO_ONE)
|
if constexpr (DepthRange == NDCDepthRange::ZERO_TO_ONE)
|
||||||
return {{Type{1} / (aspect_ratio * fov_half_tan), Type{0}, Type{0}, Type{0}},
|
return {{Type{1} / (aspect_ratio * fov_half_tan), Type{0}, Type{0}, Type{0}},
|
||||||
{Type{0}, Type{1} / fov_half_tan, Type{0}, Type{0}},
|
{Type{0}, Type{1} / fov_half_tan, Type{0}, Type{0}},
|
||||||
@@ -737,11 +765,15 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use perspective matrix")]]
|
[[nodiscard("You must use perspective matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_perspective_right_handed_vertical_fov(const Type field_of_view, const Type aspect_ratio,
|
Mat<4, 4, Type, St> mat_perspective_right_handed_vertical_fov(const Type field_of_view, const Type aspect_ratio,
|
||||||
const Type near, const Type far) noexcept
|
const Type near, const Type far) noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
const auto fov_half_tan = gcem::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
||||||
|
#else
|
||||||
const auto fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
const auto fov_half_tan = std::tan(angles::degrees_to_radians(field_of_view) / Type{2});
|
||||||
|
#endif
|
||||||
|
|
||||||
if constexpr (DepthRange == NDCDepthRange::ZERO_TO_ONE)
|
if constexpr (DepthRange == NDCDepthRange::ZERO_TO_ONE)
|
||||||
return {{Type{1} / (aspect_ratio * fov_half_tan), Type{0}, Type{0}, Type{0}},
|
return {{Type{1} / (aspect_ratio * fov_half_tan), Type{0}, Type{0}, Type{0}},
|
||||||
@@ -762,12 +794,16 @@ namespace omath
|
|||||||
// X and Y scales derived as: X = 1 / tan(hfov/2), Y = aspect / tan(hfov/2).
|
// X and Y scales derived as: X = 1 / tan(hfov/2), Y = aspect / tan(hfov/2).
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use perspective matrix")]]
|
[[nodiscard("You must use perspective matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_perspective_left_handed_horizontal_fov(const Type horizontal_fov,
|
Mat<4, 4, Type, St> mat_perspective_left_handed_horizontal_fov(const Type horizontal_fov,
|
||||||
const Type aspect_ratio, const Type near,
|
const Type aspect_ratio, const Type near,
|
||||||
const Type far) noexcept
|
const Type far) noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
const auto inv_tan_half_hfov = Type{1} / gcem::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
||||||
|
#else
|
||||||
const auto inv_tan_half_hfov = Type{1} / std::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
const auto inv_tan_half_hfov = Type{1} / std::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
||||||
|
#endif
|
||||||
const auto x_axis = inv_tan_half_hfov;
|
const auto x_axis = inv_tan_half_hfov;
|
||||||
const auto y_axis = inv_tan_half_hfov * aspect_ratio;
|
const auto y_axis = inv_tan_half_hfov * aspect_ratio;
|
||||||
|
|
||||||
@@ -787,12 +823,17 @@ namespace omath
|
|||||||
|
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use perspective matrix")]]
|
[[nodiscard("You must use perspective matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_perspective_right_handed_horizontal_fov(const Type horizontal_fov,
|
Mat<4, 4, Type, St> mat_perspective_right_handed_horizontal_fov(const Type horizontal_fov,
|
||||||
const Type aspect_ratio, const Type near,
|
const Type aspect_ratio, const Type near,
|
||||||
const Type far) noexcept
|
const Type far) noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
const auto inv_tan_half_hfov = Type{1} / gcem::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
||||||
|
#else
|
||||||
const auto inv_tan_half_hfov = Type{1} / std::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
const auto inv_tan_half_hfov = Type{1} / std::tan(angles::degrees_to_radians(horizontal_fov) / Type{2});
|
||||||
|
#endif
|
||||||
|
|
||||||
const auto x_axis = inv_tan_half_hfov;
|
const auto x_axis = inv_tan_half_hfov;
|
||||||
const auto y_axis = inv_tan_half_hfov * aspect_ratio;
|
const auto y_axis = inv_tan_half_hfov * aspect_ratio;
|
||||||
|
|
||||||
@@ -811,7 +852,7 @@ namespace omath
|
|||||||
}
|
}
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use ortho matrix")]]
|
[[nodiscard("You must use ortho matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, const Type bottom, const Type top,
|
Mat<4, 4, Type, St> mat_ortho_left_handed(const Type left, const Type right, const Type bottom, const Type top,
|
||||||
const Type near, const Type far) noexcept
|
const Type near, const Type far) noexcept
|
||||||
{
|
{
|
||||||
@@ -836,7 +877,7 @@ namespace omath
|
|||||||
}
|
}
|
||||||
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
template<class Type = float, MatStoreType St = MatStoreType::ROW_MAJOR,
|
||||||
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
NDCDepthRange DepthRange = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
|
||||||
[[nodiscard("You must use ortho matrix")]]
|
[[nodiscard("You must use ortho matrix")]] OMATH_CONSTEXPR
|
||||||
Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, const Type bottom, const Type top,
|
Mat<4, 4, Type, St> mat_ortho_right_handed(const Type left, const Type right, const Type bottom, const Type top,
|
||||||
const Type near, const Type far) noexcept
|
const Type near, const Type far) noexcept
|
||||||
{
|
{
|
||||||
@@ -860,7 +901,7 @@ namespace omath
|
|||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
template<class T = float, MatStoreType St = MatStoreType::COLUMN_MAJOR>
|
template<class T = float, MatStoreType St = MatStoreType::COLUMN_MAJOR>
|
||||||
Mat<4, 4, T, St> mat_look_at_left_handed(const Vector3<T>& eye, const Vector3<T>& center, const Vector3<T>& up)
|
OMATH_CONSTEXPR Mat<4, 4, T, St> mat_look_at_left_handed(const Vector3<T>& eye, const Vector3<T>& center, const Vector3<T>& up)
|
||||||
{
|
{
|
||||||
const Vector3<T> f = (center - eye).normalized();
|
const Vector3<T> f = (center - eye).normalized();
|
||||||
const Vector3<T> s = f.cross(up).normalized();
|
const Vector3<T> s = f.cross(up).normalized();
|
||||||
@@ -869,7 +910,7 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<class T = float, MatStoreType St = MatStoreType::COLUMN_MAJOR>
|
template<class T = float, MatStoreType St = MatStoreType::COLUMN_MAJOR>
|
||||||
Mat<4, 4, T, St>mat_look_at_right_handed(const Vector3<T>& eye, const Vector3<T>& center, const Vector3<T>& up)
|
OMATH_CONSTEXPR Mat<4, 4, T, St> mat_look_at_right_handed(const Vector3<T>& eye, const Vector3<T>& center, const Vector3<T>& up)
|
||||||
{
|
{
|
||||||
const Vector3<T> f = (center - eye).normalized();
|
const Vector3<T> f = (center - eye).normalized();
|
||||||
const Vector3<T> s = f.cross(up).normalized();
|
const Vector3<T> s = f.cross(up).normalized();
|
||||||
|
|||||||
@@ -2,8 +2,8 @@
|
|||||||
// Created by Orange on 11/13/2024.
|
// Created by Orange on 11/13/2024.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "omath/internal/optional_constexpr_math.hpp"
|
||||||
#include "vector3.hpp"
|
#include "vector3.hpp"
|
||||||
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
@@ -40,13 +40,13 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector::ContainedType side_a_length() const
|
OMATH_CONSTEXPR Vector::ContainedType side_a_length() const
|
||||||
{
|
{
|
||||||
return m_vertex1.distance_to(m_vertex2);
|
return m_vertex1.distance_to(m_vertex2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Vector::ContainedType side_b_length() const
|
OMATH_CONSTEXPR Vector::ContainedType side_b_length() const
|
||||||
{
|
{
|
||||||
return m_vertex3.distance_to(m_vertex2);
|
return m_vertex3.distance_to(m_vertex2);
|
||||||
}
|
}
|
||||||
@@ -69,7 +69,11 @@ namespace omath
|
|||||||
const auto side_b = side_b_length();
|
const auto side_b = side_b_length();
|
||||||
const auto hypot_value = hypot();
|
const auto hypot_value = hypot();
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f;
|
||||||
|
#else
|
||||||
return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f;
|
return std::abs(side_a * side_a + side_b * side_b - hypot_value * hypot_value) <= 0.0001f;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr Vector side_b_vector() const
|
constexpr Vector side_b_vector() const
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "omath/internal/optional_constexpr_math.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <format>
|
#include <format>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
@@ -116,9 +117,13 @@ namespace omath
|
|||||||
|
|
||||||
// Basic vector operations
|
// Basic vector operations
|
||||||
[[nodiscard("You must use distance")]]
|
[[nodiscard("You must use distance")]]
|
||||||
Type distance_to(const Vector2& other) const noexcept
|
OMATH_CONSTEXPR Type distance_to(const Vector2& other) const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::sqrt(distance_to_sqr(other));
|
||||||
|
#else
|
||||||
return std::sqrt(distance_to_sqr(other));
|
return std::sqrt(distance_to_sqr(other));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use squared distance")]]
|
[[nodiscard("You must use squared distance")]]
|
||||||
@@ -136,7 +141,11 @@ namespace omath
|
|||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
[[nodiscard("You must use length")]] constexpr Type length() const noexcept
|
[[nodiscard("You must use length")]] constexpr Type length() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::hypot(this->x, this->y);
|
||||||
|
#else
|
||||||
return std::hypot(this->x, this->y);
|
return std::hypot(this->x, this->y);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use normalized vector")]] constexpr Vector2 normalized() const noexcept
|
[[nodiscard("You must use normalized vector")]] constexpr Vector2 normalized() const noexcept
|
||||||
@@ -146,13 +155,17 @@ namespace omath
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
[[nodiscard("You must use length")]]
|
[[nodiscard("You must use length")]]
|
||||||
Type length() const noexcept
|
OMATH_CONSTEXPR Type length() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::hypot(x, y);
|
||||||
|
#else
|
||||||
return std::hypot(x, y);
|
return std::hypot(x, y);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use normalized vector")]]
|
[[nodiscard("You must use normalized vector")]]
|
||||||
Vector2 normalized() const noexcept
|
OMATH_CONSTEXPR Vector2 normalized() const noexcept
|
||||||
{
|
{
|
||||||
const Type len = length();
|
const Type len = length();
|
||||||
return len > static_cast<Type>(0) ? *this / len : *this;
|
return len > static_cast<Type>(0) ? *this / len : *this;
|
||||||
@@ -216,24 +229,24 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator<(const Vector2& other) const noexcept
|
OMATH_CONSTEXPR bool operator<(const Vector2& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() < other.length();
|
return length() < other.length();
|
||||||
}
|
}
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator>(const Vector2& other) const noexcept
|
OMATH_CONSTEXPR bool operator>(const Vector2& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() > other.length();
|
return length() > other.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator<=(const Vector2& other) const noexcept
|
OMATH_CONSTEXPR bool operator<=(const Vector2& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() <= other.length();
|
return length() <= other.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator>=(const Vector2& other) const noexcept
|
OMATH_CONSTEXPR bool operator>=(const Vector2& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() >= other.length();
|
return length() >= other.length();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -140,20 +140,24 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _MSC_VER
|
#ifndef _MSC_VER
|
||||||
[[nodiscard("You must use length")]] constexpr Type length() const
|
[[nodiscard("You must use length")]] constexpr Type length() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::sqrt(this->x * this->x + this->y * this->y + z * z);
|
||||||
|
#else
|
||||||
return std::hypot(this->x, this->y, z);
|
return std::hypot(this->x, this->y, z);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use 2D length")]] constexpr Type length_2d() const
|
[[nodiscard("You must use 2D length")]] constexpr Type length_2d() const noexcept
|
||||||
{
|
{
|
||||||
return Vector2<Type>::length();
|
return Vector2<Type>::length();
|
||||||
}
|
}
|
||||||
[[nodiscard("You must use distance")]] Type distance_to(const Vector3& other) const
|
[[nodiscard("You must use distance")]] OMATH_CONSTEXPR Type distance_to(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
return (*this - other).length();
|
return (*this - other).length();
|
||||||
}
|
}
|
||||||
[[nodiscard("You must use normalized vector")]] constexpr Vector3 normalized() const
|
[[nodiscard("You must use normalized vector")]] constexpr Vector3 normalized() const noexcept
|
||||||
{
|
{
|
||||||
const Type length_value = this->length();
|
const Type length_value = this->length();
|
||||||
|
|
||||||
@@ -161,13 +165,17 @@ namespace omath
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
[[nodiscard("You must use length")]]
|
[[nodiscard("You must use length")]]
|
||||||
Type length() const noexcept
|
OMATH_CONSTEXPR Type length() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::sqrt(this->x * this->x + this->y * this->y + this->z * this->z);
|
||||||
|
#else
|
||||||
return std::hypot(this->x, this->y, z);
|
return std::hypot(this->x, this->y, z);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use normalized vector")]]
|
[[nodiscard("You must use normalized vector")]]
|
||||||
Vector3 normalized() const noexcept
|
OMATH_CONSTEXPR Vector3 normalized() const noexcept
|
||||||
{
|
{
|
||||||
const Type len = this->length();
|
const Type len = this->length();
|
||||||
|
|
||||||
@@ -175,13 +183,13 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use 2D length")]]
|
[[nodiscard("You must use 2D length")]]
|
||||||
Type length_2d() const noexcept
|
OMATH_CONSTEXPR Type length_2d() const noexcept
|
||||||
{
|
{
|
||||||
return Vector2<Type>::length();
|
return Vector2<Type>::length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use distance")]]
|
[[nodiscard("You must use distance")]]
|
||||||
Type distance_to(const Vector3& v_other) const noexcept
|
OMATH_CONSTEXPR Type distance_to(const Vector3& v_other) const noexcept
|
||||||
{
|
{
|
||||||
return (*this - v_other).length();
|
return (*this - v_other).length();
|
||||||
}
|
}
|
||||||
@@ -249,24 +257,28 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use direction check result")]]
|
[[nodiscard("You must use direction check result")]]
|
||||||
bool point_to_same_direction(const Vector3& other) const
|
OMATH_CONSTEXPR bool point_to_same_direction(const Vector3& other) const
|
||||||
{
|
{
|
||||||
return dot(other) > static_cast<Type>(0);
|
return dot(other) > static_cast<Type>(0);
|
||||||
}
|
}
|
||||||
[[nodiscard("You must use angle between vectors")]]
|
[[nodiscard("You must use angle between vectors")]]
|
||||||
std::expected<Angle<float, 0.f, 180.f, AngleFlags::Clamped>, Vector3Error>
|
OMATH_CONSTEXPR std::expected<Angle<float, 0.f, 180.f, AngleFlags::Clamped>, Vector3Error>
|
||||||
angle_between(const Vector3& other) const noexcept
|
angle_between(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
const auto bottom = length() * other.length();
|
const auto bottom = length() * other.length();
|
||||||
|
|
||||||
if (bottom == static_cast<Type>(0))
|
if (bottom == static_cast<Type>(0))
|
||||||
return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE);
|
return std::unexpected(Vector3Error::IMPOSSIBLE_BETWEEN_ANGLE);
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(gcem::acos(dot(other) / bottom));
|
||||||
|
#else
|
||||||
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
|
return Angle<float, 0.f, 180.f, AngleFlags::Clamped>::from_radians(std::acos(dot(other) / bottom));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use perpendicularity check result")]]
|
[[nodiscard("You must use perpendicularity check result")]]
|
||||||
bool is_perpendicular(const Vector3& other, Type epsilon = static_cast<Type>(0.0001)) const noexcept
|
OMATH_CONSTEXPR bool is_perpendicular(const Vector3& other,
|
||||||
|
Type epsilon = static_cast<Type>(0.0001)) const noexcept
|
||||||
{
|
{
|
||||||
if (const auto angle = angle_between(other))
|
if (const auto angle = angle_between(other))
|
||||||
return std::abs(angle->as_degrees() - static_cast<Type>(90)) <= epsilon;
|
return std::abs(angle->as_degrees() - static_cast<Type>(90)) <= epsilon;
|
||||||
@@ -287,25 +299,25 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator<(const Vector3& other) const noexcept
|
OMATH_CONSTEXPR bool operator<(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() < other.length();
|
return length() < other.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator>(const Vector3& other) const noexcept
|
OMATH_CONSTEXPR bool operator>(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() > other.length();
|
return length() > other.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator<=(const Vector3& other) const noexcept
|
OMATH_CONSTEXPR bool operator<=(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() <= other.length();
|
return length() <= other.length();
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard("You must use comparison result")]]
|
[[nodiscard("You must use comparison result")]]
|
||||||
bool operator>=(const Vector3& other) const noexcept
|
OMATH_CONSTEXPR bool operator>=(const Vector3& other) const noexcept
|
||||||
{
|
{
|
||||||
return length() >= other.length();
|
return length() >= other.length();
|
||||||
}
|
}
|
||||||
@@ -320,6 +332,7 @@ namespace omath
|
|||||||
|
|
||||||
template<> struct std::hash<omath::Vector3<float>>
|
template<> struct std::hash<omath::Vector3<float>>
|
||||||
{
|
{
|
||||||
|
// NOTE: Cannot be constexpr because of MSVC
|
||||||
[[nodiscard("You must use hash value")]]
|
[[nodiscard("You must use hash value")]]
|
||||||
std::size_t operator()(const omath::Vector3<float>& vec) const noexcept
|
std::size_t operator()(const omath::Vector3<float>& vec) const noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "omath/internal/optional_constexpr_math.hpp"
|
||||||
#include "omath/trigonometry/angles.hpp"
|
#include "omath/trigonometry/angles.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <format>
|
#include <format>
|
||||||
@@ -70,31 +71,51 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Type sin() const noexcept
|
OMATH_CONSTEXPR Type sin() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::sin(as_radians());
|
||||||
|
#else
|
||||||
return std::sin(as_radians());
|
return std::sin(as_radians());
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Type cos() const noexcept
|
OMATH_CONSTEXPR Type cos() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::cos(as_radians());
|
||||||
|
#else
|
||||||
return std::cos(as_radians());
|
return std::cos(as_radians());
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Type tan() const noexcept
|
OMATH_CONSTEXPR Type tan() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::tan(as_radians());
|
||||||
|
#else
|
||||||
return std::tan(as_radians());
|
return std::tan(as_radians());
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Type atan() const noexcept
|
OMATH_CONSTEXPR Type atan() const noexcept
|
||||||
{
|
{
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
return gcem::atan(as_radians());
|
||||||
|
#else
|
||||||
return std::atan(as_radians());
|
return std::atan(as_radians());
|
||||||
|
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
Type cot() const noexcept
|
OMATH_CONSTEXPR Type cot() const noexcept
|
||||||
{
|
{
|
||||||
return cos() / sin();
|
return cos() / sin();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "omath/internal/optional_constexpr_math.hpp"
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
|
|
||||||
@@ -47,14 +48,18 @@ namespace omath::angles
|
|||||||
|
|
||||||
template<class Type>
|
template<class Type>
|
||||||
requires std::is_arithmetic_v<Type>
|
requires std::is_arithmetic_v<Type>
|
||||||
[[nodiscard]] Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept
|
[[nodiscard]] OMATH_CONSTEXPR Type wrap_angle(const Type& angle, const Type& min, const Type& max) noexcept
|
||||||
{
|
{
|
||||||
if (angle <= max && angle >= min)
|
if (angle <= max && angle >= min)
|
||||||
return angle;
|
return angle;
|
||||||
|
|
||||||
const Type range = max - min;
|
const Type range = max - min;
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
Type wrapped_angle = gcem::fmod(angle - min, range);
|
||||||
|
#else
|
||||||
Type wrapped_angle = std::fmod(angle - min, range);
|
Type wrapped_angle = std::fmod(angle - min, range);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (wrapped_angle < 0)
|
if (wrapped_angle < 0)
|
||||||
wrapped_angle += range;
|
wrapped_angle += range;
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
// Created by Vladislav on 30.12.2025.
|
// Created by Vladislav on 30.12.2025.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
class ElfPatternScanner final
|
class ElfPatternScanner final
|
||||||
@@ -18,14 +19,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
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");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&ElfPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
// Created by Copilot on 04.02.2026.
|
// Created by Copilot on 04.02.2026.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
class MachOPatternScanner final
|
class MachOPatternScanner final
|
||||||
@@ -18,14 +19,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = "__text");
|
const std::string_view& target_section_name = "__text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
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");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = "__text");
|
const std::string_view& target_section_name = "__text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = "__text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&MachOPatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
|
|||||||
@@ -3,9 +3,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include <algorithm>
|
||||||
|
#include <array>
|
||||||
#include <expected>
|
#include <expected>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <stdexcept>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -15,6 +18,8 @@ class unit_test_pattern_scan_corner_case_1_Test;
|
|||||||
class unit_test_pattern_scan_corner_case_2_Test;
|
class unit_test_pattern_scan_corner_case_2_Test;
|
||||||
class unit_test_pattern_scan_corner_case_3_Test;
|
class unit_test_pattern_scan_corner_case_3_Test;
|
||||||
class unit_test_pattern_scan_corner_case_4_Test;
|
class unit_test_pattern_scan_corner_case_4_Test;
|
||||||
|
class unit_test_pattern_scan_consteval_read_test_Test;
|
||||||
|
class unit_test_pattern_scan_consteval_spacing_and_case_Test;
|
||||||
// ReSharper restore CppInconsistentNaming
|
// ReSharper restore CppInconsistentNaming
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
@@ -29,8 +34,22 @@ namespace omath
|
|||||||
friend unit_test_pattern_scan_corner_case_2_Test;
|
friend unit_test_pattern_scan_corner_case_2_Test;
|
||||||
friend unit_test_pattern_scan_corner_case_3_Test;
|
friend unit_test_pattern_scan_corner_case_3_Test;
|
||||||
friend unit_test_pattern_scan_corner_case_4_Test;
|
friend unit_test_pattern_scan_corner_case_4_Test;
|
||||||
|
friend unit_test_pattern_scan_consteval_read_test_Test;
|
||||||
|
friend unit_test_pattern_scan_consteval_spacing_and_case_Test;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
template<std::size_t N>
|
||||||
|
struct ConstevalPattern final
|
||||||
|
{
|
||||||
|
char value[N]{};
|
||||||
|
|
||||||
|
// ReSharper disable once CppNonExplicitConvertingConstructor
|
||||||
|
constexpr ConstevalPattern(const char (&text)[N]) // NOLINT(*-explicit-constructor)
|
||||||
|
{
|
||||||
|
std::ranges::copy(text, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
[[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);
|
||||||
@@ -49,9 +68,26 @@ namespace omath
|
|||||||
if (!parsed_pattern) [[unlikely]]
|
if (!parsed_pattern) [[unlikely]]
|
||||||
return end;
|
return end;
|
||||||
|
|
||||||
|
return scan_for_parsed_pattern(begin, end, parsed_pattern.value());
|
||||||
|
}
|
||||||
|
template<ConstevalPattern Pattern, class IteratorType>
|
||||||
|
requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>>
|
||||||
|
static IteratorType scan_for_pattern(const IteratorType& begin, const IteratorType& end)
|
||||||
|
{
|
||||||
|
constexpr auto parsed_pattern = parse_pattern<Pattern>();
|
||||||
|
|
||||||
|
return scan_for_parsed_pattern(begin, end, parsed_pattern);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
template<class IteratorType, class ParsedPattern>
|
||||||
|
requires std::input_or_output_iterator<std::remove_cvref_t<IteratorType>>
|
||||||
|
static IteratorType scan_for_parsed_pattern(const IteratorType& begin, const IteratorType& end,
|
||||||
|
const ParsedPattern& parsed_pattern)
|
||||||
|
{
|
||||||
const auto whole_range_size = static_cast<std::ptrdiff_t>(std::distance(begin, end));
|
const auto whole_range_size = static_cast<std::ptrdiff_t>(std::distance(begin, end));
|
||||||
|
|
||||||
const auto pattern_size = static_cast<std::ptrdiff_t>(parsed_pattern->size());
|
const auto pattern_size = static_cast<std::ptrdiff_t>(parsed_pattern.size());
|
||||||
const std::ptrdiff_t scan_size = whole_range_size - pattern_size;
|
const std::ptrdiff_t scan_size = whole_range_size - pattern_size;
|
||||||
|
|
||||||
if (scan_size < 0)
|
if (scan_size < 0)
|
||||||
@@ -61,9 +97,9 @@ namespace omath
|
|||||||
{
|
{
|
||||||
bool found = true;
|
bool found = true;
|
||||||
|
|
||||||
for (std::ptrdiff_t j = 0; j < static_cast<std::ptrdiff_t>(parsed_pattern->size()); j++)
|
for (std::ptrdiff_t j = 0; j < static_cast<std::ptrdiff_t>(parsed_pattern.size()); j++)
|
||||||
{
|
{
|
||||||
found = parsed_pattern->at(j) == std::nullopt || parsed_pattern->at(j) == *(begin + i + j);
|
found = parsed_pattern.at(j) == std::nullopt || parsed_pattern.at(j) == *(begin + i + j);
|
||||||
|
|
||||||
if (!found)
|
if (!found)
|
||||||
break;
|
break;
|
||||||
@@ -73,10 +109,97 @@ namespace omath
|
|||||||
}
|
}
|
||||||
return end;
|
return end;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::expected<std::vector<std::optional<std::byte>>, PatternScanError>
|
static std::expected<std::vector<std::optional<std::byte>>, PatternScanError>
|
||||||
parse_pattern(const std::string_view& pattern_string);
|
parse_pattern(const std::string_view& pattern_string);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static bool is_space(const char c)
|
||||||
|
{
|
||||||
|
return c == ' ' || c == '\t' || c == '\n' || c == '\r';
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
constexpr static int hex_value(const char c)
|
||||||
|
{
|
||||||
|
if (c >= '0' && c <= '9')
|
||||||
|
return c - '0';
|
||||||
|
if (c >= 'A' && c <= 'F')
|
||||||
|
return c - 'A' + 10;
|
||||||
|
if (c >= 'a' && c <= 'f')
|
||||||
|
return c - 'a' + 10;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
template<ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static consteval std::size_t signature_size()
|
||||||
|
{
|
||||||
|
std::size_t count = 0;
|
||||||
|
bool in_token = false;
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i + 1 < sizeof(Pattern.value); ++i)
|
||||||
|
{
|
||||||
|
if (is_space(Pattern.value[i]))
|
||||||
|
{
|
||||||
|
in_token = false;
|
||||||
|
}
|
||||||
|
else if (!in_token)
|
||||||
|
{
|
||||||
|
++count;
|
||||||
|
in_token = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<ConstevalPattern Pattern>
|
||||||
|
static consteval std::array<std::optional<std::byte>, signature_size<Pattern>()> parse_pattern()
|
||||||
|
{
|
||||||
|
std::array<std::optional<std::byte>, signature_size<Pattern>()> result{};
|
||||||
|
std::size_t out = 0;
|
||||||
|
std::size_t i = 0;
|
||||||
|
|
||||||
|
while (i + 1 < sizeof(Pattern.value))
|
||||||
|
{
|
||||||
|
while (i + 1 < sizeof(Pattern.value) && is_space(Pattern.value[i]))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
const std::size_t token_start = i;
|
||||||
|
|
||||||
|
while (i + 1 < sizeof(Pattern.value) && !is_space(Pattern.value[i]))
|
||||||
|
++i;
|
||||||
|
|
||||||
|
const std::size_t token_size = i - token_start;
|
||||||
|
|
||||||
|
if (token_size == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// ReSharper disable once CppTooWideScope
|
||||||
|
const bool is_wildcard = (token_size == 1 || token_size == 2) && Pattern.value[token_start] == '?';
|
||||||
|
|
||||||
|
if (is_wildcard)
|
||||||
|
{
|
||||||
|
if (token_size == 2 && Pattern.value[token_start + 1] != '?')
|
||||||
|
throw std::logic_error("invalid wildcard token");
|
||||||
|
|
||||||
|
result[out++] = std::nullopt;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (token_size != 2)
|
||||||
|
throw std::logic_error("invalid byte token");
|
||||||
|
|
||||||
|
const int high = hex_value(Pattern.value[token_start]);
|
||||||
|
const int low = hex_value(Pattern.value[token_start + 1]);
|
||||||
|
|
||||||
|
if (high < 0 || low < 0)
|
||||||
|
throw std::logic_error("invalid hex byte");
|
||||||
|
|
||||||
|
result[out++] = static_cast<std::byte>((high << 4) | low);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -3,12 +3,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "pattern_scan.hpp"
|
||||||
|
#include "section_scan_result.hpp"
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <span>
|
#include <span>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include "section_scan_result.hpp"
|
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -20,14 +21,74 @@ namespace omath
|
|||||||
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_loaded_module(module_base_address, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
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");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_file(path_to_file, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
static std::optional<SectionScanResult>
|
static std::optional<SectionScanResult>
|
||||||
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data, const std::string_view& pattern,
|
||||||
const std::string_view& target_section_name = ".text");
|
const std::string_view& target_section_name = ".text");
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name = ".text")
|
||||||
|
{
|
||||||
|
return scan_for_pattern_in_memory_file(file_data, target_section_name,
|
||||||
|
&PePatternScanner::scan_section_for_pattern<Pattern>);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
using SectionScanFunction = std::optional<std::ptrdiff_t> (*)(std::span<const std::byte>);
|
||||||
|
|
||||||
|
template<PatternScanner::ConstevalPattern Pattern>
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::ptrdiff_t> scan_section_for_pattern(const std::span<const std::byte> section_data)
|
||||||
|
{
|
||||||
|
const auto result = PatternScanner::scan_for_pattern<Pattern>(section_data.begin(), section_data.end());
|
||||||
|
|
||||||
|
if (result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return result - section_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<std::uintptr_t>
|
||||||
|
scan_for_pattern_in_loaded_module(const void* module_base_address, const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult> scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
SectionScanFunction scan_pattern);
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
static std::optional<SectionScanResult>
|
||||||
|
scan_for_pattern_in_memory_file(std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name, SectionScanFunction scan_pattern);
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
+1
-22
@@ -1,27 +1,6 @@
|
|||||||
site_name: OMATH Docs
|
site_name: OMATH Docs
|
||||||
theme:
|
theme:
|
||||||
name: material
|
name: darkly
|
||||||
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:
|
extra_css:
|
||||||
- styles/center.css
|
- styles/center.css
|
||||||
- styles/custom-header.css
|
- styles/custom-header.css
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Vlad on 3/22/2025.
|
|
||||||
//
|
|
||||||
#include "omath/engines/cry_engine/formulas.hpp"
|
|
||||||
|
|
||||||
namespace omath::cry_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::cry_engine
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Created by Vlad on 8/11/2025.
|
|
||||||
//
|
|
||||||
#include "omath/engines/cry_engine/traits/camera_trait.hpp"
|
|
||||||
|
|
||||||
namespace omath::cry_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 cry_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::cry_engine
|
|
||||||
@@ -304,8 +304,8 @@ namespace
|
|||||||
elf_headers);
|
elf_headers);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<class FileHeader, class SectionHeader>
|
template<class FileHeader, class SectionHeader, class ScanPattern>
|
||||||
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const std::string_view pattern,
|
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const ScanPattern scan_pattern,
|
||||||
const std::string_view target_section_name)
|
const std::string_view target_section_name)
|
||||||
{
|
{
|
||||||
const auto* file_header = reinterpret_cast<const FileHeader*>(base);
|
const auto* file_header = reinterpret_cast<const FileHeader*>(base);
|
||||||
@@ -336,13 +336,14 @@ namespace
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto* section_begin = base + static_cast<std::size_t>(section->sh_addr);
|
const auto* section_begin = base + static_cast<std::size_t>(section->sh_addr);
|
||||||
const auto* section_end = section_begin + static_cast<std::size_t>(section->sh_size);
|
const auto scan_range =
|
||||||
|
std::span<const std::byte>{section_begin, static_cast<std::size_t>(section->sh_size)};
|
||||||
|
|
||||||
const auto scan_result = omath::PatternScanner::scan_for_pattern(section_begin, section_end, pattern);
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
if (scan_result == section_end)
|
if (!target_offset.has_value())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return reinterpret_cast<std::uintptr_t>(scan_result);
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -374,15 +375,58 @@ namespace omath
|
|||||||
if (!arch.has_value()) [[unlikely]]
|
if (!arch.has_value()) [[unlikely]]
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto scan_pattern =
|
||||||
|
[&pattern](const std::span<const std::byte> section_data) -> std::optional<std::ptrdiff_t>
|
||||||
|
{
|
||||||
|
const auto scan_result =
|
||||||
|
PatternScanner::scan_for_pattern(section_data.begin(), section_data.end(), pattern);
|
||||||
|
|
||||||
|
if (scan_result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return scan_result - section_data.begin();
|
||||||
|
};
|
||||||
|
|
||||||
if (arch == FileArch::x64)
|
if (arch == FileArch::x64)
|
||||||
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(static_cast<const std::byte*>(module_base_address),
|
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(static_cast<const std::byte*>(module_base_address),
|
||||||
pattern, target_section_name);
|
scan_pattern, target_section_name);
|
||||||
if (arch == FileArch::x32)
|
if (arch == FileArch::x32)
|
||||||
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(static_cast<const std::byte*>(module_base_address),
|
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(static_cast<const std::byte*>(module_base_address),
|
||||||
pattern, target_section_name);
|
scan_pattern, target_section_name);
|
||||||
|
|
||||||
std::unreachable();
|
std::unreachable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
if (module_base_address == nullptr) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* base = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
constexpr std::string_view valid_elf_signature = "\x7F"
|
||||||
|
"ELF";
|
||||||
|
if (std::string_view{reinterpret_cast<const char*>(base), valid_elf_signature.size()} != valid_elf_signature)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto ei_class_value = static_cast<uint8_t>(base[ei_class]);
|
||||||
|
const auto arch = ei_class_value == elfclass64 ? FileArch::x64
|
||||||
|
: ei_class_value == elfclass32 ? FileArch::x32
|
||||||
|
: std::optional<FileArch>{};
|
||||||
|
if (!arch.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
if (arch == FileArch::x64)
|
||||||
|
return scan_in_module_impl<Elf64Ehdr, Elf64Shdr>(base, scan_pattern, target_section_name);
|
||||||
|
if (arch == FileArch::x32)
|
||||||
|
return scan_in_module_impl<Elf32Ehdr, Elf32Shdr>(base, scan_pattern, target_section_name);
|
||||||
|
|
||||||
|
std::unreachable();
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -405,6 +449,26 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_elf_section_by_name(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -427,4 +491,24 @@ namespace omath
|
|||||||
.raw_base_addr = section->raw_base_addr,
|
.raw_base_addr = section->raw_base_addr,
|
||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_elf_section_by_name_from_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -316,8 +316,8 @@ namespace
|
|||||||
data, section_name);
|
data, section_name);
|
||||||
|
|
||||||
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
return extract_section_from_memory_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(data,
|
return extract_section_from_memory_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(
|
||||||
section_name);
|
data, section_name);
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
@@ -344,8 +344,9 @@ namespace
|
|||||||
return extract_section_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(file, section_name);
|
return extract_section_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(file, section_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename HeaderType, typename SegmentType, typename SectionType, std::uint32_t segment_cmd>
|
template<typename HeaderType, typename SegmentType, typename SectionType, std::uint32_t segment_cmd,
|
||||||
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const std::string_view pattern,
|
class ScanPattern>
|
||||||
|
std::optional<std::uintptr_t> scan_in_module_impl(const std::byte* base, const ScanPattern scan_pattern,
|
||||||
const std::string_view target_section_name)
|
const std::string_view target_section_name)
|
||||||
{
|
{
|
||||||
const auto* header = reinterpret_cast<const HeaderType*>(base);
|
const auto* header = reinterpret_cast<const HeaderType*>(base);
|
||||||
@@ -374,12 +375,13 @@ namespace
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const auto* section_begin = base + static_cast<std::size_t>(section->addr);
|
const auto* section_begin = base + static_cast<std::size_t>(section->addr);
|
||||||
const auto* section_end = section_begin + static_cast<std::size_t>(section->size);
|
const auto scan_range =
|
||||||
|
std::span<const std::byte>{section_begin, static_cast<std::size_t>(section->size)};
|
||||||
|
|
||||||
const auto scan_result = omath::PatternScanner::scan_for_pattern(section_begin, section_end, pattern);
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
|
|
||||||
if (scan_result != section_end)
|
if (target_offset.has_value())
|
||||||
return reinterpret_cast<std::uintptr_t>(scan_result);
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -404,12 +406,48 @@ namespace omath
|
|||||||
std::uint32_t magic{};
|
std::uint32_t magic{};
|
||||||
std::memcpy(&magic, base, sizeof(magic));
|
std::memcpy(&magic, base, sizeof(magic));
|
||||||
|
|
||||||
|
const auto scan_pattern =
|
||||||
|
[&pattern](const std::span<const std::byte> section_data) -> std::optional<std::ptrdiff_t>
|
||||||
|
{
|
||||||
|
const auto scan_result =
|
||||||
|
PatternScanner::scan_for_pattern(section_data.begin(), section_data.end(), pattern);
|
||||||
|
|
||||||
|
if (scan_result == section_data.end())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return scan_result - section_data.begin();
|
||||||
|
};
|
||||||
|
|
||||||
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
||||||
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, pattern,
|
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, scan_pattern,
|
||||||
target_section_name);
|
target_section_name);
|
||||||
|
|
||||||
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, pattern,
|
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, scan_pattern,
|
||||||
|
target_section_name);
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
if (module_base_address == nullptr) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* base = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
std::uint32_t magic{};
|
||||||
|
std::memcpy(&magic, base, sizeof(magic));
|
||||||
|
|
||||||
|
if (magic == mh_magic_64 || magic == mh_cigam_64)
|
||||||
|
return scan_in_module_impl<MachHeader64, SegmentCommand64, Section64, lc_segment_64>(base, scan_pattern,
|
||||||
|
target_section_name);
|
||||||
|
|
||||||
|
if (magic == mh_magic_32 || magic == mh_cigam_32)
|
||||||
|
return scan_in_module_impl<MachHeader32, SegmentCommand32, Section32, lc_segment>(base, scan_pattern,
|
||||||
target_section_name);
|
target_section_name);
|
||||||
|
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
@@ -438,6 +476,27 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto macho_section = get_macho_section_by_name(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!macho_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{macho_section->data.data(), macho_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = macho_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = macho_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -460,4 +519,24 @@ namespace omath
|
|||||||
.raw_base_addr = section->raw_base_addr,
|
.raw_base_addr = section->raw_base_addr,
|
||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto section = get_macho_section_by_name_from_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(std::span<const std::byte>{section->data.data(), section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = section->virtual_base_addr,
|
||||||
|
.raw_base_addr = section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
|
|||||||
@@ -234,8 +234,12 @@ namespace
|
|||||||
constexpr bool invalid_nt_header_file(const NtHeaderVariant& variant)
|
constexpr bool invalid_nt_header_file(const NtHeaderVariant& variant)
|
||||||
{
|
{
|
||||||
constexpr std::uint32_t nt_hdr_magic = 0x4550;
|
constexpr std::uint32_t nt_hdr_magic = 0x4550;
|
||||||
return std::visit([&nt_hdr_magic](const auto& header) -> bool { return header.signature != nt_hdr_magic; },
|
return std::visit(
|
||||||
variant);
|
[&nt_hdr_magic](const auto& header) -> bool
|
||||||
|
{
|
||||||
|
return header.signature != nt_hdr_magic;
|
||||||
|
},
|
||||||
|
variant);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ExtractedSection final
|
struct ExtractedSection final
|
||||||
@@ -262,8 +266,7 @@ namespace
|
|||||||
if (nt_off + sizeof(ImageNtHeaders<NtArchitecture::x32_bit>) > data.size())
|
if (nt_off + sizeof(ImageNtHeaders<NtArchitecture::x32_bit>) > data.size())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
const auto* x86_hdrs =
|
const auto* x86_hdrs = reinterpret_cast<const ImageNtHeaders<NtArchitecture::x32_bit>*>(data.data() + nt_off);
|
||||||
reinterpret_cast<const ImageNtHeaders<NtArchitecture::x32_bit>*>(data.data() + nt_off);
|
|
||||||
|
|
||||||
NtHeaderVariant nt_headers;
|
NtHeaderVariant nt_headers;
|
||||||
if (x86_hdrs->optional_header.magic == opt_hdr32_magic)
|
if (x86_hdrs->optional_header.magic == opt_hdr32_magic)
|
||||||
@@ -284,8 +287,8 @@ namespace
|
|||||||
[&data, §ion_name, nt_off](const auto& concrete_headers) -> std::optional<ExtractedSection>
|
[&data, §ion_name, nt_off](const auto& concrete_headers) -> std::optional<ExtractedSection>
|
||||||
{
|
{
|
||||||
constexpr std::size_t sig_size = sizeof(concrete_headers.signature);
|
constexpr std::size_t sig_size = sizeof(concrete_headers.signature);
|
||||||
const auto section_table_off = nt_off + sig_size + sizeof(FileHeader)
|
const auto section_table_off =
|
||||||
+ concrete_headers.file_header.size_optional_header;
|
nt_off + sig_size + sizeof(FileHeader) + concrete_headers.file_header.size_optional_header;
|
||||||
|
|
||||||
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; ++i)
|
for (std::size_t i = 0; i < concrete_headers.file_header.num_sections; ++i)
|
||||||
{
|
{
|
||||||
@@ -435,6 +438,65 @@ namespace omath
|
|||||||
},
|
},
|
||||||
nt_header_variant.value());
|
nt_header_variant.value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<std::uintptr_t>
|
||||||
|
PePatternScanner::scan_for_pattern_in_loaded_module(const void* module_base_address,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto base_address = reinterpret_cast<std::uintptr_t>(module_base_address);
|
||||||
|
const auto* base_bytes = static_cast<const std::byte*>(module_base_address);
|
||||||
|
|
||||||
|
if (!base_address)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto* dos_header = static_cast<const DosHeader*>(module_base_address);
|
||||||
|
|
||||||
|
if (invalid_dos_header_file(*dos_header)) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto nt_header_variant = get_nt_header_from_loaded_module(module_base_address);
|
||||||
|
|
||||||
|
if (!nt_header_variant) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return std::visit(
|
||||||
|
[base_bytes, base_address, lfanew = dos_header->e_lfanew, &target_section_name,
|
||||||
|
scan_pattern](const auto& nt_header) -> std::optional<std::uintptr_t>
|
||||||
|
{
|
||||||
|
constexpr std::size_t signature_size = sizeof(nt_header.signature);
|
||||||
|
const auto section_table_off = static_cast<std::size_t>(lfanew) + signature_size
|
||||||
|
+ sizeof(FileHeader) + nt_header.file_header.size_optional_header;
|
||||||
|
|
||||||
|
const auto* section_table = reinterpret_cast<const SectionHeader*>(base_bytes + section_table_off);
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < nt_header.file_header.num_sections; ++i)
|
||||||
|
{
|
||||||
|
const auto* section = section_table + i;
|
||||||
|
|
||||||
|
if (std::string_view{section->name} != target_section_name
|
||||||
|
|| section->size_raw_data == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const auto section_size = section->virtual_size != 0
|
||||||
|
? static_cast<std::size_t>(section->virtual_size)
|
||||||
|
: static_cast<std::size_t>(section->size_raw_data);
|
||||||
|
|
||||||
|
const auto* section_begin =
|
||||||
|
reinterpret_cast<const std::byte*>(base_address + section->virtual_address);
|
||||||
|
const auto scan_range = std::span<const std::byte>{section_begin, section_size};
|
||||||
|
|
||||||
|
const auto target_offset = scan_pattern(scan_range);
|
||||||
|
|
||||||
|
if (target_offset.has_value())
|
||||||
|
return reinterpret_cast<std::uintptr_t>(section_begin + target_offset.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::nullopt;
|
||||||
|
},
|
||||||
|
nt_header_variant.value());
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
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,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -457,6 +519,27 @@ namespace omath
|
|||||||
.target_offset = offset};
|
.target_offset = offset};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<SectionScanResult>
|
||||||
|
PePatternScanner::scan_for_pattern_in_file(const std::filesystem::path& path_to_file,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto pe_section = extract_section_from_pe_file(path_to_file, target_section_name);
|
||||||
|
|
||||||
|
if (!pe_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{pe_section->data.data(), pe_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = pe_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = pe_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<SectionScanResult>
|
std::optional<SectionScanResult>
|
||||||
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
const std::string_view& pattern,
|
const std::string_view& pattern,
|
||||||
@@ -479,4 +562,25 @@ 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<SectionScanResult>
|
||||||
|
PePatternScanner::scan_for_pattern_in_memory_file(const std::span<const std::byte> file_data,
|
||||||
|
const std::string_view& target_section_name,
|
||||||
|
const SectionScanFunction scan_pattern)
|
||||||
|
{
|
||||||
|
const auto pe_section = extract_section_from_pe_memory(file_data, target_section_name);
|
||||||
|
|
||||||
|
if (!pe_section.has_value()) [[unlikely]]
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
const auto target_offset =
|
||||||
|
scan_pattern(std::span<const std::byte>{pe_section->data.data(), pe_section->data.size()});
|
||||||
|
|
||||||
|
if (!target_offset.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return SectionScanResult{.virtual_base_addr = pe_section->virtual_base_addr,
|
||||||
|
.raw_base_addr = pe_section->raw_base_addr,
|
||||||
|
.target_offset = target_offset.value()};
|
||||||
|
}
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
@@ -9,6 +9,98 @@
|
|||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr bool close_to(const float actual, const float expected, const float epsilon)
|
||||||
|
{
|
||||||
|
const float diff = actual - expected;
|
||||||
|
return (diff < 0.0f ? -diff : diff) <= epsilon;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr bool vec_close_to(const Vector3<float>& actual, const Vector3<float>& expected, const float epsilon)
|
||||||
|
{
|
||||||
|
return close_to(actual.x, expected.x, epsilon) && close_to(actual.y, expected.y, epsilon)
|
||||||
|
&& close_to(actual.z, expected.z, epsilon);
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr omath::cry_engine::ViewAngles angles{};
|
||||||
|
constexpr auto forward = omath::cry_engine::forward_vector(angles);
|
||||||
|
constexpr auto right = omath::cry_engine::right_vector(angles);
|
||||||
|
constexpr auto up = omath::cry_engine::up_vector(angles);
|
||||||
|
constexpr auto rotation = omath::cry_engine::rotation_matrix(angles);
|
||||||
|
|
||||||
|
return vec_close_to(forward, omath::cry_engine::k_abs_forward, 1e-5f)
|
||||||
|
&& vec_close_to(right, omath::cry_engine::k_abs_right, 1e-5f)
|
||||||
|
&& vec_close_to(up, omath::cry_engine::k_abs_up, 1e-5f) && close_to(rotation.at(0, 0), 1.0f, 1e-5f)
|
||||||
|
&& close_to(rotation.at(1, 1), 1.0f, 1e-5f) && close_to(rotation.at(2, 2), 1.0f, 1e-5f)
|
||||||
|
&& close_to(rotation.at(3, 3), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"CryEngine basis vectors should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto view = omath::cry_engine::calc_view_matrix({}, {});
|
||||||
|
return close_to(view.at(0, 0), 1.0f, 1e-5f) && close_to(view.at(1, 2), 1.0f, 1e-5f)
|
||||||
|
&& close_to(view.at(2, 1), 1.0f, 1e-5f) && close_to(view.at(3, 3), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"CryEngine view matrix should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto transform = mat_translation<float, MatStoreType::ROW_MAJOR>({1.0f, 2.0f, 3.0f})
|
||||||
|
* mat_scale<float, MatStoreType::ROW_MAJOR>({2.0f, 3.0f, 4.0f});
|
||||||
|
constexpr auto origin = omath::cry_engine::extract_origin(transform);
|
||||||
|
constexpr auto scale = omath::cry_engine::extract_scale(transform);
|
||||||
|
|
||||||
|
return vec_close_to(origin, {1.0f, 2.0f, 3.0f}, 1e-5f) && vec_close_to(scale, {2.0f, 3.0f, 4.0f}, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"CryEngine transform extraction should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto projection = omath::cry_engine::calc_perspective_projection_matrix(
|
||||||
|
90.0f, 1.0f, 1.0f, 11.0f, NDCDepthRange::ZERO_TO_ONE);
|
||||||
|
return close_to(projection.at(0, 0), 1.0f, 1e-5f) && close_to(projection.at(1, 1), 1.0f, 1e-5f)
|
||||||
|
&& close_to(projection.at(2, 2), 1.1f, 1e-5f) && close_to(projection.at(2, 3), -1.1f, 1e-5f)
|
||||||
|
&& close_to(projection.at(3, 2), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"CryEngine projection matrix should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto angles =
|
||||||
|
omath::cry_engine::CameraTrait::calc_look_at_angle({}, omath::cry_engine::k_abs_forward);
|
||||||
|
constexpr auto view = omath::cry_engine::CameraTrait::calc_view_matrix(angles, {});
|
||||||
|
constexpr auto projection = omath::cry_engine::CameraTrait::calc_projection_matrix(
|
||||||
|
projection::FieldOfView::from_degrees(90.0f), {1.0f, 1.0f}, 1.0f, 11.0f,
|
||||||
|
NDCDepthRange::ZERO_TO_ONE);
|
||||||
|
|
||||||
|
return close_to(angles.pitch.as_degrees(), 0.0f, 1e-5f) && close_to(angles.yaw.as_degrees(), 0.0f, 1e-5f)
|
||||||
|
&& close_to(angles.roll.as_degrees(), 0.0f, 1e-5f) && close_to(view.at(0, 0), 1.0f, 1e-5f)
|
||||||
|
&& close_to(view.at(2, 1), 1.0f, 1e-5f) && close_to(projection.at(0, 0), 1.0f, 1e-5f)
|
||||||
|
&& close_to(projection.at(1, 1), 1.0f, 1e-5f) && close_to(projection.at(2, 2), 1.1f, 1e-5f)
|
||||||
|
&& close_to(projection.at(2, 3), -1.1f, 1e-5f) && close_to(projection.at(3, 2), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"CryEngine CameraTrait should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(omath::cry_engine::units_to_centimeters(100.0f) == 1.0f);
|
||||||
|
static_assert(omath::cry_engine::units_to_meters(2.0f) == 2.0f);
|
||||||
|
static_assert(omath::cry_engine::units_to_kilometers(2000.0f) == 2.0f);
|
||||||
|
static_assert(omath::cry_engine::centimeters_to_units(1.0f) == 100.0f);
|
||||||
|
static_assert(omath::cry_engine::meters_to_units(2.0f) == 2.0f);
|
||||||
|
static_assert(omath::cry_engine::kilometers_to_units(2.0f) == 2000.0f);
|
||||||
|
#endif
|
||||||
|
|
||||||
TEST(unit_test_cry_engine, look_at_forward)
|
TEST(unit_test_cry_engine, look_at_forward)
|
||||||
{
|
{
|
||||||
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_forward);
|
const auto angles = cry_engine::CameraTrait::calc_look_at_angle({}, cry_engine::k_abs_forward);
|
||||||
@@ -251,11 +343,9 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3Zero)
|
|||||||
|
|
||||||
TEST(unit_test_cry_engine, ViewAnglesAsVector3Values)
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3Values)
|
||||||
{
|
{
|
||||||
const omath::cry_engine::ViewAngles angles{
|
const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(45.f),
|
||||||
omath::cry_engine::PitchAngle::from_degrees(45.f),
|
omath::cry_engine::YawAngle::from_degrees(-90.f),
|
||||||
omath::cry_engine::YawAngle::from_degrees(-90.f),
|
omath::cry_engine::RollAngle::from_degrees(30.f)};
|
||||||
omath::cry_engine::RollAngle::from_degrees(30.f)
|
|
||||||
};
|
|
||||||
const auto vec = angles.as_vector3();
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
@@ -266,11 +356,9 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3Values)
|
|||||||
TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch)
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
{
|
{
|
||||||
// Pitch is clamped to [-90, 90]
|
// Pitch is clamped to [-90, 90]
|
||||||
const omath::cry_engine::ViewAngles angles{
|
const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(120.f),
|
||||||
omath::cry_engine::PitchAngle::from_degrees(120.f),
|
omath::cry_engine::YawAngle::from_degrees(0.f),
|
||||||
omath::cry_engine::YawAngle::from_degrees(0.f),
|
omath::cry_engine::RollAngle::from_degrees(0.f)};
|
||||||
omath::cry_engine::RollAngle::from_degrees(0.f)
|
|
||||||
};
|
|
||||||
const auto vec = angles.as_vector3();
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
@@ -279,11 +367,9 @@ TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch)
|
|||||||
TEST(unit_test_cry_engine, ViewAnglesAsVector3NormalizedYaw)
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
{
|
{
|
||||||
// Yaw is normalized to [-180, 180], 270 wraps to -90
|
// Yaw is normalized to [-180, 180], 270 wraps to -90
|
||||||
const omath::cry_engine::ViewAngles angles{
|
const omath::cry_engine::ViewAngles angles{omath::cry_engine::PitchAngle::from_degrees(0.f),
|
||||||
omath::cry_engine::PitchAngle::from_degrees(0.f),
|
omath::cry_engine::YawAngle::from_degrees(270.f),
|
||||||
omath::cry_engine::YawAngle::from_degrees(270.f),
|
omath::cry_engine::RollAngle::from_degrees(0.f)};
|
||||||
omath::cry_engine::RollAngle::from_degrees(0.f)
|
|
||||||
};
|
|
||||||
const auto vec = angles.as_vector3();
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
// Created by Orange on 11/30/2024.
|
// Created by Orange on 11/30/2024.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <omath/trigonometry/angle.hpp>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <numbers>
|
#include <numbers>
|
||||||
|
#include <omath/trigonometry/angle.hpp>
|
||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
@@ -19,6 +19,14 @@ namespace
|
|||||||
|
|
||||||
constexpr float k_eps = 1e-5f;
|
constexpr float k_eps = 1e-5f;
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
constexpr bool close_to(const float actual, const float expected, const float epsilon)
|
||||||
|
{
|
||||||
|
const float diff = actual - expected;
|
||||||
|
return (diff < 0.0f ? -diff : diff) <= epsilon;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// ---------- Construction / factories ----------
|
// ---------- Construction / factories ----------
|
||||||
@@ -190,3 +198,11 @@ TEST(UnitTestAngle, BinaryMinus_ReturnsWrappedDiff)
|
|||||||
const Deg c = a - b; // expect 340°
|
const Deg c = a - b; // expect 340°
|
||||||
EXPECT_FLOAT_EQ(c.as_degrees(), 340.0f);
|
EXPECT_FLOAT_EQ(c.as_degrees(), 340.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
static_assert(close_to(Pitch::from_degrees(0.0f).sin(), 0.0f, k_eps), "Sin should be constexpr with gcem");
|
||||||
|
static_assert(close_to(Pitch::from_degrees(0.0f).cos(), 1.0f, k_eps), "Cos should be constexpr with gcem");
|
||||||
|
static_assert(close_to(Pitch::from_degrees(45.0f).tan(), 1.0f, 1e-4f), "Tan should be constexpr with gcem");
|
||||||
|
static_assert(close_to(Pitch::from_degrees(45.0f).cot(), 1.0f, 1e-4f), "Cot should be constexpr with gcem");
|
||||||
|
static_assert(close_to(Pitch::from_degrees(45.0f).atan(), 0.66577375f, 1e-6f), "Atan should be constexpr with gcem");
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -35,13 +35,22 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
|||||||
|
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w8 = [&](std::size_t off, std::uint8_t v) { buf[off] = std::byte{v}; };
|
auto w8 = [&](std::size_t off, std::uint8_t v)
|
||||||
|
{
|
||||||
|
buf[off] = std::byte{v};
|
||||||
|
};
|
||||||
auto w16 = [&](std::size_t off, std::uint16_t v)
|
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 2); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 2);
|
||||||
|
};
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v)
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 4); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v)
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
{ std::memcpy(buf.data() + off, &v, 8); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// --- ELF64 file header ---
|
// --- ELF64 file header ---
|
||||||
// e_ident
|
// e_ident
|
||||||
@@ -53,19 +62,19 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
|||||||
w8(5, 1); // ELFDATA2LSB
|
w8(5, 1); // ELFDATA2LSB
|
||||||
w8(6, 1); // EV_CURRENT
|
w8(6, 1); // EV_CURRENT
|
||||||
// rest of e_ident is 0
|
// rest of e_ident is 0
|
||||||
w16(16, 2); // e_type = ET_EXEC
|
w16(16, 2); // e_type = ET_EXEC
|
||||||
w16(18, 62); // e_machine = EM_X86_64
|
w16(18, 62); // e_machine = EM_X86_64
|
||||||
w32(20, 1); // e_version
|
w32(20, 1); // e_version
|
||||||
w64(24, 0); // e_entry
|
w64(24, 0); // e_entry
|
||||||
w64(32, 0); // e_phoff
|
w64(32, 0); // e_phoff
|
||||||
w64(40, static_cast<std::uint64_t>(shdr_table_off)); // e_shoff
|
w64(40, static_cast<std::uint64_t>(shdr_table_off)); // e_shoff
|
||||||
w32(48, 0); // e_flags
|
w32(48, 0); // e_flags
|
||||||
w16(52, 64); // e_ehsize
|
w16(52, 64); // e_ehsize
|
||||||
w16(54, 56); // e_phentsize
|
w16(54, 56); // e_phentsize
|
||||||
w16(56, 0); // e_phnum
|
w16(56, 0); // e_phnum
|
||||||
w16(58, static_cast<std::uint16_t>(shdr_size)); // e_shentsize
|
w16(58, static_cast<std::uint16_t>(shdr_size)); // e_shentsize
|
||||||
w16(60, static_cast<std::uint16_t>(num_sections)); // e_shnum
|
w16(60, static_cast<std::uint16_t>(num_sections)); // e_shnum
|
||||||
w16(62, 2); // e_shstrndx = 2 (.shstrtab is section index 2)
|
w16(62, 2); // e_shstrndx = 2 (.shstrtab is section index 2)
|
||||||
|
|
||||||
// --- section data (.text) ---
|
// --- section data (.text) ---
|
||||||
const std::size_t copy_len = std::min(code_bytes.size(), text_size);
|
const std::size_t copy_len = std::min(code_bytes.size(), text_size);
|
||||||
@@ -104,9 +113,9 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
|||||||
// Section 1: .text
|
// Section 1: .text
|
||||||
{
|
{
|
||||||
const std::size_t base = shdr_table_off + 1 * shdr_size;
|
const std::size_t base = shdr_table_off + 1 * shdr_size;
|
||||||
w32(base + 0, 1); // sh_name → index 1 in shstrtab → ".text"
|
w32(base + 0, 1); // sh_name → index 1 in shstrtab → ".text"
|
||||||
w32(base + 4, 1); // sh_type = SHT_PROGBITS
|
w32(base + 4, 1); // sh_type = SHT_PROGBITS
|
||||||
w64(base + 8, 6); // sh_flags = SHF_ALLOC|SHF_EXECINSTR
|
w64(base + 8, 6); // sh_flags = SHF_ALLOC|SHF_EXECINSTR
|
||||||
w64(base + 16, static_cast<std::uint64_t>(text_off)); // sh_addr (same as offset in test)
|
w64(base + 16, static_cast<std::uint64_t>(text_off)); // sh_addr (same as offset in test)
|
||||||
w64(base + 24, static_cast<std::uint64_t>(text_off)); // sh_offset
|
w64(base + 24, static_cast<std::uint64_t>(text_off)); // sh_offset
|
||||||
w64(base + 32, static_cast<std::uint64_t>(text_size)); // sh_size
|
w64(base + 32, static_cast<std::uint64_t>(text_size)); // sh_size
|
||||||
@@ -116,8 +125,8 @@ static std::vector<std::byte> make_elf64_with_text_section(const std::vector<std
|
|||||||
// Section 2: .shstrtab
|
// Section 2: .shstrtab
|
||||||
{
|
{
|
||||||
const std::size_t base = shdr_table_off + 2 * shdr_size;
|
const std::size_t base = shdr_table_off + 2 * shdr_size;
|
||||||
w32(base + 0, 0); // sh_name → index 0 → "" (good enough for scanner)
|
w32(base + 0, 0); // sh_name → index 0 → "" (good enough for scanner)
|
||||||
w32(base + 4, 3); // sh_type = SHT_STRTAB
|
w32(base + 4, 3); // sh_type = SHT_STRTAB
|
||||||
w64(base + 24, static_cast<std::uint64_t>(shstrtab_off)); // sh_offset
|
w64(base + 24, static_cast<std::uint64_t>(shstrtab_off)); // sh_offset
|
||||||
w64(base + 32, static_cast<std::uint64_t>(shstrtab_size)); // sh_size
|
w64(base + 32, static_cast<std::uint64_t>(shstrtab_size)); // sh_size
|
||||||
}
|
}
|
||||||
@@ -151,6 +160,18 @@ TEST(unit_test_elf_pattern_scan_memory, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
ElfPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf}, ".text");
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_elf_pattern_scan_memory, pattern_not_found_returns_nullopt)
|
TEST(unit_test_elf_pattern_scan_memory, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03, 0x04};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03, 0x04};
|
||||||
@@ -182,8 +203,8 @@ TEST(unit_test_elf_pattern_scan_memory, missing_section_returns_nullopt)
|
|||||||
const std::vector<std::uint8_t> code = {0x90, 0x90};
|
const std::vector<std::uint8_t> code = {0x90, 0x90};
|
||||||
const auto buf = make_elf64_with_text_section(code);
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
|
||||||
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
const auto result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "90 90",
|
||||||
"90 90", ".nonexistent");
|
".nonexistent");
|
||||||
|
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
@@ -201,8 +222,8 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
|||||||
}
|
}
|
||||||
|
|
||||||
const auto file_result = ElfPatternScanner::scan_for_pattern_in_file(tmp_path, "48 89 E5 DE AD", ".text");
|
const auto file_result = ElfPatternScanner::scan_for_pattern_in_file(tmp_path, "48 89 E5 DE AD", ".text");
|
||||||
const auto mem_result =
|
const auto mem_result = ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf},
|
||||||
ElfPatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "48 89 E5 DE AD", ".text");
|
"48 89 E5 DE AD", ".text");
|
||||||
|
|
||||||
std::filesystem::remove(tmp_path);
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
@@ -212,3 +233,27 @@ TEST(unit_test_elf_pattern_scan_memory, matches_file_scan)
|
|||||||
EXPECT_EQ(file_result->raw_base_addr, mem_result->raw_base_addr);
|
EXPECT_EQ(file_result->raw_base_addr, mem_result->raw_base_addr);
|
||||||
EXPECT_EQ(file_result->target_offset, mem_result->target_offset);
|
EXPECT_EQ(file_result->target_offset, mem_result->target_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_elf64_with_text_section(code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_elf_consteval_test.elf";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = ElfPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path, ".text");
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_elf_pattern_scan_memory, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = ElfPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Tests for MachOPatternScanner::scan_for_pattern_in_memory_file
|
// Tests for MachOPatternScanner::scan_for_pattern_in_memory_file
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <omath/utility/macho_pattern_scan.hpp>
|
#include <omath/utility/macho_pattern_scan.hpp>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -37,38 +39,43 @@ static std::vector<std::byte> make_macho64_with_text_section(const std::vector<s
|
|||||||
constexpr std::size_t total_size = text_raw_off + text_raw_size;
|
constexpr std::size_t total_size = text_raw_off + text_raw_size;
|
||||||
constexpr std::uint64_t text_vmaddr = 0x1000ULL;
|
constexpr std::uint64_t text_vmaddr = 0x1000ULL;
|
||||||
|
|
||||||
constexpr std::uint32_t cmd_size =
|
constexpr std::uint32_t cmd_size = static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
||||||
static_cast<std::uint32_t>(seg_size + sect_hdr_size); // segment + 1 section
|
|
||||||
|
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// MachHeader64
|
// MachHeader64
|
||||||
w32(0, mh_magic_64);
|
w32(0, mh_magic_64);
|
||||||
w32(4, 0x0100000C); // cputype = CPU_TYPE_ARM64 (doesn't matter for scan)
|
w32(4, 0x0100000C); // cputype = CPU_TYPE_ARM64 (doesn't matter for scan)
|
||||||
w32(12, 2); // filetype = MH_EXECUTE
|
w32(12, 2); // filetype = MH_EXECUTE
|
||||||
w32(16, 1); // ncmds = 1
|
w32(16, 1); // ncmds = 1
|
||||||
w32(20, cmd_size); // sizeofcmds
|
w32(20, cmd_size); // sizeofcmds
|
||||||
|
|
||||||
// SegmentCommand64 at 0x20
|
// SegmentCommand64 at 0x20
|
||||||
constexpr std::size_t seg_off = hdr_size;
|
constexpr std::size_t seg_off = hdr_size;
|
||||||
w32(seg_off + 0, lc_segment_64);
|
w32(seg_off + 0, lc_segment_64);
|
||||||
w32(seg_off + 4, cmd_size);
|
w32(seg_off + 4, cmd_size);
|
||||||
std::memcpy(buf.data() + seg_off + 8, "__TEXT", 6); // segname
|
std::memcpy(buf.data() + seg_off + 8, "__TEXT", 6); // segname
|
||||||
w64(seg_off + 24, text_vmaddr); // vmaddr
|
w64(seg_off + 24, text_vmaddr); // vmaddr
|
||||||
w64(seg_off + 32, text_raw_size); // vmsize
|
w64(seg_off + 32, text_raw_size); // vmsize
|
||||||
w64(seg_off + 40, text_raw_off); // fileoff
|
w64(seg_off + 40, text_raw_off); // fileoff
|
||||||
w64(seg_off + 48, text_raw_size); // filesize
|
w64(seg_off + 48, text_raw_size); // filesize
|
||||||
w32(seg_off + 64, 1); // nsects
|
w32(seg_off + 64, 1); // nsects
|
||||||
|
|
||||||
// Section64 at 0x68
|
// Section64 at 0x68
|
||||||
constexpr std::size_t sect_off = seg_off + seg_size;
|
constexpr std::size_t sect_off = seg_off + seg_size;
|
||||||
std::memcpy(buf.data() + sect_off + 0, "__text", 6); // sectname
|
std::memcpy(buf.data() + sect_off + 0, "__text", 6); // sectname
|
||||||
std::memcpy(buf.data() + sect_off + 16, "__TEXT", 6); // segname
|
std::memcpy(buf.data() + sect_off + 16, "__TEXT", 6); // segname
|
||||||
w64(sect_off + 32, text_vmaddr); // addr
|
w64(sect_off + 32, text_vmaddr); // addr
|
||||||
w64(sect_off + 40, text_raw_size); // size
|
w64(sect_off + 40, text_raw_size); // size
|
||||||
w32(sect_off + 48, static_cast<std::uint32_t>(text_raw_off)); // offset (file offset)
|
w32(sect_off + 48, static_cast<std::uint32_t>(text_raw_off)); // offset (file offset)
|
||||||
|
|
||||||
// Section data
|
// Section data
|
||||||
@@ -105,6 +112,18 @@ TEST(unit_test_macho_memory_file_scan, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_macho64_with_text_section(code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
MachOPatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_macho_memory_file_scan, pattern_not_found_returns_nullopt)
|
TEST(unit_test_macho_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||||
@@ -143,3 +162,27 @@ TEST(unit_test_macho_memory_file_scan, raw_addr_and_virtual_addr_correct)
|
|||||||
EXPECT_EQ(result->raw_base_addr, expected_raw_off);
|
EXPECT_EQ(result->raw_base_addr, expected_raw_off);
|
||||||
EXPECT_EQ(result->virtual_base_addr, 0x1000u);
|
EXPECT_EQ(result->virtual_base_addr, 0x1000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_macho64_with_text_section(code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_macho_consteval_test.bin";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = MachOPatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_macho_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = MachOPatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
// UnitTestMat.cpp
|
// UnitTestMat.cpp
|
||||||
#include "omath/linear_algebra/mat.hpp"
|
#include "omath/linear_algebra/mat.hpp"
|
||||||
#include "omath/linear_algebra/vector3.hpp"
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
|
#include "omath/trigonometry/angle.hpp"
|
||||||
#include "omath/trigonometry/angles.hpp"
|
#include "omath/trigonometry/angles.hpp"
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
using Pitch = Angle<float, static_cast<float>(-90), static_cast<float>(90), AngleFlags::Clamped>;
|
||||||
|
|
||||||
|
constexpr bool close_to(const float actual, const float expected, const float epsilon)
|
||||||
|
{
|
||||||
|
const float diff = actual - expected;
|
||||||
|
return (diff < 0.0f ? -diff : diff) <= epsilon;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
class UnitTestMat : public ::testing::Test
|
class UnitTestMat : public ::testing::Test
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -523,3 +537,47 @@ TEST(UnitTestMatStandalone, MatOrthoNegativeOneToOneDefault)
|
|||||||
|
|
||||||
EXPECT_EQ(ortho_default, ortho_explicit);
|
EXPECT_EQ(ortho_default, ortho_explicit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto scale = mat_extract_scale(mat_scale(Vector3{2.0f, 3.0f, 4.0f}));
|
||||||
|
return close_to(scale.x, 2.0f, 1e-5f) && close_to(scale.y, 3.0f, 1e-5f) && close_to(scale.z, 4.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"Mat scale extraction should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto rotation = mat_rotation_axis_z<float>(Pitch::from_degrees(90.0f));
|
||||||
|
return close_to(rotation.at(0, 0), 0.0f, 1e-5f) && close_to(rotation.at(0, 1), -1.0f, 1e-5f)
|
||||||
|
&& close_to(rotation.at(1, 0), 1.0f, 1e-5f) && close_to(rotation.at(1, 1), 0.0f, 1e-5f)
|
||||||
|
&& close_to(rotation.at(2, 2), 1.0f, 1e-5f) && close_to(rotation.at(3, 3), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"Mat rotation should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto projection =
|
||||||
|
mat_perspective_left_handed_vertical_fov<float, MatStoreType::ROW_MAJOR,
|
||||||
|
NDCDepthRange::ZERO_TO_ONE>(90.0f, 1.0f, 1.0f, 11.0f);
|
||||||
|
return close_to(projection.at(0, 0), 1.0f, 1e-5f) && close_to(projection.at(1, 1), 1.0f, 1e-5f)
|
||||||
|
&& close_to(projection.at(2, 2), 1.1f, 1e-5f) && close_to(projection.at(2, 3), -1.1f, 1e-5f)
|
||||||
|
&& close_to(projection.at(3, 2), 1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"Mat vertical-FOV perspective should be constexpr with gcem");
|
||||||
|
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto projection =
|
||||||
|
mat_perspective_right_handed_horizontal_fov<float, MatStoreType::ROW_MAJOR,
|
||||||
|
NDCDepthRange::ZERO_TO_ONE>(90.0f, 2.0f, 1.0f, 11.0f);
|
||||||
|
return close_to(projection.at(0, 0), 1.0f, 1e-5f) && close_to(projection.at(1, 1), 2.0f, 1e-5f)
|
||||||
|
&& close_to(projection.at(2, 2), -1.1f, 1e-5f) && close_to(projection.at(2, 3), -1.1f, 1e-5f)
|
||||||
|
&& close_to(projection.at(3, 2), -1.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"Mat horizontal-FOV perspective should be constexpr with gcem");
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
#include "omath/utility/pe_pattern_scan.hpp"
|
#include "omath/utility/pe_pattern_scan.hpp"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include <omath/utility/pattern_scan.hpp>
|
#include <omath/utility/pattern_scan.hpp>
|
||||||
#include <source_location>
|
|
||||||
#include <print>
|
#include <print>
|
||||||
|
#include <source_location>
|
||||||
TEST(unit_test_pattern_scan, read_test)
|
TEST(unit_test_pattern_scan, read_test)
|
||||||
{
|
{
|
||||||
const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
|
const auto result = omath::PatternScanner::parse_pattern("FF ? ?? E9");
|
||||||
@@ -51,3 +51,37 @@ TEST(unit_test_pattern_scan, corner_case_4)
|
|||||||
const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 ");
|
const auto result = omath::PatternScanner::parse_pattern("X ? ?? E9 ");
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan, consteval_read_test)
|
||||||
|
{
|
||||||
|
constexpr auto result = omath::PatternScanner::parse_pattern<"FF ? ?? E9">();
|
||||||
|
|
||||||
|
static_assert(result.size() == 4);
|
||||||
|
static_assert(result[0] == static_cast<std::byte>(0xFF));
|
||||||
|
static_assert(result[1] == std::nullopt);
|
||||||
|
static_assert(result[2] == std::nullopt);
|
||||||
|
static_assert(result[3] == static_cast<std::byte>(0xE9));
|
||||||
|
|
||||||
|
EXPECT_EQ(result[0], static_cast<std::byte>(0xFF));
|
||||||
|
EXPECT_EQ(result[1], std::nullopt);
|
||||||
|
EXPECT_EQ(result[2], std::nullopt);
|
||||||
|
EXPECT_EQ(result[3], static_cast<std::byte>(0xE9));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan, consteval_spacing_and_case)
|
||||||
|
{
|
||||||
|
constexpr auto result = omath::PatternScanner::parse_pattern<" \tde\nAD\r?? ? ef ">();
|
||||||
|
|
||||||
|
static_assert(result.size() == 5);
|
||||||
|
static_assert(result[0] == static_cast<std::byte>(0xDE));
|
||||||
|
static_assert(result[1] == static_cast<std::byte>(0xAD));
|
||||||
|
static_assert(result[2] == std::nullopt);
|
||||||
|
static_assert(result[3] == std::nullopt);
|
||||||
|
static_assert(result[4] == static_cast<std::byte>(0xEF));
|
||||||
|
|
||||||
|
EXPECT_EQ(result[0], static_cast<std::byte>(0xDE));
|
||||||
|
EXPECT_EQ(result[1], static_cast<std::byte>(0xAD));
|
||||||
|
EXPECT_EQ(result[2], std::nullopt);
|
||||||
|
EXPECT_EQ(result[3], std::nullopt);
|
||||||
|
EXPECT_EQ(result[4], static_cast<std::byte>(0xEF));
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,6 +14,16 @@ TEST(unit_test_pattern_scan_extra, IteratorScanFound)
|
|||||||
EXPECT_EQ(std::distance(buf.begin(), it), 0);
|
EXPECT_EQ(std::distance(buf.begin(), it), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pattern_scan_extra, ConstevalIteratorScan)
|
||||||
|
{
|
||||||
|
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0xDE),
|
||||||
|
static_cast<std::byte>(0xAD), static_cast<std::byte>(0xBE),
|
||||||
|
static_cast<std::byte>(0xEF), static_cast<std::byte>(0x11)};
|
||||||
|
const auto it = PatternScanner::scan_for_pattern<"DE ?? BE EF">(buf.begin(), buf.end());
|
||||||
|
EXPECT_NE(it, buf.end());
|
||||||
|
EXPECT_EQ(std::distance(buf.begin(), it), 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_pattern_scan_extra, IteratorScanNotFound)
|
TEST(unit_test_pattern_scan_extra, IteratorScanNotFound)
|
||||||
{
|
{
|
||||||
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0x11),
|
std::vector<std::byte> buf = {static_cast<std::byte>(0x00), static_cast<std::byte>(0x11),
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
// Tests for PePatternScanner::scan_for_pattern_in_memory_file
|
// Tests for PePatternScanner::scan_for_pattern_in_memory_file
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <omath/utility/pe_pattern_scan.hpp>
|
#include <omath/utility/pe_pattern_scan.hpp>
|
||||||
#include <span>
|
#include <span>
|
||||||
@@ -10,8 +12,7 @@ using namespace omath;
|
|||||||
// Reuse the fake-module builder from unit_test_pe_pattern_scan_loaded.cpp but
|
// Reuse the fake-module builder from unit_test_pe_pattern_scan_loaded.cpp but
|
||||||
// lay out the buffer as a raw PE *file* (ptr_raw_data != virtual_address).
|
// lay out the buffer as a raw PE *file* (ptr_raw_data != virtual_address).
|
||||||
static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, std::uint32_t ptr_raw_data,
|
static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, std::uint32_t ptr_raw_data,
|
||||||
std::uint32_t section_size,
|
std::uint32_t section_size, const std::vector<std::uint8_t>& code_bytes)
|
||||||
const std::vector<std::uint8_t>& code_bytes)
|
|
||||||
{
|
{
|
||||||
constexpr std::uint32_t e_lfanew = 0x80;
|
constexpr std::uint32_t e_lfanew = 0x80;
|
||||||
constexpr std::uint32_t nt_sig = 0x4550;
|
constexpr std::uint32_t nt_sig = 0x4550;
|
||||||
@@ -24,9 +25,18 @@ static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, s
|
|||||||
const std::uint32_t total_size = ptr_raw_data + section_size + 0x100;
|
const std::uint32_t total_size = ptr_raw_data + section_size + 0x100;
|
||||||
std::vector<std::byte> buf(total_size, std::byte{0});
|
std::vector<std::byte> buf(total_size, std::byte{0});
|
||||||
|
|
||||||
auto w16 = [&](std::size_t off, std::uint16_t v) { std::memcpy(buf.data() + off, &v, 2); };
|
auto w16 = [&](std::size_t off, std::uint16_t v)
|
||||||
auto w32 = [&](std::size_t off, std::uint32_t v) { std::memcpy(buf.data() + off, &v, 4); };
|
{
|
||||||
auto w64 = [&](std::size_t off, std::uint64_t v) { std::memcpy(buf.data() + off, &v, 8); };
|
std::memcpy(buf.data() + off, &v, 2);
|
||||||
|
};
|
||||||
|
auto w32 = [&](std::size_t off, std::uint32_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 4);
|
||||||
|
};
|
||||||
|
auto w64 = [&](std::size_t off, std::uint64_t v)
|
||||||
|
{
|
||||||
|
std::memcpy(buf.data() + off, &v, 8);
|
||||||
|
};
|
||||||
|
|
||||||
// DOS header
|
// DOS header
|
||||||
w16(0x00, 0x5A4D);
|
w16(0x00, 0x5A4D);
|
||||||
@@ -48,10 +58,10 @@ static std::vector<std::byte> make_fake_pe_file(std::uint32_t virtual_address, s
|
|||||||
// Section header (.text)
|
// Section header (.text)
|
||||||
const std::size_t sh_off = section_table_off;
|
const std::size_t sh_off = section_table_off;
|
||||||
std::memcpy(buf.data() + sh_off, ".text", 5);
|
std::memcpy(buf.data() + sh_off, ".text", 5);
|
||||||
w32(sh_off + 8, section_size); // VirtualSize
|
w32(sh_off + 8, section_size); // VirtualSize
|
||||||
w32(sh_off + 12, virtual_address); // VirtualAddress
|
w32(sh_off + 12, virtual_address); // VirtualAddress
|
||||||
w32(sh_off + 16, section_size); // SizeOfRawData
|
w32(sh_off + 16, section_size); // SizeOfRawData
|
||||||
w32(sh_off + 20, ptr_raw_data); // PointerToRawData
|
w32(sh_off + 20, ptr_raw_data); // PointerToRawData
|
||||||
|
|
||||||
// Place code at raw file offset
|
// Place code at raw file offset
|
||||||
const std::size_t copy_len = std::min(code_bytes.size(), static_cast<std::size_t>(section_size));
|
const std::size_t copy_len = std::min(code_bytes.size(), static_cast<std::size_t>(section_size));
|
||||||
@@ -87,13 +97,24 @@ TEST(unit_test_pe_memory_file_scan, finds_pattern_with_wildcard)
|
|||||||
EXPECT_EQ(result->target_offset, 0);
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_finds_pattern_with_wildcard)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x00, 0xDE, 0xAD, 0xBE, 0xEF};
|
||||||
|
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
|
||||||
|
const auto result =
|
||||||
|
PePatternScanner::scan_for_pattern_in_memory_file<"DE ?? BE EF">(std::span<const std::byte>{buf});
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 1);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_pe_memory_file_scan, pattern_not_found_returns_nullopt)
|
TEST(unit_test_pe_memory_file_scan, pattern_not_found_returns_nullopt)
|
||||||
{
|
{
|
||||||
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
const std::vector<std::uint8_t> code = {0x01, 0x02, 0x03};
|
||||||
const auto buf = make_fake_pe_file(0x1000, 0x400, static_cast<std::uint32_t>(code.size()), code);
|
const auto buf = make_fake_pe_file(0x1000, 0x400, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
|
||||||
const auto result =
|
const auto result = PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
||||||
PePatternScanner::scan_for_pattern_in_memory_file(std::span<const std::byte>{buf}, "AA BB CC");
|
|
||||||
|
|
||||||
EXPECT_FALSE(result.has_value());
|
EXPECT_FALSE(result.has_value());
|
||||||
}
|
}
|
||||||
@@ -126,3 +147,27 @@ TEST(unit_test_pe_memory_file_scan, raw_addr_differs_from_virtual_address)
|
|||||||
// virtual_base_addr = virtual_address + image_base (image_base = 0)
|
// virtual_base_addr = virtual_address + image_base (image_base = 0)
|
||||||
EXPECT_EQ(result->virtual_base_addr, 0x3000u);
|
EXPECT_EQ(result->virtual_base_addr, 0x3000u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_file_scan_finds_pattern)
|
||||||
|
{
|
||||||
|
const std::vector<std::uint8_t> code = {0x48, 0x89, 0xE5, 0xDE, 0xAD};
|
||||||
|
const auto buf = make_fake_pe_file(0x2000, 0x600, static_cast<std::uint32_t>(code.size()), code);
|
||||||
|
const auto tmp_path = std::filesystem::temp_directory_path() / "omath_pe_consteval_test.exe";
|
||||||
|
{
|
||||||
|
std::ofstream out(tmp_path, std::ios::binary);
|
||||||
|
out.write(reinterpret_cast<const char*>(buf.data()), static_cast<std::streamsize>(buf.size()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto result = PePatternScanner::scan_for_pattern_in_file<"48 ?? E5">(tmp_path);
|
||||||
|
|
||||||
|
std::filesystem::remove(tmp_path);
|
||||||
|
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
EXPECT_EQ(result->target_offset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_pe_memory_file_scan, consteval_loaded_module_null_returns_nullopt)
|
||||||
|
{
|
||||||
|
const auto result = PePatternScanner::scan_for_pattern_in_loaded_module<"DE AD">(nullptr);
|
||||||
|
EXPECT_FALSE(result.has_value());
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,17 @@
|
|||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
constexpr bool close_to(const float actual, const float expected, const float epsilon)
|
||||||
|
{
|
||||||
|
const float diff = actual - expected;
|
||||||
|
return (diff < 0.0f ? -diff : diff) <= epsilon;
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
#endif
|
||||||
|
|
||||||
class UnitTestTriangle : public ::testing::Test
|
class UnitTestTriangle : public ::testing::Test
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
@@ -129,3 +140,20 @@ TEST_F(UnitTestTriangle, MidPoint)
|
|||||||
EXPECT_FLOAT_EQ(mid2.y, (2.0f + 5.0f + 8.0f) / 3.0f);
|
EXPECT_FLOAT_EQ(mid2.y, (2.0f + 5.0f + 8.0f) / 3.0f);
|
||||||
EXPECT_FLOAT_EQ(mid2.z, (3.0f + 6.0f + 9.0f) / 3.0f);
|
EXPECT_FLOAT_EQ(mid2.z, (3.0f + 6.0f + 9.0f) / 3.0f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr Triangle<Vector3<float>> triangle{{3.0f, 0.0f, 0.0f}, {0.0f, 0.0f, 0.0f}, {0.0f, 4.0f, 0.0f}};
|
||||||
|
constexpr auto normal = triangle.calculate_normal();
|
||||||
|
constexpr auto mid_point = triangle.mid_point();
|
||||||
|
|
||||||
|
return close_to(triangle.side_a_length(), 3.0f, 1e-5f) && close_to(triangle.side_b_length(), 4.0f, 1e-5f)
|
||||||
|
&& close_to(triangle.hypot(), 5.0f, 1e-5f) && triangle.is_rectangular()
|
||||||
|
&& close_to(normal.length(), 1.0f, 1e-5f) && close_to(normal.z, -1.0f, 1e-5f)
|
||||||
|
&& close_to(mid_point.x, 1.0f, 1e-5f) && close_to(mid_point.y, 4.0f / 3.0f, 1e-5f)
|
||||||
|
&& close_to(mid_point.z, 0.0f, 1e-5f);
|
||||||
|
}(),
|
||||||
|
"Triangle helpers should be constexpr with gcem");
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -2,10 +2,10 @@
|
|||||||
// Created by Vlad on 02.09.2024.
|
// Created by Vlad on 02.09.2024.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <omath/linear_algebra/vector2.hpp>
|
|
||||||
#include <cfloat> // For FLT_MAX and FLT_MIN
|
#include <cfloat> // For FLT_MAX and FLT_MIN
|
||||||
#include <cmath> // For std::isinf and std::isnan
|
#include <cmath> // For std::isinf and std::isnan
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
#include <omath/linear_algebra/vector2.hpp>
|
||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
@@ -399,7 +399,6 @@ TEST_F(UnitTestVector2, GreaterEqualOperator)
|
|||||||
EXPECT_TRUE(omath::Vector2(1.f, 1.f) >= omath::Vector2<float>{});
|
EXPECT_TRUE(omath::Vector2(1.f, 1.f) >= omath::Vector2<float>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ── Cast operator tests ──────────────────────────────────────────────────────
|
// ── Cast operator tests ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
TEST(Vector2Cast, FloatToDouble)
|
TEST(Vector2Cast, FloatToDouble)
|
||||||
@@ -463,3 +462,9 @@ static_assert(Vector2(1.0f, 2.0f).length_sqr() == 5.0f, "LengthSqr should be 5")
|
|||||||
static_assert(Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)) == 14.0f, "Dot product should be 14");
|
static_assert(Vector2(1.0f, 2.0f).dot(Vector2(4.0f, 5.0f)) == 14.0f, "Dot product should be 14");
|
||||||
static_assert(Vector2(4.0f, 5.0f).distance_to_sqr(Vector2(1.0f, 2.0f)) == 18.0f, "DistToSqr should be 18");
|
static_assert(Vector2(4.0f, 5.0f).distance_to_sqr(Vector2(1.0f, 2.0f)) == 18.0f, "DistToSqr should be 18");
|
||||||
static_assert(Vector2(-1.0f, -2.0f).abs() == Vector2(1.0f, 2.0f), "Abs should convert negative values to positive");
|
static_assert(Vector2(-1.0f, -2.0f).abs() == Vector2(1.0f, 2.0f), "Abs should convert negative values to positive");
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
static_assert(Vector2(3.0f, 4.0f).length() == 5.0f, "Length should be constexpr with gcem");
|
||||||
|
static_assert(Vector2(0.0f, 0.0f).distance_to(Vector2(3.0f, 4.0f)) == 5.0f, "Distance should be constexpr with gcem");
|
||||||
|
static_assert(Vector2(1.0f, 1.0f) < Vector2(3.0f, 4.0f), "Comparison should be constexpr with gcem");
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
// Created by Vlad on 01.09.2024.
|
// Created by Vlad on 01.09.2024.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <omath/linear_algebra/vector3.hpp>
|
|
||||||
#include <cfloat> // For FLT_MAX, FLT_MIN
|
#include <cfloat> // For FLT_MAX, FLT_MIN
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
#include <limits> // For std::numeric_limits
|
#include <limits> // For std::numeric_limits
|
||||||
|
#include <omath/linear_algebra/vector3.hpp>
|
||||||
|
|
||||||
using namespace omath;
|
using namespace omath;
|
||||||
|
|
||||||
@@ -31,35 +31,33 @@ TEST(Vector3More, ArithmeticAndDotCross)
|
|||||||
constexpr Vector3<float> a{1.f, 0.f, 0.f};
|
constexpr Vector3<float> a{1.f, 0.f, 0.f};
|
||||||
constexpr Vector3<float> b{0.f, 1.f, 0.f};
|
constexpr Vector3<float> b{0.f, 1.f, 0.f};
|
||||||
const auto c = a + b;
|
const auto c = a + b;
|
||||||
constexpr Vector3<float> expect_c{1.f,1.f,0.f};
|
constexpr Vector3<float> expect_c{1.f, 1.f, 0.f};
|
||||||
EXPECT_EQ(c, expect_c);
|
EXPECT_EQ(c, expect_c);
|
||||||
|
|
||||||
const auto d = a - b;
|
const auto d = a - b;
|
||||||
constexpr Vector3<float> expect_d{1.f,-1.f,0.f};
|
constexpr Vector3<float> expect_d{1.f, -1.f, 0.f};
|
||||||
EXPECT_EQ(d, expect_d);
|
EXPECT_EQ(d, expect_d);
|
||||||
|
|
||||||
const auto e = a * 2.f;
|
const auto e = a * 2.f;
|
||||||
constexpr Vector3<float> expect_e{2.f,0.f,0.f};
|
constexpr Vector3<float> expect_e{2.f, 0.f, 0.f};
|
||||||
EXPECT_EQ(e, expect_e);
|
EXPECT_EQ(e, expect_e);
|
||||||
|
|
||||||
EXPECT_FLOAT_EQ(a.dot(b), 0.f);
|
EXPECT_FLOAT_EQ(a.dot(b), 0.f);
|
||||||
// manual cross product check
|
// manual cross product check
|
||||||
const auto cr = Vector3<float>{ a.y * b.z - a.z * b.y,
|
const auto cr = Vector3<float>{a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x};
|
||||||
a.z * b.x - a.x * b.z,
|
constexpr Vector3<float> expect_cr{0.f, 0.f, 1.f};
|
||||||
a.x * b.y - a.y * b.x };
|
|
||||||
constexpr Vector3<float> expect_cr{0.f,0.f,1.f};
|
|
||||||
EXPECT_EQ(cr, expect_cr);
|
EXPECT_EQ(cr, expect_cr);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(Vector3More, NormalizationEdgeCases)
|
TEST(Vector3More, NormalizationEdgeCases)
|
||||||
{
|
{
|
||||||
constexpr Vector3<double> z{0.0,0.0,0.0};
|
constexpr Vector3<double> z{0.0, 0.0, 0.0};
|
||||||
const auto zn = z.normalized();
|
const auto zn = z.normalized();
|
||||||
EXPECT_DOUBLE_EQ(zn.x, 0.0);
|
EXPECT_DOUBLE_EQ(zn.x, 0.0);
|
||||||
EXPECT_DOUBLE_EQ(zn.y, 0.0);
|
EXPECT_DOUBLE_EQ(zn.y, 0.0);
|
||||||
EXPECT_DOUBLE_EQ(zn.z, 0.0);
|
EXPECT_DOUBLE_EQ(zn.z, 0.0);
|
||||||
|
|
||||||
constexpr Vector3<double> v{3.0,4.0,0.0};
|
constexpr Vector3<double> v{3.0, 4.0, 0.0};
|
||||||
const auto vn = v.normalized();
|
const auto vn = v.normalized();
|
||||||
EXPECT_NEAR(vn.x, 0.6, 1e-12);
|
EXPECT_NEAR(vn.x, 0.6, 1e-12);
|
||||||
EXPECT_NEAR(vn.y, 0.8, 1e-12);
|
EXPECT_NEAR(vn.y, 0.8, 1e-12);
|
||||||
@@ -481,16 +479,14 @@ TEST_F(UnitTestVector3, AsTuple)
|
|||||||
// Test AsTuple method
|
// Test AsTuple method
|
||||||
TEST_F(UnitTestVector3, AngleBeatween)
|
TEST_F(UnitTestVector3, AngleBeatween)
|
||||||
{
|
{
|
||||||
EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({1, 0, 0}).value().as_degrees(),
|
EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({1, 0, 0}).value().as_degrees(), 90.0f, 0.001f);
|
||||||
90.0f, 0.001f);
|
EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({0.0f, 0.0f, 1.0f}).value().as_degrees(), 0.0f, 0.001f);
|
||||||
EXPECT_NEAR(Vector3(0.0f, 0.0f, 1.0f).angle_between({0.0f, 0.0f, 1.0f}).value().as_degrees(),
|
|
||||||
0.0f, 0.001f);
|
|
||||||
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).angle_between({0.0f, 0.0f, 1.0f}).has_value());
|
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).angle_between({0.0f, 0.0f, 1.0f}).has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(UnitTestVector3, IsPerpendicular)
|
TEST_F(UnitTestVector3, IsPerpendicular)
|
||||||
{
|
{
|
||||||
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({1, 0 ,0}), true);
|
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({1, 0, 0}), true);
|
||||||
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({0.0f, 0.0f, 1.0f}), false);
|
EXPECT_EQ(Vector3(0.0f, 0.0f, 1.0f).is_perpendicular({0.0f, 0.0f, 1.0f}), false);
|
||||||
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).is_perpendicular({0.0f, 0.0f, 1.0f}));
|
EXPECT_FALSE(Vector3(0.0f, 0.0f, 0.0f).is_perpendicular({0.0f, 0.0f, 1.0f}));
|
||||||
}
|
}
|
||||||
@@ -585,4 +581,19 @@ TEST(Vector3Cast, SameTypeRoundtrip)
|
|||||||
static_assert(Vector3(1.0f, 2.0f, 3.0f).length_sqr() == 14.0f, "LengthSqr should be 14");
|
static_assert(Vector3(1.0f, 2.0f, 3.0f).length_sqr() == 14.0f, "LengthSqr should be 14");
|
||||||
static_assert(Vector3(1.0f, 2.0f, 3.0f).dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32");
|
static_assert(Vector3(1.0f, 2.0f, 3.0f).dot(Vector3(4.0f, 5.0f, 6.0f)) == 32.0f, "Dot product should be 32");
|
||||||
static_assert(Vector3(4.0f, 5.0f, 6.0f).distance_to_sqr(Vector3(1.0f, 2.0f, 3.0f)) == 27.0f, "DistToSqr should be 27");
|
static_assert(Vector3(4.0f, 5.0f, 6.0f).distance_to_sqr(Vector3(1.0f, 2.0f, 3.0f)) == 27.0f, "DistToSqr should be 27");
|
||||||
static_assert(Vector3(-1.0f, -2.0f, -3.0f).abs() == Vector3(1.0f, 2.0f, 3.0f), "Abs should convert negative values to positive");
|
static_assert(Vector3(-1.0f, -2.0f, -3.0f).abs() == Vector3(1.0f, 2.0f, 3.0f),
|
||||||
|
"Abs should convert negative values to positive");
|
||||||
|
|
||||||
|
#ifdef OMATH_USE_GCEM
|
||||||
|
static_assert(Vector3(1.0f, 2.0f, 2.0f).length() == 3.0f, "Length should be constexpr with gcem");
|
||||||
|
static_assert(Vector3(0.0f, 0.0f, 0.0f).distance_to(Vector3(1.0f, 2.0f, 2.0f)) == 3.0f,
|
||||||
|
"Distance should be constexpr with gcem");
|
||||||
|
static_assert(Vector3(1.0f, 1.0f, 1.0f) < Vector3(3.0f, 4.0f, 5.0f), "Comparison should be constexpr with gcem");
|
||||||
|
static_assert(
|
||||||
|
[]
|
||||||
|
{
|
||||||
|
constexpr auto angle = Vector3(1.0f, 0.0f, 0.0f).angle_between(Vector3(0.0f, 1.0f, 0.0f));
|
||||||
|
return angle.has_value() && angle->as_degrees() > 89.999f && angle->as_degrees() < 90.001f;
|
||||||
|
}(),
|
||||||
|
"Angle between should be constexpr with gcem");
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -82,6 +82,13 @@
|
|||||||
"lua",
|
"lua",
|
||||||
"sol2"
|
"sol2"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
"gcem":
|
||||||
|
{
|
||||||
|
"description": "Improve constexpr compatibility using gcem",
|
||||||
|
"dependencies": [
|
||||||
|
"gcem"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user