Nodes: Add support to mute node wires

This patch adds the ability to mute individual wires in the node editor.
This is invoked like the cut links operator but with a new shortcut.

Mute = Ctrl + Alt
Cut = Ctrl

Dragging over wires will toggle the mute state for that wire.
The muted wires are drawn in red with a bar across the center.
Red is used in the nodes context to indicate invalid links, muted links and internal links.

When a wire is muted it exposes the original node buttons which are normally hidden when a wire is connected.

Downstream and upstream links connected using reroute nodes are also muted.

Outside scope of patch:
- Add support for pynodes e.g. Animation Nodes
- Requires minor change to check for muted links using the `is_muted` link property or the `is_linked` socket property.

Maniphest Tasks: T52659

Differential Revision: https://developer.blender.org/D2807
This commit is contained in:
Charlie Jolly 2021-03-16 19:11:54 +00:00 committed by Charlie Jolly
parent 20bf736ff8
commit 266cd7bb82
Notes: blender-bot 2023-02-14 09:29:42 +01:00
Referenced by commit d4a36c7ad5, Fix T88234: Crash with Eevee when shader displacement socket connection is muted
Referenced by issue #52659, Mute node links (design task)
19 changed files with 324 additions and 22 deletions

View File

@ -1234,7 +1234,8 @@ static void add_nodes(Scene *scene,
for (BL::NodeLink &b_link : b_ntree.links) {
/* Ignore invalid links to avoid unwanted cycles created in graph.
* Also ignore links with unavailable sockets. */
if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled())) {
if (!(b_link.is_valid() && b_link.from_socket().enabled() && b_link.to_socket().enabled()) ||
b_link.is_muted()) {
continue;
}
/* get blender link data */

View File

@ -1844,6 +1844,7 @@ def km_node_editor(params):
("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("node.add_reroute", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "shift": True}, None),
("node.links_cut", {"type": 'EVT_TWEAK_L' if params.legacy else 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True}, None),
("node.links_mute", {"type": 'EVT_TWEAK_R', "value": 'ANY', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_move", {"type": 'MIDDLEMOUSE', "value": 'PRESS', "alt": True}, None),
("node.backimage_zoom", {"type": 'V', "value": 'PRESS', "repeat": True},

View File

@ -1113,6 +1113,7 @@ def km_node_editor(params):
("node.resize", {"type": 'EVT_TWEAK_L', "value": 'ANY'}, None),
("node.add_reroute", {"type": params.action_tweak, "value": 'ANY', "shift": True}, None),
("node.links_cut", {"type": params.action_tweak, "value": 'ANY', "ctrl": True}, None),
("node.links_mute", {"type": params.action_tweak, "value": 'ANY', "ctrl": True, "alt": True}, None),
("node.select_link_viewer", {"type": 'LEFTMOUSE', "value": 'PRESS', "shift": True, "ctrl": True}, None),
("node.backimage_fit", {"type": 'A', "value": 'PRESS', "alt": True}, None),
("node.backimage_sample", {"type": 'LEFTMOUSE', "value": 'PRESS', "alt": True}, None),

View File

@ -331,6 +331,7 @@ class NODE_MT_node(Menu):
layout.operator("node.link_make", text="Make and Replace Links").replace = True
layout.operator("node.links_cut")
layout.operator("node.links_detach")
layout.operator("node.links_mute")
layout.separator()

View File

@ -627,6 +627,7 @@ struct bNodeLink *nodeAddLink(struct bNodeTree *ntree,
struct bNodeSocket *tosock);
void nodeRemLink(struct bNodeTree *ntree, struct bNodeLink *link);
void nodeRemSocketLinks(struct bNodeTree *ntree, struct bNodeSocket *sock);
void nodeMuteLinkToggle(struct bNodeTree *ntree, struct bNodeLink *link);
bool nodeLinkIsHidden(const struct bNodeLink *link);
void nodeInternalRelink(struct bNodeTree *ntree, struct bNode *node);

View File

@ -107,6 +107,9 @@ static void node_free_node(bNodeTree *ntree, bNode *node);
static void node_socket_interface_free(bNodeTree *UNUSED(ntree),
bNodeSocket *sock,
const bool do_id_user);
static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
struct bNode *node,
const bool mute);
static void ntree_init_data(ID *id)
{
@ -2215,6 +2218,106 @@ void nodeRemLink(bNodeTree *ntree, bNodeLink *link)
}
}
/* Check if all output links are muted or not. */
static bool nodeMuteFromSocketLinks(const bNodeTree *ntree, const bNodeSocket *sock)
{
int tot = 0;
int muted = 0;
LISTBASE_FOREACH (const bNodeLink *, link, &ntree->links) {
if (link->fromsock == sock) {
tot++;
if (link->flag & NODE_LINK_MUTED) {
muted++;
}
}
}
return tot == muted;
}
static void nodeMuteLink(bNodeLink *link)
{
link->flag |= NODE_LINK_MUTED;
link->flag |= NODE_LINK_TEST;
if (!(link->tosock->flag & SOCK_MULTI_INPUT)) {
link->tosock->flag &= ~SOCK_IN_USE;
}
}
static void nodeUnMuteLink(bNodeLink *link)
{
link->flag &= ~NODE_LINK_MUTED;
link->flag |= NODE_LINK_TEST;
link->tosock->flag |= SOCK_IN_USE;
}
/* Upstream muting. Always happens when unmuting but checks when muting. O(n^2) algorithm.*/
static void nodeMuteRerouteInputLinks(bNodeTree *ntree, bNode *node, const bool mute)
{
if (node->type != NODE_REROUTE) {
return;
}
if (!mute || nodeMuteFromSocketLinks(ntree, (bNodeSocket *)node->outputs.first)) {
bNodeSocket *sock = (bNodeSocket *)node->inputs.first;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!(link->flag & NODE_LINK_VALID) || (link->tosock != sock)) {
continue;
}
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
}
}
}
/* Downstream muting propagates when reaching reroute nodes. O(n^2) algorithm.*/
static void nodeMuteRerouteOutputLinks(bNodeTree *ntree, bNode *node, const bool mute)
{
if (node->type != NODE_REROUTE) {
return;
}
bNodeSocket *sock;
sock = (bNodeSocket *)node->outputs.first;
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
if (!(link->flag & NODE_LINK_VALID) || (link->fromsock != sock)) {
continue;
}
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
}
}
void nodeMuteLinkToggle(bNodeTree *ntree, bNodeLink *link)
{
if (link->tosock) {
bool mute = !(link->flag & NODE_LINK_MUTED);
if (mute) {
nodeMuteLink(link);
}
else {
nodeUnMuteLink(link);
}
if (link->tonode->type == NODE_REROUTE) {
nodeMuteRerouteOutputLinks(ntree, link->tonode, mute);
}
if (link->fromnode->type == NODE_REROUTE) {
nodeMuteRerouteInputLinks(ntree, link->fromnode, mute);
}
}
if (ntree) {
ntree->update |= NTREE_UPDATE_LINKS;
}
}
void nodeRemSocketLinks(bNodeTree *ntree, bNodeSocket *sock)
{
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &ntree->links) {
@ -2257,6 +2360,10 @@ void nodeInternalRelink(bNodeTree *ntree, bNode *node)
link->flag &= ~NODE_LINK_VALID;
}
if (fromlink->flag & NODE_LINK_MUTED) {
link->flag |= NODE_LINK_MUTED;
}
ntree->update |= NTREE_UPDATE_LINKS;
}
else {
@ -4014,7 +4121,9 @@ void ntreeTagUsedSockets(bNodeTree *ntree)
LISTBASE_FOREACH (bNodeLink *, link, &ntree->links) {
link->fromsock->flag |= SOCK_IN_USE;
link->tosock->flag |= SOCK_IN_USE;
if (!(link->flag & NODE_LINK_MUTED)) {
link->tosock->flag |= SOCK_IN_USE;
}
}
}

