From 39b1222b220c5f4aa14354b1c9fff807d8094b1d Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 29 Jun 2022 12:14:41 +0200 Subject: [PATCH] Tech ENABLE_RAYCAST_PICKING - Rendering for rectangle selection made only inside the rectangle, on systems supporting framebuffers --- src/slic3r/GUI/Camera.cpp | 125 +++++++++++++++++++++++++++++ src/slic3r/GUI/Camera.hpp | 18 +++++ src/slic3r/GUI/GLCanvas3D.cpp | 147 ++++++++++++++++++++++++++++++++++ src/slic3r/GUI/GLCanvas3D.hpp | 4 + 4 files changed, 294 insertions(+) diff --git a/src/slic3r/GUI/Camera.cpp b/src/slic3r/GUI/Camera.cpp index 1370484c3..56980c43a 100644 --- a/src/slic3r/GUI/Camera.cpp +++ b/src/slic3r/GUI/Camera.cpp @@ -91,6 +91,80 @@ void Camera::select_view(const std::string& direction) look_at(m_target + m_distance * Vec3d::UnitY(), m_target, Vec3d::UnitZ()); } +#if ENABLE_RAYCAST_PICKING +double Camera::get_near_left() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) - 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_right() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(0, 2) + 1.0) / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(0, 0) - 0.5 * m_projection_matrix.matrix()(0, 0) * m_projection_matrix.matrix()(0, 3); + } +} + +double Camera::get_near_top() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) + 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_bottom() const +{ + switch (m_type) + { + case EType::Perspective: + return m_frustrum_zs.first * (m_projection_matrix.matrix()(1, 2) - 1.0) / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return -1.0 / m_projection_matrix.matrix()(1, 1) - 0.5 * m_projection_matrix.matrix()(1, 1) * m_projection_matrix.matrix()(1, 3); + } +} + +double Camera::get_near_width() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(0, 0); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(0, 0); + } +} + +double Camera::get_near_height() const +{ + switch (m_type) + { + case EType::Perspective: + return 2.0 * m_frustrum_zs.first / m_projection_matrix.matrix()(1, 1); + default: + case EType::Ortho: + return 2.0 / m_projection_matrix.matrix()(1, 1); + } +} +#endif // ENABLE_RAYCAST_PICKING + double Camera::get_fov() const { switch (m_type) @@ -103,6 +177,21 @@ double Camera::get_fov() const }; } +#if ENABLE_RAYCAST_PICKING +void Camera::set_viewport(int x, int y, unsigned int w, unsigned int h) +{ +#if ENABLE_LEGACY_OPENGL_REMOVAL + m_viewport = { 0, 0, int(w), int(h) }; +#else + glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); +#endif // ENABLE_LEGACY_OPENGL_REMOVAL +} + +void Camera::apply_viewport() const +{ + glsafe(::glViewport(m_viewport[0], m_viewport[1], m_viewport[2], m_viewport[3])); +} +#else void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) { glsafe(::glViewport(0, 0, w, h)); @@ -112,6 +201,7 @@ void Camera::apply_viewport(int x, int y, unsigned int w, unsigned int h) glsafe(::glGetIntegerv(GL_VIEWPORT, m_viewport.data())); #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL void Camera::apply_view_matrix() @@ -170,6 +260,9 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa } #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + apply_projection(-w, w, -h, h, m_frustrum_zs.first, m_frustrum_zs.second); +#else switch (m_type) { default: @@ -197,6 +290,7 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa break; } } +#endif // ENABLE_RAYCAST_PICKING #else glsafe(::glMatrixMode(GL_PROJECTION)); glsafe(::glLoadIdentity()); @@ -221,6 +315,37 @@ void Camera::apply_projection(const BoundingBoxf3& box, double near_z, double fa #endif // ENABLE_LEGACY_OPENGL_REMOVAL } +#if ENABLE_RAYCAST_PICKING +void Camera::apply_projection(double left, double right, double bottom, double top, double near_z, double far_z) +{ + assert(left != right && bottom != top && near_z != far_z); + const double inv_dx = 1.0 / (right - left); + const double inv_dy = 1.0 / (top - bottom); + const double inv_dz = 1.0 / (far_z - near_z); + + switch (m_type) + { + default: + case EType::Ortho: + { + m_projection_matrix.matrix() << 2.0 * inv_dx, 0.0, 0.0, -(left + right) * inv_dx, + 0.0, 2.0 * inv_dy, 0.0, -(bottom + top) * inv_dy, + 0.0, 0.0, -2.0 * inv_dz, -(near_z + far_z) * inv_dz, + 0.0, 0.0, 0.0, 1.0; + break; + } + case EType::Perspective: + { + m_projection_matrix.matrix() << 2.0 * near_z * inv_dx, 0.0, (left + right) * inv_dx, 0.0, + 0.0, 2.0 * near_z * inv_dy, (bottom + top) * inv_dy, 0.0, + 0.0, 0.0, -(near_z + far_z) * inv_dz, -2.0 * near_z * far_z * inv_dz, + 0.0, 0.0, -1.0, 0.0; + break; + } + } +} +#endif // ENABLE_RAYCAST_PICKING + void Camera::zoom_to_box(const BoundingBoxf3& box, double margin_factor) { // Calculate the zoom factor needed to adjust the view around the given box. diff --git a/src/slic3r/GUI/Camera.hpp b/src/slic3r/GUI/Camera.hpp index a1d742688..b2672e14e 100644 --- a/src/slic3r/GUI/Camera.hpp +++ b/src/slic3r/GUI/Camera.hpp @@ -89,9 +89,23 @@ public: double get_far_z() const { return m_frustrum_zs.second; } const std::pair& get_z_range() const { return m_frustrum_zs; } +#if ENABLE_RAYCAST_PICKING + double get_near_left() const; + double get_near_right() const; + double get_near_top() const; + double get_near_bottom() const; + double get_near_width() const; + double get_near_height() const; +#endif // ENABLE_RAYCAST_PICKING + double get_fov() const; +#if ENABLE_RAYCAST_PICKING + void set_viewport(int x, int y, unsigned int w, unsigned int h); + void apply_viewport() const; +#else void apply_viewport(int x, int y, unsigned int w, unsigned int h); +#endif // ENABLE_RAYCAST_PICKING #if !ENABLE_LEGACY_OPENGL_REMOVAL void apply_view_matrix(); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL @@ -99,6 +113,10 @@ public: // If larger z span is needed, pass the desired values of near and far z (negative values are ignored) void apply_projection(const BoundingBoxf3& box, double near_z = -1.0, double far_z = -1.0); +#if ENABLE_RAYCAST_PICKING + void apply_projection(double left, double right, double bottom, double top, double near_z, double far_z); +#endif // ENABLE_RAYCAST_PICKING + void zoom_to_box(const BoundingBoxf3& box, double margin_factor = DefaultZoomToBoxMarginFactor); void zoom_to_volumes(const GLVolumePtrs& volumes, double margin_factor = DefaultZoomToVolumesMarginFactor); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 582c6349a..4b3dd163f 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -1628,7 +1628,12 @@ void GLCanvas3D::render() // and the viewport was set incorrectly, leading to tripping glAsserts further down // the road (in apply_projection). That's why the minimum size is forced to 10. Camera& camera = wxGetApp().plater()->get_camera(); +#if ENABLE_RAYCAST_PICKING + camera.set_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); + camera.apply_viewport(); +#else camera.apply_viewport(0, 0, std::max(10u, (unsigned int)cnv_size.get_width()), std::max(10u, (unsigned int)cnv_size.get_height())); +#endif // ENABLE_RAYCAST_PICKING if (camera.requires_zoom_to_bed) { zoom_to_bed(); @@ -4606,7 +4611,12 @@ void GLCanvas3D::_render_thumbnail_internal(ThumbnailData& thumbnail_data, const Camera camera; camera.set_type(camera_type); camera.set_scene_box(scene_bounding_box()); +#if ENABLE_RAYCAST_PICKING + camera.set_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); + camera.apply_viewport(); +#else camera.apply_viewport(0, 0, thumbnail_data.width, thumbnail_data.height); +#endif // ENABLE_RAYCAST_PICKING camera.zoom_to_box(volumes_box); #if ENABLE_LEGACY_OPENGL_REMOVAL @@ -4908,7 +4918,11 @@ void GLCanvas3D::_render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigne #endif // ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT // restore the default framebuffer size to avoid flickering on the 3D scene +#if ENABLE_RAYCAST_PICKING + wxGetApp().plater()->get_camera().apply_viewport(); +#else wxGetApp().plater()->get_camera().apply_viewport(0, 0, cnv_size.get_width(), cnv_size.get_height()); +#endif // ENABLE_RAYCAST_PICKING } bool GLCanvas3D::_init_toolbars() @@ -5567,6 +5581,58 @@ void GLCanvas3D::_rectangular_selection_picking_pass() std::set idxs; if (m_picking_enabled) { +#if ENABLE_RAYCAST_PICKING + const size_t width = std::max(m_rectangle_selection.get_width(), 1); + const size_t height = std::max(m_rectangle_selection.get_height(), 1); + + const OpenGLManager::EFramebufferType framebuffers_type = OpenGLManager::get_framebuffers_type(); + bool use_framebuffer = framebuffers_type != OpenGLManager::EFramebufferType::Unknown; + + GLuint render_fbo = 0; + GLuint render_tex = 0; + GLuint render_depth = 0; + if (use_framebuffer) { + // setup a framebuffer which covers only the selection rectangle + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glGenFramebuffers(1, &render_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, render_fbo)); + } + else { + glsafe(::glGenFramebuffersEXT(1, &render_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, render_fbo)); + } + glsafe(::glGenTextures(1, &render_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, render_tex)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffers(1, &render_depth)); + glsafe(::glBindRenderbuffer(GL_RENDERBUFFER, render_depth)); + glsafe(::glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, render_depth)); + } + else { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, render_tex, 0)); + glsafe(::glGenRenderbuffersEXT(1, &render_depth)); + glsafe(::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render_depth)); + glsafe(::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, width, height)); + glsafe(::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render_depth)); + } + const GLenum drawBufs[] = { GL_COLOR_ATTACHMENT0 }; + glsafe(::glDrawBuffers(1, drawBufs)); + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + if (::glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) + use_framebuffer = false; + } + else { + if (::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) != GL_FRAMEBUFFER_COMPLETE_EXT) + use_framebuffer = false; + } + } +#endif // ENABLE_RAYCAST_PICKING + if (m_multisample_allowed) // This flag is often ignored by NVIDIA drivers if rendering into a screen buffer. glsafe(::glDisable(GL_MULTISAMPLE)); @@ -5576,10 +5642,49 @@ void GLCanvas3D::_rectangular_selection_picking_pass() glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); +#if ENABLE_RAYCAST_PICKING + const Camera& main_camera = wxGetApp().plater()->get_camera(); + Camera framebuffer_camera; + const Camera* camera = &main_camera; + if (use_framebuffer) { + // setup a camera which covers only the selection rectangle + const std::array& viewport = camera->get_viewport(); + const double near_left = camera->get_near_left(); + const double near_bottom = camera->get_near_bottom(); + const double near_width = camera->get_near_width(); + const double near_height = camera->get_near_height(); + + const double ratio_x = near_width / double(viewport[2]); + const double ratio_y = near_height / double(viewport[3]); + + const double rect_near_left = near_left + double(m_rectangle_selection.get_left()) * ratio_x; + const double rect_near_bottom = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_bottom())) * ratio_y; + double rect_near_right = near_left + double(m_rectangle_selection.get_right()) * ratio_x; + double rect_near_top = near_bottom + (double(viewport[3]) - double(m_rectangle_selection.get_top())) * ratio_y; + + if (rect_near_left == rect_near_right) + rect_near_right = rect_near_left + ratio_x; + if (rect_near_bottom == rect_near_top) + rect_near_top = rect_near_bottom + ratio_y; + + framebuffer_camera.look_at(camera->get_position(), camera->get_target(), camera->get_dir_up()); + framebuffer_camera.apply_projection(rect_near_left, rect_near_right, rect_near_bottom, rect_near_top, camera->get_near_z(), camera->get_far_z()); + framebuffer_camera.set_viewport(0, 0, width, height); + framebuffer_camera.apply_viewport(); + camera = &framebuffer_camera; + } + + _render_volumes_for_picking(*camera); +#else _render_volumes_for_picking(); +#endif // ENABLE_RAYCAST_PICKING #if ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + _render_bed_for_picking(camera->get_view_matrix(), camera->get_projection_matrix(), !camera->is_looking_downward()); +#else const Camera& camera = wxGetApp().plater()->get_camera(); _render_bed_for_picking(camera.get_view_matrix(), camera.get_projection_matrix(), !camera.is_looking_downward()); +#endif // ENABLE_RAYCAST_PICKING #else _render_bed_for_picking(!wxGetApp().plater()->get_camera().is_looking_downward()); #endif // ENABLE_LEGACY_OPENGL_REMOVAL @@ -5587,6 +5692,12 @@ void GLCanvas3D::_rectangular_selection_picking_pass() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); +#if ENABLE_RAYCAST_PICKING + const size_t px_count = width * height; + + const size_t left = use_framebuffer ? 0 : (size_t)m_rectangle_selection.get_left(); + const size_t top = use_framebuffer ? 0 : (size_t)get_canvas_size().get_height() - (size_t)m_rectangle_selection.get_top(); +#else int width = std::max((int)m_rectangle_selection.get_width(), 1); int height = std::max((int)m_rectangle_selection.get_height(), 1); int px_count = width * height; @@ -5594,6 +5705,7 @@ void GLCanvas3D::_rectangular_selection_picking_pass() 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) { +#endif // ENABLE_RAYCAST_PICKING #define USE_PARALLEL 1 #if USE_PARALLEL struct Pixel @@ -5635,7 +5747,30 @@ void GLCanvas3D::_rectangular_selection_picking_pass() idxs.insert(volume_id); } #endif // USE_PARALLEL +#if ENABLE_RAYCAST_PICKING + if (camera != &main_camera) + main_camera.apply_viewport(); + + if (framebuffers_type == OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffers(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffers(1, &render_fbo)); + } + else if (framebuffers_type == OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + if (render_depth != 0) + glsafe(::glDeleteRenderbuffersEXT(1, &render_depth)); + if (render_fbo != 0) + glsafe(::glDeleteFramebuffersEXT(1, &render_fbo)); + } + + if (render_tex != 0) + glsafe(::glDeleteTextures(1, &render_tex)); +#else } +#endif // ENABLE_RAYCAST_PICKING } m_hover_volume_idxs.assign(idxs.begin(), idxs.end()); @@ -6038,7 +6173,11 @@ void GLCanvas3D::_render_overlays() #endif // !ENABLE_LEGACY_OPENGL_REMOVAL } +#if ENABLE_RAYCAST_PICKING +void GLCanvas3D::_render_volumes_for_picking(const Camera& camera) const +#else void GLCanvas3D::_render_volumes_for_picking() const +#endif // ENABLE_RAYCAST_PICKING { #if ENABLE_LEGACY_OPENGL_REMOVAL GLShaderProgram* shader = wxGetApp().get_shader("flat_clip"); @@ -6054,7 +6193,11 @@ void GLCanvas3D::_render_volumes_for_picking() const glsafe(::glEnableClientState(GL_NORMAL_ARRAY)); #endif // !ENABLE_LEGACY_OPENGL_REMOVAL +#if ENABLE_RAYCAST_PICKING + const Transform3d& view_matrix = camera.get_view_matrix(); +#else const Transform3d& view_matrix = wxGetApp().plater()->get_camera().get_view_matrix(); +#endif // ENABLE_RAYCAST_PICKING for (size_t type = 0; type < 2; ++ type) { GLVolumeWithIdAndZList to_render = volumes_to_render(m_volumes.volumes, (type == 0) ? GLVolumeCollection::ERenderType::Opaque : GLVolumeCollection::ERenderType::Transparent, view_matrix); for (const GLVolumeWithIdAndZ& volume : to_render) @@ -6066,8 +6209,12 @@ void GLCanvas3D::_render_volumes_for_picking() const #if ENABLE_LEGACY_OPENGL_REMOVAL volume.first->model.set_color(picking_decode(id)); shader->start_using(); +#if ENABLE_RAYCAST_PICKING + shader->set_uniform("view_model_matrix", view_matrix * volume.first->world_matrix()); +#else const Camera& camera = wxGetApp().plater()->get_camera(); shader->set_uniform("view_model_matrix", camera.get_view_matrix() * volume.first->world_matrix()); +#endif // ENABLE_RAYCAST_PICKING shader->set_uniform("projection_matrix", camera.get_projection_matrix()); shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("z_range", m_volumes.get_z_range()); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 22c237e29..8f4efffd8 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -997,7 +997,11 @@ private: #endif // ENABLE_RENDER_SELECTION_CENTER void _check_and_update_toolbar_icon_scale(); void _render_overlays(); +#if ENABLE_RAYCAST_PICKING + void _render_volumes_for_picking(const Camera& camera) const; +#else void _render_volumes_for_picking() const; +#endif // ENABLE_RAYCAST_PICKING void _render_current_gizmo() const; void _render_gizmos_overlay(); void _render_main_toolbar();