Implement 2D convex separation routine & change separation return type

This commit is contained in:
Nikita Lisitsa 2021-05-07 17:37:45 +03:00
parent a04aac5968
commit b8ac384770
2 changed files with 146 additions and 99 deletions

View file

@ -8,10 +8,75 @@
namespace psemek::cg
{
// returns pair(normalized vector from 1 to 2, signed distance)
// distance <= 0 means intersection
template <typename T, std::size_t N>
struct separation_info
{
T distance = -std::numeric_limits<T>::infinity();
geom::vector<T, N> direction = geom::vector<T, N>::zero();
separation_info & operator |= (separation_info const & i);
};
template <typename T, std::size_t N>
separation_info<T, N> operator | (separation_info<T, N> const & i1, separation_info<T, N> const & i2)
{
if (i1.distance > i2.distance)
return i1;
return i2;
}
template <typename T, std::size_t N>
separation_info<T, N> & separation_info<T, N>::operator |= (separation_info<T, N> const & i)
{
return *this = *this | i;
}
namespace detail
{
template <typename Body1, typename Body2>
auto separation(Body1 const & b1, Body2 const & b2)
auto separation_2d(Body1 const & b1, Body2 const & b2)
{
auto const & vs1 = vertices(b1);
auto const & vs2 = vertices(b2);
auto const & es1 = edges(b1);
auto const & es2 = edges(b2);
using scalar_type = std::remove_cvref_t<decltype(vs1[0][0])>;
using result_type = separation_info<scalar_type, 3>;
auto process_edges = [](auto const & vs1, auto const & es1, auto const & vs2)
{
result_type result;
for (auto const & e : es1)
{
result_type edge_result;
edge_result.direction = -geom::ort(geom::normalized(vs1[e[1]] - vs1[e[0]]));
edge_result.distance = std::numeric_limits<scalar_type>::infinity();
for (auto const & v : vs2)
{
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);
}
result |= edge_result;
}
return result;
};
result_type result;
result |= process_edges(vs1, es1, vs2);
result |= process_edges(vs2, es2, vs1);
return result;
}
template <typename Body1, typename Body2>
auto separation_3d(Body1 const & b1, Body2 const & b2)
{
auto const & vs1 = vertices(b1);
auto const & vs2 = vertices(b2);
@ -22,120 +87,102 @@ namespace psemek::cg
auto const & eds1 = edge_directions(b1);
auto const & eds2 = edge_directions(b2);
using vector_type = std::remove_cvref_t<decltype(vs1[0] - vs1[0])>;
using scalar_type = std::remove_cvref_t<decltype(vs1[0][0])>;
vector_type res_n = vector_type::zero();
auto res_d = -std::numeric_limits<scalar_type>::infinity();
using result_type = separation_info<scalar_type, 3>;
auto process_faces = [](auto const & vs1, auto const & fs1, auto const & vs2)
{
vector_type res_n = vector_type::zero();
scalar_type res_d = -std::numeric_limits<scalar_type>::infinity();
result_type result;
for (auto const & f : fs1)
{
auto const face_n = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]);
scalar_type face_d = std::numeric_limits<scalar_type>::infinity();
result_type face_result;
face_result.direction = geom::normal(vs1[f[0]], vs1[f[1]], vs1[f[2]]);
face_result.distance = std::numeric_limits<scalar_type>::infinity();
auto const face_p = vs1[f[0]];
for (auto const & v : vs2)
{
auto const d = geom::dot(face_n, v - face_p);
face_d = std::min(d, face_d);
auto const d = geom::dot(face_result.direction, v - face_p);
face_result.distance = std::min(d, face_result.distance);
}
if (face_d > res_d)
{
res_d = face_d;
res_n = face_n;
}
result |= face_result;
}
return std::make_pair(res_n, res_d);
return result;
};
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<scalar_type>::infinity();
result_type result;
for (auto const & ed1 : eds1)
{
for (auto const & ed2 : eds2)
{
auto edge_n = geom::cross(ed1, ed2);
auto l = geom::length(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_n /= l;
edge_result.direction /= l;
geom::interval<scalar_type> i1, i2;
for (auto const & v : vs1)
{
i1 |= geom::dot(geom::homogeneous(v), geom::homogeneous(edge_n));
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_n));
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;
scalar_type edge_d;
if (edge_d12 > edge_d21)
{
edge_d = edge_d12;
edge_result.distance = edge_d12;
}
else
{
edge_d = edge_d21;
edge_n = -edge_n;
edge_result.distance = edge_d21;
edge_result.direction = -edge_result.direction;
}
if (edge_d > res_d)
{
res_d = edge_d;
res_n = edge_n;
}
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;
}
}
{
auto res21 = process_faces(vs2, fs2, vs1);
if (res21.second > res_d)
{
res_d = res21.second;
res_n = -res21.first;
}
}
{
auto rese = process_edges(vs1, eds1, vs2, eds2);
if (rese.second > res_d)
{
res_d = rese.second;
res_n = rese.first;
}
}
return std::make_pair(res_n, res_d);
result_type result;
result |= process_faces(vs1, fs1, vs2);
result |= process_faces(vs2, fs2, vs1);
result |= process_edges(vs1, eds1, vs2, eds2);
return result;
}
}
template <typename Body1, typename Body2>
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)
{
return detail::separation_2d(b1, b2);
}
else if (dimension(b1) == 3)
{
return detail::separation_3d(b1, b2);
}
}
}

View file

@ -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)
{