1407 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1407 lines
		
	
	
		
			40 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * ***** 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.
 | |
|  *
 | |
|  * The Original Code is Copyright (C) 2008 Blender Foundation.
 | |
|  * All rights reserved.
 | |
|  *
 | |
|  * The Original Code is: all of this file.
 | |
|  * Contributor(s): Nathan Letwory
 | |
|  *
 | |
|  * ***** END GPL LICENSE BLOCK *****
 | |
|  */
 | |
| 
 | |
| /** \file blender/editors/space_node/node_draw.c
 | |
|  *  \ingroup spnode
 | |
|  *  \brief higher level node drawing for the node editor.
 | |
|  */
 | |
| 
 | |
| #include "DNA_lamp_types.h"
 | |
| #include "DNA_node_types.h"
 | |
| #include "DNA_material_types.h"
 | |
| #include "DNA_screen_types.h"
 | |
| #include "DNA_space_types.h"
 | |
| #include "DNA_texture_types.h"
 | |
| #include "DNA_world_types.h"
 | |
| #include "DNA_linestyle_types.h"
 | |
| 
 | |
| #include "BLI_math.h"
 | |
| #include "BLI_blenlib.h"
 | |
| 
 | |
| #include "BLT_translation.h"
 | |
| 
 | |
| #include "BKE_context.h"
 | |
| #include "BKE_depsgraph.h"
 | |
| #include "BKE_library.h"
 | |
| #include "BKE_main.h"
 | |
| #include "BKE_node.h"
 | |
| 
 | |
| #include "BLF_api.h"
 | |
| 
 | |
| #include "BIF_gl.h"
 | |
| #include "BIF_glutil.h"
 | |
| 
 | |
| #include "WM_api.h"
 | |
| #include "WM_types.h"
 | |
| 
 | |
| #include "ED_node.h"
 | |
| #include "ED_gpencil.h"
 | |
| #include "ED_space_api.h"
 | |
| 
 | |
| #include "UI_resources.h"
 | |
| #include "UI_view2d.h"
 | |
| 
 | |
| #include "RNA_access.h"
 | |
| 
 | |
| #include "node_intern.h"  /* own include */
 | |
| 
 | |
| #ifdef WITH_COMPOSITOR
 | |
| #  include "COM_compositor.h"
 | |
| #endif
 | |
| 
 | |
| /* XXX interface.h */
 | |
| extern void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
 | |
| 
 | |
| float ED_node_grid_size(void)
 | |
| {
 | |
| 	return U.widget_unit;
 | |
| }
 | |
| 
 | |
| void ED_node_tree_update(const bContext *C)
 | |
