From 83d3ec97f53ec07d002265ac2a988236d96f5064 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sat, 13 Jun 2026 07:12:54 +0100 Subject: [PATCH] Auto-enable top-surface coloring when projecting top-surface raw atlas data --- src/libslic3r/TextureMapping.hpp | 5 +- src/libslic3r/TextureMappingContoning.cpp | 6 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 261 +++++++++++++++++- src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 6 +- src/slic3r/GUI/Plater.cpp | 9 +- 5 files changed, 266 insertions(+), 21 deletions(-) diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index 4fb3bb0227f..81b2dd8d4a7 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -228,7 +228,7 @@ struct TextureMappingZone static constexpr int DefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillDefault); static constexpr int SlicerDefaultTopSurfaceContoningFlatSurfaceInfillMode = int(ContoningFlatSurfaceInfillBoundarySkinVariable); static constexpr bool DefaultTopSurfaceContoningLayerPhaseEnabled = false; - static constexpr bool DefaultTopSurfaceContoningVariedInfillAnglesEnabled = false; + static constexpr bool DefaultTopSurfaceContoningVariedInfillAnglesEnabled = true; static constexpr bool DefaultTopSurfaceContoningBlueNoiseErrorDiffusionEnabled = false; static constexpr bool DefaultTopSurfaceContoningSupersampledCellsEnabled = false; static constexpr bool DefaultTopSurfaceContoningPolygonizeColorRegionsEnabled = true; @@ -742,8 +742,7 @@ struct TextureMappingZone mode == int(ContoningColorPredictionBeerLambertRgb); top_surface_contoning_beer_lambert_rgb_correction_enabled = beer_lambert_rgb; top_surface_contoning_td_effective_alpha_correction_enabled = - mode == int(ContoningColorPredictionTdEffectiveAlpha) || - mode == int(ContoningColorPredictionCalibratedCurrentLinearAffine); + mode == int(ContoningColorPredictionTdEffectiveAlpha); } void apply_top_surface_contoning_experimental_defaults() diff --git a/src/libslic3r/TextureMappingContoning.cpp b/src/libslic3r/TextureMappingContoning.cpp index 1eaa22eaacc..96dfff39c21 100644 --- a/src/libslic3r/TextureMappingContoning.cpp +++ b/src/libslic3r/TextureMappingContoning.cpp @@ -420,11 +420,11 @@ TextureMappingContoningSolver::TextureMappingContoningSolver(const TextureMappin zone.top_surface_contoning_color_prediction_mode); m_adaptive_spectral_correction_enabled = m_td_adjustment_enabled && - m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionAdaptiveSpectral); + (m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionAdaptiveSpectral) || + m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine)); m_td_effective_alpha_correction_enabled = m_td_adjustment_enabled && - (m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) || - m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine)); + m_effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha); m_beer_lambert_rgb_correction_enabled = m_td_adjustment_enabled && !m_td_effective_alpha_correction_enabled && diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 6c36cd0ff22..eb3d7afa0b3 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -6531,7 +6531,8 @@ struct ProjectionPaintableImageMask }; static ProjectionPaintableImageMask build_projection_paintable_image_mask(const ProjectionContext &context, - bool transparent_bg_paintable = false) + bool transparent_bg_paintable = false, + float paintable_alpha_threshold = 0.5f) { ProjectionPaintableImageMask mask; mask.width = context.image_width; @@ -6554,7 +6555,7 @@ static ProjectionPaintableImageMask build_projection_paintable_image_mask(const uint32_t row_sum = 0; for (uint32_t x = 0; x < mask.width; ++x) { const size_t pixel_idx = (size_t(y) * size_t(mask.width) + size_t(x)) * 4 + 3; - const bool paintable = float((*context.image_rgba)[pixel_idx]) * opacity > 0.5f; + const bool paintable = float((*context.image_rgba)[pixel_idx]) * opacity > paintable_alpha_threshold; row_sum += paintable ? 1u : 0u; mask.prefix[(size_t(y) + 1) * stride + size_t(x) + 1] = mask.prefix[size_t(y) * stride + size_t(x) + 1] + row_sum; @@ -8275,6 +8276,220 @@ static unsigned int texture_mapping_zone_id_for_erasing_region_projection(ModelO return ensure_texture_mapping_zone(); } +static void append_unique_texture_mapping_zone_id(std::vector &ids, unsigned int zone_id) +{ + if (zone_id != 0 && std::find(ids.begin(), ids.end(), zone_id) == ids.end()) + ids.emplace_back(zone_id); +} + +static void append_assigned_image_texture_zone_id(const TextureMappingManager &texture_mgr, + std::vector &ids, + int filament_id) +{ + if (filament_id > 0 && image_texture_zone_is_usable(texture_mgr, unsigned(filament_id))) + append_unique_texture_mapping_zone_id(ids, unsigned(filament_id)); +} + +static bool image_projection_volume_has_projected_image(const ModelObject &object, + const ModelVolume &volume, + size_t volume_idx, + const GLCanvas3D &parent, + const ProjectionContext &context, + int instance_idx, + bool pass_through_model, + const ProjectionVisibility &visibility, + const ProjectionPaintableImageMask &paintable_mask) +{ + if (!volume.is_model_part()) + return false; + + const indexed_triangle_set &its = volume.mesh().its; + if (its.vertices.empty() || its.indices.empty()) + return false; + + const Transform3d world_matrix = + projection_world_matrix_for_context(context, parent, &object, &volume, volume_idx, instance_idx); + + for (size_t tri_idx = 0; tri_idx < its.indices.size(); ++tri_idx) { + const stl_triangle_vertex_indices &tri = its.indices[tri_idx]; + if (tri[0] < 0 || tri[1] < 0 || tri[2] < 0) + continue; + if (size_t(tri[0]) >= its.vertices.size() || + size_t(tri[1]) >= its.vertices.size() || + size_t(tri[2]) >= its.vertices.size()) + continue; + + const std::array vertices = { + its.vertices[size_t(tri[0])].cast(), + its.vertices[size_t(tri[1])].cast(), + its.vertices[size_t(tri[2])].cast() + }; + if (projection_triangle_should_project(context, + visibility, + paintable_mask, + world_matrix, + vertices, + projection_visibility_triangle_key(volume_idx, tri_idx), + !pass_through_model)) + return true; + } + + return false; +} + +static std::vector raw_top_surface_projection_affected_zone_ids(const ModelObject &object, + const GLCanvas3D &parent, + const ImageMapRawFilamentOffsetAtlas &raw_atlas, + bool raw_atlas_valid, + const ProjectionContext &context, + int instance_idx, + bool pass_through_model, + bool improve_projection_accuracy, + bool project_regions, + unsigned int texture_mapping_filament_id) +{ + std::vector ids; + if (!raw_atlas_valid || raw_atlas.top_surface_layers.empty() || wxGetApp().preset_bundle == nullptr) + return ids; + + ProjectionContext raw_projection_context = context; + raw_projection_context.image_opacity = 1.f; + + ProjectionVisibility visibility; + if (!pass_through_model) + visibility = build_projection_visibility(raw_projection_context, + parent, + &object, + instance_idx, + ImageProjectionCancelCheckFn(), + improve_projection_accuracy); + const ProjectionPaintableImageMask paintable_mask = build_projection_paintable_image_mask(raw_projection_context, false, 127.49f); + + for (size_t volume_idx = 0; volume_idx < object.volumes.size(); ++volume_idx) { + const ModelVolume *volume = object.volumes[volume_idx]; + if (volume == nullptr) + continue; + if (!image_projection_volume_has_projected_image(object, + *volume, + volume_idx, + parent, + raw_projection_context, + instance_idx, + pass_through_model, + visibility, + paintable_mask)) + continue; + + const TextureMappingManager &texture_mgr = wxGetApp().preset_bundle->texture_mapping_zones; + if (project_regions && texture_mapping_filament_id != 0) { + if (image_texture_zone_is_usable(texture_mgr, texture_mapping_filament_id)) + append_unique_texture_mapping_zone_id(ids, texture_mapping_filament_id); + return ids; + } + append_assigned_image_texture_zone_id(texture_mgr, ids, volume->extruder_id()); + } + + return ids; +} + +static wxString texture_mapping_zone_id_list_text(const std::vector &ids) +{ + wxString text; + for (size_t idx = 0; idx < ids.size(); ++idx) { + if (idx > 0) + text += ", "; + text += wxString::Format("%u", ids[idx]); + } + return text; +} + +static std::vector auto_enable_raw_top_surface_projection_zones(const ModelObject &object, + const GLCanvas3D &parent, + const ImageMapRawFilamentOffsetAtlas &raw_atlas, + bool raw_atlas_valid, + const ProjectionContext &context, + int instance_idx, + bool pass_through_model, + bool improve_projection_accuracy, + bool project_regions, + unsigned int texture_mapping_filament_id) +{ + std::vector newly_enabled_ids; + if (wxGetApp().preset_bundle == nullptr) + return newly_enabled_ids; + + const std::vector affected_ids = + raw_top_surface_projection_affected_zone_ids(object, + parent, + raw_atlas, + raw_atlas_valid, + context, + instance_idx, + pass_through_model, + improve_projection_accuracy, + project_regions, + texture_mapping_filament_id); + if (affected_ids.empty()) + return newly_enabled_ids; + + TextureMappingManager &texture_mgr = wxGetApp().preset_bundle->texture_mapping_zones; + bool changed = false; + for (const unsigned int zone_id : affected_ids) { + TextureMappingZone *zone = texture_mgr.zone_from_id(zone_id); + if (zone == nullptr || !zone->enabled || zone->deleted || !zone->is_image_texture()) + continue; + + if (!zone->top_surface_image_printing_enabled) + newly_enabled_ids.emplace_back(zone_id); + + if (!zone->top_surface_image_printing_enabled) { + zone->top_surface_image_printing_enabled = true; + changed = true; + } + if (zone->top_surface_image_printing_method != int(TextureMappingZone::TopSurfaceImageContoning)) { + zone->top_surface_image_printing_method = int(TextureMappingZone::TopSurfaceImageContoning); + changed = true; + } + if (zone->top_surface_image_fixed_coloring_filaments != true) { + zone->top_surface_image_fixed_coloring_filaments = true; + changed = true; + } + if (zone->top_surface_contoning_colored_surfaces != int(TextureMappingZone::ContoningColoredUpperAndLowerSurfaces)) { + zone->top_surface_contoning_colored_surfaces = int(TextureMappingZone::ContoningColoredUpperAndLowerSurfaces); + changed = true; + } + if (!zone->top_surface_contoning_color_lower_surfaces) { + zone->top_surface_contoning_color_lower_surfaces = true; + changed = true; + } + if (zone->modulation_mode != int(TextureMappingZone::ModulationPerimeterPathV2)) { + zone->modulation_mode = int(TextureMappingZone::ModulationPerimeterPathV2); + changed = true; + } + if (!zone->modulation_mode_manually_changed) { + zone->modulation_mode_manually_changed = true; + changed = true; + } + } + + if (changed) + persist_texture_mapping_zone_definitions(texture_mgr); + + std::sort(newly_enabled_ids.begin(), newly_enabled_ids.end()); + return newly_enabled_ids; +} + +static void show_auto_enabled_raw_top_surface_projection_message(const std::vector &zone_ids) +{ + if (zone_ids.empty()) + return; + + show_info(wxGetApp().mainframe, + _L("Top-surface coloring has been automatically enabled for affected texture mapping zones: ") + + texture_mapping_zone_id_list_text(zone_ids), + _L("Top-Surface Coloring Enabled")); +} + struct ManagedColorSourceFlags { bool use_rgba = false; @@ -18822,6 +19037,17 @@ bool GLGizmoImageProjection::start_projection_job() image_projection_apply_volume_result(*dst, *src); } + const std::vector auto_enabled_top_surface_zone_ids = + auto_enable_raw_top_surface_projection_zones(*object, + m_parent, + result->input.raw_atlas, + result->input.raw_atlas_valid, + result->input.context, + result->input.instance_idx, + result->input.pass_through_model, + result->input.improve_projection_accuracy, + result->input.project_regions, + result->texture_mapping_filament_id); refresh_projected_object(object); remember_projected_object(object); m_projection_mode_initialized = true; @@ -18830,6 +19056,7 @@ bool GLGizmoImageProjection::start_projection_job() show_raw_offset_data_converted_to_rgba_image_message(); if (result->creating_rgba_data_from_raw && object_has_rgba_data(*object)) show_raw_offset_data_converted_to_rgba_message(); + show_auto_enabled_raw_top_surface_projection_message(auto_enabled_top_surface_zone_ids); m_show_overlay = false; finish(); }; @@ -18886,16 +19113,20 @@ bool GLGizmoImageProjection::project_image_to_object(ModelObject *object, if (check_cancel) check_cancel(); - if (input.project_regions && texture_mapping_filament_id != 0) + if (input.project_regions && texture_mapping_filament_id != 0) { + ProjectionContext region_projection_context = input.context; + if (input.raw_atlas_valid) + region_projection_context.image_opacity = 1.f; project_texture_mapping_zone_to_regions(*object, m_parent, - input.context, + region_projection_context, input.instance_idx, input.pass_through_model, texture_mapping_filament_id, stage_progress(80, 98), check_cancel, input.improve_projection_accuracy); + } if (progress_fn) progress_fn(100); @@ -18975,6 +19206,17 @@ bool GLGizmoImageProjection::project_image_to_selected_object() if (!project_image_to_object(object, texture_mapping_filament_id, input)) return false; + const std::vector auto_enabled_top_surface_zone_ids = + auto_enable_raw_top_surface_projection_zones(*object, + m_parent, + input.raw_atlas, + input.raw_atlas_valid, + input.context, + input.instance_idx, + input.pass_through_model, + input.improve_projection_accuracy, + input.project_regions, + texture_mapping_filament_id); refresh_projected_object(object); remember_projected_object(object); m_projection_mode_initialized = true; @@ -18983,6 +19225,7 @@ bool GLGizmoImageProjection::project_image_to_selected_object() show_raw_offset_data_converted_to_rgba_image_message(); if (creating_rgba_data_from_raw && object_has_rgba_data(*object)) show_raw_offset_data_converted_to_rgba_message(); + show_auto_enabled_raw_top_surface_projection_message(auto_enabled_top_surface_zone_ids); return true; } @@ -19154,6 +19397,10 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object, visibility = build_projection_visibility(context, m_parent, object, instance_idx, check_cancel, input.improve_projection_accuracy); const bool raw_atlas_projection = input.raw_atlas_valid; const ImageMapRawFilamentOffsetAtlas &raw_projection_atlas = input.raw_atlas; + ProjectionContext raw_projection_context = context; + if (raw_atlas_projection) + raw_projection_context.image_opacity = 1.f; + const ProjectionContext &projection_alpha_context = raw_atlas_projection ? raw_projection_context : context; RawAtlasProjectionLayout raw_layout; if (raw_atlas_projection) { std::string raw_atlas_error; @@ -19247,7 +19494,7 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object, if (projection_visibility_exact(visibility)) { exact_projected_triangles.assign(its.indices.size(), 0); const ProjectionPaintableImageMask paintable_mask = - build_projection_paintable_image_mask(context, context.apply_transparency_as_background); + build_projection_paintable_image_mask(projection_alpha_context, projection_alpha_context.apply_transparency_as_background); tbb::parallel_for(tbb::blocked_range(0, its.indices.size(), 512), [&](const tbb::blocked_range &range) { for (size_t tri_idx = range.begin(); tri_idx < range.end(); ++tri_idx) { @@ -19425,7 +19672,7 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object, projected_image_color_at_point(context, world_matrix, projection_point)) { const bool transparent_sample = !context.apply_transparency_as_background && - !projection_overlay_has_paintable_alpha(*projected, context); + !projection_overlay_has_paintable_alpha(*projected, projection_alpha_context); if (transparent_sample) { if (!rewrite_texture_base) { continue; @@ -19442,7 +19689,7 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object, std::vector(); if (atlas_raw_values.empty() && atlas_top_surface_slots.empty()) continue; - const float alpha = projection_overlay_alpha(*projected, context); + const float alpha = projection_overlay_alpha(*projected, projection_alpha_context); if (!atlas_raw_values.empty()) { std::vector raw_values = raw_offset_pixel_values(*volume, wrapped_x, wrapped_y); if (raw_values.size() != size_t(volume->imported_texture_raw_channels)) diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index e67e061984a..44c8955b430 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -3266,11 +3266,11 @@ std::optional texture_preview_simulation_setti effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb); settings.contoning_flat_surface_adaptive_spectral_correction = settings.contoning_flat_surface_td_adjustment && - effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionAdaptiveSpectral); + (effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionAdaptiveSpectral) || + effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine)); settings.contoning_flat_surface_td_effective_alpha_correction = settings.contoning_flat_surface_td_adjustment && - (effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) || - effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine)); + effective_color_prediction_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha); settings.contoning_flat_surface_beam_search_stack_expansion = zone->effective_top_surface_contoning_beam_search_stack_expansion_enabled(); settings.contoning_flat_surface_force_low_resolution = diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index ab06cefc166..f65766605e9 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2425,9 +2425,9 @@ static wxString texture_mapping_contoning_color_prediction_mode_label(int mode) case int(TextureMappingZone::ContoningColorPredictionBeerLambertRgb): return _L("RGB Beer-Lambert - not recommended"); case int(TextureMappingZone::ContoningColorPredictionBasicReflectance): - return _L("Basic Reflectance"); + return _L("Basic Reflectance - not recommended"); case int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine): - return _L("Calibrated: current linear affine"); + return _L("Calibrated: linear affine - not recommended"); case int(TextureMappingZone::ContoningColorPredictionCalibratedTdAlphaEffective): return _L("Calibrated: TD alpha effective"); case int(TextureMappingZone::ContoningColorPredictionCalibratedFreeAlphaEffective): @@ -2437,7 +2437,7 @@ static wxString texture_mapping_contoning_color_prediction_mode_label(int mode) case int(TextureMappingZone::ContoningColorPredictionCalibratedNearestMeasuredSample): return _L("Calibrated: nearest measured sample"); default: - return _L("TD Effective Alpha"); + return _L("TD Effective Alpha - not recommended"); } } @@ -4246,8 +4246,7 @@ public: { const int effective_mode = TextureMappingZone::effective_top_surface_contoning_color_prediction_mode( top_surface_contoning_color_prediction_mode()); - return effective_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha) || - effective_mode == int(TextureMappingZone::ContoningColorPredictionCalibratedCurrentLinearAffine); + return effective_mode == int(TextureMappingZone::ContoningColorPredictionTdEffectiveAlpha); } bool top_surface_contoning_surface_scatter_enabled() const {