From d6299220608281ea141d40cc918cf5b6dbf9b291 Mon Sep 17 00:00:00 2001 From: SoftFever Date: Sat, 10 Oct 2026 11:44:09 +0800 Subject: [PATCH] Keep the paint highlight off every texture preview between strokes --- .../GUI/Gizmos/GLGizmoTextureDisplacement.cpp | 78 ++++++++----------- .../GUI/Gizmos/GLGizmoTextureDisplacement.hpp | 19 ++--- 2 files changed, 37 insertions(+), 60 deletions(-) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp index 761de8c6b9..96af821505 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.cpp @@ -592,26 +592,17 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() glsafe(::glEnable(GL_DEPTH_TEST)); // Once anything is painted, m_preview_glmodel holds the true displaced result (same algorithm - // Bake uses). The untouched original topology (what render_triangles() draws) coincides - // exactly with it everywhere except the painted/displaced area, so both are drawn: the real - // preview geometry first, then the usual selection-highlight overlay with a small depth bias - // so it wins the depth test on the coincident (unpainted) surface - keeping the familiar - // enforcer/blocker highlight for precise brush editing there. Where the surface has actually - // been displaced, the raised preview geometry legitimately occludes the flat overlay - that - // visible relief is itself the "this is painted" indicator in that area. + // Bake uses). The untouched original topology (what render_triangles() draws) coincides exactly + // with it everywhere except the displaced area, so both can be drawn: the real preview geometry + // first, then the selectors' highlight with a small depth bias so it wins the depth test on the + // coincident surface. Where the surface has actually been displaced, the raised preview geometry + // legitimately occludes the flat highlight - that visible relief is itself the "this is painted" + // indicator in that area. // - // The shaded preview is different: it never actually moves geometry (it only shades), so - // its depth is identical to the overlay's *everywhere*, not just in the unpainted area - the - // depth-biased opaque overlay would win the depth test across the whole surface and hide the relief - // shading entirely. So render_triangles() leaves the textured volume out there. What is *not* skipped is - // render_paint_overlay(): leaving the shading as the only paint feedback meant a stroke that - // erased paint, or added it with no texture picked, changed nothing on screen until the whole - // preview rebuilt at stroke end - and in the true-displacement view the opaque overlay is hidden - // by the raised surface for the same reason. The translucent tint covers both cases. - // - // A colour preview is the exception to both: the opaque overlay buries its colours under a flat plane - // wherever the relief does not rise, and the tint washes them green. Both are what the user steers by - // while painting, though, so they give way only between strokes. + // The shaded preview never actually moves geometry (it only shades), so its depth is identical to + // the highlight's *everywhere*: the depth-biased opaque highlight would win the depth test across the + // whole surface and hide the shading entirely. So render_triangles() leaves the textured volume out + // there, and the translucent tint drawn further down is the paint feedback instead. // Coalesced shaded-preview rebuild from an in-progress UV island drag (see on_island_edited): done here, at // most once per drawn frame, rather than synchronously in the UV canvas's mouse-move handler. if (m_use_shaded_preview && m_shaded_preview_dirty) { @@ -654,14 +645,12 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() m_parent.toggle_model_objects_visibility(false, m_c->selection_info()->model_object(), m_c->selection_info()->get_active_instance(), mv); - // Which colour preview is on screen decides what the highlight gives way to: the Normal mesh colours - // from every layer, the shaded one from the active layer only. Only a stroke brings it back - a fill - // tool's hover does not, so the colours stay on screen until the click. The debug view shows a captured - // stage, not a colour preview, so it keeps its highlight. - const TextureDisplacementLayer *al = active_layer(); - const bool color_view = !is_painting() && m_debug_stage < 0; - const bool stack_colors = color_view && mv != nullptr && any_layer_colors(*mv); - const bool active_colors = color_view && al != nullptr && layer_shows_color(*al); + // Over a preview, the paint feedback - the selectors' highlight in the Normal view, the tint in both - + // is drawn only while a mouse button is down: during a stroke, a fill click included. Between strokes + // it would cover the preview: the highlight buries it under a flat plane wherever the relief does not + // rise, and the tint washes it green. The debug view shows a captured stage rather than a preview, so + // it keeps the feedback. + const bool paint_feedback = is_painting() || m_debug_stage >= 0; // Whether the textured volume's selector - and with it a fill tool's contour - is drawn this frame. bool textured_selector_drawn = true; @@ -672,15 +661,12 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() textured_selector_drawn = false; } else if (use_true_preview) { render_preview_mesh(); - - if (show_paint_overlay) { - glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); - glsafe(::glPolygonOffset(-1.0f, -1.0f)); - // Over a colour preview only the other model parts: render_preview_mesh() draws the textured one. - render_triangles(selection, stack_colors ? mv : nullptr); - glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); - } - textured_selector_drawn = show_paint_overlay && !stack_colors; + glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); + glsafe(::glPolygonOffset(-1.0f, -1.0f)); + // Without paint feedback, only the other model parts: render_preview_mesh() draws the textured one. + render_triangles(selection, paint_feedback ? nullptr : mv); + glsafe(::glDisable(GL_POLYGON_OFFSET_FILL)); + textured_selector_drawn = paint_feedback; } else { // render_triangles() *is* the model in a painter gizmo (it draws every model-part volume with the // selector's colours), not an overlay on top of one - so it still has to run under a UV-check @@ -707,7 +693,7 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() // preview, the highlight is the selectors' own, except during a stroke over the colour paint: the paint // there is not flushed yet, so the colours are still drawn over the stroke's highlight. const bool preview_drawn = use_shaded || use_true_preview; - if (show_paint_overlay && (preview_drawn ? !active_colors : painted_colors_drawn && is_painting())) + if (show_paint_overlay && (preview_drawn ? paint_feedback : painted_colors_drawn && is_painting())) render_paint_overlay(m_paint_overlay_glmodel); // A fill tool's contour is drawn with the selectors, under the colour paint drawn since - which covers it @@ -732,7 +718,8 @@ void GLGizmoTextureDisplacement::render_painter_gizmo() // The UV editor's island selection, shown on the model. Polled here rather than pushed: the pane // changes its selection in its own mouse handling, and a compare of a few ints per frame is free. { - const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas(); + const TextureDisplacementLayer *al = active_layer(); + const UVEditorCanvas *uv_canvas = wxGetApp().plater()->get_uv_editor_canvas(); if (m_show_uv_editor && al != nullptr && al->projection_method == TextureProjectionMethod::LSCM && uv_canvas != nullptr && !m_uv_editor_unwrap.empty()) { if (uv_canvas->selected_islands() != m_island_overlay_selection) @@ -1844,9 +1831,8 @@ bool GLGizmoTextureDisplacement::render_painted_colors(bool whole_stack) shader->set_uniform("slope.normal_z", float(-std::cos(Geometry::deg2rad(m_highlight_by_angle_threshold_deg)))); shader->set_uniform("slope.up_direction", get_tilt_up_direction()); shader->set_uniform("show_wireframe", false); - // A full depth unit in front of the selectors' highlight at -1 in the Normal view, the least OpenGL - // guarantees to tell apart. Depth writes off, as for the tint: the wireframe and seam overlays drawn later - // test against the real surface, and the tint drawn after this shows on top of it. + // Pulled forward and without depth writes, as the tint is (see render_paint_overlay()). The tint is + // drawn after this, so it shows on top. glsafe(::glEnable(GL_POLYGON_OFFSET_FILL)); glsafe(::glPolygonOffset(-2.f, -2.f)); glsafe(::glDepthMask(GL_FALSE)); @@ -4509,14 +4495,12 @@ TextureDisplacementFacetsData GLGizmoTextureDisplacement::facets_data_of(const M return out; } -bool GLGizmoTextureDisplacement::layer_shows_color(const TextureDisplacementLayer &layer) -{ - return layer.color_enabled && !layer.empty() && height_texture_has_color(layer); -} - bool GLGizmoTextureDisplacement::any_layer_colors(const ModelVolume &mv) { - return std::any_of(mv.texture_displacement_layers.begin(), mv.texture_displacement_layers.end(), layer_shows_color); + for (const TextureDisplacementLayer &layer : mv.texture_displacement_layers) + if (layer.color_enabled && !layer.empty() && height_texture_has_color(layer)) + return true; + return false; } TextureColorSettings GLGizmoTextureDisplacement::color_settings_for(const ModelVolume &mv) diff --git a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp index 3267ed18a8..48e0ab72f6 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoTextureDisplacement.hpp @@ -177,11 +177,9 @@ public: ColorRGBA color; }; std::vector m_preview_color_runs; - // True if the layer would actually colour something: colour turned on, and a texture that has colour - // to give. - static bool layer_shows_color(const TextureDisplacementLayer &layer); - // True if any of the volume's layers would. What decides whether a palette is captured into a job at - // all, and so whether the colour criterion and the mmu write ever run. + // True if any of the volume's layers would actually colour something: colour turned on, and a + // texture that has colour to give. What decides whether a palette is captured into a job at all, + // and so whether the colour criterion and the mmu write ever run. static bool any_layer_colors(const ModelVolume &mv); // The model's own colour paint (mmu_segmentation_facets) as the gizmo draws it over its surface: the @@ -729,14 +727,9 @@ private: bool m_shaded_preview_dirty = false; GLModel m_shaded_preview_glmodel; - // Translucent tint over the active layer's painted triangles, drawn on top of whichever preview - // is showing - except, between strokes, the active layer's own colour preview, which it would wash - // green (see render_painter_gizmo()). The base painter's own opaque paint highlight - // (render_triangles()) cannot be used in either preview mode - it is coincident with the surface - // and simply covers it - so the only paint feedback the gizmo had was the relief itself, which - // meant erasing showed nothing at all until the stroke ended and the whole preview rebuilt. This - // is that feedback: cheap (the painted patch only), translucent (the preview stays visible through - // it) and rebuilt live during a stroke. + // Translucent tint over the active layer's painted triangles: the paint feedback over a preview (see + // render_painter_gizmo()). Cheap (the painted patch only), translucent so the preview shows through, + // and rebuilt live during a stroke. GLModel m_paint_overlay_glmodel; // The islands selected in the UV editor, tinted on the model so the pane's selection can be seen // in place. Rebuilt whenever the pane's selection differs from the one it was built for.