mirror of
https://github.com/orange-cpp/omath.git
synced 2026-04-19 11:03:27 +00:00
Compare commits
9 Commits
cc4e01b100
...
v5.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| a9ff7868cf | |||
| be80a5d243 | |||
| 881d3b9a2a | |||
| f60e18b6ba | |||
| 0769d3d079 | |||
| b6755e21f9 | |||
| 2287602fa2 | |||
| 663890706e | |||
| ab103f626b |
@@ -35,13 +35,8 @@ namespace omath::algorithm
|
|||||||
const auto current_target_angles = camera.calc_look_at_angles(get_position(*current));
|
const auto current_target_angles = camera.calc_look_at_angles(get_position(*current));
|
||||||
const auto best_target_angles = camera.calc_look_at_angles(get_position(*best_target));
|
const auto best_target_angles = camera.calc_look_at_angles(get_position(*best_target));
|
||||||
|
|
||||||
const Vector2<float> current_angles_vec = {current_target_angles.pitch.as_degrees(),
|
const auto current_target_distance = camera_angles_vec.distance_to(current_target_angles.as_vector3());
|
||||||
current_target_angles.yaw.as_degrees()};
|
const auto best_target_distance = camera_angles.as_vector3().distance_to(best_target_angles.as_vector3());
|
||||||
const Vector2<float> best_angles_vec = {best_target_angles.pitch.as_degrees(),
|
|
||||||
best_target_angles.yaw.as_degrees()};
|
|
||||||
|
|
||||||
const auto current_target_distance = camera_angles_vec.distance_to(current_angles_vec);
|
|
||||||
const auto best_target_distance = camera_angles_vec.distance_to(best_angles_vec);
|
|
||||||
if (current_target_distance < best_target_distance)
|
if (current_target_distance < best_target_distance)
|
||||||
best_target = current;
|
best_target = current;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -116,19 +116,31 @@ namespace omath::rev_eng
|
|||||||
return call_method<ReturnType>(vtable[Id], arg_list...);
|
return call_method<ReturnType>(vtable[Id], arg_list...);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<std::size_t TableIndex, std::size_t Id, class ReturnType>
|
template<std::ptrdiff_t TableOffset, std::size_t Id, class ReturnType>
|
||||||
ReturnType call_virtual_method(auto... arg_list)
|
ReturnType call_virtual_method(auto... arg_list)
|
||||||
{
|
{
|
||||||
const auto vtable = *reinterpret_cast<void***>(
|
auto sub_this = reinterpret_cast<void*>(
|
||||||
reinterpret_cast<std::uintptr_t>(this) + TableIndex * sizeof(void*));
|
reinterpret_cast<std::uintptr_t>(this) + TableOffset);
|
||||||
return call_method<ReturnType>(vtable[Id], arg_list...);
|
const auto vtable = *reinterpret_cast<void***>(sub_this);
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
using Fn = ReturnType(__thiscall*)(void*, decltype(arg_list)...);
|
||||||
|
#else
|
||||||
|
using Fn = ReturnType(*)(void*, decltype(arg_list)...);
|
||||||
|
#endif
|
||||||
|
return reinterpret_cast<Fn>(vtable[Id])(sub_this, arg_list...);
|
||||||
}
|
}
|
||||||
template<std::size_t TableIndex, std::size_t Id, class ReturnType>
|
template<std::ptrdiff_t TableOffset, std::size_t Id, class ReturnType>
|
||||||
ReturnType call_virtual_method(auto... arg_list) const
|
ReturnType call_virtual_method(auto... arg_list) const
|
||||||
{
|
{
|
||||||
const auto vtable = *reinterpret_cast<void* const* const*>(
|
auto sub_this = reinterpret_cast<const void*>(
|
||||||
reinterpret_cast<std::uintptr_t>(this) + TableIndex * sizeof(void*));
|
reinterpret_cast<std::uintptr_t>(this) + TableOffset);
|
||||||
return call_method<ReturnType>(vtable[Id], arg_list...);
|
const auto vtable = *reinterpret_cast<void* const* const*>(sub_this);
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
using Fn = ReturnType(__thiscall*)(const void*, decltype(arg_list)...);
|
||||||
|
#else
|
||||||
|
using Fn = ReturnType(*)(const void*, decltype(arg_list)...);
|
||||||
|
#endif
|
||||||
|
return reinterpret_cast<Fn>(vtable[Id])(sub_this, arg_list...);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ namespace omath
|
|||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
using ArithmeticType = Type;
|
||||||
[[nodiscard]]
|
[[nodiscard]]
|
||||||
constexpr static Angle from_degrees(const Type& degrees) noexcept
|
constexpr static Angle from_degrees(const Type& degrees) noexcept
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,14 +2,25 @@
|
|||||||
// Created by Orange on 11/30/2024.
|
// Created by Orange on 11/30/2024.
|
||||||
//
|
//
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "omath/linear_algebra/vector3.hpp"
|
||||||
|
#include <type_traits>
|
||||||
namespace omath
|
namespace omath
|
||||||
{
|
{
|
||||||
template<class PitchType, class YawType, class RollType>
|
template<class PitchType, class YawType, class RollType>
|
||||||
|
requires std::is_same_v<typename PitchType::ArithmeticType, typename YawType::ArithmeticType>
|
||||||
|
&& std::is_same_v<typename YawType::ArithmeticType, typename RollType::ArithmeticType>
|
||||||
struct ViewAngles
|
struct ViewAngles
|
||||||
{
|
{
|
||||||
|
using ArithmeticType = PitchType::ArithmeticType;
|
||||||
|
|
||||||
PitchType pitch;
|
PitchType pitch;
|
||||||
YawType yaw;
|
YawType yaw;
|
||||||
RollType roll;
|
RollType roll;
|
||||||
|
|
||||||
|
[[nodiscard]]
|
||||||
|
Vector3<ArithmeticType> as_vector3() const
|
||||||
|
{
|
||||||
|
return {pitch.as_degrees(), yaw.as_degrees(), roll.as_degrees()};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
} // namespace omath
|
} // namespace omath
|
||||||
|
|||||||
@@ -237,4 +237,54 @@ TEST(unit_test_cry_engine, loook_at_random_z_axis)
|
|||||||
failed_points++;
|
failed_points++;
|
||||||
}
|
}
|
||||||
EXPECT_LE(failed_points, 100);
|
EXPECT_LE(failed_points, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::cry_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::cry_engine::ViewAngles angles{
|
||||||
|
omath::cry_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::cry_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::cry_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
// Pitch is clamped to [-90, 90]
|
||||||
|
const omath::cry_engine::ViewAngles angles{
|
||||||
|
omath::cry_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::cry_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::cry_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_cry_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
// Yaw is normalized to [-180, 180], 270 wraps to -90
|
||||||
|
const omath::cry_engine::ViewAngles angles{
|
||||||
|
omath::cry_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::cry_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::cry_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
}
|
}
|
||||||
@@ -405,3 +405,51 @@ TEST(unit_test_frostbite_engine, look_at_down)
|
|||||||
std::views::zip(dir_vector.as_array(), (-omath::frostbite_engine::k_abs_up).as_array()))
|
std::views::zip(dir_vector.as_array(), (-omath::frostbite_engine::k_abs_up).as_array()))
|
||||||
EXPECT_NEAR(result, etalon, 0.0001f);
|
EXPECT_NEAR(result, etalon, 0.0001f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_frostbite_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::frostbite_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_frostbite_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::frostbite_engine::ViewAngles angles{
|
||||||
|
omath::frostbite_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::frostbite_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::frostbite_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_frostbite_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
const omath::frostbite_engine::ViewAngles angles{
|
||||||
|
omath::frostbite_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::frostbite_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::frostbite_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_frostbite_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
const omath::frostbite_engine::ViewAngles angles{
|
||||||
|
omath::frostbite_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::frostbite_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::frostbite_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
|
}
|
||||||
|
|||||||
@@ -280,4 +280,54 @@ TEST(unit_test_iw_engine, look_at_down)
|
|||||||
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
|
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
|
||||||
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
|
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
|
||||||
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
|
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_iw_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::iw_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_iw_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::iw_engine::ViewAngles angles{
|
||||||
|
omath::iw_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::iw_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::iw_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_iw_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
// Pitch is clamped to [-89, 89]
|
||||||
|
const omath::iw_engine::ViewAngles angles{
|
||||||
|
omath::iw_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::iw_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::iw_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 89.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_iw_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
// Yaw is normalized to [-180, 180], 270 wraps to -90
|
||||||
|
const omath::iw_engine::ViewAngles angles{
|
||||||
|
omath::iw_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::iw_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::iw_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
}
|
}
|
||||||
@@ -394,4 +394,52 @@ TEST(unit_test_opengl_engine, look_at_down)
|
|||||||
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
|
const auto dir_vector = omath::opengl_engine::forward_vector(angles);
|
||||||
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::opengl_engine::k_abs_up).as_array()))
|
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::opengl_engine::k_abs_up).as_array()))
|
||||||
EXPECT_NEAR(result, etalon, 0.0001f);
|
EXPECT_NEAR(result, etalon, 0.0001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_opengl, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::opengl_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_opengl, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::opengl_engine::ViewAngles angles{
|
||||||
|
omath::opengl_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::opengl_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::opengl_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_opengl, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
const omath::opengl_engine::ViewAngles angles{
|
||||||
|
omath::opengl_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::opengl_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::opengl_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_opengl, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
const omath::opengl_engine::ViewAngles angles{
|
||||||
|
omath::opengl_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::opengl_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::opengl_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
}
|
}
|
||||||
@@ -422,4 +422,54 @@ TEST(unit_test_source_engine, look_at_down)
|
|||||||
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
|
EXPECT_NEAR(dir_vector.z, -0.99984f, 0.0001f);
|
||||||
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
|
EXPECT_NEAR(dir_vector.x,- 0.017f, 0.01f);
|
||||||
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
|
EXPECT_NEAR(dir_vector.y, 0.f, 0.001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_source_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::source_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_source_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::source_engine::ViewAngles angles{
|
||||||
|
omath::source_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::source_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::source_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_source_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
// Pitch is clamped to [-89, 89]
|
||||||
|
const omath::source_engine::ViewAngles angles{
|
||||||
|
omath::source_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::source_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::source_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 89.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_source_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
// Yaw is normalized to [-180, 180], 270 wraps to -90
|
||||||
|
const omath::source_engine::ViewAngles angles{
|
||||||
|
omath::source_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::source_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::source_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
}
|
}
|
||||||
@@ -417,3 +417,51 @@ TEST(unit_test_unity_engine, look_at_down)
|
|||||||
std::views::zip(dir_vector.as_array(), (-omath::unity_engine::k_abs_up).as_array()))
|
std::views::zip(dir_vector.as_array(), (-omath::unity_engine::k_abs_up).as_array()))
|
||||||
EXPECT_NEAR(result, etalon, 0.0001f);
|
EXPECT_NEAR(result, etalon, 0.0001f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unity_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::unity_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unity_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::unity_engine::ViewAngles angles{
|
||||||
|
omath::unity_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::unity_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::unity_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unity_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
const omath::unity_engine::ViewAngles angles{
|
||||||
|
omath::unity_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::unity_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::unity_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unity_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
const omath::unity_engine::ViewAngles angles{
|
||||||
|
omath::unity_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::unity_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::unity_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
|
}
|
||||||
|
|||||||
@@ -417,4 +417,52 @@ TEST(unit_test_unreal_engine, look_at_down)
|
|||||||
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
|
const auto dir_vector = omath::unreal_engine::forward_vector(angles);
|
||||||
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::unreal_engine::k_abs_up).as_array()))
|
for (const auto& [result, etalon] : std::views::zip(dir_vector.as_array(), (-omath::unreal_engine::k_abs_up).as_array()))
|
||||||
EXPECT_NEAR(result, etalon, 0.0001f);
|
EXPECT_NEAR(result, etalon, 0.0001f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unreal_engine, ViewAnglesAsVector3Zero)
|
||||||
|
{
|
||||||
|
const omath::unreal_engine::ViewAngles angles{};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, 0.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 0.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unreal_engine, ViewAnglesAsVector3Values)
|
||||||
|
{
|
||||||
|
const omath::unreal_engine::ViewAngles angles{
|
||||||
|
omath::unreal_engine::PitchAngle::from_degrees(45.f),
|
||||||
|
omath::unreal_engine::YawAngle::from_degrees(-90.f),
|
||||||
|
omath::unreal_engine::RollAngle::from_degrees(30.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 45.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.y, -90.f);
|
||||||
|
EXPECT_FLOAT_EQ(vec.z, 30.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unreal_engine, ViewAnglesAsVector3ClampedPitch)
|
||||||
|
{
|
||||||
|
const omath::unreal_engine::ViewAngles angles{
|
||||||
|
omath::unreal_engine::PitchAngle::from_degrees(120.f),
|
||||||
|
omath::unreal_engine::YawAngle::from_degrees(0.f),
|
||||||
|
omath::unreal_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_FLOAT_EQ(vec.x, 90.f);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_unreal_engine, ViewAnglesAsVector3NormalizedYaw)
|
||||||
|
{
|
||||||
|
const omath::unreal_engine::ViewAngles angles{
|
||||||
|
omath::unreal_engine::PitchAngle::from_degrees(0.f),
|
||||||
|
omath::unreal_engine::YawAngle::from_degrees(270.f),
|
||||||
|
omath::unreal_engine::RollAngle::from_degrees(0.f)
|
||||||
|
};
|
||||||
|
const auto vec = angles.as_vector3();
|
||||||
|
|
||||||
|
EXPECT_NEAR(vec.y, -90.f, 0.01f);
|
||||||
}
|
}
|
||||||
@@ -30,7 +30,7 @@ inline const void* get_vtable_entry(const void* obj, const std::size_t index)
|
|||||||
class BaseA
|
class BaseA
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~BaseA() = default;
|
int m_field_a{42};
|
||||||
[[nodiscard]] virtual int get_a() const { return 10; }
|
[[nodiscard]] virtual int get_a() const { return 10; }
|
||||||
[[nodiscard]] virtual int get_a2() const { return 11; }
|
[[nodiscard]] virtual int get_a2() const { return 11; }
|
||||||
};
|
};
|
||||||
@@ -38,7 +38,8 @@ public:
|
|||||||
class BaseB
|
class BaseB
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
virtual ~BaseB() = default;
|
float m_field_b{3.14f};
|
||||||
|
double m_field_b2{2.71};
|
||||||
[[nodiscard]] virtual int get_b() const { return 20; }
|
[[nodiscard]] virtual int get_b() const { return 20; }
|
||||||
[[nodiscard]] virtual int get_b2() const { return 21; }
|
[[nodiscard]] virtual int get_b2() const { return 21; }
|
||||||
};
|
};
|
||||||
@@ -46,26 +47,31 @@ public:
|
|||||||
class MultiPlayer final : public BaseA, public BaseB
|
class MultiPlayer final : public BaseA, public BaseB
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
int m_own_field{999};
|
||||||
[[nodiscard]] int get_a() const override { return 100; }
|
[[nodiscard]] int get_a() const override { return 100; }
|
||||||
[[nodiscard]] int get_a2() const override { return 101; }
|
[[nodiscard]] int get_a2() const override { return 101; }
|
||||||
[[nodiscard]] int get_b() const override { return 200; }
|
[[nodiscard]] int get_b() const override { return 200; }
|
||||||
[[nodiscard]] int get_b2() const override { return 201; }
|
[[nodiscard]] int get_b2() const override { return 201; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// BaseA layout: [vptr_a][m_field_a(int)] — sizeof(BaseA) gives the full subobject size
|
||||||
|
// BaseB starts right after BaseA in MultiPlayer's layout
|
||||||
|
constexpr std::ptrdiff_t BASE_B_OFFSET = static_cast<std::ptrdiff_t>(sizeof(BaseA));
|
||||||
|
|
||||||
class RevMultiPlayer final : omath::rev_eng::InternalReverseEngineeredObject
|
class RevMultiPlayer final : omath::rev_eng::InternalReverseEngineeredObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// Table 0 (BaseA vtable): index 0 = destructor, 1 = get_a, 2 = get_a2
|
// Table at offset 0 (BaseA vtable): index 0 = get_a, 1 = get_a2
|
||||||
[[nodiscard]] int rev_get_a() const { return call_virtual_method<0, 1, int>(); }
|
[[nodiscard]] int rev_get_a() const { return call_virtual_method<0, 0, int>(); }
|
||||||
[[nodiscard]] int rev_get_a2() const { return call_virtual_method<0, 2, int>(); }
|
[[nodiscard]] int rev_get_a2() const { return call_virtual_method<0, 1, int>(); }
|
||||||
|
|
||||||
// Table 1 (BaseB vtable): index 0 = destructor, 1 = get_b, 2 = get_b2
|
// Table at BaseB offset (BaseB vtable): index 0 = get_b, 1 = get_b2
|
||||||
[[nodiscard]] int rev_get_b() const { return call_virtual_method<1, 1, int>(); }
|
[[nodiscard]] int rev_get_b() const { return call_virtual_method<BASE_B_OFFSET, 0, int>(); }
|
||||||
[[nodiscard]] int rev_get_b2() const { return call_virtual_method<1, 2, int>(); }
|
[[nodiscard]] int rev_get_b2() const { return call_virtual_method<BASE_B_OFFSET, 1, int>(); }
|
||||||
|
|
||||||
// Non-const versions
|
// Non-const versions
|
||||||
int rev_get_a_mut() { return call_virtual_method<0, 1, int>(); }
|
int rev_get_a_mut() { return call_virtual_method<0, 0, int>(); }
|
||||||
int rev_get_b_mut() { return call_virtual_method<1, 1, int>(); }
|
int rev_get_b_mut() { return call_virtual_method<BASE_B_OFFSET, 0, int>(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject
|
class RevPlayer final : omath::rev_eng::InternalReverseEngineeredObject
|
||||||
@@ -160,6 +166,15 @@ TEST(unit_test_reverse_enineering, call_virtual_method_delegates_to_call_method)
|
|||||||
EXPECT_EQ(2, rev->rev_bar_const());
|
EXPECT_EQ(2, rev->rev_bar_const());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(unit_test_reverse_enineering, multi_player_base_b_offset_is_correct)
|
||||||
|
{
|
||||||
|
// Verify our compile-time offset matches the actual layout
|
||||||
|
MultiPlayer mp;
|
||||||
|
const auto* mp_addr = reinterpret_cast<const char*>(&mp);
|
||||||
|
const auto* b_addr = reinterpret_cast<const char*>(static_cast<const BaseB*>(&mp));
|
||||||
|
EXPECT_EQ(b_addr - mp_addr, BASE_B_OFFSET);
|
||||||
|
}
|
||||||
|
|
||||||
TEST(unit_test_reverse_enineering, call_virtual_method_table_index_first_table)
|
TEST(unit_test_reverse_enineering, call_virtual_method_table_index_first_table)
|
||||||
{
|
{
|
||||||
MultiPlayer mp;
|
MultiPlayer mp;
|
||||||
@@ -173,7 +188,7 @@ TEST(unit_test_reverse_enineering, call_virtual_method_table_index_first_table)
|
|||||||
|
|
||||||
TEST(unit_test_reverse_enineering, call_virtual_method_table_index_second_table)
|
TEST(unit_test_reverse_enineering, call_virtual_method_table_index_second_table)
|
||||||
{
|
{
|
||||||
MultiPlayer mp;
|
constexpr MultiPlayer mp;
|
||||||
const auto* rev = reinterpret_cast<const RevMultiPlayer*>(&mp);
|
const auto* rev = reinterpret_cast<const RevMultiPlayer*>(&mp);
|
||||||
|
|
||||||
EXPECT_EQ(mp.get_b(), rev->rev_get_b());
|
EXPECT_EQ(mp.get_b(), rev->rev_get_b());
|
||||||
@@ -194,7 +209,7 @@ TEST(unit_test_reverse_enineering, call_virtual_method_table_index_non_const)
|
|||||||
TEST(unit_test_reverse_enineering, call_virtual_method_table_zero_matches_default)
|
TEST(unit_test_reverse_enineering, call_virtual_method_table_zero_matches_default)
|
||||||
{
|
{
|
||||||
// Table 0 with the TableIndex overload should match the original non-TableIndex overload
|
// Table 0 with the TableIndex overload should match the original non-TableIndex overload
|
||||||
MultiPlayer mp;
|
constexpr MultiPlayer mp;
|
||||||
const auto* rev = reinterpret_cast<const RevMultiPlayer*>(&mp);
|
const auto* rev = reinterpret_cast<const RevMultiPlayer*>(&mp);
|
||||||
|
|
||||||
// Both access table 0, method index 1 — should return the same value
|
// Both access table 0, method index 1 — should return the same value
|
||||||
|
|||||||
Reference in New Issue
Block a user