From 6166dac3ee9b17a7b1b2cd4ea9fe0c73faccc459 Mon Sep 17 00:00:00 2001 From: Weizhen Huang Date: Fri, 24 Feb 2023 22:54:24 +0100 Subject: [PATCH] Gizmo: support scaling of light gizmos with zero size Zero-sized gizmos were not shown because GPU does not draw filled triangles with zero area. This problem is resolved by drawing lines or points in these degenerated cases. Scaling is supported via computing the original and the new size, then treat the case with zero original size separately, instead of only computing the scale itself. Actually applies to all `cage2d` gizmos, but not compatible when translate flag is set. --- .../gizmo_library/gizmo_types/cage2d_gizmo.c | 159 +++++++++--------- .../editors/space_view3d/view3d_gizmo_light.c | 3 +- 2 files changed, 82 insertions(+), 80 deletions(-) diff --git a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c index 436a2a676e3..621cc469e73 100644 --- a/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c +++ b/source/blender/editors/gizmo_library/gizmo_types/cage2d_gizmo.c @@ -16,6 +16,7 @@ #include "BLI_dial_2d.h" #include "BLI_math.h" +#include "BLI_math_base_safe.h" #include "BLI_rect.h" #include "BKE_context.h" @@ -46,7 +47,7 @@ static int gizmo_cage2d_transform_flag_get(const wmGizmo *gz); -static bool gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[2], float scale[2]) +static void gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[2], float scale[2]) { float matrix_final_no_offset[4][4]; float asp[2] = {1.0f, 1.0f}; @@ -67,29 +68,21 @@ static bool gizmo_calc_rect_view_scale(const wmGizmo *gz, const float dims[2], f float len_x_axis = len_v3(x_axis); float len_y_axis = len_v3(y_axis); - if (len_x_axis == 0.0f || len_y_axis == 0.0f) { - return false; - } - - scale[0] = 1.0f / len_x_axis; - scale[1] = 1.0f / len_y_axis; - return true; + /* Set scale to zero if axis length is zero. */ + scale[0] = safe_divide(1.0f, len_x_axis); + scale[1] = safe_divide(1.0f, len_y_axis); } -static bool gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2], float margin[2]) +static void gizmo_calc_rect_view_margin(const wmGizmo *gz, const float dims[2], float margin[2]) { float handle_size; handle_size = 0.15f; handle_size *= gz->scale_final; float scale_xy[2]; - if (!gizmo_calc_rect_view_scale(gz, dims, scale_xy)) { - zero_v2(margin); - return false; - } + gizmo_calc_rect_view_scale(gz, dims, scale_xy); margin[0] = (handle_size * scale_xy[0]); margin[1] = (handle_size * scale_xy[1]); - return true; } /* -------------------------------------------------------------------- */ @@ -396,7 +389,17 @@ static void cage2d_draw_box_interaction(const float color[4], { if (is_solid) { - BLI_assert(ELEM(prim_type, GPU_PRIM_TRI_FAN)); + + if (margin[0] == 0.0f && margin[1] == 0.0) { + prim_type = GPU_PRIM_POINTS; + } + else if (margin[0] == 0.0f || margin[1] == 0.0) { + prim_type = GPU_PRIM_LINE_STRIP; + } + else { + BLI_assert(ELEM(prim_type, GPU_PRIM_TRI_FAN)); + } + immBegin(prim_type, verts_len); immAttr3f(attr_id.col, 0.0f, 0.0f, 0.0f); for (uint i = 0; i < verts_len; i++) { @@ -445,6 +448,18 @@ static void cage2d_draw_box_interaction(const float color[4], static void imm_draw_point_aspect_2d( uint pos, float x, float y, float rad_x, float rad_y, bool solid) { + if (rad_x == 0 && rad_y == 0) { + immBegin(GPU_PRIM_POINTS, 1); + immVertex2f(pos, x, y); + immEnd(); + return; + } + + if (rad_x == 0 || rad_y == 0) { + /* Do not draw anything if only on of the radii is zero. */ + return; + } + if (solid) { /* NOTE(Metal/AMD): Small Triangle-list primitives more optimal for GPU HW than Triangle-strip. */ @@ -539,20 +554,28 @@ static void cage2d_draw_rect_wire(const rctf *r, static void cage2d_draw_circle_wire(const float color[3], const float size[2], - const float margin, + const float margin[2], const float line_width) { uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); + immBindBuiltinProgram(is_zero_v2(margin) ? GPU_SHADER_3D_UNIFORM_COLOR : + GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniformColor3fv(color); - float viewport[4]; - GPU_viewport_size_get_f(viewport); - immUniform2fv("viewportSize", &viewport[2]); - immUniform1f("lineWidth", line_width * U.pixelsize + margin); - - imm_draw_circle_wire_aspect_3d(pos, 0.0f, 0.0f, size[0], size[1], CIRCLE_RESOL); + if (is_zero_v2(margin)) { + /* Draw a central point. */ + immBegin(GPU_PRIM_POINTS, 1); + immVertex3f(pos, 0.0f, 0.0f, 0.0f); + immEnd(); + } + else { + float viewport[4]; + GPU_viewport_size_get_f(viewport); + immUniform2fv("viewportSize", &viewport[2]); + immUniform1f("lineWidth", line_width * U.pixelsize); + imm_draw_circle_wire_aspect_3d(pos, 0.0f, 0.0f, size[0], size[1], CIRCLE_RESOL); + } immUnbindProgram(); } @@ -592,20 +615,6 @@ static void cage2d_draw_rect_handles(const rctf *r, immUnbindProgram(); } -static void cage2d_draw_central_handle(const float color[3], const float margin[2]) -{ - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); - - const float rad[2] = {margin[0] * 0.25f, margin[1] * 0.25f}; - - immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - immUniformColor3fv(color); - - imm_draw_circle_fill_aspect_3d(pos, 0.0f, 0.0f, rad[0], rad[1], CIRCLE_RESOL); - - immUnbindProgram(); -} - /** \} */ static void gizmo_cage2d_draw_intern(wmGizmo *gz, @@ -650,10 +659,8 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) { /* Only scaling is needed for now. */ GPU_select_load_id(select_id | ED_GIZMO_CAGE2D_PART_SCALE); - cage2d_draw_circle_wire( - gz->color, size_real, 0.5f * (margin[0] + margin[1]), gz->line_width); - cage2d_draw_central_handle(gz->color, margin); + cage2d_draw_circle_wire(gz->color, size_real, margin, gz->line_width); } else { if (transform_flag & ED_GIZMO_CAGE_XFORM_FLAG_SCALE) { @@ -747,15 +754,13 @@ static void gizmo_cage2d_draw_intern(wmGizmo *gz, cage2d_draw_rect_wire(&r, margin, black, transform_flag, draw_options, outline_line_width); cage2d_draw_rect_wire(&r, margin, color, transform_flag, draw_options, gz->line_width); - /* corner gizmos */ - cage2d_draw_rect_handles(&r, margin, color, transform_flag, true); + /* Corner gizmos. */ cage2d_draw_rect_handles(&r, margin, black, transform_flag, false); + cage2d_draw_rect_handles(&r, margin, color, transform_flag, true); } else if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) { - cage2d_draw_circle_wire(black, size_real, 0.0f, outline_line_width); - cage2d_draw_circle_wire(color, size_real, 0.0f, gz->line_width); - - cage2d_draw_central_handle(color, margin); + cage2d_draw_circle_wire(black, size_real, margin, outline_line_width); + cage2d_draw_circle_wire(color, size_real, margin, gz->line_width); } else { BLI_assert(0); @@ -826,9 +831,7 @@ static int gizmo_cage2d_test_select(bContext *C, wmGizmo *gz, const int mval[2]) } float margin[2]; - if (!gizmo_calc_rect_view_margin(gz, dims, margin)) { - return -1; - } + gizmo_calc_rect_view_margin(gz, dims, margin); /* Expand for hots-pot. */ const float size[2] = {size_real[0] + margin[0] / 2, size_real[1] + margin[1] / 2}; @@ -1035,14 +1038,6 @@ static int gizmo_cage2d_modal(bContext *C, const wmEvent *event, eWM_GizmoFlagTweak UNUSED(tweak_flag)) { - /* For transform logic to be manageable we operate in -0.5..0.5 2D space, - * no matter the size of the rectangle, mouse coords are scaled to unit space. - * The mouse coords have been projected into the matrix - * so we don't need to worry about axis alignment. - * - * - The cursor offset are multiplied by 'dims'. - * - Matrix translation is also multiplied by 'dims'. - */ RectTransformInteraction *data = gz->interaction_data; int transform_flag = RNA_enum_get(gz->ptr, "transform"); if ((transform_flag & ED_GIZMO_CAGE_XFORM_FLAG_SCALE_UNIFORM) == 0) { @@ -1073,6 +1068,8 @@ static int gizmo_cage2d_modal(bContext *C, copy_m4_m4(matrix_back, gz->matrix_offset); copy_m4_m4(gz->matrix_offset, data->orig_matrix_offset); + /* The mouse coords are projected into the matrix so we don't need to worry about axis + * alignment. */ bool ok = gizmo_window_project_2d( C, gz, (const float[2]){UNPACK2(event->mval)}, 2, false, point_local); copy_m4_m4(gz->matrix_offset, matrix_back); @@ -1145,6 +1142,7 @@ static int gizmo_cage2d_modal(bContext *C, float pivot[2]; if (transform_flag & ED_GIZMO_CAGE_XFORM_FLAG_TRANSLATE) { gizmo_pivot_from_scale_part(gz->highlight_part, pivot); + mul_v2_v2(pivot, dims); } else { zero_v2(pivot); @@ -1153,27 +1151,26 @@ static int gizmo_cage2d_modal(bContext *C, bool constrain_axis[2] = {false}; gizmo_constrain_from_scale_part(gz->highlight_part, constrain_axis); - float scale[2] = {1.0f, 1.0f}; + float size_new[2], size_orig[2]; for (int i = 0; i < 2; i++) { + size_orig[i] = len_v3(data->orig_matrix_offset[i]); + size_new[i] = size_orig[i]; if (constrain_axis[i] == false) { - /* Original cursor position relative to pivot, remapped to [-1, 1] */ - const float delta_orig = (data->orig_mouse[i] - data->orig_matrix_offset[3][i]) / - (dims[i] * len_v3(data->orig_matrix_offset[i])) - - pivot[i]; - const float delta_curr = (point_local[i] - data->orig_matrix_offset[3][i]) / - (dims[i] * len_v3(data->orig_matrix_offset[i])) - - pivot[i]; + /* Original cursor position relative to pivot. */ + const float delta_orig = data->orig_mouse[i] - data->orig_matrix_offset[3][i] - + pivot[i] * size_orig[i]; + const float delta_curr = point_local[i] - data->orig_matrix_offset[3][i] - + pivot[i] * size_orig[i]; if ((transform_flag & ED_GIZMO_CAGE_XFORM_FLAG_SCALE_SIGNED) == 0) { if (signum_i(delta_orig) != signum_i(delta_curr)) { - scale[i] = 0.0f; + size_new[i] = 0.0f; continue; } } /* Original cursor position does not exactly lie on the cage boundary due to margin. */ - const float delta_boundary = signf(delta_orig) * 0.5f - pivot[i]; - scale[i] = delta_curr / delta_boundary; + size_new[i] = delta_curr / (signf(delta_orig) * 0.5f * dims[i] - pivot[i]); } } @@ -1181,33 +1178,39 @@ static int gizmo_cage2d_modal(bContext *C, if (constrain_axis[0] == false && constrain_axis[1] == false) { if (draw_style == ED_GIZMO_CAGE2D_STYLE_CIRCLE) { /* So that the cursor lies on the circle. */ - scale[1] = scale[0] = len_v2(scale); + size_new[1] = size_new[0] = len_v2(size_new); } else { - scale[1] = scale[0] = (scale[1] + scale[0]) / 2.0f; + size_new[1] = size_new[0] = (size_new[1] + size_new[0]) / 2.0f; } } else if (constrain_axis[0] == false) { - scale[1] = scale[0]; + size_new[1] = size_new[0]; } else if (constrain_axis[1] == false) { - scale[0] = scale[1]; + size_new[0] = size_new[1]; } else { BLI_assert(0); } } - /* scale around pivot */ + /* Scale around pivot. */ + float scale[2]; float matrix_scale[4][4]; unit_m4(matrix_scale); - mul_v3_fl(matrix_scale[0], scale[0]); - mul_v3_fl(matrix_scale[1], scale[1]); + for (int i = 0; i < 2; i++) { + if (size_orig[i] == 0) { + size_orig[i] = 1.0f; + gz->matrix_offset[i][i] = 1.0f; + } + scale[i] = size_new[i] / size_orig[i]; + mul_v3_fl(matrix_scale[i], scale[i]); + } - transform_pivot_set_m4(matrix_scale, - (const float[3]){pivot[0] * dims[0], pivot[1] * dims[1], 0.0f}); - mul_m4_m4m4(gz->matrix_offset, data->orig_matrix_offset, matrix_scale); + transform_pivot_set_m4(matrix_scale, (const float[3]){UNPACK2(pivot), 0.0f}); + mul_m4_m4_post(gz->matrix_offset, matrix_scale); } if (gz_prop->type != NULL) { diff --git a/source/blender/editors/space_view3d/view3d_gizmo_light.c b/source/blender/editors/space_view3d/view3d_gizmo_light.c index f3db63b9a20..d7486074f79 100644 --- a/source/blender/editors/space_view3d/view3d_gizmo_light.c +++ b/source/blender/editors/space_view3d/view3d_gizmo_light.c @@ -108,8 +108,7 @@ static void gizmo_light_radius_prop_matrix_get(const wmGizmo *UNUSED(gz), BKE_view_layer_synced_ensure(CTX_data_scene(C), view_layer); const Light *la = BKE_view_layer_active_object_get(view_layer)->data; - /* Draw gizmo even when radius is zero. */ - const float diameter = fmaxf(2.0f * la->radius, 1e-2f); + const float diameter = 2.0f * la->radius; matrix[0][0] = diameter; matrix[1][1] = diameter; }