static 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; /* 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); } } /* 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_verify(ntree, node, (ID *)ntree); 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); }
static void node_group_make_insert_selected(const bContext *C, bNodeTree *ntree, bNode *gnode) { bNodeTree *ngroup = (bNodeTree *)gnode->id; bNodeLink *link, *linkn; bNode *node, *nextn; bNodeSocket *sock; ListBase anim_basepaths = {NULL, NULL}; float min[2], max[2], center[2]; int totselect; int expose_all = FALSE; bNode *input_node, *output_node; /* XXX rough guess, not nice but we don't have access to UI constants here ... */ static const float offsetx = 200; static const float offsety = 0.0f; /* deselect all nodes in the target tree */ for (node = ngroup->nodes.first; node; node = node->next) nodeSetSelected(node, FALSE); totselect = node_get_selected_minmax(ntree, gnode, min, max); add_v2_v2v2(center, min, max); mul_v2_fl(center, 0.5f); /* auto-add interface for "solo" nodes */ if (totselect == 1) expose_all = TRUE; /* move nodes over */ for (node = ntree->nodes.first; node; node = nextn) { nextn = node->next; if (node_group_make_use_node(node, gnode)) { /* keep track of this node's RNA "base" path (the part of the pat identifying the node) * if the old nodetree has animation data which potentially covers this node */ if (ntree->adt) { PointerRNA ptr; char *path; RNA_pointer_create(&ntree->id, &RNA_Node, node, &ptr); path = RNA_path_from_ID_to_struct(&ptr); if (path) BLI_addtail(&anim_basepaths, BLI_genericNodeN(path)); } /* ensure valid parent pointers, detach if parent stays outside the group */ if (node->parent && !(node->parent->flag & NODE_SELECT)) nodeDetachNode(node); /* change node-collection membership */ BLI_remlink(&ntree->nodes, node); BLI_addtail(&ngroup->nodes, node); /* ensure unique node name in the ngroup */ nodeUniqueName(ngroup, node); } } /* move animation data over */ if (ntree->adt) { LinkData *ld, *ldn = NULL; BKE_animdata_separate_by_basepath(&ntree->id, &ngroup->id, &anim_basepaths); /* paths + their wrappers need to be freed */ for (ld = anim_basepaths.first; ld; ld = ldn) { ldn = ld->next; MEM_freeN(ld->data); BLI_freelinkN(&anim_basepaths, ld); } } /* node groups don't use internal cached data */ ntreeFreeCache(ngroup); /* create input node */ input_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_INPUT); input_node->locx = min[0] - center[0] - offsetx; input_node->locy = -offsety; /* create output node */ output_node = nodeAddStaticNode(C, ngroup, NODE_GROUP_OUTPUT); output_node->locx = max[0] - center[0] + offsetx; output_node->locy = -offsety; /* relink external sockets */ for (link = ntree->links.first; link; link = linkn) { int fromselect = node_group_make_use_node(link->fromnode, gnode); int toselect = node_group_make_use_node(link->tonode, gnode); linkn = link->next; if ((fromselect && link->tonode == gnode) || (toselect && link->fromnode == gnode)) { /* remove all links to/from the gnode. * this can remove link information, but there's no general way to preserve it. */ nodeRemLink(ntree, link); } else if (fromselect && toselect) { BLI_remlink(&ntree->links, link); BLI_addtail(&ngroup->links, link); } else if (toselect) { bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->tonode, link->tosock); bNodeSocket *input_sock; /* update the group node and interface node sockets, * so the new interface socket can be linked. */ node_group_verify(ntree, gnode, (ID *)ngroup); node_group_input_verify(ngroup, input_node, (ID *)ngroup); /* create new internal link */ input_sock = node_group_input_find_socket(input_node, iosock->identifier); nodeAddLink(ngroup, input_node, input_sock, link->tonode, link->tosock); /* redirect external link */ link->tonode = gnode; link->tosock = node_group_find_input_socket(gnode, iosock->identifier); } else if (fromselect) { bNodeSocket *iosock = ntreeAddSocketInterfaceFromSocket(ngroup, link->fromnode, link->fromsock); bNodeSocket *output_sock; /* update the group node and interface node sockets, * so the new interface socket can be linked. */ node_group_verify(ntree, gnode, (ID *)ngroup); node_group_output_verify(ngroup, output_node, (ID *)ngroup); /* create new internal link */ output_sock = node_group_output_find_socket(output_node, iosock->identifier); nodeAddLink(ngroup, link->fromnode, link->fromsock, output_node, output_sock); /* redirect external link */ link->fromnode = gnode; link->fromsock = node_group_find_output_socket(gnode, iosock->identifier); } } /* move nodes in the group to the center */ for (node = ngroup->nodes.first; node; node = node->next) { if (node_group_make_use_node(node, gnode) && !node->parent) { node->locx -= center[0]; node->locy -= center[1]; } } /* expose all unlinked sockets too */ if (expose_all) { for (node = ngroup->nodes.first; node; node = node->next) { if (node_group_make_use_node(node, gnode)) { for (sock = node->inputs.first; sock; sock = sock->next) { bNodeSocket *iosock, *input_sock; int skip = FALSE; for (link = ngroup->links.first; link; link = link->next) { if (link->tosock == sock) { skip = TRUE; break; } } if (skip) continue; iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); node_group_input_verify(ngroup, input_node, (ID *)ngroup); /* create new internal link */ input_sock = node_group_input_find_socket(input_node, iosock->identifier); nodeAddLink(ngroup, input_node, input_sock, node, sock); } for (sock = node->outputs.first; sock; sock = sock->next) { bNodeSocket *iosock, *output_sock; int skip = FALSE; for (link = ngroup->links.first; link; link = link->next) if (link->fromsock == sock) skip = TRUE; if (skip) continue; iosock = ntreeAddSocketInterfaceFromSocket(ngroup, node, sock); node_group_output_verify(ngroup, output_node, (ID *)ngroup); /* create new internal link */ output_sock = node_group_output_find_socket(output_node, iosock->identifier); nodeAddLink(ngroup, node, sock, output_node, output_sock); } } } } /* update of the group tree */ ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS; /* update of the tree containing the group instance node */ ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; }