From c40a1a9836b66719ca157d306794d967e80388a5 Mon Sep 17 00:00:00 2001 From: Sietse Brouwer Date: Thu, 12 Jun 2025 08:20:46 +0200 Subject: [PATCH 1/3] Fix #120291: Grease Pencil: Geometry not selected after paste In Grease Pencil, when pasting geometry from the clipboard, the pasted geometry should always be selected, so that the user can continue working with it. In some cases though, the geometry wasn't selected. And selection failed when there was a mismatch between the selection domain on the clipboard and the active selection domain when pasting (e.g. selection domain on the clipboard was 'Point' and the active selection domain was 'Curve'). This PR ensures that the pasted geometry is always selected, in the correct selection domain. Pull Request: https://projects.blender.org/blender/blender/pulls/140127 --- .../editors/grease_pencil/intern/grease_pencil_edit.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc index 0cfdeb86cb2..22d3cd35062 100644 --- a/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc +++ b/source/blender/editors/grease_pencil/intern/grease_pencil_edit.cc @@ -2683,11 +2683,18 @@ static wmOperatorStatus grease_pencil_paste_strokes_exec(bContext *C, wmOperator const bool keep_world_transform = RNA_boolean_get(op->ptr, "keep_world_transform"); const bool paste_on_back = RNA_boolean_get(op->ptr, "paste_back"); - const Clipboard &clipboard = ensure_grease_pencil_clipboard(); + Clipboard &clipboard = ensure_grease_pencil_clipboard(); if (clipboard.layers.is_empty()) { return OPERATOR_CANCELLED; } + /* Make sure everything on the clipboard is selected, in the correct selection domain. */ + threading::parallel_for_each(clipboard.layers, [&](Clipboard::ClipboardLayer &layer) { + bke::GSpanAttributeWriter selection = ed::curves::ensure_selection_attribute( + layer.curves, selection_domain, CD_PROP_BOOL); + selection.finish(); + }); + if (type == PasteType::Active) { Layer *active_layer = grease_pencil.get_active_layer(); if (!active_layer) { From 7099cb3c9aec0c3c30d7e3824b35725f853d8a17 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 12 Jun 2025 11:31:33 +0200 Subject: [PATCH 2/3] Fix #132944: improve handling of high subdivision levels in nodes Previously, the subdivision level was clamped to 11. While such high subdivision levels are rare, there are still use-cases for going above those if hardware resources allow for it. This patch sets the hard upper limit to 15. When going above that, even subdividing a single triangle would result in data that's too large to store in `Mesh` because it has too many face corners for an `int`. Furthermore, instead of clamping the subdiv level, there is an error when going above that. This might help when accidentally connecting a value >= 16 to the subdiv level input. Pull Request: https://projects.blender.org/blender/blender/pulls/140057 --- .../blender/blenkernel/BKE_blender_version.h | 2 +- .../blenloader/intern/versioning_450.cc | 72 +++++++++++++++++++ .../geometry/nodes/node_geo_mesh_subdivide.cc | 8 ++- .../nodes/node_geo_subdivision_surface.cc | 8 ++- 4 files changed, 87 insertions(+), 3 deletions(-) diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 9fd1a0c3f44..8f296e7a2c7 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -27,7 +27,7 @@ /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 86 +#define BLENDER_FILE_SUBVERSION 87 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and cancel loading the file, showing a warning to diff --git a/source/blender/blenloader/intern/versioning_450.cc b/source/blender/blenloader/intern/versioning_450.cc index 26ce0022e41..a34cd73d178 100644 --- a/source/blender/blenloader/intern/versioning_450.cc +++ b/source/blender/blenloader/intern/versioning_450.cc @@ -4424,6 +4424,69 @@ static void do_version_flip_node_options_to_inputs(bNodeTree *node_tree, bNode * } } +static void clamp_subdivision_node_level_input(bNodeTree &tree) +{ + blender::Map links_to_level_and_max_inputs; + LISTBASE_FOREACH (bNodeLink *, link, &tree.links) { + if (link->tosock) { + if (ELEM(blender::StringRef(link->tosock->identifier), "Level", "Max")) { + links_to_level_and_max_inputs.add(link->tosock, link); + } + } + } + LISTBASE_FOREACH_MUTABLE (bNode *, node, &tree.nodes) { + if (!ELEM(node->type_legacy, GEO_NODE_SUBDIVISION_SURFACE, GEO_NODE_SUBDIVIDE_MESH)) { + continue; + } + bNodeSocket *level_input = blender::bke::node_find_socket(*node, SOCK_IN, "Level"); + if (!level_input || level_input->type != SOCK_INT) { + continue; + } + bNodeLink *link = links_to_level_and_max_inputs.lookup_default(level_input, nullptr); + if (link) { + bNode *origin_node = link->fromnode; + if (origin_node->type_legacy == SH_NODE_CLAMP) { + bNodeSocket *max_input_socket = blender::bke::node_find_socket( + *origin_node, SOCK_IN, "Max"); + if (max_input_socket->type == SOCK_FLOAT && + !links_to_level_and_max_inputs.contains(max_input_socket)) + { + if (max_input_socket->default_value_typed()->value <= 11.0f) { + /* There is already a clamp node, so no need to add another one. */ + continue; + } + } + } + /* Insert clamp node. */ + bNode &clamp_node = version_node_add_empty(tree, "ShaderNodeClamp"); + clamp_node.parent = node->parent; + clamp_node.location[0] = node->location[0] - 25; + clamp_node.location[1] = node->location[1]; + bNodeSocket &clamp_value_input = version_node_add_socket( + tree, clamp_node, SOCK_IN, "NodeSocketFloat", "Value"); + bNodeSocket &clamp_min_input = version_node_add_socket( + tree, clamp_node, SOCK_IN, "NodeSocketFloat", "Min"); + bNodeSocket &clamp_max_input = version_node_add_socket( + tree, clamp_node, SOCK_IN, "NodeSocketFloat", "Max"); + bNodeSocket &clamp_value_output = version_node_add_socket( + tree, clamp_node, SOCK_OUT, "NodeSocketFloat", "Result"); + + static_cast(clamp_min_input.default_value)->value = 0.0f; + static_cast(clamp_max_input.default_value)->value = 11.0f; + + link->tosock = &clamp_value_input; + version_node_add_link(tree, clamp_node, clamp_value_output, *node, *level_input); + } + else { + /* Clamp value directly. */ + bNodeSocketValueInt *value = level_input->default_value_typed(); + value->value = std::clamp(value->value, 0, 11); + } + } + + version_socket_update_is_used(&tree); +} + void do_versions_after_linking_450(FileData * /*fd*/, Main *bmain) { if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 8)) { @@ -6302,6 +6365,15 @@ void blo_do_versions_450(FileData * /*fd*/, Library * /*lib*/, Main *bmain) fix_curve_nurbs_knot_mode_custom(bmain); } + if (!MAIN_VERSION_FILE_ATLEAST(bmain, 405, 87)) { + FOREACH_NODETREE_BEGIN (bmain, tree, id) { + if (tree->type == NTREE_GEOMETRY) { + clamp_subdivision_node_level_input(*tree); + } + } + FOREACH_NODETREE_END; + } + /* Always run this versioning (keep at the bottom of the function). Meshes are written with the * legacy format which always needs to be converted to the new format on file load. To be moved * to a subversion check in 5.0. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc index ccc36fbaecc..e702c68f375 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_subdivide.cc @@ -58,11 +58,17 @@ static void node_geo_exec(GeoNodeExecParams params) GeometrySet geometry_set = params.extract_input("Mesh"); #ifdef WITH_OPENSUBDIV /* See CCGSUBSURF_LEVEL_MAX for max limit. */ - const int level = clamp_i(params.extract_input("Level"), 0, 11); + const int level = std::max(params.extract_input("Level"), 0); if (level == 0) { params.set_output("Mesh", std::move(geometry_set)); return; } + /* At this limit, a subdivided single triangle would be too large to be stored in #Mesh. */ + if (level >= 16) { + params.error_message_add(NodeWarningType::Error, TIP_("The subdivision level is too large")); + params.set_default_remaining_outputs(); + return; + } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (const Mesh *mesh = geometry_set.get_mesh()) { diff --git a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc index 1b4c000815c..fa869d59001 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_subdivision_surface.cc @@ -186,12 +186,18 @@ static void node_geo_exec(GeoNodeExecParams params) const NodeGeometrySubdivisionSurface &storage = node_storage(params.node()); const int uv_smooth = storage.uv_smooth; const int boundary_smooth = storage.boundary_smooth; - const int level = std::clamp(params.extract_input("Level"), 0, 11); + const int level = std::max(params.extract_input("Level"), 0); const bool use_limit_surface = params.extract_input("Limit Surface"); if (level == 0) { params.set_output("Mesh", std::move(geometry_set)); return; } + /* At this limit, a subdivided single triangle would be too large to be stored in #Mesh. */ + if (level >= 16) { + params.error_message_add(NodeWarningType::Error, TIP_("The subdivision level is too large")); + params.set_default_remaining_outputs(); + return; + } geometry_set.modify_geometry_sets([&](GeometrySet &geometry_set) { if (const Mesh *mesh = geometry_set.get_mesh()) { From 794cd8974b1fa56adf63b967c35f6dd7261a3ff3 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 12 Jun 2025 11:52:51 +0200 Subject: [PATCH 3/3] Fix #140240: overlapping buttons in color input node --- source/blender/nodes/composite/nodes/node_composite_rgb.cc | 2 +- source/blender/nodes/function/nodes/node_fn_input_color.cc | 2 +- source/blender/nodes/shader/nodes/node_shader_rgb.cc | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/source/blender/nodes/composite/nodes/node_composite_rgb.cc b/source/blender/nodes/composite/nodes/node_composite_rgb.cc index dea3657d043..a9f5e997697 100644 --- a/source/blender/nodes/composite/nodes/node_composite_rgb.cc +++ b/source/blender/nodes/composite/nodes/node_composite_rgb.cc @@ -27,7 +27,7 @@ static void cmp_node_rgb_declare(NodeDeclarationBuilder &b) .default_value({0.5f, 0.5f, 0.5f, 1.0f}) .custom_draw([](CustomSocketDrawParams ¶ms) { uiLayoutSetAlignment(¶ms.layout, UI_LAYOUT_ALIGN_EXPAND); - uiLayout &col = params.layout.column(true); + uiLayout &col = params.layout.column(false); uiTemplateColorPicker( &col, ¶ms.socket_ptr, "default_value", true, false, false, false); col.prop(¶ms.socket_ptr, "default_value", UI_ITEM_R_SLIDER, "", ICON_NONE); diff --git a/source/blender/nodes/function/nodes/node_fn_input_color.cc b/source/blender/nodes/function/nodes/node_fn_input_color.cc index ba052b711dd..410aa8c77b8 100644 --- a/source/blender/nodes/function/nodes/node_fn_input_color.cc +++ b/source/blender/nodes/function/nodes/node_fn_input_color.cc @@ -15,7 +15,7 @@ static void node_declare(NodeDeclarationBuilder &b) { b.add_output("Color").custom_draw([](CustomSocketDrawParams ¶ms) { uiLayoutSetAlignment(¶ms.layout, UI_LAYOUT_ALIGN_EXPAND); - uiLayout &col = params.layout.column(true); + uiLayout &col = params.layout.column(false); uiTemplateColorPicker(&col, ¶ms.node_ptr, "value", true, false, false, true); col.prop(¶ms.node_ptr, "value", UI_ITEM_R_SPLIT_EMPTY_NAME, "", ICON_NONE); }); diff --git a/source/blender/nodes/shader/nodes/node_shader_rgb.cc b/source/blender/nodes/shader/nodes/node_shader_rgb.cc index ec73580405d..eff4d1a3023 100644 --- a/source/blender/nodes/shader/nodes/node_shader_rgb.cc +++ b/source/blender/nodes/shader/nodes/node_shader_rgb.cc @@ -19,7 +19,7 @@ static void node_declare(NodeDeclarationBuilder &b) .default_value({0.5f, 0.5f, 0.5f, 1.0f}) .custom_draw([](CustomSocketDrawParams ¶ms) { uiLayoutSetAlignment(¶ms.layout, UI_LAYOUT_ALIGN_EXPAND); - uiLayout &col = params.layout.column(true); + uiLayout &col = params.layout.column(false); uiTemplateColorPicker( &col, ¶ms.socket_ptr, "default_value", true, false, false, false); col.prop(¶ms.socket_ptr, "default_value", UI_ITEM_R_SLIDER, "", ICON_NONE);