Nodes: support looking up node sockets by identifier and name

The main goal of this patch is to turn `node.inputs[...]` and `node.outputs[...]` into
the main way to lookup sockets on a node. Currently, it's used for that sometimes,
but often it is not, because it does not work in all cases. More specifically, it does
 not work when a node has multiple sockets with the same name but different
identifiers, which is relatively common.

This patch proposes to make the string lookup more convenient, more useful and
less prone to breaking after changes in Blender.

This is achieved by changing how the lookup works:
* First, it tries to find an available socket with an identifier that matches the given
  key. This is checked first, it makes sure that every socket can be accessed by its
  identifier. The identifier matching is currently disabled in some nodes where we
  want to change identifiers soonish.
* If that didn't work, it tries to find an available socket with a matching name. This
  is often convenient because it generally matches what can be seen in the UI.
  Furthermore, it's also necessary to avoid breakage with old usages of this lookup
  function.
* If both options above didn't work, it checks whether the given key matches any
  socket name/identifier that the node used to have in the past, but has been
  renamed for whatever reason. This mainly exists to avoid breaking scripts by certain
  kinds of changes in the future. This is not included in this patch.

Note, previously, string lookup would also find unavailable sockets. This is disabled
now, because the way we handle unavailable sockets internally is likely to change.
Therefor, any use of unavailable sockets might break (unavailable != hidden). For
the time being, it's still possible to find unavailable sockets by iterating over all sockets.

This change has a small chance to break existing scripts because the behavior
changes for some inputs in some nodes. However, for the cases where the function
is used in practice, I don't really expect any breakage.

This patch also allows us to give a better answer to reports like #113106.

Pull Request: https://projects.blender.org/blender/blender/pulls/113984
This commit is contained in:
Jacques Lucke 2023-10-24 14:04:18 +02:00
parent 7c5b78d2c7
commit e4ad58114b

@ -2021,6 +2021,82 @@ static void rna_Node_internal_links_begin(CollectionPropertyIterator *iter, Poin
rna_iterator_array_begin(iter, begin, sizeof(bNodeLink), len, false, nullptr);
}
/**
* Forbid identifier lookup in nodes whose identifiers are likely to change soon because
* dynamically typed sockets are joined into one.
*/
static bool allow_identifier_lookup(const bNode &node)
{
switch (node.type) {
case GEO_NODE_SWITCH:
case GEO_NODE_ACCUMULATE_FIELD:
case GEO_NODE_CAPTURE_ATTRIBUTE:
case GEO_NODE_ATTRIBUTE_STATISTIC:
case GEO_NODE_BLUR_ATTRIBUTE:
case GEO_NODE_SAMPLE_CURVE:
case GEO_NODE_EVALUATE_AT_INDEX:
case GEO_NODE_EVALUATE_ON_DOMAIN:
case GEO_NODE_INPUT_NAMED_ATTRIBUTE:
case GEO_NODE_RAYCAST:
case GEO_NODE_SAMPLE_INDEX:
case GEO_NODE_SAMPLE_NEAREST_SURFACE:
case FN_NODE_RANDOM_VALUE:
case GEO_NODE_SAMPLE_UV_SURFACE:
case GEO_NODE_STORE_NAMED_ATTRIBUTE:
case GEO_NODE_VIEWER:
case SH_NODE_MIX:
case FN_NODE_COMPARE:
case SH_NODE_MAP_RANGE:
return false;
default:
return true;
}
}
static bNodeSocket *find_socket_by_key(bNode &node,
const eNodeSocketInOut in_out,
const blender::StringRef key)
{
ListBase *sockets = in_out == SOCK_IN ? &node.inputs : &node.outputs;
if (allow_identifier_lookup(node)) {
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
if (socket->is_available()) {
if (socket->identifier == key) {
return socket;
}
}
}
}
LISTBASE_FOREACH (bNodeSocket *, socket, sockets) {
if (socket->is_available()) {
if (socket->name == key) {
return socket;
}
}
}
return nullptr;
}
static int rna_NodeInputs_lookup_string(PointerRNA *ptr, const char *key, PointerRNA *r_ptr)
{
bNode *node = static_cast<bNode *>(ptr->data);
if (bNodeSocket *socket = find_socket_by_key(*node, SOCK_IN, key)) {
*r_ptr = RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, socket);
return true;
}
return false;
}
static int rna_NodeOutputs_lookup_string(PointerRNA *ptr, const char *key, PointerRNA *r_ptr)
{
bNode *node = static_cast<bNode *>(ptr->data);
if (bNodeSocket *socket = find_socket_by_key(*node, SOCK_OUT, key)) {
*r_ptr = RNA_pointer_create(ptr->owner_id, &RNA_NodeSocket, socket);
return true;
}
return false;
}
static bool rna_Node_parent_poll(PointerRNA *ptr, PointerRNA value)
{
bNode *node = static_cast<bNode *>(ptr->data);
@ -9630,6 +9706,15 @@ static void rna_def_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "inputs", nullptr);
RNA_def_property_collection_funcs(prop,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"rna_NodeInputs_lookup_string",
nullptr);
RNA_def_property_struct_type(prop, "NodeSocket");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Inputs", "");
@ -9637,6 +9722,15 @@ static void rna_def_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "outputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "outputs", nullptr);
RNA_def_property_collection_funcs(prop,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
nullptr,
"rna_NodeOutputs_lookup_string",
nullptr);
RNA_def_property_struct_type(prop, "NodeSocket");
RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY);
RNA_def_property_ui_text(prop, "Outputs", "");