From 6952711491bf8939bfa41538d3f2374063e6d2dc Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Sun, 10 May 2026 06:39:16 +0100 Subject: [PATCH] Hide other objects when image projection panel is open. Prevent projecting on back face when using section view (unless pass through model is enabled) --- .../process/fdm_process_U1_common.json | 2 +- src/libslic3r/PrintConfig.cpp | 2 +- src/slic3r/GUI/3DScene.cpp | 2 + .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 212 +++++++++++++++--- 4 files changed, 189 insertions(+), 29 deletions(-) diff --git a/resources/profiles/Snapmaker/process/fdm_process_U1_common.json b/resources/profiles/Snapmaker/process/fdm_process_U1_common.json index 05f736422cf..5fb7295a45d 100644 --- a/resources/profiles/Snapmaker/process/fdm_process_U1_common.json +++ b/resources/profiles/Snapmaker/process/fdm_process_U1_common.json @@ -46,7 +46,7 @@ "internal_solid_infill_speed": "150", "initial_layer_infill_speed": "60", "resolution": "0.012", - "support_type": "normal(auto)", + "support_type": "tree(auto)", "support_style": "default", "support_top_z_distance": "0.2", "support_bottom_z_distance": "0.2", diff --git a/src/libslic3r/PrintConfig.cpp b/src/libslic3r/PrintConfig.cpp index ab145f0abc4..a65f293dbaa 100644 --- a/src/libslic3r/PrintConfig.cpp +++ b/src/libslic3r/PrintConfig.cpp @@ -5702,7 +5702,7 @@ void PrintConfigDef::init_fff_params() def->enum_labels.push_back(L("Normal (manual)")); def->enum_labels.push_back(L("Tree (manual)")); def->mode = comSimple; - def->set_default_value(new ConfigOptionEnum(stNormalAuto)); + def->set_default_value(new ConfigOptionEnum(stTreeAuto)); def = this->add("support_object_xy_distance", coFloat); def->label = L("Support/object XY distance"); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 44bef05be3b..1467630b9db 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -2198,6 +2198,8 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo for (unsigned int i = 0; i < (unsigned int)volumes.size(); ++i) { GLVolume* volume = volumes[i]; + if (!volume->is_active) + continue; bool is_transparent = volume->render_color.is_transparent(); if (volume->is_wipe_tower) { GLWipeTowerVolume *wipe_tower_volume = static_cast(volume); diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index af6ada234ce..864e4eae4a2 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -2082,6 +2082,8 @@ static bool initialize_volume_rgb_data(ModelVolume &volume, const ColorRGBA &bac struct ProjectionContext { Matrix4d view_projection = Matrix4d::Identity(); + Vec3d camera_forward = Vec3d::Zero(); + Vec3d camera_position = Vec3d::Zero(); int canvas_width = 1; int canvas_height = 1; float overlay_left = 0.f; @@ -2106,6 +2108,26 @@ static bool projection_world_point_visible_in_section(const ProjectionContext &c return !projection_section_view_active(context) || context.section_clipping_plane.distance(world_point) >= -1e-7; } +static bool projection_sample_allowed_by_camera_facing(const ProjectionContext &context, + const Vec3d &world_point, + const Vec3d &world_normal) +{ + static constexpr double back_face_rejection_dot = 0.25; + + if (!projection_section_view_active(context)) + return true; + if (world_normal.squaredNorm() <= EPSILON) + return true; + + Vec3d view_direction = world_point - context.camera_position; + if (view_direction.squaredNorm() <= EPSILON) + view_direction = context.camera_forward; + if (view_direction.squaredNorm() <= EPSILON) + return true; + + return world_normal.normalized().dot(view_direction.normalized()) <= back_face_rejection_dot; +} + static std::vector projection_visible_world_polygon(const ProjectionContext &context, const Transform3d &world_matrix, const std::array &vertices) @@ -3190,6 +3212,73 @@ static Transform3d projection_world_matrix_for_volume(const GLCanvas3D &parent, return instance->get_transformation().get_matrix() * volume->get_matrix(); } +static std::vector projection_smoothed_vertex_normals(const indexed_triangle_set &its) +{ + std::vector normals(its.vertices.size(), Vec3d::Zero()); + for (const stl_triangle_vertex_indices &tri : its.indices) { + 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 Vec3d v0 = its.vertices[size_t(tri[0])].cast(); + const Vec3d v1 = its.vertices[size_t(tri[1])].cast(); + const Vec3d v2 = its.vertices[size_t(tri[2])].cast(); + const Vec3d normal = (v1 - v0).cross(v2 - v0); + if (normal.squaredNorm() <= EPSILON) + continue; + + normals[size_t(tri[0])] += normal; + normals[size_t(tri[1])] += normal; + normals[size_t(tri[2])] += normal; + } + + for (Vec3d &normal : normals) + if (normal.squaredNorm() > EPSILON) + normal.normalize(); + return normals; +} + +static Vec3d projection_interpolated_local_normal(const std::vector &vertex_normals, + const stl_triangle_vertex_indices &tri, + const Vec3f &barycentric) +{ + if (tri[0] < 0 || tri[1] < 0 || tri[2] < 0) + return Vec3d::Zero(); + if (size_t(tri[0]) >= vertex_normals.size() || + size_t(tri[1]) >= vertex_normals.size() || + size_t(tri[2]) >= vertex_normals.size()) + return Vec3d::Zero(); + + Vec3d normal = vertex_normals[size_t(tri[0])] * double(barycentric.x()) + + vertex_normals[size_t(tri[1])] * double(barycentric.y()) + + vertex_normals[size_t(tri[2])] * double(barycentric.z()); + if (normal.squaredNorm() > EPSILON) + normal.normalize(); + return normal; +} + +static bool projection_point_allowed_by_camera_facing(const ProjectionContext &context, + const Transform3d &world_matrix, + const Matrix3d &world_normal_matrix, + const std::vector &vertex_normals, + const stl_triangle_vertex_indices &tri, + const Vec3f &point, + const Vec3f &barycentric) +{ + if (!projection_section_view_active(context)) + return true; + + const Vec3d world_point = world_matrix * point.cast(); + const Vec3d local_normal = projection_interpolated_local_normal(vertex_normals, tri, barycentric); + Vec3d world_normal = world_normal_matrix * local_normal; + if (world_normal.squaredNorm() > EPSILON) + world_normal.normalize(); + return projection_sample_allowed_by_camera_facing(context, world_point, world_normal); +} + struct ProjectionVisibility { int width = 0; @@ -3204,8 +3293,8 @@ struct ProjectionVisibility static constexpr float PROJECTION_VISIBILITY_DEPTH_TOLERANCE = 2e-4f; static constexpr float PROJECTION_VISIBILITY_SAME_TRIANGLE_DEPTH_TOLERANCE = 2e-3f; -static constexpr float PROJECTION_VISIBILITY_PROJECTED_TRIANGLE_DEPTH_TOLERANCE = 2e-3f; -static constexpr float PROJECTION_VISIBILITY_MAX_LOCAL_DEPTH_TOLERANCE = 8e-3f; +static constexpr float PROJECTION_VISIBILITY_PROJECTED_TRIANGLE_DEPTH_TOLERANCE = 5e-3f; +static constexpr float PROJECTION_VISIBILITY_MAX_LOCAL_DEPTH_TOLERANCE = 2e-2f; static constexpr uint64_t PROJECTION_VISIBILITY_INVALID_TRIANGLE_KEY = std::numeric_limits::max(); static uint64_t projection_visibility_triangle_key(size_t volume_idx, size_t tri_idx) @@ -3275,8 +3364,6 @@ static bool projection_visibility_depth_matches_sample(const ProjectionVisibilit const size_t center_idx = size_t(y) * size_t(visibility.width) + size_t(x); const float center_nearest = visibility.depth[center_idx]; - if (!std::isfinite(center_nearest)) - return false; const bool has_triangle_key = triangle_key != PROJECTION_VISIBILITY_INVALID_TRIANGLE_KEY; const bool center_same_triangle = @@ -3288,17 +3375,17 @@ static bool projection_visibility_depth_matches_sample(const ProjectionVisibilit } else if (has_triangle_key) { center_tolerance = std::max(PROJECTION_VISIBILITY_PROJECTED_TRIANGLE_DEPTH_TOLERANCE, local_tolerance); } - if (depth <= center_nearest + center_tolerance) + if (std::isfinite(center_nearest) && depth <= center_nearest + center_tolerance) return true; - if (!has_triangle_key || - depth > center_nearest + std::max(PROJECTION_VISIBILITY_SAME_TRIANGLE_DEPTH_TOLERANCE, local_tolerance)) + if (!has_triangle_key) return false; - const int min_x = std::max(0, x - 1); - const int max_x = std::min(visibility.width - 1, x + 1); - const int min_y = std::max(0, y - 1); - const int max_y = std::min(visibility.height - 1, y + 1); + const int search_radius = std::isfinite(center_nearest) ? 1 : 2; + const int min_x = std::max(0, x - search_radius); + const int max_x = std::min(visibility.width - 1, x + search_radius); + const int min_y = std::max(0, y - search_radius); + const int max_y = std::min(visibility.height - 1, y + search_radius); for (int sample_y = min_y; sample_y <= max_y; ++sample_y) { for (int sample_x = min_x; sample_x <= max_x; ++sample_x) { if (sample_x == x && sample_y == y) @@ -3317,6 +3404,21 @@ static bool projection_visibility_depth_matches_sample(const ProjectionVisibilit } } + for (int sample_y = min_y; sample_y <= max_y; ++sample_y) { + for (int sample_x = min_x; sample_x <= max_x; ++sample_x) { + if (sample_x == x && sample_y == y) + continue; + + const size_t idx = size_t(sample_y) * size_t(visibility.width) + size_t(sample_x); + const float nearest = visibility.depth[idx]; + const float nearby_tolerance = + std::max(PROJECTION_VISIBILITY_PROJECTED_TRIANGLE_DEPTH_TOLERANCE, + projection_visibility_local_depth_tolerance(visibility, sample_x, sample_y)); + if (std::isfinite(nearest) && depth <= nearest + nearby_tolerance) + return true; + } + } + return false; } @@ -4065,6 +4167,8 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj continue; const Transform3d world_matrix = projection_world_matrix_for_volume(parent, &object, volume, instance_idx); + 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 projected_triangle_depths(its.indices.size(), 0); size_t projected_triangle_count = 0; @@ -4116,20 +4220,31 @@ static bool project_texture_mapping_zone_to_regions(ModelObject &obj ProjectionRegionStateSampler sampler = [context, world_matrix, + world_normal_matrix, pass_through_model, texture_mapping_filament_id, + volume, volume_idx, &visibility, + &vertex_normals, &projected_triangles, - &state_source](size_t tri_idx, const Vec3f &point, const Vec3f &) { + &state_source](size_t tri_idx, const Vec3f &point, const Vec3f &barycentric) { unsigned int state = sample_projection_region_state(state_source, int(tri_idx), point); if (tri_idx < projected_triangles.size() && projected_triangles[tri_idx]) { + const stl_triangle_vertex_indices &tri = volume->mesh().its.indices[tri_idx]; if (pass_through_model || - projection_point_is_visible(visibility, - context, - world_matrix, - point, - projection_visibility_triangle_key(volume_idx, tri_idx))) { + (projection_point_allowed_by_camera_facing(context, + world_matrix, + world_normal_matrix, + vertex_normals, + tri, + point, + barycentric) && + projection_point_is_visible(visibility, + context, + world_matrix, + point, + projection_visibility_triangle_key(volume_idx, tri_idx)))) { if (std::optional projected = projected_image_color_at_point(context, world_matrix, point); projected && projection_overlay_has_paintable_alpha(*projected, context)) { state = texture_mapping_filament_id; @@ -9435,6 +9550,8 @@ bool GLGizmoImageProjection::project_image_to_selected_object() ProjectionContext context; context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + context.camera_forward = camera.get_dir_forward(); + context.camera_position = camera.get_position(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -9477,6 +9594,8 @@ bool GLGizmoImageProjection::project_to_vertex_colors(ModelObject *object) ProjectionContext context; context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + context.camera_forward = camera.get_dir_forward(); + context.camera_position = camera.get_position(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -9546,6 +9665,8 @@ bool GLGizmoImageProjection::project_to_vertex_colors(ModelObject *object) std::vector> projected_accum(its.vertices.size(), { 0.f, 0.f, 0.f, 0.f }); std::vector projected_counts(its.vertices.size(), 0); const Transform3d world_matrix = projection_world_matrix_for_volume(m_parent, object, volume, instance_idx); + 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); for (size_t tri_idx = 0; tri_idx < its.indices.size(); ++tri_idx) { const stl_triangle_vertex_indices &tri = its.indices[tri_idx]; @@ -9561,8 +9682,20 @@ bool GLGizmoImageProjection::project_to_vertex_colors(ModelObject *object) its.vertices[size_t(tri[1])].cast(), its.vertices[size_t(tri[2])].cast() }; + for (int corner = 0; corner < 3; ++corner) { const size_t vertex_idx = size_t(tri[corner]); + Vec3f barycentric = Vec3f::Zero(); + barycentric[corner] = 1.f; + if (!m_pass_through_model && + !projection_point_allowed_by_camera_facing(context, + world_matrix, + world_normal_matrix, + vertex_normals, + tri, + vertices[size_t(corner)], + barycentric)) + continue; if (!m_pass_through_model && !projection_point_is_visible(visibility, context, @@ -9611,6 +9744,8 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object) ProjectionContext context; context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + context.camera_forward = camera.get_dir_forward(); + context.camera_position = camera.get_position(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -9672,6 +9807,8 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object) const ColorRGBA fallback_color = projection_base_color_for_volume(*volume); const Transform3d world_matrix = projection_world_matrix_for_volume(m_parent, object, volume, instance_idx); + 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); const bool had_raw_atlas_texture = model_volume_has_raw_atlas_texture_data(volume); const bool discard_existing_texture_for_raw_atlas = raw_atlas_projection && @@ -9818,11 +9955,18 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object) raw_seeded_pixels[raw_seed_idx] = 1; } if (m_pass_through_model || - projection_point_is_visible(visibility, - context, - world_matrix, - point, - projection_visibility_triangle_key(volume_idx, tri_idx))) { + (projection_point_allowed_by_camera_facing(context, + world_matrix, + world_normal_matrix, + vertex_normals, + tri, + point, + barycentric) && + projection_point_is_visible(visibility, + context, + world_matrix, + point, + projection_visibility_triangle_key(volume_idx, tri_idx)))) { if (std::optional projected = projected_image_color_at_point(context, world_matrix, point)) { const bool transparent_sample = !context.apply_transparency_as_background && @@ -9903,6 +10047,8 @@ bool GLGizmoImageProjection::project_to_rgb_data(ModelObject *object) ProjectionContext context; context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); + context.camera_forward = camera.get_dir_forward(); + context.camera_position = camera.get_position(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -9934,6 +10080,8 @@ bool GLGizmoImageProjection::project_to_rgb_data(ModelObject *object) const VolumeColorSource source = build_volume_color_source(*volume); const ColorRGBA fallback_color = projection_base_color_for_volume(*volume); const Transform3d world_matrix = projection_world_matrix_for_volume(m_parent, object, volume, instance_idx); + 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 projected_triangle_depths(its.indices.size(), 0); const float projection_target_span = image_projection_rgb_target_triangle_pixel_span(context); @@ -9978,20 +10126,30 @@ bool GLGizmoImageProjection::project_to_rgb_data(ModelObject *object) source, context, world_matrix, + world_normal_matrix, fallback_color, volume_idx, + &vertex_normals, &projected_triangles, &visibility](size_t tri_idx, const Vec3f &point, const Vec3f &barycentric) { ColorRGBA color = sample_volume_color_source(*volume, source, tri_idx, point, barycentric, true, &fallback_color); if (tri_idx < projected_triangles.size() && projected_triangles[tri_idx]) { + const stl_triangle_vertex_indices &tri = volume->mesh().its.indices[tri_idx]; if (m_pass_through_model || - projection_point_is_visible(visibility, - context, - world_matrix, - point, - projection_visibility_triangle_key(volume_idx, tri_idx))) { + (projection_point_allowed_by_camera_facing(context, + world_matrix, + world_normal_matrix, + vertex_normals, + tri, + point, + barycentric) && + projection_point_is_visible(visibility, + context, + world_matrix, + point, + projection_visibility_triangle_key(volume_idx, tri_idx)))) { if (std::optional projected = projected_image_color_at_point(context, world_matrix, point)) { if (!context.apply_transparency_as_background && !projection_overlay_has_paintable_alpha(*projected, context)) return pack_vertex_color_rgba(color);