diff --git a/include/omath/algorithm/radar.hpp b/include/omath/algorithm/radar.hpp index 060aa97..b4f7897 100644 --- a/include/omath/algorithm/radar.hpp +++ b/include/omath/algorithm/radar.hpp @@ -13,11 +13,16 @@ namespace omath::algorithm Vector3 world_to_radar(const Camera& camera, const Vector3& position, const FloatingType scale) { const auto origin = static_cast>(camera.get_origin()); - const auto forward = static_cast>(camera.get_abs_forward()); - const auto right = static_cast>(camera.get_abs_right()); - const auto direction = position - origin; + auto current_angles = camera.get_view_angles(); + auto look_at_angles = camera.calc_look_at_angles(position); + auto yaw = look_at_angles.yaw - current_angles.yaw; - return {static_cast(direction.dot(right) * scale), static_cast(-direction.dot(forward) * scale), - 0.f}; + const auto right = origin + static_cast>(camera.get_abs_right()); + auto right_yaw = camera.calc_look_at_angles(right).yaw - current_angles.yaw; + const auto right_sign = right_yaw.sin() < FloatingType{0} ? FloatingType{-1} : FloatingType{1}; + const auto radar_distance = position.distance_to(origin) * scale; + + return {static_cast(yaw.sin() * right_sign * radar_distance), + static_cast(-yaw.cos() * radar_distance), 0.f}; } } // namespace omath::algorithm diff --git a/tests/general/unit_test_radar.cpp b/tests/general/unit_test_radar.cpp index 75ad7b8..ea22b42 100644 --- a/tests/general/unit_test_radar.cpp +++ b/tests/general/unit_test_radar.cpp @@ -12,7 +12,8 @@ using namespace omath; template -static void verify_world_to_radar_uses_camera_axes() +static void verify_world_to_radar_uses_engine_world_axes(const Vector3& world_forward, + const Vector3& world_right) { const Vector3 origin{NumericType{10}, NumericType{-20}, NumericType{5}}; const NumericType world_distance = NumericType{20}; @@ -23,14 +24,14 @@ static void verify_world_to_radar_uses_camera_axes() origin, {}, {1280.f, 720.f}, projection::FieldOfView::from_degrees(90.f), NumericType{0.01}, NumericType{1000}}; - const auto forward_position = origin + camera.get_abs_forward() * world_distance; + const auto forward_position = origin + world_forward * world_distance; const auto forward_radar = algorithm::world_to_radar(camera, forward_position, scale); EXPECT_NEAR(forward_radar.x, 0.f, 1e-4f); EXPECT_NEAR(forward_radar.y, -radar_distance, 1e-4f); EXPECT_NEAR(forward_radar.z, 0.f, 1e-6f); - const auto right_position = origin + camera.get_abs_right() * world_distance; + const auto right_position = origin + world_right * world_distance; const auto right_radar = algorithm::world_to_radar(camera, right_position, scale); EXPECT_NEAR(right_radar.x, radar_distance, 1e-4f); @@ -38,42 +39,131 @@ static void verify_world_to_radar_uses_camera_axes() EXPECT_NEAR(right_radar.z, 0.f, 1e-6f); } +template +static void verify_world_to_radar_uses_changed_camera_yaw(const float yaw_degrees, + const Vector3& world_forward, + const Vector3& world_right) +{ + const Vector3 origin{NumericType{10}, NumericType{-20}, NumericType{5}}; + const NumericType world_distance = NumericType{20}; + const NumericType scale = NumericType{0.5}; + const auto radar_distance = static_cast(world_distance * scale); + + CameraType camera{ + origin, {}, {1280.f, 720.f}, projection::FieldOfView::from_degrees(90.f), NumericType{0.01}, + NumericType{1000}}; + + auto angles = camera.get_view_angles(); + angles.yaw = decltype(angles.yaw)::from_degrees(yaw_degrees); + camera.set_view_angles(angles); + + const auto forward_position = origin + world_right * world_distance; + const auto forward_radar = algorithm::world_to_radar(camera, forward_position, scale); + + EXPECT_NEAR(forward_radar.x, 0.f, 1e-4f); + EXPECT_NEAR(forward_radar.y, -radar_distance, 1e-4f); + EXPECT_NEAR(forward_radar.z, 0.f, 1e-6f); + + const auto left_position = origin + world_forward * world_distance; + const auto left_radar = algorithm::world_to_radar(camera, left_position, scale); + + EXPECT_NEAR(left_radar.x, -radar_distance, 1e-4f); + EXPECT_NEAR(left_radar.y, 0.f, 1e-4f); + EXPECT_NEAR(left_radar.z, 0.f, 1e-6f); +} + +template +static void verify_world_to_radar_ignores_camera_pitch(const Vector3& world_forward) +{ + const Vector3 origin{NumericType{10}, NumericType{-20}, NumericType{5}}; + const NumericType world_distance = NumericType{20}; + const NumericType scale = NumericType{0.5}; + const auto radar_distance = static_cast(world_distance * scale); + + CameraType camera{ + origin, {}, {1280.f, 720.f}, projection::FieldOfView::from_degrees(90.f), NumericType{0.01}, + NumericType{1000}}; + + auto angles = camera.get_view_angles(); + angles.pitch = decltype(angles.pitch)::from_degrees(45.f); + camera.set_view_angles(angles); + + const auto forward_position = origin + world_forward * world_distance; + const auto forward_radar = algorithm::world_to_radar(camera, forward_position, scale); + + EXPECT_NEAR(forward_radar.x, 0.f, 1e-4f); + EXPECT_NEAR(forward_radar.y, -radar_distance, 1e-4f); + EXPECT_NEAR(forward_radar.z, 0.f, 1e-6f); +} + TEST(WorldToRadarTests, SourceEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(source_engine::k_abs_forward, + source_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(-90.f, source_engine::k_abs_forward, + source_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(source_engine::k_abs_forward); } TEST(WorldToRadarTests, IWEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(iw_engine::k_abs_forward, + iw_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(-90.f, iw_engine::k_abs_forward, + iw_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(iw_engine::k_abs_forward); } TEST(WorldToRadarTests, FrostbiteEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(frostbite_engine::k_abs_forward, + frostbite_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw( + 90.f, frostbite_engine::k_abs_forward, frostbite_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(frostbite_engine::k_abs_forward); } TEST(WorldToRadarTests, OpenGLEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(opengl_engine::k_abs_forward, + opengl_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(-90.f, opengl_engine::k_abs_forward, + opengl_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(opengl_engine::k_abs_forward); } TEST(WorldToRadarTests, UnityEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(unity_engine::k_abs_forward, + unity_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(90.f, unity_engine::k_abs_forward, + unity_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(unity_engine::k_abs_forward); } TEST(WorldToRadarTests, CryEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(cry_engine::k_abs_forward, + cry_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(-90.f, cry_engine::k_abs_forward, + cry_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(cry_engine::k_abs_forward); } TEST(WorldToRadarTests, RageEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(rage_engine::k_abs_forward, + rage_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(-90.f, rage_engine::k_abs_forward, + rage_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(rage_engine::k_abs_forward); } TEST(WorldToRadarTests, UnrealEngineCamera) { - verify_world_to_radar_uses_camera_axes(); + verify_world_to_radar_uses_engine_world_axes(unreal_engine::k_abs_forward, + unreal_engine::k_abs_right); + verify_world_to_radar_uses_changed_camera_yaw(90.f, unreal_engine::k_abs_forward, + unreal_engine::k_abs_right); + verify_world_to_radar_ignores_camera_pitch(unreal_engine::k_abs_forward); }