fixed unreal bug, improved interface

This commit is contained in:
2026-04-15 03:38:02 +03:00
parent dbe29926dc
commit 79f64d9679
9 changed files with 180 additions and 12 deletions

View File

@@ -9,5 +9,5 @@
namespace omath::cry_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, false, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::cry_engine

View File

@@ -9,5 +9,5 @@
namespace omath::frostbite_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, false, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::unity_engine

View File

@@ -9,5 +9,5 @@
namespace omath::iw_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, false, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::iw_engine

View File

@@ -8,5 +8,5 @@
namespace omath::opengl_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, true, NDCDepthRange::NEGATIVE_ONE_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::NEGATIVE_ONE_TO_ONE, {.inverted_forward = true}>;
} // namespace omath::opengl_engine

View File

@@ -7,5 +7,5 @@
#include "traits/camera_trait.hpp"
namespace omath::source_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, false, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE>;
} // namespace omath::source_engine

View File

@@ -9,5 +9,5 @@
namespace omath::unity_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, true, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE, {.inverted_forward = true}>;
} // namespace omath::unity_engine

View File

@@ -9,5 +9,5 @@
namespace omath::unreal_engine
{
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, false, NDCDepthRange::ZERO_TO_ONE>;
using Camera = projection::Camera<Mat4X4, ViewAngles, CameraTrait, NDCDepthRange::ZERO_TO_ONE, {.inverted_right = true}>;
} // namespace omath::unreal_engine

View File

@@ -42,6 +42,12 @@ namespace omath::projection
AUTO,
MANUAL,
};
struct CameraAxes
{
bool inverted_forward = false;
bool inverted_right = false;
};
template<class T, class MatType, class ViewAnglesType>
concept CameraEngineConcept =
requires(const Vector3<float>& cam_origin, const Vector3<float>& look_at, const ViewAnglesType& angles,
@@ -58,8 +64,9 @@ namespace omath::projection
requires noexcept(T::calc_projection_matrix(fov, viewport, znear, zfar, ndc_depth_range));
};
template<class Mat4X4Type, class ViewAnglesType, class TraitClass, bool inverted_z = false,
NDCDepthRange depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE>
template<class Mat4X4Type, class ViewAnglesType, class TraitClass,
NDCDepthRange depth_range = NDCDepthRange::NEGATIVE_ONE_TO_ONE,
CameraAxes axes = {}>
requires CameraEngineConcept<TraitClass, Mat4X4Type, ViewAnglesType>
class Camera final
{
@@ -87,7 +94,7 @@ namespace omath::projection
static ViewAnglesType calc_view_angles_from_view_matrix(const Mat4X4Type& view_matrix) noexcept
{
Vector3<float> forward_vector = {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
if constexpr (inverted_z)
if constexpr (axes.inverted_forward)
forward_vector = -forward_vector;
return TraitClass::calc_look_at_angle({}, forward_vector);
}
@@ -121,7 +128,7 @@ namespace omath::projection
{
const auto& view_matrix = get_view_matrix();
if constexpr (inverted_z)
if constexpr (axes.inverted_forward)
return -Vector3<float>{view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
return {view_matrix[2, 0], view_matrix[2, 1], view_matrix[2, 2]};
}
@@ -130,6 +137,8 @@ namespace omath::projection
Vector3<float> get_right() const noexcept
{
const auto& view_matrix = get_view_matrix();
if constexpr (axes.inverted_right)
return -Vector3<float>{view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]};
return {view_matrix[0, 0], view_matrix[0, 1], view_matrix[0, 2]};
}

View File

@@ -5,8 +5,12 @@
#include <complex>
#include <gtest/gtest.h>
#include <omath/3d_primitives/aabb.hpp>
#include <omath/engines/cry_engine/camera.hpp>
#include <omath/engines/frostbite_engine/camera.hpp>
#include <omath/engines/iw_engine/camera.hpp>
#include <omath/engines/opengl_engine/camera.hpp>
#include <omath/engines/source_engine/camera.hpp>
#include <omath/engines/unreal_engine/camera.hpp>
#include <omath/projection/camera.hpp>
#include <print>
#include <random>
@@ -781,4 +785,159 @@ TEST(UnitTestProjection, Unity_CalcOriginFromViewMatrix_WithRotation)
EXPECT_NEAR(origin.x, 300.f, k_eps);
EXPECT_NEAR(origin.y, -100.f, k_eps);
EXPECT_NEAR(origin.z, 75.f, k_eps);
}
}
// ---- Camera basis vectors at zero angles ----
TEST(UnitTestProjection, SourceEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::source_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::source_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::source_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::source_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::source_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::source_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::source_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::source_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::source_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::source_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, UnityEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::unity_engine::Camera({}, {}, {1280.f, 720.f},
omath::projection::FieldOfView::from_degrees(60.f), 0.03f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::unity_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::unity_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::unity_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::unity_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::unity_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::unity_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::unity_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::unity_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::unity_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, OpenGLEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::opengl_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::opengl_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::opengl_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::opengl_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::opengl_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::opengl_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::opengl_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::opengl_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::opengl_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::opengl_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, UnrealEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::unreal_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::unreal_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::unreal_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::unreal_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::unreal_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::unreal_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::unreal_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::unreal_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::unreal_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::unreal_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, FrostbiteEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::frostbite_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::frostbite_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::frostbite_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::frostbite_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::frostbite_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::frostbite_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::frostbite_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::frostbite_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::frostbite_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::frostbite_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, CryEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::cry_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::cry_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::cry_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::cry_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::cry_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::cry_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::cry_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::cry_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::cry_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::cry_engine::k_abs_up.z, k_eps);
}
TEST(UnitTestProjection, IWEngine_ZeroAngles_BasisVectors)
{
constexpr float k_eps = 1e-5f;
const auto cam = omath::iw_engine::Camera({}, {}, {1920.f, 1080.f},
omath::projection::FieldOfView::from_degrees(90.f), 0.01f, 1000.f);
const auto fwd = cam.get_forward();
const auto right = cam.get_right();
const auto up = cam.get_up();
EXPECT_NEAR(fwd.x, omath::iw_engine::k_abs_forward.x, k_eps);
EXPECT_NEAR(fwd.y, omath::iw_engine::k_abs_forward.y, k_eps);
EXPECT_NEAR(fwd.z, omath::iw_engine::k_abs_forward.z, k_eps);
EXPECT_NEAR(right.x, omath::iw_engine::k_abs_right.x, k_eps);
EXPECT_NEAR(right.y, omath::iw_engine::k_abs_right.y, k_eps);
EXPECT_NEAR(right.z, omath::iw_engine::k_abs_right.z, k_eps);
EXPECT_NEAR(up.x, omath::iw_engine::k_abs_up.x, k_eps);
EXPECT_NEAR(up.y, omath::iw_engine::k_abs_up.y, k_eps);
EXPECT_NEAR(up.z, omath::iw_engine::k_abs_up.z, k_eps);
}