From b8ac384770a93d4408309d6962f7ada8705aabe1 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 7 May 2021 17:37:45 +0300 Subject: [PATCH] Implement 2D convex separation routine & change separation return type --- .../include/psemek/cg/convex/separation.hpp | 241 +++++++++++------- libs/gfx/source/renderer/deferred.cpp | 4 +- 2 files changed, 146 insertions(+), 99 deletions(-) diff --git a/libs/cg/include/psemek/cg/convex/separation.hpp b/libs/cg/include/psemek/cg/convex/separation.hpp index 20530013..3a0a717b 100644 --- a/libs/cg/include/psemek/cg/convex/separation.hpp +++ b/libs/cg/include/psemek/cg/convex/separation.hpp @@ -8,134 +8,181 @@ namespace psemek::cg { - // returns pair(normalized vector from 1 to 2, signed distance) - // distance <= 0 means intersection - template - auto separation(Body1 const & b1, Body2 const & b2) + template + struct separation_info { - auto const & vs1 = vertices(b1); - auto const & vs2 = vertices(b2); + T distance = -std::numeric_limits::infinity(); + geom::vector direction = geom::vector::zero(); - auto const & fs1 = faces(b1); - auto const & fs2 = faces(b2); + separation_info & operator |= (separation_info const & i); + }; - auto const & eds1 = edge_directions(b1); - auto const & eds2 = edge_directions(b2); + template + separation_info operator | (separation_info const & i1, separation_info const & i2) + { + if (i1.distance > i2.distance) + return i1; + return i2; + } - using vector_type = std::remove_cvref_t; - using scalar_type = std::remove_cvref_t; + template + separation_info & separation_info::operator |= (separation_info const & i) + { + return *this = *this | i; + } - vector_type res_n = vector_type::zero(); - auto res_d = -std::numeric_limits::infinity(); + namespace detail + { - auto process_faces = [](auto const & vs1, auto const & fs1, auto const & vs2) + template + auto separation_2d(Body1 const & b1, Body2 const & b2) { - vector_type res_n = vector_type::zero(); - scalar_type res_d = -std::numeric_limits::infinity(); + auto const & vs1 = vertices(b1); + auto const & vs2 = vertices(b2); - for (auto const & f : fs1) + auto const & es1 = edges(b1); + auto const & es2 = edges(b2); + + using scalar_type = std::remove_cvref_t; + + using result_type = separation_info; + + auto process_edges = [](auto const & vs1, auto const & es1, auto const & vs2) { - auto const face_n = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]); - scalar_type face_d = std::numeric_limits::infinity(); + result_type result; - auto const face_p = vs1[f[0]]; - - for (auto const & v : vs2) + for (auto const & e : es1) { - auto const d = geom::dot(face_n, v - face_p); - face_d = std::min(d, face_d); - } - - if (face_d > res_d) - { - res_d = face_d; - res_n = face_n; - } - } - - return std::make_pair(res_n, res_d); - }; - - auto process_edges = [](auto const & vs1, auto const & eds1, auto const & vs2, auto const & eds2) - { - vector_type res_n = vector_type::zero(); - scalar_type res_d = -std::numeric_limits::infinity(); - - for (auto const & ed1 : eds1) - { - for (auto const & ed2 : eds2) - { - auto edge_n = geom::cross(ed1, ed2); - auto l = geom::length(edge_n); - if (l == 0) continue; - edge_n /= l; - - geom::interval i1, i2; - - for (auto const & v : vs1) - { - i1 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); - } + result_type edge_result; + edge_result.direction = -geom::ort(geom::normalized(vs1[e[1]] - vs1[e[0]])); + edge_result.distance = std::numeric_limits::infinity(); for (auto const & v : vs2) { - i2 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n)); + auto const d = geom::dot(geom::homogeneous(edge_result.direction), geom::homogeneous(v - vs1[e[0]])); + edge_result.distance = std::min(edge_result.distance, d); } - auto edge_d12 = i2.min - i1.max; - auto edge_d21 = i1.min - i2.max; + result |= edge_result; + } - scalar_type edge_d; + return result; + }; - if (edge_d12 > edge_d21) + result_type result; + result |= process_edges(vs1, es1, vs2); + result |= process_edges(vs2, es2, vs1); + return result; + } + + template + auto separation_3d(Body1 const & b1, Body2 const & b2) + { + auto const & vs1 = vertices(b1); + auto const & vs2 = vertices(b2); + + auto const & fs1 = faces(b1); + auto const & fs2 = faces(b2); + + auto const & eds1 = edge_directions(b1); + auto const & eds2 = edge_directions(b2); + + using scalar_type = std::remove_cvref_t; + + using result_type = separation_info; + + auto process_faces = [](auto const & vs1, auto const & fs1, auto const & vs2) + { + result_type result; + + for (auto const & f : fs1) + { + result_type face_result; + face_result.direction = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]); + face_result.distance = std::numeric_limits::infinity(); + + auto const face_p = vs1[f[0]]; + + for (auto const & v : vs2) { - edge_d = edge_d12; - } - else - { - edge_d = edge_d21; - edge_n = -edge_n; + auto const d = geom::dot(face_result.direction, v - face_p); + face_result.distance = std::min(d, face_result.distance); } - if (edge_d > res_d) + result |= face_result; + } + + return result; + }; + + auto process_edges = [](auto const & vs1, auto const & eds1, auto const & vs2, auto const & eds2) + { + result_type result; + + for (auto const & ed1 : eds1) + { + for (auto const & ed2 : eds2) { - res_d = edge_d; - res_n = edge_n; + result_type edge_result; + edge_result.direction = geom::cross(ed1, ed2); + auto l = geom::length(edge_result.direction); + if (l == 0) continue; + edge_result.direction /= l; + + geom::interval i1, i2; + + for (auto const & v : vs1) + { + i1 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_result.direction)); + } + + for (auto const & v : vs2) + { + i2 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_result.direction)); + } + + auto edge_d12 = i2.min - i1.max; + auto edge_d21 = i1.min - i2.max; + + if (edge_d12 > edge_d21) + { + edge_result.distance = edge_d12; + } + else + { + edge_result.distance = edge_d21; + edge_result.direction = -edge_result.direction; + } + + result |= edge_result; } } - } - return std::make_pair(res_n, res_d); - }; + return result; + }; - { - auto res12 = process_faces(vs1, fs1, vs2); - if (res12.second > res_d) - { - res_d = res12.second; - res_n = res12.first; - } + result_type result; + result |= process_faces(vs1, fs1, vs2); + result |= process_faces(vs2, fs2, vs1); + result |= process_edges(vs1, eds1, vs2, eds2); + return result; } - { - auto res21 = process_faces(vs2, fs2, vs1); - if (res21.second > res_d) - { - res_d = res21.second; - res_n = -res21.first; - } - } + } + template + auto separation(Body1 const & b1, Body2 const & b2) + { + static_assert(dimension(b1) == dimension(b2)); + static_assert(dimension(b1) == 2 || dimension(b1) == 3); + if constexpr (dimension(b1) == 2) { - auto rese = process_edges(vs1, eds1, vs2, eds2); - if (rese.second > res_d) - { - res_d = rese.second; - res_n = rese.first; - } + return detail::separation_2d(b1, b2); + } + else if (dimension(b1) == 3) + { + return detail::separation_3d(b1, b2); } - - return std::make_pair(res_n, res_d); } } diff --git a/libs/gfx/source/renderer/deferred.cpp b/libs/gfx/source/renderer/deferred.cpp index 700c21ff..dbb0daca 100644 --- a/libs/gfx/source/renderer/deferred.cpp +++ b/libs/gfx/source/renderer/deferred.cpp @@ -1101,7 +1101,7 @@ void main(){} { b.bbox = geom::expand(b.bbox, b.bbox.dimensions() / 64.f); - b.camera_separation = cg::separation(camera_frustum, cg::box{b.bbox}).second; + b.camera_separation = cg::separation(camera_frustum, cg::box{b.bbox}).distance; b.contains_near_clip = true; for (int i = 0; i < 4; ++i) @@ -1696,7 +1696,7 @@ void main(){} { auto const & b = bins.data()[bi]; - if (cg::separation(light_frustum, cg::box{b.bbox}).second > 0.f) continue; + if (cg::separation(light_frustum, cg::box{b.bbox}).distance > 0.f) continue; for (auto const & p : b.buckets) {