Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/interface/interface_node.c')
-rw-r--r--source/blender/editors/interface/interface_node.c567
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);
+}
+