Implement 2D convex separation routine & change separation return type
This commit is contained in:
parent
a04aac5968
commit
b8ac384770
2 changed files with 146 additions and 99 deletions
|
|
@ -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;
|
||||
}
|
||||
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 <typename Body1, typename Body2>
|
||||
auto separation(Body1 const & b1, Body2 const & b2)
|
||||
{
|
||||
auto rese = process_edges(vs1, eds1, vs2, eds2);
|
||||
if (rese.second > res_d)
|
||||
static_assert(dimension(b1) == dimension(b2));
|
||||
static_assert(dimension(b1) == 2 || dimension(b1) == 3);
|
||||
if constexpr (dimension(b1) == 2)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue