diff options
-rw-r--r-- | source/blender/blenkernel/intern/node.cc | 1 | ||||
-rw-r--r-- | source/blender/editors/space_node/drawnode.c | 21 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_draw.c | 154 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_edit.c | 45 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_intern.h | 8 | ||||
-rw-r--r-- | source/blender/editors/space_node/node_relationships.c | 186 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_node_types.h | 10 |
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(®ion->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 = ®ion->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(®ion->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 |