Nodes: Add new shader for node sockets

Add a new shader specifically for node sockets rather than using the
keyframe shader.

Motivation:
1. Allow easier addition of new socket shapes
2. Simplify socket drawing by avoiding special handling of multi-inputs
3. Support multi-inputs for all socket types (diamond, square, etc.)

The new shader is tweaked to look the same to the old ones.

**Comparison**
The biggest difference is that the multi socket is now more consistent
with the other sockets.
For single sockets there can be small size differences depending on zoom
level because the old socket shader always aligned the sockets to the
pixel grid. This could cause a bit of jiggling compared to the rest of
the node when slowly zooming. Therefore I left it out of the new shader
and it now scales strictly linear with the view.

**Multi Socket Types**
While there currently is no need for (.) internally, there are a few
obvious use-cases for multi-input field (diamond) sockets like
generalized math nodes with an arbitrary number of inputs (Add,
Multiply, Minimum etc.).

Co-authored-by: Jacques Lucke <jacques@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/119243
This commit is contained in:
Leon Schittek
2024-11-23 16:42:38 +01:00
committed by Hans Goudey
parent b6b312d98f
commit 13e0077c5c
14 changed files with 561 additions and 372 deletions

View File

@@ -47,8 +47,6 @@ void node_insert_on_link_flags_clear(bNodeTree &node_tree);
/**
* Draw a single node socket at default size.
* \note this is only called from external code, internally #node_socket_draw_nested() is used for
* optimized drawing of multiple/all sockets of a node.
*/
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale);

View File

