diff options
Diffstat (limited to 'source/blender/editors/space_node')
-rw-r--r-- | source/blender/editors/space_node/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/editors/space_node/SConscript | 2 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 106 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_buttons.c | 37 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_draw.c | 5 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_edit.c | 64 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_header.c | 48 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_templates.c | 647 |
8 files changed, 851 insertions, 60 deletions
diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index 8a27bcca8af..f33b784c5d2 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -25,6 +25,7 @@ set(INC ../../blenlib ../../blenloader ../../imbuf + ../../gpu ../../makesdna ../../makesrna ../../nodes @@ -47,6 +48,7 @@ set(SRC node_ops.c node_select.c node_state.c + node_templates.c space_node.c node_intern.h diff --git a/source/blender/editors/space_node/SConscript b/source/blender/editors/space_node/SConscript index c4309dcfca3..6b72fd066e0 100644 --- a/source/blender/editors/space_node/SConscript +++ b/source/blender/editors/space_node/SConscript @@ -4,7 +4,7 @@ Import ('env') sources = env.Glob('*.c') incs = '../include ../../blenfont ../../blenlib ../../blenkernel ../../makesdna ../../makesrna ../../imbuf' -incs += ' ../../nodes ../../render/extern/include ../../blenloader' +incs += ' ../../nodes ../../render/extern/include ../../blenloader ../../gpu' incs += ' ../../windowmanager #intern/guardedalloc #extern/glew/include' defs = [] cf = [] diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index b4d89e1365d..4394b4b1920 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -95,6 +95,14 @@ static void node_sync_cb(bContext *UNUSED(C), void *snode_v, void *node_v) } } +static void node_socket_button_label(const bContext *UNUSED(C), uiBlock *block, + bNodeTree *UNUSED(ntree), bNode *UNUSED(node), bNodeSocket *sock, + const char *UNUSED(name), int x, int y, int width) +{ + uiDefBut(block, LABEL, 0, sock->name, x, y, width, NODE_DY, NULL, 0, 0, 0, 0, ""); +} + + static void node_socket_button_default(const bContext *C, uiBlock *block, bNodeTree *ntree, bNode *node, bNodeSocket *sock, const char *name, int x, int y, int width) @@ -938,28 +946,28 @@ static void node_shader_buts_material(uiLayout *layout, bContext *C, PointerRNA static void node_shader_buts_mapping(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) { + PointerRNA mappingptr = RNA_pointer_get(ptr, "mapping"); uiLayout *row; uiItemL(layout, "Location:", ICON_NONE); row= uiLayoutRow(layout, 1); - uiItemR(row, ptr, "location", 0, "", ICON_NONE); + uiItemR(row, &mappingptr, "location", 0, "", ICON_NONE); uiItemL(layout, "Rotation:", ICON_NONE); row= uiLayoutRow(layout, 1); - uiItemR(row, ptr, "rotation", 0, "", ICON_NONE); + uiItemR(row, &mappingptr, "rotation", 0, "", ICON_NONE); uiItemL(layout, "Scale:", ICON_NONE); row= uiLayoutRow(layout, 1); - uiItemR(row, ptr, "scale", 0, "", ICON_NONE); + uiItemR(row, &mappingptr, "scale", 0, "", ICON_NONE); row= uiLayoutRow(layout, 1); - uiItemR(row, ptr, "use_min", 0, "Min", ICON_NONE); - uiItemR(row, ptr, "min", 0, "", ICON_NONE); + uiItemR(row, &mappingptr, "use_min", 0, "Min", ICON_NONE); + uiItemR(row, &mappingptr, "min", 0, "", ICON_NONE); row= uiLayoutRow(layout, 1); - uiItemR(row, ptr, "use_max", 0, "Max", ICON_NONE); - uiItemR(row, ptr, "max", 0, "", ICON_NONE); - + uiItemR(row, &mappingptr, "use_max", 0, "Max", ICON_NONE); + uiItemR(row, &mappingptr, "max", 0, "", ICON_NONE); } static void node_shader_buts_vect_math(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) @@ -986,6 +994,54 @@ static void node_shader_buts_geometry(uiLayout *layout, bContext *C, PointerRNA } } +static void node_shader_buts_attribute(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "attribute_name", 0, "Name", ICON_NONE); +} + +static void node_shader_buts_tex_image(uiLayout *layout, bContext *C, PointerRNA *ptr) +{ + //uiItemR(layout, ptr, "image", 0, "", ICON_NONE); + uiTemplateID(layout, C, ptr, "image", NULL, "IMAGE_OT_open", NULL); + uiItemR(layout, ptr, "color_space", 0, "", ICON_NONE); +} + +static void node_shader_buts_tex_sky(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "sun_direction", 0, "", ICON_NONE); + uiItemR(layout, ptr, "turbidity", 0, NULL, ICON_NONE); +} + +static void node_shader_buts_tex_gradient(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "gradient_type", 0, "", ICON_NONE); +} + +static void node_shader_buts_tex_magic(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "turbulence_depth", 0, NULL, ICON_NONE); +} + +static void node_shader_buts_tex_wave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "wave_type", 0, "", ICON_NONE); +} + +static void node_shader_buts_tex_musgrave(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "musgrave_type", 0, "", ICON_NONE); +} + +static void node_shader_buts_tex_voronoi(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "coloring", 0, "", ICON_NONE); +} + +static void node_shader_buts_glossy(uiLayout *layout, bContext *UNUSED(C), PointerRNA *ptr) +{ + uiItemR(layout, ptr, "distribution", 0, "", ICON_NONE); +} + static void node_shader_buts_dynamic(uiLayout *layout, bContext *C, PointerRNA *ptr) { Main *bmain= CTX_data_main(C); @@ -1072,6 +1128,37 @@ static void node_shader_set_butfunc(bNodeType *ntype) case SH_NODE_GEOMETRY: ntype->uifunc= node_shader_buts_geometry; break; + case SH_NODE_ATTRIBUTE: + ntype->uifunc= node_shader_buts_attribute; + break; + case SH_NODE_TEX_SKY: + ntype->uifunc= node_shader_buts_tex_sky; + break; + case SH_NODE_TEX_IMAGE: + ntype->uifunc= node_shader_buts_tex_image; + break; + case SH_NODE_TEX_ENVIRONMENT: + ntype->uifunc= node_shader_buts_tex_image; + break; + case SH_NODE_TEX_GRADIENT: + ntype->uifunc= node_shader_buts_tex_gradient; + break; + case SH_NODE_TEX_MAGIC: + ntype->uifunc= node_shader_buts_tex_magic; + break; + case SH_NODE_TEX_WAVE: + ntype->uifunc= node_shader_buts_tex_wave; + break; + case SH_NODE_TEX_MUSGRAVE: + ntype->uifunc= node_shader_buts_tex_musgrave; + break; + case SH_NODE_TEX_VORONOI: + ntype->uifunc= node_shader_buts_tex_voronoi; + break; + case SH_NODE_BSDF_GLOSSY: + case SH_NODE_BSDF_GLASS: + ntype->uifunc= node_shader_buts_glossy; + break; case NODE_DYNAMIC: ntype->uifunc= node_shader_buts_dynamic; break; @@ -2089,6 +2176,9 @@ void ED_init_node_butfuncs(void) case SOCK_RGBA: stype->buttonfunc = node_socket_button_color; break; + case SOCK_SHADER: + stype->buttonfunc = node_socket_button_label; + break; default: stype->buttonfunc = NULL; } diff --git a/source/blender/editors/space_node/node_buttons.c b/source/blender/editors/space_node/node_buttons.c index 3886c709196..15e5719be37 100644 --- a/source/blender/editors/space_node/node_buttons.c +++ b/source/blender/editors/space_node/node_buttons.c @@ -45,6 +45,7 @@ #include "BLI_utildefines.h" #include "BKE_context.h" +#include "BKE_global.h" #include "BKE_node.h" #include "BKE_screen.h" @@ -122,6 +123,34 @@ static void active_node_panel(const bContext *C, Panel *pa) node->typeinfo->uifunc(layout, (bContext *)C, &ptr); } +static int node_sockets_poll(const bContext *C, PanelType *UNUSED(pt)) +{ + SpaceNode *snode= CTX_wm_space_node(C); + + return (snode && snode->nodetree && G.rt == 777); +} + +static void node_sockets_panel(const bContext *C, Panel *pa) +{ + SpaceNode *snode= CTX_wm_space_node(C); + bNodeTree *ntree= (snode) ? snode->edittree : NULL; + bNode *node = (ntree) ? nodeGetActive(ntree) : NULL; + bNodeSocket *sock; + uiLayout *layout= pa->layout, *split; + char name[UI_MAX_NAME_STR]; + + if(ELEM(NULL, ntree, node)) + return; + + for(sock=node->inputs.first; sock; sock=sock->next) { + BLI_snprintf(name, sizeof(name), "%s:", sock->name); + + split = uiLayoutSplit(layout, 0.35f, 0); + uiItemL(split, name, ICON_NONE); + uiTemplateNodeLink(split, ntree, node, sock); + } +} + /* ******************* node buttons registration ************** */ void node_buttons_register(ARegionType *art) @@ -134,6 +163,14 @@ void node_buttons_register(ARegionType *art) pt->draw= active_node_panel; pt->poll= active_node_poll; BLI_addtail(&art->paneltypes, pt); + + pt= MEM_callocN(sizeof(PanelType), "spacetype node panel node sockets"); + strcpy(pt->idname, "NODE_PT_sockets"); + strcpy(pt->label, "Sockets"); + pt->draw= node_sockets_panel; + pt->poll= node_sockets_poll; + pt->flag |= PNL_DEFAULT_CLOSED; + BLI_addtail(&art->paneltypes, pt); pt= MEM_callocN(sizeof(PanelType), "spacetype node panel gpencil"); strcpy(pt->idname, "NODE_PT_gpencil"); diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c index c2a2f319c28..a932f1b10d9 100644 --- a/source/blender/editors/space_node/node_draw.c +++ b/source/blender/editors/space_node/node_draw.c @@ -108,7 +108,8 @@ void ED_node_changed_update(ID *id, bNode *node) WM_main_add_notifier(NC_WORLD|ND_WORLD_DRAW, id); } else if(treetype==NTREE_COMPOSIT) { - nodeUpdate(edittree, node); + if(node) + nodeUpdate(edittree, node); /* don't use NodeTagIDChanged, it gives far too many recomposites for image, scene layers, ... */ node= node_tree_get_editgroup(nodetree); @@ -691,7 +692,7 @@ static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bN node_socket_circle_draw(ntree, sock, NODE_SOCKSIZE); - if (sock->link) { + if (sock->link || (sock->flag & SOCK_HIDE_VALUE)) { uiDefBut(node->block, LABEL, 0, sock->name, sock->locx+NODE_DYS, sock->locy-NODE_DYS, node->width-NODE_DY, NODE_DY, NULL, 0, 0, 0, 0, ""); } diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 745611c6251..23855ff24e1 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -61,6 +61,7 @@ #include "BKE_material.h" #include "BKE_modifier.h" #include "BKE_paint.h" +#include "BKE_scene.h" #include "BKE_screen.h" #include "BKE_texture.h" #include "BKE_report.h" @@ -89,6 +90,8 @@ #include "RNA_enum_types.h" +#include "GPU_material.h" + #include "node_intern.h" static EnumPropertyItem socket_in_out_items[] = { @@ -269,13 +272,14 @@ bNode *node_tree_get_editgroup(bNodeTree *nodetree) /* assumes nothing being done in ntree yet, sets the default in/out node */ /* called from shading buttons or header */ -void ED_node_shader_default(Scene *UNUSED(scene), ID *id) +void ED_node_shader_default(Scene *scene, ID *id) { bNode *in, *out; - bNodeSocket *fromsock, *tosock; + bNodeSocket *fromsock, *tosock, *sock; bNodeTree *ntree; bNodeTemplate ntemp; int output_type, shader_type; + float color[3], strength = 1.0f; ntree= ntreeAddTree("Shader Nodetree", NTREE_SHADER, 0); @@ -284,24 +288,42 @@ void ED_node_shader_default(Scene *UNUSED(scene), ID *id) Material *ma= (Material*)id; ma->nodetree = ntree; - output_type = SH_NODE_OUTPUT; - shader_type = SH_NODE_MATERIAL; + if(scene_use_new_shading_nodes(scene)) { + output_type = SH_NODE_OUTPUT_MATERIAL; + shader_type = SH_NODE_BSDF_DIFFUSE; + } + else { + output_type = SH_NODE_OUTPUT; + shader_type = SH_NODE_MATERIAL; + } + + copy_v3_v3(color, &ma->r); + strength= 0.0f; break; } case ID_WO: { World *wo= (World*)id; wo->nodetree = ntree; - output_type = SH_NODE_OUTPUT; - shader_type = SH_NODE_MATERIAL; + output_type = SH_NODE_OUTPUT_WORLD; + shader_type = SH_NODE_BACKGROUND; + + copy_v3_v3(color, &wo->horr); + strength= 1.0f; break; } case ID_LA: { Lamp *la= (Lamp*)id; la->nodetree = ntree; - output_type = SH_NODE_OUTPUT; - shader_type = SH_NODE_MATERIAL; + output_type = SH_NODE_OUTPUT_LAMP; + shader_type = SH_NODE_EMISSION; + + copy_v3_v3(color, &la->r); + if(la->type == LA_LOCAL || la->type == LA_SPOT || la->type == LA_AREA) + strength= 100.0f; + else + strength= 1.0f; break; } default: @@ -322,6 +344,17 @@ void ED_node_shader_default(Scene *UNUSED(scene), ID *id) fromsock= in->outputs.first; tosock= out->inputs.first; nodeAddLink(ntree, in, fromsock, out, tosock); + + /* default values */ + if(scene_use_new_shading_nodes(scene)) { + sock= in->inputs.first; + copy_v3_v3(((bNodeSocketValueRGBA*)sock->default_value)->value, color); + + if(strength != 0.0f) { + sock= in->inputs.last; + ((bNodeSocketValueFloat*)sock->default_value)->value= strength; + } + } ntreeUpdateTree(ntree); } @@ -567,6 +600,8 @@ static int has_nodetree(bNodeTree *ntree, bNodeTree *lookup) void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) { + int was_active_texture = (node->flag & NODE_ACTIVE_TEXTURE); + nodeSetActive(ntree, node); if(node->type!=NODE_GROUP) { @@ -590,6 +625,15 @@ void ED_node_set_active(Main *bmain, bNodeTree *ntree, bNode *node) ED_node_generic_update(bmain, ntree, node); } + /* if active texture changed, free glsl materials */ + if((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) { + Material *ma; + + for(ma=bmain->mat.first; ma; ma=ma->id.next) + if(ma->nodetree && ma->use_nodes && has_nodetree(ma->nodetree, ntree)) + GPU_material_free(ma); + } + WM_main_add_notifier(NC_MATERIAL|ND_NODES, node->id); } else if(ntree->type==NTREE_COMPOSIT) { @@ -2211,9 +2255,7 @@ bNode *node_add_node(SpaceNode *snode, Main *bmain, Scene *scene, bNodeTemplate node->id = &scene->id; } else if(ELEM3(node->type, CMP_NODE_MOVIECLIP, CMP_NODE_MOVIEDISTORTION, CMP_NODE_STABILIZE2D)) { - if(G.main->movieclip.first == G.main->movieclip.last) { - node->id= G.main->movieclip.first; - } + node->id = (ID *)scene->clip; } ntreeCompositForceHidden(snode->edittree, scene); diff --git a/source/blender/editors/space_node/node_header.c b/source/blender/editors/space_node/node_header.c index 42b5dafa3e1..6dd5eeba832 100644 --- a/source/blender/editors/space_node/node_header.c +++ b/source/blender/editors/space_node/node_header.c @@ -226,52 +226,24 @@ static void node_add_menu(bContext *C, uiLayout *layout, void *arg_nodeclass) } } +static void node_menu_add_foreach_cb(void *calldata, int nclass, const char *name) +{ + uiLayout *layout= calldata; + uiItemMenuF(layout, name, 0, node_add_menu, SET_INT_IN_POINTER(nclass)); +} + static void node_menu_add(const bContext *C, Menu *menu) { Scene *scene= CTX_data_scene(C); SpaceNode *snode= CTX_wm_space_node(C); uiLayout *layout= menu->layout; + bNodeTreeType *ntreetype= ntreeGetType(snode->treetype); if(!snode->nodetree) uiLayoutSetActive(layout, 0); - - if(snode->treetype==NTREE_SHADER) { - uiItemMenuF(layout, IFACE_("Input"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_INPUT)); - uiItemMenuF(layout, IFACE_("Output"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OUTPUT)); - if(scene_use_new_shading_nodes(scene)) { - uiItemMenuF(layout, IFACE_("Shader"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_SHADER)); - uiItemMenuF(layout, IFACE_("Texture"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_TEXTURE)); - } - uiItemMenuF(layout, IFACE_("Color"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_COLOR)); - uiItemMenuF(layout, IFACE_("Vector"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_VECTOR)); - uiItemMenuF(layout, IFACE_("Convertor"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_CONVERTOR)); - uiItemMenuF(layout, IFACE_("Group"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_GROUP)); - //uiItemMenuF(layout, IFACE_("Dynamic"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_DYNAMIC)); - uiItemMenuF(layout, IFACE_("Layout"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_LAYOUT)); - } - else if(snode->treetype==NTREE_COMPOSIT) { - uiItemMenuF(layout, IFACE_("Input"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_INPUT)); - uiItemMenuF(layout, IFACE_("Output"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OUTPUT)); - uiItemMenuF(layout, IFACE_("Color"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_COLOR)); - uiItemMenuF(layout, IFACE_("Vector"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_VECTOR)); - uiItemMenuF(layout, IFACE_("Filter"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_FILTER)); - uiItemMenuF(layout, IFACE_("Convertor"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_CONVERTOR)); - uiItemMenuF(layout, IFACE_("Matte"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_MATTE)); - uiItemMenuF(layout, IFACE_("Distort"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_DISTORT)); - uiItemMenuF(layout, IFACE_("Group"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_GROUP)); - uiItemMenuF(layout, IFACE_("Layout"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_LAYOUT)); - } - else if(snode->treetype==NTREE_TEXTURE) { - uiItemMenuF(layout, IFACE_("Input"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_INPUT)); - uiItemMenuF(layout, IFACE_("Output"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OUTPUT)); - uiItemMenuF(layout, IFACE_("Color"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_OP_COLOR)); - uiItemMenuF(layout, IFACE_("Patterns"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_PATTERN)); - uiItemMenuF(layout, IFACE_("Textures"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_TEXTURE)); - uiItemMenuF(layout, IFACE_("Convertor"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_CONVERTOR)); - uiItemMenuF(layout, IFACE_("Distort"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_DISTORT)); - uiItemMenuF(layout, IFACE_("Group"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_GROUP)); - uiItemMenuF(layout, IFACE_("Layout"), 0, node_add_menu, SET_INT_IN_POINTER(NODE_CLASS_LAYOUT)); - } + + if(ntreetype && ntreetype->foreach_nodeclass) + ntreetype->foreach_nodeclass(scene, layout, node_menu_add_foreach_cb); } void node_menus_register(void) diff --git a/source/blender/editors/space_node/node_templates.c b/source/blender/editors/space_node/node_templates.c new file mode 100644 index 00000000000..fedb12f747c --- /dev/null +++ b/source/blender/editors/space_node/node_templates.c @@ -0,0 +1,647 @@ +/* + * $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_scene_types.h" +#include "DNA_screen_types.h" + +#include "BLI_listbase.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BLF_translation.h" + +#include "BKE_context.h" +#include "BKE_depsgraph.h" +#include "BKE_library.h" +#include "BKE_main.h" +#include "BKE_node.h" +#include "BKE_scene.h" + +#include "RNA_access.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "UI_interface.h" +#include "UI_resources.h" +#include "../interface/interface_intern.h" + +#include "ED_node.h" + +/************************* Node Socket Manipulation **************************/ + +static void node_tag_recursive(bNode *node) +{ + bNodeSocket *input; + + if(!node || (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) + node_tag_recursive(input->link->fromnode); +} + +static void node_clear_recursive(bNode *node) +{ + bNodeSocket *input; + + if(!node || !(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) + node_clear_recursive(input->link->fromnode); +} + +static void node_remove_linked(bNodeTree *ntree, bNode *rem_node) +{ + bNode *node, *next; + bNodeSocket *sock; + + if(!rem_node) + return; + + /* tag linked nodes to be removed */ + for(node=ntree->nodes.first; node; node=node->next) + node->flag &= ~NODE_TEST; + + 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) + 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); + } + } +} + +/* disconnect socket from the node it is connected to */ +static void node_socket_disconnect(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to) +{ + if(!sock_to->link) + return; + + nodeRemLink(ntree, sock_to->link); + + nodeUpdate(ntree, node_to); + ntreeUpdateTree(ntree); + + ED_node_generic_update(bmain, ntree, node_to); +} + +/* remove all nodes connected to this socket, if they aren't connected to other nodes */ +static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bNodeSocket *sock_to) +{ + if(!sock_to->link) + return; + + node_remove_linked(ntree, sock_to->link->fromnode); + + nodeUpdate(ntree, node_to); + ntreeUpdateTree(ntree); + + ED_node_generic_update(bmain, ntree, node_to); +} + +/* 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(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); + } + + if(sock_prev->default_value) { + if(sock_from->default_value) + MEM_freeN(sock_from->default_value); + + sock_from->default_value = MEM_dupallocN(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); +} + +/****************************** Node Link Menu *******************************/ + +#define UI_NODE_LINK_ADD 0 +#define UI_NODE_LINK_DISCONNECT -1 +#define UI_NODE_LINK_REMOVE -2 + +typedef struct NodeLinkArg { + Main *bmain; + Scene *scene; + bNodeTree *ntree; + bNode *node; + bNodeSocket *sock; + + bNodeTree *ngroup; + int type; + int output; + + uiLayout *layout; +} NodeLinkArg; + +static void ui_node_link(bContext *UNUSED(C), void *arg_p, void *event_p) +{ + NodeLinkArg *arg = (NodeLinkArg*)arg_p; + Main *bmain = arg->bmain; + bNode *node_to = arg->node; + bNodeSocket *sock_to = arg->sock; + bNodeTree *ntree = arg->ntree; + int event = GET_INT_FROM_POINTER(event_p); + bNodeTemplate ntemp; + + ntemp.type = arg->type; + ntemp.ngroup = arg->ngroup; + + if(event == UI_NODE_LINK_DISCONNECT) + node_socket_disconnect(bmain, ntree, node_to, sock_to); + else if(event == UI_NODE_LINK_REMOVE) + node_socket_remove(bmain, ntree, node_to, sock_to); + else + node_socket_add_replace(bmain, ntree, node_to, sock_to, &ntemp, arg->output); +} + +static void ui_node_sock_name(bNodeSocket *sock, char name[UI_MAX_NAME_STR]) +{ + if(sock->link && sock->link->fromnode) { + bNode *node = sock->link->fromnode; + char node_name[UI_MAX_NAME_STR]; + + if(node->type == NODE_GROUP) + BLI_strncpy(node_name, node->id->name+2, UI_MAX_NAME_STR); + else + BLI_strncpy(node_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", node_name, sock->link->fromsock->name); + else + BLI_strncpy(name, node_name, UI_MAX_NAME_STR); + } + else if(sock->type == SOCK_SHADER) + BLI_strncpy(name, "None", UI_MAX_NAME_STR); + else + BLI_strncpy(name, "Default", UI_MAX_NAME_STR); +} + +static int ui_compatible_sockets(int typeA, int typeB) +{ + return (typeA == typeB); +} + +static void ui_node_menu_column(NodeLinkArg *arg, int nclass, const char *cname) +{ + Main *bmain = arg->bmain; + bNodeTree *ntree = arg->ntree; + bNodeSocket *sock = arg->sock; + uiLayout *layout = arg->layout; + uiLayout *column = NULL; + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but; + bNodeType *ntype; + bNodeTree *ngroup; + NodeLinkArg *argN; + int first = 1; + int compatibility= 0; + + if(ntree->type == NTREE_SHADER) { + if(scene_use_new_shading_nodes(arg->scene)) + compatibility= NODE_NEW_SHADING; + else + compatibility= NODE_OLD_SHADING; + } + + 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_NODE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + + first = 0; + } + + if(num > 1) { + if(j == 0) { + uiItemL(column, ngroup->id.name+2, ICON_NODE); + 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 { + bNodeTreeType *ttype= ntreeGetType(ntree->type); + + for(ntype=ttype->node_types.first; ntype; ntype=ntype->next) { + bNodeSocketTemplate *stemp; + char name[UI_MAX_NAME_STR]; + int i, j, num = 0; + + if(compatibility && !(ntype->compatibility & compatibility)) + continue; + + if(ntype->nclass != nclass) + continue; + + for(i=0, stemp=ntype->outputs; stemp && stemp->type != -1; stemp++, i++) + if(ui_compatible_sockets(stemp->type, sock->type)) + num++; + + for(i=0, j=0, stemp=ntype->outputs; stemp && stemp->type != -1; stemp++, i++) { + if(!ui_compatible_sockets(stemp->type, sock->type)) + continue; + + if(first) { + column= uiLayoutColumn(layout, 0); + uiBlockSetCurLayout(block, column); + + uiItemL(column, cname, ICON_NODE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + + first = 0; + } + + if(num > 1) { + if(j == 0) { + uiItemL(column, ntype->name, ICON_NODE); + but= block->buttons.last; + but->flag= UI_TEXT_LEFT; + } + + BLI_snprintf(name, UI_MAX_NAME_STR, " %s", stemp->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 node_menu_column_foreach_cb(void *calldata, int nclass, const char *name) +{ + NodeLinkArg *arg = (NodeLinkArg*)calldata; + + if(!ELEM(nclass, NODE_CLASS_GROUP, NODE_CLASS_LAYOUT)) + ui_node_menu_column(arg, nclass, name); +} + +static void ui_template_node_link_menu(bContext *C, uiLayout *layout, void *but_p) +{ + Main *bmain= CTX_data_main(C); + Scene *scene= CTX_data_scene(C); + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = (uiBut*)but_p; + uiLayout *split, *column; + NodeLinkArg *arg = (NodeLinkArg*)but->func_argN; + bNodeSocket *sock = arg->sock; + bNodeTreeType *ntreetype= ntreeGetType(arg->ntree->type); + + uiBlockSetCurLayout(block, layout); + split= uiLayoutSplit(layout, 0, 0); + + arg->bmain= bmain; + arg->scene= scene; + arg->layout= split; + + if(ntreetype && ntreetype->foreach_nodeclass) + ntreetype->foreach_nodeclass(scene, arg, node_menu_column_foreach_cb); + + 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(arg, NODE_CLASS_GROUP, IFACE_("Group")); +} + +void uiTemplateNodeLink(uiLayout *layout, bNodeTree *ntree, bNode *node, bNodeSocket *sock) +{ + uiBlock *block = uiLayoutGetBlock(layout); + NodeLinkArg *arg; + uiBut *but; + + arg = MEM_callocN(sizeof(NodeLinkArg), "NodeLinkArg"); + arg->ntree = ntree; + arg->node = node; + arg->sock = sock; + arg->type = 0; + arg->output = 0; + + uiBlockSetCurLayout(block, layout); + + if(sock->link || sock->type == SOCK_SHADER || (sock->flag & SOCK_HIDE_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; + + if(sock->link && sock->link->fromnode) + if(sock->link->fromnode->flag & NODE_ACTIVE_TEXTURE) + but->flag |= UI_BUT_NODE_ACTIVE; +} + +/**************************** Node Tree Layout *******************************/ + +static void ui_node_draw_input(uiLayout *layout, bContext *C, + bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth); + +static void ui_node_draw_node(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, int depth) +{ + bNodeSocket *input; + uiLayout *col, *split; + PointerRNA nodeptr; + + RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr); + + if(node->typeinfo->uifunc) { + if(node->type != NODE_GROUP) { + split = uiLayoutSplit(layout, 0.35f, 0); + col = uiLayoutColumn(split, 0); + col = uiLayoutColumn(split, 0); + + node->typeinfo->uifunc(col, C, &nodeptr); + } + } + + for(input=node->inputs.first; input; input=input->next) + ui_node_draw_input(layout, C, ntree, node, input, depth+1); +} + +static void ui_node_draw_input(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input, int depth) +{ + PointerRNA inputptr; + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *bt; + uiLayout *split, *row, *col; + bNode *lnode; + char label[UI_MAX_NAME_STR]; + int indent = (depth > 1)? 2*(depth - 1): 0; + + if(input->flag & SOCK_UNAVAIL) + return; + + /* 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); + bt= block->buttons.last; + bt->flag= UI_TEXT_LEFT; + + if(lnode) { + /* input linked to a node */ + uiTemplateNodeLink(split, ntree, node, input); + + if(!(input->flag & SOCK_COLLAPSED)) { + if(depth == 0) + uiItemS(layout); + + ui_node_draw_node(layout, C, ntree, lnode, depth); + } + } + else { + /* input not linked, show value */ + if(input->type != SOCK_SHADER && !(input->flag & SOCK_HIDE_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, ntree, node, input); + } + + /* clear */ + node->flag &= ~NODE_TEST; +} + +void uiTemplateNodeView(uiLayout *layout, bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *input) +{ + bNode *tnode; + + if(!ntree) + return; + + /* clear for cycle check */ + for(tnode=ntree->nodes.first; tnode; tnode=tnode->next) + tnode->flag &= ~NODE_TEST; + + if(input) + ui_node_draw_input(layout, C, ntree, node, input, 0); + else + ui_node_draw_node(layout, C, ntree, node, 0); +} + |