diff --git a/include/omath/algorithm/radar.hpp b/include/omath/algorithm/radar.hpp new file mode 100644 index 0000000..060aa97 --- /dev/null +++ b/include/omath/algorithm/radar.hpp @@ -0,0 +1,23 @@ +// +// Created by orange on 7/1/2026. +// +#pragma once +#include "omath/linear_algebra/vector3.hpp" +#include + +namespace omath::algorithm +{ + template + requires std::is_floating_point_v + [[nodiscard]] + 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; + + return {static_cast(direction.dot(right) * scale), static_cast(-direction.dot(forward) * scale), + 0.f}; + } +} // namespace omath::algorithm diff --git a/tests/general/unit_test_radar.cpp b/tests/general/unit_test_radar.cpp new file mode 100644 index 0000000..75ad7b8 --- /dev/null +++ b/tests/general/unit_test_radar.cpp @@ -0,0 +1,79 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace omath; + +template +static void verify_world_to_radar_uses_camera_axes() +{ + 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}}; + + const auto forward_position = origin + camera.get_abs_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_radar = algorithm::world_to_radar(camera, right_position, scale); + + EXPECT_NEAR(right_radar.x, radar_distance, 1e-4f); + EXPECT_NEAR(right_radar.y, 0.f, 1e-4f); + EXPECT_NEAR(right_radar.z, 0.f, 1e-6f); +} + +TEST(WorldToRadarTests, SourceEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, IWEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, FrostbiteEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, OpenGLEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, UnityEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, CryEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, RageEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +} + +TEST(WorldToRadarTests, UnrealEngineCamera) +{ + verify_world_to_radar_uses_camera_axes(); +}