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:
-rw-r--r--source/blender/blenkernel/intern/node.cc1
-rw-r--r--source/blender/editors/space_node/drawnode.c21
-rw-r--r--source/blender/editors/space_node/node_draw.c154
-rw-r--r--source/blender/editors/space_node/node_edit.c45
-rw-r--r--source/blender/editors/space_node/node_intern.h8
-rw-r--r--source/blender/editors/space_node/node_relationships.c186
-rw-r--r--source/blender/makesdna/DNA_node_types.h10
7 files changed, 391 insertions, 34 deletions
diff --git a/source/blender/blenkernel/intern/node.cc b/source/blender/blenkernel/intern/node.cc
index ed8dd84a9bb..e34afd1ce17 100644
--- a/source/blender/blenkernel/intern/node.cc
+++ b/source/blender/blenkernel/intern/node.cc
@@ -573,6 +573,7 @@ static void direct_link_node_socket(BlendDataReader *reader, bNodeSocket *sock)
sock->typeinfo = nullptr;
BLO_read_data_address(reader, &sock->storage);
BLO_read_data_address(reader, &sock->default_value);
+ sock->total_inputs = 0; /* Clear runtime data set before drawing. */
sock->cache = nullptr;
}
diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c
index 2d65302c656..4716f1c29ea 100644
--- a/source/blender/editors/space_node/drawnode.c
+++ b/source/blender/editors/space_node/drawnode.c
@@ -3560,10 +3560,10 @@ void draw_nodespace_back_pix(const bContext *C,
}
/* return quadratic beziers points for a given nodelink and clip if v2d is not NULL. */
-static bool node_link_bezier_handles(const View2D *v2d,
- const SpaceNode *snode,
- const bNodeLink *link,
- float vec[4][2])
+bool node_link_bezier_handles(const View2D *v2d,
+ const SpaceNode *snode,
+ const bNodeLink *link,
+ float vec[4][2])
{
float cursor[2] = {0.0f, 0.0f};
@@ -3591,6 +3591,9 @@ static bool node_link_bezier_handles(const View2D *v2d,
if (link->tosock) {
vec[3][0] = link->tosock->locx;
vec[3][1] = link->tosock->locy;
+ if (!(link->tonode->flag & NODE_HIDDEN) && link->tosock->flag & SOCK_MULTI_INPUT) {
+ node_link_calculate_multi_input_position(link, vec[3]);
+ }
toreroute = (link->tonode && link->tonode->type == NODE_REROUTE);
}
else {
@@ -3902,7 +3905,7 @@ void node_draw_link_bezier(const View2D *v2d,
int th_col3)
{
float vec[4][2];
-
+ const bool highlighted = link->flag & NODE_LINK_TEMP_HIGHLIGHT;
if (node_link_bezier_handles(v2d, snode, link, vec)) {
int drawarrow = ((link->tonode && (link->tonode->type == NODE_REROUTE)) &&
(link->fromnode && (link->fromnode->type == NODE_REROUTE)));
@@ -3911,7 +3914,7 @@ void node_draw_link_bezier(const View2D *v2d,
nodelink_batch_init();
}
- if (g_batch_link.enabled) {
+ if (g_batch_link.enabled && !highlighted) {
/* Add link to batch. */
nodelink_batch_add_link(
snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow);
@@ -3925,6 +3928,12 @@ void node_draw_link_bezier(const View2D *v2d,
UI_GetThemeColor4fv(th_col1, colors[1]);
UI_GetThemeColor4fv(th_col2, colors[2]);
+ if (highlighted) {
+ float link_preselection_highlight_color[4];
+ UI_GetThemeColor4fv(TH_SELECT, link_preselection_highlight_color);
+ copy_v4_v4(colors[2], link_preselection_highlight_color);
+ }
+
GPUBatch *batch = g_batch_link.batch_single;
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODELINK);
GPU_batch_uniform_2fv_array(batch, "bezierPts", 4, vec);
diff --git a/source/blender/editors/space_node/node_draw.c b/source/blender/editors/space_node/node_draw.c
index 4dd945c4cf1..9ef914af75b 100644
--- a/source/blender/editors/space_node/node_draw.c
+++ b/source/blender/editors/space_node/node_draw.c
@@ -22,6 +22,8 @@
* \brief higher level node drawing for the node editor.
*/
+#include "MEM_guardedalloc.h"
+
#include "DNA_light_types.h"
#include "DNA_linestyle_types.h"
#include "DNA_material_types.h"
@@ -500,6 +502,16 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
PointerRNA sockptr;
RNA_pointer_create(&ntree->id, &RNA_NodeSocket, nsock, &sockptr);
+ /* Add the half the height of a multi-input socket to cursor Y
+ * to account for the increased height of the taller sockets. */
+ float multi_input_socket_offset = 0.0f;
+ if (nsock->flag & SOCK_MULTI_INPUT) {
+ if (nsock->total_inputs > 2) {
+ multi_input_socket_offset = (nsock->total_inputs - 2) * NODE_MULTI_INPUT_LINK_GAP;
+ }
+ }
+ dy -= multi_input_socket_offset * 0.5f;
+
uiLayout *layout = UI_block_layout(node->block,
UI_LAYOUT_VERTICAL,
UI_LAYOUT_PANEL,
@@ -533,7 +545,7 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node)
/* place the socket circle in the middle of the layout */
nsock->locy = 0.5f * (dy + buty);
- dy = buty;
+ dy = buty - multi_input_socket_offset * 0.5;
if (nsock->next) {
dy -= NODE_SOCKDY;
}
@@ -751,6 +763,27 @@ static void node_socket_draw(const bNodeSocket *sock,
immVertex2f(pos_id, locx, locy);
}
+static void node_socket_draw_multi_input(const float color[4],
+ const float color_outline[4],
+ const float width,
+ const float height,
+ const int locx,
+ const int locy)
+{
+ const float outline_width = 1.0f;
+ /* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width. */
+ const rctf rect = {
+ .xmin = locx - width + outline_width * 0.5f,
+ .xmax = locx + width - outline_width * 0.5f,
+ .ymin = locy - height + outline_width * 0.5f,
+ .ymax = locy + height - outline_width * 0.5f,
+ };
+
+ UI_draw_roundbox_corner_set(UI_CNR_ALL);
+ UI_draw_roundbox_4fv_ex(
+ &rect, color, NULL, 1.0f, color_outline, outline_width, width - outline_width * 0.5f);
+}
+
static void node_socket_outline_color_get(bool selected, float r_outline_color[4])
{
if (selected) {
@@ -1006,6 +1039,10 @@ void node_draw_sockets(const View2D *v2d,
selected_input_len++;
continue;
}
+ /* Don't draw multi-input sockets here since they are drawn in a different batch. */
+ if (sock->flag & SOCK_MULTI_INPUT) {
+ continue;
+ }
node_socket_draw_nested(C,
ntree,
@@ -1115,6 +1152,28 @@ void node_draw_sockets(const View2D *v2d,
GPU_program_point_size(false);
GPU_blend(GPU_BLEND_NONE);
+
+ /* Draw multi-nput sockets after the others because they are drawn with "UI_roundbox"
+ * rather than with GL_POINT. */
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (nodeSocketIsHidden(socket)) {
+ continue;
+ }
+ if (!(socket->flag & SOCK_MULTI_INPUT)) {
+ continue;
+ }
+
+ const bool is_node_hidden = (node->flag & NODE_HIDDEN);
+ const float width = NODE_SOCKSIZE;
+ float height = is_node_hidden ? width : node_socket_calculate_height(socket) - width;
+
+ float color[4];
+ float outline_color[4];
+ node_socket_color_get((bContext *)C, ntree, &node_ptr, socket, color);
+ node_socket_outline_color_get(selected, outline_color);
+
+ node_socket_draw_multi_input(color, outline_color, width, height, socket->locx, socket->locy);
+ }
}
static void node_draw_basis(const bContext *C,
@@ -1590,17 +1649,67 @@ static void node_update(const bContext *C, bNodeTree *ntree, bNode *node)
}
}
+static void count_mutli_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ LISTBASE_FOREACH (struct bNodeSocket *, socket, &node->inputs) {
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ socket->total_inputs = 0;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tosock == socket) {
+ socket->total_inputs++;
+ }
+ }
+ /* Count temporary links going into this socket. */
+ LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
+ LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
+ bNodeLink *link = (bNodeLink *)linkdata->data;
+ if (link->tosock == socket) {
+ socket->total_inputs++;
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
void node_update_nodetree(const bContext *C, bNodeTree *ntree)
{
/* make sure socket "used" tags are correct, for displaying value buttons */
+ SpaceNode *snode = CTX_wm_space_node(C);
ntreeTagUsedSockets(ntree);
+ count_mutli_input_socket_links(ntree, snode);
+
/* update nodes front to back, so children sizes get updated before parents */
LISTBASE_FOREACH_BACKWARD (bNode *, node, &ntree->nodes) {
node_update(C, ntree, node);
}
}
+static int compare_link_by_angle_to_node(const void *a, const void *b)
+{
+ const bNodeLink *link_a = *(const bNodeLink **)a;
+ const bNodeLink *link_b = *(const bNodeLink **)b;
+
+ BLI_assert(link_a->tosock == link_b->tosock);
+ const float socket_location[2] = {link_a->tosock->locx, link_a->tosock->locy};
+ const float up_direction[2] = {0.0f, 1.0f};
+
+ float delta_a[2] = {link_a->fromsock->locx - socket_location[0],
+ link_a->fromsock->locy - socket_location[1]};
+ normalize_v2(delta_a);
+ const float angle_a = angle_normalized_v2v2(up_direction, delta_a);
+
+ float delta_b[2] = {link_b->fromsock->locx - socket_location[0],
+ link_b->fromsock->locy - socket_location[1]};
+ normalize_v2(delta_b);
+ const float angle_b = angle_normalized_v2v2(up_direction, delta_b);
+
+ return angle_a < angle_b ? 1 : -1;
+}
+
static void node_draw(const bContext *C,
ARegion *region,
SpaceNode *snode,
@@ -1615,6 +1724,46 @@ static void node_draw(const bContext *C,
#define USE_DRAW_TOT_UPDATE
+/**
+ * Automatically sort the input links to multi-input sockets to avoid crossing noodles.
+ */
+static void sort_multi_input_socket_links(bNodeTree *ntree, SpaceNode *snode)
+{
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ /* The total is calculated in #node_update_nodetree, which runs before this draw step. */
+ const int total_inputs = socket->total_inputs;
+ bNodeLink **input_links = MEM_malloc_arrayN(total_inputs, sizeof(bNodeLink *), __func__);
+
+ int index = 0;
+ LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
+ if (link->tosock == socket) {
+ input_links[index] = (bNodeLink *)link;
+ index++;
+ }
+ }
+ LISTBASE_FOREACH (bNodeLinkDrag *, nldrag, &snode->runtime->linkdrag) {
+ LISTBASE_FOREACH (LinkData *, linkdata, &nldrag->links) {
+ bNodeLink *link = (bNodeLink *)linkdata->data;
+ if (link->tosock == socket) {
+ input_links[index] = (bNodeLink *)link;
+ index++;
+ }
+ }
+ }
+
+ qsort(input_links, total_inputs, sizeof(bNodeLink *), compare_link_by_angle_to_node);
+ for (int i = 0; i < total_inputs; i++) {
+ input_links[i]->multi_input_socket_index = i;
+ }
+
+ MEM_freeN(input_links);
+ }
+ }
+ }
+}
+
void node_draw_nodetree(const bContext *C,
ARegion *region,
SpaceNode *snode,
@@ -1650,6 +1799,9 @@ void node_draw_nodetree(const bContext *C,
/* node lines */
GPU_blend(GPU_BLEND_ALPHA);
nodelink_batch_start(snode);
+
+ sort_multi_input_socket_links(ntree, snode);
+
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!nodeLinkIsHidden(link)) {
node_draw_link(&region->v2d, snode, link);
diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c
index c006889f0f7..04787748937 100644
--- a/source/blender/editors/space_node/node_edit.c
+++ b/source/blender/editors/space_node/node_edit.c
@@ -98,6 +98,25 @@ typedef struct CompoJob {
float *progress;
} CompoJob;
+float node_socket_calculate_height(const bNodeSocket *socket)
+{
+ float sock_height = NODE_SOCKSIZE * 2.0f;
+ if (socket->flag & SOCK_MULTI_INPUT) {
+ sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket->total_inputs, NODE_SOCKSIZE);
+ }
+ return sock_height;
+}
+
+void node_link_calculate_multi_input_position(const bNodeLink *link, float r[2])
+{
+ float offset = (link->tosock->total_inputs * NODE_MULTI_INPUT_LINK_GAP -
+ NODE_MULTI_INPUT_LINK_GAP) *
+ 0.5;
+ r[0] = link->tosock->locx - NODE_SOCKSIZE * 0.5f;
+ r[1] = link->tosock->locy - offset +
+ (link->multi_input_socket_index * NODE_MULTI_INPUT_LINK_GAP);
+}
+
static void compo_tag_output_nodes(bNodeTree *nodetree, int recalc_flags)
{
LISTBASE_FOREACH (bNode *, node, &nodetree->nodes) {
@@ -1104,6 +1123,21 @@ void node_set_hidden_sockets(SpaceNode *snode, bNode *node, int set)
}
/* checks snode->mouse position, and returns found node/socket */
+static bool cursor_isect_multi_input_socket(const float cursor[2], const bNodeSocket *socket)
+{
+ const float node_socket_height = node_socket_calculate_height(socket);
+ const rctf multi_socket_rect = {
+ .xmin = socket->locx - NODE_SOCKSIZE * 4,
+ .xmax = socket->locx + NODE_SOCKSIZE,
+ .ymin = socket->locy - node_socket_height * 0.5 - NODE_SOCKSIZE * 2.0f,
+ .ymax = socket->locy + node_socket_height * 0.5 + NODE_SOCKSIZE * 2.0f,
+ };
+ if (BLI_rctf_isect_pt(&multi_socket_rect, cursor[0], cursor[1])) {
+ return true;
+ }
+ return false;
+}
+
/* type is SOCK_IN and/or SOCK_OUT */
int node_find_indicated_socket(
SpaceNode *snode, bNode **nodep, bNodeSocket **sockp, float cursor[2], int in_out)
@@ -1132,7 +1166,16 @@ int node_find_indicated_socket(
if (in_out & SOCK_IN) {
LISTBASE_FOREACH (bNodeSocket *, sock, &node->inputs) {
if (!nodeSocketIsHidden(sock)) {
- if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
+ if (sock->flag & SOCK_MULTI_INPUT && !(node->flag & NODE_HIDDEN)) {
+ if (cursor_isect_multi_input_socket(cursor, sock)) {
+ if (node == visible_node(snode, &rect)) {
+ *nodep = node;
+ *sockp = sock;
+ return 1;
+ }
+ }
+ }
+ else if (BLI_rctf_isect_pt(&rect, sock->locx, sock->locy)) {
if (node == visible_node(snode, &rect)) {
*nodep = node;
*sockp = sock;
diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h
index 51333fd5a09..2f3fa6996af 100644
--- a/source/blender/editors/space_node/node_intern.h
+++ b/source/blender/editors/space_node/node_intern.h
@@ -78,6 +78,9 @@ typedef struct SpaceNode_Runtime {
void space_node_group_offset(struct SpaceNode *snode, float *x, float *y);
/* node_draw.c */
+float node_socket_calculate_height(const bNodeSocket *socket);
+void node_link_calculate_multi_input_position(const bNodeLink *link, float r[2]);
+
int node_get_colorid(struct bNode *node);
int node_get_resize_cursor(int directions);
void node_draw_shadow(const struct SpaceNode *snode,
@@ -178,6 +181,10 @@ bool node_link_bezier_points(const struct View2D *v2d,
const struct bNodeLink *link,
float coord_array[][2],
const int resol);
+bool node_link_bezier_handles(const struct View2D *v2d,
+ const struct SpaceNode *snode,
+ const struct bNodeLink *link,
+ float vec[4][2]);
void draw_nodespace_back_pix(const struct bContext *C,
struct ARegion *region,
struct SpaceNode *snode,
@@ -290,6 +297,7 @@ extern const char *node_context_dir[];
#define NODE_HEIGHT(node) (node->height * UI_DPI_FAC)
#define NODE_MARGIN_X (1.10f * U.widget_unit)
#define NODE_SOCKSIZE (0.25f * U.widget_unit)
+#define NODE_MULTI_INPUT_LINK_GAP (0.25f * U.widget_unit)
#define NODE_RESIZE_MARGIN (0.20f * U.widget_unit)
#define NODE_LINK_RESOL 12
diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c
index f3ce60bbcd9..93accad319f 100644
--- a/source/blender/editors/space_node/node_relationships.c
+++ b/source/blender/editors/space_node/node_relationships.c
@@ -32,6 +32,7 @@
#include "BKE_anim_data.h"
#include "BKE_context.h"
+#include "BKE_curve.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
@@ -179,6 +180,137 @@ typedef struct NodeInsertOfsData {
float offset_x; /* offset to apply to node chain */
} NodeInsertOfsData;
+static void clear_picking_highlight(ListBase *links)
+{
+ LISTBASE_FOREACH (bNodeLink *, link, links) {
+ link->flag &= ~NODE_LINK_TEMP_HIGHLIGHT;
+ }
+}
+
+static LinkData *create_drag_link(Main *bmain, SpaceNode *snode, bNode *node, bNodeSocket *sock)
+{
+ LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
+ bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
+ linkdata->data = oplink;
+ if (sock->in_out == SOCK_OUT) {
+ oplink->fromnode = node;
+ oplink->fromsock = sock;
+ }
+ else {
+ oplink->tonode = node;
+ oplink->tosock = sock;
+ }
+ oplink->flag |= NODE_LINK_VALID;
+ oplink->flag &= ~NODE_LINK_TEST;
+ if (node_connected_to_output(bmain, snode->edittree, node)) {
+ oplink->flag |= NODE_LINK_TEST;
+ }
+ return linkdata;
+}
+
+static void pick_link(const bContext *C,
+ wmOperator *op,
+ bNodeLinkDrag *nldrag,
+ SpaceNode *snode,
+ bNode *node,
+ bNodeLink *link_to_pick)
+{
+ clear_picking_highlight(&snode->edittree->links);
+ RNA_boolean_set(op->ptr, "has_link_picked", true);
+
+ Main *bmain = CTX_data_main(C);
+ LinkData *linkdata = create_drag_link(
+ bmain, snode, link_to_pick->fromnode, link_to_pick->fromsock);
+
+ BLI_addtail(&nldrag->links, linkdata);
+ nodeRemLink(snode->edittree, link_to_pick);
+ /* Send changed event to original link->tonode. */
+ if (node) {
+ snode_update(snode, node);
+ }
+}
+
+static void pick_input_link_by_link_intersect(const bContext *C,
+ wmOperator *op,
+ bNodeLinkDrag *nldrag,
+ const float *cursor)
+{
+ SpaceNode *snode = CTX_wm_space_node(C);
+ const ARegion *region = CTX_wm_region(C);
+ const View2D *v2d = &region->v2d;
+
+ float drag_start[2];
+ RNA_float_get_array(op->ptr, "drag_start", drag_start);
+ bNode *node;
+ bNodeSocket *socket;
+ node_find_indicated_socket(snode, &node, &socket, drag_start, SOCK_IN);
+
+ const float trigger_drag_distance = 25.0f;
+ const float cursor_link_touch_distance = 25.0f;
+
+ const float socket_height = node_socket_calculate_height(socket);
+
+ float cursor_to_socket_relative[2];
+ float socket_position[2] = {socket->locx, socket->locy};
+ sub_v2_v2v2(cursor_to_socket_relative, cursor, socket_position);
+ float distance_from_socket_v2[2] = {
+ max_ff(0, fabs(cursor_to_socket_relative[0]) - NODE_SOCKSIZE * 0.5),
+ max_ff(0, fabs(cursor_to_socket_relative[1]) - socket_height)};
+ const float distance_from_socket = len_v2(distance_from_socket_v2);
+
+ const int resolution = NODE_LINK_RESOL;
+
+ bNodeLink *link_to_pick = NULL;
+ clear_picking_highlight(&snode->edittree->links);
+ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
+ if (link->tosock == socket) {
+ /* Test if the cursor is near a link. */
+ float vec[4][2];
+ node_link_bezier_handles(v2d, snode, link, vec);
+
+ float data[NODE_LINK_RESOL * 2 + 2];
+ BKE_curve_forward_diff_bezier(
+ vec[0][0], vec[1][0], vec[2][0], vec[3][0], data, resolution, sizeof(float[2]));
+ BKE_curve_forward_diff_bezier(
+ vec[0][1], vec[1][1], vec[2][1], vec[3][1], data + 1, resolution, sizeof(float[2]));
+
+ for (int i = 0; i < resolution * 2; i += 2) {
+ float *l1 = &data[i];
+ float *l2 = &data[i + 2];
+ float distance = dist_squared_to_line_segment_v2(cursor, l1, l2);
+ if (distance < cursor_link_touch_distance) {
+ link_to_pick = link;
+ RNA_int_set(op->ptr, "last_picked_link_index", link->multi_input_socket_index);
+ }
+ }
+ }
+ }
+
+ /* If no linked was picked in this call, try using the one picked in the previous call.
+ * Not essential for the basic behavior, but can make interaction feel a bit better if
+ * the mouse moves to the right and loses the "selection." */
+ if (!link_to_pick) {
+ int last_picked_link_index = RNA_int_get(op->ptr, "last_picked_link_index");
+ if (last_picked_link_index > -1) {
+ LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
+ if (link->multi_input_socket_index == last_picked_link_index) {
+ link_to_pick = link;
+ }
+ }
+ }
+ }
+
+ if (link_to_pick) {
+ /* Highlight is set here and cleared in the next iteration or if the operation finishes. */
+ link_to_pick->flag |= NODE_LINK_TEMP_HIGHLIGHT;
+ ED_area_tag_redraw(CTX_wm_area(C));
+
+ if (distance_from_socket > trigger_drag_distance) {
+ pick_link(C, op, nldrag, snode, node, link_to_pick);
+ }
+ }
+}
+
static int sort_nodes_locx(const void *a, const void *b)
{
const bNodeListItem *nli1 = a;
@@ -600,6 +732,13 @@ static void node_remove_extra_links(SpaceNode *snode, bNodeLink *link)
tlink = NULL;
to_count--;
}
+ /* Also remove link if it comes from the same output. */
+ if (tlink->fromsock == from) {
+ nodeRemLink(ntree, tlink);
+ tlink = NULL;
+ to_count--;
+ from_count--;
+ }
}
}
}
@@ -748,10 +887,15 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
switch (event->type) {
case MOUSEMOVE:
- node_link_find_socket(C, op, cursor);
+ if (nldrag->from_multi_input_socket && !RNA_boolean_get(op->ptr, "has_link_picked")) {
+ pick_input_link_by_link_intersect(C, op, nldrag, cursor);
+ }
+ else {
+ node_link_find_socket(C, op, cursor);
- node_link_update_header(C, nldrag);
- ED_region_tag_redraw(region);
+ node_link_update_header(C, nldrag);
+ ED_region_tag_redraw(region);
+ }
break;
case LEFTMOUSE:
@@ -762,6 +906,8 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
ED_workspace_status_text(C, NULL);
ED_region_tag_redraw(region);
+ SpaceNode *snode = CTX_wm_space_node(C);
+ clear_picking_highlight(&snode->edittree->links);
return OPERATOR_FINISHED;
}
break;
@@ -817,16 +963,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* dragged links are fixed on output side */
nldrag->in_out = SOCK_OUT;
/* create a new link */
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- linkdata->data = oplink;
- oplink->fromnode = node;
- oplink->fromsock = sock;
- oplink->flag |= NODE_LINK_VALID;
- oplink->flag &= ~NODE_LINK_TEST;
- if (node_connected_to_output(bmain, snode->edittree, node)) {
- oplink->flag |= NODE_LINK_TEST;
- }
+ LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
BLI_addtail(&nldrag->links, linkdata);
}
@@ -875,16 +1012,7 @@ static bNodeLinkDrag *node_link_init(Main *bmain, SpaceNode *snode, float cursor
/* dragged links are fixed on input side */
nldrag->in_out = SOCK_IN;
/* create a new link */
- LinkData *linkdata = MEM_callocN(sizeof(LinkData), "drag link op link data");
- bNodeLink *oplink = MEM_callocN(sizeof(bNodeLink), "drag link op link");
- linkdata->data = oplink;
- oplink->tonode = node;
- oplink->tosock = sock;
- oplink->flag |= NODE_LINK_VALID;
- oplink->flag &= ~NODE_LINK_TEST;
- if (node_connected_to_output(bmain, snode->edittree, node)) {
- oplink->flag |= NODE_LINK_TEST;
- }
+ LinkData *linkdata = create_drag_link(bmain, snode, node, sock);
BLI_addtail(&nldrag->links, linkdata);
}
@@ -904,6 +1032,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
float cursor[2];
UI_view2d_region_to_view(&region->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]);
RNA_float_set_array(op->ptr, "drag_start", cursor);
+ RNA_int_set(op->ptr, "last_picked_link_index", -1);
RNA_boolean_set(op->ptr, "has_link_picked", false);
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@@ -931,6 +1060,7 @@ static void node_link_cancel(bContext *C, wmOperator *op)
BLI_freelistN(&nldrag->links);
MEM_freeN(nldrag);
+ clear_picking_highlight(&snode->edittree->links);
}
void NODE_OT_link(wmOperatorType *ot)
@@ -972,6 +1102,16 @@ void NODE_OT_link(wmOperatorType *ot)
-UI_PRECISION_FLOAT_MAX,
UI_PRECISION_FLOAT_MAX);
RNA_def_property_flag(prop, PROP_HIDDEN);
+ RNA_def_int(ot->srna,
+ "last_picked_link_index",
+ -1,
+ -1,
+ 4095,
+ "Last Picked Link Index",
+ "The index of the last picked link on a multi-input socket",
+ -1,
+ 4095);
+ RNA_def_property_flag(prop, PROP_HIDDEN);
}
/* ********************** Make Link operator ***************** */
diff --git a/source/blender/makesdna/DNA_node_types.h b/source/blender/makesdna/DNA_node_types.h
index c5d2384b1aa..43c5fa81651 100644
--- a/source/blender/makesdna/DNA_node_types.h
+++ b/source/blender/makesdna/DNA_node_types.h
@@ -118,7 +118,9 @@ typedef struct bNodeSocket {
/* XXX deprecated, kept for forward compatibility */
short stack_type DNA_DEPRECATED;
char display_shape;
- char _pad[3];
+ char _pad[1];
+ /* Runtime-only cache of the number of input links, for multi-input sockets. */
+ short total_inputs;
/** Custom dynamic defined label, MAX_NAME. */
char label[64];
@@ -391,13 +393,15 @@ typedef struct bNodeLink {
bNodeSocket *fromsock, *tosock;
int flag;
- char _pad[4];
+ /* A runtime storage for automatically sorted links to multi-input sockets. */
+ int multi_input_socket_index;
} bNodeLink;
/* link->flag */
#define NODE_LINKFLAG_HILITE (1 << 0) /* link has been successfully validated */
#define NODE_LINK_VALID (1 << 1)
-#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
+#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
+#define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */
/* tree->edit_quality/tree->render_quality */
#define NTREE_QUALITY_HIGH 0