From 11490dfb066e56829a6a11d63d29cde7d0956846 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Thu, 25 Apr 2019 09:46:26 +0200 Subject: [PATCH] Rectangle selection in 3D scene -> hovering detection --- src/slic3r/GUI/3DScene.cpp | 14 ++- src/slic3r/GUI/3DScene.hpp | 9 +- src/slic3r/GUI/GLCanvas3D.cpp | 137 +++++++++++++++++++++--- src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/GLSelectionRectangle.cpp | 6 +- src/slic3r/GUI/GLSelectionRectangle.hpp | 7 ++ 6 files changed, 148 insertions(+), 26 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 9038e388c..9d1407b00 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -223,7 +223,8 @@ void GLIndexedVertexArray::render( } const float GLVolume::SELECTED_COLOR[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; -const float GLVolume::HOVER_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; +const float GLVolume::HOVER_SELECT_COLOR[4] = { 0.4f, 0.9f, 0.1f, 1.0f }; +const float GLVolume::HOVER_DESELECT_COLOR[4] = { 0.9f, 0.4f, 0.1f, 1.0f }; const float GLVolume::OUTSIDE_COLOR[4] = { 0.0f, 0.38f, 0.8f, 1.0f }; const float GLVolume::SELECTED_OUTSIDE_COLOR[4] = { 0.19f, 0.58f, 1.0f, 1.0f }; const float GLVolume::DISABLED_COLOR[4] = { 0.25f, 0.25f, 0.25f, 1.0f }; @@ -251,7 +252,8 @@ GLVolume::GLVolume(float r, float g, float b, float a) , zoom_to_volumes(true) , shader_outside_printer_detection_enabled(false) , is_outside(false) - , hover(false) + , hover_select(false) + , hover_deselect(false) , is_modifier(false) , is_wipe_tower(false) , is_extrusion_path(false) @@ -291,10 +293,12 @@ void GLVolume::set_render_color() if (force_native_color) set_render_color(color, 4); else { - if (selected) + if (hover_select) + set_render_color(HOVER_SELECT_COLOR, 4); + else if (hover_deselect) + set_render_color(HOVER_DESELECT_COLOR, 4); + else if (selected) set_render_color(is_outside ? SELECTED_OUTSIDE_COLOR : SELECTED_COLOR, 4); - else if (hover) - set_render_color(HOVER_COLOR, 4); else if (disabled) set_render_color(DISABLED_COLOR, 4); else if (is_outside && shader_outside_printer_detection_enabled) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 88547359e..fa7d6f7d1 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -225,7 +225,8 @@ private: class GLVolume { public: static const float SELECTED_COLOR[4]; - static const float HOVER_COLOR[4]; + static const float HOVER_SELECT_COLOR[4]; + static const float HOVER_DESELECT_COLOR[4]; static const float OUTSIDE_COLOR[4]; static const float SELECTED_OUTSIDE_COLOR[4]; static const float DISABLED_COLOR[4]; @@ -296,8 +297,10 @@ public: bool shader_outside_printer_detection_enabled; // Wheter or not this volume is outside print volume. bool is_outside; - // Boolean: Is mouse over this object? - bool hover; + // Boolean: Is mouse over this object to select it ? + bool hover_select; + // Boolean: Is mouse over this object to deselect it ? + bool hover_deselect; // Wheter or not this volume has been generated from a modifier bool is_modifier; // Wheter or not this volume has been generated from the wipe tower diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 084a4f3b4..621a726b9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1610,8 +1610,12 @@ void GLCanvas3D::render() wxGetApp().imgui()->new_frame(); - // picking pass - _picking_pass(); + if (m_rectangle_selection.is_dragging()) + // picking pass using rectangle selection + _rectangular_selection_picking_pass(); + else + // regular picking pass + _picking_pass(); // draw scene glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); @@ -2361,6 +2365,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) } set_cursor(Standard); } + else if (keyCode == WXK_CONTROL) + m_dirty = true; } else if (evt.GetEventType() == wxEVT_KEY_DOWN) { m_tab_down = keyCode == WXK_TAB && !evt.HasAnyModifiers(); @@ -2374,6 +2380,8 @@ void GLCanvas3D::on_key(wxKeyEvent& evt) if (m_picking_enabled && (m_gizmos.get_current_type() != GLGizmosManager::SlaSupports)) set_cursor(Cross); } + else if (keyCode == WXK_CONTROL) + m_dirty = true; } } } @@ -3662,7 +3670,7 @@ void GLCanvas3D::_picking_pass() const if (inside) { glsafe(::glReadPixels(pos(0), cnv_size.get_height() - pos(1) - 1, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, (void*)color)); - volume_id = color[0] + color[1] * 256 + color[2] * 256 * 256; + volume_id = color[0] + (color[1] << 8) + (color[2] << 16); } if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) { @@ -3676,6 +3684,83 @@ void GLCanvas3D::_picking_pass() const } } +void GLCanvas3D::_rectangular_selection_picking_pass() const +{ + m_gizmos.set_hover_id(-1); + + std::set idxs; + + if (m_picking_enabled) + { + if (m_multisample_allowed) + glsafe(::glDisable(GL_MULTISAMPLE)); + + glsafe(::glDisable(GL_BLEND)); + glsafe(::glEnable(GL_DEPTH_TEST)); + + glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); + + _render_volumes_for_picking(); + + if (m_multisample_allowed) + glsafe(::glEnable(GL_MULTISAMPLE)); + + int width = (int)m_rectangle_selection.get_width(); + int height = (int)m_rectangle_selection.get_height(); + int px_count = width * height; + + if (px_count > 0) + { + int left = (int)m_rectangle_selection.get_left(); + int top = get_canvas_size().get_height() - (int)m_rectangle_selection.get_top(); + if ((left >= 0) && (top >= 0)) + { +#define USE_PARALLEL 1 +#if USE_PARALLEL + struct Pixel + { + std::array data; + int id() const { return data[0] + (data[1] << 8) + (data[2] << 16); } + }; + + std::vector frame(px_count); + glsafe(::glReadPixels(left, top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)frame.data())); + + tbb::spin_mutex mutex; + tbb::parallel_for(tbb::blocked_range(0, frame.size(), (size_t)width), + [this, &frame, &idxs, &mutex](const tbb::blocked_range& range) { + for (size_t i = range.begin(); i < range.end(); ++i) + { + int volume_id = frame[i].id(); + if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) + { + mutex.lock(); + idxs.insert(volume_id); + mutex.unlock(); + } + } + } + ); +#else + std::vector frame(4 * px_count); + glsafe(::glReadPixels(left, top, width, height, GL_RGBA, GL_UNSIGNED_BYTE, (void*)frame.data())); + + for (int i = 0; i < px_count; ++i) + { + int px_id = 4 * i; + int volume_id = frame[px_id] + (frame[px_id + 1] << 8) + (frame[px_id + 2] << 16); + if ((0 <= volume_id) && (volume_id < (int)m_volumes.volumes.size())) + idxs.insert(volume_id); + } +#endif // USE_PARALLEL + } + } + } + + m_hover_volume_idxs.assign(idxs.begin(), idxs.end()); + _update_volumes_hover_state(); +} + void GLCanvas3D::_render_background() const { glsafe(::glPushMatrix()); @@ -4161,24 +4246,48 @@ void GLCanvas3D::_update_volumes_hover_state() const { for (GLVolume* v : m_volumes.volumes) { - v->hover = false; + v->hover_select = false; + v->hover_deselect = false; } if (m_hover_volume_idxs.empty()) return; - GLVolume* volume = m_volumes.volumes[get_first_hover_volume_idx()]; - if (volume->is_modifier) - volume->hover = true; - else - { - int object_idx = volume->object_idx(); - int instance_idx = volume->instance_idx(); + bool is_ctrl_pressed = wxGetKeyState(WXK_CONTROL); + bool is_shift_pressed = wxGetKeyState(WXK_SHIFT); + bool is_alt_pressed = wxGetKeyState(WXK_ALT); - for (GLVolume* v : m_volumes.volumes) + for (int i : m_hover_volume_idxs) + { + GLVolume* volume = m_volumes.volumes[i]; + bool deselect = volume->selected && ((is_ctrl_pressed && !is_shift_pressed) || (!is_ctrl_pressed && (m_rectangle_selection.get_state() == GLSelectionRectangle::Deselect))); + bool select = !volume->selected || (volume->is_modifier && ((is_ctrl_pressed && !is_alt_pressed) || (!is_ctrl_pressed && (!m_rectangle_selection.is_dragging() || (m_rectangle_selection.get_state() == GLSelectionRectangle::Select))))); + + if (select || deselect) { - if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) - v->hover = true; + if (volume->is_modifier && (!deselect || ((volume->object_idx() == m_selection.get_object_idx()) && (volume->instance_idx() == m_selection.get_instance_idx())))) + { + if (deselect) + volume->hover_deselect = true; + else + volume->hover_select = true; + } + else + { + int object_idx = volume->object_idx(); + int instance_idx = volume->instance_idx(); + + for (GLVolume* v : m_volumes.volumes) + { + if ((v->object_idx() == object_idx) && (v->instance_idx() == instance_idx)) + { + if (deselect) + v->hover_deselect = true; + else + v->hover_select = true; + } + } + } } } } diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index e8c301967..bae9f0b5e 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -623,6 +623,7 @@ private: void _refresh_if_shown_on_screen(); void _picking_pass() const; + void _rectangular_selection_picking_pass() const; void _render_background() const; void _render_bed(float theta) const; void _render_axes() const; diff --git a/src/slic3r/GUI/GLSelectionRectangle.cpp b/src/slic3r/GUI/GLSelectionRectangle.cpp index 03ee7e1f6..9684bb5ec 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.cpp +++ b/src/slic3r/GUI/GLSelectionRectangle.cpp @@ -59,10 +59,8 @@ namespace GUI { void GLSelectionRectangle::stop_dragging() { - if (!is_dragging()) - return; - - m_state = Off; + if (is_dragging()) + m_state = Off; } void GLSelectionRectangle::render(const GLCanvas3D& canvas) const diff --git a/src/slic3r/GUI/GLSelectionRectangle.hpp b/src/slic3r/GUI/GLSelectionRectangle.hpp index f26d3eb4b..db72d9415 100644 --- a/src/slic3r/GUI/GLSelectionRectangle.hpp +++ b/src/slic3r/GUI/GLSelectionRectangle.hpp @@ -35,6 +35,13 @@ public: bool is_dragging() const { return m_state != Off; } EState get_state() const { return m_state; } + float get_width() const { return std::abs(m_start_corner(0) - m_end_corner(0)); } + float get_height() const { return std::abs(m_start_corner(1) - m_end_corner(1)); } + float get_left() const { return std::min(m_start_corner(0), m_end_corner(0)); } + float get_right() const { return std::max(m_start_corner(0), m_end_corner(0)); } + float get_top() const { return std::max(m_start_corner(1), m_end_corner(1)); } + float get_bottom() const { return std::min(m_start_corner(1), m_end_corner(1)); } + private: EState m_state = Off; Vec2d m_start_corner;