UI: Improve menu dropshadow #111794

Merged
Harley Acheson merged 7 commits from lone_noel/blender:menu-dropshadow into main 2023-11-24 22:50:32 +01:00
9 changed files with 82 additions and 256 deletions

View File

@ -493,7 +493,8 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect,
int UI_draw_roundbox_corner_get();
#endif
void UI_draw_box_shadow(const rctf *rect, unsigned char alpha);
void ui_draw_dropshadow(const rctf *rct, float radius, float width, float aspect, float alpha);
void UI_draw_text_underline(int pos_x, int pos_y, int len, int height, const float color[4]);
/**

View File

@ -2178,108 +2178,20 @@ void ui_draw_but_TRACKPREVIEW(ARegion * /*region*/,
/* ****************************************************** */
/* TODO(merwin): high quality UI drop shadows using GLSL shader and single draw call
* would replace / modify the following 3 functions. */
static void ui_shadowbox(const rctf *rect, uint pos, uint color, float shadsize, uchar alpha)
void ui_draw_dropshadow(
const rctf *rct, const float radius, const float width, const float aspect, const float alpha)
{
/**
* <pre>
* v1-_
* | -_v2
* | |
* | |
* | |
* v7_______v3____v4
* \ | /
* \ | _v5
* v8______v6_-
* </pre>
*/
const float v1[2] = {rect->xmax, rect->ymax - 0.3f * shadsize};
const float v2[2] = {rect->xmax + shadsize, rect->ymax - 0.75f * shadsize};
const float v3[2] = {rect->xmax, rect->ymin};
const float v4[2] = {rect->xmax + shadsize, rect->ymin};
if (width == 0.0f) {
return;
}
const float v5[2] = {rect->xmax + 0.7f * shadsize, rect->ymin - 0.7f * shadsize};
const float v6[2] = {rect->xmax, rect->ymin - shadsize};
const float v7[2] = {rect->xmin + 0.3f * shadsize, rect->ymin};
const float v8[2] = {rect->xmin + 0.5f * shadsize, rect->ymin - shadsize};
/* right quad */
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
immVertex2fv(pos, v1);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v2);
immVertex2fv(pos, v2);
immVertex2fv(pos, v4);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
/* corner shape */
// immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v4);
immVertex2fv(pos, v5);
immVertex2fv(pos, v5);
immVertex2fv(pos, v6);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v3);
/* bottom quad */
// immAttr4ub(color, 0, 0, 0, alpha); /* Not needed, done above in previous tri. */
immVertex2fv(pos, v3);
immAttr4ub(color, 0, 0, 0, 0);
immVertex2fv(pos, v6);
immVertex2fv(pos, v8);
immVertex2fv(pos, v8);
immAttr4ub(color, 0, 0, 0, alpha);
immVertex2fv(pos, v7);
immVertex2fv(pos, v3);
}
void UI_draw_box_shadow(const rctf *rect, uchar alpha)
{
GPU_blend(GPU_BLEND_ALPHA);
GPUVertFormat *format = immVertexFormat();
const uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
uint color = GPU_vertformat_attr_add(
format, "color", GPU_COMP_U8, 4, GPU_FETCH_INT_TO_FLOAT_UNIT);
immBindBuiltinProgram(GPU_SHADER_3D_SMOOTH_COLOR);
immBegin(GPU_PRIM_TRIS, 54);
/* accumulated outline boxes to make shade not linear, is more pleasant */
ui_shadowbox(rect, pos, color, 11.0, (20 * alpha) >> 8);
ui_shadowbox(rect, pos, color, 7.0, (40 * alpha) >> 8);
ui_shadowbox(rect, pos, color, 5.0, (80 * alpha) >> 8);
immEnd();
immUnbindProgram();
GPU_blend(GPU_BLEND_NONE);
}
void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int /*select*/)
{
/* This undoes the scale of the view for higher zoom factors to clamp the shadow size. */
const float clamped_aspect = smoothminf(aspect, 1.0f, 0.5f);
const float shadow_width = width * clamped_aspect;
const float shadow_offset = min_ff(shadow_width, BLI_rctf_size_y(rct) - 2.0f * radius);
const float shadow_softness = 0.6f * U.widget_unit * clamped_aspect;
const float shadow_offset = 0.5f * U.widget_unit * clamped_aspect;
const float shadow_alpha = 0.5f * alpha;
const float max_radius = (BLI_rctf_size_y(rct) - shadow_offset) * 0.5f;
const float rad = min_ff(radius, max_radius);
const float inner_radius = max_ff(radius - U.pixelsize, 0.0);
const float shadow_radius = radius + shadow_width - U.pixelsize;
GPU_blend(GPU_BLEND_ALPHA);
@ -2287,13 +2199,13 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
widget_params.recti.xmin = rct->xmin;
widget_params.recti.ymin = rct->ymin;
widget_params.recti.xmax = rct->xmax;
widget_params.recti.ymax = rct->ymax - shadow_offset;
widget_params.rect.xmin = rct->xmin - shadow_softness;
widget_params.rect.ymin = rct->ymin - shadow_softness;
widget_params.rect.xmax = rct->xmax + shadow_softness;
widget_params.rect.ymax = rct->ymax - shadow_offset + shadow_softness;
widget_params.radi = rad;
widget_params.rad = rad + shadow_softness;
widget_params.recti.ymax = rct->ymax;
widget_params.rect.xmin = rct->xmin - shadow_width;
widget_params.rect.ymin = rct->ymin - shadow_width;
widget_params.rect.xmax = rct->xmax + shadow_width;
widget_params.rect.ymax = rct->ymax + shadow_width - shadow_offset;
widget_params.radi = inner_radius;
widget_params.rad = shadow_radius;
widget_params.round_corners[0] = (roundboxtype & UI_CNR_BOTTOM_LEFT) ? 1.0f : 0.0f;
widget_params.round_corners[1] = (roundboxtype & UI_CNR_BOTTOM_RIGHT) ? 1.0f : 0.0f;
widget_params.round_corners[2] = (roundboxtype & UI_CNR_TOP_RIGHT) ? 1.0f : 0.0f;
@ -2303,17 +2215,8 @@ void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha
GPUBatch *batch = ui_batch_roundbox_shadow_get();
GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW);
GPU_batch_uniform_4fv_array(batch, "parameters", 4, (const float(*)[4]) & widget_params);
GPU_batch_uniform_1f(batch, "alpha", shadow_alpha);
GPU_batch_uniform_1f(batch, "alpha", alpha);
GPU_batch_draw(batch);
/* outline emphasis */
const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
rctf rect{};
rect.xmin = rct->xmin - 0.5f;
rect.xmax = rct->xmax + 0.5f;
rect.ymin = rct->ymin - 0.5f;
rect.ymax = rct->ymax + 0.5f;
UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
GPU_blend(GPU_BLEND_NONE);
}

