Merge branch 'blender-v5.2-release'

This commit is contained in:
Lukas Tönne 2026-06-26 15:00:36 +02:00
commit 8faae58201
7 changed files with 87 additions and 78 deletions

View file

@ -28,7 +28,8 @@ struct bNodeType;
struct bNodeSocketType;
} // namespace bke
#define NODE_GRID_STEP_SIZE (20.0f * UI_SCALE_FAC) /* Based on the grid nodes snap to. */
#define NODE_GRID_UNIT 20.0f
#define NODE_GRID_STEP_SIZE (NODE_GRID_UNIT * UI_SCALE_FAC) /* Size of node snap grid. */
#define NODE_EDGE_PAN_INSIDE_PAD 2
#define NODE_EDGE_PAN_OUTSIDE_PAD 0 /* Disable clamping for node panning, use whole screen. */
#define NODE_EDGE_PAN_SPEED_RAMP 1

View file

@ -53,37 +53,33 @@
namespace blender {
const bNodeSocket &NodeAndSocket::find_socket_in_node(const bNode &other_node) const
const bNodeSocket *NodeAndSocket::find_socket_in_node(const bNode &other_node) const
{
/* Don't use "by_identifier" functions of bNode because they depend on valid topology cache. */
ListBaseT<bNodeSocket> sockets = (this->in_out == SOCK_IN) ? other_node.inputs :
other_node.outputs;
const bNodeSocket *socket = reinterpret_cast<bNodeSocket *>(BLI_findstring(
return reinterpret_cast<bNodeSocket *>(BLI_findstring(
&sockets, this->socket_identifier.c_str(), offsetof(bNodeSocket, identifier)));
BLI_assert(socket != nullptr);
return *socket;
}
bNodeSocket &NodeAndSocket::find_socket_in_node(bNode &other_node) const
bNodeSocket *NodeAndSocket::find_socket_in_node(bNode &other_node) const
{
return const_cast<bNodeSocket &>(
return const_cast<bNodeSocket *>(
this->find_socket_in_node(const_cast<const bNode &>(other_node)));
}
const bNodeSocket &MutableNodeAndSocket::find_socket_in_node(const bNode &other_node) const
const bNodeSocket *MutableNodeAndSocket::find_socket_in_node(const bNode &other_node) const
{
/* Don't use "by_identifier" functions of bNode because they depend on valid topology cache. */
ListBaseT<bNodeSocket> sockets = (this->in_out == SOCK_IN) ? other_node.inputs :
other_node.outputs;
const bNodeSocket *socket = reinterpret_cast<bNodeSocket *>(BLI_findstring(
return reinterpret_cast<bNodeSocket *>(BLI_findstring(
&sockets, this->socket_identifier.c_str(), offsetof(bNodeSocket, identifier)));
BLI_assert(socket != nullptr);
return *socket;
}
bNodeSocket &MutableNodeAndSocket::find_socket_in_node(bNode &other_node) const
bNodeSocket *MutableNodeAndSocket::find_socket_in_node(bNode &other_node) const
{
return const_cast<bNodeSocket &>(
return const_cast<bNodeSocket *>(
this->find_socket_in_node(const_cast<const bNode &>(other_node)));
}
@ -397,9 +393,11 @@ void NodeSetInterfaceBuilder::expose_socket(const bNodeSocket &src_socket,
if (use_external_socket_key) {
/* Create a unique interface socket for each external link. */
for (const MutableNodeAndSocket &external_socket : external_links) {
if (InterfaceSocketData *data = try_add_socket_data(external_socket.find_socket())) {
data->internal_sockets.add({src_socket.owner_node(), src_socket, false});
data->external_sockets.add(external_socket);
if (const bNodeSocket *socket = external_socket.find_socket()) {
if (InterfaceSocketData *data = try_add_socket_data(*socket)) {
data->internal_sockets.add({src_socket.owner_node(), src_socket, false});
data->external_sockets.add(external_socket);
}
}
}
}
@ -816,8 +814,12 @@ GroupInputOutputNodes connect_copied_nodes_to_interface(const bContext &C,
for (const auto &item : io_mapping.socket_data.items()) {
for (const NodeAndSocket &origin : item.value.internal_sockets) {
bNode &new_node = *copied_nodes.node_map().lookup(&origin.node);
bNodeSocket &new_socket = origin.find_socket_in_node(new_node);
if (new_socket.is_input()) {
bNodeSocket *new_socket = origin.find_socket_in_node(new_node);
/* Some nodes change after unlinking (e.g. viewers), socket may not exist any more. */
if (!new_socket) {
continue;
}
if (new_socket->is_input()) {
bNodeSocket *group_input_socket = node_group_input_find_socket(io_nodes.input_node,
item.key->identifier);
BLI_assert(group_input_socket);
@ -826,7 +828,7 @@ GroupInputOutputNodes connect_copied_nodes_to_interface(const bContext &C,
*io_nodes.input_node,
*group_input_socket,
new_node,
new_socket,
*new_socket,
origin.link_muted,
origin.multi_input_sort_id);
}
@ -837,7 +839,7 @@ GroupInputOutputNodes connect_copied_nodes_to_interface(const bContext &C,
add_link_and_make_visible(tree,
links_by_socket,
new_node,
new_socket,
*new_socket,
*io_nodes.output_node,
*group_output_socket,
origin.link_muted,
@ -896,9 +898,10 @@ static bool any_link_need_conversion(const Span<MutableNodeAndSocket> links,
/* A proxy is needed if any internal or external connection has a different type and therefore
* cannot directly be connected without loss of conversion. */
for (const MutableNodeAndSocket &in_link : links) {
const bNodeSocket &in_socket = in_link.find_socket();
if (socket_types_need_conversion(in_socket.idname, io_socket.socket_type)) {
return true;
if (const bNodeSocket *in_socket = in_link.find_socket()) {
if (socket_types_need_conversion(in_socket->idname, io_socket.socket_type)) {
return true;
}
}
}
return false;
@ -1159,15 +1162,19 @@ InterfaceProxyNodes connect_copied_nodes_to_external_sockets(
/* Map the old internal to the new socket in the target tree. */
bNode &new_node = *copied_nodes.node_map().lookup_default(&origin.node, nullptr);
bNodeSocket &new_socket = origin.find_socket_in_node(new_node);
bNodeSocket *new_socket = origin.find_socket_in_node(new_node);
/* Some nodes change after unlinking (e.g. viewers), socket may not exist any more. */
if (!new_socket) {
continue;
}
if (is_input) {
outgoing_links.append(
{new_node, new_socket, origin.link_muted, origin.multi_input_sort_id});
{new_node, *new_socket, origin.link_muted, origin.multi_input_sort_id});
}
else {
/* Outputs shouldn't have multi-input sort ids. */
BLI_assert(!origin.multi_input_sort_id.has_value());
incoming_links.append({new_node, new_socket, origin.link_muted});
incoming_links.append({new_node, *new_socket, origin.link_muted});
}
}
@ -1191,14 +1198,18 @@ InterfaceProxyNodes connect_copied_nodes_to_external_sockets(
const bool link_muted = item.first.link_muted || item.second.link_muted;
/* Outputs shouldn't have multi-input sort ids. */
BLI_assert(!item.first.multi_input_sort_id.has_value());
add_link_and_make_visible(dst_tree,
links_by_socket,
item.first.node,
item.first.find_socket(),
item.second.node,
item.second.find_socket(),
link_muted,
item.second.multi_input_sort_id);
bNodeSocket *from_socket = item.first.find_socket();
bNodeSocket *to_socket = item.second.find_socket();
if (from_socket && to_socket) {
add_link_and_make_visible(dst_tree,
links_by_socket,
item.first.node,
*from_socket,
item.second.node,
*to_socket,
link_muted,
item.second.multi_input_sort_id);
}
}
BKE_animdata_copy_by_basepath(
@ -1235,14 +1246,16 @@ void connect_group_node_to_external_sockets(bNode &group_node,
}
for (const MutableNodeAndSocket &link : data->external_sockets) {
BLI_assert(owner_tree.all_nodes().contains(&link.node));
add_link_and_make_visible(owner_tree,
links_by_socket,
link.node,
link.find_socket(),
group_node,
*group_node_input,
link.link_muted,
std::nullopt);
if (bNodeSocket *link_socket = link.find_socket()) {
add_link_and_make_visible(owner_tree,
links_by_socket,
link.node,
*link_socket,
group_node,
*group_node_input,
link.link_muted,
std::nullopt);
}
}
/* Keep old socket visibility. */
SET_FLAG_FROM_TEST(group_node_input->flag, data->hidden, SOCK_HIDDEN);
@ -1260,14 +1273,16 @@ void connect_group_node_to_external_sockets(bNode &group_node,
}
for (const MutableNodeAndSocket &link : data->external_sockets) {
BLI_assert(owner_tree.all_nodes().contains(&link.node));
add_link_and_make_visible(owner_tree,
links_by_socket,
group_node,
*group_node_output,
link.node,
link.find_socket(),
link.link_muted,
link.multi_input_sort_id);
if (bNodeSocket *link_socket = link.find_socket()) {
add_link_and_make_visible(owner_tree,
links_by_socket,
group_node,
*group_node_output,
link.node,
*link_socket,
link.link_muted,
link.multi_input_sort_id);
}
}
/* Keep old socket visibility. */
SET_FLAG_FROM_TEST(group_node_output->flag, data->hidden, SOCK_HIDDEN);

View file

@ -516,7 +516,7 @@ float nearest_node_grid_coord(float co)
{
/* Size and location of nodes are independent of UI scale, so grid size should be independent of
* UI scale as well. */
float grid_size = grid_size_get() / UI_SCALE_FAC;
float grid_size = NODE_GRID_UNIT;
float rest = fmod(co, grid_size);
float offset = rest - grid_size / 2 >= 0 ? grid_size : 0;

View file

@ -97,10 +97,10 @@ struct NodeAndSocket {
return in_out == SOCK_OUT;
}
const bNodeSocket &find_socket_in_node(const bNode &other_node) const;
bNodeSocket &find_socket_in_node(bNode &other_node) const;
const bNodeSocket *find_socket_in_node(const bNode &other_node) const;
bNodeSocket *find_socket_in_node(bNode &other_node) const;
const bNodeSocket &find_socket() const
const bNodeSocket *find_socket() const
{
return find_socket_in_node(this->node);
}
@ -174,10 +174,10 @@ struct MutableNodeAndSocket {
return in_out == SOCK_OUT;
}
const bNodeSocket &find_socket_in_node(const bNode &other_node) const;
bNodeSocket &find_socket_in_node(bNode &other_node) const;
const bNodeSocket *find_socket_in_node(const bNode &other_node) const;
bNodeSocket *find_socket_in_node(bNode &other_node) const;
bNodeSocket &find_socket() const
bNodeSocket *find_socket() const
{
return find_socket_in_node(this->node);
}

View file

@ -23,7 +23,7 @@ static void node_declare(NodeDeclarationBuilder &b)
b.use_custom_socket_order();
b.allow_any_socket_order();
b.add_input<decl::Geometry>("Target"_ustr);
auto &target_geo = b.add_input<decl::Geometry>("Target"_ustr);
b.add_output<decl::Geometry>("Target"_ustr).align_with_previous().propagate_all_geometry();
b.add_output<decl::String>("Transferred Names"_ustr)
.structure_type(StructureType::List)
@ -40,10 +40,10 @@ static void node_declare(NodeDeclarationBuilder &b)
for (BaseSocketDeclarationBuilder *socket : sockets) {
socket->default_input_type(NODE_DEFAULT_INPUT_INDEX_FIELD);
socket->structure_type(StructureType::Field);
socket->evaluated_geometry_field({target_geo.index()});
}
}
b.add_input<decl::Geometry>("Source"_ustr);
auto &source_geo = b.add_input<decl::Geometry>("Source"_ustr);
{
auto &p = b.add_panel("Source IDs"_ustr).default_closed(true);
Vector<BaseSocketDeclarationBuilder *> sockets;
@ -56,7 +56,7 @@ static void node_declare(NodeDeclarationBuilder &b)
for (BaseSocketDeclarationBuilder *socket : sockets) {
socket->default_input_type(NODE_DEFAULT_INPUT_INDEX_FIELD);
socket->structure_type(StructureType::Field);
socket->evaluated_geometry_field({source_geo.index()});
}
}

View file

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d9044d14df274a4d9df0e12f3a66fc00ecabb2cb86b5eed33de7c2a45eaec605
size 336847
oid sha256:5cd9cb11dbb559a807c3915aa8de6ac1c5a1d1c4bd02b80b6038322a356b187a
size 341071

View file

@ -10,6 +10,9 @@ from mathutils import Vector
import bpy
# To update the test data, set the `BLENDER_TEST_UPDATE` environment variable:
# `BLENDER_TEST_UPDATE=1 ctest -R bl_node_copy_operators`
#
# Test cases listed below must have a test data in the "Tests" node group in the main test file.
# Each test case should have a frame node whose content is used as input for operators.
# Nodes labeled "external" are excluded from the grouping operator to test external links.
@ -27,13 +30,6 @@ import bpy
# - bpy.data.node_groups["ExpectedUngroup"]: Result of node.ungroup operator.
# - bpy.data.node_groups["ExpectedGroupSeparateCopy"]: Result of node.group_separate operator with type='COPY'.
# - bpy.data.node_groups["ExpectedGroupSeparateMove"]: Result of node.group_separate operator with type='MOVE'.
#
# The script can be invoked with an additional argument '--generate' to update the ground truth test data.
# Nodes in the "Expected***" node trees are replaced with the result of operators applied to the "Tests" node tree.
#
# ./bin/blender --factory-startup --python <SOURCEPATH>/tests/python/bl_node_copy_operators.py
# --
# --testdir <SOURCEPATH>/tests/files/node_group --generate
args = None
testfile = "node_copy_operators.blend"
@ -65,6 +61,7 @@ all_test_cases = [
('test_insert_group_with_sockets', {'GROUP_INSERT'}),
('test_ungroup_multiple', {'NODE_GROUP'}),
('test_ungroup_proxy_nodes', {'NODE_GROUP'}),
('test_viewer_node', {}),
]
@ -404,7 +401,6 @@ class AbstractNodeCopyOperatorTest(unittest.TestCase):
self.assertTrue(args.testdir.exists(),
'Test dir {0} should exist'.format(args.testdir))
open_test_file()
self.assertEqual(bpy.data.version, (5, 2, 5))
def tearDown(self):
self._tempdir.cleanup()
@ -632,6 +628,7 @@ def generate_test_data():
def main():
global args
import argparse
import os
if '--' in sys.argv:
argv = [sys.argv[0]] + sys.argv[sys.argv.index('--') + 1:]
@ -640,17 +637,13 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument('--testdir', required=True, type=pathlib.Path)
parser.add_argument(
'--generate',
action='store_true',
help="Generate ground truth test data instead of running the test")
parser.add_argument('--subtest', default=None, help="Select a single test case")
args, remaining = parser.parse_known_args(argv)
if args.generate:
if os.getenv("BLENDER_TEST_UPDATE") is not None:
generate_test_data()
else:
unittest.main(argv=remaining)
unittest.main(argv=remaining)
if __name__ == "__main__":