diff --git a/resources/shaders/110/gouraud.fs b/resources/shaders/110/gouraud.fs index e602d6067d3..b4bec9651ec 100644 --- a/resources/shaders/110/gouraud.fs +++ b/resources/shaders/110/gouraud.fs @@ -34,11 +34,6 @@ uniform vec4 uniform_color_clip_plane_1; uniform vec4 uniform_color_clip_plane_2; 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; @@ -47,9 +42,6 @@ uniform vec2 screen_size; uniform PrintVolumeDetection print_volume; -uniform float z_far; -uniform float z_near; - varying vec3 clipping_planes_dots; varying float color_clip_plane_dot; @@ -60,71 +52,6 @@ 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))) @@ -166,23 +93,10 @@ void main() } 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) { - 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) + 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); -#endif else +#endif gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); -} \ No newline at end of file +} diff --git a/resources/shaders/110/selection_composite.fs b/resources/shaders/110/selection_composite.fs new file mode 100644 index 00000000000..565b7223f28 --- /dev/null +++ b/resources/shaders/110/selection_composite.fs @@ -0,0 +1,32 @@ +#version 110 + +uniform sampler2D mask_texture; +uniform sampler2D edge_texture; +uniform sampler2D glow_texture; +uniform vec3 outline_color; + +const float fillAlpha = 0.4; +const float outlineExclusionLow = 0.25; +const float outlineExclusionHigh = 0.75; +const float edgeStrength = 3.0; +const float edgeGlow = 0.8; + +varying vec2 tex_coord; + +void main() +{ + float coverage = texture2D(mask_texture, tex_coord).a; + float edgeCoverage = texture2D(edge_texture, tex_coord).a; + float glowCoverage = texture2D(glow_texture, tex_coord).a; + float outlineExclusion = smoothstep(outlineExclusionLow, outlineExclusionHigh, coverage); + float outsideFill = 1.0 - outlineExclusion; + float fillCoverage = coverage * fillAlpha; + float edgeAlpha = clamp(edgeStrength * edgeCoverage * outsideFill, 0.0, 1.0); + float glowAlpha = clamp(edgeStrength * edgeGlow * glowCoverage * outsideFill, 0.0, 1.0); + + // Precompose normal-alpha Fill and Edge followed by additive Glow. + float compositeAlpha = fillCoverage + edgeAlpha * (1.0 - fillCoverage); + vec3 compositeColor = vec3(fillAlpha) * fillCoverage * (1.0 - edgeAlpha) + + outline_color * (edgeAlpha + glowAlpha); + gl_FragColor = vec4(compositeColor, compositeAlpha); +} diff --git a/resources/shaders/110/selection_edge.fs b/resources/shaders/110/selection_edge.fs new file mode 100644 index 00000000000..e923780a1bb --- /dev/null +++ b/resources/shaders/110/selection_edge.fs @@ -0,0 +1,19 @@ +#version 110 + +uniform sampler2D mask_texture; +uniform vec2 inverse_texture_size; + +varying vec2 tex_coord; + +void main() +{ + float leftCoverage = texture2D(mask_texture, tex_coord - vec2(inverse_texture_size.x, 0.0)).a; + float rightCoverage = texture2D(mask_texture, tex_coord + vec2(inverse_texture_size.x, 0.0)).a; + float bottomCoverage = texture2D(mask_texture, tex_coord - vec2(0.0, inverse_texture_size.y)).a; + float topCoverage = texture2D(mask_texture, tex_coord + vec2(0.0, inverse_texture_size.y)).a; + float horizontalEdge = (rightCoverage - leftCoverage) * 0.5; + float verticalEdge = (topCoverage - bottomCoverage) * 0.5; + float edge = length(vec2(horizontalEdge, verticalEdge)); + + gl_FragColor = vec4(0.0, 0.0, 0.0, edge); +} diff --git a/resources/shaders/110/selection_gaussian.fs b/resources/shaders/110/selection_gaussian.fs new file mode 100644 index 00000000000..fdbbb108611 --- /dev/null +++ b/resources/shaders/110/selection_gaussian.fs @@ -0,0 +1,41 @@ +#version 110 + +uniform sampler2D source_texture; +uniform vec2 inverse_texture_size; +uniform vec2 blur_direction; +uniform float blur_radius; + +varying vec2 tex_coord; + +float gaussianPdf(float x, float sigma) +{ + return 0.39894 * exp(-0.5 * x * x / (sigma * sigma)) / sigma; +} + +void main() +{ + if (blur_radius <= 0.0) + { + gl_FragColor = vec4(0.0, 0.0, 0.0, texture2D(source_texture, tex_coord).a); + return; + } + + const int maxBlurRadius = 4; + float sigma = max(blur_radius * 0.5, 0.001); + float weightSum = gaussianPdf(0.0, sigma); + float blurred = texture2D(source_texture, tex_coord).a * weightSum; + vec2 stepUv = blur_direction * inverse_texture_size * blur_radius / float(maxBlurRadius); + vec2 sampleOffset = stepUv; + + for (int i = 1; i <= maxBlurRadius; ++i) + { + float offset = blur_radius * float(i) / float(maxBlurRadius); + float weight = gaussianPdf(offset, sigma); + blurred += (texture2D(source_texture, tex_coord + sampleOffset).a + + texture2D(source_texture, tex_coord - sampleOffset).a) * weight; + weightSum += 2.0 * weight; + sampleOffset += stepUv; + } + + gl_FragColor = vec4(0.0, 0.0, 0.0, blurred / weightSum); +} diff --git a/resources/shaders/110/selection_mask.fs b/resources/shaders/110/selection_mask.fs new file mode 100644 index 00000000000..f3c853bd683 --- /dev/null +++ b/resources/shaders/110/selection_mask.fs @@ -0,0 +1,8 @@ +#version 110 + +uniform vec3 output_color; + +void main() +{ + gl_FragColor = vec4(output_color, 1.0); +} diff --git a/resources/shaders/140/gouraud.fs b/resources/shaders/140/gouraud.fs index bbfb76f7a18..7e30f3185f7 100644 --- a/resources/shaders/140/gouraud.fs +++ b/resources/shaders/140/gouraud.fs @@ -34,11 +34,6 @@ uniform vec4 uniform_color_clip_plane_1; uniform vec4 uniform_color_clip_plane_2; 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; uniform bool use_environment_tex; @@ -46,9 +41,6 @@ uniform vec2 screen_size; uniform PrintVolumeDetection print_volume; -uniform float z_far; -uniform float z_near; - in vec3 clipping_planes_dots; in float color_clip_plane_dot; @@ -59,71 +51,6 @@ 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() @@ -167,23 +94,10 @@ void main() } 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) { - 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) + 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); -#endif else +#endif out_color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); -} \ No newline at end of file +} diff --git a/resources/shaders/140/selection_composite.fs b/resources/shaders/140/selection_composite.fs new file mode 100644 index 00000000000..07e3b1925e8 --- /dev/null +++ b/resources/shaders/140/selection_composite.fs @@ -0,0 +1,32 @@ +#version 140 + +uniform sampler2D mask_texture; +uniform sampler2D edge_texture; +uniform sampler2D glow_texture; +uniform vec3 outline_color; + +const float fillAlpha = 0.4; +const float outlineExclusionLow = 0.25; +const float outlineExclusionHigh = 0.75; +const float edgeStrength = 3.0; +const float edgeGlow = 0.8; + +in vec2 tex_coord; +out vec4 frag_color; + +void main() +{ + float coverage = texture(mask_texture, tex_coord).a; + float edgeCoverage = texture(edge_texture, tex_coord).a; + float glowCoverage = texture(glow_texture, tex_coord).a; + float outlineExclusion = smoothstep(outlineExclusionLow, outlineExclusionHigh, coverage); + float outsideFill = 1.0 - outlineExclusion; + float fillCoverage = coverage * fillAlpha; + float edgeAlpha = clamp(edgeStrength * edgeCoverage * outsideFill, 0.0, 1.0); + float glowAlpha = clamp(edgeStrength * edgeGlow * glowCoverage * outsideFill, 0.0, 1.0); + + // Precompose normal-alpha Fill and Edge followed by additive Glow. + float compositeAlpha = fillCoverage + edgeAlpha * (1.0 - fillCoverage); + vec3 compositeColor = vec3(fillAlpha) * fillCoverage * (1.0 - edgeAlpha) + outline_color * (edgeAlpha + glowAlpha); + frag_color = vec4(compositeColor, compositeAlpha); +} diff --git a/resources/shaders/140/selection_edge.fs b/resources/shaders/140/selection_edge.fs new file mode 100644 index 00000000000..9ca9196d948 --- /dev/null +++ b/resources/shaders/140/selection_edge.fs @@ -0,0 +1,20 @@ +#version 140 + +uniform sampler2D mask_texture; +uniform vec2 inverse_texture_size; + +in vec2 tex_coord; +out vec4 frag_color; + +void main() +{ + float leftCoverage = texture(mask_texture, tex_coord - vec2(inverse_texture_size.x, 0.0)).a; + float rightCoverage = texture(mask_texture, tex_coord + vec2(inverse_texture_size.x, 0.0)).a; + float bottomCoverage = texture(mask_texture, tex_coord - vec2(0.0, inverse_texture_size.y)).a; + float topCoverage = texture(mask_texture, tex_coord + vec2(0.0, inverse_texture_size.y)).a; + float horizontalEdge = (rightCoverage - leftCoverage) * 0.5; + float verticalEdge = (topCoverage - bottomCoverage) * 0.5; + float edge = length(vec2(horizontalEdge, verticalEdge)); + + frag_color = vec4(0.0, 0.0, 0.0, edge); +} diff --git a/resources/shaders/140/selection_gaussian.fs b/resources/shaders/140/selection_gaussian.fs new file mode 100644 index 00000000000..cd7b7110e11 --- /dev/null +++ b/resources/shaders/140/selection_gaussian.fs @@ -0,0 +1,42 @@ +#version 140 + +uniform sampler2D source_texture; +uniform vec2 inverse_texture_size; +uniform vec2 blur_direction; +uniform float blur_radius; + +in vec2 tex_coord; +out vec4 frag_color; + +float gaussianPdf(float x, float sigma) +{ + return 0.39894 * exp(-0.5 * x * x / (sigma * sigma)) / sigma; +} + +void main() +{ + if (blur_radius <= 0.0) + { + frag_color = vec4(0.0, 0.0, 0.0, texture(source_texture, tex_coord).a); + return; + } + + const int maxBlurRadius = 4; + float sigma = max(blur_radius * 0.5, 0.001); + float weightSum = gaussianPdf(0.0, sigma); + float blurred = texture(source_texture, tex_coord).a * weightSum; + vec2 stepUv = blur_direction * inverse_texture_size * blur_radius / float(maxBlurRadius); + vec2 sampleOffset = stepUv; + + for (int i = 1; i <= maxBlurRadius; ++i) + { + float offset = blur_radius * float(i) / float(maxBlurRadius); + float weight = gaussianPdf(offset, sigma); + blurred += (texture(source_texture, tex_coord + sampleOffset).a + + texture(source_texture, tex_coord - sampleOffset).a) * weight; + weightSum += 2.0 * weight; + sampleOffset += stepUv; + } + + frag_color = vec4(0.0, 0.0, 0.0, blurred / weightSum); +} diff --git a/resources/shaders/140/selection_mask.fs b/resources/shaders/140/selection_mask.fs new file mode 100644 index 00000000000..1c2257627e3 --- /dev/null +++ b/resources/shaders/140/selection_mask.fs @@ -0,0 +1,10 @@ +#version 140 + +uniform vec3 output_color; + +out vec4 frag_color; + +void main() +{ + frag_color = vec4(output_color, 1.0); +} diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp index 440ac33387c..893c8e832f6 100644 --- a/src/libslic3r/AppConfig.cpp +++ b/src/libslic3r/AppConfig.cpp @@ -219,9 +219,6 @@ 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 //#ifdef SUPPORT_3D_CONNEXION diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 67c9709bbc2..831ab1d35b4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -415,96 +415,6 @@ void GLVolume::render() simple_render(shader, model_objects, colors); } -// BBS: add outline related logic -void GLVolume::render_with_outline(const GUI::Size& cnv_size) -{ - if (!is_active) - return; - - GLShaderProgram* shader = GUI::wxGetApp().get_current_shader(); - if (shader == nullptr) - return; - - ModelObjectPtrs& model_objects = GUI::wxGetApp().model().objects; - std::vector colors = get_extruders_colors(); - - 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; - } - - // 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)); - - 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, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0)); - } else { - glsafe(::glGenFramebuffersEXT(1, &depth_fbo)); - glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo)); - - 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)); - - // 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); - 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 void GLVolume::simple_render(GLShaderProgram* shader, ModelObjectPtrs& model_objects, @@ -875,11 +785,9 @@ int GLVolumeCollection::get_selection_support_threshold_angle(bool& enable_suppo return support_threshold_angle; } -// BBS: add outline drawing logic void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, bool disable_cullface, const GUI::Camera& camera, - const GUI::Size& cnv_size, std::function filter_func, bool partly_inside_enable) const { @@ -948,10 +856,6 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, shader->set_uniform("color_clip_plane", m_color_clip_plane); shader->set_uniform("uniform_color_clip_plane_1", m_color_clip_plane_colors[0]); shader->set_uniform("uniform_color_clip_plane_2", m_color_clip_plane_colors[1]); - // BOOST_LOG_TRIVIAL(info) << boost::format("set uniform_color to {%1%, %2%, %3%, %4%}, with_outline=%5%, selected %6%") - // %volume.first->render_color[0]%volume.first->render_color[1]%volume.first->render_color[2]%volume.first->render_color[3] - // %with_outline%volume.first->selected; - // BBS set print_volume to render volume // shader->set_uniform("print_volume.type", static_cast(m_render_volume.type)); // shader->set_uniform("print_volume.xy_data", m_render_volume.data); @@ -995,11 +899,7 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, 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 (volume.first->selected && GUI::wxGetApp().show_outline()) - volume.first->render_with_outline(cnv_size); - else - volume.first->render(); + volume.first->render(); #if ENABLE_ENVIRONMENT_MAP if (use_environment_texture) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 68c6ace8c6b..bd526190be8 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -40,7 +40,6 @@ extern Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::C namespace Slic3r { namespace GUI { - class Size; class Camera; } @@ -328,9 +327,6 @@ class GLVolume { virtual void render(); - //BBS: add outline related logic and add virtual specifier - 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); @@ -362,7 +358,6 @@ class GLWipeTowerVolume : public GLVolume { public: GLWipeTowerVolume(const std::vector& colors); void render() override; - void render_with_outline(const GUI::Size& cnv_size) override { render(); } std::vector model_per_colors; bool IsTransparent(); @@ -473,13 +468,11 @@ class GLVolumeCollection 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 GUI::Camera& camera, - const GUI::Size& cnv_size, - std::function filter_func = std::function(), - bool partly_inside_enable =true + bool disable_cullface, + const GUI::Camera& camera, + std::function filter_func = std::function(), + bool partly_inside_enable =true ) const; // Clear the geometry diff --git a/src/slic3r/GUI/GCodeViewer.cpp b/src/slic3r/GUI/GCodeViewer.cpp index 358f2a8ca13..5b362bccab0 100644 --- a/src/slic3r/GUI/GCodeViewer.cpp +++ b/src/slic3r/GUI/GCodeViewer.cpp @@ -1363,7 +1363,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(canvas_width, canvas_height); + render_shells(); if (m_roles.empty()) return; @@ -4083,7 +4083,7 @@ void GCodeViewer::render_toolpaths() } } -void GCodeViewer::render_shells(int canvas_width, int canvas_height) +void GCodeViewer::render_shells() { //BBS: add shell previewing logic if ((!m_shells.previewing && !m_shells.visible) || m_shells.volumes.empty()) @@ -4099,9 +4099,7 @@ void GCodeViewer::render_shells(int canvas_width, int canvas_height) shader->start_using(); shader->set_uniform("emission_factor", 0.1f); 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()); - m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera, {canvas_width, canvas_height}); + m_shells.volumes.render(GLVolumeCollection::ERenderType::Transparent, false, camera); 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 12c3a3fce39..bd26244d469 100644 --- a/src/slic3r/GUI/GCodeViewer.hpp +++ b/src/slic3r/GUI/GCodeViewer.hpp @@ -898,7 +898,7 @@ mutable bool m_no_render_path { false }; //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(int canvas_width, int canvas_height); + void render_shells(); //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 67cc9a477a2..f653dbab74c 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -101,6 +101,346 @@ static constexpr const size_t MAX_VERTEX_BUFFER_SIZE = 131072 * 6; // 3.15MB namespace Slic3r { namespace GUI { +namespace { + +constexpr float SELECTION_MASK_SCALE = 0.5f; +constexpr float SELECTION_GLOW_SCALE = 0.5f; +constexpr float SELECTION_EDGE_THICKNESS = 1.0f; +constexpr float SELECTION_GLOW_BLUR_RADIUS = 4.0f; +constexpr double STENCIL_OUTLINE_SCALE = 1.02; +const ColorRGB SELECTION_OUTLINE_COLOR = ColorRGB::WHITE(); +const ColorRGB ASSEMBLE_VIEW_SELECTION_OUTLINE_COLOR{ 0.76f, 0.76f, 0.16f }; + +/** @brief Persistent OpenGL states changed by the selection Mask Pass. */ +struct MaskRenderState +{ + OpenGLManager::EFramebufferType framebufferType{ OpenGLManager::EFramebufferType::Unknown }; + GLboolean blendEnabled{ GL_FALSE }; + GLboolean cullFaceEnabled{ GL_FALSE }; + std::array colorWriteMask{ GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE }; + std::array clearColor{ 0.0f, 0.0f, 0.0f, 0.0f }; + GLint frontFace{ GL_CCW }; + GLint cullFaceMode{ GL_BACK }; + GLint activeTexture{ GL_TEXTURE0 }; + GLint texture0Binding2D{ 0 }; + GLint currentProgram{ 0 }; + GLint drawFramebuffer{ 0 }; + std::array viewport{ 0, 0, 0, 0 }; +}; + +/** + * @brief Captures the persistent OpenGL states changed by the selection Mask Pass. + * @param framebufferType Framebuffer API used by the current OpenGL context. + * @return Captured state used by RestoreMaskRenderState(). + */ +MaskRenderState SaveMaskRenderState(OpenGLManager::EFramebufferType framebufferType) +{ + MaskRenderState state; + state.framebufferType = framebufferType; + state.blendEnabled = glIsEnabled(GL_BLEND); + state.cullFaceEnabled = glIsEnabled(GL_CULL_FACE); + glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, state.colorWriteMask.data())); + glsafe(::glGetFloatv(GL_COLOR_CLEAR_VALUE, state.clearColor.data())); + glsafe(::glGetIntegerv(GL_FRONT_FACE, &state.frontFace)); + glsafe(::glGetIntegerv(GL_CULL_FACE_MODE, &state.cullFaceMode)); + glsafe(::glGetIntegerv(GL_ACTIVE_TEXTURE, &state.activeTexture)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.texture0Binding2D)); + glsafe(::glActiveTexture(static_cast(state.activeTexture))); + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &state.currentProgram)); + glsafe(::glGetIntegerv(GL_VIEWPORT, state.viewport.data())); + + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + glsafe(::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &state.drawFramebuffer)); + else if (framebufferType == OpenGLManager::EFramebufferType::Ext) + glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &state.drawFramebuffer)); + + return state; +} + +/** + * @brief Restores a state captured by SaveMaskRenderState(). + * @param state Previously captured OpenGL state. + */ +void RestoreMaskRenderState(const MaskRenderState& state) +{ + if (state.blendEnabled == GL_TRUE) + glsafe(::glEnable(GL_BLEND)); + else + glsafe(::glDisable(GL_BLEND)); + + if (state.cullFaceEnabled == GL_TRUE) + glsafe(::glEnable(GL_CULL_FACE)); + else + glsafe(::glDisable(GL_CULL_FACE)); + + glsafe(::glColorMask(state.colorWriteMask[0], state.colorWriteMask[1], + state.colorWriteMask[2], state.colorWriteMask[3])); + glsafe(::glClearColor(state.clearColor[0], state.clearColor[1], state.clearColor[2], state.clearColor[3])); + glsafe(::glFrontFace(static_cast(state.frontFace))); + glsafe(::glCullFace(static_cast(state.cullFaceMode))); + glsafe(::glUseProgram(static_cast(state.currentProgram))); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, static_cast(state.texture0Binding2D))); + glsafe(::glActiveTexture(static_cast(state.activeTexture))); + + if (state.framebufferType == OpenGLManager::EFramebufferType::Arb) + glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast(state.drawFramebuffer))); + else if (state.framebufferType == OpenGLManager::EFramebufferType::Ext) + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, static_cast(state.drawFramebuffer))); + + glsafe(::glViewport(state.viewport[0], state.viewport[1], state.viewport[2], state.viewport[3])); +} + +/** @brief Persistent OpenGL states changed by the selection Composite Pass. */ +struct CompositeRenderState +{ + GLboolean depthTestEnabled{ GL_FALSE }; + GLboolean blendEnabled{ GL_FALSE }; + GLboolean cullFaceEnabled{ GL_FALSE }; + GLint blendSourceRgb{ GL_ONE }; + GLint blendDestinationRgb{ GL_ZERO }; + GLint blendSourceAlpha{ GL_ONE }; + GLint blendDestinationAlpha{ GL_ZERO }; + GLint activeTexture{ GL_TEXTURE0 }; + GLint texture0Binding2D{ 0 }; + GLint texture1Binding2D{ 0 }; + GLint texture2Binding2D{ 0 }; + GLint currentProgram{ 0 }; +}; + +/** + * @brief Captures the persistent OpenGL states changed by the selection Composite Pass. + * @return Captured state used by RestoreCompositeRenderState(). + */ +CompositeRenderState SaveCompositeRenderState() +{ + CompositeRenderState state; + state.depthTestEnabled = glIsEnabled(GL_DEPTH_TEST); + state.blendEnabled = glIsEnabled(GL_BLEND); + state.cullFaceEnabled = glIsEnabled(GL_CULL_FACE); + glsafe(::glGetIntegerv(GL_BLEND_SRC_RGB, &state.blendSourceRgb)); + glsafe(::glGetIntegerv(GL_BLEND_DST_RGB, &state.blendDestinationRgb)); + glsafe(::glGetIntegerv(GL_BLEND_SRC_ALPHA, &state.blendSourceAlpha)); + glsafe(::glGetIntegerv(GL_BLEND_DST_ALPHA, &state.blendDestinationAlpha)); + glsafe(::glGetIntegerv(GL_ACTIVE_TEXTURE, &state.activeTexture)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.texture0Binding2D)); + glsafe(::glActiveTexture(GL_TEXTURE1)); + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.texture1Binding2D)); + glsafe(::glActiveTexture(GL_TEXTURE2)); + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &state.texture2Binding2D)); + glsafe(::glActiveTexture(static_cast(state.activeTexture))); + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &state.currentProgram)); + return state; +} + +/** + * @brief Restores a state captured by SaveCompositeRenderState(). + * @param state Previously captured OpenGL state. + */ +void RestoreCompositeRenderState(const CompositeRenderState& state) +{ + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, static_cast(state.texture0Binding2D))); + glsafe(::glActiveTexture(GL_TEXTURE1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, static_cast(state.texture1Binding2D))); + glsafe(::glActiveTexture(GL_TEXTURE2)); + glsafe(::glBindTexture(GL_TEXTURE_2D, static_cast(state.texture2Binding2D))); + glsafe(::glActiveTexture(static_cast(state.activeTexture))); + glsafe(::glBlendFuncSeparate(static_cast(state.blendSourceRgb), + static_cast(state.blendDestinationRgb), + static_cast(state.blendSourceAlpha), + static_cast(state.blendDestinationAlpha))); + glsafe(::glUseProgram(static_cast(state.currentProgram))); + + if (state.depthTestEnabled == GL_TRUE) + glsafe(::glEnable(GL_DEPTH_TEST)); + else + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (state.blendEnabled == GL_TRUE) + glsafe(::glEnable(GL_BLEND)); + else + glsafe(::glDisable(GL_BLEND)); + + if (state.cullFaceEnabled == GL_TRUE) + glsafe(::glEnable(GL_CULL_FACE)); + else + glsafe(::glDisable(GL_CULL_FACE)); +} + +/** + * @brief Binds a framebuffer as the active selection-highlight draw target. + * @param framebufferType Available framebuffer API implementation. + * @param framebuffer Target framebuffer object. + */ +void BindSelectionHighlightDrawFramebuffer(OpenGLManager::EFramebufferType framebufferType, unsigned int framebuffer) +{ + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, framebuffer)); + else if (framebufferType == OpenGLManager::EFramebufferType::Ext) + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer)); +} + +/** + * @brief Creates a texture-backed framebuffer used by the selection highlight. + * @param framebufferType Available framebuffer API implementation. + * @param width Target texture width in pixels. + * @param height Target texture height in pixels. + * @param framebuffer Destination framebuffer object. + * @param texture Destination color texture object. + * @return true when the framebuffer is complete. + */ +bool CreateSelectionHighlightFramebuffer(OpenGLManager::EFramebufferType framebufferType, unsigned int width, + unsigned int height, unsigned int& framebuffer, unsigned int& texture) +{ + if (width == 0 || height == 0 || framebuffer != 0 || texture != 0 || + framebufferType == OpenGLManager::EFramebufferType::Unknown) + return false; + + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + { + glsafe(::glGenFramebuffers(1, &framebuffer)); + glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, framebuffer)); + } + else + { + glsafe(::glGenFramebuffersEXT(1, &framebuffer)); + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, framebuffer)); + } + + glsafe(::glGenTextures(1, &texture)); + glsafe(::glBindTexture(GL_TEXTURE_2D, texture)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)); + glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)); + 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(::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, static_cast(width), static_cast(height), 0, + GL_RGBA, GL_UNSIGNED_BYTE, nullptr)); + + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + { + glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0)); + return ::glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + } + + glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0)); + return ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT; +} + +/** @brief Persistent OpenGL states changed by the selection Stencil Fallback. */ +struct StencilFallbackRenderState +{ + GLboolean stencilTestEnabled{ GL_FALSE }; + GLboolean depthTestEnabled{ GL_FALSE }; + GLboolean depthWriteEnabled{ GL_TRUE }; + GLboolean blendEnabled{ GL_FALSE }; + GLboolean cullFaceEnabled{ GL_FALSE }; + std::array colorWriteMask{ GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE }; + GLint stencilFunction{ GL_ALWAYS }; + GLint stencilReference{ 0 }; + GLint stencilValueMask{ -1 }; + GLint stencilWriteMask{ -1 }; + GLint stencilFail{ GL_KEEP }; + GLint stencilDepthFail{ GL_KEEP }; + GLint stencilDepthPass{ GL_KEEP }; + GLint stencilBackFunction{ GL_ALWAYS }; + GLint stencilBackReference{ 0 }; + GLint stencilBackValueMask{ -1 }; + GLint stencilBackWriteMask{ -1 }; + GLint stencilBackFail{ GL_KEEP }; + GLint stencilBackDepthFail{ GL_KEEP }; + GLint stencilBackDepthPass{ GL_KEEP }; + GLint clearStencilValue{ 0 }; + GLint frontFace{ GL_CCW }; + GLint cullFaceMode{ GL_BACK }; + GLint currentProgram{ 0 }; +}; + +/** + * @brief Captures the persistent OpenGL states changed by the selection Stencil Fallback. + * @return Captured state used by RestoreStencilFallbackRenderState(). + */ +StencilFallbackRenderState SaveStencilFallbackRenderState() +{ + StencilFallbackRenderState state; + state.stencilTestEnabled = glIsEnabled(GL_STENCIL_TEST); + state.depthTestEnabled = glIsEnabled(GL_DEPTH_TEST); + state.blendEnabled = glIsEnabled(GL_BLEND); + state.cullFaceEnabled = glIsEnabled(GL_CULL_FACE); + glsafe(::glGetBooleanv(GL_DEPTH_WRITEMASK, &state.depthWriteEnabled)); + glsafe(::glGetBooleanv(GL_COLOR_WRITEMASK, state.colorWriteMask.data())); + glsafe(::glGetIntegerv(GL_STENCIL_FUNC, &state.stencilFunction)); + glsafe(::glGetIntegerv(GL_STENCIL_REF, &state.stencilReference)); + glsafe(::glGetIntegerv(GL_STENCIL_VALUE_MASK, &state.stencilValueMask)); + glsafe(::glGetIntegerv(GL_STENCIL_WRITEMASK, &state.stencilWriteMask)); + glsafe(::glGetIntegerv(GL_STENCIL_FAIL, &state.stencilFail)); + glsafe(::glGetIntegerv(GL_STENCIL_PASS_DEPTH_FAIL, &state.stencilDepthFail)); + glsafe(::glGetIntegerv(GL_STENCIL_PASS_DEPTH_PASS, &state.stencilDepthPass)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_FUNC, &state.stencilBackFunction)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_REF, &state.stencilBackReference)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_VALUE_MASK, &state.stencilBackValueMask)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_WRITEMASK, &state.stencilBackWriteMask)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_FAIL, &state.stencilBackFail)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_FAIL, &state.stencilBackDepthFail)); + glsafe(::glGetIntegerv(GL_STENCIL_BACK_PASS_DEPTH_PASS, &state.stencilBackDepthPass)); + glsafe(::glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &state.clearStencilValue)); + glsafe(::glGetIntegerv(GL_FRONT_FACE, &state.frontFace)); + glsafe(::glGetIntegerv(GL_CULL_FACE_MODE, &state.cullFaceMode)); + glsafe(::glGetIntegerv(GL_CURRENT_PROGRAM, &state.currentProgram)); + return state; +} + +/** + * @brief Restores a state captured by SaveStencilFallbackRenderState(). + * @param state Previously captured OpenGL state. + */ +void RestoreStencilFallbackRenderState(const StencilFallbackRenderState& state) +{ + glsafe(::glStencilFuncSeparate(GL_FRONT, static_cast(state.stencilFunction), state.stencilReference, + static_cast(state.stencilValueMask))); + glsafe(::glStencilFuncSeparate(GL_BACK, static_cast(state.stencilBackFunction), state.stencilBackReference, + static_cast(state.stencilBackValueMask))); + glsafe(::glStencilMaskSeparate(GL_FRONT, static_cast(state.stencilWriteMask))); + glsafe(::glStencilMaskSeparate(GL_BACK, static_cast(state.stencilBackWriteMask))); + glsafe(::glStencilOpSeparate(GL_FRONT, static_cast(state.stencilFail), + static_cast(state.stencilDepthFail), + static_cast(state.stencilDepthPass))); + glsafe(::glStencilOpSeparate(GL_BACK, static_cast(state.stencilBackFail), + static_cast(state.stencilBackDepthFail), + static_cast(state.stencilBackDepthPass))); + glsafe(::glClearStencil(state.clearStencilValue)); + glsafe(::glColorMask(state.colorWriteMask[0], state.colorWriteMask[1], + state.colorWriteMask[2], state.colorWriteMask[3])); + glsafe(::glDepthMask(state.depthWriteEnabled)); + glsafe(::glFrontFace(static_cast(state.frontFace))); + glsafe(::glCullFace(static_cast(state.cullFaceMode))); + glsafe(::glUseProgram(static_cast(state.currentProgram))); + + if (state.stencilTestEnabled == GL_TRUE) + glsafe(::glEnable(GL_STENCIL_TEST)); + else + glsafe(::glDisable(GL_STENCIL_TEST)); + + if (state.depthTestEnabled == GL_TRUE) + glsafe(::glEnable(GL_DEPTH_TEST)); + else + glsafe(::glDisable(GL_DEPTH_TEST)); + + if (state.blendEnabled == GL_TRUE) + glsafe(::glEnable(GL_BLEND)); + else + glsafe(::glDisable(GL_BLEND)); + + if (state.cullFaceEnabled == GL_TRUE) + glsafe(::glEnable(GL_CULL_FACE)); + else + glsafe(::glDisable(GL_CULL_FACE)); +} + +} // namespace + #ifdef __WXGTK3__ // wxGTK3 seems to simulate OSX behavior in regard to HiDPI scaling support. RetinaHelper::RetinaHelper(wxWindow* window) : m_window(window), m_self(nullptr) {} @@ -1203,6 +1543,22 @@ GLCanvas3D::GLCanvas3D(wxGLCanvas* canvas, Bed3D &bed) GLCanvas3D::~GLCanvas3D() { + const bool hasSelectionHighlightResources = + m_selectionHighlightResources.fullResolutionMaskFramebuffer != 0 || + m_selectionHighlightResources.fullResolutionMaskTexture != 0 || + m_selectionHighlightResources.maskFramebuffer != 0 || + m_selectionHighlightResources.maskTexture != 0 || + m_selectionHighlightResources.edgeTempFramebuffer != 0 || + m_selectionHighlightResources.edgeTempTexture != 0 || + m_selectionHighlightResources.edgeFramebuffer != 0 || + m_selectionHighlightResources.edgeTexture != 0 || + m_selectionHighlightResources.glowTempFramebuffer != 0 || + m_selectionHighlightResources.glowTempTexture != 0 || + m_selectionHighlightResources.glowFramebuffer != 0 || + m_selectionHighlightResources.glowTexture != 0; + if (hasSelectionHighlightResources && m_canvas != nullptr && _set_current()) + ReleaseSelectionHighlightResources(); + reset_volumes(ResetVolumesMode::CanvasDestruction); m_sel_plate_toolbar.del_all_item(); @@ -1240,6 +1596,22 @@ bool GLCanvas3D::init() if (m_multisample_allowed) glsafe(::glEnable(GL_MULTISAMPLE)); + m_selectionFramebufferAvailable = OpenGLManager::are_framebuffers_supported(); + + if (!m_selectionFramebufferAvailable) + { + BOOST_LOG_TRIVIAL(info) << "Selection highlight framebuffer path is unavailable"; + } + + GLint stencilBits = 0; + glsafe(::glGetIntegerv(GL_STENCIL_BITS, &stencilBits)); + m_stencilFallbackAvailable = stencilBits > 0; + if (stencilBits < 8) + { + BOOST_LOG_TRIVIAL(warning) << "Default framebuffer provides " << stencilBits + << " stencil bits; 8 bits were requested"; + } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": before m_layers_editing init"; if (m_main_toolbar.is_enabled()) m_layers_editing.init(); @@ -1273,6 +1645,487 @@ bool GLCanvas3D::init() return true; } +GLCanvas3D::ESelectionHighlightMode GLCanvas3D::ResolveSelectionHighlightMode() +{ + const bool highlightEnabled = m_picking_enabled && !m_selection.is_empty(); + if (!highlightEnabled) + return ESelectionHighlightMode::Disabled; + + GLShaderProgram* const maskShader = wxGetApp().get_shader("selection_mask"); + GLShaderProgram* const compositeShader = wxGetApp().get_shader("selection_composite"); + GLShaderProgram* const edgeShader = wxGetApp().get_shader("selection_edge"); + GLShaderProgram* const gaussianShader = wxGetApp().get_shader("selection_gaussian"); + if (m_selectionFramebufferAvailable && maskShader != nullptr && compositeShader != nullptr && edgeShader != nullptr && + gaussianShader != nullptr && RenderSelectionHighlightMask()) + { + return ESelectionHighlightMode::UnifiedFramebuffer; + } + + if (maskShader != nullptr && m_stencilFallbackAvailable) + return ESelectionHighlightMode::StencilFallback; + + return ESelectionHighlightMode::Disabled; +} + +bool GLCanvas3D::EnsureSelectionHighlightResources(const Size& canvasSize) +{ + if (canvasSize.get_width() <= 0 || canvasSize.get_height() <= 0) + return false; + + if (!m_selectionFramebufferAvailable) + return false; + + const unsigned int canvasWidth = static_cast(canvasSize.get_width()); + const unsigned int canvasHeight = static_cast(canvasSize.get_height()); + const unsigned int width = std::max(1U, static_cast(std::ceil(canvasWidth * SELECTION_MASK_SCALE))); + const unsigned int height = std::max(1U, static_cast(std::ceil(canvasHeight * SELECTION_MASK_SCALE))); + const unsigned int glowWidth = std::max(1U, static_cast(std::ceil(width * SELECTION_GLOW_SCALE))); + const unsigned int glowHeight = std::max(1U, static_cast(std::ceil(height * SELECTION_GLOW_SCALE))); + const SelectionHighlightResources& resources = m_selectionHighlightResources; + const bool resourcesReady = resources.fullResolutionMaskFramebuffer != 0 && + resources.fullResolutionMaskTexture != 0 && + resources.maskFramebuffer != 0 && resources.maskTexture != 0 && + resources.edgeTempFramebuffer != 0 && resources.edgeTempTexture != 0 && + resources.edgeFramebuffer != 0 && resources.edgeTexture != 0 && + resources.glowTempFramebuffer != 0 && resources.glowTempTexture != 0 && + resources.glowFramebuffer != 0 && resources.glowTexture != 0; + if (resourcesReady && resources.fullResolutionWidth == canvasWidth && + resources.fullResolutionHeight == canvasHeight && resources.width == width && resources.height == height) + return true; + + const OpenGLManager::EFramebufferType framebufferType = OpenGLManager::get_framebuffers_type(); + GLint previousDrawFramebuffer = 0; + GLint previousReadFramebuffer = 0; + GLint previousExtFramebuffer = 0; + GLint previousTexture = 0; + glsafe(::glGetIntegerv(GL_TEXTURE_BINDING_2D, &previousTexture)); + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + { + glsafe(::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousDrawFramebuffer)); + glsafe(::glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &previousReadFramebuffer)); + } + else if (framebufferType == OpenGLManager::EFramebufferType::Ext) + glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &previousExtFramebuffer)); + else + { + m_selectionFramebufferAvailable = false; + return false; + } + + ReleaseSelectionHighlightResources(); + + const bool framebufferComplete = + CreateSelectionHighlightFramebuffer(framebufferType, canvasWidth, canvasHeight, + m_selectionHighlightResources.fullResolutionMaskFramebuffer, + m_selectionHighlightResources.fullResolutionMaskTexture) && + CreateSelectionHighlightFramebuffer(framebufferType, width, height, + m_selectionHighlightResources.maskFramebuffer, + m_selectionHighlightResources.maskTexture) && + CreateSelectionHighlightFramebuffer(framebufferType, width, height, + m_selectionHighlightResources.edgeTempFramebuffer, + m_selectionHighlightResources.edgeTempTexture) && + CreateSelectionHighlightFramebuffer(framebufferType, width, height, + m_selectionHighlightResources.edgeFramebuffer, + m_selectionHighlightResources.edgeTexture) && + CreateSelectionHighlightFramebuffer(framebufferType, glowWidth, glowHeight, + m_selectionHighlightResources.glowTempFramebuffer, + m_selectionHighlightResources.glowTempTexture) && + CreateSelectionHighlightFramebuffer(framebufferType, glowWidth, glowHeight, + m_selectionHighlightResources.glowFramebuffer, + m_selectionHighlightResources.glowTexture); + + glsafe(::glBindTexture(GL_TEXTURE_2D, static_cast(previousTexture))); + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + { + glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast(previousDrawFramebuffer))); + glsafe(::glBindFramebuffer(GL_READ_FRAMEBUFFER, static_cast(previousReadFramebuffer))); + } + else + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, static_cast(previousExtFramebuffer))); + + if (!framebufferComplete) + { + ReleaseSelectionHighlightResources(); + m_selectionFramebufferAvailable = false; + BOOST_LOG_TRIVIAL(warning) << "Unable to create complete selection highlight framebuffers"; + return false; + } + + m_selectionHighlightResources.fullResolutionWidth = canvasWidth; + m_selectionHighlightResources.fullResolutionHeight = canvasHeight; + m_selectionHighlightResources.width = width; + m_selectionHighlightResources.height = height; + return true; +} + +bool GLCanvas3D::RenderSelectionHighlightMask() +{ + const Size canvasSize = get_canvas_size(); + if (!EnsureSelectionHighlightResources(canvasSize)) + return false; + + GLShaderProgram* const shader = wxGetApp().get_shader("selection_mask"); + GLShaderProgram* const downsampleShader = wxGetApp().get_shader("selection_gaussian"); + if (shader == nullptr || downsampleShader == nullptr || !m_background.is_initialized()) + return false; + + const OpenGLManager::EFramebufferType framebufferType = OpenGLManager::get_framebuffers_type(); + if (framebufferType == OpenGLManager::EFramebufferType::Unknown) + return false; + + const MaskRenderState previousState = SaveMaskRenderState(framebufferType); + ScopeGuard stateGuard([&previousState]() + { + RestoreMaskRenderState(previousState); + }); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.fullResolutionMaskFramebuffer); + glsafe(::glViewport(0, 0, static_cast(m_selectionHighlightResources.fullResolutionWidth), + static_cast(m_selectionHighlightResources.fullResolutionHeight))); + glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDisable(GL_CULL_FACE)); + glsafe(::glClearColor(0.0f, 0.0f, 0.0f, 0.0f)); + glsafe(::glClear(GL_COLOR_BUFFER_BIT)); + + shader->start_using(); + shader->set_uniform("output_color", ColorRGB::WHITE()); + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& viewMatrix = camera.get_view_matrix(); + const Transform3d& projectionMatrix = camera.get_projection_matrix(); + shader->set_uniform("projection_matrix", projectionMatrix); + + const Selection::IndicesList& volumeIndices = m_selection.get_volume_idxs(); + for (unsigned int volumeIdx : volumeIndices) + { + GLVolume* const volume = m_selection.get_volume(volumeIdx); + if (volume == nullptr) + continue; + + if (!m_render_sla_auxiliaries && volume->composite_id.volume_id < 0) + continue; + + if (!camera.GetFrustum().Intersects(volume->transformed_bounding_box())) + continue; + + shader->set_uniform("view_model_matrix", viewMatrix * volume->world_matrix()); + volume->render(); + } + shader->stop_using(); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.maskFramebuffer); + glsafe(::glViewport(0, 0, static_cast(m_selectionHighlightResources.width), + static_cast(m_selectionHighlightResources.height))); + downsampleShader->start_using(); + downsampleShader->set_uniform("source_texture", 0); + downsampleShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(m_selectionHighlightResources.fullResolutionWidth), + 1.0f / static_cast(m_selectionHighlightResources.fullResolutionHeight))); + downsampleShader->set_uniform("blur_radius", 0.0f); + downsampleShader->set_uniform("blur_direction", Vec2f(1.0f, 0.0f)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.fullResolutionMaskTexture)); + m_background.render(); + downsampleShader->stop_using(); + + return true; +} + +bool GLCanvas3D::RenderSelectionOutlineTextures() +{ + GLShaderProgram* const edgeShader = wxGetApp().get_shader("selection_edge"); + GLShaderProgram* const gaussianShader = wxGetApp().get_shader("selection_gaussian"); + if (edgeShader == nullptr || gaussianShader == nullptr || + m_selectionHighlightResources.maskTexture == 0 || + m_selectionHighlightResources.edgeTempFramebuffer == 0 || + m_selectionHighlightResources.edgeTempTexture == 0 || + m_selectionHighlightResources.edgeFramebuffer == 0 || + m_selectionHighlightResources.edgeTexture == 0 || + m_selectionHighlightResources.glowTempFramebuffer == 0 || + m_selectionHighlightResources.glowTempTexture == 0 || + m_selectionHighlightResources.glowFramebuffer == 0 || + m_selectionHighlightResources.glowTexture == 0) + { + return false; + } + + const OpenGLManager::EFramebufferType framebufferType = OpenGLManager::get_framebuffers_type(); + if (framebufferType == OpenGLManager::EFramebufferType::Unknown) + return false; + + GLint previousFramebuffer = 0; + std::array previousViewport{ 0, 0, 0, 0 }; + glsafe(::glGetIntegerv(GL_VIEWPORT, previousViewport.data())); + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + glsafe(::glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previousFramebuffer)); + else + glsafe(::glGetIntegerv(GL_FRAMEBUFFER_BINDING_EXT, &previousFramebuffer)); + ScopeGuard stateGuard([framebufferType, previousFramebuffer, previousViewport]() + { + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + glsafe(::glBindFramebuffer(GL_DRAW_FRAMEBUFFER, static_cast(previousFramebuffer))); + else + glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, static_cast(previousFramebuffer))); + + glsafe(::glViewport(previousViewport[0], previousViewport[1], + previousViewport[2], previousViewport[3])); + }); + + glsafe(::glViewport(0, 0, static_cast(m_selectionHighlightResources.width), + static_cast(m_selectionHighlightResources.height))); + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDisable(GL_CULL_FACE)); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.edgeFramebuffer); + edgeShader->start_using(); + edgeShader->set_uniform("mask_texture", 0); + edgeShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(m_selectionHighlightResources.width), + 1.0f / static_cast(m_selectionHighlightResources.height))); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.maskTexture)); + m_background.render(); + edgeShader->stop_using(); + + gaussianShader->start_using(); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.edgeTempFramebuffer); + gaussianShader->set_uniform("source_texture", 0); + gaussianShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(m_selectionHighlightResources.width), + 1.0f / static_cast(m_selectionHighlightResources.height))); + gaussianShader->set_uniform("blur_radius", SELECTION_EDGE_THICKNESS); + gaussianShader->set_uniform("blur_direction", Vec2f(1.0f, 0.0f)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.edgeTexture)); + m_background.render(); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.edgeFramebuffer); + gaussianShader->set_uniform("source_texture", 0); + gaussianShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(m_selectionHighlightResources.width), + 1.0f / static_cast(m_selectionHighlightResources.height))); + gaussianShader->set_uniform("blur_radius", SELECTION_EDGE_THICKNESS); + gaussianShader->set_uniform("blur_direction", Vec2f(0.0f, 1.0f)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.edgeTempTexture)); + m_background.render(); + + const unsigned int glowWidth = std::max(1U, static_cast( + std::ceil(m_selectionHighlightResources.width * SELECTION_GLOW_SCALE))); + const unsigned int glowHeight = std::max(1U, static_cast( + std::ceil(m_selectionHighlightResources.height * SELECTION_GLOW_SCALE))); + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.glowTempFramebuffer); + glsafe(::glViewport(0, 0, static_cast(glowWidth), static_cast(glowHeight))); + gaussianShader->set_uniform("source_texture", 0); + gaussianShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(glowWidth), + 1.0f / static_cast(glowHeight))); + gaussianShader->set_uniform("blur_radius", SELECTION_GLOW_BLUR_RADIUS); + gaussianShader->set_uniform("blur_direction", Vec2f(1.0f, 0.0f)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.edgeTexture)); + m_background.render(); + + BindSelectionHighlightDrawFramebuffer(framebufferType, m_selectionHighlightResources.glowFramebuffer); + gaussianShader->set_uniform("source_texture", 0); + gaussianShader->set_uniform("inverse_texture_size", + Vec2f(1.0f / static_cast(glowWidth), + 1.0f / static_cast(glowHeight))); + gaussianShader->set_uniform("blur_radius", SELECTION_GLOW_BLUR_RADIUS); + gaussianShader->set_uniform("blur_direction", Vec2f(0.0f, 1.0f)); + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.glowTempTexture)); + m_background.render(); + gaussianShader->stop_using(); + + return true; +} + +void GLCanvas3D::CompositeSelectionHighlight() +{ + const Size canvasSize = get_canvas_size(); + if (canvasSize.get_width() <= 0 || canvasSize.get_height() <= 0) + return; + + const unsigned int canvasWidth = static_cast(canvasSize.get_width()); + const unsigned int canvasHeight = static_cast(canvasSize.get_height()); + const unsigned int maskWidth = std::max(1U, static_cast(std::ceil(canvasWidth * SELECTION_MASK_SCALE))); + const unsigned int maskHeight = std::max(1U, static_cast(std::ceil(canvasHeight * SELECTION_MASK_SCALE))); + if (m_selectionHighlightResources.maskTexture == 0 || + m_selectionHighlightResources.edgeTexture == 0 || + m_selectionHighlightResources.glowTexture == 0 || + m_selectionHighlightResources.width != maskWidth || + m_selectionHighlightResources.height != maskHeight || !m_background.is_initialized()) + { + return; + } + + GLShaderProgram* const shader = wxGetApp().get_shader("selection_composite"); + if (shader == nullptr) + return; + + const CompositeRenderState previousState = SaveCompositeRenderState(); + ScopeGuard stateGuard([&previousState]() + { + RestoreCompositeRenderState(previousState); + }); + + if (!RenderSelectionOutlineTextures()) + return; + + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glEnable(GL_BLEND)); + glsafe(::glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA)); + glsafe(::glDisable(GL_CULL_FACE)); + + shader->start_using(); + shader->set_uniform("mask_texture", 0); + shader->set_uniform("edge_texture", 1); + shader->set_uniform("glow_texture", 2); + const ColorRGB outlineColor = m_canvas_type == ECanvasType::CanvasAssembleView ? + ASSEMBLE_VIEW_SELECTION_OUTLINE_COLOR : SELECTION_OUTLINE_COLOR; + shader->set_uniform("outline_color", outlineColor); + + glsafe(::glActiveTexture(GL_TEXTURE0)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.maskTexture)); + glsafe(::glActiveTexture(GL_TEXTURE1)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.edgeTexture)); + glsafe(::glActiveTexture(GL_TEXTURE2)); + glsafe(::glBindTexture(GL_TEXTURE_2D, m_selectionHighlightResources.glowTexture)); + + m_background.render(); + shader->stop_using(); +} + +void GLCanvas3D::RenderSelectionStencilFallback() +{ + if (!m_stencilFallbackAvailable) + return; + + GLShaderProgram* const shader = wxGetApp().get_shader("selection_mask"); + if (shader == nullptr) + return; + + const StencilFallbackRenderState previousState = SaveStencilFallbackRenderState(); + ScopeGuard stateGuard([&previousState]() + { + RestoreStencilFallbackRenderState(previousState); + }); + + glsafe(::glEnable(GL_STENCIL_TEST)); + glsafe(::glClearStencil(0)); + glsafe(::glStencilMask(0xFFU)); + glsafe(::glClear(GL_STENCIL_BUFFER_BIT)); + glsafe(::glDisable(GL_DEPTH_TEST)); + glsafe(::glDepthMask(GL_FALSE)); + glsafe(::glDisable(GL_BLEND)); + glsafe(::glDisable(GL_CULL_FACE)); + + shader->start_using(); + const ColorRGB outlineColor = m_canvas_type == ECanvasType::CanvasAssembleView ? + ASSEMBLE_VIEW_SELECTION_OUTLINE_COLOR : SELECTION_OUTLINE_COLOR; + shader->set_uniform("output_color", outlineColor); + + const Camera& camera = wxGetApp().plater()->get_camera(); + const Transform3d& viewMatrix = camera.get_view_matrix(); + const Transform3d& projectionMatrix = camera.get_projection_matrix(); + shader->set_uniform("projection_matrix", projectionMatrix); + + const Selection::IndicesList& volumeIndices = m_selection.get_volume_idxs(); + + // Pass 1 writes the complete selected projection into a shared stencil union. + glsafe(::glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE)); + glsafe(::glStencilFunc(GL_ALWAYS, 1, 0xFFU)); + glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE)); + for (unsigned int volumeIdx : volumeIndices) + { + GLVolume* const volume = m_selection.get_volume(volumeIdx); + if (volume == nullptr) + continue; + + if (!m_render_sla_auxiliaries && volume->composite_id.volume_id < 0) + continue; + + shader->set_uniform("view_model_matrix", viewMatrix * volume->world_matrix()); + volume->render(); + } + + // Pass 2 draws only the scaled geometry outside the original stencil union. + glsafe(::glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE)); + glsafe(::glStencilMask(0x00U)); + glsafe(::glStencilFunc(GL_NOTEQUAL, 1, 0xFFU)); + glsafe(::glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP)); + for (unsigned int volumeIdx : volumeIndices) + { + GLVolume* const volume = m_selection.get_volume(volumeIdx); + if (volume == nullptr) + continue; + + if (!m_render_sla_auxiliaries && volume->composite_id.volume_id < 0) + continue; + + Transform3d outlineMatrix = volume->world_matrix(); + outlineMatrix.scale(STENCIL_OUTLINE_SCALE); + shader->set_uniform("view_model_matrix", viewMatrix * outlineMatrix); + volume->render(); + } + + shader->stop_using(); +} + +void GLCanvas3D::ReleaseSelectionHighlightResources() +{ + const OpenGLManager::EFramebufferType framebufferType = OpenGLManager::get_framebuffers_type(); + if (framebufferType == OpenGLManager::EFramebufferType::Arb) + { + if (m_selectionHighlightResources.fullResolutionMaskFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.fullResolutionMaskFramebuffer)); + if (m_selectionHighlightResources.maskFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.maskFramebuffer)); + if (m_selectionHighlightResources.edgeTempFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.edgeTempFramebuffer)); + if (m_selectionHighlightResources.edgeFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.edgeFramebuffer)); + if (m_selectionHighlightResources.glowTempFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.glowTempFramebuffer)); + if (m_selectionHighlightResources.glowFramebuffer != 0) + glsafe(::glDeleteFramebuffers(1, &m_selectionHighlightResources.glowFramebuffer)); + } + else if (framebufferType == OpenGLManager::EFramebufferType::Ext) + { + if (m_selectionHighlightResources.fullResolutionMaskFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.fullResolutionMaskFramebuffer)); + if (m_selectionHighlightResources.maskFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.maskFramebuffer)); + if (m_selectionHighlightResources.edgeTempFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.edgeTempFramebuffer)); + if (m_selectionHighlightResources.edgeFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.edgeFramebuffer)); + if (m_selectionHighlightResources.glowTempFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.glowTempFramebuffer)); + if (m_selectionHighlightResources.glowFramebuffer != 0) + glsafe(::glDeleteFramebuffersEXT(1, &m_selectionHighlightResources.glowFramebuffer)); + } + + if (m_selectionHighlightResources.fullResolutionMaskTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.fullResolutionMaskTexture)); + if (m_selectionHighlightResources.maskTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.maskTexture)); + if (m_selectionHighlightResources.edgeTempTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.edgeTempTexture)); + if (m_selectionHighlightResources.edgeTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.edgeTexture)); + if (m_selectionHighlightResources.glowTempTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.glowTempTexture)); + if (m_selectionHighlightResources.glowTexture != 0) + glsafe(::glDeleteTextures(1, &m_selectionHighlightResources.glowTexture)); + + m_selectionHighlightResources = SelectionHighlightResources{}; +} + void GLCanvas3D::on_change_color_mode(bool is_dark, bool reinit) { m_is_dark = is_dark; // Bed color @@ -1972,6 +2825,8 @@ void GLCanvas3D::render(bool only_init) } } + const ESelectionHighlightMode highlightMode = ResolveSelectionHighlightMode(); + // draw scene glsafe(::glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)); _render_background(); @@ -2028,6 +2883,15 @@ void GLCanvas3D::render(bool only_init) _render_objects(GLVolumeCollection::ERenderType::Transparent, !m_gizmos.is_running()); } + if (highlightMode == ESelectionHighlightMode::UnifiedFramebuffer) + { + CompositeSelectionHighlight(); + } + else if (highlightMode == ESelectionHighlightMode::StencilFallback) + { + RenderSelectionStencilFallback(); + } + _render_sequential_clearance(); #if ENABLE_RENDER_SELECTION_CENTER _render_selection_center(); @@ -7480,12 +8344,6 @@ 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: @@ -7497,7 +8355,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, cvn_size, [object_id](const GLVolume& volume) { + m_volumes.render(type, false, camera, [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); }); @@ -7513,7 +8371,7 @@ 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, cvn_size, [this, canvas_type](const GLVolume& volume) { + m_volumes.render(type, m_picking_enabled, camera, [this, canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier && !volume.is_wipe_tower; } @@ -7548,7 +8406,7 @@ 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, cvn_size, [canvas_type](const GLVolume& volume) { + m_volumes.render(type, false, camera, [canvas_type](const GLVolume& volume) { if (canvas_type == ECanvasType::CanvasAssembleView) { return !volume.is_modifier; } @@ -7616,8 +8474,7 @@ void GLCanvas3D::_render_selection() scale_factor = m_retina_helper->get_scale_factor(); #endif // ENABLE_RETINA_GL - if (!m_gizmos.is_running()) - m_selection.render(scale_factor); + m_selection.render(scale_factor); } void GLCanvas3D::_render_sequential_clearance() diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 1a13a9a2efb..56a3864a15c 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -508,6 +508,35 @@ class GLCanvas3D int GetHoverId(); private: + /** @brief Rendering paths available for the selected-object highlight. */ + enum class ESelectionHighlightMode : unsigned char + { + Disabled, + UnifiedFramebuffer, + StencilFallback + }; + + /** @brief GPU resources shared by the selection Mask, Edge, Glow and Composite passes. */ + struct SelectionHighlightResources + { + unsigned int fullResolutionMaskFramebuffer{ 0 }; + unsigned int fullResolutionMaskTexture{ 0 }; + unsigned int maskFramebuffer{ 0 }; + unsigned int maskTexture{ 0 }; + unsigned int edgeTempFramebuffer{ 0 }; + unsigned int edgeTempTexture{ 0 }; + unsigned int edgeFramebuffer{ 0 }; + unsigned int edgeTexture{ 0 }; + unsigned int glowTempFramebuffer{ 0 }; + unsigned int glowTempTexture{ 0 }; + unsigned int glowFramebuffer{ 0 }; + unsigned int glowTexture{ 0 }; + unsigned int fullResolutionWidth{ 0 }; + unsigned int fullResolutionHeight{ 0 }; + unsigned int width{ 0 }; + unsigned int height{ 0 }; + }; + bool m_is_dark = false; wxGLCanvas* m_canvas; wxGLContext* m_context; @@ -570,6 +599,8 @@ class GLCanvas3D bool m_moving_enabled; bool m_dynamic_background_enabled; bool m_multisample_allowed; + bool m_selectionFramebufferAvailable{ false }; + bool m_stencilFallbackAvailable{ false }; bool m_moving; bool m_tab_down; bool m_camera_movement; @@ -606,6 +637,8 @@ class GLCanvas3D bool m_tooltip_enabled{ true }; Slope m_slope; + SelectionHighlightResources m_selectionHighlightResources; + OrientSettings m_orient_settings_fff, m_orient_settings_sla; ArrangeSettings m_arrange_settings_fff, m_arrange_settings_sla, @@ -1142,6 +1175,31 @@ class GLCanvas3D private: bool _is_shown_on_screen() const; + /** @brief Selects and prepares the selection highlight path for the current frame. */ + ESelectionHighlightMode ResolveSelectionHighlightMode(); + + /** + * @brief Creates or resizes the selection highlight framebuffer resources. + * @param canvasSize Physical framebuffer dimensions in pixels. + * @return true when the Mask, Edge and Glow framebuffers are ready. + */ + bool EnsureSelectionHighlightResources(const Size& canvasSize); + + /** @brief Renders selected volumes at full resolution and downscales the selection Mask. */ + bool RenderSelectionHighlightMask(); + + /** @brief Generates the main selection edge and its outer Glow from the selection Mask. */ + bool RenderSelectionOutlineTextures(); + + /** @brief Composites the linearly upsampled selection Fill and Outline over the main scene. */ + void CompositeSelectionHighlight(); + + /** @brief Renders an occlusion-independent selection Outline through the default framebuffer stencil. */ + void RenderSelectionStencilFallback(); + + /** @brief Releases all selection highlight framebuffer resources. */ + void ReleaseSelectionHighlightResources(); + void _switch_toolbars_icon_filename(); bool _init_toolbars(); bool _init_main_toolbar(); diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index d525ce272ef..7dd1dfda115 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -9,6 +9,7 @@ #include using namespace std::literals; +#include #include namespace Slic3r { @@ -29,6 +30,16 @@ std::pair GLShadersManager::init() return true; }; + auto appendOptionalShader = [&append_shader, &error](const std::string& name, + const GLShaderProgram::ShaderFilenames& filenames) { + const size_t errorLength = error.size(); + if (append_shader(name, filenames)) + return; + + error.erase(errorLength); + BOOST_LOG_TRIVIAL(warning) << "Selection highlight shader unavailable: " << name; + }; + assert(m_shaders.empty()); bool valid = true; @@ -38,12 +49,20 @@ std::pair GLShadersManager::init() valid &= append_shader("imgui", { prefix + "imgui.vs", prefix + "imgui.fs" }); // basic shader, used to render all what was previously rendered using the immediate mode valid &= append_shader("flat", { prefix + "flat.vs", prefix + "flat.fs" }); + // used to render selected geometry into the unified mask and stencil fallback + appendOptionalShader("selection_mask", { prefix + "flat.vs", prefix + "selection_mask.fs" }); // basic shader with plane clipping, used to render volumes in picking pass valid &= append_shader("flat_clip", { prefix + "flat_clip.vs", prefix + "flat_clip.fs" }); // basic shader for textures, used to render textures valid &= append_shader("flat_texture", { prefix + "flat_texture.vs", prefix + "flat_texture.fs" }); // used to render 3D scene background valid &= append_shader("background", { prefix + "background.vs", prefix + "background.fs" }); + // used to composite the selection fill and outline over the completed scene + appendOptionalShader("selection_composite", { prefix + "background.vs", prefix + "selection_composite.fs" }); + // used to extract the selection edge from the low-resolution selection mask + appendOptionalShader("selection_edge", { prefix + "background.vs", prefix + "selection_edge.fs" }); + // used to apply directional Gaussian blur to selection edge textures + appendOptionalShader("selection_gaussian", { prefix + "background.vs", prefix + "selection_gaussian.fs" }); // used to render bed axes and model, selection hints, gcode sequential view marker model, preview shells, options in gcode preview valid &= append_shader("gouraud_light", { prefix + "gouraud_light.vs", prefix + "gouraud_light.fs" }); //used to render thumbnail diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index edaf1b11101..3c109a7188e 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -433,9 +433,6 @@ class GUI_App : public wxApp 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 f03b91222ff..16f2dee4110 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -2888,15 +2888,6 @@ void MainFrame::init_menubar_as_editor() }, 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 (beta)"), _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") + "\t" + ctrl + shift + _L("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,