View File

@ -188,7 +188,8 @@ void NodeGraph::add_bNodeLink(const NodeRange &node_range, bNodeLink *b_nodelink
if (!(b_nodelink->flag & NODE_LINK_VALID)) {
return;
}
if ((b_nodelink->fromsock->flag & SOCK_UNAVAIL) || (b_nodelink->tosock->flag & SOCK_UNAVAIL)) {
if ((b_nodelink->fromsock->flag & SOCK_UNAVAIL) || (b_nodelink->tosock->flag & SOCK_UNAVAIL) ||
(b_nodelink->flag & NODE_LINK_MUTED)) {
return;
}

View File

@ -3751,17 +3751,21 @@ bool node_link_bezier_points(const View2D *v2d,
#define LINK_WIDTH (2.5f * UI_DPI_FAC)
#define ARROW_SIZE (7 * UI_DPI_FAC)
/* Reroute arrow shape and mute bar. These are expanded here and shrunk in the glsl code.
* See: gpu_shader_2D_nodelink_vert.glsl */
static float arrow_verts[3][2] = {{-1.0f, 1.0f}, {0.0f, 0.0f}, {-1.0f, -1.0f}};
static float arrow_expand_axis[3][2] = {{0.7071f, 0.7071f}, {M_SQRT2, 0.0f}, {0.7071f, -0.7071f}};
static float mute_verts[3][2] = {{0.7071f, 1.0f}, {0.7071f, 0.0f}, {0.7071f, -1.0f}};
static float mute_expand_axis[3][2] = {{1.0f, 0.0f}, {1.0f, 0.0f}, {1.0f, -0.0f}};
static struct {
GPUBatch *batch; /* for batching line together */
GPUBatch *batch_single; /* for single line */
GPUVertBuf *inst_vbo;
uint p0_id, p1_id, p2_id, p3_id;
uint colid_id;
uint colid_id, muted_id;
GPUVertBufRaw p0_step, p1_step, p2_step, p3_step;
GPUVertBufRaw colid_step;
GPUVertBufRaw colid_step, muted_step;
uint count;
bool enabled;
} g_batch_link = {0};
@ -3774,6 +3778,8 @@ static void nodelink_batch_reset(void)
GPU_vertbuf_attr_get_raw_data(g_batch_link.inst_vbo, g_batch_link.p3_id, &g_batch_link.p3_step);
GPU_vertbuf_attr_get_raw_data(
g_batch_link.inst_vbo, g_batch_link.colid_id, &g_batch_link.colid_step);
GPU_vertbuf_attr_get_raw_data(
g_batch_link.inst_vbo, g_batch_link.muted_id, &g_batch_link.muted_step);
g_batch_link.count = 0;
}
@ -3801,6 +3807,8 @@ static void nodelink_batch_init(void)
int vcount = LINK_RESOL * 2; /* curve */
vcount += 2; /* restart strip */
vcount += 3 * 2; /* arrow */
vcount += 2; /* restart strip */
vcount += 3 * 2; /* mute */
vcount *= 2; /* shadow */
vcount += 2; /* restart strip */
GPU_vertbuf_data_alloc(vbo, vcount);
@ -3843,6 +3851,25 @@ static void nodelink_batch_init(void)
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
}
/* restart */
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
uv[0] = 127;
uv[1] = 0;
copy_v2_v2(pos, mute_verts[0]);
copy_v2_v2(exp, mute_expand_axis[0]);
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
/* bar */
for (int i = 0; i < 3; ++i) {
uv[1] = 0;
copy_v2_v2(pos, mute_verts[i]);
copy_v2_v2(exp, mute_expand_axis[i]);
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
uv[1] = 255;
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
}
/* restart */
if (k == 0) {
set_nodelink_vertex(vbo, uv_id, pos_id, expand_id, v++, uv, pos, exp);
@ -3867,6 +3894,8 @@ static void nodelink_batch_init(void)
&format_inst, "P3", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
g_batch_link.colid_id = GPU_vertformat_attr_add(
&format_inst, "colid_doarrow", GPU_COMP_U8, 4, GPU_FETCH_INT);
g_batch_link.muted_id = GPU_vertformat_attr_add(
&format_inst, "domuted", GPU_COMP_U8, 2, GPU_FETCH_INT);
g_batch_link.inst_vbo = GPU_vertbuf_create_with_format_ex(&format_inst, GPU_USAGE_STREAM);
/* Alloc max count but only draw the range we need. */
GPU_vertbuf_data_alloc(g_batch_link.inst_vbo, NODELINK_GROUP_SIZE);
@ -3941,12 +3970,13 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
int th_col1,
int th_col2,
int th_col3,
bool drawarrow)
bool drawarrow,
bool drawmuted)
{
/* Only allow these colors. If more is needed, you need to modify the shader accordingly. */
BLI_assert(ELEM(th_col1, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
BLI_assert(ELEM(th_col2, TH_WIRE_INNER, TH_WIRE, TH_ACTIVE, TH_EDGE_SELECT, TH_REDALERT));
BLI_assert(ELEM(th_col3, TH_WIRE, -1));
BLI_assert(ELEM(th_col3, TH_WIRE, TH_REDALERT, -1));
g_batch_link.count++;
copy_v2_v2(GPU_vertbuf_raw_step(&g_batch_link.p0_step), p0);
@ -3958,6 +3988,8 @@ static void nodelink_batch_add_link(const SpaceNode *snode,
colid[1] = nodelink_get_color_id(th_col2);
colid[2] = nodelink_get_color_id(th_col3);
colid[3] = drawarrow;
char *muted = GPU_vertbuf_raw_step(&g_batch_link.muted_step);
muted[0] = drawmuted;
if (g_batch_link.count == NODELINK_GROUP_SIZE) {
nodelink_batch_draw(snode);
@ -3977,7 +4009,7 @@ void node_draw_link_bezier(const View2D *v2d,
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)));
int drawmuted = (link->flag & NODE_LINK_MUTED);
if (g_batch_link.batch == NULL) {
nodelink_batch_init();
}
@ -3985,7 +4017,7 @@ void node_draw_link_bezier(const View2D *v2d,
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);
snode, vec[0], vec[1], vec[2], vec[3], th_col1, th_col2, th_col3, drawarrow, drawmuted);
}
else {
/* Draw single link. */
@ -4009,6 +4041,7 @@ void node_draw_link_bezier(const View2D *v2d,
GPU_batch_uniform_1f(batch, "expandSize", snode->runtime->aspect * LINK_WIDTH);
GPU_batch_uniform_1f(batch, "arrowSize", ARROW_SIZE);
GPU_batch_uniform_1i(batch, "doArrow", drawarrow);
GPU_batch_uniform_1i(batch, "doMuted", drawmuted);
GPU_batch_draw(batch);
}
}
@ -4041,8 +4074,11 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
if (link->flag & NODE_LINKFLAG_HILITE) {
th_col1 = th_col2 = TH_ACTIVE;
}
else if (link->flag & NODE_LINK_MUTED) {
th_col1 = th_col2 = TH_REDALERT;
}
else {
/* regular link */
/* Regular link, highlight if connected to selected node. */
if (link->fromnode && link->fromnode->flag & SELECT) {
th_col1 = TH_EDGE_SELECT;
}
@ -4052,7 +4088,8 @@ void node_draw_link(View2D *v2d, SpaceNode *snode, bNodeLink *link)
}
}
else {
th_col1 = th_col2 = TH_REDALERT;
/* Invalid link. */
th_col1 = th_col2 = th_col3 = TH_REDALERT;
// th_col3 = -1; /* no shadow */
}
}

