mirror of
https://github.com/enduro2d/enduro2d.git
synced 2025-12-15 00:11:55 +07:00
math: project, unproject
This commit is contained in:
@@ -7,6 +7,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "_math.hpp"
|
||||
#include "rect.hpp"
|
||||
#include "trig.hpp"
|
||||
#include "trs2.hpp"
|
||||
#include "trs3.hpp"
|
||||
@@ -414,7 +415,8 @@ namespace e2d::math
|
||||
}
|
||||
|
||||
template < typename T, typename AngleTag >
|
||||
mat4<T> make_rotation_matrix4(
|
||||
std::enable_if_t<std::is_floating_point_v<T>, mat4<T>>
|
||||
make_rotation_matrix4(
|
||||
const unit<T, AngleTag>& angle,
|
||||
const vec3<T>& axis_xyz) noexcept
|
||||
{
|
||||
@@ -430,7 +432,8 @@ namespace e2d::math
|
||||
//
|
||||
|
||||
template < typename T, typename AngleTag >
|
||||
mat4<T> make_rotation_matrix4(
|
||||
std::enable_if_t<std::is_floating_point_v<T>, mat4<T>>
|
||||
make_rotation_matrix4(
|
||||
const unit<T, AngleTag>& roll,
|
||||
const unit<T, AngleTag>& pitch,
|
||||
const unit<T, AngleTag>& yaw) noexcept
|
||||
@@ -455,7 +458,8 @@ namespace e2d::math
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
mat4<T> make_rotation_matrix4(const vec3<T>& rpy) noexcept {
|
||||
std::enable_if_t<std::is_floating_point_v<T>, mat4<T>>
|
||||
make_rotation_matrix4(const vec3<T>& rpy) noexcept {
|
||||
return make_rotation_matrix4(
|
||||
make_rad(rpy.x),
|
||||
make_rad(rpy.y),
|
||||
@@ -467,14 +471,16 @@ namespace e2d::math
|
||||
//
|
||||
|
||||
template < typename T >
|
||||
mat4<T> make_trs_matrix4(const trs2<T>& trs) noexcept {
|
||||
std::enable_if_t<std::is_floating_point_v<T>, mat4<T>>
|
||||
make_trs_matrix4(const trs2<T>& trs) noexcept {
|
||||
return make_scale_matrix4(trs.scale)
|
||||
* make_rotation_matrix4(make_rad(trs.rotation), vec3<T>::unit_z())
|
||||
* make_translation_matrix4(trs.translation);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
mat4<T> make_trs_matrix4(const trs3<T>& trs) noexcept {
|
||||
std::enable_if_t<std::is_floating_point_v<T>, mat4<T>>
|
||||
make_trs_matrix4(const trs3<T>& trs) noexcept {
|
||||
return make_scale_matrix4(trs.scale)
|
||||
* make_rotation_matrix4(trs.rotation)
|
||||
* make_translation_matrix4(trs.translation);
|
||||
@@ -847,4 +853,160 @@ namespace e2d::math
|
||||
mm[2], mm[6], mm[10], mm[14],
|
||||
mm[3], mm[7], mm[11], mm[15]};
|
||||
}
|
||||
|
||||
//
|
||||
// project
|
||||
//
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
project_zo(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept
|
||||
{
|
||||
const vec4<T> t4 = vec4<T>(v, T(1)) * model * projection;
|
||||
|
||||
if ( math::is_near_zero(t4.w, T(0)) ) {
|
||||
return std::make_pair(vec3<T>::zero(), false);
|
||||
}
|
||||
|
||||
vec3<T> t3 = vec3<T>(t4) / t4.w;
|
||||
|
||||
t3.x = t3.x * T(0.5) + T(0.5);
|
||||
t3.y = t3.y * T(0.5) + T(0.5);
|
||||
|
||||
t3.x = t3.x * viewport.size.x + viewport.position.x;
|
||||
t3.y = t3.y * viewport.size.y + viewport.position.y;
|
||||
|
||||
return std::make_pair(t3, true);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
project_no(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept
|
||||
{
|
||||
const vec4<T> t4 = vec4<T>(v, T(1)) * model * projection;
|
||||
|
||||
if ( math::is_near_zero(t4.w, T(0)) ) {
|
||||
return std::make_pair(vec3<T>::zero(), false);
|
||||
}
|
||||
|
||||
vec3<T> t3 = vec3<T>(t4) / t4.w;
|
||||
|
||||
t3.x = t3.x * T(0.5) + T(0.5);
|
||||
t3.y = t3.y * T(0.5) + T(0.5);
|
||||
t3.z = t3.z * T(0.5) + T(0.5);
|
||||
|
||||
t3.x = t3.x * viewport.size.x + viewport.position.x;
|
||||
t3.y = t3.y * viewport.size.y + viewport.position.y;
|
||||
|
||||
return std::make_pair(t3, true);
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
project(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept {
|
||||
#if defined(E2D_CLIPPING_MODE) && E2D_CLIPPING_MODE == E2D_CLIPPING_MODE_ZO
|
||||
return impl::project_zo(v, model, projection, viewport);
|
||||
#elif defined(E2D_CLIPPING_MODE) && E2D_CLIPPING_MODE == E2D_CLIPPING_MODE_NO
|
||||
return impl::project_no(v, model, projection, viewport);
|
||||
#else
|
||||
# error "E2D_CLIPPING_MODE not detected"
|
||||
#endif
|
||||
}
|
||||
|
||||
//
|
||||
// unproject
|
||||
//
|
||||
|
||||
namespace impl
|
||||
{
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
unproject_zo(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept
|
||||
{
|
||||
const std::pair<mat4<T>, bool> inv_matrix = math::inversed(model * projection);
|
||||
|
||||
if ( !inv_matrix.second || math::is_near_zero(math::area(viewport), T(0)) ) {
|
||||
return std::make_pair(vec3<T>::zero(), false);
|
||||
}
|
||||
|
||||
vec4<T> t4 = vec4<T>(v, T(1));
|
||||
|
||||
t4.x = (t4.x - viewport.position.x) / viewport.size.x;
|
||||
t4.y = (t4.y - viewport.position.y) / viewport.size.y;
|
||||
|
||||
t4.x = t4.x * T(2) - T(1);
|
||||
t4.y = t4.y * T(2) - T(1);
|
||||
|
||||
t4 = t4 * inv_matrix.first;
|
||||
|
||||
return math::is_near_zero(t4.w, T(0))
|
||||
? std::make_pair(vec3<T>::zero(), false)
|
||||
: std::make_pair(vec3<T>(t4) / t4.w, true);
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
unproject_no(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept
|
||||
{
|
||||
const std::pair<mat4<T>, bool> inv_matrix = math::inversed(model * projection);
|
||||
|
||||
if ( !inv_matrix.second || math::is_near_zero(math::area(viewport), T(0)) ) {
|
||||
return std::make_pair(vec3<T>::zero(), false);
|
||||
}
|
||||
|
||||
vec4<T> t4 = vec4<T>(v, T(1));
|
||||
|
||||
t4.x = (t4.x - viewport.position.x) / viewport.size.x;
|
||||
t4.y = (t4.y - viewport.position.y) / viewport.size.y;
|
||||
|
||||
t4.x = t4.x * T(2) - T(1);
|
||||
t4.y = t4.y * T(2) - T(1);
|
||||
t4.z = t4.z * T(2) - T(1);
|
||||
|
||||
t4 = t4 * inv_matrix.first;
|
||||
|
||||
return math::is_near_zero(t4.w, T(0))
|
||||
? std::make_pair(vec3<T>::zero(), false)
|
||||
: std::make_pair(vec3<T>(t4) / t4.w, true);
|
||||
}
|
||||
}
|
||||
|
||||
template < typename T >
|
||||
std::enable_if_t<std::is_floating_point_v<T>, std::pair<vec3<T>, bool>>
|
||||
unproject(
|
||||
const vec3<T>& v,
|
||||
const mat4<T>& model,
|
||||
const mat4<T>& projection,
|
||||
const rect<T>& viewport) noexcept {
|
||||
#if defined(E2D_CLIPPING_MODE) && E2D_CLIPPING_MODE == E2D_CLIPPING_MODE_ZO
|
||||
return impl::unproject_zo(v, model, projection, viewport);
|
||||
#elif defined(E2D_CLIPPING_MODE) && E2D_CLIPPING_MODE == E2D_CLIPPING_MODE_NO
|
||||
return impl::unproject_no(v, model, projection, viewport);
|
||||
#else
|
||||
# error "E2D_CLIPPING_MODE not detected"
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -166,4 +166,158 @@ TEST_CASE("mat4") {
|
||||
REQUIRE(m4i::identity() == m4i::identity());
|
||||
REQUIRE_FALSE(m4i::identity() != m4i::identity());
|
||||
}
|
||||
{
|
||||
m4f projection = math::make_perspective_lh_matrix4(make_deg(80.f), 4.f / 3.f, 0.01f, 1.f);
|
||||
b2f viewport = make_rect(800.f, 600.f);
|
||||
|
||||
const v3f p1 = v3f(0.f,0.f,0.01f);
|
||||
const v3f p2 = v3f(+1.f,0.f,0.01f);
|
||||
const v3f p3 = v3f(-1.f,0.f,0.01f);
|
||||
|
||||
REQUIRE(math::project(p1, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::project(p2, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::project(p3, m4f::identity(), projection, viewport).second);
|
||||
|
||||
REQUIRE(math::impl::project_zo(p1, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::impl::project_zo(p2, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::impl::project_zo(p3, m4f::identity(), projection, viewport).second);
|
||||
|
||||
REQUIRE(math::impl::project_no(p1, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::impl::project_no(p2, m4f::identity(), projection, viewport).second);
|
||||
REQUIRE(math::impl::project_no(p3, m4f::identity(), projection, viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p1, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p2, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p3, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p1, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).first == v3f(p1));
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p2, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).first == v3f(p2));
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p3, m4f::identity(), projection, viewport).first,
|
||||
m4f::identity(),
|
||||
projection,
|
||||
viewport).first == v3f(p3));
|
||||
}
|
||||
{
|
||||
m4f projection = math::make_orthographic_lh_matrix4({400.f, 300.f}, 0.f, 1.f);
|
||||
m4f model = math::make_scale_matrix4(2.f, 2.f);
|
||||
b2f viewport = make_rect(800.f, 600.f);
|
||||
|
||||
const v3f p1 = v3f(0.f,0.f,0.f);
|
||||
const v3f p2 = v3f(+1.f,0.f,0.f);
|
||||
const v3f p3 = v3f(-1.f,0.f,0.f);
|
||||
|
||||
REQUIRE(math::project(p1, model, projection, viewport).second);
|
||||
REQUIRE(math::project(p2, model, projection, viewport).second);
|
||||
REQUIRE(math::project(p3, model, projection, viewport).second);
|
||||
|
||||
m4f projection_zo = math::impl::make_orthographic_lh_zo_matrix4(400.f, 300.f, 0.f, 1.f);
|
||||
REQUIRE(math::impl::project_zo(p1, model, projection_zo, viewport).second);
|
||||
REQUIRE(math::impl::project_zo(p2, model, projection_zo, viewport).second);
|
||||
REQUIRE(math::impl::project_zo(p3, model, projection_zo, viewport).second);
|
||||
|
||||
m4f projection_no = math::impl::make_orthographic_lh_no_matrix4(400.f, 300.f, 0.f, 1.f);
|
||||
REQUIRE(math::impl::project_no(p1, model, projection_no, viewport).second);
|
||||
REQUIRE(math::impl::project_no(p2, model, projection_no, viewport).second);
|
||||
REQUIRE(math::impl::project_no(p3, model, projection_no, viewport).second);
|
||||
|
||||
REQUIRE(math::approximately(
|
||||
math::project(p1, model, projection, viewport).first,
|
||||
v3f(400.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::project(p2, model, projection, viewport).first,
|
||||
v3f(404.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::project(p3, model, projection, viewport).first,
|
||||
v3f(396.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_zo(p1, model, projection_zo, viewport).first,
|
||||
v3f(400.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_zo(p2, model, projection_zo, viewport).first,
|
||||
v3f(404.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_zo(p3, model, projection_zo, viewport).first,
|
||||
v3f(396.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_no(p1, model, projection_no, viewport).first,
|
||||
v3f(400.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_no(p2, model, projection_no, viewport).first,
|
||||
v3f(404.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
REQUIRE(math::approximately(
|
||||
math::impl::project_no(p3, model, projection_no, viewport).first,
|
||||
v3f(396.f, 300.f, 0.f),
|
||||
0.01f));
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p1, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p2, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p3, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).second);
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p1, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).first == v3f(p1));
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p2, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).first == v3f(p2));
|
||||
|
||||
REQUIRE(math::unproject(
|
||||
math::project(p3, model, projection, viewport).first,
|
||||
model,
|
||||
projection,
|
||||
viewport).first == v3f(p3));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user