diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index fd3569e2ca7..50d4cfcfa6f 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -978,12 +978,51 @@ GLVolumeWithIdAndZList volumes_to_render(const GLVolumePtrs& volumes, GLVolumeCo return list; } -int GLVolumeCollection::get_selection_support_threshold_angle(bool &enable_support) const +// ORCA: Compute slope.normal_z for 3D overhang highlight directly from support settings. +// If support_threshold_angle is 0, use tree fallback angle (30 deg) for tree supports, +// and derive an equivalent angle from threshold overlap for normal supports. +float GLVolumeCollection::get_selection_support_normal_z() const { - const DynamicPrintConfig& glb_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config; - enable_support = glb_cfg.opt_bool("enable_support"); - int support_threshold_angle = glb_cfg.opt_int("support_threshold_angle"); - return support_threshold_angle ; + const DynamicPrintConfig& glb_cfg = GUI::wxGetApp().preset_bundle->prints.get_edited_preset().config; + const auto& full_cfg = GUI::wxGetApp().preset_bundle->full_config(); + const auto support_type = glb_cfg.opt_enum("support_type"); + const int support_threshold_angle = glb_cfg.opt_int("support_threshold_angle"); + double angle_rad; + + if (support_threshold_angle > 0) { + // Match support generation: explicit threshold angles are treated as inclusive. + const int effective_support_threshold_angle = std::min(support_threshold_angle + 1, 89); + angle_rad = Geometry::deg2rad(static_cast(effective_support_threshold_angle)); + } else if (is_tree(support_type)) { + angle_rad = Geometry::deg2rad(30.0); // fallback value for tree supports + } else { // For normal supports, if the angle is set to 0, calculate normal_z from overlap. + const double layer_height = full_cfg.opt_float("layer_height"); + const auto* nozzle_diameter_opt = full_cfg.option("nozzle_diameter"); + const int wall_filament = full_cfg.opt_int("wall_filament"); + const size_t nozzle_count = nozzle_diameter_opt->values.size(); + const size_t wall_extruder_idx = (wall_filament > 0 && wall_filament <= static_cast(nozzle_count)) + ? static_cast(wall_filament - 1) + : 0; // Invalid extruder index falls back to extruder 1. + + // Use wall extruder's nozzle diameter for better estimation of external perimeter width, + // which is more relevant to overhang printing than the default nozzle diameter. + const double nozzle_diameter = nozzle_diameter_opt->values[wall_extruder_idx]; + + double external_perimeter_width = full_cfg.get_abs_value("outer_wall_line_width", nozzle_diameter); + if (external_perimeter_width <= 0.0) { + external_perimeter_width = full_cfg.get_abs_value("line_width", nozzle_diameter); + + if (external_perimeter_width <= 0.0) + external_perimeter_width = nozzle_diameter; + } + + const double overlap_width = full_cfg.get_abs_value("support_threshold_overlap", external_perimeter_width); + const double lower_layer_offset = std::max(0.0, external_perimeter_width - overlap_width); + + angle_rad = lower_layer_offset <= EPSILON ? Geometry::deg2rad(89.0) : std::atan(layer_height / lower_layer_offset); + } + + return static_cast(-std::cos(std::clamp(angle_rad, 0.0, Geometry::deg2rad(89.0)))); } //BBS: add outline drawing logic @@ -1019,6 +1058,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, if (disable_cullface) glsafe(::glDisable(GL_CULL_FACE)); + const float support_normal_z = get_selection_support_normal_z(); + for (GLVolumeWithIdAndZ& volume : to_render) { #if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT if (type == ERenderType::Transparent) { @@ -1075,16 +1116,11 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type, //use -1 ad a invalid type shader->set_uniform("print_volume.type", -1); } - - bool enable_support; - int support_threshold_angle = get_selection_support_threshold_angle(enable_support); - - float normal_z = -::cos(Geometry::deg2rad((float) support_threshold_angle)); shader->set_uniform("volume_world_matrix", volume.first->world_matrix()); shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower); shader->set_uniform("slope.volume_world_normal_matrix", static_cast(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast())); - shader->set_uniform("slope.normal_z", normal_z); + shader->set_uniform("slope.normal_z", support_normal_z); #if ENABLE_ENVIRONMENT_MAP unsigned int environment_texture_id = GUI::wxGetApp().plater()->get_environment_texture_id(); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index b12d048aa9a..76d10620d96 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -488,7 +488,7 @@ public: GLVolume* new_toolpath_volume(const ColorRGBA& rgba); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); - int get_selection_support_threshold_angle(bool&) const; + float get_selection_support_normal_z() const; // Render the volumes by OpenGL. //BBS: add outline drawing logic void render(ERenderType type,