From 5d02c827e3830909b038b784aed24ae633c25a56 Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Mon, 25 May 2026 12:05:19 +0100 Subject: [PATCH] Change default mode for texture mapping zones to generic solver. Optimizations for filament region projection --- src/libslic3r/TextureMapping.cpp | 2 +- src/libslic3r/TextureMapping.hpp | 2 +- .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 187 +++++++++++++++++- 3 files changed, 184 insertions(+), 7 deletions(-) diff --git a/src/libslic3r/TextureMapping.cpp b/src/libslic3r/TextureMapping.cpp index d05c43214ed..27e7183142c 100644 --- a/src/libslic3r/TextureMapping.cpp +++ b/src/libslic3r/TextureMapping.cpp @@ -1214,7 +1214,7 @@ TextureMappingZone *TextureMappingManager::add_zone(size_t num_physical, zone.apply_default_modulation_mode(); std::vector ids; - for (size_t i = 1; i <= std::min(num_physical, 9); ++i) + for (size_t i = 1; i <= std::min(num_physical, 4); ++i) ids.emplace_back(unsigned(i)); zone.component_ids = encode_component_ids(ids); zone.component_a = ids[0]; diff --git a/src/libslic3r/TextureMapping.hpp b/src/libslic3r/TextureMapping.hpp index d5bf7fc725b..d4e23060c31 100644 --- a/src/libslic3r/TextureMapping.hpp +++ b/src/libslic3r/TextureMapping.hpp @@ -128,7 +128,7 @@ struct TextureMappingZone static constexpr int DefaultOffsetFadeMode = int(OffsetFadeNone); static constexpr int DefaultOffsetAngleMode = int(OffsetAngleObjectCenter); static constexpr int DefaultTextureMappingMode = int(TextureMappingFilamentBlending); - static constexpr int DefaultFilamentColorMode = int(FilamentColorCMYK); + static constexpr int DefaultFilamentColorMode = int(FilamentColorAny); static constexpr bool DefaultForceSequentialFilaments = false; static constexpr bool DefaultReduceOuterSurfaceTexture = false; static constexpr bool DefaultSeamHiding = false; diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 9732767429a..105f22f6a85 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -5990,6 +5990,36 @@ static bool projection_image_rect_has_paintable_alpha(const ProjectionPaintableI return count > 0; } +static bool projection_image_rect_is_fully_paintable_alpha(const ProjectionPaintableImageMask &mask, + int min_x, + int min_y, + int max_x, + int max_y) +{ + if (mask.width == 0 || mask.height == 0) + return false; + if (max_x < min_x || max_y < min_y) + return false; + if (min_x < 0 || min_y < 0 || max_x >= int(mask.width) || max_y >= int(mask.height)) + return false; + if (mask.all_paintable) + return true; + if (mask.prefix.empty()) + return false; + + const size_t stride = size_t(mask.width) + 1; + const size_t x0 = size_t(min_x); + const size_t y0 = size_t(min_y); + const size_t x1 = size_t(max_x) + 1; + const size_t y1 = size_t(max_y) + 1; + const uint32_t count = mask.prefix[y1 * stride + x1] - + mask.prefix[y0 * stride + x1] - + mask.prefix[y1 * stride + x0] + + mask.prefix[y0 * stride + x0]; + const uint64_t area = uint64_t(max_x - min_x + 1) * uint64_t(max_y - min_y + 1); + return uint64_t(count) == area; +} + static bool projection_triangle_image_pixel_bounds(const ProjectionContext &context, const Transform3d &world_matrix, const std::array &vertices, @@ -6046,6 +6076,45 @@ static bool projection_triangle_intersects_paintable_image(const ProjectionConte int(std::ceil(max_y)) + 1); } +static bool projection_triangle_is_fully_inside_paintable_image(const ProjectionContext &context, + const ProjectionPaintableImageMask &mask, + const Transform3d &world_matrix, + const std::array &vertices) +{ + if (mask.width == 0 || mask.height == 0) + return false; + + float min_x = std::numeric_limits::max(); + float min_y = std::numeric_limits::max(); + float max_x = std::numeric_limits::lowest(); + float max_y = std::numeric_limits::lowest(); + + for (const Vec3f &vertex : vertices) { + const Vec3d world_point = world_matrix * vertex.cast(); + if (!projection_world_point_visible_in_section(context, world_point)) + return false; + + Vec2f screen = Vec2f::Zero(); + if (!project_point_to_screen(context, world_point, screen)) + return false; + + Vec2f image_pixel = Vec2f::Zero(); + if (!projection_screen_to_image_pixel(context, screen, image_pixel, true)) + return false; + + min_x = std::min(min_x, image_pixel.x()); + min_y = std::min(min_y, image_pixel.y()); + max_x = std::max(max_x, image_pixel.x()); + max_y = std::max(max_y, image_pixel.y()); + } + + return projection_image_rect_is_fully_paintable_alpha(mask, + int(std::floor(min_x)) - 1, + int(std::floor(min_y)) - 1, + int(std::ceil(max_x)) + 1, + int(std::ceil(max_y)) + 1); +} + static bool projection_triangle_has_visible_sample(const ProjectionVisibility &visibility, const ProjectionContext &context, const Transform3d &world_matrix, @@ -6088,6 +6157,73 @@ static bool projection_triangle_has_visible_sample(const ProjectionVisibility &v return false; } +static bool projection_screen_triangle_is_fully_visible(const ProjectionVisibility &visibility, + const std::array &screen, + const std::array &depths, + uint64_t triangle_key) +{ + if (!projection_visibility_valid(visibility)) + return true; + + const float min_screen_x = std::min({ screen[0].x(), screen[1].x(), screen[2].x() }); + const float max_screen_x = std::max({ screen[0].x(), screen[1].x(), screen[2].x() }); + const float min_screen_y = std::min({ screen[0].y(), screen[1].y(), screen[2].y() }); + const float max_screen_y = std::max({ screen[0].y(), screen[1].y(), screen[2].y() }); + const float visibility_right = visibility.left + float(visibility.width) / visibility.scale; + const float visibility_bottom = visibility.top + float(visibility.height) / visibility.scale; + if (max_screen_x < visibility.left || + min_screen_x > visibility_right || + max_screen_y < visibility.top || + min_screen_y > visibility_bottom) + return false; + + const int min_x = std::clamp(int(std::floor((min_screen_x - visibility.left) * visibility.scale)) - 1, 0, visibility.width - 1); + const int max_x = std::clamp(int(std::ceil((max_screen_x - visibility.left) * visibility.scale)) + 1, 0, visibility.width - 1); + const int min_y = std::clamp(int(std::floor((min_screen_y - visibility.top) * visibility.scale)) - 1, 0, visibility.height - 1); + const int max_y = std::clamp(int(std::ceil((max_screen_y - visibility.top) * visibility.scale)) + 1, 0, visibility.height - 1); + + bool covered = false; + for (int y = min_y; y <= max_y; ++y) { + for (int x = min_x; x <= max_x; ++x) { + const Vec2f pixel(visibility.left + (float(x) + 0.5f) / visibility.scale, + visibility.top + (float(y) + 0.5f) / visibility.scale); + Vec3f weights = Vec3f::Zero(); + if (!conservative_barycentric_weights_2d(pixel, screen[0], screen[1], screen[2], 0.f, weights)) + continue; + + covered = true; + const float depth = depths[0] * weights.x() + depths[1] * weights.y() + depths[2] * weights.z(); + if (!projection_visibility_depth_matches_sample(visibility, x, y, depth, triangle_key)) + return false; + } + } + + return covered; +} + +static bool projection_triangle_is_fully_visible(const ProjectionVisibility &visibility, + const ProjectionContext &context, + const Transform3d &world_matrix, + const std::array &vertices, + uint64_t triangle_key) +{ + if (!projection_visibility_valid(visibility)) + return true; + + const std::vector polygon = projection_visible_world_polygon(context, world_matrix, vertices); + if (polygon.size() != 3) + return false; + + std::array screen; + std::array depths; + for (size_t idx = 0; idx < polygon.size(); ++idx) { + if (!project_point_to_depth_clipped_screen(context, polygon[idx], screen[idx], &depths[idx])) + return false; + } + + return projection_screen_triangle_is_fully_visible(visibility, screen, depths, triangle_key); +} + static bool projection_triangle_should_project(const ProjectionContext &context, const ProjectionVisibility &visibility, const ProjectionPaintableImageMask &paintable_mask, @@ -6100,6 +6236,23 @@ static bool projection_triangle_should_project(const ProjectionContext projection_triangle_has_visible_sample(visibility, context, world_matrix, vertices, triangle_key); } +static bool projection_region_triangle_is_fully_projected(const ProjectionContext &context, + const ProjectionVisibility &visibility, + const ProjectionPaintableImageMask &paintable_mask, + const Transform3d &world_matrix, + const std::array &vertices, + uint64_t triangle_key, + bool pass_through_model) +{ + if (!projection_triangle_is_fully_inside_paintable_image(context, paintable_mask, world_matrix, vertices)) + return false; + if (pass_through_model) + return true; + if (projection_section_view_active(context)) + return false; + return projection_triangle_is_fully_visible(visibility, context, world_matrix, vertices, triangle_key); +} + struct ProjectionRegionStateSource { std::vector> triangles_per_type; @@ -6505,8 +6658,10 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj const Matrix3d world_normal_matrix = world_matrix.matrix().block(0, 0, 3, 3).inverse().transpose(); const std::vector vertex_normals = projection_smoothed_vertex_normals(its); std::vector projected_triangles(its.indices.size(), false); + std::vector fully_projected_triangles(its.indices.size(), false); std::vector projected_triangle_depths(its.indices.size(), 0); size_t projected_triangle_count = 0; + size_t fully_projected_triangle_count = 0; for (size_t tri_idx = 0; tri_idx < its.indices.size(); ++tri_idx) { if (check_cancel) @@ -6538,6 +6693,16 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj texture_mapping_depth_from_span(projection_triangle_overlay_image_pixel_span(context, world_matrix, vertices), projection_target_span, 7); + if (projection_region_triangle_is_fully_projected(context, + visibility, + paintable_mask, + world_matrix, + vertices, + projection_visibility_triangle_key(volume_idx, tri_idx), + pass_through_model)) { + fully_projected_triangles[tri_idx] = true; + ++fully_projected_triangle_count; + } ++projected_triangle_count; } @@ -6547,11 +6712,17 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj } const TriangleSelector::TriangleSplittingData &old_data = volume->mmu_segmentation_facets.get_data(); - const std::vector existing_depths = projection_region_existing_source_triangle_depths(old_data, its.indices.size()); - const std::vector rgb_depths = - rgb_existing_source_triangle_depths(volume->texture_mapping_color_facets.get_data(), its.indices.size()); - const ProjectionRegionStateSource state_source = build_projection_region_state_source(*volume); - const int projected_safe_max_depth = texture_mapping_depth_for_budget(projected_triangle_count, 7, 2200000); + const size_t sampled_projected_triangle_count = projected_triangle_count - fully_projected_triangle_count; + std::vector existing_depths; + std::vector rgb_depths; + ProjectionRegionStateSource state_source; + int projected_safe_max_depth = 0; + if (sampled_projected_triangle_count > 0) { + existing_depths = projection_region_existing_source_triangle_depths(old_data, its.indices.size()); + rgb_depths = rgb_existing_source_triangle_depths(volume->texture_mapping_color_facets.get_data(), its.indices.size()); + state_source = build_projection_region_state_source(*volume); + projected_safe_max_depth = texture_mapping_depth_for_budget(sampled_projected_triangle_count, 7, 2200000); + } TriangleSelector::TriangleSplittingData new_data; new_data.triangles_to_split.reserve(std::max(old_data.triangles_to_split.size(), projected_triangle_count)); @@ -6614,6 +6785,12 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj continue; } + if (tri_idx < fully_projected_triangles.size() && fully_projected_triangles[tri_idx]) { + new_data.triangles_to_split.emplace_back(int(tri_idx), int(new_data.bitstream.size())); + projection_region_append_leaf(new_data, texture_mapping_filament_id); + continue; + } + const std::array vertices = { its.vertices[size_t(tri[0])].cast(), its.vertices[size_t(tri[1])].cast(),