View File

@ -234,6 +234,7 @@ void NODE_OT_link(struct wmOperatorType *ot);
void NODE_OT_link_make(struct wmOperatorType *ot);
void NODE_OT_links_cut(struct wmOperatorType *ot);
void NODE_OT_links_detach(struct wmOperatorType *ot);
void NODE_OT_links_mute(struct wmOperatorType *ot);
void NODE_OT_parent_set(struct wmOperatorType *ot);
void NODE_OT_join(struct wmOperatorType *ot);

View File

@ -68,6 +68,7 @@ void node_operatortypes(void)
WM_operatortype_append(NODE_OT_link_make);
WM_operatortype_append(NODE_OT_links_cut);
WM_operatortype_append(NODE_OT_links_detach);
WM_operatortype_append(NODE_OT_links_mute);
WM_operatortype_append(NODE_OT_add_reroute);
WM_operatortype_append(NODE_OT_group_make);

View File

@ -1227,8 +1227,8 @@ void NODE_OT_link_make(wmOperatorType *ot)
ot->srna, "replace", 0, "Replace", "Replace socket connections with the new links");
}
/* ********************** Cut Link operator ***************** */
static bool cut_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
/* ********************** Node Link Intersect ***************** */
static bool node_links_intersect(bNodeLink *link, const float mcoords[][2], int tot)
{
float coord_array[NODE_LINK_RESOL + 1][2];
@ -1244,6 +1244,7 @@ static bool cut_links_intersect(bNodeLink *link, const float mcoords[][2], int t
return 0;
}
/* ********************** Cut Link operator ***************** */
static int cut_links_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
@ -1276,7 +1277,7 @@ static int cut_links_exec(bContext *C, wmOperator *op)
continue;
}
if (cut_links_intersect(link, mcoords, i)) {
if (node_links_intersect(link, mcoords, i)) {
if (found == false) {
/* TODO(sergey): Why did we kill jobs twice? */
@ -1335,6 +1336,110 @@ void NODE_OT_links_cut(wmOperatorType *ot)
RNA_def_int(ot->srna, "cursor", WM_CURSOR_KNIFE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
/* ********************** Mute links operator ***************** */
static int mute_links_exec(bContext *C, wmOperator *op)
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
ARegion *region = CTX_wm_region(C);
bool do_tag_update = false;
int i = 0;
float mcoords[256][2];
RNA_BEGIN (op->ptr, itemptr, "path") {
float loc[2];
RNA_float_get_array(&itemptr, "loc", loc);
UI_view2d_region_to_view(
&region->v2d, (int)loc[0], (int)loc[1], &mcoords[i][0], &mcoords[i][1]);
i++;
if (i >= 256) {
break;
}
}
RNA_END;
if (i > 1) {
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
/* Count intersected links and clear test flag. */
int tot = 0;
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (nodeLinkIsHidden(link)) {
continue;
}
link->flag &= ~NODE_LINK_TEST;
if (node_links_intersect(link, mcoords, i)) {
tot++;
}
}
if (tot == 0) {
return OPERATOR_CANCELLED;
}
/* Mute links. */
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (nodeLinkIsHidden(link) || (link->flag & NODE_LINK_TEST)) {
continue;
}
if (node_links_intersect(link, mcoords, i)) {
do_tag_update |= (do_tag_update ||
node_connected_to_output(bmain, snode->edittree, link->tonode));
snode_update(snode, link->tonode);
nodeMuteLinkToggle(snode->edittree, link);
}
}
/* Clear remaining test flags. */
LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) {
if (nodeLinkIsHidden(link)) {
continue;
}
link->flag &= ~NODE_LINK_TEST;
}
do_tag_update |= ED_node_is_geometry(snode);
ntreeUpdateTree(CTX_data_main(C), snode->edittree);
snode_notify(C, snode);
if (do_tag_update) {
snode_dag_update(C, snode);
}
return OPERATOR_FINISHED;
}
return OPERATOR_CANCELLED | OPERATOR_PASS_THROUGH;
}
void NODE_OT_links_mute(wmOperatorType *ot)
{
ot->name = "Mute Links";
ot->idname = "NODE_OT_links_mute";
ot->description = "Use the mouse to mute links";
ot->invoke = WM_gesture_lines_invoke;
ot->modal = WM_gesture_lines_modal;
ot->exec = mute_links_exec;
ot->cancel = WM_gesture_lines_cancel;
ot->poll = ED_operator_node_editable;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
/* properties */
PropertyRNA *prop;
prop = RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
/* internal */
RNA_def_int(ot->srna, "cursor", WM_CURSOR_MUTE, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
}
/* ********************** Detach links operator ***************** */
static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))

