mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-13 07:03:25 +00:00
Merge pull request #100 from orange-cpp/feature/improved_scree_to_world
Improves screen to world conversion accuracy
This commit is contained in:
@@ -7,13 +7,17 @@
|
||||
#include "omath/linear_algebra/mat.hpp"
|
||||
#include "omath/linear_algebra/vector3.hpp"
|
||||
#include "omath/projection/error_codes.hpp"
|
||||
#include <omath/trigonometry/angle.hpp>
|
||||
#include <expected>
|
||||
#include <omath/trigonometry/angle.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef OMATH_BUILD_TESTS
|
||||
// ReSharper disable once CppInconsistentNaming
|
||||
// ReSharper disable CppInconsistentNaming
|
||||
class UnitTestProjection_Projection_Test;
|
||||
class UnitTestProjection_ScreenToNdcTopLeft_Test;
|
||||
class UnitTestProjection_ScreenToNdcBottomLeft_Test;
|
||||
// ReSharper restore CppInconsistentNaming
|
||||
|
||||
#endif
|
||||
|
||||
namespace omath::projection
|
||||
@@ -52,6 +56,8 @@ namespace omath::projection
|
||||
{
|
||||
#ifdef OMATH_BUILD_TESTS
|
||||
friend UnitTestProjection_Projection_Test;
|
||||
friend UnitTestProjection_ScreenToNdcTopLeft_Test;
|
||||
friend UnitTestProjection_ScreenToNdcBottomLeft_Test;
|
||||
#endif
|
||||
public:
|
||||
enum class ScreenStart
|
||||
@@ -152,7 +158,6 @@ namespace omath::projection
|
||||
return m_origin;
|
||||
}
|
||||
|
||||
|
||||
template<ScreenStart screen_start = ScreenStart::TOP_LEFT_CORNER>
|
||||
[[nodiscard]] std::expected<Vector3<float>, Error>
|
||||
world_to_screen(const Vector3<float>& world_position) const noexcept
|
||||
@@ -206,17 +211,19 @@ namespace omath::projection
|
||||
inverted_projection.at(2, 0)};
|
||||
}
|
||||
|
||||
template<ScreenStart screen_start = ScreenStart::TOP_LEFT_CORNER>
|
||||
[[nodiscard]]
|
||||
std::expected<Vector3<float>, Error> screen_to_world(const Vector3<float>& screen_pos) const noexcept
|
||||
{
|
||||
return view_port_to_screen(screen_to_ndc(screen_pos));
|
||||
return view_port_to_screen(screen_to_ndc<screen_start>(screen_pos));
|
||||
}
|
||||
|
||||
template<ScreenStart screen_start = ScreenStart::TOP_LEFT_CORNER>
|
||||
[[nodiscard]]
|
||||
std::expected<Vector3<float>, Error> screen_to_world(const Vector2<float>& screen_pos) const noexcept
|
||||
{
|
||||
const auto& [x, y] = screen_pos;
|
||||
return screen_to_world({x, y, 1.f});
|
||||
return screen_to_world<screen_start>({x, y, 1.f});
|
||||
}
|
||||
|
||||
protected:
|
||||
@@ -286,10 +293,17 @@ namespace omath::projection
|
||||
return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (ndc.y / 2.f + 0.5f) * m_view_port.m_height, ndc.z};
|
||||
}
|
||||
|
||||
template<ScreenStart screen_start = ScreenStart::TOP_LEFT_CORNER>
|
||||
[[nodiscard]] Vector3<float> screen_to_ndc(const Vector3<float>& screen_pos) const noexcept
|
||||
{
|
||||
return {screen_pos.x / m_view_port.m_width * 2.f - 1.f, 1.f - screen_pos.y / m_view_port.m_height * 2.f,
|
||||
screen_pos.z};
|
||||
if constexpr (screen_start == ScreenStart::TOP_LEFT_CORNER)
|
||||
return {screen_pos.x / m_view_port.m_width * 2.f - 1.f, 1.f - screen_pos.y / m_view_port.m_height * 2.f,
|
||||
screen_pos.z};
|
||||
else if (screen_start == ScreenStart::BOTTOM_LEFT_CORNER)
|
||||
return {screen_pos.x / m_view_port.m_width * 2.f - 1.f,
|
||||
(screen_pos.y / m_view_port.m_height - 0.5f) * 2.f, screen_pos.z};
|
||||
else
|
||||
std::unreachable();
|
||||
}
|
||||
};
|
||||
} // namespace omath::projection
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
//
|
||||
// Created by Vlad on 27.08.2024.
|
||||
//
|
||||
#include "omath/engines/unity_engine/camera.hpp"
|
||||
#include <complex>
|
||||
#include <gtest/gtest.h>
|
||||
#include <omath/engines/source_engine/camera.hpp>
|
||||
#include <omath/projection/camera.hpp>
|
||||
#include <print>
|
||||
#include <random>
|
||||
|
||||
TEST(UnitTestProjection, Projection)
|
||||
{
|
||||
@@ -22,4 +24,76 @@ TEST(UnitTestProjection, Projection)
|
||||
EXPECT_NEAR(projected->x, 960.f, 0.001f);
|
||||
EXPECT_NEAR(projected->y, 504.f, 0.001f);
|
||||
EXPECT_NEAR(projected->z, 1.f, 0.001f);
|
||||
}
|
||||
TEST(UnitTestProjection, ScreenToNdcTopLeft)
|
||||
{
|
||||
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
|
||||
const auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov,
|
||||
0.01f, 1000.f);
|
||||
using ScreenStart = omath::source_engine::Camera::ScreenStart;
|
||||
|
||||
const auto ndc_top_left = cam.screen_to_ndc<ScreenStart::TOP_LEFT_CORNER>({1500, 300, 1.f});
|
||||
EXPECT_NEAR(ndc_top_left.x, 0.5625f, 0.0001f);
|
||||
EXPECT_NEAR(ndc_top_left.y, 0.4444f, 0.0001f);
|
||||
}
|
||||
|
||||
TEST(UnitTestProjection, ScreenToNdcBottomLeft)
|
||||
{
|
||||
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.03f, 1000.f);
|
||||
using ScreenStart = omath::unity_engine::Camera::ScreenStart;
|
||||
|
||||
const auto ndc_bottom_left =
|
||||
cam.screen_to_ndc<ScreenStart::BOTTOM_LEFT_CORNER>({1263.53833f, 547.061523f, 0.99405992f});
|
||||
EXPECT_NEAR(ndc_bottom_left.x, 0.974278628f, 0.0001f);
|
||||
EXPECT_NEAR(ndc_bottom_left.y, 0.519615293f, 0.0001f);
|
||||
}
|
||||
|
||||
TEST(UnitTestProjection, ScreenToWorldTopLeftCorner)
|
||||
{
|
||||
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
|
||||
|
||||
std::uniform_real_distribution dist_x(1.f, 1900.f);
|
||||
std::uniform_real_distribution dist_y(1.f, 1070.f);
|
||||
|
||||
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
|
||||
const auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov,
|
||||
0.01f, 1000.f);
|
||||
using ScreenStart = omath::source_engine::Camera::ScreenStart;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
const auto initial_screen_cords = omath::Vector2{dist_x(gen), dist_y(gen)};
|
||||
|
||||
const auto world_cords = cam.screen_to_world<ScreenStart::TOP_LEFT_CORNER>(initial_screen_cords);
|
||||
const auto screen_cords = cam.world_to_screen<ScreenStart::TOP_LEFT_CORNER>(world_cords.value());
|
||||
|
||||
EXPECT_NEAR(screen_cords->x, initial_screen_cords.x, 0.001f);
|
||||
EXPECT_NEAR(screen_cords->y, initial_screen_cords.y, 0.001f);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(UnitTestProjection, ScreenToWorldBottomLeftCorner)
|
||||
{
|
||||
std::mt19937 gen(std::random_device{}()); // Seed with a non-deterministic source
|
||||
|
||||
std::uniform_real_distribution dist_x(1.f, 1900.f);
|
||||
std::uniform_real_distribution dist_y(1.f, 1070.f);
|
||||
|
||||
constexpr auto fov = omath::Angle<float, 0.f, 180.f, omath::AngleFlags::Clamped>::from_degrees(90.f);
|
||||
const auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov,
|
||||
0.01f, 1000.f);
|
||||
using ScreenStart = omath::source_engine::Camera::ScreenStart;
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
const auto initial_screen_cords = omath::Vector2{dist_x(gen), dist_y(gen)};
|
||||
|
||||
const auto world_cords = cam.screen_to_world<ScreenStart::BOTTOM_LEFT_CORNER>(initial_screen_cords);
|
||||
const auto screen_cords = cam.world_to_screen<ScreenStart::BOTTOM_LEFT_CORNER>(world_cords.value());
|
||||
|
||||
EXPECT_NEAR(screen_cords->x, initial_screen_cords.x, 0.001f);
|
||||
EXPECT_NEAR(screen_cords->y, initial_screen_cords.y, 0.001f);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user