WIP: FDM supports gizmo now works with multiple part objects into some extent

This commit is contained in:
Lukas Matena 2019-11-01 12:23:49 +01:00
parent 1b05ecae8c
commit f275461354
3 changed files with 154 additions and 147 deletions

View file

@ -17,9 +17,8 @@ namespace GUI {
GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id) GLGizmoFdmSupports::GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id)
: GLGizmoBase(parent, icon_filename, sprite_id) : GLGizmoBase(parent, icon_filename, sprite_id)
, m_quadric(nullptr) , m_quadric(nullptr)
, m_its(nullptr)
{ {
m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.)); m_clipping_plane.reset(new ClippingPlane());
m_quadric = ::gluNewQuadric(); m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr) if (m_quadric != nullptr)
// using GLU_FILL does not work when the instance's transformation // using GLU_FILL does not work when the instance's transformation
@ -99,7 +98,7 @@ void GLGizmoFdmSupports::on_render() const
return; return;
} }
if (! m_its || ! m_mesh) if (m_meshes.empty())
const_cast<GLGizmoFdmSupports*>(this)->update_mesh(); const_cast<GLGizmoFdmSupports*>(this)->update_mesh();
glsafe(::glEnable(GL_BLEND)); glsafe(::glEnable(GL_BLEND));
@ -114,60 +113,67 @@ void GLGizmoFdmSupports::on_render() const
void GLGizmoFdmSupports::render_triangles(const Selection& selection) const void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
{ {
if (! m_mesh) // if (m_meshes.empty())
return; // return;
// Get transformation of the instance
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Transform3d trafo = vol->get_instance_transformation().get_matrix();
::glColor3f(0.0f, 0.37f, 1.0f); for (size_t mesh_id=0; mesh_id<m_meshes.size(); ++mesh_id) {
for (size_t facet_idx=0; facet_idx<m_selected_facets.size(); ++facet_idx) { const Transform3d trafo_matrix =
if (! m_selected_facets[facet_idx]) m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
continue; m_model_object->volumes[mesh_id]->get_matrix();
stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(m_mesh->its, facet_idx); const TriangleMesh* mesh = m_meshes[mesh_id];
::glPushMatrix();
::glTranslatef(normal(0), normal(1), normal(2));
::glMultMatrixd(trafo.data());
::glBegin(GL_TRIANGLES);
::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](0)](2));
::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](1)](2)); ::glColor3f(0.0f, 0.37f, 1.0f);
::glVertex3f(m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](0), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](1), m_mesh->its.vertices[m_mesh->its.indices[facet_idx](2)](2));
::glEnd(); for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
::glPopMatrix(); if (! m_selected_facets[mesh_id][facet_idx])
continue;
stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(mesh->its, facet_idx);
::glPushMatrix();
::glTranslatef(normal(0), normal(1), normal(2));
::glMultMatrixd(trafo_matrix.data());
::glBegin(GL_TRIANGLES);
::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](0)](0), mesh->its.vertices[mesh->its.indices[facet_idx](0)](1), mesh->its.vertices[mesh->its.indices[facet_idx](0)](2));
::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](1)](0), mesh->its.vertices[mesh->its.indices[facet_idx](1)](1), mesh->its.vertices[mesh->its.indices[facet_idx](1)](2));
::glVertex3f(mesh->its.vertices[mesh->its.indices[facet_idx](2)](0), mesh->its.vertices[mesh->its.indices[facet_idx](2)](1), mesh->its.vertices[mesh->its.indices[facet_idx](2)](2));
::glEnd();
::glPopMatrix();
}
} }
} }
void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const void GLGizmoFdmSupports::render_clipping_plane(const Selection& selection) const
{ {
if (m_clipping_plane_distance == 0.f) // if (m_clipping_plane_distance == 0.f)
return; // return;
// Get transformation of the instance // // Get transformation of the instance
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin()); // const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = vol->get_instance_transformation(); // Geometry::Transformation trafo = vol->get_instance_transformation();
// Now initialize the TMS for the object, perform the cut and save the result. // // Now initialize the TMS for the object, perform the cut and save the result.
if (! m_object_clipper) { // if (! m_object_clipper) {
m_object_clipper.reset(new MeshClipper); // m_object_clipper.reset(new MeshClipper);
m_object_clipper->set_mesh(*m_mesh); // m_object_clipper->set_mesh(*m_mesh);
} // }
m_object_clipper->set_plane(*m_clipping_plane); // m_object_clipper->set_plane(*m_clipping_plane);
m_object_clipper->set_transformation(trafo); // m_object_clipper->set_transformation(trafo);
// At this point we have the triangulated cuts for both the object and supports - let's render. // // At this point we have the triangulated cuts for both the object and supports - let's render.
if (! m_object_clipper->get_triangles().empty()) { // if (! m_object_clipper->get_triangles().empty()) {
::glPushMatrix(); // ::glPushMatrix();
::glColor3f(1.0f, 0.37f, 0.0f); // ::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES); // ::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_object_clipper->get_triangles()) // for (const Vec3f& point : m_object_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2)); // ::glVertex3f(point(0), point(1), point(2));
::glEnd(); // ::glEnd();
::glPopMatrix(); // ::glPopMatrix();
} // }
} }
void GLGizmoFdmSupports::render_cursor_circle() const void GLGizmoFdmSupports::render_cursor_circle() const
@ -187,6 +193,8 @@ void GLGizmoFdmSupports::render_cursor_circle() const
glsafe(::glLineWidth(1.5f)); glsafe(::glLineWidth(1.5f));
float color[3]; float color[3];
color[0] = 0.f;
color[1] = 1.f;
color[2] = 0.3f; color[2] = 0.3f;
glsafe(::glColor3fv(color)); glsafe(::glColor3fv(color));
glsafe(::glDisable(GL_DEPTH_TEST)); glsafe(::glDisable(GL_DEPTH_TEST));
@ -219,23 +227,14 @@ void GLGizmoFdmSupports::on_render_for_picking() const
} }
bool GLGizmoFdmSupports::is_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;
return m_clipping_plane->distance(transformed_point) < 0.;
}
bool GLGizmoFdmSupports::is_mesh_update_necessary() const bool GLGizmoFdmSupports::is_mesh_update_necessary() const
{ {
std::vector<ObjectID> volumes_ids;
for (const ModelVolume* vol : m_model_object->volumes)
volumes_ids.push_back(vol->id());
return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty()) return ((m_state == On) && (m_model_object != nullptr) && !m_model_object->instances.empty())
&& ((m_model_object->id() != m_model_object_id) || m_its == nullptr); && (m_model_object->id() != m_model_object_id || m_volumes_ids != volumes_ids);
} }
@ -246,26 +245,33 @@ void GLGizmoFdmSupports::update_mesh()
return; return;
wxBusyCursor wait; wxBusyCursor wait;
// this way we can use that mesh directly.
// This mesh does not account for the possible Z up SLA offset.
m_mesh = &m_model_object->volumes.front()->mesh();
m_its = &m_mesh->its;
m_selected_facets.assign(m_mesh->its.indices.size(), false); size_t num_of_volumes = m_model_object->volumes.size();
m_meshes.clear();
m_selected_facets.resize(num_of_volumes);
m_neighbors.resize(num_of_volumes);
m_meshes_raycaster.clear();
// Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets for (size_t volume_id=0; volume_id<num_of_volumes; ++volume_id) {
m_neighbors.resize(3 * m_mesh->its.indices.size()); // This mesh does not account for the possible Z up SLA offset.
for (size_t i=0; i<m_mesh->its.indices.size(); ++i) { const TriangleMesh* mesh = &m_model_object->volumes[volume_id]->mesh();
const stl_triangle_vertex_indices& ind = m_mesh->its.indices[i]; m_meshes.push_back(mesh);
m_neighbors[3*i] = std::make_pair(ind(0), i);
m_neighbors[3*i+1] = std::make_pair(ind(1), i); m_selected_facets[volume_id].assign(mesh->its.indices.size(), false);
m_neighbors[3*i+2] = std::make_pair(ind(2), i); m_neighbors[volume_id].resize(3 * mesh->its.indices.size());
// Prepare vector of vertex_index - facet_index pairs to quickly find adjacent facets
for (size_t i=0; i<mesh->its.indices.size(); ++i) {
const stl_triangle_vertex_indices& ind = mesh->its.indices[i];
m_neighbors[volume_id][3*i] = std::make_pair(ind(0), i);
m_neighbors[volume_id][3*i+1] = std::make_pair(ind(1), i);
m_neighbors[volume_id][3*i+2] = std::make_pair(ind(2), i);
}
std::sort(m_neighbors[volume_id].begin(), m_neighbors[volume_id].end());
// Recalculate raycaster.
m_meshes_raycaster.emplace_back(new MeshRaycaster(*mesh));
} }
std::sort(m_neighbors.begin(), m_neighbors.end());
// If this is different mesh than last time or if the AABB tree is uninitialized, recalculate it.
if (m_model_object_id != m_model_object->id() || ! m_mesh_raycaster)
m_mesh_raycaster.reset(new MeshRaycaster(*m_mesh));
m_model_object_id = m_model_object->id(); m_model_object_id = m_model_object->id();
} }
@ -275,22 +281,21 @@ void GLGizmoFdmSupports::update_mesh()
// Unprojects the mouse position on the mesh and saves hit facet index into facet_idx // Unprojects the mouse position on the mesh and saves hit facet index into facet_idx
// Position of the hit in mesh coords is copied into *position, if provided. // Position of the hit in mesh coords is copied into *position, if provided.
// Returns false if no intersection was found, true otherwise. // Returns false if no intersection was found, true otherwise.
bool GLGizmoFdmSupports::unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position) bool GLGizmoFdmSupports::unproject_on_mesh(size_t mesh_id, const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position)
{ {
// if the gizmo doesn't have the V, F structures for igl, calculate them first: // if the gizmo doesn't have the V, F structures for igl, calculate them first:
if (! m_mesh_raycaster) //if (! m_meshes_raycaster[mesh_id])
update_mesh(); // update_mesh();
const Camera& camera = m_parent.get_camera(); const Camera& camera = m_parent.get_camera();
const Selection& selection = m_parent.get_selection(); const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); const Transform3d trafo_matrix =
Geometry::Transformation trafo = volume->get_instance_transformation(); m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
trafo.set_offset(trafo.get_offset()); m_model_object->volumes[mesh_id]->get_matrix();
// The raycaster query // The raycaster query
Vec3f hit; Vec3f hit;
Vec3f normal; Vec3f normal;
if (m_mesh_raycaster->unproject_on_mesh(mouse_pos, trafo.get_matrix(), camera, hit, normal, m_clipping_plane.get(), &facet_idx)) { if (m_meshes_raycaster[mesh_id]->unproject_on_mesh(mouse_pos, trafo_matrix, camera, hit, normal, m_clipping_plane.get(), &facet_idx)) {
if (position) if (position)
*position = hit; *position = hit;
return true; return true;
@ -330,66 +335,74 @@ bool GLGizmoFdmSupports::gizmo_event(SLAGizmoEventType action, const Vec2d& mous
if (action == SLAGizmoEventType::LeftDown || (action == SLAGizmoEventType::Dragging && m_wait_for_up_event)) { if (action == SLAGizmoEventType::LeftDown || (action == SLAGizmoEventType::Dragging && m_wait_for_up_event)) {
size_t facet = 0; size_t facet = 0;
Vec3f hit_pos; Vec3f hit_pos;
if (unproject_on_mesh(mouse_position, facet, &hit_pos)) { bool mesh_was_hit = false;
bool select = ! shift_down;
// Calculate direction from camera to the hit (in mesh coords): for (size_t mesh_id=0; mesh_id<m_model_object->volumes.size(); ++mesh_id) {
const Selection& selection = m_parent.get_selection(); if (unproject_on_mesh(mesh_id, mouse_position, facet, &hit_pos)) {
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); mesh_was_hit = true;
Geometry::Transformation trafo = volume->get_instance_transformation(); const TriangleMesh* mesh = m_meshes[mesh_id];
trafo.set_offset(trafo.get_offset()); std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
Vec3f dir = ((trafo.get_matrix().inverse() * m_parent.get_camera().get_position()).cast<float>() - hit_pos).normalized();
// Calculate how far can a point be from the line (in mesh coords). bool select = ! shift_down;
// FIXME: This should account for (possibly non-uniform) scaling of the mesh.
float limit = pow(m_cursor_radius, 2.f);
// Calculate direction from camera to the hit (in mesh coords):
const Selection& selection = m_parent.get_selection();
const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = volume->get_instance_transformation();
trafo.set_offset(trafo.get_offset());
Vec3f dir = ((trafo.get_matrix().inverse() * m_parent.get_camera().get_position()).cast<float>() - hit_pos).normalized();
// A lambda to calculate distance from the line: // Calculate how far can a point be from the line (in mesh coords).
auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float { // FIXME: This should account for (possibly non-uniform) scaling of the mesh.
Vec3f diff = hit_pos - point; float limit = pow(m_cursor_radius, 2.f);
return (diff - diff.dot(dir) * dir).squaredNorm();
};
// A lambda to determine whether this facet is potentionally visible (still can be obscured) // A lambda to calculate distance from the line:
auto faces_camera = [&dir, this](const size_t& facet) -> bool { auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float {
return (m_mesh->stl.facet_start[facet].normal.dot(dir) > 0.); Vec3f diff = hit_pos - point;
}; return (diff - diff.dot(dir) * dir).squaredNorm();
};
// Now start with the facet the pointer points to and check all adjacent facets. m_neighbors vector stores // A lambda to determine whether this facet is potentionally visible (still can be obscured)
// pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be auto faces_camera = [&dir, this](const size_t& mesh_id, const size_t& facet) -> bool {
// quickly found by finding a vertex in the list and read the respective facet ids. return (m_meshes[mesh_id]->stl.facet_start[facet].normal.dot(dir) > 0.);
std::vector<size_t> facets_to_select{facet}; };
NeighborData vertex = std::make_pair(0, 0); // Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
std::vector<bool> visited(m_selected_facets.size(), false); // keep track of facets we already processed // pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
size_t facet_idx = 0; // index into facets_to_select // quickly found by finding a vertex in the list and read the respective facet ids.
auto it = m_neighbors.end(); std::vector<size_t> facets_to_select{facet};
NeighborData vertex = std::make_pair(0, 0);
while (facet_idx < facets_to_select.size()) { std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
size_t facet = facets_to_select[facet_idx]; size_t facet_idx = 0; // index into facets_to_select
if (! visited[facet]) { auto it = neighbors.end();
// check all three vertices and in case they're close enough, find the remaining facets while (facet_idx < facets_to_select.size()) {
// and add them to the list to be proccessed later size_t facet = facets_to_select[facet_idx];
for (size_t i=0; i<3; ++i) { if (! visited[facet]) {
vertex.first = m_mesh->its.indices[facet](i); // vertex index // check all three vertices and in case they're close enough, find the remaining facets
float dist = squared_distance_from_line(m_mesh->its.vertices[vertex.first]); // and add them to the list to be proccessed later
if (dist < limit) { for (size_t i=0; i<3; ++i) {
it = std::lower_bound(m_neighbors.begin(), m_neighbors.end(), vertex); vertex.first = mesh->its.indices[facet](i); // vertex index
while (it != m_neighbors.end() && it->first == vertex.first) { float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
if (it->second != facet && faces_camera(it->second)) if (dist < limit) {
facets_to_select.push_back(it->second); it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
++it; while (it != neighbors.end() && it->first == vertex.first) {
if (it->second != facet && faces_camera(mesh_id, it->second))
facets_to_select.push_back(it->second);
++it;
}
} }
} }
visited[facet] = true;
} }
visited[facet] = true; ++facet_idx;
} }
++facet_idx; // Now just select all facets that passed
for (size_t next_facet : facets_to_select)
m_selected_facets[mesh_id][next_facet] = select;
} }
// Now just select all facets that passed }
for (size_t next_facet : facets_to_select)
m_selected_facets[next_facet] = select;
if (mesh_was_hit)
{
m_wait_for_up_event = true; m_wait_for_up_event = true;
m_parent.set_as_dirty(); m_parent.set_as_dirty();
return true; return true;
@ -529,9 +542,8 @@ void GLGizmoFdmSupports::on_set_state()
m_parent.toggle_model_objects_visibility(true); m_parent.toggle_model_objects_visibility(true);
m_clipping_plane_distance = 0.f; m_clipping_plane_distance = 0.f;
// Release clippers and the AABB raycaster. // Release clippers and the AABB raycaster.
m_its = nullptr; m_meshes_clipper.clear();
m_object_clipper.reset(); m_meshes_raycaster.clear();
m_mesh_raycaster.reset();
} }
m_old_state = m_state; m_old_state = m_state;
} }

View file

@ -19,20 +19,20 @@ class GLGizmoFdmSupports : public GLGizmoBase
private: private:
ModelObject* m_model_object = nullptr; ModelObject* m_model_object = nullptr;
ObjectID m_model_object_id = 0; ObjectID m_model_object_id = 0;
std::vector<ObjectID> m_volumes_ids;
int m_active_instance = -1; int m_active_instance = -1;
float m_active_instance_bb_radius; // to cache the bb float m_active_instance_bb_radius; // to cache the bb
bool unproject_on_mesh(const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position = nullptr); bool unproject_on_mesh(size_t mesh_id, const Vec2d& mouse_pos, size_t& facet_idx, Vec3f* position = nullptr);
GLUquadricObj* m_quadric; GLUquadricObj* m_quadric;
std::unique_ptr<MeshRaycaster> m_mesh_raycaster; std::vector<std::unique_ptr<MeshRaycaster>> m_meshes_raycaster;
const TriangleMesh* m_mesh; std::vector<const TriangleMesh*> m_meshes;
const indexed_triangle_set* m_its;
mutable std::vector<Vec2f> m_triangles; mutable std::vector<Vec2f> m_triangles;
float m_cursor_radius = 2.f; float m_cursor_radius = 2.f;
std::vector<bool> m_selected_facets; std::vector<std::vector<bool>> m_selected_facets;
public: public:
GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id); GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@ -64,12 +64,10 @@ private:
bool m_wait_for_up_event = false; bool m_wait_for_up_event = false;
EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state) EState m_old_state = Off; // to be able to see that the gizmo has just been closed (see on_set_state)
mutable std::unique_ptr<MeshClipper> m_object_clipper; mutable std::vector<std::unique_ptr<MeshClipper>> m_meshes_clipper;
std::vector<NeighborData> m_neighbors; // pairs of vertex_index - facet_index std::vector<std::vector<NeighborData>> m_neighbors; // pairs of vertex_index - facet_index for each mesh
bool is_point_clipped(const Vec3d& point) const;
void update_clipping_plane(bool keep_normal = false) const; void update_clipping_plane(bool keep_normal = false) const;
protected: protected:

View file

@ -28,10 +28,7 @@ class ClippingPlane
public: public:
ClippingPlane() ClippingPlane()
{ {
m_data[0] = 0.0; *this = ClipsNothing();
m_data[1] = 0.0;
m_data[2] = 1.0;
m_data[3] = 0.0;
} }
ClippingPlane(const Vec3d& direction, double offset) ClippingPlane(const Vec3d& direction, double offset)