Compare commits
14 Commits
T93558
...
node-group
Author | SHA1 | Date | |
---|---|---|---|
fd7e8634fa | |||
f2fb5b6ff2 | |||
9bff211ac9 | |||
23209dc8f9 | |||
04379b5bde | |||
c21fc1687e | |||
f83f5ef6e7 | |||
0e8542ebb7 | |||
94f1726526 | |||
add2815723 | |||
7d66c04841 | |||
a91212e147 | |||
3366fc265b | |||
8cab5afe46 |
@@ -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"),
|
||||
|
@@ -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,
|
||||
|
@@ -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;
|
||||
|
@@ -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(
|
||||
|
@@ -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 (link->tosock == extsock) {
|
||||
bNodeLink *tlink = MEM_callocN(sizeof(bNodeLink), "temporary link");
|
||||
*tlink = *link;
|
||||
BLI_addtail(&tmplinks, tlink);
|
||||
|
||||
nodeRemLink(ntree, link);
|
||||
}
|
||||
if (use_extension_socket) {
|
||||
node_group_handle_extension(ntree, node, &node->inputs);
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
/* 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);
|
||||
|
||||
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__", "");
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user