View File

@ -2,7 +2,7 @@
* 2D Quadratic Bezier thick line drawing
*/
#define MID_VERTEX 57
#define MID_VERTEX 65
/* u is position along the curve, defining the tangent space.
* v is "signed" distance (compressed to [0..1] range) from the pos in expand direction */
@ -17,6 +17,7 @@ in vec2 P1;
in vec2 P2;
in vec2 P3;
in ivec4 colid_doarrow;
in ivec2 domuted;
uniform vec4 colors[6];
@ -24,6 +25,7 @@ uniform vec4 colors[6];
# define colEnd colors[colid_doarrow[1]]
# define colShadow colors[colid_doarrow[2]]
# define doArrow (colid_doarrow[3] != 0)
# define doMuted (domuted[0] != 0)
#else
/* Single curve drawcall, use uniform. */
@ -36,6 +38,7 @@ uniform vec2 bezierPts[4];
uniform vec4 colors[3];
uniform bool doArrow;
uniform bool doMuted;
# define colShadow colors[0]
# define colStart colors[1]
@ -90,13 +93,18 @@ void main(void)
/* Second pass */
finalColor = mix(colStart, colEnd, uv.x);
expand_dist *= 0.5;
if (doMuted) {
finalColor[3] = 0.65;
}
}
/* Expand into a line */
gl_Position.xy += exp_axis * expandSize * expand_dist;
/* if arrow */
if (expand.y != 1.0 && !doArrow) {
/* If the link is not muted or is not a reroute arrow the points are squashed to the center of
* the line. Magic numbers are defined in drawnode.c */
if ((expand.x == 1.0 && !doMuted) ||
(expand.y != 1.0 && (pos.x < 0.70 || pos.x > 0.71) && !doArrow)) {
gl_Position.xy *= 0.0;
}
}

