Node Editor: Add overlay to automatically label reroute nodes #113368

Open
Leon Schittek wants to merge 25 commits from lone_noel/blender:node-editor-reroute-label-propagation into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
9 changed files with 147 additions and 23 deletions

View File

@ -862,6 +862,7 @@ class NODE_PT_overlay(Panel):
col = layout.column()
col.prop(overlay, "show_wire_color", text="Wire Colors")
col.prop(overlay, "show_reroute_auto_labels", text="Reroute Auto Labels")
col.separator()

View File

@ -29,7 +29,7 @@ extern "C" {
/* Blender file format version. */
#define BLENDER_FILE_VERSION BLENDER_VERSION
#define BLENDER_FILE_SUBVERSION 33
#define BLENDER_FILE_SUBVERSION 34
/* Minimum Blender version that supports reading file written with the current
* version. Older Blender versions will test this and cancel loading the file, showing a warning to

View File

@ -1679,6 +1679,8 @@ void node_type_size_preset(bNodeType *ntype, eNodeSizePreset size);
/** \name Node Generic Functions
* \{ */
void ntree_update_auto_labels(bNodeTree &ntree);
bool node_is_connected_to_output(const bNodeTree *ntree, const bNode *node);
bNodeSocket *node_find_enabled_socket(bNode &node, eNodeSocketInOut in_out, StringRef name);

View File

@ -3479,6 +3479,19 @@ void blo_do_versions_400(FileData *fd, Library * /*lib*/, Main *bmain)
}
}
if (!MAIN_VERSION_FILE_ATLEAST(bmain, 402, 34)) {
LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) {
LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) {
LISTBASE_FOREACH (SpaceLink *, sl, &area->spacedata) {
if (sl->spacetype == SPACE_NODE) {
SpaceNode *snode = (SpaceNode *)sl;
snode->overlay.flag |= SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS;
}
}
}
}
}
/**
* Always bump subversion in BKE_blender_version.h when adding versioning
* code here, and wrap it inside a MAIN_VERSION_FILE_ATLEAST check.

View File

@ -2046,6 +2046,15 @@ void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4],
GPU_blend(state);
}
/* Some elements of the node UI are hidden, when they get too small. */
#define NODE_TREE_SCALE_SMALL 0.2f
/* The node tree scales both with the view and with the UI. */
static float node_tree_view_scale(const SpaceNode &snode)
{
return (1.0f / snode.runtime->aspect) * UI_SCALE_FAC;
}
static void node_draw_preview_background(rctf *rect)
{
GPUVertFormat *format = immVertexFormat();
@ -3568,11 +3577,8 @@ static void node_draw_basis(const bContext &C,
UI_draw_roundbox_4fv(&rect, false, BASIS_RAD + outline_width, color_outline);
}
float scale;
UI_view2d_scale_get(&v2d, &scale, nullptr);
/* Skip slow socket drawing if zoom is small. */
if (scale > 0.2f) {
if (node_tree_view_scale(snode) > NODE_TREE_SCALE_SMALL) {
node_draw_sockets(v2d, C, ntree, node, block, true, false);
}
@ -4094,8 +4100,45 @@ static void frame_node_draw(const bContext &C,
UI_block_draw(&C, &block);
}
static void reroute_node_draw(
const bContext &C, ARegion &region, bNodeTree &ntree, const bNode &node, uiBlock &block)
static void reroute_node_draw_label(const SpaceNode &snode, const bNode &node, uiBlock &block)
{
const bool has_label = node.label[0] != '\0';
const bool use_auto_label = !has_label && (snode.overlay.flag & SN_OVERLAY_SHOW_OVERLAYS) &&
(snode.overlay.flag & SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS);
if (!has_label && !use_auto_label) {
return;
}
/* Don't show the automatic label, when being zoomed out. */
if (!has_label && node_tree_view_scale(snode) < NODE_TREE_SCALE_SMALL) {
return;
}
const bNodeSocket *output = static_cast<bNodeSocket *>(node.outputs.first);
char showname[128]; /* 128 used below */
STRNCPY(showname, use_auto_label ? output->label : node.label);
const short width = 512;
const int x = BLI_rctf_cent_x(&node.runtime->totr) - (width / 2);
const int y = node.runtime->totr.ymax;
uiBut *label_but = uiDefBut(
&block, UI_BTYPE_LABEL, 0, showname, x, y, width, short(NODE_DY), nullptr, 0, 0, nullptr);
UI_but_drawflag_disable(label_but, UI_BUT_TEXT_LEFT);
if (use_auto_label && !(node.flag & NODE_SELECT)) {
UI_but_flag_enable(label_but, UI_BUT_INACTIVE);
}
}
static void reroute_node_draw(const bContext &C,
ARegion &region,
const SpaceNode &snode,
bNodeTree &ntree,
const bNode &node,
uiBlock &block)
{
/* Skip if out of view. */
const rctf &rct = node.runtime->totr;
@ -4106,19 +4149,7 @@ static void reroute_node_draw(
return;
}
if (node.label[0] != '\0') {
/* Draw title (node label). */
char showname[128]; /* 128 used below */
STRNCPY(showname, node.label);
const short width = 512;
const int x = BLI_rctf_cent_x(&node.runtime->totr) - (width / 2);
const int y = node.runtime->totr.ymax;
uiBut *label_but = uiDefBut(
&block, UI_BTYPE_LABEL, 0, showname, x, y, width, short(NODE_DY), nullptr, 0, 0, nullptr);
UI_but_drawflag_disable(label_but, UI_BUT_TEXT_LEFT);
}
reroute_node_draw_label(snode, node, block);
/* Only draw input socket as they all are placed on the same position highlight
* if node itself is selected, since we don't display the node body separately. */
@ -4141,7 +4172,7 @@ static void node_draw(const bContext &C,
frame_node_draw(C, tree_draw_ctx, region, snode, ntree, node, block);
}
else if (node.is_reroute()) {
reroute_node_draw(C, region, ntree, node, block);
reroute_node_draw(C, region, snode, ntree, node, block);
}
else {
const View2D &v2d = region.v2d;

View File

@ -1555,6 +1555,7 @@ typedef enum eSpaceNodeOverlay_Flag {
SN_OVERLAY_SHOW_PATH = (1 << 4),
SN_OVERLAY_SHOW_NAMED_ATTRIBUTES = (1 << 5),
SN_OVERLAY_SHOW_PREVIEWS = (1 << 6),
SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS = (1 << 7),
} eSpaceNodeOverlay_Flag;
typedef enum eSpaceNodeOverlay_preview_shape {

View File

@ -2221,6 +2221,16 @@ void rna_Node_update_relations(Main *bmain, Scene *scene, PointerRNA *ptr)
DEG_relations_tag_update(bmain);
}
static void rna_Node_update_node_labels(Main *bmain, Scene *scene, PointerRNA *ptr)
{
bNodeTree &ntree = *reinterpret_cast<bNodeTree *>(ptr->owner_id);
const bNode &node = *static_cast<bNode *>(ptr->data);
if (node.is_reroute()) {
blender::bke::ntree_update_auto_labels(ntree);
}
}
static void rna_Node_socket_value_update(ID *id, bNode * /*node*/, bContext *C)
{
BKE_ntree_update_tag_all(reinterpret_cast<bNodeTree *>(id));
@ -9955,7 +9965,7 @@ static void rna_def_node(BlenderRNA *brna)
prop = RNA_def_property(srna, "label", PROP_STRING, PROP_NONE);
RNA_def_property_string_sdna(prop, nullptr, "label");
RNA_def_property_ui_text(prop, "Label", "Optional custom node label");
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, nullptr);
RNA_def_property_update(prop, NC_NODE | ND_DISPLAY, "rna_Node_update_node_labels");
prop = RNA_def_property(srna, "inputs", PROP_COLLECTION, PROP_NONE);
RNA_def_property_collection_sdna(prop, nullptr, "inputs", nullptr);

View File

@ -7573,6 +7573,15 @@ static void rna_def_space_node_overlay(BlenderRNA *brna)
prop, "Show Wire Colors", "Color node links based on their connected sockets");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, nullptr);
prop = RNA_def_property(srna, "show_reroute_auto_labels", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(
prop, nullptr, "overlay.flag", SN_OVERLAY_SHOW_REROUTE_AUTO_LABELS);
RNA_def_property_boolean_default(prop, false);
RNA_def_property_ui_text(prop,
"Show Reroute Auto Labels",
"Label reroute nodes based on the label of connected reroute nodes");
RNA_def_property_update(prop, NC_SPACE | ND_SPACE_NODE, nullptr);
prop = RNA_def_property(srna, "show_timing", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, nullptr, "overlay.flag", SN_OVERLAY_SHOW_TIMINGS);
RNA_def_property_boolean_default(prop, false);

View File

@ -585,6 +585,52 @@ void register_node_type_reroute()
nodeRegisterType(ntype);
}
/* NOTE: This uses the label of the reroute node's output socket to
* propagate the label from connected reroute nodes. */
static void update_reroute_node_auto_labels(bNodeTree *ntree)
{
using namespace blender;
ntree->ensure_topology_cache();
/* Clear auto-label. */
LISTBASE_FOREACH (bNode *, reroute, &ntree->nodes) {
if (!reroute->is_reroute()) {
continue;
}
bNodeSocket *output = reroute->output_sockets().first();
node_sock_label_clear(output);
}
for (bNode *reroute : ntree->toposort_left_to_right()) {
if (!reroute->is_reroute()) {
continue;
}
bNodeSocket *output = reroute->output_sockets().first();
const bNodeSocket &input = *reroute->input_sockets().first();
const Span<const bNodeSocket *> linked_sockets = input.directly_linked_sockets();
if (linked_sockets.is_empty()) {
continue;
}
const bNodeSocket &from_sock = *linked_sockets.first();
const bNode &from_node = from_sock.owner_node();
if (!from_node.is_reroute()) {
continue;
}
if (from_node.label[0] != '\0') {
/* Use the connected reroute's label to also label this reroute. */
node_sock_label(output, from_node.label);
continue;
}
node_sock_label(output, from_sock.label);
}
}
static void propagate_reroute_type_from_start_socket(
bNodeSocket *start_socket,
const MultiValueMap<bNodeSocket *, bNodeLink *> &links_map,
@ -618,7 +664,7 @@ static void propagate_reroute_type_from_start_socket(
}
}
void ntree_update_reroute_nodes(bNodeTree *ntree)
void update_reroute_node_socket_types(bNodeTree *ntree)
{
/* Contains nodes that are linked to at least one reroute node. */
Set<bNode *> nodes_linked_with_reroutes;
@ -676,6 +722,17 @@ void ntree_update_reroute_nodes(bNodeTree *ntree)
}
}
void ntree_update_reroute_nodes(bNodeTree *ntree)
{
update_reroute_node_socket_types(ntree);
update_reroute_node_auto_labels(ntree);
}
void blender::bke::ntree_update_auto_labels(bNodeTree &ntree)
{
update_reroute_node_auto_labels(&ntree);
}
bool blender::bke::node_is_connected_to_output(const bNodeTree *ntree, const bNode *node)
{
ntree->ensure_topology_cache();