View File

@ -1028,10 +1028,6 @@ void ui_draw_aligned_panel(const uiStyle *style,
bool region_search_filter_active);
void ui_panel_tag_search_filter_match(Panel *panel);
/* interface_draw.cc */
void ui_draw_dropshadow(const rctf *rct, float radius, float aspect, float alpha, int select);
/**
* Draws in resolution of 48x4 colors.
*/

View File

@ -599,86 +599,6 @@ static void widget_init(uiWidgetBase *wtb)
/** \name Draw Round Box
* \{ */
/* helper call, makes shadow rect, with 'sun' above menu, so only shadow to left/right/bottom */
/* return tot */
static int round_box_shadow_edges(
float (*vert)[2], const rcti *rect, float rad, int roundboxalign, float step)
{
float vec[WIDGET_CURVE_RESOLU][2];
int tot = 0;
rad += step;
if (2.0f * rad > BLI_rcti_size_y(rect)) {
rad = 0.5f * BLI_rcti_size_y(rect);
}
const float minx = rect->xmin - step;
const float miny = rect->ymin - step;
const float maxx = rect->xmax + step;
const float maxy = rect->ymax + step;
/* Multiply. */
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++) {
vec[a][0] = rad * cornervec[a][0];
vec[a][1] = rad * cornervec[a][1];
}
/* start with left-top, anti clockwise */
if (roundboxalign & UI_CNR_TOP_LEFT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx + rad - vec[a][0];
vert[tot][1] = maxy - vec[a][1];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx;
vert[tot][1] = maxy;
}
}
if (roundboxalign & UI_CNR_BOTTOM_LEFT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx + vec[a][1];
vert[tot][1] = miny + rad - vec[a][0];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = minx;
vert[tot][1] = miny;
}
}
if (roundboxalign & UI_CNR_BOTTOM_RIGHT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx - rad + vec[a][0];
vert[tot][1] = miny + vec[a][1];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx;
vert[tot][1] = miny;
}
}
if (roundboxalign & UI_CNR_TOP_RIGHT) {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx - vec[a][1];
vert[tot][1] = maxy - rad + vec[a][0];
}
}
else {
for (int a = 0; a < WIDGET_CURVE_RESOLU; a++, tot++) {
vert[tot][0] = maxx;
vert[tot][1] = maxy;
}
}
return tot;
}
/* this call has 1 extra arg to allow mask outline */
static void round_box__edges(
uiWidgetBase *wt, int roundboxalign, const rcti *rect, float rad, float radi)
@ -2828,54 +2748,18 @@ static void widget_state_menu_item(uiWidgetType *wt,
/* outside of rect, rad to left/bottom/right */
static void widget_softshadow(const rcti *rect, int roundboxalign, const float radin)
{
bTheme *btheme = UI_GetTheme();
uiWidgetBase wtb;
rcti rect1 = *rect;
float triangle_strip[WIDGET_SIZE_MAX * 2 + 2][2];
const float radout = UI_ThemeMenuShadowWidth();
const float outline = U.pixelsize;
/* disabled shadow */
if (radout == 0.0f) {
return;
}
rctf shadow_rect;
BLI_rctf_rcti_copy(&shadow_rect, rect);
BLI_rctf_pad(&shadow_rect, -outline, -outline);
/* prevent tooltips to not show round shadow */
if (radout > 0.2f * BLI_rcti_size_y(&rect1)) {
rect1.ymax -= 0.2f * BLI_rcti_size_y(&rect1);
}
else {
rect1.ymax -= radout;
}
UI_draw_roundbox_corner_set(roundboxalign);
/* inner part */
const int totvert = round_box_shadow_edges(wtb.inner_v,
&rect1,
radin,
roundboxalign &
(UI_CNR_BOTTOM_RIGHT | UI_CNR_BOTTOM_LEFT),
0.0f);
const float shadow_alpha = UI_GetTheme()->tui.menu_shadow_fac;
const float shadow_width = UI_ThemeMenuShadowWidth();
/* we draw a number of increasing size alpha quad strips */
const float alphastep = 3.0f * btheme->tui.menu_shadow_fac / radout;
const uint pos = GPU_vertformat_attr_add(
immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
for (int step = 1; step <= int(radout); step++) {
const float expfac = sqrtf(step / radout);
round_box_shadow_edges(wtb.outer_v, &rect1, radin, UI_CNR_ALL, float(step));
immUniformColor4f(0.0f, 0.0f, 0.0f, alphastep * (1.0f - expfac));
widget_verts_to_triangle_strip(&wtb, totvert, triangle_strip);
widget_draw_vertex_buffer(pos, 0, GPU_PRIM_TRI_STRIP, triangle_strip, nullptr, totvert * 2);
}
immUnbindProgram();
ui_draw_dropshadow(&shadow_rect, radin, shadow_width, 1.0f, shadow_alpha);
}
static void widget_menu_back(
@ -5534,9 +5418,7 @@ static void ui_draw_widget_back_color(uiWidgetTypeEnum type,
uiWidgetType *wt = widget_type(type);
if (use_shadow) {
GPU_blend(GPU_BLEND_ALPHA);
widget_softshadow(rect, UI_CNR_ALL, 0.25f * U.widget_unit);
GPU_blend(GPU_BLEND_NONE);
}
rcti rect_copy = *rect;

View File

@ -91,8 +91,6 @@
#include "GEO_fillet_curves.hh"
#include "../interface/interface_intern.hh" /* TODO: Remove */
#include "node_intern.hh" /* own include */
#include <fmt/format.h>
@ -1857,7 +1855,20 @@ static void node_draw_shadow(const SpaceNode &snode,
{
const rctf &rct = node.runtime->totr;
UI_draw_roundbox_corner_set(UI_CNR_ALL);
ui_draw_dropshadow(&rct, radius, snode.runtime->aspect, alpha, node.flag & SELECT);
const float shadow_width = 0.6f * U.widget_unit;
const float shadow_alpha = 0.5f * alpha;
ui_draw_dropshadow(&rct, radius, shadow_width, snode.runtime->aspect, shadow_alpha);
/* Outline emphasis. Slight darkening _inside_ the outline. */
const float color[4] = {0.0f, 0.0f, 0.0f, 0.4f};
rctf rect{};
rect.xmin = rct.xmin - 0.5f;
rect.xmax = rct.xmax + 0.5f;
rect.ymin = rct.ymin - 0.5f;
rect.ymax = rct.ymax + 0.5f;
UI_draw_roundbox_4fv(&rect, false, radius + 0.5f, color);
}
static void node_draw_sockets(const View2D &v2d,

View File

@ -1056,7 +1056,7 @@ static void draw_suggestion_list(const SpaceText *st, const TextDrawContext *tdc
rect.xmax = x + boxw;
rect.ymin = y - boxh;
rect.ymax = y;
UI_draw_box_shadow(&rect, 220);
ui_draw_dropshadow(&rect, 0.0f, 8.0f, 1.0f, 0.5f);
}
uint pos = GPU_vertformat_attr_add(

View File

@ -6,5 +6,8 @@ void main()
{
fragColor = vec4(0.0);
/* Manual curve fit of the falloff curve of previous drawing method. */
fragColor.a = alpha * (shadowFalloff * shadowFalloff * 0.722 + shadowFalloff * 0.277);
float shadow_alpha = alpha * (shadowFalloff * shadowFalloff * 0.722 + shadowFalloff * 0.277);
float inner_alpha = smoothstep(0.0, 0.05, innerMask);
fragColor.a = inner_alpha * shadow_alpha;
}

View File

@ -67,17 +67,41 @@ void main()
vec2(0.02, -0.805),
vec2(0.0, -1.0));
const vec2 center_offset[4] = vec2[4](
vec2(1.0, 1.0), vec2(-1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0));
uint cflag = vflag & CNR_FLAG_RANGE;
uint vofs = (vflag >> CORNER_VEC_OFS) & CORNER_VEC_RANGE;
vec2 v = cornervec[cflag * 9u + vofs];
bool is_inner = (vflag & INNER_FLAG) != 0u;
shadowFalloff = (is_inner) ? 1.0 : 0.0;
float shadow_width = rads - radsi;
float shadow_width_top = rect.w - recti.w;
/* Scale by corner radius */
v *= roundCorners[cflag] * ((is_inner) ? radsi : rads);
float rad_inner = radsi * roundCorners[cflag];
float rad_outer = rad_inner + shadow_width;
float radius = (is_inner) ? rad_inner : rad_outer;
float shadow_offset = (is_inner && (cflag > BOTTOM_RIGHT)) ? (shadow_width - shadow_width_top) :
0.0;
vec2 c = center_offset[cflag];
vec2 center_outer = rad_outer * c;
vec2 center = radius * c;
/* First expand all vertices to the outer shadow border. */
vec2 v = rad_outer * cornervec[cflag * 9u + vofs];
/* Now shrink the inner vertices onto the inner rectangle.
* At the top corners we keep the vertical offset to distribute a few of the vertices along the
* straight part of the rectangle. This allows us to get a better falloff at the top. */
if (is_inner && (cflag > BOTTOM_RIGHT) && (v.y < (shadow_offset - rad_outer))) {
v.y += shadow_width_top;
v.x = 0.0;
}
else {
v = radius * normalize(v - (center_outer + vec2(0.0, shadow_offset))) + center;
}
/* Position to corner */
vec4 rct = (is_inner) ? recti : rect;
@ -94,5 +118,9 @@ void main()
v += rct.xw;
}
float inner_shadow_strength = min((rect.w - v.y) / rad_outer + 0.1, 1.0);
shadowFalloff = (is_inner) ? inner_shadow_strength : 0.0;
innerMask = (is_inner) ? 0.0 : 1.0;
gl_Position = ModelViewProjectionMatrix * vec4(v, 0.0, 1.0);
}

View File

@ -49,7 +49,9 @@ GPU_SHADER_CREATE_INFO(gpu_shader_2D_widget_base_inst)
.push_constant(Type::VEC4, "parameters", (MAX_PARAM * MAX_INSTANCE))
.additional_info("gpu_shader_2D_widget_shared");
GPU_SHADER_INTERFACE_INFO(gpu_widget_shadow_iface, "").smooth(Type::FLOAT, "shadowFalloff");
GPU_SHADER_INTERFACE_INFO(gpu_widget_shadow_iface, "")
.smooth(Type::FLOAT, "shadowFalloff")
.smooth(Type::FLOAT, "innerMask");
GPU_SHADER_CREATE_INFO(gpu_shader_2D_widget_shadow)
.do_static_compilation(true)