1
1

Compare commits

...

14 Commits

Author SHA1 Message Date
fd7e8634fa Merge branch 'master' into node-group-single-socket-nodes 2021-07-25 12:50:09 +01:00
f2fb5b6ff2 Hide new group input sockets by default. 2021-07-24 18:08:09 +01:00
9bff211ac9 Merge branch 'master' into node-group-single-socket-nodes 2021-07-24 15:32:28 +01:00
23209dc8f9 Disable the virtual group extension socket by default. 2021-07-16 08:51:45 +01:00
04379b5bde Merge branch 'master' into node-group-single-socket-nodes 2021-07-16 08:07:59 +01:00
c21fc1687e Show group output nodes with all sockets. 2021-07-09 19:29:30 +01:00
f83f5ef6e7 Merge remote-tracking branch 'origin/node-group-single-socket-nodes' into node-group-single-socket-nodes 2021-07-09 16:38:10 +01:00
0e8542ebb7 Merge branch 'master' into node-group-single-socket-nodes 2021-07-09 16:35:56 +01:00
94f1726526 Merge branch 'master' into node-group-single-socket-nodes 2021-07-09 08:15:37 +01:00
add2815723 Single-socket input/output nodes in node groups.
Changes the default group input/output nodes in groups to show only
a single socket. This is more convenient for keeping connections short
and put parameters closer to where they are used inside a group.

For input nodes this is purely a UI feature: they can already be
duplicated and unused sockets can be hidden (ctrl+h). The patch only
makes this the default setup of input nodes by providing all inputs
separately in the "Add Node" menu.

For output nodes the situation requires some functional changes,
because outputs have to be unique.

The "virtual" extension socket on group input/output nodes is now
disabled by default, since it adds a lot of visual noise without saving
much work (see discussion in T68733). The feature can be enabled with
a per-node flag.

Todo:
- [ ] Implement unique output handling across different node systems.

Differential Revision: https://developer.blender.org/D11853
2021-07-08 15:53:34 +01:00
7d66c04841 Flag for group nodes to disable extension socket. 2021-07-08 10:52:06 +01:00
a91212e147 Sanity checks for the extension socket in groups. 2021-07-08 09:30:05 +01:00
3366fc265b Add single-socket input/output nodes in groups. 2021-07-08 09:29:11 +01:00
8cab5afe46 Simplify group node extension handler function.
Use a common function for handling the "extension" socket in group
input/output nodes.
2021-07-08 08:30:06 +01:00
5 changed files with 150 additions and 113 deletions

View File