View File

@ -402,6 +402,7 @@ typedef struct bNodeLink {
#define NODE_LINK_VALID (1 << 1)
#define NODE_LINK_TEST (1 << 2) /* free test flag, undefined */
#define NODE_LINK_TEMP_HIGHLIGHT (1 << 3) /* Link is highlighted for picking. */
#define NODE_LINK_MUTED (1 << 4) /* Link is muted. */
/* tree->edit_quality/tree->render_quality */
#define NTREE_QUALITY_HIGH 0

View File

@ -10872,6 +10872,11 @@ static void rna_def_node_link(BlenderRNA *brna)
RNA_def_struct_ui_text(srna, "Valid", "Link is valid");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, NULL);
prop = RNA_def_property(srna, "is_muted", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "flag", NODE_LINK_MUTED);
RNA_def_struct_ui_text(srna, "Muted", "Link is muted and can be ignored");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, NULL);
prop = RNA_def_property(srna, "from_node", PROP_POINTER, PROP_NONE);
RNA_def_property_pointer_sdna(prop, NULL, "fromnode");
RNA_def_property_struct_type(prop, "Node");

View File

@ -71,7 +71,8 @@ void node_get_stack(bNode *node, bNodeStack *stack, bNodeStack **in, bNodeStack
static void node_init_input_index(bNodeSocket *sock, int *index)
{
/* Only consider existing link if from socket is valid! */
if (sock->link && sock->link->fromsock && sock->link->fromsock->stack_index >= 0) {
if (sock->link && !(sock->link->flag & NODE_LINK_MUTED) && sock->link->fromsock &&
sock->link->fromsock->stack_index >= 0) {
sock->stack_index = sock->link->fromsock->stack_index;
}
else {
@ -131,7 +132,7 @@ static struct bNodeStack *setup_stack(bNodeStack *stack,
}
/* don't mess with remote socket stacks, these are initialized by other nodes! */
if (sock->link) {
if (sock->link && !(sock->link->flag & NODE_LINK_MUTED)) {
return ns;
}

View File

@ -77,6 +77,9 @@ NodeTreeRef::NodeTreeRef(bNodeTree *btree) : btree_(btree)
}
LISTBASE_FOREACH (bNodeLink *, blink, &btree->links) {
if (blink->flag & NODE_LINK_MUTED) {
continue;
}
OutputSocketRef &from_socket = this->find_output_socket(
node_mapping, blink->fromnode, blink->fromsock);
InputSocketRef &to_socket = this->find_input_socket(

View File

@ -388,7 +388,7 @@ static void ntree_shader_groups_expand_inputs(bNodeTree *localtree)
if (is_group || is_group_output) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->link != NULL) {
if (socket->link != NULL && !(socket->link->flag & NODE_LINK_MUTED)) {
bNodeLink *link = socket->link;
/* Fix the case where the socket is actually converting the data. (see T71374)
* We only do the case of lossy conversion to float.*/
@ -557,7 +557,7 @@ static bool ntree_shader_has_displacement(bNodeTree *ntree,
/* Non-cycles node is used as an output. */
return false;
}
if (displacement->link != NULL) {
if ((displacement->link != NULL) && !(displacement->link->flag & NODE_LINK_MUTED)) {
*r_node = displacement->link->fromnode;
*r_socket = displacement->link->fromsock;
*r_link = displacement->link;

View File

@ -520,13 +520,37 @@ void wm_init_cursor_data(void)
BlenderCursor[WM_CURSOR_WAIT] = &WaitCursor;
END_CURSOR_BLOCK;
/********************** Mute Cursor ***********************/
BEGIN_CURSOR_BLOCK;
static char mute_bitmap[] = {
0x00, 0x00, 0x22, 0x00, 0x14, 0x00, 0x08, 0x03, 0x14, 0x03, 0x22,
0x03, 0x00, 0x03, 0x00, 0x03, 0xf8, 0x7c, 0xf8, 0x7c, 0x00, 0x03,
0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x00,
};
static char mute_mask[] = {
0x63, 0x00, 0x77, 0x00, 0x3e, 0x03, 0x1c, 0x03, 0x3e, 0x03, 0x77,
0x03, 0x63, 0x03, 0x80, 0x07, 0xfc, 0xfc, 0xfc, 0xfc, 0x80, 0x07,
0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03, 0x00, 0x03,
};
static BCursor MuteCursor = {
mute_bitmap,
mute_mask,
9,
8,
true,
};
BlenderCursor[WM_CURSOR_MUTE] = &MuteCursor;
END_CURSOR_BLOCK;
/****************** Normal Cross Cursor ************************/
BEGIN_CURSOR_BLOCK;
static char cross_bitmap[] = {
0x00, 0x00, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80,
0x01, 0x00, 0x00, 0x3e, 0x7c, 0x3e, 0x7c, 0x00, 0x00, 0x80, 0x01,
0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x80, 0x01, 0x00, 0x00,
};
static char cross_mask[] = {

View File

@ -72,6 +72,7 @@ typedef enum WMCursorType {
WM_CURSOR_ZOOM_OUT,
WM_CURSOR_NONE,
WM_CURSOR_MUTE,
/* --- ALWAYS LAST ----- */
WM_CURSOR_NUM,