MeshUtils.cpp: MeshRaycaster is now aware of the clipping plane
This commit is contained in:
parent
a68a72a660
commit
c1e3be9b27
4 changed files with 46 additions and 50 deletions
|
@ -252,7 +252,7 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
const sla::SupportPoint& support_point = m_editing_mode ? m_editing_cache[i].support_point : m_normal_cache[i];
|
||||
const bool& point_selected = m_editing_mode ? m_editing_cache[i].selected : false;
|
||||
|
||||
if (is_point_clipped(support_point.pos.cast<double>()))
|
||||
if (is_mesh_point_clipped(support_point.pos.cast<double>()))
|
||||
continue;
|
||||
|
||||
// First decide about the color of the point.
|
||||
|
@ -335,14 +335,14 @@ void GLGizmoSlaSupports::render_points(const Selection& selection, bool picking)
|
|||
|
||||
|
||||
|
||||
bool GLGizmoSlaSupports::is_point_clipped(const Vec3d& point) const
|
||||
bool GLGizmoSlaSupports::is_mesh_point_clipped(const Vec3d& point) const
|
||||
{
|
||||
if (m_clipping_plane_distance == 0.f)
|
||||
return false;
|
||||
|
||||
Vec3d transformed_point = m_model_object->instances.front()->get_transformation().get_matrix() * point;
|
||||
transformed_point(2) += m_z_shift;
|
||||
return m_clipping_plane->distance(transformed_point) < 0.;
|
||||
return m_clipping_plane->is_point_clipped(transformed_point);
|
||||
}
|
||||
|
||||
|
||||
|
@ -391,27 +391,15 @@ bool GLGizmoSlaSupports::unproject_on_mesh(const Vec2d& mouse_pos, std::pair<Vec
|
|||
trafo.set_offset(trafo.get_offset() + Vec3d(0., 0., m_z_shift));
|
||||
|
||||
// The raycaster query
|
||||
std::vector<Vec3f> hits;
|
||||
std::vector<Vec3f> normals;
|
||||
m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, &hits, &normals);
|
||||
|
||||
// We must also take care of the clipping plane (if active)
|
||||
unsigned i = 0;
|
||||
if (m_clipping_plane_distance != 0.f) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! is_point_clipped(hits[i].cast<double>()))
|
||||
break;
|
||||
Vec3f hit;
|
||||
Vec3f normal;
|
||||
if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get())) {
|
||||
// Return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hit, normal);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
// Calculate and return both the point and the facet normal.
|
||||
pos_and_normal = std::make_pair(hits[i], normals[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Following function is called from GLCanvas3D to inform the gizmo about a mouse/keyboard event.
|
||||
|
@ -481,19 +469,15 @@ bool GLGizmoSlaSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
|
|||
std::vector<Vec3f> points_inside;
|
||||
std::vector<unsigned int> points_idxs = m_selection_rectangle.stop_dragging(m_parent, points);
|
||||
for (size_t idx : points_idxs)
|
||||
points_inside.push_back((trafo.get_matrix() * points[idx]).cast<float>());
|
||||
points_inside.push_back(points[idx].cast<float>());
|
||||
|
||||
// Only select/deselect points that are actually visible
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside,
|
||||
[this](const Vec3f& pt) { return is_point_clipped(pt.cast<double>()); }))
|
||||
for (size_t idx : m_mesh_raycaster->get_unobscured_idxs(trafo, m_parent.get_camera(), points_inside, m_clipping_plane.get()))
|
||||
{
|
||||
const sla::SupportPoint &support_point = m_editing_cache[points_idxs[idx]].support_point;
|
||||
if (! is_point_clipped(support_point.pos.cast<double>())) {
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
if (rectangle_status == GLSelectionRectangle::Deselect)
|
||||
unselect_point(points_idxs[idx]);
|
||||
else
|
||||
select_point(points_idxs[idx]);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -125,7 +125,7 @@ private:
|
|||
mutable std::unique_ptr<MeshClipper> m_supports_clipper;
|
||||
|
||||
std::vector<const ConfigOption*> get_config_options(const std::vector<std::string>& keys) const;
|
||||
bool is_point_clipped(const Vec3d& point) const;
|
||||
bool is_mesh_point_clipped(const Vec3d& point) const;
|
||||
//void find_intersecting_facets(const igl::AABB<Eigen::MatrixXf, 3>* aabb, const Vec3f& normal, double offset, std::vector<unsigned int>& out) const;
|
||||
|
||||
// Methods that do the model_object and editing cache synchronization,
|
||||
|
|
|
@ -152,8 +152,8 @@ Vec3f MeshRaycaster::AABBWrapper::get_hit_normal(const igl::Hit& hit) const
|
|||
}
|
||||
|
||||
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo,
|
||||
const Camera& camera, std::vector<Vec3f>* positions, std::vector<Vec3f>* normals) const
|
||||
bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
const std::array<int, 4>& viewport = camera.get_viewport();
|
||||
const Transform3d& model_mat = camera.get_view_matrix();
|
||||
|
@ -179,25 +179,30 @@ bool MeshRaycaster::unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d&
|
|||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& a, const igl::Hit& b) { return a.t < b.t; });
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
if (positions != nullptr) {
|
||||
positions->clear();
|
||||
if (normals != nullptr)
|
||||
normals->clear();
|
||||
for (const igl::Hit& hit : hits) {
|
||||
positions->push_back(m_AABB_wrapper->get_hit_pos(hit));
|
||||
unsigned i = 0;
|
||||
|
||||
if (normals != nullptr)
|
||||
normals->push_back(m_AABB_wrapper->get_hit_normal(hit));
|
||||
// Remove points that are obscured or cut by the clipping plane
|
||||
if (clipping_plane) {
|
||||
for (i=0; i<hits.size(); ++i)
|
||||
if (! clipping_plane->is_point_clipped(trafo * m_AABB_wrapper->get_hit_pos(hits[i]).cast<double>()))
|
||||
break;
|
||||
|
||||
if (i==hits.size() || (hits.size()-i) % 2 != 0) {
|
||||
// All hits are either clipped, or there is an odd number of unclipped
|
||||
// hits - meaning the nearest must be from inside the mesh.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Now stuff the points in the provided vector and calculate normals if asked about them:
|
||||
position = m_AABB_wrapper->get_hit_pos(hits[i]);
|
||||
normal = m_AABB_wrapper->get_hit_normal(hits[i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera, const std::vector<Vec3f>& points,
|
||||
std::function<bool(const Vec3f&)> fn_ignore_hit) const
|
||||
const ClippingPlane* clipping_plane) const
|
||||
{
|
||||
std::vector<unsigned> out;
|
||||
|
||||
|
@ -206,19 +211,24 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
Vec3f direction_to_camera_mesh = (instance_matrix_no_translation_no_scaling.inverse().cast<float>() * direction_to_camera).normalized().eval();
|
||||
Vec3f scaling = trafo.get_scaling_factor().cast<float>();
|
||||
direction_to_camera_mesh = Vec3f(direction_to_camera_mesh(0)*scaling(0), direction_to_camera_mesh(1)*scaling(1), direction_to_camera_mesh(2)*scaling(2));
|
||||
const Transform3f inverse_trafo = trafo.get_matrix().inverse().cast<float>();
|
||||
|
||||
for (size_t i=0; i<points.size(); ++i) {
|
||||
const Vec3f& pt = points[i];
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(pt.cast<double>()))
|
||||
continue;
|
||||
|
||||
bool is_obscured = false;
|
||||
// Cast a ray in the direction of the camera and look for intersection with the mesh:
|
||||
std::vector<igl::Hit> hits;
|
||||
// Offset the start of the ray to the front of the ball + EPSILON to account for numerical inaccuracies.
|
||||
// Offset the start of the ray by EPSILON to account for numerical inaccuracies.
|
||||
if (m_AABB_wrapper->m_AABB.intersect_ray(
|
||||
AABBWrapper::MapMatrixXfUnaligned(m_mesh->its.vertices.front().data(), m_mesh->its.vertices.size(), 3),
|
||||
AABBWrapper::MapMatrixXiUnaligned(m_mesh->its.indices.front().data(), m_mesh->its.indices.size(), 3),
|
||||
pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
inverse_trafo * pt + direction_to_camera_mesh * EPSILON, direction_to_camera_mesh, hits)) {
|
||||
|
||||
std::sort(hits.begin(), hits.end(), [](const igl::Hit& h1, const igl::Hit& h2) { return h1.t < h2.t; });
|
||||
|
||||
// If the closest hit facet normal points in the same direction as the ray,
|
||||
// we are looking through the mesh and should therefore discard the point:
|
||||
if (m_AABB_wrapper->get_hit_normal(hits.front()).dot(direction_to_camera_mesh) > 0.f)
|
||||
|
@ -227,11 +237,12 @@ std::vector<unsigned> MeshRaycaster::get_unobscured_idxs(const Geometry::Transfo
|
|||
// Eradicate all hits that the caller wants to ignore
|
||||
for (unsigned j=0; j<hits.size(); ++j) {
|
||||
const igl::Hit& hit = hits[j];
|
||||
if (fn_ignore_hit(m_AABB_wrapper->get_hit_pos(hit))) {
|
||||
if (clipping_plane && clipping_plane->is_point_clipped(trafo.get_matrix() * m_AABB_wrapper->get_hit_pos(hit).cast<double>())) {
|
||||
hits.erase(hits.begin()+j);
|
||||
--j;
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: the intersection could in theory be behind the camera, but as of now we only have camera direction.
|
||||
// Also, the threshold is in mesh coordinates, not in actual dimensions.
|
||||
if (! hits.empty())
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
return (-get_normal().dot(pt) + m_data[3]);
|
||||
}
|
||||
|
||||
bool is_point_clipped(const Vec3d& point) const { return distance(point) < 0.; }
|
||||
void set_normal(const Vec3d& normal) { for (size_t i=0; i<3; ++i) m_data[i] = normal(i); }
|
||||
void set_offset(double offset) { m_data[3] = offset; }
|
||||
Vec3d get_normal() const { return Vec3d(m_data[0], m_data[1], m_data[2]); }
|
||||
|
@ -98,10 +99,10 @@ public:
|
|||
void set_camera(const Camera& camera);
|
||||
|
||||
bool unproject_on_mesh(const Vec2d& mouse_pos, const Transform3d& trafo, const Camera& camera,
|
||||
std::vector<Vec3f>* positions = nullptr, std::vector<Vec3f>* normals = nullptr) const;
|
||||
Vec3f& position, Vec3f& normal, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
std::vector<unsigned> get_unobscured_idxs(const Geometry::Transformation& trafo, const Camera& camera,
|
||||
const std::vector<Vec3f>& points, std::function<bool(const Vec3f&)> fn_ignore_hit) const;
|
||||
const std::vector<Vec3f>& points, const ClippingPlane* clipping_plane = nullptr) const;
|
||||
|
||||
Vec3f get_closest_point(const Vec3f& point, Vec3f* normal = nullptr) const;
|
||||
|
||||
|
|
Loading…
Reference in a new issue