e16deb71304defe9ed25d1c6616e9da08dfa05fa
[blender.git] / source / blender / editors / gizmo_library / gizmo_types / button2d_gizmo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file button2d_gizmo.c
22  *  \ingroup edgizmolib
23  *
24  * \name Button Gizmo
25  *
26  * 2D Gizmo, also works in 3D views.
27  *
28  * \brief Single click button action for use in gizmo groups.
29  *
30  * \note Currently only basic icon & vector-shape buttons are supported.
31  *
32  */
33
34 #include "MEM_guardedalloc.h"
35
36 #include "BLI_math.h"
37
38 #include "BKE_context.h"
39
40 #include "BIF_gl.h"
41 #include "BIF_glutil.h"
42
43 #include "GPU_immediate.h"
44 #include "GPU_immediate_util.h"
45 #include "GPU_matrix.h"
46 #include "GPU_select.h"
47 #include "GPU_batch.h"
48 #include "GPU_batch_utils.h"
49 #include "GPU_state.h"
50
51 #include "RNA_access.h"
52 #include "RNA_define.h"
53 #include "RNA_enum_types.h"
54
55 #include "WM_api.h"
56 #include "WM_types.h"
57
58 #include "ED_screen.h"
59 #include "ED_view3d.h"
60 #include "ED_gizmo_library.h"
61
62 #include "UI_interface.h"
63 #include "UI_interface_icons.h"
64 #include "UI_resources.h"
65
66 /* own includes */
67 #include "../gizmo_geometry.h"
68 #include "../gizmo_library_intern.h"
69
70 typedef struct ButtonGizmo2D {
71         wmGizmo gizmo;
72         bool is_init;
73         /* Use an icon or shape */
74         int icon;
75         GPUBatch *shape_batch[2];
76 } ButtonGizmo2D;
77
78 #define CIRCLE_RESOLUTION 32
79
80 /* -------------------------------------------------------------------- */
81
82 static void button2d_geom_draw_backdrop(
83         const wmGizmo *gz, const float color[4], const bool select)
84 {
85         GPU_line_width(gz->line_width);
86
87         GPUVertFormat *format = immVertexFormat();
88         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
89
90         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
91
92         immUniformColor4fv(color);
93
94         /* TODO, other draw styles */
95         imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, CIRCLE_RESOLUTION);
96
97         immUnbindProgram();
98
99         UNUSED_VARS(select);
100 }
101
102 static void button2d_draw_intern(
103         const bContext *C, wmGizmo *gz,
104         const bool select, const bool highlight)
105 {
106         ButtonGizmo2D *button = (ButtonGizmo2D *)gz;
107
108         const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
109         if (button->is_init == false) {
110                 button->is_init = true;
111                 PropertyRNA *prop = RNA_struct_find_property(gz->ptr, "icon");
112                 if (RNA_property_is_set(gz->ptr, prop)) {
113                         button->icon = RNA_property_enum_get(gz->ptr, prop);
114                 }
115                 else {
116                         prop = RNA_struct_find_property(gz->ptr, "shape");
117                         const uint polys_len = RNA_property_string_length(gz->ptr, prop);
118                         /* We shouldn't need the +1, but a NULL char is set. */
119                         char *polys = MEM_mallocN(polys_len + 1, __func__);
120                         RNA_property_string_get(gz->ptr, prop, polys);
121                         button->shape_batch[0] = GPU_batch_tris_from_poly_2d_encoded((uchar *)polys, polys_len, NULL);
122                         button->shape_batch[1] = GPU_batch_wire_from_poly_2d_encoded((uchar *)polys, polys_len, NULL);
123                         MEM_freeN(polys);
124                 }
125         }
126
127         float color[4];
128         float matrix_final[4][4];
129
130         gizmo_color_get(gz, highlight, color);
131         WM_gizmo_calc_matrix_final(gz, matrix_final);
132
133
134         bool is_3d = (gz->parent_gzgroup->type->flag & WM_GIZMOGROUPTYPE_3D) != 0;
135
136         if ((select == false) &&
137             (draw_options & ED_GIZMO_BUTTON_SHOW_HELPLINE))
138         {
139                 float matrix_final_no_offset[4][4];
140                 WM_gizmo_calc_matrix_final_no_offset(gz, matrix_final_no_offset);
141                 uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
142                 immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
143                 immUniformColor4fv(color);
144                 GPU_line_width(gz->line_width);
145                 immUniformColor4fv(color);
146                 immBegin(GPU_PRIM_LINE_STRIP, 2);
147                 immVertex3fv(pos, matrix_final[3]);
148                 immVertex3fv(pos, matrix_final_no_offset[3]);
149                 immEnd();
150                 immUnbindProgram();
151         }
152
153         bool need_to_pop = true;
154         GPU_matrix_push();
155         GPU_matrix_mul(matrix_final);
156
157         if (is_3d) {
158                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
159                 float matrix_align[4][4];
160                 float matrix_final_unit[4][4];
161                 normalize_m4_m4(matrix_final_unit, matrix_final);
162                 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
163                 zero_v3(matrix_align[3]);
164                 transpose_m4(matrix_align);
165                 GPU_matrix_mul(matrix_align);
166         }
167
168         if (select) {
169                 BLI_assert(is_3d);
170                 button2d_geom_draw_backdrop(gz, color, select);
171         }
172         else {
173
174                 GPU_blend(true);
175                 if (button->shape_batch[0] != NULL) {
176                         GPU_line_smooth(true);
177                         GPU_polygon_smooth(false);
178                         GPU_line_width(1.0f);
179                         for (uint i = 0; i < ARRAY_SIZE(button->shape_batch) && button->shape_batch[i]; i++) {
180                                 /* Invert line color for wire. */
181                                 GPU_batch_program_set_builtin(button->shape_batch[i], GPU_SHADER_2D_UNIFORM_COLOR);
182                                 GPU_batch_uniform_4f(button->shape_batch[i], "color", UNPACK4(color));
183                                 GPU_batch_draw(button->shape_batch[i]);
184
185                                 if (draw_options & ED_GIZMO_BUTTON_SHOW_OUTLINE) {
186                                         color[0] = 1.0f - color[0];
187                                         color[1] = 1.0f - color[1];
188                                         color[2] = 1.0f - color[2];
189                                 }
190                         }
191                         GPU_line_smooth(false);
192                         GPU_polygon_smooth(true);
193                 }
194                 else if (button->icon != ICON_NONE) {
195                         if (draw_options & ED_GIZMO_BUTTON_SHOW_BACKDROP) {
196                                 button2d_geom_draw_backdrop(gz, color, select);
197                         }
198
199                         float pos[2];
200                         if (is_3d) {
201                                 const float fac = 2.0f;
202                                 GPU_matrix_translate_2f(-(fac / 2), -(fac / 2));
203                                 GPU_matrix_scale_2f(fac / (ICON_DEFAULT_WIDTH * UI_DPI_FAC), fac / (ICON_DEFAULT_HEIGHT * UI_DPI_FAC));
204                                 pos[0] = 1.0f;
205                                 pos[1] = 1.0f;
206                         }
207                         else {
208                                 pos[0] = gz->matrix_basis[3][0] - (ICON_DEFAULT_WIDTH / 2.0) * UI_DPI_FAC;
209                                 pos[1] = gz->matrix_basis[3][1] - (ICON_DEFAULT_HEIGHT / 2.0) * UI_DPI_FAC;
210                                 GPU_matrix_pop();
211                                 need_to_pop = false;
212                         }
213
214                         float alpha = (highlight) ? 1.0f : 0.8f;
215                         UI_icon_draw_alpha(pos[0], pos[1], button->icon, alpha);
216                 }
217                 GPU_blend(false);
218         }
219
220         if (need_to_pop) {
221                 GPU_matrix_pop();
222         }
223 }
224
225 static void gizmo_button2d_draw_select(const bContext *C, wmGizmo *gz, int select_id)
226 {
227         GPU_select_load_id(select_id);
228         button2d_draw_intern(C, gz, true, false);
229 }
230
231 static void gizmo_button2d_draw(const bContext *C, wmGizmo *gz)
232 {
233         const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
234
235         GPU_blend(true);
236         button2d_draw_intern(C, gz, false, is_highlight);
237         GPU_blend(false);
238 }
239
240 static int gizmo_button2d_test_select(
241         bContext *C, wmGizmo *gz, const int mval[2])
242 {
243         float point_local[2];
244
245         if (0) {
246                 /* correct, but unnecessarily slow. */
247                 if (gizmo_window_project_2d(
248                         C, gz, (const float[2]){UNPACK2(mval)}, 2, true, point_local) == false)
249                 {
250                         return -1;
251                 }
252         }
253         else {
254                 copy_v2_v2(point_local, (float[2]){UNPACK2(mval)});
255                 sub_v2_v2(point_local, gz->matrix_basis[3]);
256                 mul_v2_fl(point_local, 1.0f / (gz->scale_basis * UI_DPI_FAC));
257         }
258         /* The 'gz->scale_final' is already applied when projecting. */
259         if (len_squared_v2(point_local) < 1.0f) {
260                 return 0;
261         }
262
263         return -1;
264 }
265
266 static int gizmo_button2d_cursor_get(wmGizmo *gz)
267 {
268         if (RNA_boolean_get(gz->ptr, "show_drag")) {
269                 return BC_NSEW_SCROLLCURSOR;
270         }
271         return CURSOR_STD;
272 }
273
274 static void gizmo_button2d_free(wmGizmo *gz)
275 {
276         ButtonGizmo2D *shape = (ButtonGizmo2D *)gz;
277
278         for (uint i = 0; i < ARRAY_SIZE(shape->shape_batch); i++) {
279                 GPU_BATCH_DISCARD_SAFE(shape->shape_batch[i]);
280         }
281 }
282
283 /** \} */
284
285 /* -------------------------------------------------------------------- */
286 /** \name Button Gizmo API
287  *
288  * \{ */
289
290 static void GIZMO_GT_button_2d(wmGizmoType *gzt)
291 {
292         /* identifiers */
293         gzt->idname = "GIZMO_GT_button_2d";
294
295         /* api callbacks */
296         gzt->draw = gizmo_button2d_draw;
297         gzt->draw_select = gizmo_button2d_draw_select;
298         gzt->test_select = gizmo_button2d_test_select;
299         gzt->cursor_get = gizmo_button2d_cursor_get;
300         gzt->free = gizmo_button2d_free;
301
302         gzt->struct_size = sizeof(ButtonGizmo2D);
303
304         /* rna */
305         static EnumPropertyItem rna_enum_draw_options[] = {
306                 {ED_GIZMO_BUTTON_SHOW_OUTLINE, "OUTLINE", 0, "Outline", ""},
307                 {ED_GIZMO_BUTTON_SHOW_BACKDROP, "BACKDROP", 0, "Backdrop", ""},
308                 {ED_GIZMO_BUTTON_SHOW_HELPLINE, "HELPLINE", 0, "Help Line", ""},
309                 {0, NULL, 0, NULL, NULL}
310         };
311         PropertyRNA *prop;
312
313         RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
314
315         prop = RNA_def_property(gzt->srna, "icon", PROP_ENUM, PROP_NONE);
316         RNA_def_property_enum_items(prop, rna_enum_icon_items);
317
318         /* Passed to 'GPU_batch_tris_from_poly_2d_encoded' */
319         RNA_def_property(gzt->srna, "shape", PROP_STRING, PROP_BYTESTRING);
320
321         /* Currently only used for cursor display. */
322         RNA_def_boolean(gzt->srna, "show_drag", true, "Show Drag", "");
323 }
324
325 void ED_gizmotypes_button_2d(void)
326 {
327         WM_gizmotype_append(GIZMO_GT_button_2d);
328 }
329
330 /** \} */ // Button Gizmo API