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)
: GLGizmoBase(parent, icon_filename, sprite_id)
, m_quadric(nullptr)
, m_its(nullptr)
{
m_clipping_plane.reset(new ClippingPlane(Vec3d::Zero(), 0.));
m_clipping_plane.reset(new ClippingPlane());
m_quadric = ::gluNewQuadric();
if (m_quadric != nullptr)
// using GLU_FILL does not work when the instance's transformation
@ -99,7 +98,7 @@ void GLGizmoFdmSupports::on_render() const
return;
}
if (! m_its || ! m_mesh)
if (m_meshes.empty())
const_cast<GLGizmoFdmSupports*>(this)->update_mesh();
glsafe(::glEnable(GL_BLEND));
@ -114,60 +113,67 @@ void GLGizmoFdmSupports::on_render() const
void GLGizmoFdmSupports::render_triangles(const Selection& selection) const
{
if (! m_mesh)
return;
// if (m_meshes.empty())
// 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) {
if (! m_selected_facets[facet_idx])
continue;
stl_normal normal = 0.01f * MeshRaycaster::get_triangle_normal(m_mesh->its, facet_idx);
::glPushMatrix();
::glTranslatef(normal(0), normal(1), normal(2));
::glMultMatrixd(trafo.data());
const Transform3d trafo_matrix =
m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
m_model_object->volumes[mesh_id]->get_matrix();
const TriangleMesh* mesh = m_meshes[mesh_id];
::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));
::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();
::glPopMatrix();
::glColor3f(0.0f, 0.37f, 1.0f);
for (size_t facet_idx=0; facet_idx<m_selected_facets[mesh_id].size(); ++facet_idx) {
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
{
if (m_clipping_plane_distance == 0.f)
return;
// if (m_clipping_plane_distance == 0.f)
// return;
// Get transformation of the instance
const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
Geometry::Transformation trafo = vol->get_instance_transformation();
// // Get transformation of the instance
// const GLVolume* vol = selection.get_volume(*selection.get_volume_idxs().begin());
// Geometry::Transformation trafo = vol->get_instance_transformation();
// Now initialize the TMS for the object, perform the cut and save the result.
if (! m_object_clipper) {
m_object_clipper.reset(new MeshClipper);
m_object_clipper->set_mesh(*m_mesh);
}
m_object_clipper->set_plane(*m_clipping_plane);
m_object_clipper->set_transformation(trafo);
// // Now initialize the TMS for the object, perform the cut and save the result.
// if (! m_object_clipper) {
// m_object_clipper.reset(new MeshClipper);
// m_object_clipper->set_mesh(*m_mesh);
// }
// m_object_clipper->set_plane(*m_clipping_plane);
// m_object_clipper->set_transformation(trafo);
// At this point we have the triangulated cuts for both the object and supports - let's render.
if (! m_object_clipper->get_triangles().empty()) {
::glPushMatrix();
::glColor3f(1.0f, 0.37f, 0.0f);
::glBegin(GL_TRIANGLES);
for (const Vec3f& point : m_object_clipper->get_triangles())
::glVertex3f(point(0), point(1), point(2));
::glEnd();
::glPopMatrix();
}
// // At this point we have the triangulated cuts for both the object and supports - let's render.
// if (! m_object_clipper->get_triangles().empty()) {
// ::glPushMatrix();
// ::glColor3f(1.0f, 0.37f, 0.0f);
// ::glBegin(GL_TRIANGLES);
// for (const Vec3f& point : m_object_clipper->get_triangles())
// ::glVertex3f(point(0), point(1), point(2));
// ::glEnd();
// ::glPopMatrix();
// }
}
void GLGizmoFdmSupports::render_cursor_circle() const
@ -187,6 +193,8 @@ void GLGizmoFdmSupports::render_cursor_circle() const
glsafe(::glLineWidth(1.5f));
float color[3];
color[0] = 0.f;
color[1] = 1.f;
color[2] = 0.3f;
glsafe(::glColor3fv(color));
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
{
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())
&& ((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;
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
m_neighbors.resize(3 * m_mesh->its.indices.size());
for (size_t i=0; i<m_mesh->its.indices.size(); ++i) {
const stl_triangle_vertex_indices& ind = m_mesh->its.indices[i];
m_neighbors[3*i] = std::make_pair(ind(0), i);
m_neighbors[3*i+1] = std::make_pair(ind(1), i);
m_neighbors[3*i+2] = std::make_pair(ind(2), i);
for (size_t volume_id=0; volume_id<num_of_volumes; ++volume_id) {
// This mesh does not account for the possible Z up SLA offset.
const TriangleMesh* mesh = &m_model_object->volumes[volume_id]->mesh();
m_meshes.push_back(mesh);
m_selected_facets[volume_id].assign(mesh->its.indices.size(), false);
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();
}
@ -275,22 +281,21 @@ void GLGizmoFdmSupports::update_mesh()
// 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.
// 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 (! m_mesh_raycaster)
update_mesh();
//if (! m_meshes_raycaster[mesh_id])
// update_mesh();
const Camera& camera = m_parent.get_camera();
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());
const Transform3d trafo_matrix =
m_model_object->instances[selection.get_instance_idx()]->get_transformation().get_matrix() *
m_model_object->volumes[mesh_id]->get_matrix();
// The raycaster query
Vec3f hit;
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)
*position = hit;
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)) {
size_t facet = 0;
Vec3f hit_pos;
if (unproject_on_mesh(mouse_position, facet, &hit_pos)) {
bool select = ! shift_down;
bool mesh_was_hit = false;
// 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();
for (size_t mesh_id=0; mesh_id<m_model_object->volumes.size(); ++mesh_id) {
if (unproject_on_mesh(mesh_id, mouse_position, facet, &hit_pos)) {
mesh_was_hit = true;
const TriangleMesh* mesh = m_meshes[mesh_id];
std::vector<NeighborData>& neighbors = m_neighbors[mesh_id];
// Calculate how far can a point be from the line (in mesh coords).
// FIXME: This should account for (possibly non-uniform) scaling of the mesh.
float limit = pow(m_cursor_radius, 2.f);
bool select = ! shift_down;
// 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:
auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float {
Vec3f diff = hit_pos - point;
return (diff - diff.dot(dir) * dir).squaredNorm();
};
// Calculate how far can a point be from the line (in mesh coords).
// FIXME: This should account for (possibly non-uniform) scaling of the mesh.
float limit = pow(m_cursor_radius, 2.f);
// A lambda to determine whether this facet is potentionally visible (still can be obscured)
auto faces_camera = [&dir, this](const size_t& facet) -> bool {
return (m_mesh->stl.facet_start[facet].normal.dot(dir) > 0.);
};
// A lambda to calculate distance from the line:
auto squared_distance_from_line = [&hit_pos, &dir](const Vec3f point) -> float {
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
// pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
// quickly found by finding a vertex in the list and read the respective facet ids.
std::vector<size_t> facets_to_select{facet};
NeighborData vertex = std::make_pair(0, 0);
std::vector<bool> visited(m_selected_facets.size(), false); // keep track of facets we already processed
size_t facet_idx = 0; // index into facets_to_select
auto it = m_neighbors.end();
while (facet_idx < facets_to_select.size()) {
size_t facet = facets_to_select[facet_idx];
if (! visited[facet]) {
// check all three vertices and in case they're close enough, find the remaining facets
// and add them to the list to be proccessed later
for (size_t i=0; i<3; ++i) {
vertex.first = m_mesh->its.indices[facet](i); // vertex index
float dist = squared_distance_from_line(m_mesh->its.vertices[vertex.first]);
if (dist < limit) {
it = std::lower_bound(m_neighbors.begin(), m_neighbors.end(), vertex);
while (it != m_neighbors.end() && it->first == vertex.first) {
if (it->second != facet && faces_camera(it->second))
facets_to_select.push_back(it->second);
++it;
// A lambda to determine whether this facet is potentionally visible (still can be obscured)
auto faces_camera = [&dir, this](const size_t& mesh_id, const size_t& facet) -> bool {
return (m_meshes[mesh_id]->stl.facet_start[facet].normal.dot(dir) > 0.);
};
// Now start with the facet the pointer points to and check all adjacent facets. neighbors vector stores
// pairs of vertex_idx - facet_idx and is sorted with respect to the former. Neighboring facet index can be
// quickly found by finding a vertex in the list and read the respective facet ids.
std::vector<size_t> facets_to_select{facet};
NeighborData vertex = std::make_pair(0, 0);
std::vector<bool> visited(m_selected_facets[mesh_id].size(), false); // keep track of facets we already processed
size_t facet_idx = 0; // index into facets_to_select
auto it = neighbors.end();
while (facet_idx < facets_to_select.size()) {
size_t facet = facets_to_select[facet_idx];
if (! visited[facet]) {
// check all three vertices and in case they're close enough, find the remaining facets
// and add them to the list to be proccessed later
for (size_t i=0; i<3; ++i) {
vertex.first = mesh->its.indices[facet](i); // vertex index
float dist = squared_distance_from_line(mesh->its.vertices[vertex.first]);
if (dist < limit) {
it = std::lower_bound(neighbors.begin(), neighbors.end(), vertex);
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_parent.set_as_dirty();
return true;
@ -529,9 +542,8 @@ void GLGizmoFdmSupports::on_set_state()
m_parent.toggle_model_objects_visibility(true);
m_clipping_plane_distance = 0.f;
// Release clippers and the AABB raycaster.
m_its = nullptr;
m_object_clipper.reset();
m_mesh_raycaster.reset();
m_meshes_clipper.clear();
m_meshes_raycaster.clear();
}
m_old_state = m_state;
}

View file

@ -19,20 +19,20 @@ class GLGizmoFdmSupports : public GLGizmoBase
private:
ModelObject* m_model_object = nullptr;
ObjectID m_model_object_id = 0;
std::vector<ObjectID> m_volumes_ids;
int m_active_instance = -1;
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;
std::unique_ptr<MeshRaycaster> m_mesh_raycaster;
const TriangleMesh* m_mesh;
const indexed_triangle_set* m_its;
std::vector<std::unique_ptr<MeshRaycaster>> m_meshes_raycaster;
std::vector<const TriangleMesh*> m_meshes;
mutable std::vector<Vec2f> m_triangles;
float m_cursor_radius = 2.f;
std::vector<bool> m_selected_facets;
std::vector<std::vector<bool>> m_selected_facets;
public:
GLGizmoFdmSupports(GLCanvas3D& parent, const std::string& icon_filename, unsigned int sprite_id);
@ -64,12 +64,10 @@ private:
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)
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;
protected:

View file

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