math: project, unproject

This commit is contained in:
2020-01-30 03:53:52 +07:00
parent 1962519f6e
commit 3237d7a0e4
2 changed files with 321 additions and 5 deletions

View File

@@ -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
}
}

View File

@@ -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));
}
}