@@ -94,9 +94,6 @@ def node_group_items(context):
yield NodeItemCustom(draw=group_tools_draw)
yield NodeItem("NodeGroupInput", poll=group_input_output_item_poll)
yield NodeItem("NodeGroupOutput", poll=group_input_output_item_poll)
yield NodeItemCustom(draw=lambda self, layout, context: layout.separator())
def contains_group(nodetree, group):
@@ -123,6 +120,40 @@ def node_group_items(context):
settings={"node_tree": "bpy.data.node_groups[%r]" % group.name})
def node_group_input_items(context):
if context is None:
return
space = context.space_data
if not space:
return
ntree = space.edit_tree
if not ntree:
return
for i, iosock in enumerate(ntree.inputs):
settings = dict()
settings["use_extension_socket"] = "False"
for k, _ in enumerate(ntree.inputs):
settings["outputs[{}].hide".format(k)] = "False" if k == i else "True"
yield NodeItem("NodeGroupInput", label=iosock.name, settings=settings, poll=group_input_output_item_poll)
def node_group_output_items(context):
if context is None:
return
space = context.space_data
if not space:
return
ntree = space.edit_tree
if not ntree:
return
# Node groups are not added as single-socket nodes currently, because only one node can be active output.
# Dividing group output between multiple nodes will require changes to the "active output" concept (NODE_DO_OUTPUT flag).
yield NodeItem("NodeGroupOutput", poll=group_input_output_item_poll)
# only show input/output nodes inside node groups
def group_input_output_item_poll(context):
space = context.space_data
@@ -294,6 +325,8 @@ shader_node_categories = [
NodeItem("ShaderNodeScript"),
]),
ShaderNodeCategory("SH_NEW_GROUP", "Group", items=node_group_items),
ShaderNodeCategory("SH_NEW_GROUP_INPUTS", "Group Inputs", items=node_group_input_items),
ShaderNodeCategory("SH_NEW_GROUP_OUTPUTS", "Group Outputs", items=node_group_output_items),
ShaderNodeCategory("SH_NEW_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
@@ -409,6 +442,8 @@ compositor_node_categories = [
NodeItem("CompositorNodeCornerPin"),
]),
CompositorNodeCategory("CMP_GROUP", "Group", items=node_group_items),
CompositorNodeCategory("CMP_GROUP_INPUTS", "Group Inputs", items=node_group_input_items),
CompositorNodeCategory("CMP_GROUP_OUTPUTS", "Group Outputs", items=node_group_output_items),
CompositorNodeCategory("CMP_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
@@ -466,6 +501,8 @@ texture_node_categories = [
NodeItem("TextureNodeAt"),
]),
TextureNodeCategory("TEX_GROUP", "Group", items=node_group_items),
TextureNodeCategory("TEX_GROUP_INPUTS", "Group Inputs", items=node_group_input_items),
TextureNodeCategory("TEX_GROUP_OUTPUTS", "Group Outputs", items=node_group_output_items),
TextureNodeCategory("TEX_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),
@@ -596,6 +633,8 @@ geometry_node_categories = [
NodeItem("GeometryNodeVolumeToMesh"),
]),
GeometryNodeCategory("GEO_GROUP", "Group", items=node_group_items),
GeometryNodeCategory("GEO_GROUP_INPUTS", "Group Inputs", items=node_group_input_items),
GeometryNodeCategory("GEO_GROUP_OUTPUTS", "Group Outputs", items=node_group_output_items),
GeometryNodeCategory("GEO_LAYOUT", "Layout", items=[
NodeItem("NodeFrame"),
NodeItem("NodeReroute"),

View File

@@ -616,6 +616,11 @@ typedef struct bNodeSocketValueMaterial {
} bNodeSocketValueMaterial;
/* Data structs, for node->storage. */
typedef enum eNodeGroupInputOutputFlags {
NODE_GROUP_USE_EXTENSION_SOCKET = 1,
} eNodeGroupInputOutputFlags;
enum {
CMP_NODE_MASKTYPE_ADD = 0,
CMP_NODE_MASKTYPE_SUBTRACT = 1,

View File

@@ -453,6 +453,7 @@ extern StructRNA RNA_NodeOutputFileSlotLayer;
extern StructRNA RNA_NodeSocket;
extern StructRNA RNA_NodeSocketInterface;
extern StructRNA RNA_NodeSocketStandard;
extern StructRNA RNA_NodeSocketVirtual;
extern StructRNA RNA_NodeTree;
extern StructRNA RNA_NoiseGpencilModifier;
extern StructRNA RNA_NoiseTexture;

View File

@@ -4650,6 +4650,13 @@ static void def_group_input(StructRNA *srna)
RNA_def_property_struct_type(prop, "PropertyGroup");
RNA_def_property_flag(prop, PROP_IDPROPERTY);
RNA_def_property_ui_text(prop, "Interface", "Interface socket data");
prop = RNA_def_property(srna, "use_extension_socket", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom1", NODE_GROUP_USE_EXTENSION_SOCKET);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(
prop, "Use Extension Socket", "Add a virtual socket to the node for extending the node group interface");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
}
static void def_group_output(StructRNA *srna)
@@ -4663,6 +4670,15 @@ static void def_group_output(StructRNA *srna)
RNA_def_property_flag(prop, PROP_IDPROPERTY);
RNA_def_property_ui_text(prop, "Interface", "Interface socket data");
prop = RNA_def_property(srna, "use_extension_socket", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "custom1", NODE_GROUP_USE_EXTENSION_SOCKET);
RNA_def_property_boolean_default(prop, true);
RNA_def_property_ui_text(
prop,
"Use Extension Socket",
"Add a virtual socket to the node for extending the node group interface");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_Node_socket_update");
prop = RNA_def_property(srna, "is_active_output", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_DO_OUTPUT);
RNA_def_property_ui_text(

View File

@@ -34,6 +34,7 @@
#include "BKE_node.h"
#include "RNA_access.h"
#include "RNA_types.h"
#include "MEM_guardedalloc.h"
@@ -121,8 +122,12 @@ bool nodeGroupPoll(bNodeTree *nodetree, bNodeTree *grouptree, const char **r_dis
}
/* used for both group nodes and interface nodes */
static bNodeSocket *group_verify_socket(
bNodeTree *ntree, bNode *gnode, bNodeSocket *iosock, ListBase *verify_lb, int in_out)
static bNodeSocket *group_verify_socket(bNodeTree *ntree,
bNode *gnode,
bNodeSocket *iosock,
ListBase *verify_lb,
int in_out,
bool hide_new)
{
bNodeSocket *sock;
@@ -151,6 +156,9 @@ static bNodeSocket *group_verify_socket(
else {
sock = nodeAddSocket(ntree, gnode, in_out, iosock->idname, iosock->identifier, iosock->name);
if (hide_new) {
sock->flag |= SOCK_HIDDEN;
}
if (iosock->typeinfo->interface_init_socket) {
iosock->typeinfo->interface_init_socket(ntree, iosock, gnode, sock, "interface");
}
@@ -163,8 +171,12 @@ static bNodeSocket *group_verify_socket(
}
/* used for both group nodes and interface nodes */
static void group_verify_socket_list(
bNodeTree *ntree, bNode *gnode, ListBase *iosock_lb, ListBase *verify_lb, int in_out)
static void group_verify_socket_list(bNodeTree *ntree,
bNode *gnode,
ListBase *iosock_lb,
ListBase *verify_lb,
int in_out,
bool hide_new)
{
bNodeSocket *iosock, *sock, *nextsock;
@@ -173,7 +185,7 @@ static void group_verify_socket_list(
iosock = iosock_lb->first;
for (; iosock; iosock = iosock->next) {
/* abusing new_sock pointer for verification here! only used inside this function */
iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out);
iosock->new_sock = group_verify_socket(ntree, gnode, iosock, verify_lb, in_out, hide_new);
}
/* leftovers are removed */
for (sock = verify_lb->first; sock; sock = nextsock) {
@@ -203,8 +215,8 @@ void node_group_update(struct bNodeTree *ntree, struct bNode *node)
}
else {
bNodeTree *ngroup = (bNodeTree *)node->id;
group_verify_socket_list(ntree, node, &ngroup->inputs, &node->inputs, SOCK_IN);
group_verify_socket_list(ntree, node, &ngroup->outputs, &node->outputs, SOCK_OUT);
group_verify_socket_list(ntree, node, &ngroup->inputs, &node->inputs, SOCK_IN, false);
group_verify_socket_list(ntree, node, &ngroup->outputs, &node->outputs, SOCK_OUT, false);
}
}
@@ -432,33 +444,25 @@ void BKE_node_tree_unlink_id(ID *id, struct bNodeTree *ntree)
/** \name Node #GROUP_INPUT / #GROUP_OUTPUT
* \{ */
static void node_group_input_init(bNodeTree *ntree, bNode *node)
/* Check if the extension socket is connected and expose internal sockets. */
static void node_group_handle_extension(bNodeTree *ntree, bNode *node, ListBase *socket_list)
{
node_group_input_update(ntree, node);
}
bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
for (sock = node->outputs.first; sock; sock = sock->next) {
if (STREQ(sock->identifier, identifier)) {
return sock;
bNodeSocket *extsock = socket_list->last;
if (!extsock) {
/* Can be called during initial update before the extension socket is added. */
return;
}
}
return NULL;
}
/* This function should only be called when virtual sockets are enabled. */
BLI_assert(RNA_struct_is_a(extsock->typeinfo->ext_socket.srna, &RNA_NodeSocketVirtual));
void node_group_input_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *extsock = node->outputs.last;
bNodeLink *link, *linknext, *exposelink;
bNodeLink *link, *linknext;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links from the extension socket
* so they can be recreated after verification.
*/
ListBase tmplinks;
/* find links from the extension socket and store them */
/* Find links from the extension socket and store them. */
BLI_listbase_clear(&tmplinks);
for (link = ntree->links.first; link; link = linknext) {
linknext = link->next;
@@ -466,7 +470,8 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
continue;
}
if (link->fromsock == extsock) {
/* Check both fromsock and tosock so it works for input as well as outputs. */
if (link->fromsock == extsock || link->tosock == extsock) {
bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
@@ -475,42 +480,69 @@ void node_group_input_update(bNodeTree *ntree, bNode *node)
}
}
/* find valid link to expose */
exposelink = NULL;
/* Find valid link to expose. */
bNode *expose_node = NULL;
bNodeSocket *expose_sock = NULL;
for (link = tmplinks.first; link; link = link->next) {
/* XXX Multiple sockets can be connected to the extension socket at once,
/* Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
* whatever that means.
*/
if (link->tosock->type != SOCK_CUSTOM) {
exposelink = link;
if (link->fromsock == extsock && link->tosock->type != SOCK_CUSTOM) {
expose_node = link->tonode;
expose_sock = link->tosock;
}
else if (link->tosock == extsock && link->fromsock->type != SOCK_CUSTOM) {
expose_node = link->fromnode;
expose_sock = link->fromsock;
break;
}
}
if (exposelink) {
if (expose_node && expose_sock) {
bNodeSocket *gsock, *newsock;
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->tonode, exposelink->tosock);
gsock = ntreeAddSocketInterfaceFromSocket(ntree, expose_node, expose_sock);
node_group_input_update(ntree, node);
newsock = node_group_input_find_socket(node, gsock->identifier);
if (node->typeinfo->updatefunc) {
node->typeinfo->updatefunc(ntree, node);
}
newsock = BLI_findstring(socket_list, gsock->identifier, offsetof(bNodeSocket, identifier));
/* redirect links from the extension socket */
for (link = tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, node, newsock, link->tonode, link->tosock);
nodeAddLink(ntree, node, newsock, expose_node, expose_sock);
}
}
BLI_freelistN(&tmplinks);
}
/* check inputs and outputs, and remove or insert them */
{
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT);
bNodeSocket *node_group_input_find_socket(bNode *node, const char *identifier)
{
return BLI_findstring(&node->outputs, identifier, offsetof(bNodeSocket, identifier));
}
/* add virtual extension socket */
static void node_group_input_init(bNodeTree *ntree, bNode *node)
{
node_group_input_update(ntree, node);
}
void node_group_input_update(bNodeTree *ntree, bNode *node)
{
const bool use_extension_socket = node->custom1 & NODE_GROUP_USE_EXTENSION_SOCKET;
if (use_extension_socket) {
node_group_handle_extension(ntree, node, &node->outputs);
}
/* Check group tree interface and remove or insert sockets as needed. */
/* SOCK_IN/SOCK_OUT is inverted for interface nodes: Group input nodes have output sockets. */
group_verify_socket_list(ntree, node, &ntree->inputs, &node->outputs, SOCK_OUT, true);
if (use_extension_socket) {
/* Add virtual extension socket. */
nodeAddSocket(ntree, node, SOCK_OUT, "NodeSocketVirtual", "__extend__", "");
}
}
@@ -529,86 +561,30 @@ void register_node_type_group_input(void)
nodeRegisterType(ntype);
}
bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
{
return BLI_findstring(&node->inputs, identifier, offsetof(bNodeSocket, identifier));
}
static void node_group_output_init(bNodeTree *ntree, bNode *node)
{
node_group_output_update(ntree, node);
}
bNodeSocket *node_group_output_find_socket(bNode *node, const char *identifier)
{
bNodeSocket *sock;
for (sock = node->inputs.first; sock; sock = sock->next) {
if (STREQ(sock->identifier, identifier)) {
return sock;
}
}
return NULL;
}
void node_group_output_update(bNodeTree *ntree, bNode *node)
{
bNodeSocket *extsock = node->inputs.last;
bNodeLink *link, *linknext, *exposelink;
/* Adding a tree socket and verifying will remove the extension socket!
* This list caches the existing links to the extension socket
* so they can be recreated after verification.
*/
ListBase tmplinks;
const bool use_extension_socket = node->custom1 & NODE_GROUP_USE_EXTENSION_SOCKET;
/* find links to the extension socket and store them */
BLI_listbase_clear(&tmplinks);
for (link = ntree->links.first; link; link = linknext) {
linknext = link->next;
if (nodeLinkIsHidden(link)) {
continue;
if (use_extension_socket) {
node_group_handle_extension(ntree, node, &node->inputs);
}
if (link->tosock == extsock) {
bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
*tlink = *link;
BLI_addtail(&tmplinks, tlink);
/* Check group tree interface and remove or insert sockets as needed. */
/* SOCK_IN/SOCK_OUT is inverted for interface nodes: Group output nodes have input sockets. */
group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN, false);
nodeRemLink(ntree, link);
}
}
/* find valid link to expose */
exposelink = NULL;
for (link = tmplinks.first; link; link = link->next) {
/* XXX Multiple sockets can be connected to the extension socket at once,
* in that case the arbitrary first link determines name and type.
* This could be improved by choosing the "best" type among all links,
* whatever that means.
*/
if (link->fromsock->type != SOCK_CUSTOM) {
exposelink = link;
break;
}
}
if (exposelink) {
bNodeSocket *gsock, *newsock;
/* XXX what if connecting virtual to virtual socket?? */
gsock = ntreeAddSocketInterfaceFromSocket(ntree, exposelink->fromnode, exposelink->fromsock);
node_group_output_update(ntree, node);
newsock = node_group_output_find_socket(node, gsock->identifier);
/* redirect links to the extension socket */
for (link = tmplinks.first; link; link = link->next) {
nodeAddLink(ntree, link->fromnode, link->fromsock, node, newsock);
}
}
BLI_freelistN(&tmplinks);
/* check inputs and outputs, and remove or insert them */
{
/* value_in_out inverted for interface nodes to get correct socket value_property */
group_verify_socket_list(ntree, node, &ntree->outputs, &node->inputs, SOCK_IN);
/* add virtual extension socket */
if (use_extension_socket) {
/* Add virtual extension socket */
nodeAddSocket(ntree, node, SOCK_IN, "NodeSocketVirtual", "__extend__", "");
}
}