diff --git a/benchmark/benchmark_projectile_pred.cpp b/benchmark/benchmark_projectile_pred.cpp new file mode 100644 index 0000000..5cbfb35 --- /dev/null +++ b/benchmark/benchmark_projectile_pred.cpp @@ -0,0 +1,3 @@ +// +// Created by Vlad on 9/18/2025. +// diff --git a/include/omath/projection/camera.hpp b/include/omath/projection/camera.hpp index 5cb6294..7d7feb1 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 6619751..a791751 100644 --- a/source/engines/iw_engine/traits/camera_trait.cpp +++ b/source/engines/iw_engine/traits/camera_trait.cpp @@ -9,7 +9,7 @@ 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 delta = look_at - cam_origin; return {PitchAngle::from_radians(-std::asin(delta.z / distance)), YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; diff --git a/source/engines/opengl_engine/traits/camera_trait.cpp b/source/engines/opengl_engine/traits/camera_trait.cpp index 98f886d..24970cb 100644 --- a/source/engines/opengl_engine/traits/camera_trait.cpp +++ b/source/engines/opengl_engine/traits/camera_trait.cpp @@ -10,10 +10,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 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)}; + return {PitchAngle::from_radians(std::asin(delta.y / distance)), + 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/source_engine/traits/camera_trait.cpp b/source/engines/source_engine/traits/camera_trait.cpp index 688ee3b..4f49a3e 100644 --- a/source/engines/source_engine/traits/camera_trait.cpp +++ b/source/engines/source_engine/traits/camera_trait.cpp @@ -9,7 +9,7 @@ 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 delta = look_at - cam_origin; return {PitchAngle::from_radians(-std::asin(delta.z / distance)), YawAngle::from_radians(std::atan2(delta.y, delta.x)), RollAngle::from_radians(0.f)}; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 061a516..041e8f7 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 199fa9b..c2843a6 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,112 @@ 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(-500.f, 500.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); + + + + for (int i = 0; i < 100; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.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); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.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); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.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); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } } \ 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 82dcc91..fe82551 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) { @@ -122,4 +122,112 @@ 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(-500.f, 500.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({dist(gen), dist(gen), dist(gen)}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + + + for (int i = 0; i < 100; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), dist(gen), dist(gen)}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({dist(gen), dist(gen), dist(gen)}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{dist(gen), 0.f, 0.f}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({dist(gen), dist(gen), dist(gen)}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, dist(gen), 0.f}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } +} + +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(-500.f, 500.f); + + constexpr auto fov = omath::projection::FieldOfView::from_degrees(90.f); + auto cam = omath::source_engine::Camera({dist(gen), dist(gen), dist(gen)}, {}, {1920.f, 1080.f}, fov, 0.001f, 10000.f); + + + + for (int i = 0; i < 1000; i++) + { + const auto position_to_look = omath::Vector3{0.f, 0.f, dist(gen)}; + 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; + + EXPECT_NEAR(projected_pos->x, 0.f, 0.00001f); + EXPECT_NEAR(projected_pos->y, 0.f, 0.00001f); + } } \ No newline at end of file