void node_group_expose_all_sockets(bNodeTree *ngroup) { bNode *node; bNodeSocket *sock, *gsock; for (node=ngroup->nodes.first; node; node=node->next) { for (sock=node->inputs.first; sock; sock=sock->next) { if (!sock->link && !nodeSocketIsHidden(sock)) { gsock = node_group_add_socket(ngroup, sock->name, sock->type, SOCK_IN); /* initialize the default value. */ node_socket_copy_default_value(gsock->type, gsock->default_value, sock->default_value); sock->link = nodeAddLink(ngroup, NULL, gsock, node, sock); } } for (sock=node->outputs.first; sock; sock=sock->next) { if (nodeCountSocketLinks(ngroup, sock)==0 && !nodeSocketIsHidden(sock)) { gsock = node_group_add_socket(ngroup, sock->name, sock->type, SOCK_OUT); /* initialize the default value. */ node_socket_copy_default_value(gsock->type, gsock->default_value, sock->default_value); gsock->link = nodeAddLink(ngroup, node, sock, NULL, gsock); } } } }
/* Use specified node and socket as an input for unconnected normal sockets. */ static void ntree_shader_link_builtin_normal(bNodeTree *ntree, bNode *node_from, bNodeSocket *socket_from) { for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) { if (node == node_from) { /* Don't connect node itself! */ continue; } bNodeSocket *sock = ntree_shader_node_find_input(node, "Normal"); /* TODO(sergey): Can we do something smarter here than just a name-based * matching? */ if (sock == NULL) { /* There's no Normal input, nothing to link. */ continue; } if (sock->link != NULL) { /* Something is linked to the normal input already. can't * use other input for that. */ continue; } /* Create connection between specified node and the normal input. */ nodeAddLink(ntree, node_from, socket_from, node, sock); } }
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 int connect_nodes(Main *bmain, bNodeTree *ntree, bNode *node_fr, bNodeSocket *sock_fr, bNode *node_to, bNodeSocket *sock_to) { bNodeLink *link = nodeAddLink(ntree, node_fr, sock_fr, node_to, sock_to); ntreeUpdateTree(bmain, ntree); if (!(link->flag & NODE_LINK_VALID)) { nodeRemLink(ntree, link); return 0; } return 1; } /* connect_nodes() */
void BKE_linestyle_default_shader(const bContext *C, FreestyleLineStyle *linestyle) { bNode *uv_along_stroke, *input_texure, *output_linestyle; bNodeSocket *fromsock, *tosock; bNodeTree *ntree; BLI_assert(linestyle->nodetree == NULL); ntree = ntreeAddTree(NULL, "stroke_shader", "ShaderNodeTree"); linestyle->nodetree = ntree; uv_along_stroke = nodeAddStaticNode(C, ntree, SH_NODE_UVALONGSTROKE); uv_along_stroke->locx = 0.0f; uv_along_stroke->locy = 300.0f; uv_along_stroke->custom1 = 0; // use_tips input_texure = nodeAddStaticNode(C, ntree, SH_NODE_TEX_IMAGE); input_texure->locx = 200.0f; input_texure->locy = 300.0f; output_linestyle = nodeAddStaticNode(C, ntree, SH_NODE_OUTPUT_LINESTYLE); output_linestyle->locx = 400.0f; output_linestyle->locy = 300.0f; output_linestyle->custom1 = MA_RAMP_BLEND; output_linestyle->custom2 = 0; // use_clamp nodeSetActive(ntree, input_texure); fromsock = BLI_findlink(&uv_along_stroke->outputs, 0); // UV tosock = BLI_findlink(&input_texure->inputs, 0); // UV nodeAddLink(ntree, uv_along_stroke, fromsock, input_texure, tosock); fromsock = BLI_findlink(&input_texure->outputs, 0); // Color tosock = BLI_findlink(&output_linestyle->inputs, 0); // Color nodeAddLink(ntree, input_texure, fromsock, output_linestyle, tosock); ntreeUpdateTree(CTX_data_main(C), ntree); }
/* Re-link displacement output to unconnected normal sockets via bump node. * This way material with have proper displacement in the viewport. */ static void ntree_shader_relink_displacement(bNodeTree *ntree, short compatibility) { if (compatibility != NODE_NEW_SHADING) { /* We can only deal with new shading system here. */ return; } bNode *displacement_node; bNodeSocket *displacement_socket; bNodeLink *displacement_link; if (!ntree_shader_has_displacement(ntree, &displacement_node, &displacement_socket, &displacement_link)) { /* There is no displacement output connected, nothing to re-link. */ return; } /* We have to disconnect displacement output socket, otherwise we'll have * cycles in the Cycles material :) */ nodeRemLink(ntree, displacement_link); /* We can't connect displacement to normal directly, use bump node for that * and hope that it gives good enough approximation. */ bNode *bump_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BUMP); bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height"); bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal"); BLI_assert(bump_input_socket != NULL); BLI_assert(bump_output_socket != NULL); /* Connect bump node to where displacement output was originally * connected to. */ nodeAddLink(ntree, displacement_node, displacement_socket, bump_node, bump_input_socket); /* Connect all free-standing Normal inputs. */ ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket, displacement_node, displacement_socket); /* TODO(sergey): Reconnect Geometry Info->Normal sockets to the new * bump node. */ /* We modified the tree, it needs to be updated now. */ ntreeUpdateTree(G.main, ntree); }
static bool ntree_shader_relink_node_normal(bNodeTree *ntree, bNode *node, bNode *node_from, bNodeSocket *socket_from) { bNodeSocket *sock = ntree_shader_node_find_input(node, "Normal"); /* TODO(sergey): Can we do something smarter here than just a name-based * matching? */ if (sock == NULL) { /* There's no Normal input, nothing to link. */ return false; } if (sock->link != NULL) { /* Something is linked to the normal input already. can't * use other input for that. */ return false; } /* Create connection between specified node and the normal input. */ nodeAddLink(ntree, node_from, socket_from, node, sock); return true; }
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; }
/* returns 1 if its OK */ static int node_group_separate_selected(bNodeTree *ntree, bNodeTree *ngroup, float offx, float offy, int make_copy) { bNodeLink *link, *link_next; bNode *node, *node_next, *newnode; ListBase anim_basepaths = {NULL, NULL}; /* deselect all nodes in the target tree */ for (node = ntree->nodes.first; node; node = node->next) nodeSetSelected(node, FALSE); /* clear new pointers, set in nodeCopyNode */ for (node = ngroup->nodes.first; node; node = node->next) node->new_node = NULL; /* add selected nodes into the ntree */ for (node = ngroup->nodes.first; node; node = node_next) { node_next = node->next; if (!(node->flag & NODE_SELECT)) continue; /* ignore interface nodes */ if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { nodeSetSelected(node, FALSE); continue; } if (make_copy) { /* make a copy */ newnode = nodeCopyNode(ngroup, node); } else { /* use the existing node */ newnode = node; } /* keep track of this node's RNA "base" path (the part of the path identifying the node) * if the old nodetree has animation data which potentially covers this node */ if (ngroup->adt) { PointerRNA ptr; char *path; RNA_pointer_create(&ngroup->id, &RNA_Node, newnode, &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 inside the group */ if (newnode->parent && !(newnode->parent->flag & NODE_SELECT)) nodeDetachNode(newnode); /* migrate node */ BLI_remlink(&ngroup->nodes, newnode); BLI_addtail(&ntree->nodes, newnode); /* ensure unique node name in the node tree */ nodeUniqueName(ntree, newnode); if (!newnode->parent) { newnode->locx += offx; newnode->locy += offy; } } /* add internal links to the ntree */ for (link = ngroup->links.first; link; link = link_next) { int fromselect = (link->fromnode && (link->fromnode->flag & NODE_SELECT)); int toselect = (link->tonode && (link->tonode->flag & NODE_SELECT)); link_next = link->next; if (make_copy) { /* make a copy of internal links */ if (fromselect && toselect) nodeAddLink(ntree, link->fromnode->new_node, link->fromsock->new_sock, link->tonode->new_node, link->tosock->new_sock); } else { /* move valid links over, delete broken links */ if (fromselect && toselect) { BLI_remlink(&ngroup->links, link); BLI_addtail(&ntree->links, link); } else if (fromselect || toselect) { nodeRemLink(ngroup, link); } } } /* and copy across the animation, * note that the animation data's action can be NULL here */ if (ngroup->adt) { LinkData *ld, *ldn = NULL; /* now perform the moving */ BKE_animdata_separate_by_basepath(&ngroup->id, &ntree->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); } } ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; if (!make_copy) ngroup->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; return 1; }
/* returns 1 if its OK */ static int node_group_ungroup(bNodeTree *ntree, bNode *gnode) { bNodeLink *link, *linkn, *tlink; bNode *node, *nextnode; bNodeTree *ngroup, *wgroup; ListBase anim_basepaths = {NULL, NULL}; ngroup = (bNodeTree *)gnode->id; /* clear new pointers, set in copytree */ for (node = ntree->nodes.first; node; node = node->next) node->new_node = NULL; /* wgroup is a temporary copy of the NodeTree we're merging in * - all of wgroup's nodes are transferred across to their new home * - ngroup (i.e. the source NodeTree) is left unscathed * - temp copy. don't change ID usercount */ wgroup = ntreeCopyTree_ex(ngroup, FALSE); /* Add the nodes into the ntree */ for (node = wgroup->nodes.first; node; node = nextnode) { nextnode = node->next; /* Remove interface nodes. * This also removes remaining links to and from interface nodes. */ if (ELEM(node->type, NODE_GROUP_INPUT, NODE_GROUP_OUTPUT)) { nodeFreeNode(wgroup, node); continue; } /* keep track of this node's RNA "base" path (the part of the path identifying the node) * if the old nodetree has animation data which potentially covers this node */ if (wgroup->adt) { PointerRNA ptr; char *path; RNA_pointer_create(&wgroup->id, &RNA_Node, node, &ptr); path = RNA_path_from_ID_to_struct(&ptr); if (path) BLI_addtail(&anim_basepaths, BLI_genericNodeN(path)); } /* migrate node */ BLI_remlink(&wgroup->nodes, node); BLI_addtail(&ntree->nodes, node); /* ensure unique node name in the node tree */ nodeUniqueName(ntree, node); if (!node->parent) { node->locx += gnode->locx; node->locy += gnode->locy; } node->flag |= NODE_SELECT; } /* Add internal links to the ntree */ for (link = wgroup->links.first; link; link = linkn) { linkn = link->next; BLI_remlink(&wgroup->links, link); BLI_addtail(&ntree->links, link); } /* and copy across the animation, * note that the animation data's action can be NULL here */ if (wgroup->adt) { LinkData *ld, *ldn = NULL; bAction *waction; /* firstly, wgroup needs to temporary dummy action that can be destroyed, as it shares copies */ waction = wgroup->adt->action = BKE_action_copy(wgroup->adt->action); /* now perform the moving */ BKE_animdata_separate_by_basepath(&wgroup->id, &ntree->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); } /* free temp action too */ if (waction) { BKE_libblock_free(&G.main->action, waction); } } /* free the group tree (takes care of user count) */ BKE_libblock_free(&G.main->nodetree, wgroup); /* restore external links to and from the gnode */ /* note: the nodes have been copied to intermediate wgroup first (so need to use new_node), * then transferred to ntree (new_node pointers remain valid). */ /* input links */ for (link = ngroup->links.first; link; link = link->next) { if (link->fromnode->type == NODE_GROUP_INPUT) { const char *identifier = link->fromsock->identifier; int num_external_links = 0; /* find external links to this input */ for (tlink = ntree->links.first; tlink; tlink = tlink->next) { if (tlink->tonode == gnode && STREQ(tlink->tosock->identifier, identifier)) { nodeAddLink(ntree, tlink->fromnode, tlink->fromsock, link->tonode->new_node, link->tosock->new_sock); ++num_external_links; } } /* if group output is not externally linked, * convert the constant input value to ensure somewhat consistent behavior */ if (num_external_links == 0) { /* XXX TODO bNodeSocket *sock = node_group_find_input_socket(gnode, identifier); BLI_assert(sock);*/ /* XXX TODO nodeSocketCopy(ntree, link->tosock->new_sock, link->tonode->new_node, ntree, sock, gnode);*/ } } } /* output links */ for (link = ntree->links.first; link; link = link->next) { if (link->fromnode == gnode) { const char *identifier = link->fromsock->identifier; int num_internal_links = 0; /* find internal links to this output */ for (tlink = ngroup->links.first; tlink; tlink = tlink->next) { /* only use active output node */ if (tlink->tonode->type == NODE_GROUP_OUTPUT && (tlink->tonode->flag & NODE_DO_OUTPUT)) { if (STREQ(tlink->tosock->identifier, identifier)) { nodeAddLink(ntree, tlink->fromnode->new_node, tlink->fromsock->new_sock, link->tonode, link->tosock); ++num_internal_links; } } } /* if group output is not internally linked, * convert the constant output value to ensure somewhat consistent behavior */ if (num_internal_links == 0) { /* XXX TODO bNodeSocket *sock = node_group_find_output_socket(gnode, identifier); BLI_assert(sock);*/ /* XXX TODO nodeSocketCopy(ntree, link->tosock, link->tonode, ntree, sock, gnode); */ } } } /* delete the group instance */ nodeFreeNode(ntree, gnode); ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS; return 1; }
/* add new node connected to this socket, or replace an existing one */ static void node_socket_add_replace(const bContext *C, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to, int type, NodeLinkItem *item) { bNode *node_from; bNodeSocket *sock_from_tmp; bNode *node_prev = NULL; /* unlink existing node */ if (sock_to->link) { node_prev = sock_to->link->fromnode; nodeRemLink(ntree, sock_to->link); } /* find existing node that we can use */ for (node_from = ntree->nodes.first; node_from; node_from = node_from->next) if (node_from->type == type) break; if (node_from) if (node_from->inputs.first || node_from->typeinfo->draw_buttons || node_from->typeinfo->draw_buttons_ex) node_from = NULL; if (node_prev && node_prev->type == type && node_link_item_compare(node_prev, item)) { /* keep the previous node if it's the same type */ node_from = node_prev; } else if (!node_from) { node_from = nodeAddStaticNode(C, ntree, type); if (node_prev != NULL) { /* If we're replacing existing node, use it's location. */ node_from->locx = node_prev->locx; node_from->locy = node_prev->locy; node_from->offsetx = node_prev->offsetx; node_from->offsety = node_prev->offsety; } else { /* Avoid exact intersection of nodes. * TODO(sergey): Still not ideal, but better than nothing. */ int index = BLI_findindex(&node_to->inputs, sock_to); BLI_assert(index != -1); node_from->locx = node_to->locx - (node_from->typeinfo->width + 50); node_from->locy = node_to->locy - (node_from->typeinfo->height * index); } node_link_item_apply(node_from, item); } nodeSetActive(ntree, node_from); /* add link */ sock_from_tmp = BLI_findlink(&node_from->outputs, item->socket_index); nodeAddLink(ntree, node_from, sock_from_tmp, node_to, sock_to); sock_to->flag &= ~SOCK_COLLAPSED; /* copy input sockets from previous node */ if (node_prev && node_from != node_prev) { bNodeSocket *sock_prev, *sock_from; for (sock_prev = node_prev->inputs.first; sock_prev; sock_prev = sock_prev->next) { for (sock_from = node_from->inputs.first; sock_from; sock_from = sock_from->next) { if (nodeCountSocketLinks(ntree, sock_from) >= sock_from->limit) continue; if (STREQ(sock_prev->name, sock_from->name) && sock_prev->type == sock_from->type) { bNodeLink *link = sock_prev->link; if (link && link->fromnode) { nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from); nodeRemLink(ntree, link); } node_socket_copy_default_value(sock_from, sock_prev); } } } /* also preserve mapping for texture nodes */ if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE) { memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); } /* remove node */ node_remove_linked(ntree, node_prev); } nodeUpdate(ntree, node_from); nodeUpdate(ntree, node_to); ntreeUpdateTree(CTX_data_main(C), ntree); ED_node_tag_update_nodetree(CTX_data_main(C), ntree); }
bNode *node_group_make_from_selected(bNodeTree *ntree) { bNodeLink *link, *linkn; bNode *node, *gnode, *nextn; bNodeTree *ngroup; bNodeSocket *gsock; ListBase anim_basepaths = {NULL, NULL}; float min[2], max[2]; int totnode=0; bNodeTemplate ntemp; INIT_MINMAX2(min, max); /* is there something to group? also do some clearing */ for(node= ntree->nodes.first; node; node= node->next) { if(node->flag & NODE_SELECT) { /* no groups in groups */ if(node->type==NODE_GROUP) return NULL; DO_MINMAX2( (&node->locx), min, max); totnode++; } node->done= 0; } if(totnode==0) return NULL; /* check if all connections are OK, no unselected node has both inputs and outputs to a selection */ for(link= ntree->links.first; link; link= link->next) { if(link->fromnode && link->tonode && link->fromnode->flag & NODE_SELECT) link->tonode->done |= 1; if(link->fromnode && link->tonode && link->tonode->flag & NODE_SELECT) link->fromnode->done |= 2; } for(node= ntree->nodes.first; node; node= node->next) { if((node->flag & NODE_SELECT)==0) if(node->done==3) break; } if(node) return NULL; /* OK! new nodetree */ ngroup= ntreeAddTree("NodeGroup", ntree->type, NODE_GROUP); /* move nodes over */ for(node= ntree->nodes.first; node; node= nextn) { nextn= node->next; if(node->flag & NODE_SELECT) { /* 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)); } /* change node-collection membership */ BLI_remlink(&ntree->nodes, node); BLI_addtail(&ngroup->nodes, node); node->locx-= 0.5f*(min[0]+max[0]); node->locy-= 0.5f*(min[1]+max[1]); } } /* 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); } } /* make group node */ ntemp.type = NODE_GROUP; ntemp.ngroup = ngroup; gnode= nodeAddNode(ntree, &ntemp); gnode->locx= 0.5f*(min[0]+max[0]); gnode->locy= 0.5f*(min[1]+max[1]); /* relink external sockets */ for(link= ntree->links.first; link; link= linkn) { linkn= link->next; if(link->fromnode && link->tonode && (link->fromnode->flag & link->tonode->flag & NODE_SELECT)) { BLI_remlink(&ntree->links, link); BLI_addtail(&ngroup->links, link); } else if(link->tonode && (link->tonode->flag & NODE_SELECT)) { gsock = node_group_expose_socket(ngroup, link->tosock, SOCK_IN); link->tosock->link = nodeAddLink(ngroup, NULL, gsock, link->tonode, link->tosock); link->tosock = node_group_add_extern_socket(ntree, &gnode->inputs, SOCK_IN, gsock); link->tonode = gnode; } else if(link->fromnode && (link->fromnode->flag & NODE_SELECT)) { /* search for existing group node socket */ for (gsock=ngroup->outputs.first; gsock; gsock=gsock->next) if (gsock->link && gsock->link->fromsock==link->fromsock) break; if (!gsock) { gsock = node_group_expose_socket(ngroup, link->fromsock, SOCK_OUT); gsock->link = nodeAddLink(ngroup, link->fromnode, link->fromsock, NULL, gsock); link->fromsock = node_group_add_extern_socket(ntree, &gnode->outputs, SOCK_OUT, gsock); } else link->fromsock = node_group_find_output(gnode, gsock); link->fromnode = gnode; } } ngroup->update |= NTREE_UPDATE; ntreeUpdateTree(ngroup); ntree->update |= NTREE_UPDATE_NODES|NTREE_UPDATE_LINKS; ntreeUpdateTree(ntree); return gnode; }
/* add new node connected to this socket, or replace an existing one */ static void node_socket_add_replace(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to, bNodeTemplate *ntemp, int sock_num) { bNode *node_from; bNodeSocket *sock_from; bNode *node_prev = NULL; /* unlink existing node */ if (sock_to->link) { node_prev = sock_to->link->fromnode; nodeRemLink(ntree, sock_to->link); } /* find existing node that we can use */ for (node_from=ntree->nodes.first; node_from; node_from=node_from->next) if (node_from->type == ntemp->type) break; if (node_from) if (!(node_from->inputs.first == NULL && !(node_from->typeinfo->flag & NODE_OPTIONS))) node_from = NULL; if (node_prev && node_prev->type == ntemp->type && (ntemp->type != NODE_GROUP || node_prev->id == &ntemp->ngroup->id)) { /* keep the previous node if it's the same type */ node_from = node_prev; } else if (!node_from) { node_from= nodeAddNode(ntree, ntemp); node_from->locx = node_to->locx - (node_from->typeinfo->width + 50); node_from->locy = node_to->locy; if (node_from->id) id_us_plus(node_from->id); } nodeSetActive(ntree, node_from); /* add link */ sock_from = BLI_findlink(&node_from->outputs, sock_num); nodeAddLink(ntree, node_from, sock_from, node_to, sock_to); /* copy input sockets from previous node */ if (node_prev && node_from != node_prev) { bNodeSocket *sock_prev, *sock_from; for (sock_prev=node_prev->inputs.first; sock_prev; sock_prev=sock_prev->next) { for (sock_from=node_from->inputs.first; sock_from; sock_from=sock_from->next) { if (nodeCountSocketLinks(ntree, sock_from) >= sock_from->limit) continue; if (strcmp(sock_prev->name, sock_from->name) == 0 && sock_prev->type == sock_from->type) { bNodeLink *link = sock_prev->link; if (link && link->fromnode) { nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from); nodeRemLink(ntree, link); } node_socket_free_default_value(sock_from->type, sock_from->default_value); sock_from->default_value = node_socket_make_default_value(sock_from->type); node_socket_copy_default_value(sock_from->type, sock_from->default_value, sock_prev->default_value); } } } /* also preserve mapping for texture nodes */ if (node_from->typeinfo->nclass == NODE_CLASS_TEXTURE && node_prev->typeinfo->nclass == NODE_CLASS_TEXTURE) { memcpy(node_from->storage, node_prev->storage, sizeof(NodeTexBase)); } /* remove node */ node_remove_linked(ntree, node_prev); } nodeUpdate(ntree, node_from); nodeUpdate(ntree, node_to); ntreeUpdateTree(ntree); ED_node_generic_update(bmain, ntree, node_to); }
static void ntree_shader_link_builtin_group_normal( bNodeTree *ntree, bNode *group_node, bNode *node_from, bNodeSocket *socket_from, bNode *displacement_node, bNodeSocket *displacement_socket) { bNodeTree *group_ntree = (bNodeTree *)group_node->id; /* Create input socket to plug displacement connection to. */ bNodeSocket *group_normal_socket = ntreeAddSocketInterface(group_ntree, SOCK_IN, "NodeSocketVector", "Normal"); /* Need to update tree so all node instances nodes gets proper sockets. */ bNode *group_input_node = ntreeFindType(group_ntree, NODE_GROUP_INPUT); node_group_verify(ntree, group_node, &group_ntree->id); if (group_input_node) node_group_input_verify(group_ntree, group_input_node, &group_ntree->id); ntreeUpdateTree(G.main, group_ntree); /* Assumes sockets are always added at the end. */ bNodeSocket *group_node_normal_socket = group_node->inputs.last; if (displacement_node == group_node) { /* If displacement is coming from this node group we need to perform * some internal re-linking in order to avoid cycles. */ bNode *group_output_node = ntreeFindType(group_ntree, NODE_GROUP_OUTPUT); BLI_assert(group_output_node != NULL); bNodeSocket *group_output_node_displacement_socket = nodeFindSocket(group_output_node, SOCK_IN, displacement_socket->identifier); bNodeLink *group_displacement_link = group_output_node_displacement_socket->link; if (group_displacement_link == NULL) { /* Displacement output is not connected to anything, can just stop * right away. */ return; } /* This code is similar to ntree_shader_relink_displacement() */ bNode *group_displacement_node = group_displacement_link->fromnode; bNodeSocket *group_displacement_socket = group_displacement_link->fromsock; /* Create and link bump node. * Can't re-use bump node from parent tree because it'll cause cycle. */ bNode *bump_node = nodeAddStaticNode(NULL, group_ntree, SH_NODE_BUMP); bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height"); bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal"); BLI_assert(bump_input_socket != NULL); BLI_assert(bump_output_socket != NULL); nodeAddLink(group_ntree, group_displacement_node, group_displacement_socket, bump_node, bump_input_socket); /* Relink normals inside of the instanced tree. */ ntree_shader_link_builtin_normal(group_ntree, bump_node, bump_output_socket, group_displacement_node, group_displacement_socket); ntreeUpdateTree(G.main, group_ntree); } else if (group_input_node) { /* Connect group node normal input. */ nodeAddLink(ntree, node_from, socket_from, group_node, group_node_normal_socket); BLI_assert(group_input_node != NULL); bNodeSocket *group_input_node_normal_socket = nodeFindSocket(group_input_node, SOCK_OUT, group_normal_socket->identifier); BLI_assert(group_input_node_normal_socket != NULL); /* Relink normals inside of the instanced tree. */ ntree_shader_link_builtin_normal(group_ntree, group_input_node, group_input_node_normal_socket, displacement_node, displacement_socket); ntreeUpdateTree(G.main, group_ntree); } }