From de8155030936598c282cea1beb360f49b60c040f Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Fri, 8 May 2026 05:16:20 +0100 Subject: [PATCH] When projecting images, prefer using texture zones that are already used on the target object --- src/libslic3r/GCode.cpp | 33 ++++--- src/libslic3r/GCode.hpp | 1 + .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 88 ++++++++++++++++--- 3 files changed, 97 insertions(+), 25 deletions(-) diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp index 0656661b56e..632e7fe0a81 100644 --- a/src/libslic3r/GCode.cpp +++ b/src/libslic3r/GCode.cpp @@ -7331,6 +7331,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( struct TextureSampleData { std::array rgba { { 0.f, 0.f, 0.f, 1.f } }; std::vector raw_component_weights; + bool raw_component_weights_from_texture { false }; }; struct WeightedTextureSample { @@ -7338,16 +7339,18 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( float y_mm { 0.f }; std::array rgba { { 0.f, 0.f, 0.f, 1.f } }; std::vector raw_component_weights; + bool raw_component_weights_from_texture { false }; float weight { 0.f }; }; std::vector samples; samples.reserve(8192); - auto accumulate_sample = [&samples](float x_mm, - float y_mm, - const std::array &rgba, - float sample_weight, - std::vector raw_component_weights = {}) { + auto accumulate_sample = [&samples, component_count](float x_mm, + float y_mm, + const std::array &rgba, + float sample_weight, + std::vector raw_component_weights = {}, + bool raw_component_weights_from_texture = false) { if (!std::isfinite(x_mm) || !std::isfinite(y_mm) || sample_weight <= EPSILON) return; if (!std::isfinite(sample_weight) || @@ -7356,8 +7359,10 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( !std::isfinite(rgba[2]) || !std::isfinite(rgba[3])) return; + if (raw_component_weights.size() != component_count) + raw_component_weights_from_texture = false; - samples.push_back({ x_mm, y_mm, rgba, std::move(raw_component_weights), sample_weight }); + samples.push_back({ x_mm, y_mm, rgba, std::move(raw_component_weights), raw_component_weights_from_texture, sample_weight }); }; struct LayerPlaneSamplePoint { @@ -7473,7 +7478,8 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( float(world_pos.y()), sample_data.rgba, sample_weight, - std::move(sample_data.raw_component_weights)); + std::move(sample_data.raw_component_weights), + sample_data.raw_component_weights_from_texture); } return true; @@ -7483,7 +7489,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( const Vec3d &p1, const Vec3d &p2, const std::array &rgba) { - if (accumulate_layer_plane_triangle_samples(p0, p1, p2, [&rgba](const Vec3f &) { return TextureSampleData{ rgba, {} }; })) + if (accumulate_layer_plane_triangle_samples(p0, p1, p2, [&rgba](const Vec3f &) { return TextureSampleData{ rgba, {}, false }; })) return; const float max_world_edge_mm = std::max({ @@ -7640,7 +7646,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( } if (raw_component_weights.size() != component_count) rgba = composite_rgba_over_background_for_gcode(rgba, background_color); - return TextureSampleData{ rgba, std::move(raw_component_weights) }; + return TextureSampleData{ rgba, std::move(raw_component_weights), use_raw_uv_texture }; }; auto sample_data_for_barycentric = [&](const Vec3f &barycentric) { @@ -7711,7 +7717,8 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( float(world_pos.y()), sample_data.rgba, sample_weight, - std::move(sample_data.raw_component_weights)); + std::move(sample_data.raw_component_weights), + sample_data.raw_component_weights_from_texture); sampled_from_uv_texture = true; } } @@ -7789,6 +7796,7 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( weight_field.sample_y_mm.resize(sample_count); weight_field.sample_weight.resize(sample_count); weight_field.sample_component_weights.assign(sample_count * component_count, 0.f); + weight_field.raw_component_weights_from_texture = false; std::vector fallback_acc(component_count, 0.f); float fallback_weight = 0.f; @@ -7796,6 +7804,8 @@ static VertexColorOverhangWeightField build_vertex_color_weight_field_for_gcode( const WeightedTextureSample &sample = samples[sample_idx]; if (sample.weight <= EPSILON) continue; + if (sample.raw_component_weights_from_texture) + weight_field.raw_component_weights_from_texture = true; weight_field.sample_x_mm[sample_idx] = sample.x_mm; weight_field.sample_y_mm[sample_idx] = sample.y_mm; @@ -7911,7 +7921,8 @@ static float sample_vertex_color_weight_field_for_gcode(const VertexColorOverhan bool high_resolution_texture_sampling, bool compact_offset_mode = false) { - if (compact_offset_mode && !weight_field.empty() && component_idx < weight_field.component_count) { + if (compact_offset_mode && !weight_field.raw_component_weights_from_texture && !weight_field.empty() && + component_idx < weight_field.component_count) { std::vector values(weight_field.component_count, 0.f); float max_value = 0.f; for (size_t idx = 0; idx < weight_field.component_count; ++idx) { diff --git a/src/libslic3r/GCode.hpp b/src/libslic3r/GCode.hpp index 597d029bf39..fa3523601e2 100644 --- a/src/libslic3r/GCode.hpp +++ b/src/libslic3r/GCode.hpp @@ -66,6 +66,7 @@ struct VertexColorOverhangWeightField { std::vector sample_component_weights; std::vector> buckets; std::vector fallback_weights; + bool raw_component_weights_from_texture { false }; bool empty() const { diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 1b4d8f2c26c..c8844089cba 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -4343,6 +4343,74 @@ static void assign_texture_mapping_zone_preserving_painted_regions(ModelObject & } } +static bool image_texture_zone_is_usable_for_projection(const TextureMappingManager &texture_mgr, unsigned int zone_id) +{ + const TextureMappingZone *zone = texture_mgr.zone_from_id(zone_id); + return zone != nullptr && zone->enabled && !zone->deleted && zone->is_image_texture(); +} + +static unsigned int painted_image_texture_zone_id_for_projection(const ModelObject &object) +{ + if (wxGetApp().preset_bundle == nullptr) + return 0; + + const TextureMappingManager &texture_mgr = wxGetApp().preset_bundle->texture_mapping_zones; + std::vector painted_ids; + for (const ModelVolume *volume : object.volumes) { + if (volume == nullptr || !volume->is_model_part() || volume->mmu_segmentation_facets.empty()) + continue; + + const auto &used_states = volume->mmu_segmentation_facets.get_data().used_states; + for (size_t state_idx = static_cast(EnforcerBlockerType::Extruder1); state_idx < used_states.size(); ++state_idx) { + if (used_states[state_idx] && image_texture_zone_is_usable_for_projection(texture_mgr, unsigned(state_idx))) + painted_ids.emplace_back(unsigned(state_idx)); + } + } + + for (const TextureMappingZone &zone : texture_mgr.zones()) + if (image_texture_zone_is_usable_for_projection(texture_mgr, zone.zone_id) && + std::find(painted_ids.begin(), painted_ids.end(), zone.zone_id) != painted_ids.end()) + return zone.zone_id; + return 0; +} + +static unsigned int base_image_texture_zone_id_for_projection(const ModelObject &object) +{ + if (wxGetApp().preset_bundle == nullptr) + return 0; + + const TextureMappingManager &texture_mgr = wxGetApp().preset_bundle->texture_mapping_zones; + std::vector base_ids; + if (const ConfigOption *opt = object.config.option("extruder"); opt != nullptr) { + const int extruder_id = opt->getInt(); + if (extruder_id > 0 && image_texture_zone_is_usable_for_projection(texture_mgr, unsigned(extruder_id))) + base_ids.emplace_back(unsigned(extruder_id)); + } + + for (const ModelVolume *volume : object.volumes) { + if (volume == nullptr || !volume->is_model_part()) + continue; + const int extruder_id = volume->extruder_id(); + if (extruder_id > 0 && image_texture_zone_is_usable_for_projection(texture_mgr, unsigned(extruder_id))) + base_ids.emplace_back(unsigned(extruder_id)); + } + + for (const TextureMappingZone &zone : texture_mgr.zones()) + if (image_texture_zone_is_usable_for_projection(texture_mgr, zone.zone_id) && + std::find(base_ids.begin(), base_ids.end(), zone.zone_id) != base_ids.end()) + return zone.zone_id; + return 0; +} + +static unsigned int texture_mapping_zone_id_for_image_projection(ModelObject &object) +{ + if (const unsigned int painted_id = painted_image_texture_zone_id_for_projection(object); painted_id != 0) + return painted_id; + if (const unsigned int base_id = base_image_texture_zone_id_for_projection(object); base_id != 0) + return base_id; + return ensure_texture_mapping_zone(); +} + struct ManagedRegionColorSource { std::vector> triangles_per_type; @@ -8742,21 +8810,13 @@ bool GLGizmoImageProjection::project_image_to_selected_object() if (!changed) return false; - unsigned int raw_texture_mapping_filament_id = 0; - if (m_raw_atlas.valid()) { - raw_texture_mapping_filament_id = ensure_texture_mapping_zone(); - if (raw_texture_mapping_filament_id != 0) { - object->config.set("extruder", int(raw_texture_mapping_filament_id)); - for (ModelVolume *volume : object->volumes) - if (volume != nullptr && volume->is_model_part()) - volume->config.set("extruder", int(raw_texture_mapping_filament_id)); - enable_texture_mapping_zone_simulated_preview(raw_texture_mapping_filament_id); - } - } + const bool whole_image_texture_mapped_without_regions = object_is_whole_image_texture_mapped_without_regions(*object); + const unsigned int texture_mapping_filament_id = + (!whole_image_texture_mapped_without_regions || m_raw_atlas.valid()) ? texture_mapping_zone_id_for_image_projection(*object) : 0; + if (m_raw_atlas.valid() && texture_mapping_filament_id != 0) + enable_texture_mapping_zone_simulated_preview(texture_mapping_filament_id); - if (!object_is_whole_image_texture_mapped_without_regions(*object)) { - const unsigned int texture_mapping_filament_id = - raw_texture_mapping_filament_id != 0 ? raw_texture_mapping_filament_id : ensure_texture_mapping_zone(); + if (!whole_image_texture_mapped_without_regions) { if (texture_mapping_filament_id != 0) { const Selection &selection = m_parent.get_selection(); const int instance_idx = selection.get_instance_idx();