/* * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ /** \file * \ingroup edgizmolib * * \name Button Gizmo * * 2D Gizmo, also works in 3D views. * * \brief Single click button action for use in gizmo groups. * * \note Currently only basic icon & vector-shape buttons are supported. */ #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BKE_context.h" #include "GPU_batch.h" #include "GPU_batch_utils.h" #include "GPU_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_state.h" #include "RNA_access.h" #include "RNA_define.h" #include "RNA_enum_types.h" #include "WM_api.h" #include "WM_types.h" #include "ED_gizmo_library.h" #include "ED_screen.h" #include "ED_view3d.h" #include "UI_interface.h" #include "UI_interface_icons.h" #include "UI_resources.h" /* own includes */ #include "../gizmo_geometry.h" #include "../gizmo_library_intern.h" typedef struct ButtonGizmo2D { wmGizmo gizmo; bool is_init; /* Use an icon or shape */ int icon; GPUBatch *shape_batch[2]; } ButtonGizmo2D; #define CIRCLE_RESOLUTION 32 /* -------------------------------------------------------------------- */ static void button2d_geom_draw_backdrop(const wmGizmo *gz, const float color[4], const float fill_alpha, const bool select) { float viewport[4]; GPU_viewport_size_get_f(viewport); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); /* TODO, other draw styles */ if (color[3] == 1.0 && fill_alpha == 1.0 && select == false) { immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(color); imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } else { /* Draw fill. */ if ((fill_alpha != 0.0f) || (select == true)) { const float fill_color[4] = {UNPACK3(color), fill_alpha * color[3]}; immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(fill_color); imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } /* Draw outline. */ if ((fill_alpha != 1.0f) && (select == false)) { immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION); immUnbindProgram(); } } UNUSED_VARS(select); } static void button2d_draw_intern(const bContext *C, wmGizmo *gz, const bool select, const bool highlight) { ButtonGizmo2D *button = (ButtonGizmo2D *)gz; float viewport[4]; GPU_viewport_size_get_f(viewport); const int draw_options = RNA_enum_get(gz->ptr, "draw_options"); if (button->is_init == false) { button->is_init = true; PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon"); button->icon = -1; if (RNA_property_is_set(gz->ptr, prop)) { button->icon = RNA_property_enum_get(gz->ptr, prop); } else { prop = RNA_struct_find_property(gz->ptr, "shape"); const uint polys_len = RNA_property_string_length(gz->ptr, prop); /* We shouldn't need the +1, but a NULL char is set. */ char *polys = MEM_mallocN(polys_len + 1, __func__); RNA_property_string_get(gz->ptr, prop, polys); button->shape_batch[0] = GPU_batch_tris_from_poly_2d_encoded( (uchar *)polys, polys_len, NULL); button->shape_batch[1] = GPU_batch_wire_from_poly_2d_encoded( (uchar *)polys, polys_len, NULL); MEM_freeN(polys); } } float color[4]; float matrix_final[4][4]; gizmo_color_get(gz, highlight, color); WM_gizmo_calc_matrix_final(gz, matrix_final); bool is_3d = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0; if ((select == false) && (draw_options & ED_GIZMO_BUTTON_SHOW_HELPLINE)) { float matrix_final_no_offset[4][4]; WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset); uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); immUniform2fv("viewportSize", &viewport[2]); immUniform1f("lineWidth", gz->line_width * U.pixelsize); immUniformColor4fv(color); immBegin(GPU_PRIM_LINE_STRIP, 2); immVertex3fv(pos, matrix_final[3]); immVertex3fv(pos, matrix_final_no_offset[3]); immEnd(); immUnbindProgram(); } bool need_to_pop = true; GPU_matrix_push(); GPU_matrix_mul(matrix_final); if (is_3d) { RegionView3D *rv3d = CTX_wm_region_view3d(C); float matrix_align[4][4]; float matrix_final_unit[4][4]; normalize_m4_m4(matrix_final_unit, matrix_final); mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit); zero_v3(matrix_align[3]); transpose_m4(matrix_align); GPU_matrix_mul(matrix_align); } if (select) { BLI_assert(is_3d); button2d_geom_draw_backdrop(gz, color, 1.0, select); } else { GPU_blend(true); if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { const float fill_alpha = RNA_float_get(gz->ptr, "backdrop_fill_alpha"); button2d_geom_draw_backdrop(gz, color, fill_alpha, select); } if (button->shape_batch[0] != NULL) { GPU_line_smooth(true); GPU_polygon_smooth(false); for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) { const bool do_wires = (i == 1); if (do_wires) { GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_3D_POLYLINE_UNIFORM_COLOR); GPU_batch_uniform_2fv(button->shape_batch[i], "viewportSize", &viewport[2]); GPU_batch_uniform_1f(button->shape_batch[i], "lineWidth", gz->line_width * U.pixelsize); } else { GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_2D_UNIFORM_COLOR); } /* Invert line color for wire. */ if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { /* If we have a backdrop already, * draw a contrasting shape over it instead of drawing it the same color. * Use a low value instead of 50% so some darker primary colors * aren't considered being close to black. */ float color_contrast[4]; copy_v3_fl(color_contrast, rgb_to_grayscale(color) < 0.2f ? 1 : 0); color_contrast[3] = color[3]; GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color_contrast)); } else { GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color)); } GPU_batch_draw(button->shape_batch[i]); if (draw_options & ED_GIZMO_BUTTON_SHOW_OUTLINE) { color[0] = 1.0f - color[0]; color[1] = 1.0f - color[1]; color[2] = 1.0f - color[2]; } } GPU_line_smooth(false); GPU_polygon_smooth(true); } else if (button->icon != -1) { float pos[2]; if (is_3d) { const float fac = 2.0f; GPU_matrix_translate_2f(-(fac / 2), -(fac / 2)); GPU_matrix_scale_2f(fac / (ICON_DEFAULT_WIDTH * UI_DPI_FAC), fac / (ICON_DEFAULT_HEIGHT * UI_DPI_FAC)); pos[0] = 1.0f; pos[1] = 1.0f; } else { pos[0] = gz->matrix_basis[3][0] - (ICON_DEFAULT_WIDTH / 2.0) * UI_DPI_FAC; pos[1] = gz->matrix_basis[3][1] - (ICON_DEFAULT_HEIGHT / 2.0) * UI_DPI_FAC; GPU_matrix_pop(); need_to_pop = false; } float alpha = (highlight) ? 1.0f : 0.8f; GPU_polygon_smooth(false); UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha); GPU_polygon_smooth(true); } GPU_blend(false); } if (need_to_pop) { GPU_matrix_pop(); } } static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id) { GPU_select_load_id(select_id); button2d_draw_intern(C, gz, true, false); } static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz) { const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0; GPU_blend(true); button2d_draw_intern(C, gz, false, is_highlight); GPU_blend(false); } static int gizmo_button2d_test_select(bContext *C, wmGizmo *gz, const int mval[2]) { float point_local[2]; if (0) { /* correct, but unnecessarily slow. */ if (gizmo_window_project_2d(C, gz, (const float[2]){UNPACK2(mval)}, 2, true, point_local) == false) { return -1; } } else { copy_v2_v2(point_local, (float[2]){UNPACK2(mval)}); sub_v2_v2(point_local, gz->matrix_basis[3]); mul_v2_fl(point_local, 1.0f / gz->scale_final); } /* The 'gz->scale_final' is already applied when projecting. */ if (len_squared_v2(point_local) < 1.0f) { return 0; } return -1; } static int gizmo_button2d_cursor_get(wmGizmo *gz) { if (RNA_boolean_get(gz->ptr, "show_drag")) { return WM_CURSOR_NSEW_SCROLL; } return WM_CURSOR_DEFAULT; } static void gizmo_button2d_free(wmGizmo *gz) { ButtonGizmo2D *shape = (ButtonGizmo2D *)gz; for (uint i = 0; i < ARRAY_SIZE(shape->shape_batch); i++) { GPU_BATCH_DISCARD_SAFE(shape->shape_batch[i]); } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Button Gizmo API * * \{ */ static void GIZMO_GT_button_2d(wmGizmoType *gzt) { /* identifiers */ gzt->idname = "GIZMO_GT_button_2d"; /* api callbacks */ gzt->draw = gizmo_button2d_draw; gzt->draw_select = gizmo_button2d_draw_select; gzt->test_select = gizmo_button2d_test_select; gzt->cursor_get = gizmo_button2d_cursor_get; gzt->free = gizmo_button2d_free; gzt->struct_size = sizeof(ButtonGizmo2D); /* rna */ static EnumPropertyItem rna_enum_draw_options[] = { {ED_GIZMO_BUTTON_SHOW_OUTLINE, "OUTLINE", 0, "Outline", ""}, {ED_GIZMO_BUTTON_SHOW_BACKDROP, "BACKDROP", 0, "Backdrop", ""}, {ED_GIZMO_BUTTON_SHOW_HELPLINE, "HELPLINE", 0, "Help Line", ""}, {0, NULL, 0, NULL, NULL}, }; PropertyRNA *prop; RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", ""); prop = RNA_def_property(gzt->srna, "icon", PROP_ENUM, PROP_NONE); RNA_def_property_enum_items(prop, rna_enum_icon_items); /* Passed to 'GPU_batch_tris_from_poly_2d_encoded' */ RNA_def_property(gzt->srna, "shape", PROP_STRING, PROP_BYTESTRING); /* Currently only used for cursor display. */ RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", ""); RNA_def_float(gzt->srna, "backdrop_fill_alpha", 1.0f, 0.0f, 1.0, "When below 1.0, draw the interior with a reduced alpha compared to the outline", "", 0.0f, 1.0f); } void ED_gizmotypes_button_2d(void) { WM_gizmotype_append(GIZMO_GT_button_2d); } /** \} */ // Button Gizmo API