diff --git a/resources/shaders/110/gouraud.fs b/resources/shaders/110/gouraud.fs index 6f354ff9a6..e602d6067d 100644 --- a/resources/shaders/110/gouraud.fs +++ b/resources/shaders/110/gouraud.fs @@ -36,6 +36,9 @@ uniform SlopeDetection slope; //BBS: add outline_color uniform bool is_outline; +uniform sampler2D depth_tex; +uniform vec2 screen_size; + #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -44,6 +47,9 @@ uniform bool is_outline; uniform PrintVolumeDetection print_volume; +uniform float z_far; +uniform float z_near; + varying vec3 clipping_planes_dots; varying float color_clip_plane_dot; @@ -54,6 +60,71 @@ varying vec4 world_pos; varying float world_normal_z; varying vec3 eye_normal; +vec3 getBackfaceColor(vec3 fill) { + float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b; + return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988); +} + +// Silhouette edge detection & rendering algorithem by leoneruggiero +// https://www.shadertoy.com/view/DslXz2 +#define INFLATE 1 + +float GetTolerance(float d, float k) +{ + // ------------------------------------------- + // Find a tolerance for depth that is constant + // in view space (k in view space). + // + // tol = k*ddx(ZtoDepth(z)) + // ------------------------------------------- + + float A=- (z_far+z_near)/(z_far-z_near); + float B=-2.0*z_far*z_near /(z_far-z_near); + + d = d*2.0-1.0; + + return -k*(d+A)*(d+A)/B; +} + +float DetectSilho(vec2 fragCoord, vec2 dir) +{ + // ------------------------------------------- + // x0 ___ x1----o + // :\ : + // r0 : \ : r1 + // : \ : + // o---x2 ___ x3 + // + // r0 and r1 are the differences between actual + // and expected (as if x0..3 where on the same + // plane) depth values. + // ------------------------------------------- + + float x0 = abs(texture2D(depth_tex, (fragCoord + dir*-2.0) / screen_size).r); + float x1 = abs(texture2D(depth_tex, (fragCoord + dir*-1.0) / screen_size).r); + float x2 = abs(texture2D(depth_tex, (fragCoord + dir* 0.0) / screen_size).r); + float x3 = abs(texture2D(depth_tex, (fragCoord + dir* 1.0) / screen_size).r); + + float d0 = (x1-x0); + float d1 = (x2-x3); + + float r0 = x1 + d0 - x2; + float r1 = x2 + d1 - x1; + + float tol = GetTolerance(x2, 0.04); + + return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0)); + +} + +float DetectSilho(vec2 fragCoord) +{ + return max( + DetectSilho(fragCoord, vec2(1,0)), // Horizontal + DetectSilho(fragCoord, vec2(0,1)) // Vertical + ); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -94,10 +165,20 @@ void main() pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); } color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; - + //BBS: add outline_color - if (is_outline) - gl_FragColor = uniform_color; + if (is_outline) { + color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); + vec2 fragCoord = gl_FragCoord.xy; + float s = DetectSilho(fragCoord); + // Makes silhouettes thicker. + for(int i=1;i<=INFLATE; i++) + { + s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0))); + s = max(s, DetectSilho(fragCoord.xy + vec2(0, i))); + } + gl_FragColor = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a); + } #ifdef ENABLE_ENVIRONMENT_MAP else if (use_environment_tex) gl_FragColor = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); diff --git a/resources/shaders/140/gouraud.fs b/resources/shaders/140/gouraud.fs index 84bce5c035..bbfb76f7a1 100644 --- a/resources/shaders/140/gouraud.fs +++ b/resources/shaders/140/gouraud.fs @@ -36,6 +36,8 @@ uniform SlopeDetection slope; //BBS: add outline_color uniform bool is_outline; +uniform sampler2D depth_tex; +uniform vec2 screen_size; #ifdef ENABLE_ENVIRONMENT_MAP uniform sampler2D environment_tex; @@ -44,6 +46,9 @@ uniform bool is_outline; uniform PrintVolumeDetection print_volume; +uniform float z_far; +uniform float z_near; + in vec3 clipping_planes_dots; in float color_clip_plane_dot; @@ -54,6 +59,71 @@ in vec4 world_pos; in float world_normal_z; in vec3 eye_normal; +vec3 getBackfaceColor(vec3 fill) { + float brightness = 0.2126 * fill.r + 0.7152 * fill.g + 0.0722 * fill.b; + return (brightness > 0.75) ? vec3(0.11, 0.165, 0.208) : vec3(0.988, 0.988, 0.988); +} + +// Silhouette edge detection & rendering algorithem by leoneruggiero +// https://www.shadertoy.com/view/DslXz2 +#define INFLATE 1 + +float GetTolerance(float d, float k) +{ + // ------------------------------------------- + // Find a tolerance for depth that is constant + // in view space (k in view space). + // + // tol = k*ddx(ZtoDepth(z)) + // ------------------------------------------- + + float A=- (z_far+z_near)/(z_far-z_near); + float B=-2.0*z_far*z_near /(z_far-z_near); + + d = d*2.0-1.0; + + return -k*(d+A)*(d+A)/B; +} + +float DetectSilho(vec2 fragCoord, vec2 dir) +{ + // ------------------------------------------- + // x0 ___ x1----o + // :\ : + // r0 : \ : r1 + // : \ : + // o---x2 ___ x3 + // + // r0 and r1 are the differences between actual + // and expected (as if x0..3 where on the same + // plane) depth values. + // ------------------------------------------- + + float x0 = abs(texture(depth_tex, (fragCoord + dir*-2.0) / screen_size).r); + float x1 = abs(texture(depth_tex, (fragCoord + dir*-1.0) / screen_size).r); + float x2 = abs(texture(depth_tex, (fragCoord + dir* 0.0) / screen_size).r); + float x3 = abs(texture(depth_tex, (fragCoord + dir* 1.0) / screen_size).r); + + float d0 = (x1-x0); + float d1 = (x2-x3); + + float r0 = x1 + d0 - x2; + float r1 = x2 + d1 - x1; + + float tol = GetTolerance(x2, 0.04); + + return smoothstep(0.0, tol*tol, max( - r0*r1, 0.0)); + +} + +float DetectSilho(vec2 fragCoord) +{ + return max( + DetectSilho(fragCoord, vec2(1,0)), // Horizontal + DetectSilho(fragCoord, vec2(0,1)) // Vertical + ); +} + out vec4 out_color; void main() @@ -96,10 +166,20 @@ void main() pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); } color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; - + //BBS: add outline_color - if (is_outline) - out_color = uniform_color; + if (is_outline) { + color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); + vec2 fragCoord = gl_FragCoord.xy; + float s = DetectSilho(fragCoord); + // Makes silhouettes thicker. + for(int i=1;i<=INFLATE; i++) + { + s = max(s, DetectSilho(fragCoord.xy + vec2(i, 0))); + s = max(s, DetectSilho(fragCoord.xy + vec2(0, i))); + } + out_color = vec4(mix(color.rgb, getBackfaceColor(color.rgb), s), color.a); + } #ifdef ENABLE_ENVIRONMENT_MAP else if (use_environment_tex) out_color = vec4(0.45 * texture(environment_tex, normalize(eye_normal).xy * 0.5 + 0.5).xyz + 0.8 * color.rgb * intensity.x, color.a); diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 7d114b45fc..0decfaac12 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -200,6 +200,8 @@ void AppConfig::set_defaults() if (get("show_3d_navigator").empty()) set_bool("show_3d_navigator", true); + if (get("show_outline").empty()) + set_bool("show_outline", false); #ifdef _WIN32 diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 86ab5d2239..dfd914f427 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -423,7 +423,7 @@ void GLVolume::render() } //BBS: add outline related logic -void GLVolume::render_with_outline(const Transform3d &view_model_matrix) +void GLVolume::render_with_outline(const GUI::Size& cnv_size) { if (!is_active) return; @@ -435,37 +435,79 @@ void GLVolume::render_with_outline(const Transform3d &view_model_matrix) ModelObjectPtrs &model_objects = GUI::wxGetApp().model().objects; std::vector colors = get_extruders_colors(); - glEnable(GL_STENCIL_TEST); - glStencilMask(0xFF); - glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE); - glClear(GL_STENCIL_BUFFER_BIT); - glStencilFunc(GL_ALWAYS, 0xff, 0xFF); + const GUI::OpenGLManager::EFramebufferType framebuffers_type = GUI::OpenGLManager::get_framebuffers_type(); + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Unknown) { + // No supported, degrade to normal rendering + simple_render(shader, model_objects, colors); + return; + } - simple_render(shader, model_objects, colors); + // 1st. render pass, render the model into a separate render target that has only depth buffer + GLuint depth_fbo = 0; + GLuint depth_tex = 0; + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glGenFramebuffers(1, &depth_fbo)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo)); - // 2nd. render pass: now draw slightly scaled versions of the objects, this time disabling stencil writing. - // Because the stencil buffer is now filled with several 1s. The parts of the buffer that are 1 are not drawn, thus only drawing - // the objects' size differences, making it look like borders. - glStencilFunc(GL_NOTEQUAL, 0xff, 0xFF); - glStencilMask(0x00); - float scale = 1.02f; - ColorRGBA body_color = { 1.0f, 1.0f, 1.0f, 1.0f }; //red + glActiveTexture(GL_TEXTURE0); + glsafe(::glGenTextures(1, &depth_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr)); - model.set_color(body_color); - shader->set_uniform("is_outline", true); + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0)); + } else { + glsafe(::glGenFramebuffersEXT(1, &depth_fbo)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo)); - Transform3d matrix = view_model_matrix; - matrix.scale(scale); - shader->set_uniform("view_model_matrix", matrix); + glActiveTexture(GL_TEXTURE0); + glsafe(::glGenTextures(1, &depth_tex)); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr)); + + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0)); + } + glsafe(::glClear(GL_DEPTH_BUFFER_BIT)); if (tverts_range == std::make_pair(0, -1)) model.render(); else model.render(this->tverts_range); + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); - shader->set_uniform("view_model_matrix", view_model_matrix); + // 2nd. render pass, just a normal render with the depth buffer passed as a texture + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + } else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + } + shader->set_uniform("is_outline", true); + shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()}); + glActiveTexture(GL_TEXTURE0); + glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex)); + shader->set_uniform("depth_tex", 0); + simple_render(shader, model_objects, colors); + + // Some clean up to do + glsafe(::glBindTexture(GL_TEXTURE_2D, 0)); shader->set_uniform("is_outline", false); - - glDisable(GL_STENCIL_TEST); + if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) { + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0)); + if (depth_fbo != 0) + glsafe(::glDeleteFramebuffers(1, &depth_fbo)); + } else if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Ext) { + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0)); + if (depth_fbo != 0) + glsafe(::glDeleteFramebuffersEXT(1, &depth_fbo)); + } + if (depth_tex != 0) + glsafe(::glDeleteTextures(1, &depth_tex)); } //BBS add render for simple case @@ -847,8 +889,8 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_suppo } //BBS: add outline drawing logic -void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, - std::function filter_func, bool with_outline) const +void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size, + std::function filter_func) const { GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func); if (to_render.empty()) @@ -953,9 +995,9 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disab const Matrix3d view_normal_matrix = view_matrix.matrix().block(0, 0, 3, 3) * model_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); shader->set_uniform("view_normal_matrix", view_normal_matrix); //BBS: add outline related logic - //if (with_outline && volume.first->selected) - // volume.first->render_with_outline(view_matrix * model_matrix); - //else + if (volume.first->selected && GUI::wxGetApp().show_outline()) + volume.first->render_with_outline(cnv_size); + else volume.first->render(); #if ENABLE_ENVIRONMENT_MAP diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 4479c24632..cd89efa36a 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -39,6 +39,10 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C namespace Slic3r { +namespace GUI { + class Size; +} + class SLAPrintObject; enum SLAPrintObjectStep : unsigned int; class BuildVolume; @@ -322,7 +326,7 @@ public: virtual void render(); //BBS: add outline related logic and add virtual specifier - virtual void render_with_outline(const Transform3d &view_model_matrix); + virtual void render_with_outline(const GUI::Size& cnv_size); //BBS: add simple render function for thumbnail void simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, std::vector& extruder_colors, bool ban_light =false); @@ -355,7 +359,7 @@ class GLWipeTowerVolume : public GLVolume { public: GLWipeTowerVolume(const std::vector& colors); void render() override; - void render_with_outline(const Transform3d &view_model_matrix) override { render(); } + void render_with_outline(const GUI::Size& cnv_size) override { render(); } std::vector model_per_colors; bool IsTransparent(); @@ -465,8 +469,8 @@ public: int get_selection_support_threshold_angle(bool&) const; // Render the volumes by OpenGL. //BBS: add outline drawing logic - void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, - std::function filter_func = std::function(), bool with_outline = true) const; + void render(ERenderType type, bool disable_cullface, const Transform3d& view_matrix, const Transform3d& projection_matrix, const GUI::Size& cnv_size, + std::function filter_func = std::function()) const; // Clear the geometry void clear() { for (auto *v : volumes) delete v; volumes.clear(); } diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 0d649b9a6a..88aa496129 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1244,7 +1244,7 @@ void GCodeViewer::render(int canvas_width, int canvas_height, int right_margin) #endif // ENABLE_GCODE_VIEWER_STATISTICS glsafe(::glEnable(GL_DEPTH_TEST)); - render_shells(); + render_shells(canvas_width, canvas_height); if (m_roles.empty()) return; @@ -4023,7 +4023,7 @@ void GCodeViewer::render_toolpaths() } } -void GCodeViewer::render_shells() +void GCodeViewer::render_shells(int canvas_width, int canvas_height) { //BBS: add shell previewing logic if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty()) @@ -4039,7 +4039,9 @@ void GCodeViewer::render_shells() shader->start_using(); shader->set_uniform("emission_factor", 0.1f); const Camera& camera = wxGetApp().plater()->get_camera(); - m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix()); + shader->set_uniform("z_far", camera.get_far_z()); + shader->set_uniform("z_near", camera.get_near_z()); + m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera.get_view_matrix(), camera.get_projection_matrix(), {canvas_width, canvas_height}); shader->set_uniform("emission_factor", 0.0f); shader->stop_using(); diff --git a/src/slic3r/GUI/GCodeViewer.hpp b/src/slic3r/GUI/GCodeViewer.hpp index 0d730bb0f9..18073c6a96 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -893,7 +893,7 @@ private: //void load_shells(const Print& print); void refresh_render_paths(bool keep_sequential_current_first, bool keep_sequential_current_last) const; void render_toolpaths(); - void render_shells(); + void render_shells(int canvas_width, int canvas_height); //BBS: GUI refactor: add canvas size void render_legend(float &legend_height, int canvas_width, int canvas_height, int right_margin); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 551697e26f..2faf3a8dae 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -7234,6 +7234,12 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with if (shader != nullptr) { shader->start_using(); + const Size& cvn_size = get_canvas_size(); + { + const Camera& camera = wxGetApp().plater()->get_camera(); + shader->set_uniform("z_far", camera.get_far_z()); + shader->set_uniform("z_near", camera.get_near_z()); + } switch (type) { default: @@ -7245,7 +7251,7 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with if (m_picking_enabled && m_layers_editing.is_enabled() && (m_layers_editing.last_object_id != -1) && (m_layers_editing.object_max_z() > 0.0f)) { int object_id = m_layers_editing.last_object_id; const Camera& camera = wxGetApp().plater()->get_camera(); - m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [object_id](const GLVolume& volume) { + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [object_id](const GLVolume& volume) { // Which volume to paint without the layer height profile shader? return volume.is_active && (volume.is_modifier || volume.composite_id.object_id != object_id); }); @@ -7261,14 +7267,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with //BBS:add assemble view related logic // do not cull backfaces to show broken geometry, if any const Camera& camera = wxGetApp().plater()->get_camera(); - m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, m_picking_enabled, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier && !volume.is_wipe_tower; } else { return (m_render_sla_auxiliaries || volume.composite_id.volume_id >= 0); } - }, with_outline); + }); } } else { @@ -7295,14 +7301,14 @@ void GLCanvas3D::_render_objects(GLVolumeCollection::ERenderType type, bool with }*/ const Camera& camera = wxGetApp().plater()->get_camera(); //BBS:add assemble view related logic - m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, false, camera.get_view_matrix(), camera.get_projection_matrix(), cvn_size, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier; } else { return true; } - }, with_outline); + }); if (m_canvas_type == CanvasAssembleView && m_gizmos.m_assemble_view_data->model_objects_clipper()->get_position() > 0) { const GLGizmosManager& gm = get_gizmos_manager(); shader->stop_using(); diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index 44d430d2d1..e4d735448c 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -344,6 +344,9 @@ private: bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); } void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); } + bool show_outline() const { return app_config->get_bool("show_outline"); } + void toggle_show_outline() const { app_config->set_bool("show_outline", !show_outline()); } + wxString get_inf_dialog_contect () {return m_info_dialog_content;}; std::vector split_str(std::string src, std::string separator); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 47d7c30e86..48212f45cb 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2642,6 +2642,16 @@ void MainFrame::init_menubar_as_editor() m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this, [this]() { return m_plater->is_view3D_shown(); }, [this]() { return m_plater->is_view3D_overhang_shown(); }, this); + + append_menu_check_item( + viewMenu, wxID_ANY, _L("Show Selected Outline (Experimental)"), _L("Show outline around selected object in 3D scene"), + [this](wxCommandEvent&) { + wxGetApp().toggle_show_outline(); + m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); + }, + this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor; }, + [this]() { return wxGetApp().show_outline(); }, this); + /*viewMenu->AppendSeparator(); append_menu_check_item(viewMenu, wxID_ANY, _L("Show &Wireframe") + "\tCtrl+Shift+Enter", _L("Show wireframes in 3D scene"), [this](wxCommandEvent&) { m_plater->toggle_show_wireframe(); m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT)); }, this,