mirror of
https://github.com/orange-cpp/omath.git
synced 2026-02-13 07:03:25 +00:00
Adds screen to world space conversion
Adds functionality to convert screen coordinates to world space, including handling for cases where the inverse view projection matrix is singular or when the world position is out of screen bounds. Also exposes Camera class to unit tests.
This commit is contained in:
@@ -100,6 +100,7 @@ target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_23)
|
||||
if (OMATH_BUILD_TESTS)
|
||||
add_subdirectory(extlibs)
|
||||
add_subdirectory(tests)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC OMATH_BUILD_TESTS)
|
||||
endif ()
|
||||
|
||||
if (OMATH_BUILD_EXAMPLES)
|
||||
|
||||
@@ -4,13 +4,15 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "omath/projection/error_codes.hpp"
|
||||
#include "omath/linear_algebra/mat.hpp"
|
||||
#include "omath/linear_algebra/vector3.hpp"
|
||||
#include "omath/projection/error_codes.hpp"
|
||||
#include <expected>
|
||||
#include <omath/angle.hpp>
|
||||
#include <type_traits>
|
||||
|
||||
#ifdef OMATH_BUILD_TESTS
|
||||
class UnitTestProjection_Projection_Test;
|
||||
#endif
|
||||
namespace omath::projection
|
||||
{
|
||||
class ViewPort final
|
||||
@@ -45,6 +47,8 @@ namespace omath::projection
|
||||
requires CameraEngineConcept<TraitClass, Mat4X4Type, ViewAnglesType>
|
||||
class Camera final
|
||||
{
|
||||
friend UnitTestProjection_Projection_Test;
|
||||
|
||||
public:
|
||||
~Camera() = default;
|
||||
Camera(const Vector3<float>& position, const ViewAnglesType& view_angles, const ViewPort& view_port,
|
||||
@@ -164,6 +168,31 @@ namespace omath::projection
|
||||
|
||||
return Vector3<float>{projected.at(0, 0), projected.at(1, 0), projected.at(2, 0)};
|
||||
}
|
||||
[[nodiscard]]
|
||||
std::expected<Vector3<float>, Error> view_port_to_screen(const Vector3<float>& ndc) const noexcept
|
||||
{
|
||||
const auto inv_view_proj = get_view_projection_matrix().inverted();
|
||||
|
||||
if (!inv_view_proj)
|
||||
return std::unexpected(Error::INV_VIEW_PROJ_MAT_DET_EQ_ZERO);
|
||||
|
||||
auto inverted_projection =
|
||||
inv_view_proj.value() * mat_column_from_vector<float, Mat4X4Type::get_store_ordering()>(ndc);
|
||||
|
||||
if (!inverted_projection.at(3, 0))
|
||||
return std::unexpected(Error::WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS);
|
||||
|
||||
inverted_projection /= inverted_projection.at(3, 0);
|
||||
|
||||
return Vector3<float>{inverted_projection.at(0, 0), inverted_projection.at(1, 0),
|
||||
inverted_projection.at(2, 0)};
|
||||
}
|
||||
|
||||
[[nodiscard]]
|
||||
std::expected<Vector3<float>, Error> screen_to_world(const Vector3<float>& screen_pos) const noexcept
|
||||
{
|
||||
return view_port_to_screen(screen_to_dnc(screen_pos));
|
||||
}
|
||||
|
||||
protected:
|
||||
ViewPort m_view_port{};
|
||||
@@ -200,5 +229,11 @@ namespace omath::projection
|
||||
*/
|
||||
return {(ndc.x + 1.f) / 2.f * m_view_port.m_width, (1.f - ndc.y) / 2.f * m_view_port.m_height, ndc.z};
|
||||
}
|
||||
|
||||
[[nodiscard]] Vector3<float> screen_to_dnc(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};
|
||||
}
|
||||
};
|
||||
} // namespace omath::projection
|
||||
|
||||
@@ -10,5 +10,6 @@ namespace omath::projection
|
||||
enum class Error : uint16_t
|
||||
{
|
||||
WORLD_POSITION_IS_OUT_OF_SCREEN_BOUNDS,
|
||||
INV_VIEW_PROJ_MAT_DET_EQ_ZERO,
|
||||
};
|
||||
}
|
||||
@@ -13,8 +13,9 @@ TEST(UnitTestProjection, Projection)
|
||||
const auto cam = omath::source_engine::Camera({0, 0, 0}, omath::source_engine::ViewAngles{}, {1920.f, 1080.f}, fov,
|
||||
0.01f, 1000.f);
|
||||
|
||||
const auto projected = cam.world_to_screen({1000, 0, 50});
|
||||
|
||||
const auto projected = cam.world_to_screen({1000, 0, 50.f});
|
||||
const auto result = cam.screen_to_world(projected.value());
|
||||
const auto result2 = cam.world_to_screen(result.value());
|
||||
EXPECT_NEAR(projected->x, 960.f, 0.001f);
|
||||
EXPECT_NEAR(projected->y, 504.f, 0.001f);
|
||||
EXPECT_NEAR(projected->z, 1.f, 0.001f);
|
||||
|
||||
Reference in New Issue
Block a user