| {
 | |
| 	SpaceNode *snode = CTX_wm_space_node(C);
 | |
| 	if (snode) {
 | |
| 		snode_set_context(C);
 | |
| 
 | |
| 		id_us_ensure_real(&snode->nodetree->id);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* id is supposed to contain a node tree */
 | |
| static bNodeTree *node_tree_from_ID(ID *id)
 | |
| {
 | |
| 	if (id) {
 | |
| 		short idtype = GS(id->name);
 | |
| 	
 | |
| 		switch (idtype) {
 | |
| 			case ID_NT:
 | |
| 				return (bNodeTree *)id;
 | |
| 			case ID_MA:
 | |
| 				return ((Material *)id)->nodetree;
 | |
| 			case ID_LA:
 | |
| 				return ((Lamp *)id)->nodetree;
 | |
| 			case ID_WO:
 | |
| 				return ((World *)id)->nodetree;
 | |
| 			case ID_SCE:
 | |
| 				return ((Scene *)id)->nodetree;
 | |
| 			case ID_TE:
 | |
| 				return ((Tex *)id)->nodetree;
 | |
| 			case ID_LS:
 | |
| 				return ((FreestyleLineStyle *)id)->nodetree;
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	return NULL;
 | |
| }
 | |
| 
 | |
| void ED_node_tag_update_id(ID *id)
 | |
| {
 | |
| 	bNodeTree *ntree = node_tree_from_ID(id);
 | |
| 	if (id == NULL || ntree == NULL)
 | |
| 		return;
 | |
| 
 | |
| 	/* TODO(sergey): With the new dependency graph it
 | |
| 	 * should be just enough to only tag ntree itself,
 | |
| 	 * all the users of this tree will have update
 | |
| 	 * flushed from the tree,
 | |
| 	 */
 | |
| 	DAG_id_tag_update(&ntree->id, 0);
 | |
| 
 | |
| 	if (ntree->type == NTREE_SHADER) {
 | |
| 		DAG_id_tag_update(id, 0);
 | |
| 		
 | |
| 		if (GS(id->name) == ID_MA)
 | |
| 			WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
 | |
| 		else if (GS(id->name) == ID_LA)
 | |
| 			WM_main_add_notifier(NC_LAMP | ND_LIGHTING, id);
 | |
| 		else if (GS(id->name) == ID_WO)
 | |
| 			WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
 | |
| 	}
 | |
| 	else if (ntree->type == NTREE_COMPOSIT) {
 | |
| 		WM_main_add_notifier(NC_SCENE | ND_NODES, id);
 | |
| 	}
 | |
| 	else if (ntree->type == NTREE_TEXTURE) {
 | |
| 		DAG_id_tag_update(id, 0);
 | |
| 		WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
 | |
| 	}
 | |
| 	else if (id == &ntree->id) {
 | |
| 		/* node groups */
 | |
| 		DAG_id_tag_update(id, 0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree)
 | |
| {
 | |
| 	if (!ntree)
 | |
| 		return;
 | |
| 	
 | |
| 	/* look through all datablocks, to support groups */
 | |
| 	FOREACH_NODETREE(bmain, tntree, id) {
 | |
| 		/* check if nodetree uses the group */
 | |
| 		if (ntreeHasTree(tntree, ntree))
 | |
| 			ED_node_tag_update_id(id);
 | |
| 	} FOREACH_NODETREE_END
 | |
| 	
 | |
| 	if (ntree->type == NTREE_TEXTURE)
 | |
| 		ntreeTexCheckCyclics(ntree);
 | |
| }
 | |
| 
 | |
| static bool compare_nodes(const bNode *a, const bNode *b)
 | |
| {
 | |
| 	bNode *parent;
 | |
| 	/* These tell if either the node or any of the parent nodes is selected.
 | |
| 	 * A selected parent means an unselected node is also in foreground!
 | |
| 	 */
 | |
| 	bool a_select = (a->flag & NODE_SELECT) != 0, b_select = (b->flag & NODE_SELECT) != 0;
 | |
| 	bool a_active = (a->flag & NODE_ACTIVE) != 0, b_active = (b->flag & NODE_ACTIVE) != 0;
 | |
| 	
 | |
| 	/* if one is an ancestor of the other */
 | |
| 	/* XXX there might be a better sorting algorithm for stable topological sort, this is O(n^2) worst case */
 | |
| 	for (parent = a->parent; parent; parent = parent->parent) {
 | |
| 		/* if b is an ancestor, it is always behind a */
 | |
| 		if (parent == b)
 | |
| 			return 1;
 | |
| 		/* any selected ancestor moves the node forward */
 | |
| 		if (parent->flag & NODE_ACTIVE)
 | |
| 			a_active = 1;
 | |
| 		if (parent->flag & NODE_SELECT)
 | |
| 			a_select = 1;
 | |
| 	}
 | |
| 	for (parent = b->parent; parent; parent = parent->parent) {
 | |
| 		/* if a is an ancestor, it is always behind b */
 | |
| 		if (parent == a)
 | |
| 			return 0;
 | |
| 		/* any selected ancestor moves the node forward */
 | |
| 		if (parent->flag & NODE_ACTIVE)
 | |
| 			b_active = 1;
 | |
| 		if (parent->flag & NODE_SELECT)
 | |
| 			b_select = 1;
 | |
| 	}
 | |
| 
 | |
| 	/* if one of the nodes is in the background and the other not */
 | |
| 	if ((a->flag & NODE_BACKGROUND) && !(b->flag & NODE_BACKGROUND))
 | |
| 		return 0;
 | |
| 	else if (!(a->flag & NODE_BACKGROUND) && (b->flag & NODE_BACKGROUND))
 | |
| 		return 1;
 | |
| 	
 | |
| 	/* if one has a higher selection state (active > selected > nothing) */
 | |
| 	if (!b_active && a_active)
 | |
| 		return 1;
 | |
| 	else if (!b_select && (a_active || a_select))
 | |
| 		return 1;
 | |
| 	
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /* Sorts nodes by selection: unselected nodes first, then selected,
 | |
|  * then the active node at the very end. Relative order is kept intact!
 | |
|  */
 | |
| void ED_node_sort(bNodeTree *ntree)
 | |
| {
 | |
| 	/* merge sort is the algorithm of choice here */
 | |
| 	bNode *first_a, *first_b, *node_a, *node_b, *tmp;
 | |
| 	int totnodes = BLI_listbase_count(&ntree->nodes);
 | |
| 	int k, a, b;
 | |
| 	
 | |
| 	k = 1;
 | |
| 	while (k < totnodes) {
 | |
| 		first_a = first_b = ntree->nodes.first;
 | |
| 		
 | |
| 		do {
 | |
| 			/* setup first_b pointer */
 | |
| 			for (b = 0; b < k && first_b; ++b) {
 | |
| 				first_b = first_b->next;
 | |
| 			}
 | |
| 			/* all batches merged? */
 | |
| 			if (first_b == NULL)
 | |
| 				break;
 | |
| 			
 | |
| 			/* merge batches */
 | |
| 			node_a = first_a;
 | |
| 			node_b = first_b;
 | |
| 			a = b = 0;
 | |
| 			while (a < k && b < k && node_b) {
 | |
| 				if (compare_nodes(node_a, node_b) == 0) {
 | |
| 					node_a = node_a->next;
 | |
| 					a++;
 | |
| 				}
 | |
| 				else {
 | |
| 					tmp = node_b;
 | |
| 					node_b = node_b->next;
 | |
| 					b++;
 | |
| 					BLI_remlink(&ntree->nodes, tmp);
 | |
| 					BLI_insertlinkbefore(&ntree->nodes, node_a, tmp);
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			/* setup first pointers for next batch */
 | |
| 			first_b = node_b;
 | |
| 			for (; b < k; ++b) {
 | |
| 				/* all nodes sorted? */
 | |
| 				if (first_b == NULL)
 | |
| 					break;
 | |
| 				first_b = first_b->next;
 | |
| 			}
 | |
| 			first_a = first_b;
 | |
| 		} while (first_b);
 | |
| 		
 | |
| 		k = k << 1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 
 | |
| static void do_node_internal_buttons(bContext *C, void *UNUSED(node_v), int event)
 | |
| {
 | |
| 	if (event == B_NODE_EXEC) {
 | |
| 		SpaceNode *snode = CTX_wm_space_node(C);
 | |
| 		if (snode && snode->id)
 | |
| 			ED_node_tag_update_id(snode->id);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void node_uiblocks_init(const bContext *C, bNodeTree *ntree)
 | |
| {
 | |
| 	bNode *node;
 | |
| 	char uiblockstr[32];
 | |
| 	
 | |
| 	/* add node uiBlocks in drawing order - prevents events going to overlapping nodes */
 | |
| 	
 | |
| 	for (node = ntree->nodes.first; node; node = node->next) {
 | |
| 		/* ui block */
 | |
| 		BLI_snprintf(uiblockstr, sizeof(uiblockstr), "node buttons %p", (void *)node);
 | |
| 		node->block = UI_block_begin(C, CTX_wm_region(C), uiblockstr, UI_EMBOSS);
 | |
| 		UI_block_func_handle_set(node->block, do_node_internal_buttons, node);
 | |
| 
 | |
| 		/* this cancels events for background nodes */
 | |
| 		UI_block_flag_enable(node->block, UI_BLOCK_CLIP_EVENTS);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void node_to_view(struct bNode *node, float x, float y, float *rx, float *ry)
 | |
| {
 | |
| 	nodeToView(node, x, y, rx, ry);
 | |
| 	*rx *= UI_DPI_FAC;
 | |
| 	*ry *= UI_DPI_FAC;
 | |
| }
 | |
| 
 | |
| void node_to_updated_rect(struct bNode *node, rctf *r_rect)
 | |
| {
 | |
| 	node_to_view(node, node->offsetx, node->offsety, &r_rect->xmin, &r_rect->ymax);
 | |
| 	node_to_view(node, node->offsetx + node->width, node->offsety - node->height, &r_rect->xmax, &r_rect->ymin);
 | |
| }
 | |
| 
 | |
| void node_from_view(struct bNode *node, float x, float y, float *rx, float *ry)
 | |
| {
 | |
| 	x /= UI_DPI_FAC;
 | |
| 	y /= UI_DPI_FAC;
 | |
| 	nodeFromView(node, x, y, rx, ry);
 | |
| }
 | |
| 
 | |
| 
 | |
| /* based on settings in node, sets drawing rect info. each redraw! */
 | |
| static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
 | |
| {
 | |
| 	uiLayout *layout, *row;
 | |
| 	PointerRNA nodeptr, sockptr;
 | |
| 	bNodeSocket *nsock;
 | |
| 	float locx, locy;
 | |
| 	float dy;
 | |
| 	int buty;
 | |
| 	
 | |
| 	RNA_pointer_create(&ntree->id, &RNA_Node, node, &nodeptr);
 | |
| 	
 | |
| 	/* get "global" coords */
 | |
| 	node_to_view(node, 0.0f, 0.0f, &locx, &locy);
 | |
| 	dy = locy;
 | |
| 	
 | |
| 	/* header */
 | |
| 	dy -= NODE_DY;
 | |
| 	
 | |
| 	/* little bit space in top */
 | |
| 	if (node->outputs.first)
 | |
| 		dy -= NODE_DYS / 2;
 | |
| 	
 | |
| 	/* output sockets */
 | |
| 	for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
 | |
| 		if (nodeSocketIsHidden(nsock))
 | |
| 			continue;
 | |
| 		
 | |
| 		RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
 | |
| 		
 | |
| 		layout = UI_block_layout(
 | |
| 		        node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
 | |
| 		        locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, 0, UI_style_get());
 | |
| 		/* context pointers for current node and socket */
 | |
| 		uiLayoutSetContextPointer(layout, "node", &nodeptr);
 | |
| 		uiLayoutSetContextPointer(layout, "socket", &sockptr);
 | |
| 		
 | |
| 		/* align output buttons to the right */
 | |
| 		row = uiLayoutRow(layout, 1);
 | |
| 		uiLayoutSetAlignment(row, UI_LAYOUT_ALIGN_RIGHT);
 | |
| 		
 | |
| 		nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(nsock->name));
 | |
| 		
 | |
| 		UI_block_align_end(node->block);
 | |
| 		UI_block_layout_resolve(node->block, NULL, &buty);
 | |
| 		
 | |
| 		/* ensure minimum socket height in case layout is empty */
 | |
| 		buty = min_ii(buty, dy - NODE_DY);
 | |
| 		
 | |
| 		nsock->locx = locx + NODE_WIDTH(node);
 | |
| 		/* place the socket circle in the middle of the layout */
 | |
| 		nsock->locy = 0.5f * (dy + buty);
 | |
| 		
 | |
| 		dy = buty;
 | |
| 		if (nsock->next)
 | |
| 			dy -= NODE_SOCKDY;
 | |
| 	}
 | |
| 
 | |
| 	node->prvr.xmin = locx + NODE_DYS;
 | |
| 	node->prvr.xmax = locx + NODE_WIDTH(node) - NODE_DYS;
 | |
| 
 | |
| 	/* preview rect? */
 | |
| 	if (node->flag & NODE_PREVIEW) {
 | |
| 		float aspect = 1.0f;
 | |
| 		
 | |
| 		if (node->preview_xsize && node->preview_ysize) 
 | |
| 			aspect = (float)node->preview_ysize / (float)node->preview_xsize;
 | |
| 		
 | |
| 		dy -= NODE_DYS / 2;
 | |
| 		node->prvr.ymax = dy;
 | |
| 		
 | |
| 		if (aspect <= 1.0f)
 | |
| 			node->prvr.ymin = dy - aspect * (NODE_WIDTH(node) - NODE_DY);
 | |
| 		else {
 | |
| 			/* width correction of image */
 | |
| 			/* XXX huh? (ton) */
 | |
| 			float dx = (NODE_WIDTH(node) - NODE_DYS) - (NODE_WIDTH(node) - NODE_DYS) / aspect;
 | |
| 			
 | |
| 			node->prvr.ymin = dy - (NODE_WIDTH(node) - NODE_DY);
 | |
| 			
 | |
| 			node->prvr.xmin += 0.5f * dx;
 | |
| 			node->prvr.xmax -= 0.5f * dx;
 | |
| 		}
 | |
| 		
 | |
| 		dy = node->prvr.ymin - NODE_DYS / 2;
 | |
| 		
 | |
| 		/* make sure that maximums are bigger or equal to minimums */
 | |
| 		if (node->prvr.xmax < node->prvr.xmin) SWAP(float, node->prvr.xmax, node->prvr.xmin);
 | |
| 		if (node->prvr.ymax < node->prvr.ymin) SWAP(float, node->prvr.ymax, node->prvr.ymin);
 | |
| 	}
 | |
| 
 | |
| 	/* buttons rect? */
 | |
| 	if (node->typeinfo->draw_buttons && (node->flag & NODE_OPTIONS)) {
 | |
| 		dy -= NODE_DYS / 2;
 | |
| 
 | |
| 		/* set this for uifunc() that don't use layout engine yet */
 | |
| 		node->butr.xmin = 0;
 | |
| 		node->butr.xmax = NODE_WIDTH(node) - 2 * NODE_DYS;
 | |
| 		node->butr.ymin = 0;
 | |
| 		node->butr.ymax = 0;
 | |
| 		
 | |
| 			
 | |
| 		layout = UI_block_layout(
 | |
| 		        node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
 | |
| 		        locx + NODE_DYS, dy, node->butr.xmax, 0, 0, UI_style_get());
 | |
| 		uiLayoutSetContextPointer(layout, "node", &nodeptr);
 | |
| 		
 | |
| 		node->typeinfo->draw_buttons(layout, (bContext *)C, &nodeptr);
 | |
| 		
 | |
| 		UI_block_align_end(node->block);
 | |
| 		UI_block_layout_resolve(node->block, NULL, &buty);
 | |
| 			
 | |
| 		dy = buty - NODE_DYS / 2;
 | |
| 	}
 | |
| 
 | |
| 	/* input sockets */
 | |
| 	for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
 | |
| 		if (nodeSocketIsHidden(nsock))
 | |
| 			continue;
 | |
| 		
 | |
| 		RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
 | |
| 		
 | |
| 		layout = UI_block_layout(
 | |
| 		        node->block, UI_LAYOUT_VERTICAL, UI_LAYOUT_PANEL,
 | |
| 		        locx + NODE_DYS, dy, NODE_WIDTH(node) - NODE_DY, NODE_DY, 0, UI_style_get());
 | |
| 		/* context pointers for current node and socket */
 | |
| 		uiLayoutSetContextPointer(layout, "node", &nodeptr);
 | |
| 		uiLayoutSetContextPointer(layout, "socket", &sockptr);
 | |
| 		
 | |
| 		row = uiLayoutRow(layout, 1);
 | |
| 		
 | |
| 		nsock->typeinfo->draw((bContext *)C, row, &sockptr, &nodeptr, IFACE_(nsock->name));
 | |
| 		
 | |
| 		UI_block_align_end(node->block);
 | |
| 		UI_block_layout_resolve(node->block, NULL, &buty);
 | |
| 		
 | |
| 		/* ensure minimum socket height in case layout is empty */
 | |
| 		buty = min_ii(buty, dy - NODE_DY);
 | |
| 		
 | |
| 		nsock->locx = locx;
 | |
| 		/* place the socket circle in the middle of the layout */
 | |
| 		nsock->locy = 0.5f * (dy + buty);
 | |
| 		
 | |
| 		dy = buty;
 | |
| 		if (nsock->next)
 | |
| 			dy -= NODE_SOCKDY;
 | |
| 	}
 | |
| 	
 | |
| 	/* little bit space in end */
 | |
| 	if (node->inputs.first || (node->flag & (NODE_OPTIONS | NODE_PREVIEW)) == 0)
 | |
| 		dy -= NODE_DYS / 2;
 | |
| 
 | |
| 	node->totr.xmin = locx;
 | |
| 	node->totr.xmax = locx + NODE_WIDTH(node);
 | |
| 	node->totr.ymax = locy;
 | |
| 	node->totr.ymin = min_ff(dy, locy - 2 * NODE_DY);
 | |
| 	
 | |
| 	/* Set the block bounds to clip mouse events from underlying nodes.
 | |
| 	 * Add a margin for sockets on each side.
 | |
| 	 */
 | |
| 	UI_block_bounds_set_explicit(
 | |
| 	        node->block,
 | |
| 	        node->totr.xmin - NODE_SOCKSIZE,
 | |
| 	        node->totr.ymin,
 | |
| 	        node->totr.xmax + NODE_SOCKSIZE,
 | |
| 	        node->totr.ymax);
 | |
| }
 | |
| 
 | |
| /* based on settings in node, sets drawing rect info. each redraw! */
 | |
| static void node_update_hidden(bNode *node)
 | |
| {
 | |
| 	bNodeSocket *nsock;
 | |
| 	float locx, locy;
 | |
| 	float rad, drad, hiddenrad = HIDDEN_RAD;
 | |
| 	int totin = 0, totout = 0, tot;
 | |
| 	
 | |
| 	/* get "global" coords */
 | |
| 	node_to_view(node, 0.0f, 0.0f, &locx, &locy);
 | |
| 
 | |
| 	/* calculate minimal radius */
 | |
| 	for (nsock = node->inputs.first; nsock; nsock = nsock->next)
 | |
| 		if (!nodeSocketIsHidden(nsock))
 | |
| 			totin++;
 | |
| 	for (nsock = node->outputs.first; nsock; nsock = nsock->next)
 | |
| 		if (!nodeSocketIsHidden(nsock))
 | |
| 			totout++;
 | |
| 	
 | |
| 	tot = MAX2(totin, totout);
 | |
| 	if (tot > 4) {
 | |
| 		hiddenrad += 5.0f * (float)(tot - 4);
 | |
| 	}
 | |
| 	
 | |
| 	node->totr.xmin = locx;
 | |
| 	node->totr.xmax = locx + 3 * hiddenrad + node->miniwidth;
 | |
| 	node->totr.ymax = locy + (hiddenrad - 0.5f * NODE_DY);
 | |
| 	node->totr.ymin = node->totr.ymax - 2 * hiddenrad;
 | |
| 	
 | |
| 	/* output sockets */
 | |
| 	rad = drad = (float)M_PI / (1.0f + (float)totout);
 | |
| 	
 | |
| 	for (nsock = node->outputs.first; nsock; nsock = nsock->next) {
 | |
| 		if (!nodeSocketIsHidden(nsock)) {
 | |
| 			nsock->locx = node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad;
 | |
| 			nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
 | |
| 			rad += drad;
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/* input sockets */
 | |
| 	rad = drad = -(float)M_PI / (1.0f + (float)totin);
 | |
| 	
 | |
| 	for (nsock = node->inputs.first; nsock; nsock = nsock->next) {
 | |
| 		if (!nodeSocketIsHidden(nsock)) {
 | |
| 			nsock->locx = node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad;
 | |
| 			nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad;
 | |
| 			rad += drad;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Set the block bounds to clip mouse events from underlying nodes.
 | |
| 	 * Add a margin for sockets on each side.
 | |
| 	 */
 | |
| 	UI_block_bounds_set_explicit(
 | |
| 	        node->block,
 | |
| 	        node->totr.xmin - NODE_SOCKSIZE,
 | |
| 	        node->totr.ymin,
 | |
| 	        node->totr.xmax + NODE_SOCKSIZE,
 | |
| 	        node->totr.ymax);
 | |
| }
 | |
| 
 | |
| void node_update_default(const bContext *C, bNodeTree *ntree, bNode *node)
 | |
| {
 | |
| 	if (node->flag & NODE_HIDDEN)
 | |
| 		node_update_hidden(node);
 | |
| 	else
 | |
| 		node_update_basis(C, ntree, node);
 | |
| }
 | |
| 
 | |
| int node_select_area_default(bNode *node, int x, int y)
 | |
| {
 | |
| 	return BLI_rctf_isect_pt(&node->totr, x, y);
 | |
| }
 | |
| 
 | |
| int node_tweak_area_default(bNode *node, int x, int y)
 | |
| {
 | |
| 	return BLI_rctf_isect_pt(&node->totr, x, y);
 | |
| }
 | |
| 
 | |
| int node_get_colorid(bNode *node)
 | |
| {
 | |
| 	switch (node->typeinfo->nclass) {
 | |
| 		case NODE_CLASS_INPUT:      return TH_NODE_INPUT;
 | |
| 		case NODE_CLASS_OUTPUT:     return (node->flag & NODE_DO_OUTPUT) ? TH_NODE_OUTPUT : TH_NODE;
 | |
| 		case NODE_CLASS_CONVERTOR:  return TH_NODE_CONVERTOR;
 | |
| 		case NODE_CLASS_OP_COLOR:   return TH_NODE_COLOR;
 | |
| 		case NODE_CLASS_OP_VECTOR:  return TH_NODE_VECTOR;
 | |
| 		case NODE_CLASS_OP_FILTER:  return TH_NODE_FILTER;
 | |
| 		case NODE_CLASS_GROUP:      return TH_NODE_GROUP;
 | |
| 		case NODE_CLASS_INTERFACE:  return TH_NODE_INTERFACE;
 | |
| 		case NODE_CLASS_MATTE:      return TH_NODE_MATTE;
 | |
| 		case NODE_CLASS_DISTORT:    return TH_NODE_DISTORT;
 | |
| 		case NODE_CLASS_TEXTURE:    return TH_NODE_TEXTURE;
 | |
| 		case NODE_CLASS_SHADER:     return TH_NODE_SHADER;
 | |
| 		case NODE_CLASS_SCRIPT:     return TH_NODE_SCRIPT;
 | |
| 		case NODE_CLASS_PATTERN:    return TH_NODE_PATTERN;
 | |
| 		case NODE_CLASS_LAYOUT:     return TH_NODE_LAYOUT;
 | |
| 		default:                    return TH_NODE;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* note: in cmp_util.c is similar code, for node_compo_pass_on()
 | |
|  *       the same goes for shader and texture nodes. */
 | |
| /* note: in node_edit.c is similar code, for untangle node */
 | |
| static void node_draw_mute_line(View2D *v2d, SpaceNode *snode, bNode *node)
 | |
| {
 | |
| 	bNodeLink *link;
 | |
| 
 | |
| 	glEnable(GL_BLEND);
 | |
| 	glEnable(GL_LINE_SMOOTH);
 | |
| 
 | |
| 	for (link = node->internal_links.first; link; link = link->next)
 | |
| 		node_draw_link_bezier(v2d, snode, link, TH_REDALERT, 0, TH_WIRE, 0, TH_WIRE);
 | |
| 
 | |
| 	glDisable(GL_BLEND);
 | |
| 	glDisable(GL_LINE_SMOOTH);
 | |
| }
 | |
| 
 | |
| /* this might have some more generic use */
 | |
| static void node_circle_draw(float x, float y, float size, const float col[4], int highlight)
 | |
| {
 | |
| 	/* 16 values of sin function */
 | |
| 	static const float si[16] = {
 | |
| 		0.00000000f, 0.39435585f, 0.72479278f, 0.93775213f,
 | |
| 		0.99871650f, 0.89780453f, 0.65137248f, 0.29936312f,
 | |
| 		-0.10116832f, -0.48530196f, -0.79077573f, -0.96807711f,
 | |
| 		-0.98846832f, -0.84864425f, -0.57126821f, -0.20129852f
 | |
| 	};
 | |
| 	/* 16 values of cos function */
 | |
| 	static const float co[16] = {
 | |
| 		1.00000000f, 0.91895781f, 0.68896691f, 0.34730525f,
 | |
| 		-0.05064916f, -0.44039415f, -0.75875812f, -0.95413925f,
 | |
| 		-0.99486932f, -0.87434661f, -0.61210598f, -0.25065253f,
 | |
| 		0.15142777f, 0.52896401f, 0.82076344f, 0.97952994f,
 | |
| 	};
 | |
| 	int a;
 | |
| 	
 | |
| 	glColor4fv(col);
 | |
| 	
 | |
| 	glEnable(GL_BLEND);
 | |
| 	glBegin(GL_POLYGON);
 | |
| 	for (a = 0; a < 16; a++)
 | |
| 		glVertex2f(x + size * si[a], y + size * co[a]);
 | |
| 	glEnd();
 | |
| 	glDisable(GL_BLEND);
 | |
| 	
 | |
| 	if (highlight) {
 | |
| 		UI_ThemeColor(TH_TEXT_HI);
 | |
| 		glLineWidth(1.5f);
 | |
| 	}
 | |
| 	else {
 | |
| 		glColor4ub(0, 0, 0, 150);
 | |
| 	}
 | |
| 	glEnable(GL_BLEND);
 | |
| 	glEnable(GL_LINE_SMOOTH);
 | |
| 	glBegin(GL_LINE_LOOP);
 | |
| 	for (a = 0; a < 16; a++)
 | |
| 		glVertex2f(x + size * si[a], y + size * co[a]);
 | |
| 	glEnd();
 | |
| 	glDisable(GL_LINE_SMOOTH);
 | |
| 	glDisable(GL_BLEND);
 | |
| 	glLineWidth(1.0f);
 | |
| }
 | |
| 
 | |
| void node_socket_circle_draw(const bContext *C, bNodeTree *ntree, bNode *node, bNodeSocket *sock, float size, int highlight)
 | |
| {
 | |
| 	PointerRNA ptr, node_ptr;
 | |
| 	float color[4];
 | |
| 	
 | |
| 	RNA_pointer_create((ID *)ntree, &RNA_NodeSocket, sock, &ptr);
 | |
| 	RNA_pointer_create((ID *)ntree, &RNA_Node, node, &node_ptr);
 | |
| 	sock->typeinfo->draw_color((bContext *)C, &ptr, &node_ptr, color);
 | |
| 	node_circle_draw(sock->locx, sock->locy, size, color, highlight);
 | |
| }
 | |
| 
 | |
| /* **************  Socket callbacks *********** */
 | |
| 
 | |
| static void node_draw_preview_background(float tile, rctf *rect)
 | |
| {
 | |
| 	float x, y;
 | |
| 	
 | |
| 	/* draw checkerboard backdrop to show alpha */
 | |
| 	glColor3ub(120, 120, 120);
 | |
| 	glRectf(rect->xmin, rect->ymin, rect->xmax, rect->ymax);
 | |
| 	glColor3ub(160, 160, 160);
 | |
| 	
 | |
| 	for (y = rect->ymin; y < rect->ymax; y += tile * 2) {
 | |
| 		for (x = rect->xmin; x < rect->xmax; x += tile * 2) {
 | |
| 			float tilex = tile, tiley = tile;
 | |
| 
 | |
| 			if (x + tile > rect->xmax)
 | |
| 				tilex = rect->xmax - x;
 | |
| 			if (y + tile > rect->ymax)
 | |
| 				tiley = rect->ymax - y;
 | |
| 
 | |
| 			glRectf(x, y, x + tilex, y + tiley);
 | |
| 		}
 | |
| 	}
 | |
| 	for (y = rect->ymin + tile; y < rect->ymax; y += tile * 2) {
 | |
| 		for (x = rect->xmin + tile; x < rect->xmax; x += tile * 2) {
 | |
| 			float tilex = tile, tiley = tile;
 | |
| 
 | |
| 			if (x + tile > rect->xmax)
 | |
| 				tilex = rect->xmax - x;
 | |
| 			if (y + tile > rect->ymax)
 | |
| 				tiley = rect->ymax - y;
 | |
| 
 | |
| 			glRectf(x, y, x + tilex, y + tiley);
 | |
| 		}
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* not a callback */
 | |
| static void node_draw_preview(bNodePreview *preview, rctf *prv)
 | |
| {
 | |
| 	float xrect = BLI_rctf_size_x(prv);
 | |
| 	float yrect = BLI_rctf_size_y(prv);
 | |
| 	float xscale = xrect / ((float)preview->xsize);
 | |
| 	float yscale = yrect / ((float)preview->ysize);
 | |
| 	float scale;
 | |
| 	rctf draw_rect;
 | |
| 	
 | |
| 	/* uniform scale and offset */
 | |
| 	draw_rect = *prv;
 | |
| 	if (xscale < yscale) {
 | |
| 		float offset = 0.5f * (yrect - ((float)preview->ysize) * xscale);
 | |
| 		draw_rect.ymin += offset;
 | |
| 		draw_rect.ymax -= offset;
 | |
| 		scale = xscale;
 | |
| 	}
 | |
| 	else {
 | |
| 		float offset = 0.5f * (xrect - ((float)preview->xsize) * yscale);
 | |
| 		draw_rect.xmin += offset;
 | |
| 		draw_rect.xmax -= offset;
 | |
| 		scale = yscale;
 | |
| 	}
 | |
| 	
 | |
| 	node_draw_preview_background(BLI_rctf_size_x(prv) / 10.0f, &draw_rect);
 | |
| 	
 | |
| 	glEnable(GL_BLEND);
 | |
| 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);  /* premul graphics */
 | |
| 	
 | |
| 	glColor4f(1.0, 1.0, 1.0, 1.0);
 | |
| 	glPixelZoom(scale, scale);
 | |
| 	glaDrawPixelsTex(draw_rect.xmin, draw_rect.ymin, preview->xsize, preview->ysize, GL_RGBA, GL_UNSIGNED_BYTE, GL_LINEAR, preview->rect);
 | |
| 	glPixelZoom(1.0f, 1.0f);
 | |
| 	
 | |
| 	glDisable(GL_BLEND);
 | |
| 
 | |
| 	UI_ThemeColorShadeAlpha(TH_BACK, -15, +100);
 | |
| 	fdrawbox(draw_rect.xmin, draw_rect.ymin, draw_rect.xmax, draw_rect.ymax);
 | |
| }
 | |
| 
 | |
| /* common handle function for operator buttons that need to select the node first */
 | |
| static void node_toggle_button_cb(struct bContext *C, void *node_argv, void *op_argv)
 | |
| {
 | |
| 	bNode *node = (bNode *)node_argv;
 | |
| 	const char *opname = (const char *)op_argv;
 | |
| 	
 | |
| 	/* select & activate only the button's node */
 | |
| 	node_select_single(C, node);
 | |
| 	
 | |
| 	WM_operator_name_call(C, opname, WM_OP_INVOKE_DEFAULT, NULL);
 | |
| }
 | |
| 
 | |
| void node_draw_shadow(SpaceNode *snode, bNode *node, float radius, float alpha)
 | |
| {
 | |
| 	rctf *rct = &node->totr;
 | |
| 	
 | |
| 	UI_draw_roundbox_corner_set(UI_CNR_ALL);
 | |
| 	if (node->parent == NULL)
 | |
| 		ui_draw_dropshadow(rct, radius, snode->aspect, alpha, node->flag & SELECT);
 | |
| 	else {
 | |
| 		const float margin = 3.0f;
 | |
| 		
 | |
| 		glColor4f(0.0f, 0.0f, 0.0f, 0.33f);
 | |
| 		glEnable(GL_BLEND);
 | |
| 		UI_draw_roundbox(rct->xmin - margin, rct->ymin - margin,
 | |
| 		                 rct->xmax + margin, rct->ymax + margin, radius + margin);
 | |
| 		glDisable(GL_BLEND);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void node_draw_basis(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
 | |
| {
 | |
| 	bNodeInstanceHash *previews = CTX_data_pointer_get(C, "node_previews").data;
 | |
| 	bNodeSocket *sock;
 | |
| 	rctf *rct = &node->totr;
 | |
| 	float iconofs;
 | |
| 	/* float socket_size = NODE_SOCKSIZE*U.dpi/72; */ /* UNUSED */
 | |
| 	float iconbutw = 0.8f * UI_UNIT_X;
 | |
| 	int color_id = node_get_colorid(node);
 | |
| 	char showname[128]; /* 128 used below */
 | |
| 	View2D *v2d = &ar->v2d;
 | |
| 	
 | |
| 	/* XXX hack: copy values from linked ID data where displayed as sockets */
 | |
| 	if (node->block)
 | |
| 		nodeSynchronizeID(node, false);
 | |
| 	
 | |
| 	/* skip if out of view */
 | |
| 	if (BLI_rctf_isect(&node->totr, &ar->v2d.cur, NULL) == false) {
 | |
| 		UI_block_end(C, node->block);
 | |
| 		node->block = NULL;
 | |
| 		return;
 | |
| 	}
 | |
| 	
 | |
| 	/* shadow */
 | |
| 	node_draw_shadow(snode, node, BASIS_RAD, 1.0f);
 | |
| 	
 | |
| 	/* header uses color from backdrop, but we make it opaqie */
 | |
| 	if (color_id == TH_NODE) {
 | |
| 		float col[3];
 | |
| 		UI_GetThemeColorShade3fv(color_id, -20, col);
 | |
| 		glColor4f(col[0], col[1], col[2], 1.0f);
 | |
| 	}
 | |
| 	else
 | |
| 		UI_ThemeColor(color_id);
 | |
| 	
 | |
| 	if (node->flag & NODE_MUTED)
 | |
| 		UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
 | |
| 	
 | |
| 
 | |
| #ifdef WITH_COMPOSITOR
 | |
| 	if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
 | |
| 		if (COM_isHighlightedbNode(node)) {
 | |
| 			UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
 | |
| 		}
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	UI_draw_roundbox_corner_set(UI_CNR_TOP_LEFT | UI_CNR_TOP_RIGHT);
 | |
| 	UI_draw_roundbox(rct->xmin, rct->ymax - NODE_DY, rct->xmax, rct->ymax, BASIS_RAD);
 | |
| 	
 | |
| 	/* show/hide icons */
 | |
| 	iconofs = rct->xmax - 0.35f * U.widget_unit;
 | |
| 	
 | |
| 	/* preview */
 | |
| 	if (node->typeinfo->flag & NODE_PREVIEW) {
 | |
| 		uiBut *but;
 | |
| 		iconofs -= iconbutw;
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
 | |
| 		but = uiDefIconBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, ICON_MATERIAL,
 | |
| 		                   iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
 | |
| 		UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_preview_toggle");
 | |
| 		/* XXX this does not work when node is activated and the operator called right afterwards,
 | |
| 		 * since active ID is not updated yet (needs to process the notifier).
 | |
| 		 * This can only work as visual indicator!
 | |
| 		 */
 | |
| //		if (!(node->flag & (NODE_ACTIVE_ID|NODE_DO_OUTPUT)))
 | |
| //			UI_but_flag_enable(but, UI_BUT_DISABLED);
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS);
 | |
| 	}
 | |
| 	/* group edit */
 | |
| 	if (node->type == NODE_GROUP) {
 | |
| 		uiBut *but;
 | |
| 		iconofs -= iconbutw;
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
 | |
| 		but = uiDefIconBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, ICON_NODETREE,
 | |
| 		                   iconofs, rct->ymax - NODE_DY, iconbutw, UI_UNIT_Y, NULL, 0, 0, 0, 0, "");
 | |
| 		UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_group_edit");
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS);
 | |
| 	}
 | |
| 	
 | |
| 	/* title */
 | |
| 	if (node->flag & SELECT) 
 | |
| 		UI_ThemeColor(TH_SELECT);
 | |
| 	else
 | |
| 		UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
 | |
| 	
 | |
| 	/* open/close entirely? */
 | |
| 	{
 | |
| 		uiBut *but;
 | |
| 		int but_size = UI_UNIT_X * 0.6f;
 | |
| 		/* XXX button uses a custom triangle draw below, so make it invisible without icon */
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
 | |
| 		but = uiDefBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, "",
 | |
| 		               rct->xmin + 0.5f * U.widget_unit - but_size / 2, rct->ymax - NODE_DY / 2.0f - but_size / 2,
 | |
| 		               but_size, but_size, NULL, 0, 0, 0, 0, "");
 | |
| 		UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS);
 | |
| 		
 | |
| 		/* custom draw function for this button */
 | |
| 		UI_draw_icon_tri(rct->xmin + 0.5f * U.widget_unit, rct->ymax - NODE_DY / 2.0f, 'v');
 | |
| 	}
 | |
| 	
 | |
| 	/* this isn't doing anything for the label, so commenting out */
 | |
| #if 0
 | |
| 	if (node->flag & SELECT) 
 | |
| 		UI_ThemeColor(TH_TEXT_HI);
 | |
| 	else
 | |
| 		UI_ThemeColor(TH_TEXT);
 | |
| #endif
 | |
| 	
 | |
| 	nodeLabel(ntree, node, showname, sizeof(showname));
 | |
| 	
 | |
| 	//if (node->flag & NODE_MUTED)
 | |
| 	//	BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
 | |
| 	
 | |
| 	uiDefBut(node->block, UI_BTYPE_LABEL, 0, showname,
 | |
| 	         (int)(rct->xmin + (NODE_MARGIN_X)), (int)(rct->ymax - NODE_DY),
 | |
| 	         (short)(iconofs - rct->xmin - 18.0f), (short)NODE_DY,
 | |
| 	         NULL, 0, 0, 0, 0, "");
 | |
| 
 | |
| 	/* body */
 | |
| 	if (!nodeIsRegistered(node))
 | |
| 		UI_ThemeColor4(TH_REDALERT);	/* use warning color to indicate undefined types */
 | |
| 	else if (node->flag & NODE_CUSTOM_COLOR)
 | |
| 		glColor3fv(node->color);
 | |
| 	else
 | |
| 		UI_ThemeColor4(TH_NODE);
 | |
| 	glEnable(GL_BLEND);
 | |
| 	UI_draw_roundbox_corner_set(UI_CNR_BOTTOM_LEFT | UI_CNR_BOTTOM_RIGHT);
 | |
| 	UI_draw_roundbox(rct->xmin, rct->ymin, rct->xmax, rct->ymax - NODE_DY, BASIS_RAD);
 | |
| 	glDisable(GL_BLEND);
 | |
| 
 | |
| 	/* outline active and selected emphasis */
 | |
| 	if (node->flag & SELECT) {
 | |
| 		
 | |
| 		glEnable(GL_BLEND);
 | |
| 		glEnable(GL_LINE_SMOOTH);
 | |
| 		
 | |
| 		if (node->flag & NODE_ACTIVE)
 | |
| 			UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
 | |
| 		else
 | |
| 			UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
 | |
| 		
 | |
| 		UI_draw_roundbox_corner_set(UI_CNR_ALL);
 | |
| 		UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, BASIS_RAD);
 | |
| 		
 | |
| 		glDisable(GL_LINE_SMOOTH);
 | |
| 		glDisable(GL_BLEND);
 | |
| 	}
 | |
| 	
 | |
| 	/* disable lines */
 | |
| 	if (node->flag & NODE_MUTED)
 | |
| 		node_draw_mute_line(v2d, snode, node);
 | |
| 
 | |
| 	
 | |
| 	/* socket inputs, buttons */
 | |
| 	for (sock = node->inputs.first; sock; sock = sock->next) {
 | |
| 		if (nodeSocketIsHidden(sock))
 | |
| 			continue;
 | |
| 		
 | |
| 		node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
 | |
| 	}
 | |
| 	
 | |
| 	/* socket outputs */
 | |
| 	for (sock = node->outputs.first; sock; sock = sock->next) {
 | |
| 		if (nodeSocketIsHidden(sock))
 | |
| 			continue;
 | |
| 		
 | |
| 		node_socket_circle_draw(C, ntree, node, sock, NODE_SOCKSIZE, sock->flag & SELECT);
 | |
| 	}
 | |
| 	
 | |
| 	/* preview */
 | |
| 	if (node->flag & NODE_PREVIEW && previews) {
 | |
| 		bNodePreview *preview = BKE_node_instance_hash_lookup(previews, key);
 | |
| 		if (preview && (preview->xsize && preview->ysize)) {
 | |
| 			if (preview->rect && !BLI_rctf_is_empty(&node->prvr)) {
 | |
| 				node_draw_preview(preview, &node->prvr);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	UI_ThemeClearColor(color_id);
 | |
| 		
 | |
| 	UI_block_end(C, node->block);
 | |
| 	UI_block_draw(C, node->block);
 | |
| 	node->block = NULL;
 | |
| }
 | |
| 
 | |
| static void node_draw_hidden(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey UNUSED(key))
 | |
| {
 | |
| 	bNodeSocket *sock;
 | |
| 	rctf *rct = &node->totr;
 | |
| 	float dx, centy = BLI_rctf_cent_y(rct);
 | |
| 	float hiddenrad = BLI_rctf_size_y(rct) / 2.0f;
 | |
| 	float socket_size = NODE_SOCKSIZE;
 | |
| 	int color_id = node_get_colorid(node);
 | |
| 	char showname[128]; /* 128 is used below */
 | |
| 	
 | |
| 	/* shadow */
 | |
| 	node_draw_shadow(snode, node, hiddenrad, 1.0f);
 | |
| 
 | |
| 	/* body */
 | |
| 	UI_ThemeColor(color_id);
 | |
| 	if (node->flag & NODE_MUTED)
 | |
| 		UI_ThemeColorBlend(color_id, TH_REDALERT, 0.5f);
 | |
| 
 | |
| #ifdef WITH_COMPOSITOR
 | |
| 	if (ntree->type == NTREE_COMPOSIT && (snode->flag & SNODE_SHOW_HIGHLIGHT)) {
 | |
| 		if (COM_isHighlightedbNode(node)) {
 | |
| 			UI_ThemeColorBlend(color_id, TH_ACTIVE, 0.5f);
 | |
| 		}
 | |
| 	}
 | |
| #else
 | |
| 	(void)ntree;
 | |
| #endif
 | |
| 	
 | |
| 	UI_draw_roundbox(rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
 | |
| 	
 | |
| 	/* outline active and selected emphasis */
 | |
| 	if (node->flag & SELECT) {
 | |
| 		glEnable(GL_BLEND);
 | |
| 		glEnable(GL_LINE_SMOOTH);
 | |
| 		
 | |
| 		if (node->flag & NODE_ACTIVE)
 | |
| 			UI_ThemeColorShadeAlpha(TH_ACTIVE, 0, -40);
 | |
| 		else
 | |
| 			UI_ThemeColorShadeAlpha(TH_SELECT, 0, -40);
 | |
| 		UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rct->xmin, rct->ymin, rct->xmax, rct->ymax, hiddenrad);
 | |
| 		
 | |
| 		glDisable(GL_LINE_SMOOTH);
 | |
| 		glDisable(GL_BLEND);
 | |
| 	}
 | |
| 
 | |
| 	/* custom color inline */
 | |
| 	if (node->flag & NODE_CUSTOM_COLOR) {
 | |
| 		glEnable(GL_BLEND);
 | |
| 		glEnable(GL_LINE_SMOOTH);
 | |
| 
 | |
| 		glColor3fv(node->color);
 | |
| 		UI_draw_roundbox_gl_mode(GL_LINE_LOOP, rct->xmin + 1, rct->ymin + 1, rct->xmax -1, rct->ymax - 1, hiddenrad);
 | |
| 
 | |
| 		glDisable(GL_LINE_SMOOTH);
 | |
| 		glDisable(GL_BLEND);
 | |
| 	}
 | |
| 
 | |
| 	/* title */
 | |
| 	if (node->flag & SELECT) 
 | |
| 		UI_ThemeColor(TH_SELECT);
 | |
| 	else
 | |
| 		UI_ThemeColorBlendShade(TH_TEXT, color_id, 0.4f, 10);
 | |
| 	
 | |
| 	/* open entirely icon */
 | |
| 	{
 | |
| 		uiBut *but;
 | |
| 		int but_size = UI_UNIT_X * 0.6f;
 | |
| 		/* XXX button uses a custom triangle draw below, so make it invisible without icon */
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS_NONE);
 | |
| 		but = uiDefBut(node->block, UI_BTYPE_BUT_TOGGLE, B_REDR, "",
 | |
| 		               rct->xmin + 10.0f - but_size / 2, centy - but_size / 2,
 | |
| 		               but_size, but_size, NULL, 0, 0, 0, 0, "");
 | |
| 		UI_but_func_set(but, node_toggle_button_cb, node, (void *)"NODE_OT_hide_toggle");
 | |
| 		UI_block_emboss_set(node->block, UI_EMBOSS);
 | |
| 		
 | |
| 		/* custom draw function for this button */
 | |
| 		UI_draw_icon_tri(rct->xmin + 10.0f, centy, 'h');
 | |
| 	}
 | |
| 	
 | |
| 	/* disable lines */
 | |
| 	if (node->flag & NODE_MUTED)
 | |
| 		node_draw_mute_line(&ar->v2d, snode, node);
 | |
| 	
 | |
| 	if (node->flag & SELECT) 
 | |
| 		UI_ThemeColor(TH_SELECT);
 | |
| 	else
 | |
| 		UI_ThemeColor(TH_TEXT);
 | |
| 	
 | |
| 	if (node->miniwidth > 0.0f) {
 | |
| 		nodeLabel(ntree, node, showname, sizeof(showname));
 | |
| 
 | |
| 		//if (node->flag & NODE_MUTED)
 | |
| 		//	BLI_snprintf(showname, sizeof(showname), "[%s]", showname); /* XXX - don't print into self! */
 | |
| 
 | |
| 		uiDefBut(node->block, UI_BTYPE_LABEL, 0, showname,
 | |
| 		         (int)(rct->xmin + (NODE_MARGIN_X)), (int)(centy - 10),
 | |
| 		         (short)(BLI_rctf_size_x(rct) - 18.0f - 12.0f), (short)NODE_DY,
 | |
| 		         NULL, 0, 0, 0, 0, "");
 | |
| 	}
 | |
| 
 | |
| 	/* scale widget thing */
 | |
| 	UI_ThemeColorShade(color_id, -10);
 | |
| 	dx = 10.0f;
 | |
| 	fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
 | |
| 	fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
 | |
| 	
 | |
| 	UI_ThemeColorShade(color_id, +30);
 | |
| 	dx -= snode->aspect;
 | |
| 	fdrawline(rct->xmax - dx, centy - 4.0f, rct->xmax - dx, centy + 4.0f);
 | |
| 	fdrawline(rct->xmax - dx - 3.0f * snode->aspect, centy - 4.0f, rct->xmax - dx - 3.0f * snode->aspect, centy + 4.0f);
 | |
| 
 | |
| 	/* sockets */
 | |
| 	for (sock = node->inputs.first; sock; sock = sock->next) {
 | |
| 		if (!nodeSocketIsHidden(sock))
 | |
| 			node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
 | |
| 	}
 | |
| 	
 | |
| 	for (sock = node->outputs.first; sock; sock = sock->next) {
 | |
| 		if (!nodeSocketIsHidden(sock))
 | |
| 			node_socket_circle_draw(C, ntree, node, sock, socket_size, sock->flag & SELECT);
 | |
| 	}
 | |
| 	
 | |
| 	UI_block_end(C, node->block);
 | |
| 	UI_block_draw(C, node->block);
 | |
| 	node->block = NULL;
 | |
| }
 | |
| 
 | |
| int node_get_resize_cursor(int directions)
 | |
| {
 | |
| 	if (directions == 0)
 | |
| 		return CURSOR_STD;
 | |
| 	else if ((directions & ~(NODE_RESIZE_TOP | NODE_RESIZE_BOTTOM)) == 0)
 | |
| 		return CURSOR_Y_MOVE;
 | |
| 	else if ((directions & ~(NODE_RESIZE_RIGHT | NODE_RESIZE_LEFT)) == 0)
 | |
| 		return CURSOR_X_MOVE;
 | |
| 	else
 | |
| 		return CURSOR_EDIT;
 | |
| }
 | |
| 
 | |
| void node_set_cursor(wmWindow *win, SpaceNode *snode, float cursor[2])
 | |
| {
 | |
| 	bNodeTree *ntree = snode->edittree;
 | |
| 	bNode *node;
 | |
| 	bNodeSocket *sock;
 | |
| 	int wmcursor = CURSOR_STD;
 | |
| 	
 | |
| 	if (ntree) {
 | |
| 		if (node_find_indicated_socket(snode, &node, &sock, cursor, SOCK_IN | SOCK_OUT)) {
 | |
| 			/* pass */
 | |
| 		}
 | |
| 		else {
 | |
| 			/* check nodes front to back */
 | |
| 			for (node = ntree->nodes.last; node; node = node->prev) {
 | |
| 				if (BLI_rctf_isect_pt(&node->totr, cursor[0], cursor[1]))
 | |
| 					break;  /* first hit on node stops */
 | |
| 			}
 | |
| 			if (node) {
 | |
| 				int dir = node->typeinfo->resize_area_func(node, cursor[0], cursor[1]);
 | |
| 				wmcursor = node_get_resize_cursor(dir);
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	WM_cursor_set(win, wmcursor);
 | |
| }
 | |
| 
 | |
| void node_draw_default(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
 | |
| {
 | |
| 	if (node->flag & NODE_HIDDEN)
 | |
| 		node_draw_hidden(C, ar, snode, ntree, node, key);
 | |
| 	else
 | |
| 		node_draw_basis(C, ar, snode, ntree, node, key);
 | |
| }
 | |
| 
 | |
| static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
 | |
| {
 | |
| 	if (node->typeinfo->draw_nodetype_prepare)
 | |
| 		node->typeinfo->draw_nodetype_prepare(C, ntree, node);
 | |
| }
 | |
| 
 | |
| void node_update_nodetree(const bContext *C, bNodeTree *ntree)
 | |
| {
 | |
| 	bNode *node;
 | |
| 	
 | |
| 	/* make sure socket "used" tags are correct, for displaying value buttons */
 | |
| 	ntreeTagUsedSockets(ntree);
 | |
| 	
 | |
| 	/* update nodes front to back, so children sizes get updated before parents */
 | |
| 	for (node = ntree->nodes.last; node; node = node->prev) {
 | |
| 		node_update(C, ntree, node);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void node_draw(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNode *node, bNodeInstanceKey key)
 | |
| {
 | |
| 	if (node->typeinfo->draw_nodetype)
 | |
| 		node->typeinfo->draw_nodetype(C, ar, snode, ntree, node, key);
 | |
| }
 | |
| 
 | |
| #define USE_DRAW_TOT_UPDATE
 | |
| 
 | |
| void node_draw_nodetree(const bContext *C, ARegion *ar, SpaceNode *snode, bNodeTree *ntree, bNodeInstanceKey parent_key)
 | |
| {
 | |
| 	bNode *node;
 | |
| 	bNodeLink *link;
 | |
| 	int a;
 | |
| 	
 | |
| 	if (ntree == NULL) return;      /* groups... */
 | |
| 
 | |
| #ifdef USE_DRAW_TOT_UPDATE
 | |
| 	if (ntree->nodes.first) {
 | |
| 		BLI_rctf_init_minmax(&ar->v2d.tot);
 | |
| 	}
 | |
| #endif
 | |
| 
 | |
| 	/* draw background nodes, last nodes in front */
 | |
| 	for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
 | |
| 		bNodeInstanceKey key;
 | |
| 
 | |
| #ifdef USE_DRAW_TOT_UPDATE
 | |
| 		/* unrelated to background nodes, update the v2d->tot,
 | |
| 		 * can be anywhere before we draw the scroll bars */
 | |
| 		BLI_rctf_union(&ar->v2d.tot, &node->totr);
 | |
| #endif
 | |
| 
 | |
| 		if (!(node->flag & NODE_BACKGROUND))
 | |
| 			continue;
 | |
| 
 | |
| 		key = BKE_node_instance_key(parent_key, ntree, node);
 | |
| 		node->nr = a;        /* index of node in list, used for exec event code */
 | |
| 		node_draw(C, ar, snode, ntree, node, key);
 | |
| 	}
 | |
| 	
 | |
| 	/* node lines */
 | |
| 	glEnable(GL_BLEND);
 | |
| 	glEnable(GL_LINE_SMOOTH);
 | |
| 	for (link = ntree->links.first; link; link = link->next) {
 | |
| 		if (!nodeLinkIsHidden(link))
 | |
| 			node_draw_link(&ar->v2d, snode, link);
 | |
| 	}
 | |
| 	glDisable(GL_LINE_SMOOTH);
 | |
| 	glDisable(GL_BLEND);
 | |
| 	
 | |
| 	/* draw foreground nodes, last nodes in front */
 | |
| 	for (a = 0, node = ntree->nodes.first; node; node = node->next, a++) {
 | |
| 		bNodeInstanceKey key;
 | |
| 		if (node->flag & NODE_BACKGROUND)
 | |
| 			continue;
 | |
| 
 | |
| 		key = BKE_node_instance_key(parent_key, ntree, node);
 | |
| 		node->nr = a;        /* index of node in list, used for exec event code */
 | |
| 		node_draw(C, ar, snode, ntree, node, key);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* draw tree path info in lower left corner */
 | |
| static void draw_tree_path(SpaceNode *snode)
 | |
| {
 | |
| 	char info[256];
 | |
| 	
 | |
| 	ED_node_tree_path_get_fixedbuf(snode, info, sizeof(info));
 | |
| 	
 | |
| 	UI_ThemeColor(TH_TEXT_HI);
 | |
| 	BLF_draw_default(1.5f * UI_UNIT_X, 1.5f * UI_UNIT_Y, 0.0f, info, sizeof(info));
 | |
| }
 | |
| 
 | |
| static void snode_setup_v2d(SpaceNode *snode, ARegion *ar, const float center[2])
 | |
| {
 | |
| 	View2D *v2d = &ar->v2d;
 | |
| 	
 | |
| 	/* shift view to node tree center */
 | |
| 	UI_view2d_center_set(v2d, center[0], center[1]);
 | |
| 	UI_view2d_view_ortho(v2d);
 | |
| 	
 | |
| 	/* aspect+font, set each time */
 | |
| 	snode->aspect = BLI_rctf_size_x(&v2d->cur) / (float)ar->winx;
 | |
| 	// XXX snode->curfont = uiSetCurFont_ext(snode->aspect);
 | |
| }
 | |
| 
 | |
| static void draw_nodetree(const bContext *C, ARegion *ar, bNodeTree *ntree, bNodeInstanceKey parent_key)
 | |
| {
 | |
| 	SpaceNode *snode = CTX_wm_space_node(C);
 | |
| 	
 | |
| 	node_uiblocks_init(C, ntree);
 | |
| 	
 | |
| #ifdef WITH_COMPOSITOR
 | |
| 	if (ntree->type == NTREE_COMPOSIT) {
 | |
| 		COM_startReadHighlights();
 | |
| 	}
 | |
| #endif
 | |
| 	
 | |
| 	node_update_nodetree(C, ntree);
 | |
| 	node_draw_nodetree(C, ar, snode, ntree, parent_key);
 | |
| }
 | |
| 
 | |
| /* shade the parent node group and add a uiBlock to clip mouse events */
 | |
| static void draw_group_overlay(const bContext *C, ARegion *ar)
 | |
| {
 | |
| 	View2D *v2d = &ar->v2d;
 | |
| 	rctf rect = v2d->cur;
 | |
| 	uiBlock *block;
 | |
| 	
 | |
| 	/* shade node groups to separate them visually */
 | |
| 	UI_ThemeColorShadeAlpha(TH_NODE_GROUP, 0, -70);
 | |
| 	glEnable(GL_BLEND);
 | |
| 	UI_draw_roundbox_corner_set(UI_CNR_NONE);
 | |
| 	UI_draw_roundbox_gl_mode(GL_POLYGON, rect.xmin, rect.ymin, rect.xmax, rect.ymax, 0);
 | |
| 	glDisable(GL_BLEND);
 | |
| 	
 | |
| 	/* set the block bounds to clip mouse events from underlying nodes */
 | |
| 	block = UI_block_begin(C, ar, "node tree bounds block", UI_EMBOSS);
 | |
| 	UI_block_bounds_set_explicit(block, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
 | |
| 	UI_block_flag_enable(block, UI_BLOCK_CLIP_EVENTS);
 | |
| 	UI_block_end(C, block);
 | |
| }
 | |
| 
 | |
| void drawnodespace(const bContext *C, ARegion *ar)
 | |
| {
 | |
| 	wmWindow *win = CTX_wm_window(C);
 | |
| 	View2DScrollers *scrollers;
 | |
| 	SpaceNode *snode = CTX_wm_space_node(C);
 | |
| 	View2D *v2d = &ar->v2d;
 | |
| 
 | |
| 	UI_ThemeClearColor(TH_BACK);
 | |
| 	glClear(GL_COLOR_BUFFER_BIT);
 | |
| 
 | |
| 	UI_view2d_view_ortho(v2d);
 | |
| 	
 | |
| 	/* XXX snode->cursor set in coordspace for placing new nodes, used for drawing noodles too */
 | |
| 	UI_view2d_region_to_view(&ar->v2d, win->eventstate->x - ar->winrct.xmin, win->eventstate->y - ar->winrct.ymin,
 | |
| 	                         &snode->cursor[0], &snode->cursor[1]);
 | |
| 	snode->cursor[0] /= UI_DPI_FAC;
 | |
| 	snode->cursor[1] /= UI_DPI_FAC;
 | |
| 	
 | |
| 	ED_region_draw_cb_draw(C, ar, REGION_DRAW_PRE_VIEW);
 | |
| 
 | |
| 	/* only set once */
 | |
| 	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 | |
| 	glEnable(GL_MAP1_VERTEX_3);
 | |
| 	
 | |
| 	/* nodes */
 | |
| 	snode_set_context(C);
 | |
| 	
 | |
| 	/* draw parent node trees */
 | |
| 	if (snode->treepath.last) {
 | |
| 		static const int max_depth = 2;
 | |
| 		bNodeTreePath *path;
 | |
| 		int depth, curdepth;
 | |
| 		float center[2];
 | |
| 		bNodeTree *ntree;
 | |
| 		bNodeLinkDrag *nldrag;
 | |
| 		LinkData *linkdata;
 | |
| 		
 | |
| 		path = snode->treepath.last;
 | |
| 		
 | |
| 		/* update tree path name (drawn in the bottom left) */
 | |
| 		if (snode->id && UNLIKELY(!STREQ(path->node_name, snode->id->name + 2))) {
 | |
| 			BLI_strncpy(path->node_name, snode->id->name + 2, sizeof(path->node_name));
 | |
| 		}
 | |
| 		
 | |
| 		/* current View2D center, will be set temporarily for parent node trees */
 | |
| 		UI_view2d_center_get(v2d, ¢er[0], ¢er[1]);
 | |
| 		
 | |
| 		/* store new view center in path and current edittree */
 | |
| 		copy_v2_v2(path->view_center, center);
 | |
| 		if (snode->edittree)
 | |
| 			copy_v2_v2(snode->edittree->view_center, center);
 | |
| 		
 | |
| 		depth = 0;
 | |
| 		while (path->prev && depth < max_depth) {
 | |
| 			path = path->prev;
 | |
| 			++depth;
 | |
| 		}
 | |
| 		
 | |
| 		/* parent node trees in the background */
 | |
| 		for (curdepth = depth; curdepth > 0; path = path->next, --curdepth) {
 | |
| 			ntree = path->nodetree;
 | |
| 			if (ntree) {
 | |
| 				snode_setup_v2d(snode, ar, path->view_center);
 | |
| 				
 | |
| 				draw_nodetree(C, ar, ntree, path->parent_key);
 | |
| 				
 | |
| 				draw_group_overlay(C, ar);
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		/* top-level edit tree */
 | |
| 		ntree = path->nodetree;
 | |
| 		if (ntree) {
 | |
| 			snode_setup_v2d(snode, ar, center);
 | |
| 			
 | |
| 			/* grid, uses theme color based on node path depth */
 | |
| 			UI_view2d_multi_grid_draw(v2d, (depth > 0 ? TH_NODE_GROUP : TH_BACK), ED_node_grid_size(), NODE_GRID_STEPS, 2);
 | |
| 			
 | |
| 			/* backdrop */
 | |
| 			draw_nodespace_back_pix(C, ar, snode, path->parent_key);
 | |
| 			
 | |
| 			draw_nodetree(C, ar, ntree, path->parent_key);
 | |
| 		}
 | |
| 		
 | |
| 		/* temporary links */
 | |
| 		glEnable(GL_BLEND);
 | |
| 		glEnable(GL_LINE_SMOOTH);
 | |
| 		for (nldrag = snode->linkdrag.first; nldrag; nldrag = nldrag->next) {
 | |
| 			for (linkdata = nldrag->links.first; linkdata; linkdata = linkdata->next)
 | |
| 				node_draw_link(v2d, snode, (bNodeLink *)linkdata->data);
 | |
| 		}
 | |
| 		glDisable(GL_LINE_SMOOTH);
 | |
| 		glDisable(GL_BLEND);
 | |
| 		
 | |
| 		if (snode->flag & SNODE_SHOW_GPENCIL) {
 | |
| 			/* draw grease-pencil ('canvas' strokes) */
 | |
| 			ED_gpencil_draw_view2d(C, true);
 | |
| 		}
 | |
| 	}
 | |
| 	else {
 | |
| 		/* default grid */
 | |
| 		UI_view2d_multi_grid_draw(v2d, TH_BACK, ED_node_grid_size(), NODE_GRID_STEPS, 2);
 | |
| 		
 | |
| 		/* backdrop */
 | |
| 		draw_nodespace_back_pix(C, ar, snode, NODE_INSTANCE_KEY_NONE);
 | |
| 	}
 | |
| 	
 | |
| 	ED_region_draw_cb_draw(C, ar, REGION_DRAW_POST_VIEW);
 | |
| 	
 | |
| 	/* reset view matrix */
 | |
| 	UI_view2d_view_restore(C);
 | |
| 	
 | |
| 	if (snode->treepath.last) {
 | |
| 		if (snode->flag & SNODE_SHOW_GPENCIL) {
 | |
| 			/* draw grease-pencil (screen strokes, and also paintbuffer) */
 | |
| 			ED_gpencil_draw_view2d(C, false);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* tree path info */
 | |
| 	draw_tree_path(snode);
 | |
| 	
 | |
| 	/* scrollers */
 | |
| 	scrollers = UI_view2d_scrollers_calc(C, v2d, 10, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY);
 | |
| 	UI_view2d_scrollers_draw(C, v2d, scrollers);
 | |
| 	UI_view2d_scrollers_free(scrollers);
 | |
| }
 |