diff options
Diffstat (limited to 'source/blender/editors/interface/interface_node.c')
-rw-r--r-- | source/blender/editors/interface/interface_node.c | 567 |
1 files changed, 567 insertions, 0 deletions
diff --git a/source/blender/editors/interface/interface_node.c b/source/blender/editors/interface/interface_node.c new file mode 100644 index 00000000000..3d0603206ee --- /dev/null +++ b/source/blender/editors/interface/interface_node.c @@ -0,0 +1,567 @@ +/* + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Blender Foundation 2009. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/editors/interface/interface_node.c + * \ingroup edinterface + */ + +#include <stdlib.h> +#include <stddef.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_node_types.h" +#include "DNA_screen_types.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_node.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "interface_intern.h" + +/************************* Node Link Menu **************************/ + +#define UI_NODE_LINK_ADD 0 +#define UI_NODE_LINK_DISCONNECT -1 +#define UI_NODE_LINK_REMOVE -2 + +typedef struct NodeLinkArg { + ID *id; + bNodeTree *ntree; + bNode *node; + bNodeSocket *sock; + + bNodeTree *ngroup; + int type; + int output; +} NodeLinkArg; + +static void ui_node_tag_recursive(bNode *node) +{ + bNodeSocket *input; + + if(node->flag & NODE_TEST) + return; /* in case of cycles */ + + node->flag |= NODE_TEST; + + for(input=node->inputs.first; input; input=input->next) + if(input->link) + ui_node_tag_recursive(input->link->fromnode); +} + +static void ui_node_clear_recursive(bNode *node) +{ + bNodeSocket *input; + + if(!(node->flag & NODE_TEST)) + return; /* in case of cycles */ + + node->flag &= ~NODE_TEST; + + for(input=node->inputs.first; input; input=input->next) + if(input->link) + ui_node_clear_recursive(input->link->fromnode); +} + +static void ntree_notify(bContext *C, ID *id, bNodeTree *ntree) +{ + if(ntree->type==NTREE_SHADER) + WM_event_add_notifier(C, NC_MATERIAL|ND_NODES, id); + else if(ntree->type==NTREE_COMPOSIT) + WM_event_add_notifier(C, NC_SCENE|ND_NODES, id); + else if(ntree->type==NTREE_TEXTURE) + WM_event_add_notifier(C, NC_TEXTURE|ND_NODES, id); + + DAG_id_tag_update(id, 0); +} + +static void ui_node_remove_linked(bNodeTree *ntree, bNode *rem_node) +{ + bNode *node, *next; + bNodeSocket *sock; + + /* tag linked nodes to be removed */ + for(node=ntree->nodes.first; node; node=node->next) + node->flag &= ~NODE_TEST; + + ui_node_tag_recursive(rem_node); + + /* clear tags on nodes that are still used by other nodes */ + for(node=ntree->nodes.first; node; node=node->next) + if(!(node->flag & NODE_TEST)) + for(sock=node->inputs.first; sock; sock=sock->next) + if(sock->link && sock->link->fromnode != rem_node) + ui_node_clear_recursive(sock->link->fromnode); + + /* remove nodes */ + for(node=ntree->nodes.first; node; node=next) { + next = node->next; + + if(node->flag & NODE_TEST) { + if(node->id) + node->id->us--; + nodeFreeNode(ntree, node); + } + } + + //node_tree_verify_groups(ntree); +} + +static void ui_node_sock_name(bNodeSocket *sock, char name[UI_MAX_NAME_STR]) +{ + if(sock->link) { + bNode *node = sock->link->fromnode; + + if(node->type == NODE_GROUP) + BLI_strncpy(name, node->id->name+2, UI_MAX_NAME_STR); + else + BLI_strncpy(name, node->typeinfo->name, UI_MAX_NAME_STR); + + if(node->inputs.first == NULL && + node->outputs.first != node->outputs.last && + !(node->typeinfo->flag & NODE_OPTIONS)) + BLI_snprintf(name, UI_MAX_NAME_STR, "%s | %s", name, sock->link->fromsock->name); + } + else if(sock->type == SOCK_CLOSURE) + BLI_strncpy(name, "None", UI_MAX_NAME_STR); + else + BLI_strncpy(name, "Default", UI_MAX_NAME_STR); +} + +static void ui_node_link(bContext *C, void *arg_p, void *event_p) +{ + NodeLinkArg *arg = (NodeLinkArg*)arg_p; + ID *id = arg->id; + bNode *node_to = arg->node; + bNodeSocket *sock_to = arg->sock; + bNodeTree *ntree = arg->ntree; + bNode *node_from; + bNodeSocket *sock_from; + int event = GET_INT_FROM_POINTER(event_p); + + if(event == UI_NODE_LINK_DISCONNECT) { + /* disconnect */ + if(sock_to->link) + nodeRemLink(ntree, sock_to->link); + } + else if(event == UI_NODE_LINK_REMOVE) { + /* remove */ + if(sock_to->link) + ui_node_remove_linked(ntree, sock_to->link->fromnode); + } + else { + 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 == arg->type) + break; + + if(node_from) + if(!(node_from->inputs.first == NULL && !(node_from->typeinfo->flag & NODE_OPTIONS))) + node_from = NULL; + + /* add new node */ + if(!node_from) { + if(arg->ngroup) + node_from = nodeAddNodeType(ntree, NODE_GROUP, arg->ngroup, NULL); + else + node_from = nodeAddNodeType(ntree, arg->type, NULL, NULL); + + 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); + } + + /* add link */ + sock_from = BLI_findlink(&node_from->outputs, arg->output); + 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(strcmp(sock_prev->name, sock_from->name) == 0 && sock_prev->type == sock_from->type) { + bNodeLink *link = sock_prev->link; + + if(link) { + nodeAddLink(ntree, link->fromnode, link->fromsock, node_from, sock_from); + nodeRemLink(ntree, link); + } + + memcpy(sock_from->ns.vec, sock_prev->ns.vec, sizeof(sock_from->ns.vec)); + } + } + } + + /* remove node */ + ui_node_remove_linked(ntree, node_prev); + } + + NodeTagChanged(ntree, node_from); + } + + NodeTagChanged(ntree, node_to); + ntreeSolveOrder(ntree); + + ntree_notify(C, id, ntree); +} + +static int ui_compatible_sockets(int typeA, int typeB) +{ + if(typeA == SOCK_CLOSURE || typeB == SOCK_CLOSURE) + return (typeA == typeB); + + return (typeA == typeB); +} + +static bNodeSocketType *ui_node_input_socket_type(bNode *node, bNodeSocket *sock) +{ + if(node->type == NODE_GROUP) + return NULL; + + return &node->typeinfo->inputs[BLI_findindex(&node->inputs, sock)]; +} + +static void ui_node_menu_column(Main *bmain, NodeLinkArg *arg, uiLayout *layout, const char *cname, int nclass) +{ + bNodeTree *ntree = arg->ntree; + bNodeSocket *sock = arg->sock; + uiLayout *column = NULL; + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but; + bNodeType *ntype; + bNodeTree *ngroup; + NodeLinkArg *argN; + int first = 1; + + if(nclass == NODE_CLASS_GROUP) { + for(ngroup=bmain->nodetree.first; ngroup; ngroup=ngroup->id.next) { + bNodeSocket *gsock; + char name[UI_MAX_NAME_STR]; + int i, j, num = 0; + + if(ngroup->type != ntree->type) + continue; + + for(gsock=ngroup->inputs.first; gsock; gsock=gsock->next) + if(ui_compatible_sockets(gsock->type, sock->type)) + num++; + + for(i=0, j=0, gsock=ngroup->outputs.first; gsock; gsock=gsock->next, i++) { + if(!ui_compatible_sockets(gsock->type, sock->type)) + continue; + + if(first) { + column= uiLayoutColumn(layout, 0); + uiBlockSetCurLayout(block, column); + + uiItemL(column, cname, ICON_NONE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + + first = 0; + } + + if(num > 1) { + if(j == 0) { + uiItemL(column, ngroup->id.name+2, ICON_NONE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + } + + BLI_snprintf(name, UI_MAX_NAME_STR, " %s", gsock->name); + j++; + } + else + BLI_strncpy(name, ngroup->id.name+2, UI_MAX_NAME_STR); + + but = uiDefBut(block, BUT, 0, ngroup->id.name+2, 0, 0, UI_UNIT_X*4, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, "Add node to input"); + + argN = MEM_dupallocN(arg); + argN->ngroup = ngroup; + argN->output = i; + uiButSetNFunc(but, ui_node_link, argN, NULL); + } + } + } + else { + for(ntype=ntree->alltypes.first; ntype; ntype=ntype->next) { + bNodeSocketType *stype; + char name[UI_MAX_NAME_STR]; + int i, j, num = 0; + + if(ntype->nclass != nclass) + continue; + + for(i=0, stype=ntype->outputs; stype && stype->type != -1; stype++, i++) + if(ui_compatible_sockets(stype->type, sock->type)) + num++; + + for(i=0, j=0, stype=ntype->outputs; stype && stype->type != -1; stype++, i++) { + if(!ui_compatible_sockets(stype->type, sock->type)) + continue; + + if(first) { + column= uiLayoutColumn(layout, 0); + uiBlockSetCurLayout(block, column); + + uiItemL(column, cname, ICON_NONE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + + first = 0; + } + + if(num > 1) { + if(j == 0) { + uiItemL(column, ntype->name, ICON_NONE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + } + + BLI_snprintf(name, UI_MAX_NAME_STR, " %s", stype->name); + j++; + } + else + BLI_strncpy(name, ntype->name, UI_MAX_NAME_STR); + + but = uiDefBut(block, BUT, 0, name, 0, 0, UI_UNIT_X*4, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, "Add node to input"); + + argN = MEM_dupallocN(arg); + argN->type = ntype->type; + argN->output = i; + uiButSetNFunc(but, ui_node_link, argN, NULL); + } + } + } +} + +static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p) +{ + Main *bmain= CTX_data_main(C); + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = (uiBut*)but_p; + uiLayout *split, *column; + NodeLinkArg *arg = (NodeLinkArg*)but->func_argN; + bNodeSocket *sock = arg->sock; + + uiBlockSetCurLayout(block, layout); + split= uiLayoutSplit(layout, 0, 0); + + ui_node_menu_column(bmain, arg, split, "Input", NODE_CLASS_INPUT); + ui_node_menu_column(bmain, arg, split, "Output", NODE_CLASS_OUTPUT); + ui_node_menu_column(bmain, arg, split, "Closure", NODE_CLASS_CLOSURE); + ui_node_menu_column(bmain, arg, split, "Texture", NODE_CLASS_TEXTURE); + ui_node_menu_column(bmain, arg, split, "Color", NODE_CLASS_OP_COLOR); + ui_node_menu_column(bmain, arg, split, "Vector", NODE_CLASS_OP_VECTOR); + ui_node_menu_column(bmain, arg, split, "Convertor", NODE_CLASS_CONVERTOR); + + column= uiLayoutColumn(split, 0); + uiBlockSetCurLayout(block, column); + + if(sock->link) { + uiItemL(column, "Link", ICON_NONE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + + but = uiDefBut(block, BUT, 0, "Remove", 0, 0, UI_UNIT_X*4, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, "Remove nodes connected to the input"); + uiButSetNFunc(but, ui_node_link, MEM_dupallocN(arg), SET_INT_IN_POINTER(UI_NODE_LINK_REMOVE)); + + but = uiDefBut(block, BUT, 0, "Disconnect", 0, 0, UI_UNIT_X*4, UI_UNIT_Y, + NULL, 0.0, 0.0, 0.0, 0.0, "Disconnect nodes connected to the input"); + uiButSetNFunc(but, ui_node_link, MEM_dupallocN(arg), SET_INT_IN_POINTER(UI_NODE_LINK_DISCONNECT)); + } + + ui_node_menu_column(bmain, arg, column, "Group", NODE_CLASS_GROUP); +} + +void uiTemplateNodeLink(uiLayout *layout, ID *id, bNodeTree *ntree, bNode *node, bNodeSocket *sock) +{ + uiBlock *block = uiLayoutGetBlock(layout); + NodeLinkArg *arg; + uiBut *but; + bNodeSocketType *stype = ui_node_input_socket_type(node, sock); + + arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); + arg->id = id; + arg->ntree = ntree; + arg->node = node; + arg->sock = sock; + arg->type = 0; + arg->output = 0; + + uiBlockSetCurLayout(block, layout); + + if(sock->link || sock->type == SOCK_CLOSURE || (stype && (stype->flag & SOCK_NO_VALUE))) { + char name[UI_MAX_NAME_STR]; + ui_node_sock_name(sock, name); + but= uiDefMenuBut(block, ui_template_node_link_menu, NULL, name, 0, 0, UI_UNIT_X*4, UI_UNIT_Y, ""); + } + else + but= uiDefIconMenuBut(block, ui_template_node_link_menu, NULL, ICON_NONE, 0, 0, UI_UNIT_X, UI_UNIT_Y, ""); + + but->type= MENU; + but->flag |= UI_TEXT_LEFT|UI_BUT_NODE_LINK; + but->poin= (char*)but; + but->func_argN = arg; +} + +/************************* Node Tree Layout **************************/ + +static void ui_node_draw_input(uiLayout *layout, bContext *C, ID *id, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth) +{ + PointerRNA inputptr; + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *bt; + uiLayout *split, *row, *col; + bNode *lnode; + bNodeSocket *linput; + char label[UI_MAX_NAME_STR]; + int indent = (depth > 1)? 2*(depth - 1): 0; + + /* to avoid eternal loops on cyclic dependencies */ + node->flag |= NODE_TEST; + lnode = (input->link)? input->link->fromnode: NULL; + + /* socket RNA pointer */ + RNA_pointer_create(&ntree->id, &RNA_NodeSocket, input, &inputptr); + + /* indented label */ + memset(label, ' ', indent); + label[indent] = '\0'; + BLI_snprintf(label, UI_MAX_NAME_STR, "%s%s:", label, input->name); + + /* split in label and value */ + split = uiLayoutSplit(layout, 0.35f, 0); + + row = uiLayoutRow(split, 1); + + if(depth > 0) { + uiBlockSetEmboss(block, UI_EMBOSSN); + + if(lnode && (lnode->inputs.first || (lnode->typeinfo->uifunc && lnode->type != NODE_GROUP))) { + int icon = (input->flag & SOCK_COLLAPSED)? ICON_DISCLOSURE_TRI_RIGHT: ICON_DISCLOSURE_TRI_DOWN; + uiItemR(row, &inputptr, "show_expanded", UI_ITEM_R_ICON_ONLY, "", icon); + } + else + uiItemL(row, "", ICON_BLANK1); + + bt = block->buttons.last; + bt->x2 = UI_UNIT_X/2; + + uiBlockSetEmboss(block, UI_EMBOSS); + } + + uiItemL(row, label, ICON_NONE); + + if(lnode) { + /* input linked to a node */ + uiTemplateNodeLink(split, id, ntree, node, input); + + if(!(input->flag & SOCK_COLLAPSED)) { + if(depth == 0) + uiItemS(layout); + + if(lnode->typeinfo->uifunc) { + if(lnode->type != NODE_GROUP) { + PointerRNA lnodeptr; + + split = uiLayoutSplit(layout, 0.35f, 0); + col = uiLayoutColumn(split, 0); + col = uiLayoutColumn(split, 0); + + RNA_pointer_create(&ntree->id, &RNA_Node, lnode, &lnodeptr); + lnode->typeinfo->uifunc(col, C, &lnodeptr); + } + } + + for(linput=lnode->inputs.first; linput; linput=linput->next) + ui_node_draw_input(layout, C, id, ntree, lnode, linput, depth+1); + } + } + else { + bNodeSocketType *stype = ui_node_input_socket_type(node, input); + + /* input not linked, show value */ + if(input->type != SOCK_CLOSURE && (!stype || !(stype->flag & SOCK_NO_VALUE))) { + if(input->type == SOCK_VECTOR) { + row = uiLayoutRow(split, 0); + col = uiLayoutColumn(row, 0); + + uiItemR(col, &inputptr, "default_value", 0, "", 0); + } + else { + row = uiLayoutRow(split, 1); + uiItemR(row, &inputptr, "default_value", 0, "", 0); + } + } + else + row = uiLayoutRow(split, 0); + + uiTemplateNodeLink(row, id, ntree, node, input); + } + + /* clear */ + node->flag &= ~NODE_TEST; +} + +void uiTemplateNodeView(uiLayout *layout, bContext *C, ID *id, bNodeTree *ntree, bNode *node, bNodeSocket *input) +{ + bNode *tnode; + + /* clear for cycle check */ + for(tnode=ntree->nodes.first; tnode; tnode=tnode->next) + tnode->flag &= ~NODE_TEST; + + ui_node_draw_input(layout, C, id, ntree, node, input, 0); +} + |