@@ -1810,6 +1810,146 @@ void node_link_bezier_points_evaluated(const bNodeLink &link,
sizeof(float2));
}
/* -------------------------------------------------------------------- */
/** \name Node Socket Drawing
* \{ */
/* Keep in sync with node socket shader. */
#define MAX_SOCKET_PARAMETERS 4
#define MAX_SOCKET_INSTANCE 32
struct GBatchNodesocket {
gpu::Batch *batch;
Vector<NodeSocketShaderParameters, MAX_SOCKET_INSTANCE> params;
bool enabled;
};
static GBatchNodesocket &g_batch_nodesocket()
{
static GBatchNodesocket nodesocket_batch;
return nodesocket_batch;
}
static gpu::Batch *nodesocket_batch_init()
{
if (g_batch_nodesocket().batch == nullptr) {
GPUIndexBufBuilder ibuf;
GPU_indexbuf_init(&ibuf, GPU_PRIM_TRIS, 2, 4);
/* Quad to draw the node socket in. */
GPU_indexbuf_add_tri_verts(&ibuf, 0, 1, 2);
GPU_indexbuf_add_tri_verts(&ibuf, 2, 1, 3);
g_batch_nodesocket().batch = GPU_batch_create_ex(
GPU_PRIM_TRIS, nullptr, GPU_indexbuf_build(&ibuf), GPU_BATCH_OWNS_INDEX);
gpu_batch_presets_register(g_batch_nodesocket().batch);
}
return g_batch_nodesocket().batch;
}
static void nodesocket_cache_flush()
{
if (g_batch_nodesocket().params.is_empty()) {
return;
}
gpu::Batch *batch = nodesocket_batch_init();
if (g_batch_nodesocket().params.size() == 1) {
/* draw single */
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODE_SOCKET);
GPU_batch_uniform_4fv_array(
batch,
"parameters",
4,
reinterpret_cast<const float(*)[4]>(g_batch_nodesocket().params.data()));
GPU_batch_draw(batch);
}
else {
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODE_SOCKET_INST);
GPU_batch_uniform_4fv_array(
batch,
"parameters",
MAX_SOCKET_PARAMETERS * MAX_SOCKET_INSTANCE,
reinterpret_cast<const float(*)[4]>(g_batch_nodesocket().params.data()));
GPU_batch_draw_instance_range(batch, 0, g_batch_nodesocket().params.size());
}
g_batch_nodesocket().params.clear();
}
void nodesocket_batch_start()
{
BLI_assert(g_batch_nodesocket().enabled == false);
g_batch_nodesocket().enabled = true;
}
void nodesocket_batch_end()
{
BLI_assert(g_batch_nodesocket().enabled == true);
g_batch_nodesocket().enabled = false;
GPU_blend(GPU_BLEND_ALPHA);
nodesocket_cache_flush();
GPU_blend(GPU_BLEND_NONE);
}
static void draw_node_socket_batch(const NodeSocketShaderParameters &socket_params)
{
if (g_batch_nodesocket().enabled) {
g_batch_nodesocket().params.append(socket_params);
if (g_batch_nodesocket().params.size() >= MAX_SOCKET_INSTANCE) {
nodesocket_cache_flush();
}
}
else {
/* Draw single instead of batch. */
gpu::Batch *batch = nodesocket_batch_init();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_NODE_SOCKET);
GPU_batch_uniform_4fv_array(
batch, "parameters", MAX_SOCKET_PARAMETERS, (const float(*)[4])(&socket_params));
GPU_batch_draw(batch);
}
}
void node_draw_nodesocket(const rctf *rect,
const float color_inner[4],
const float color_outline[4],
const float outline_thickness,
const int shape,
const float aspect)
{
/* WATCH: This is assuming the ModelViewProjectionMatrix is area pixel space.
* If it has been scaled, then it's no longer valid. */
BLI_assert((color_inner != nullptr) && (color_outline != nullptr));
NodeSocketShaderParameters socket_params = {};
socket_params.rect[0] = rect->xmin;
socket_params.rect[1] = rect->xmax;
socket_params.rect[2] = rect->ymin;
socket_params.rect[3] = rect->ymax;
socket_params.color_inner[0] = color_inner[0];
socket_params.color_inner[1] = color_inner[1];
socket_params.color_inner[2] = color_inner[2];
socket_params.color_inner[3] = color_inner[3];
socket_params.color_outline[0] = color_outline[0];
socket_params.color_outline[1] = color_outline[1];
socket_params.color_outline[2] = color_outline[2];
socket_params.color_outline[3] = color_outline[3];
socket_params.outline_thickness = outline_thickness;
socket_params.outline_offset = 0.0;
socket_params.shape = float(shape) + 0.1f;
socket_params.aspect = aspect;
GPU_blend(GPU_BLEND_ALPHA);
draw_node_socket_batch(socket_params);
GPU_blend(GPU_BLEND_NONE);
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Node Link Drawing
* \{ */
#define NODELINK_GROUP_SIZE 256
#define LINK_RESOL 24
#define LINK_WIDTH 2.5f
@@ -2433,3 +2573,5 @@ void ED_node_draw_snap(View2D *v2d, const float cent[2], float size, NodeBorder
immEnd();
}
/** \} */

View File

@@ -1386,52 +1386,6 @@ static void node_draw_mute_line(const bContext &C,
GPU_blend(GPU_BLEND_NONE);
}
static void node_socket_draw(const bNodeSocket &sock,
const float color[4],
const float color_outline[4],
const float size,
const float locx,
const float locy,
uint pos_id,
uint col_id,
uint shape_id,
uint size_id,
uint outline_col_id)
{
int flags;
/* Set shape flags. */
switch (sock.display_shape) {
case SOCK_DISPLAY_SHAPE_DIAMOND:
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT:
flags = GPU_KEYFRAME_SHAPE_DIAMOND;
break;
case SOCK_DISPLAY_SHAPE_SQUARE:
case SOCK_DISPLAY_SHAPE_SQUARE_DOT:
flags = GPU_KEYFRAME_SHAPE_SQUARE;
break;
default:
case SOCK_DISPLAY_SHAPE_CIRCLE:
case SOCK_DISPLAY_SHAPE_CIRCLE_DOT:
flags = GPU_KEYFRAME_SHAPE_CIRCLE;
break;
}
if (ELEM(sock.display_shape,
SOCK_DISPLAY_SHAPE_DIAMOND_DOT,
SOCK_DISPLAY_SHAPE_SQUARE_DOT,
SOCK_DISPLAY_SHAPE_CIRCLE_DOT))
{
flags |= GPU_KEYFRAME_SHAPE_INNER_DOT;
}
immAttr4fv(col_id, color);
immAttr1u(shape_id, flags);
immAttr1f(size_id, size);
immAttr4fv(outline_col_id, color_outline);
immVertex2f(pos_id, locx, locy);
}
static void node_socket_tooltip_set(uiBlock &block,
const int socket_index_in_tree,
const float2 location,
@@ -1470,41 +1424,6 @@ static void node_socket_tooltip_set(uiBlock &block,
UI_block_emboss_set(&block, old_emboss);
}
static void node_socket_draw_multi_input(uiBlock &block,
const int index_in_tree,
const float2 location,
const float2 draw_size,
const float color[4],
const float color_outline[4],
const float2 tooltip_size)
{
/* The other sockets are drawn with the keyframe shader. There, the outline has a base
* thickness that can be varied but always scales with the size the socket is drawn at. Using
* `UI_SCALE_FAC` has the same effect here. It scales the outline correctly across different
* screen DPI's and UI scales without being affected by the 'line-width'. */
const float half_outline_width = NODE_SOCK_OUTLINE_SCALE * UI_SCALE_FAC * 0.5f;
/* UI_draw_roundbox draws the outline on the outer side, so compensate for the outline width.
*/
const rctf rect = {
location.x - draw_size.x + half_outline_width,
location.x + draw_size.x + half_outline_width,
location.y - draw_size.y + half_outline_width,
location.y + draw_size.y + half_outline_width,
};
UI_draw_roundbox_corner_set(UI_CNR_ALL);
UI_draw_roundbox_4fv_ex(&rect,
color,
nullptr,
1.0f,
color_outline,
half_outline_width * 2.0f,
draw_size.x - half_outline_width);
node_socket_tooltip_set(block, index_in_tree, location, tooltip_size);
}
static const float virtual_node_socket_outline_color[4] = {0.5, 0.5, 0.5, 1.0};
static void node_socket_outline_color_get(const bool selected,
@@ -2242,87 +2161,26 @@ void node_socket_add_tooltip(const bNodeTree &ntree, const bNodeSocket &sock, ui
MEM_freeN);
}
static void node_socket_draw_nested(const bContext &C,
const bNodeTree &ntree,
PointerRNA &node_ptr,
uiBlock &block,
const bNodeSocket &sock,
const uint pos_id,
const uint col_id,
const uint shape_id,
const uint size_id,
const uint outline_col_id,
const float size,
const bool selected)
{
const float2 location = sock.runtime->location;
float color[4];
float outline_color[4];
node_socket_color_get(C, ntree, node_ptr, sock, color);
node_socket_outline_color_get(selected, sock.type, outline_color);
node_socket_draw(sock,
color,
outline_color,
size,
location.x,
location.y,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id);
node_socket_tooltip_set(block, sock.index_in_tree(), location, float2(size, size));
}
#define NODE_SOCKET_OUTLINE U.pixelsize
void node_socket_draw(bNodeSocket *sock, const rcti *rect, const float color[4], float scale)
{
const float size = NODE_SOCKSIZE_DRAW_MULIPLIER * NODE_SOCKSIZE * scale;
rcti draw_rect = *rect;
const float radius = NODE_SOCKSIZE * scale;
const float2 center = {BLI_rcti_cent_x_fl(rect), BLI_rcti_cent_y_fl(rect)};
const rctf draw_rect = {
center.x - radius,
center.x + radius,
center.y - radius,
center.y + radius,
};
float outline_color[4] = {0};
node_socket_outline_color_get(sock->flag & SELECT, sock->type, outline_color);
BLI_rcti_resize(&draw_rect, size, size);
GPUVertFormat *format = immVertexFormat();
uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
uint shape_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
uint outline_col_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
eGPUBlend state = GPU_blend_get();
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Single point. */
immBegin(GPU_PRIM_POINTS, 1);
node_socket_draw(*sock,
color,
outline_color,
BLI_rcti_size_y(&draw_rect),
BLI_rcti_cent_x(&draw_rect),
BLI_rcti_cent_y(&draw_rect),
pos_id,
col_id,
shape_id,
size_id,
outline_col_id);
immEnd();
immUnbindProgram();
GPU_program_point_size(false);
/* Restore. */
GPU_blend(state);
node_draw_nodesocket(&draw_rect,
color,
outline_color,
NODE_SOCKET_OUTLINE * scale,
sock->display_shape,
1.0 / scale);
}
/** Some elements of the node UI are hidden, when they get too small. */
@@ -2334,6 +2192,15 @@ static float node_tree_view_scale(const SpaceNode &snode)
return (1.0f / snode.runtime->aspect) * UI_SCALE_FAC;
}
/* Some elements of the node tree like labels or node sockets are hardly visible when zoomed
* out and can slow down the drawing quite a bit.
* This function can be used to check if it's worth to draw those details and return
* early. */
static bool draw_node_details(const SpaceNode &snode)
{
return node_tree_view_scale(snode) > NODE_TREE_SCALE_SMALL * UI_INV_SCALE_FAC;
}
static void node_draw_preview_background(rctf *rect)
{
GPUVertFormat *format = immVertexFormat();
@@ -2439,212 +2306,79 @@ static void node_draw_shadow(const SpaceNode &snode,
UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
}
static void node_draw_sockets(const View2D &v2d,
const bContext &C,
const bNodeTree &ntree,
const bNode &node,
uiBlock &block,
const bool draw_outputs,
const bool select_all)
static void node_draw_socket(const bContext &C,
const bNodeTree &ntree,
const bNode &node,
PointerRNA &node_ptr,
uiBlock &block,
const bNodeSocket &sock,
const float outline_thickness,
const bool selected,
const float aspect)
{
const float half_width = NODE_SOCKSIZE;
const bool multi_socket = (sock.flag & SOCK_MULTI_INPUT) && !(node.flag & NODE_HIDDEN);
float half_height = multi_socket ? node_socket_calculate_height(sock) : half_width;
ColorTheme4f socket_color;
ColorTheme4f outline_color;
node_socket_color_get(C, ntree, node_ptr, sock, socket_color);
node_socket_outline_color_get(selected, sock.type, outline_color);
const float2 socket_location = sock.runtime->location;
const rctf rect = {
socket_location.x - half_width,
socket_location.x + half_width,
socket_location.y - half_height,
socket_location.y + half_height,
};
node_draw_nodesocket(
&rect, socket_color, outline_color, outline_thickness, sock.display_shape, aspect);
node_socket_tooltip_set(
block, sock.index_in_tree(), socket_location, float2(2.0f * half_width, 2.0f * half_height));
}
static void node_draw_sockets(
const bContext &C, uiBlock &block, const SpaceNode &snode, bNodeTree &ntree, const bNode &node)
{
if (!draw_node_details(snode)) {
return;
}
if (node.input_sockets().is_empty() && node.output_sockets().is_empty()) {
return;
}
bool selected = false;
PointerRNA nodeptr = RNA_pointer_create(
const_cast<ID *>(&ntree.id), &RNA_Node, const_cast<bNode *>(&node));
GPUVertFormat *format = immVertexFormat();
uint pos_id = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint col_id = GPU_vertformat_attr_add(format, "color", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
uint shape_id = GPU_vertformat_attr_add(format, "flags", GPU_COMP_U32, 1, GPU_FETCH_INT);
uint size_id = GPU_vertformat_attr_add(format, "size", GPU_COMP_F32, 1, GPU_FETCH_FLOAT);
uint outline_col_id = GPU_vertformat_attr_add(
format, "outlineColor", GPU_COMP_F32, 4, GPU_FETCH_FLOAT);
const float outline_thickness = NODE_SOCKET_OUTLINE;
GPU_blend(GPU_BLEND_ALPHA);
GPU_program_point_size(true);
immBindBuiltinProgram(GPU_SHADER_KEYFRAME_SHAPE);
immUniform1f("outline_scale", NODE_SOCK_OUTLINE_SCALE);
immUniform2f("ViewportSize", -1.0f, -1.0f);
/* Set handle size. */
const float socket_draw_size = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER;
float scale;
UI_view2d_scale_get(&v2d, &scale, nullptr);
scale *= socket_draw_size;
if (!select_all) {
immBeginAtMost(GPU_PRIM_POINTS, node.input_sockets().size() + node.output_sockets().size());
}
PointerRNA node_ptr = RNA_pointer_create(
&const_cast<ID &>(ntree.id), &RNA_Node, &const_cast<bNode &>(node));
/* Socket inputs. */
int selected_input_len = 0;
nodesocket_batch_start();
/* Input sockets. */
for (const bNodeSocket *sock : node.input_sockets()) {
/* In "hidden" nodes: draw sockets even when panels are collapsed. */
if (!node.is_socket_icon_drawn(*sock)) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
if (!(sock->flag & SOCK_MULTI_INPUT)) {
/* Don't add multi-input sockets here since they are drawn in a different batch. */
selected_input_len++;
}
const bool selected = (sock->flag & SELECT);
node_draw_socket(
C, ntree, node, nodeptr, block, *sock, outline_thickness, selected, snode.runtime->aspect);
}
/* Output sockets. */
for (const bNodeSocket *sock : node.output_sockets()) {
if (!node.is_socket_icon_drawn(*sock)) {
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,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
}
/* Socket outputs. */
int selected_output_len = 0;
if (draw_outputs) {
for (const bNodeSocket *sock : node.output_sockets()) {
/* In "hidden" nodes: draw sockets even when panels are collapsed. */
if (!node.is_socket_icon_drawn(*sock)) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
selected_output_len++;
continue;
}
node_socket_draw_nested(C,
ntree,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
}
}
if (!select_all) {
immEnd();
}
/* Go back and draw selected sockets. */
if (selected_input_len + selected_output_len > 0) {
/* Outline for selected sockets. */
selected = true;
immBegin(GPU_PRIM_POINTS, selected_input_len + selected_output_len);
if (selected_input_len) {
/* Socket inputs. */
for (const bNodeSocket *sock : node.input_sockets()) {
if (!node.is_socket_icon_drawn(*sock)) {
continue;
}
/* Don't draw multi-input sockets here since they are drawn in a different batch. */
if (sock->flag & SOCK_MULTI_INPUT) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
if (--selected_input_len == 0) {
/* Stop as soon as last one is drawn. */
break;
}
}
}
}
if (selected_output_len) {
/* Socket outputs. */
for (const bNodeSocket *sock : node.output_sockets()) {
if (!node.is_socket_icon_drawn(*sock)) {
continue;
}
if (select_all || (sock->flag & SELECT)) {
node_socket_draw_nested(C,
ntree,
node_ptr,
block,
*sock,
pos_id,
col_id,
shape_id,
size_id,
outline_col_id,
scale,
selected);
if (--selected_output_len == 0) {
/* Stop as soon as last one is drawn. */
break;
}
}
}
}
immEnd();
}
immUnbindProgram();
GPU_program_point_size(false);
GPU_blend(GPU_BLEND_NONE);
/* Draw multi-input sockets after the others because they are drawn with `UI_draw_roundbox`
* rather than with `GL_POINT`. */
for (const bNodeSocket *socket : node.input_sockets()) {
if (!node.is_socket_icon_drawn(*socket)) {
continue;
}
if (!(socket->flag & SOCK_MULTI_INPUT)) {
continue;
}
const bool is_node_hidden = (node.flag & NODE_HIDDEN);
const float width = 0.5f * socket_draw_size;
float height = is_node_hidden ? width : node_socket_calculate_height(*socket) - width;
float color[4];
float outline_color[4];
node_socket_color_get(C, ntree, node_ptr, *socket, color);
node_socket_outline_color_get(socket->flag & SELECT, socket->type, outline_color);
const int index_in_tree = socket->index_in_tree();
const float2 location = socket->runtime->location;
const float2 draw_size(width, height);
const float2 tooltip_size(scale, height * 2.0f - socket_draw_size + scale);
node_socket_draw_multi_input(
block, index_in_tree, location, draw_size, color, outline_color, tooltip_size);
const bool selected = (sock->flag & SELECT);
node_draw_socket(
C, ntree, node, nodeptr, block, *sock, outline_thickness, selected, snode.runtime->aspect);
}
nodesocket_batch_end();
}
static void node_panel_toggle_button_cb(bContext *C, void *panel_state_argv, void *ntree_argv)
@@ -3849,8 +3583,8 @@ static void node_draw_basis(const bContext &C,
}
/* Skip slow socket drawing if zoom is small. */
if (node_tree_view_scale(snode) > NODE_TREE_SCALE_SMALL) {
node_draw_sockets(v2d, C, ntree, node, block, true, false);
if (draw_node_details(snode)) {
node_draw_sockets(C, block, snode, ntree, node);
}
if (is_node_panels_supported(node)) {
@@ -4046,7 +3780,7 @@ static void node_draw_hidden(const bContext &C,
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
node_draw_sockets(v2d, C, ntree, node, block, true, false);
node_draw_sockets(C, block, snode, ntree, node);
UI_block_end_ex(&C,
tree_draw_ctx.bmain,
@@ -4201,16 +3935,16 @@ static void reroute_node_prepare_for_draw(bNode &node)
{
const float2 loc = node_to_view(node, float2(0));
/* Reroute node has exactly one input and one output, both in the same place. */
/* When the node is hidden, the input and output socket are both in the same place. */
node.input_socket(0).runtime->location = loc;
node.output_socket(0).runtime->location = loc;
const float size = 8.0f;
node.width = size * 2;
node.runtime->totr.xmin = loc.x - size;
node.runtime->totr.xmax = loc.x + size;
node.runtime->totr.ymax = loc.y + size;
node.runtime->totr.ymin = loc.y - size;
const float radius = NODE_SOCKSIZE;
node.width = radius * 2;
node.runtime->totr.xmin = loc.x - radius;
node.runtime->totr.xmax = loc.x + radius;
node.runtime->totr.ymax = loc.y + radius;
node.runtime->totr.ymin = loc.y - radius;
}
static void node_update_nodetree(const bContext &C,
@@ -4522,6 +4256,40 @@ static StringRefNull reroute_node_get_auto_label(TreeDrawContext &tree_draw_ctx,
return label;
}
static void reroute_node_draw_body(const bContext &C,
const SpaceNode &snode,
const bNodeTree &ntree,
const bNode &node,
uiBlock &block,
const bool selected)
{
BLI_assert(node.is_reroute());
bNodeSocket &sock = *static_cast<bNodeSocket *>(node.inputs.first);
PointerRNA nodeptr = RNA_pointer_create(
const_cast<ID *>(&ntree.id), &RNA_Node, const_cast<bNode *>(&node));
ColorTheme4f socket_color;
ColorTheme4f outline_color;
node_socket_color_get(C, ntree, nodeptr, sock, socket_color);
node_socket_outline_color_get(selected, sock.type, outline_color);
node_draw_nodesocket(&node.runtime->totr,
socket_color,
outline_color,
NODE_SOCKET_OUTLINE,
sock.display_shape,
snode.runtime->aspect);
const float2 location = float2(BLI_rctf_cent_x(&node.runtime->totr),
BLI_rctf_cent_y(&node.runtime->totr));
const float2 size = float2(BLI_rctf_size_x(&node.runtime->totr),
BLI_rctf_size_y(&node.runtime->totr));
node_socket_tooltip_set(block, sock.index_in_tree(), location, size);
}
static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx,
const SpaceNode &snode,
const bNode &node,
@@ -4536,7 +4304,7 @@ static void reroute_node_draw_label(TreeDrawContext &tree_draw_ctx,
}
/* Don't show the automatic label, when being zoomed out. */
if (!has_label && node_tree_view_scale(snode) < NODE_TREE_SCALE_SMALL) {
if (!has_label && !draw_node_details(snode)) {
return;
}
@@ -4566,10 +4334,12 @@ static void reroute_node_draw(const bContext &C,
const bNode &node,
uiBlock &block)
{
/* Skip if out of view. */
const rctf &rct = node.runtime->totr;
if (rct.xmax < region.v2d.cur.xmin || rct.xmin > region.v2d.cur.xmax ||
rct.ymax < region.v2d.cur.ymin || node.runtime->totr.ymin > region.v2d.cur.ymax)
const View2D &v2d = region.v2d;
/* Skip if out of view. */
if (rct.xmax < v2d.cur.xmin || rct.xmin > v2d.cur.xmax || rct.ymax < v2d.cur.ymin ||
node.runtime->totr.ymin > v2d.cur.ymax)
{
UI_block_end_ex(&C,
tree_draw_ctx.bmain,
@@ -4581,11 +4351,13 @@ static void reroute_node_draw(const bContext &C,
return;
}
reroute_node_draw_label(tree_draw_ctx, snode, node, block);
if (draw_node_details(snode)) {
reroute_node_draw_label(tree_draw_ctx, 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. */
node_draw_sockets(region.v2d, C, ntree, node, block, false, node.flag & SELECT);
/* Only draw the input socket, since all sockets are at the same location. */
const bool selected = node.flag & NODE_SELECT;
reroute_node_draw_body(C, snode, ntree, node, block, selected);
UI_block_end_ex(&C,
tree_draw_ctx.bmain,

View File

@@ -107,7 +107,7 @@ struct CompoJob {
float node_socket_calculate_height(const bNodeSocket &socket)
{
float sock_height = NODE_SOCKSIZE * NODE_SOCKSIZE_DRAW_MULIPLIER;
float sock_height = NODE_SOCKSIZE;
if (socket.flag & SOCK_MULTI_INPUT) {
sock_height += max_ii(NODE_MULTI_INPUT_LINK_GAP * 0.5f * socket.runtime->total_inputs,
NODE_SOCKSIZE);

View File

@@ -138,8 +138,6 @@ ENUM_OPERATORS(NodeResizeDirection, NODE_RESIZE_LEFT);
#define NODE_HEIGHT(node) (node.height * UI_SCALE_FAC)
#define NODE_MARGIN_X (1.2f * U.widget_unit)
#define NODE_SOCKSIZE (0.25f * U.widget_unit)
#define NODE_SOCKSIZE_DRAW_MULIPLIER 2.25f
#define NODE_SOCK_OUTLINE_SCALE 1.0f
#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
@@ -239,6 +237,17 @@ NodeResizeDirection node_get_resize_direction(const SpaceNode &snode,
int x,
int y);
/* node socket batched drawing */
void UI_node_socket_draw_cache_flush();
void nodesocket_batch_start();
void nodesocket_batch_end();
void node_draw_nodesocket(const rctf *rect,
const float color_inner[4],
const float color_outline[4],
float outline_thickness,
int shape,
float aspect);
void nodelink_batch_start(SpaceNode &snode);
void nodelink_batch_end(SpaceNode &snode);

View File

@@ -482,6 +482,8 @@ set(GLSL_SRC
shaders/gpu_shader_2D_widget_base_frag.glsl
shaders/gpu_shader_2D_widget_shadow_vert.glsl
shaders/gpu_shader_2D_widget_shadow_frag.glsl
shaders/gpu_shader_2D_node_socket_frag.glsl
shaders/gpu_shader_2D_node_socket_vert.glsl
shaders/gpu_shader_2D_nodelink_frag.glsl
shaders/gpu_shader_2D_nodelink_vert.glsl
shaders/gpu_shader_2D_line_dashed_frag.glsl

View File

@@ -58,6 +58,10 @@ enum eGPUBuiltinShader {
GPU_SHADER_2D_WIDGET_BASE,
GPU_SHADER_2D_WIDGET_BASE_INST,
GPU_SHADER_2D_WIDGET_SHADOW,
/** Draw a node socket given it's bounding rectangle. All socket shapes are supported through
* a single shader. */
GPU_SHADER_2D_NODE_SOCKET,
GPU_SHADER_2D_NODE_SOCKET_INST,
/** Draw a node link given an input quadratic Bezier curve. */
GPU_SHADER_2D_NODELINK,
GPU_SHADER_2D_NODELINK_INST,

View File

@@ -32,6 +32,21 @@ enum eGPUKeyframeShapes : uint32_t {
GPU_KEYFRAME_SHAPE_CLIPPED_HORIZONTAL),
};
#define MAX_SOCKET_PARAMETERS 4
#define MAX_SOCKET_INSTANCE 32
/* Node Socket shader parameters. Must match the shader layout of "gpu_shader_2D_node_socket". */
struct NodeSocketShaderParameters {
float4 rect;
float4 color_inner;
float4 color_outline;
float outline_thickness;
float outline_offset;
float shape;
float aspect;
};
BLI_STATIC_ASSERT_ALIGN(NodeSocketShaderParameters, 16)
struct NodeLinkData {
float4 colors[3];
/* bezierPts Is actually a float2, but due to std140 each element needs to be aligned to 16

View File

@@ -81,6 +81,10 @@ static const char *builtin_shader_create_info_name(eGPUBuiltinShader shader)
return "gpu_shader_2D_widget_base_inst";
case GPU_SHADER_2D_WIDGET_SHADOW:
return "gpu_shader_2D_widget_shadow";
case GPU_SHADER_2D_NODE_SOCKET:
return "gpu_shader_2D_node_socket";
case GPU_SHADER_2D_NODE_SOCKET_INST:
return "gpu_shader_2D_node_socket_inst";
case GPU_SHADER_2D_NODELINK:
return "gpu_shader_2D_nodelink";
case GPU_SHADER_2D_NODELINK_INST:

View File

@@ -14,6 +14,7 @@
#include "gpu_shader_2D_image_overlays_stereo_merge_info.hh"
#include "gpu_shader_2D_image_rect_color_info.hh"
#include "gpu_shader_2D_image_shuffle_color_info.hh"
#include "gpu_shader_2D_node_socket_info.hh"
#include "gpu_shader_2D_nodelink_info.hh"
#include "gpu_shader_2D_point_uniform_size_uniform_color_aa_info.hh"
#include "gpu_shader_2D_point_uniform_size_uniform_color_outline_aa_info.hh"

View File

@@ -25,6 +25,7 @@ set(SRC_GLSL_VERT
gpu_shader_2D_area_borders_vert.glsl
gpu_shader_2D_image_rect_vert.glsl
gpu_shader_2D_image_vert.glsl
gpu_shader_2D_node_socket_vert.glsl
gpu_shader_2D_nodelink_vert.glsl
gpu_shader_2D_point_uniform_size_aa_vert.glsl
gpu_shader_2D_point_uniform_size_outline_aa_vert.glsl
@@ -53,6 +54,7 @@ set(SRC_GLSL_VERT
set(SRC_GLSL_FRAG
gpu_shader_2D_area_borders_frag.glsl
gpu_shader_2D_line_dashed_frag.glsl
gpu_shader_2D_node_socket_frag.glsl
gpu_shader_2D_nodelink_frag.glsl
gpu_shader_2D_widget_base_frag.glsl
gpu_shader_2D_widget_shadow_frag.glsl

View File

@@ -0,0 +1,113 @@
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_node_socket_info.hh"
#include "gpu_shader_math_matrix_lib.glsl"
FRAGMENT_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
/* Values in `eNodeSocketDisplayShape` in DNA_node_types.h. Keep in sync. */
#define SOCK_DISPLAY_SHAPE_CIRCLE 0
#define SOCK_DISPLAY_SHAPE_SQUARE 1
#define SOCK_DISPLAY_SHAPE_DIAMOND 2
#define SOCK_DISPLAY_SHAPE_CIRCLE_DOT 3
#define SOCK_DISPLAY_SHAPE_SQUARE_DOT 4
#define SOCK_DISPLAY_SHAPE_DIAMOND_DOT 5
/* Calculates a squared distance field of a square. */
float square_sdf(vec2 absCo, float half_width_x, float half_width_y)
{
vec2 extruded_co = absCo - vec2(half_width_x, half_width_y);
vec2 clamped_extruded_co = vec2(max(0.0, extruded_co.x), max(0.0, extruded_co.y));
float exterior_distance_squared = dot(clamped_extruded_co, clamped_extruded_co);
float interior_distance = min(max(extruded_co.x, extruded_co.y), 0.0);
float interior_distance_squared = interior_distance * interior_distance;
return exterior_distance_squared - interior_distance_squared;
}
vec2 rotate_45(vec2 co)
{
return from_rotation(Angle(M_PI * 0.25)) * co;
}
/* Calculates an upper and lower limit for an antialiased cutoff of the squared distance. */
vec2 calculate_thresholds(float threshold)
{
/* Use the absolute on one of the factors to preserve the sign. */
float inner_threshold = (threshold - 0.5 * AAsize) * abs(threshold - 0.5 * AAsize);
float outer_threshold = (threshold + 0.5 * AAsize) * abs(threshold + 0.5 * AAsize);
return vec2(inner_threshold, outer_threshold);
}
void main()
{
vec2 absUV = abs(uv);
vec2 co = vec2(max(absUV.x - extrusion.x, 0.0), max(absUV.y - extrusion.y, 0.0));
float distance_squared = 0.0;
float alpha_threshold = 0.0;
float dot_threshold = -1.0;
const float circle_radius = 0.5;
const float square_radius = 0.5 / sqrt(2.0 / M_PI) * M_SQRT1_2;
const float diamond_radius = 0.5 / sqrt(2.0 / M_PI) * M_SQRT1_2;
const float corner_rounding = 0.0;
switch (finalShape) {
default:
case SOCK_DISPLAY_SHAPE_CIRCLE: {
distance_squared = dot(co, co);
alpha_threshold = circle_radius;
break;
}
case SOCK_DISPLAY_SHAPE_CIRCLE_DOT: {
distance_squared = dot(co, co);
alpha_threshold = circle_radius;
dot_threshold = finalDotRadius;
break;
}
case SOCK_DISPLAY_SHAPE_SQUARE: {
float square_radius = square_radius - corner_rounding;
distance_squared = square_sdf(co, square_radius, square_radius);
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_SQUARE_DOT: {
float square_radius = square_radius - corner_rounding;
distance_squared = square_sdf(co, square_radius, square_radius);
alpha_threshold = corner_rounding;
dot_threshold = finalDotRadius;
break;
}
case SOCK_DISPLAY_SHAPE_DIAMOND: {
float diamond_radius = diamond_radius - corner_rounding;
distance_squared = square_sdf(abs(rotate_45(co)), diamond_radius, diamond_radius);
alpha_threshold = corner_rounding;
break;
}
case SOCK_DISPLAY_SHAPE_DIAMOND_DOT: {
float diamond_radius = diamond_radius - corner_rounding;
distance_squared = square_sdf(abs(rotate_45(co)), diamond_radius, diamond_radius);
alpha_threshold = corner_rounding;
dot_threshold = finalDotRadius;
break;
}
}
vec2 alpha_thresholds = calculate_thresholds(alpha_threshold);
vec2 outline_thresholds = calculate_thresholds(alpha_threshold - finalOutlineThickness);
vec2 dot_thresholds = calculate_thresholds(dot_threshold);
float alpha_mask = smoothstep(alpha_thresholds[1], alpha_thresholds[0], distance_squared);
float dot_mask = smoothstep(dot_thresholds[1], dot_thresholds[0], dot(co, co));
float outline_mask = smoothstep(outline_thresholds[0], outline_thresholds[1], distance_squared) +
dot_mask;
fragColor = mix(finalColor, finalOutlineColor, outline_mask);
fragColor.a *= alpha_mask;
}

View File

@@ -0,0 +1,70 @@
/* SPDX-FileCopyrightText: 2018-2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "infos/gpu_shader_2D_node_socket_info.hh"
#include "gpu_shader_math_base_lib.glsl"
VERTEX_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
#define rect parameters[widgetID * MAX_SOCKET_PARAMETERS + 0]
#define colorInner parameters[widgetID * MAX_SOCKET_PARAMETERS + 1]
#define colorOutline parameters[widgetID * MAX_SOCKET_PARAMETERS + 2]
#define outlineThickness parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].x
#define outlineOffset parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].y
#define shape parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].z
#define aspect parameters[widgetID * MAX_SOCKET_PARAMETERS + 3].w
void main()
{
/* Scale the original rectangle to accomodate the diagonal of the diamond shape. */
vec2 originalRectSize = rect.yw - rect.xz;
float offset = 0.125 * min(originalRectSize.x, originalRectSize.y) +
outlineOffset * outlineThickness;
vec2 ofs = vec2(offset, -offset);
vec2 pos;
switch (gl_VertexID) {
default:
case 0: {
pos = rect.xz + ofs.yy;
break;
}
case 1: {
pos = rect.xw + ofs.yx;
break;
}
case 2: {
pos = rect.yz + ofs.xy;
break;
}
case 3: {
pos = rect.yw + ofs.xx;
break;
}
}
gl_Position = ModelViewProjectionMatrix * vec4(pos, 0.0, 1.0);
vec2 rectSize = rect.yw - rect.xz + 2.0 * vec2(outlineOffset, outlineOffset);
float minSize = min(rectSize.x, rectSize.y);
vec2 centeredCoordinates = pos - ((rect.xz + rect.yw) / 2.0);
uv = centeredCoordinates / minSize;
/* Calculate the necessary "extrusion" of the coordinates to draw the middle part of
* multi sockets. */
float ratio = rectSize.x / rectSize.y;
extrusion = (ratio > 1.0) ? vec2((ratio - 1.0) / 2.0, 0.0) :
vec2(0.0, ((1.0 / ratio) - 1.0) / 2.0);
/* Shape parameters. */
finalShape = int(shape);
finalOutlineThickness = outlineThickness / minSize;
finalDotRadius = outlineThickness / minSize;
AAsize = 1.0 * aspect / minSize;
/* Pass through parameters. */
finalColor = colorInner;
finalOutlineColor = colorOutline;
}

View File

@@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup gpu
*/
#ifdef GPU_SHADER
# pragma once
# include "gpu_glsl_cpp_stubs.hh"
# define widgetID 0
#endif
#include "gpu_shader_create_info.hh"
GPU_SHADER_INTERFACE_INFO(gpu_node_socket_iface)
FLAT(VEC4, finalColor)
FLAT(VEC4, finalOutlineColor)
FLAT(FLOAT, finalDotRadius)
FLAT(FLOAT, finalOutlineThickness)
FLAT(FLOAT, AAsize)
FLAT(VEC2, extrusion)
FLAT(INT, finalShape)
SMOOTH(VEC2, uv)
GPU_SHADER_INTERFACE_END()
/* TODO(lone_noel): Share with C code. */
#define MAX_SOCKET_PARAMETERS 4
#define MAX_SOCKET_INSTANCE 32
GPU_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_shared)
DEFINE_VALUE("MAX_SOCKET_PARAMETERS", STRINGIFY(MAX_SOCKET_PARAMETERS))
PUSH_CONSTANT(MAT4, ModelViewProjectionMatrix)
VERTEX_OUT(gpu_node_socket_iface)
FRAGMENT_OUT(0, VEC4, fragColor)
VERTEX_SOURCE("gpu_shader_2D_node_socket_vert.glsl")
FRAGMENT_SOURCE("gpu_shader_2D_node_socket_frag.glsl")
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_2D_node_socket)
DO_STATIC_COMPILATION()
/* gl_InstanceID is supposed to be 0 if not drawing instances, but this seems
* to be violated in some drivers. For example, macOS 10.15.4 and Intel Iris
* causes #78307 when using gl_InstanceID outside of instance. */
DEFINE_VALUE("widgetID", "0")
PUSH_CONSTANT_ARRAY(VEC4, parameters, MAX_SOCKET_PARAMETERS)
ADDITIONAL_INFO(gpu_shader_2D_node_socket_shared)
GPU_SHADER_CREATE_END()
GPU_SHADER_CREATE_INFO(gpu_shader_2D_node_socket_inst)
DO_STATIC_COMPILATION()
DEFINE_VALUE("widgetID", "gl_InstanceID")
PUSH_CONSTANT_ARRAY(VEC4, parameters, (MAX_SOCKET_PARAMETERS * MAX_SOCKET_INSTANCE))
ADDITIONAL_INFO(gpu_shader_2D_node_socket_shared)
GPU_SHADER_CREATE_END()