diff --git a/benchmark/benchmark_projectile_pred.cpp b/benchmark/benchmark_projectile_pred.cpp new file mode 100644 index 00000000..5cbfb350 --- /dev/null +++ b/benchmark/benchmark_projectile_pred.cpp @@ -0,0 +1,3 @@ +// +// Created by Vlad on 9/18/2025. +// diff --git a/include/omath/linear_algebra/mat.hpp b/include/omath/linear_algebra/mat.hpp index 99c60816..08e8f89a 100644 --- a/include/omath/linear_algebra/mat.hpp +++ b/include/omath/linear_algebra/mat.hpp @@ -15,6 +15,8 @@ #include #endif +#undef near +#undef far namespace omath { struct MatSize @@ -675,6 +677,23 @@ namespace omath { 0.f, 0.f, 0.f, 1.f } }; } + template + Mat<4, 4, T, St> mat_look_at_left_handed(const Vector3& eye, const Vector3& center, const Vector3& up) + { + const Vector3 f = (center - eye).normalized(); + const Vector3 s = f.cross(up).normalized(); + const Vector3 u = s.cross(f); + return mat_camera_view(f, s, u, eye); + } + + template + Mat<4, 4, T, St>mat_look_at_right_handed(const Vector3& eye, const Vector3& center, const Vector3& up) + { + const Vector3 f = (center - eye).normalized(); + const Vector3 s = f.cross(up).normalized(); + const Vector3 u = s.cross(f); + return mat_camera_view(-f, s, u, eye); + } } // namespace omath diff --git a/include/omath/projection/camera.hpp b/include/omath/projection/camera.hpp index 5cb6294c..7d7feb14 100644 --- a/include/omath/projection/camera.hpp +++ b/include/omath/projection/camera.hpp @@ -62,12 +62,13 @@ namespace omath::projection { } - protected: void look_at(const Vector3& target) { m_view_angles = TraitClass::calc_look_at_angle(m_origin, target); + m_view_projection_matrix = std::nullopt; } + protected: [[nodiscard]] Mat4X4Type calc_view_projection_matrix() const noexcept { return TraitClass::calc_projection_matrix(m_field_of_view, m_view_port, m_near_plane_distance, diff --git a/source/engines/iw_engine/traits/camera_trait.cpp b/source/engines/iw_engine/traits/camera_trait.cpp index 6619751d..427a3c58 100644 --- a/source/engines/iw_engine/traits/camera_trait.cpp +++ b/source/engines/iw_engine/traits/camera_trait.cpp @@ -8,11 +8,10 @@ namespace omath::iw_engine ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; + const auto direction = (look_at - cam_origin).normalized(); - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; + return {PitchAngle::from_radians(-std::asin(direction.z)), + YawAngle::from_radians(std::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; } Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { diff --git a/source/engines/opengl_engine/formulas.cpp b/source/engines/opengl_engine/formulas.cpp index 4a14ab2f..da8ab494 100644 --- a/source/engines/opengl_engine/formulas.cpp +++ b/source/engines/opengl_engine/formulas.cpp @@ -28,14 +28,13 @@ namespace omath::opengl_engine } Mat4X4 calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { - return mat_camera_view(-forward_vector(angles), right_vector(angles), - up_vector(angles), cam_origin); + return mat_look_at_right_handed(cam_origin, cam_origin+forward_vector(angles), up_vector(angles)); } Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept { - return mat_rotation_axis_x(-angles.pitch) - * mat_rotation_axis_y(-angles.yaw) - * mat_rotation_axis_z(angles.roll); + return mat_rotation_axis_z(angles.roll) + * mat_rotation_axis_y(angles.yaw) + * mat_rotation_axis_x(angles.pitch); } Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept diff --git a/source/engines/opengl_engine/traits/camera_trait.cpp b/source/engines/opengl_engine/traits/camera_trait.cpp index 98f886df..d034bef1 100644 --- a/source/engines/opengl_engine/traits/camera_trait.cpp +++ b/source/engines/opengl_engine/traits/camera_trait.cpp @@ -9,11 +9,10 @@ namespace omath::opengl_engine ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; + const auto direction = (look_at - cam_origin).normalized(); - return {PitchAngle::from_radians(-std::asin(delta.y / distance)), - YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; + return {PitchAngle::from_radians(std::asin(direction.y)), + YawAngle::from_radians(-std::atan2(direction.x, -direction.z)), RollAngle::from_radians(0.f)}; } Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { diff --git a/source/engines/source_engine/traits/camera_trait.cpp b/source/engines/source_engine/traits/camera_trait.cpp index 688ee3b2..071326e2 100644 --- a/source/engines/source_engine/traits/camera_trait.cpp +++ b/source/engines/source_engine/traits/camera_trait.cpp @@ -8,11 +8,11 @@ namespace omath::source_engine ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; + const auto direction = (look_at - cam_origin).normalized(); - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; + + return {PitchAngle::from_radians(-std::asin(direction.z)), + YawAngle::from_radians(std::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; } Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { diff --git a/source/engines/unity_engine/formulas.cpp b/source/engines/unity_engine/formulas.cpp index 4560e277..cb603be6 100644 --- a/source/engines/unity_engine/formulas.cpp +++ b/source/engines/unity_engine/formulas.cpp @@ -30,9 +30,9 @@ namespace omath::unity_engine } Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept { - return mat_rotation_axis_x(angles.pitch) + return mat_rotation_axis_z(angles.roll) * mat_rotation_axis_y(angles.yaw) - * mat_rotation_axis_z(angles.roll); + * mat_rotation_axis_x(angles.pitch); } Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept diff --git a/source/engines/unity_engine/traits/camera_trait.cpp b/source/engines/unity_engine/traits/camera_trait.cpp index d76426b7..e309cd7c 100644 --- a/source/engines/unity_engine/traits/camera_trait.cpp +++ b/source/engines/unity_engine/traits/camera_trait.cpp @@ -9,10 +9,10 @@ namespace omath::unity_engine ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept { const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; + const auto delta = look_at - cam_origin; return {PitchAngle::from_radians(-std::asin(delta.y / distance)), - YawAngle::from_radians(std::atan2(delta.z, delta.x)), RollAngle::from_radians(0.f)}; + YawAngle::from_radians(std::atan2(delta.x, delta.z)), RollAngle::from_radians(0.f)}; } Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { diff --git a/source/engines/unreal_engine/formulas.cpp b/source/engines/unreal_engine/formulas.cpp index 6f02ae85..e4a0ad9d 100644 --- a/source/engines/unreal_engine/formulas.cpp +++ b/source/engines/unreal_engine/formulas.cpp @@ -31,8 +31,8 @@ namespace omath::unreal_engine Mat4X4 rotation_matrix(const ViewAngles& angles) noexcept { return mat_rotation_axis_x(angles.roll) - * mat_rotation_axis_y(angles.pitch) - * mat_rotation_axis_z(angles.yaw); + * mat_rotation_axis_z(angles.yaw) + * mat_rotation_axis_y(angles.pitch); } Mat4X4 calc_perspective_projection_matrix(const float field_of_view, const float aspect_ratio, const float near, const float far) noexcept diff --git a/source/engines/unreal_engine/traits/camera_trait.cpp b/source/engines/unreal_engine/traits/camera_trait.cpp index 5eb81262..9eedce3c 100644 --- a/source/engines/unreal_engine/traits/camera_trait.cpp +++ b/source/engines/unreal_engine/traits/camera_trait.cpp @@ -8,11 +8,10 @@ namespace omath::unreal_engine ViewAngles CameraTrait::calc_look_at_angle(const Vector3& cam_origin, const Vector3& look_at) noexcept { - const auto distance = cam_origin.distance_to(look_at); - const auto delta = cam_origin - look_at; + const auto direction = (look_at - cam_origin).normalized(); - return {PitchAngle::from_radians(-std::asin(delta.z / distance)), - YawAngle::from_radians(std::atan2(delta.x, delta.y)), RollAngle::from_radians(0.f)}; + return {PitchAngle::from_radians(-std::asin(direction.z)), + YawAngle::from_radians(std::atan2(direction.y, direction.x)), RollAngle::from_radians(0.f)}; } Mat4X4 CameraTrait::calc_view_matrix(const ViewAngles& angles, const Vector3& cam_origin) noexcept { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 061a516a..041e8f72 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,8 +11,6 @@ set_target_properties(unit_tests PROPERTIES ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/out/${CMAKE_BUILD_TYPE}" - UNITY_BUILD ON - UNITY_BUILD_BATCH_SIZE 20 CXX_STANDARD 23 CXX_STANDARD_REQUIRED ON) diff --git a/tests/engines/unit_test_iw_engine.cpp b/tests/engines/unit_test_iw_engine.cpp index 199fa9b2..ed274578 100644 --- a/tests/engines/unit_test_iw_engine.cpp +++ b/tests/engines/unit_test_iw_engine.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include TEST(unit_test_iw_engine, ForwardVector) { @@ -68,7 +68,6 @@ TEST(unit_test_iw_engine, ProjectTargetMovedFromCamera) constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); const auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - for (float distance = 0.02f; distance < 1000.f; distance += 0.01f) { const auto projected = cam.world_to_screen({distance, 0, 0}); @@ -102,4 +101,126 @@ TEST(unit_test_iw_engine, CameraSetAndGetOrigin) cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); +} + +TEST(unit_test_iw_engine, loook_at_random_all_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.001f || std::abs(projected_pos->y - 0.f) >= 0.001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_iw_engine, loook_at_random_x_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::iw_engine::Camera({dist(gen), dist(gen), dist(gen)}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_iw_engine, loook_at_random_y_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_iw_engine, loook_at_random_z_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::iw_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.025f) + failed_points++; + } + EXPECT_LE(failed_points, 100); } \ No newline at end of file diff --git a/tests/engines/unit_test_open_gl.cpp b/tests/engines/unit_test_open_gl.cpp index 89506e61..48b808bf 100644 --- a/tests/engines/unit_test_open_gl.cpp +++ b/tests/engines/unit_test_open_gl.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include TEST(unit_test_opengl, ForwardVector) { @@ -29,7 +29,7 @@ TEST(unit_test_opengl, ForwardVectorRotationYaw) { omath::opengl_engine::ViewAngles angles; - angles.yaw = omath::opengl_engine::YawAngle::from_degrees(90.f); + angles.yaw = omath::opengl_engine::YawAngle::from_degrees(-90.f); const auto forward = omath::opengl_engine::forward_vector(angles); EXPECT_NEAR(forward.x, omath::opengl_engine::k_abs_right.x, 0.00001f); @@ -37,13 +37,11 @@ TEST(unit_test_opengl, ForwardVectorRotationYaw) EXPECT_NEAR(forward.z, omath::opengl_engine::k_abs_right.z, 0.00001f); } - - TEST(unit_test_opengl, ForwardVectorRotationPitch) { omath::opengl_engine::ViewAngles angles; - angles.pitch = omath::opengl_engine::PitchAngle::from_degrees(-90.f); + angles.pitch = omath::opengl_engine::PitchAngle::from_degrees(90.f); const auto forward = omath::opengl_engine::forward_vector(angles); EXPECT_NEAR(forward.x, omath::opengl_engine::k_abs_up.x, 0.00001f); @@ -68,7 +66,6 @@ TEST(unit_test_opengl, ProjectTargetMovedFromCamera) constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); const auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - for (float distance = -10.f; distance > -1000.f; distance -= 0.01f) { const auto projected = cam.world_to_screen({0, 0, distance}); @@ -102,4 +99,123 @@ TEST(unit_test_opengl, CameraSetAndGetOrigin) cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); +} +TEST(unit_test_opengl_engine, loook_at_random_all_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_opengl_engine, loook_at_random_x_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_opengl_engine, loook_at_random_y_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_opengl_engine, loook_at_random_z_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::opengl_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); } \ No newline at end of file diff --git a/tests/engines/unit_test_source_engine.cpp b/tests/engines/unit_test_source_engine.cpp index 82dcc918..ac480e46 100644 --- a/tests/engines/unit_test_source_engine.cpp +++ b/tests/engines/unit_test_source_engine.cpp @@ -5,7 +5,7 @@ #include #include #include - +#include TEST(unit_test_source_engine, ForwardVector) { @@ -68,7 +68,6 @@ TEST(unit_test_source_engine, ProjectTargetMovedFromCamera) constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); const auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.01f, 1000.f); - for (float distance = 0.02f; distance < 1000.f; distance += 0.01f) { const auto projected = cam.world_to_screen({distance, 0, 0}); @@ -122,4 +121,125 @@ TEST(unit_test_source_engine, CameraSetAndGetOrigin) cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); +} + +TEST(unit_test_source_engine, loook_at_random_all_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_source_engine, loook_at_random_x_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_source_engine, loook_at_random_y_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_source_engine, loook_at_random_z_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.025f) + failed_points++; + } + EXPECT_LE(failed_points, 100); } \ No newline at end of file diff --git a/tests/engines/unit_test_unity_engine.cpp b/tests/engines/unit_test_unity_engine.cpp index 47ce136f..f5e9abdb 100644 --- a/tests/engines/unit_test_unity_engine.cpp +++ b/tests/engines/unit_test_unity_engine.cpp @@ -6,6 +6,7 @@ #include #include #include +#include TEST(unit_test_unity_engine, ForwardVector) { @@ -68,7 +69,6 @@ TEST(unit_test_unity_engine, ProjectTargetMovedFromCamera) constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f); const auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f); - for (float distance = 0.02f; distance < 100.f; distance += 0.01f) { const auto projected = cam.world_to_screen({0, 0, distance}); @@ -112,4 +112,125 @@ TEST(unit_test_unity_engine, CameraSetAndGetOrigin) cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); +} +TEST(unit_test_unity_engine, loook_at_random_all_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unity_engine, loook_at_random_x_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.001f || std::abs(projected_pos->y - 0.f) >= 0.001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unity_engine, loook_at_random_y_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unity_engine, loook_at_random_z_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unity_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); } \ No newline at end of file diff --git a/tests/engines/unit_test_unreal_engine.cpp b/tests/engines/unit_test_unreal_engine.cpp index 09912096..9fe0a60f 100644 --- a/tests/engines/unit_test_unreal_engine.cpp +++ b/tests/engines/unit_test_unreal_engine.cpp @@ -6,6 +6,7 @@ #include #include #include +#include TEST(unit_test_unreal_engine, ForwardVector) { @@ -68,7 +69,6 @@ TEST(unit_test_unreal_engine, ProjectTargetMovedFromCamera) constexpr auto fov = omath::projection::FieldOfView::from_degrees(60.f); const auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1280.f, 720.f}, fov, 0.01f, 1000.f); - for (float distance = 0.02f; distance < 100.f; distance += 0.01f) { const auto projected = cam.world_to_screen({distance, 0, 0}); @@ -102,4 +102,128 @@ TEST(unit_test_unreal_engine, CameraSetAndGetOrigin) cam.set_field_of_view(omath::projection::FieldOfView::from_degrees(50.f)); EXPECT_EQ(cam.get_field_of_view().as_degrees(), 50.f); +} + +TEST(unit_test_unreal_engine, loook_at_random_all_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + + std::size_t failed_points = 0; + for (int i = 0; i < 100; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.0001f || std::abs(projected_pos->y - 0.f) >= 0.0001f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unreal_engine, loook_at_random_x_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unreal_engine, loook_at_random_y_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); +} + +TEST(unit_test_unreal_engine, loook_at_random_z_axis) +{ + std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source + std::uniform_real_distribution dist(-1000.f, 1000.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::unreal_engine::Camera({0, 0, 0}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + std::size_t failed_points = 0; + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + + if (cam.get_origin().distance_to(position_to_look) < 10) + continue; + + cam.look_at(position_to_look); + + auto projected_pos = cam.world_to_view_port(position_to_look); + + EXPECT_TRUE(projected_pos.has_value()); + + if (!projected_pos) + continue; + + if (std::abs(projected_pos->x - 0.f) >= 0.01f || std::abs(projected_pos->y - 0.f) >= 0.01f) + failed_points++; + } + EXPECT_LE(failed_points, 100); } \ No newline at end of file