/* * 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_immediate.h" #include "GPU_immediate_util.h" #include "GPU_matrix.h" #include "GPU_select.h" #include "GPU_batch.h" #include "GPU_batch_utils.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_screen.h" #include "ED_view3d.h" #include "ED_gizmo_library.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 bool select) { GPU_line_width(gz->line_width); GPUVertFormat *format = immVertexFormat(); uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformColor4fv(color); /* TODO, other draw styles */ imm_draw_circle_fill_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; 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"); 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_UNIFORM_COLOR); immUniformColor4fv(color); GPU_line_width(gz->line_width); 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, select); } else { GPU_blend(true); if (button->shape_batch[0] != NULL) { GPU_line_smooth(true); GPU_polygon_smooth(false); GPU_line_width(1.0f); for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) { /* Invert line color for wire. */ GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_2D_UNIFORM_COLOR); 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 != ICON_NONE) { if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) { button2d_geom_draw_backdrop(gz, color, select); } 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_basis * UI_DPI_FAC)); } /* 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 BC_NSEW_SCROLLCURSOR; } return CURSOR_STD; } 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", ""); } void ED_gizmotypes_button_2d(void) { WM_gizmotype_append(GIZMO_GT_button_2d); } /** \} */ // Button Gizmo API