Gizmo: add option to move & dial gizmos to select as filled
[blender.git] / source / blender / editors / gizmo_library / gizmo_types / move3d_gizmo.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16
17 /** \file
18  * \ingroup edgizmolib
19  *
20  * \name Move Gizmo
21  *
22  * 3D Gizmo, also works in 2D views.
23  *
24  * \brief Simple gizmo to move and translate.
25  *
26  * - `matrix[0]` is derived from Y and Z.
27  * - `matrix[1]` currently not used.
28  * - `matrix[2]` is the widget direction (for all gizmos).
29  */
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_math.h"
34
35 #include "BKE_context.h"
36
37
38 #include "GPU_immediate.h"
39 #include "GPU_immediate_util.h"
40 #include "GPU_matrix.h"
41 #include "GPU_select.h"
42 #include "GPU_state.h"
43
44 #include "RNA_access.h"
45 #include "RNA_define.h"
46
47 #include "WM_api.h"
48 #include "WM_types.h"
49
50 #include "ED_screen.h"
51 #include "ED_view3d.h"
52 #include "ED_gizmo_library.h"
53 #include "ED_transform_snap_object_context.h"
54
55 /* own includes */
56 #include "../gizmo_geometry.h"
57 #include "../gizmo_library_intern.h"
58
59 #define MVAL_MAX_PX_DIST 12.0f
60
61 typedef struct MoveGizmo3D {
62         wmGizmo gizmo;
63         /* Added to 'matrix_basis' when calculating the matrix. */
64         float prop_co[3];
65 } MoveGizmo3D;
66
67 static void gizmo_move_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4])
68 {
69         MoveGizmo3D *move = (MoveGizmo3D *)gz;
70
71         copy_m4_m4(r_matrix, move->gizmo.matrix_basis);
72         add_v3_v3(r_matrix[3], move->prop_co);
73 }
74
75 static int gizmo_move_modal(
76         bContext *C, wmGizmo *gz, const wmEvent *event,
77         eWM_GizmoFlagTweak tweak_flag);
78
79 typedef struct MoveInteraction {
80         struct {
81                 float mval[2];
82                 /* Only for when using properties. */
83                 float prop_co[3];
84                 float matrix_final[4][4];
85         } init;
86         struct {
87                 eWM_GizmoFlagTweak tweak_flag;
88         } prev;
89
90         /* We could have other snap contexts, for now only support 3D view. */
91         struct SnapObjectContext *snap_context_v3d;
92
93 } MoveInteraction;
94
95 #define DIAL_RESOLUTION 32
96
97 /* -------------------------------------------------------------------- */
98
99 static void move_geom_draw(
100         const wmGizmo *gz, const float color[4], const bool select, const int draw_options)
101 {
102 #ifdef USE_GIZMO_CUSTOM_DIAL
103         UNUSED_VARS(move3d, col, axis_modal_mat);
104         wm_gizmo_geometryinfo_draw(&wm_gizmo_geom_data_move3d, select);
105 #else
106         const int draw_style = RNA_enum_get(gz->ptr, "draw_style");
107         const bool filled = (
108                 (draw_options &
109                  (select ?
110                   (ED_GIZMO_MOVE_DRAW_FLAG_FILL | ED_GIZMO_MOVE_DRAW_FLAG_FILL_SELECT) :
111                   ED_GIZMO_MOVE_DRAW_FLAG_FILL)));
112
113         GPU_line_width(gz->line_width);
114
115         GPUVertFormat *format = immVertexFormat();
116         uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
117
118         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
119
120         immUniformColor4fv(color);
121
122         if (draw_style == ED_GIZMO_MOVE_STYLE_RING_2D) {
123                 if (filled) {
124                         imm_draw_circle_fill_2d(pos, 0, 0, 1.0f, DIAL_RESOLUTION);
125                 }
126                 else {
127                         imm_draw_circle_wire_2d(pos, 0, 0, 1.0f, DIAL_RESOLUTION);
128                 }
129         }
130         else if (draw_style == ED_GIZMO_MOVE_STYLE_CROSS_2D) {
131                 immBegin(GPU_PRIM_LINES, 4);
132                 immVertex2f(pos,  1.0f,  1.0f);
133                 immVertex2f(pos, -1.0f, -1.0f);
134
135                 immVertex2f(pos, -1.0f,  1.0f);
136                 immVertex2f(pos,  1.0f, -1.0f);
137                 immEnd();
138         }
139         else {
140                 BLI_assert(0);
141         }
142
143         immUnbindProgram();
144
145         UNUSED_VARS(select);
146 #endif
147 }
148
149 static void move3d_get_translate(
150         const wmGizmo *gz, const wmEvent *event, const ARegion *ar,
151         float co_delta[3])
152 {
153         MoveInteraction *inter = gz->interaction_data;
154         const float mval_delta[2] = {
155             event->mval[0] - inter->init.mval[0],
156             event->mval[1] - inter->init.mval[1],
157         };
158
159         RegionView3D *rv3d = ar->regiondata;
160         float co_ref[3];
161         mul_v3_mat3_m4v3(co_ref, gz->matrix_space, inter->init.prop_co);
162         const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
163
164         ED_view3d_win_to_delta(ar, mval_delta, co_delta, zfac);
165
166         float matrix_space_inv[3][3];
167         copy_m3_m4(matrix_space_inv, gz->matrix_space);
168         invert_m3(matrix_space_inv);
169         mul_m3_v3(matrix_space_inv, co_delta);
170 }
171
172 static void move3d_draw_intern(
173         const bContext *C, wmGizmo *gz,
174         const bool select, const bool highlight)
175 {
176         MoveInteraction *inter = gz->interaction_data;
177         const int draw_options = RNA_enum_get(gz->ptr, "draw_options");
178         const bool align_view = (draw_options & ED_GIZMO_MOVE_DRAW_FLAG_ALIGN_VIEW) != 0;
179         float color[4];
180         float matrix_final[4][4];
181         float matrix_align[4][4];
182
183         gizmo_color_get(gz, highlight, color);
184         WM_gizmo_calc_matrix_final(gz, matrix_final);
185
186         GPU_matrix_push();
187         GPU_matrix_mul(matrix_final);
188
189         if (align_view) {
190                 float matrix_final_unit[4][4];
191                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
192                 normalize_m4_m4(matrix_final_unit, matrix_final);
193                 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
194                 zero_v3(matrix_align[3]);
195                 transpose_m4(matrix_align);
196                 GPU_matrix_mul(matrix_align);
197         }
198
199         GPU_blend(true);
200         move_geom_draw(gz, color, select, draw_options);
201         GPU_blend(false);
202         GPU_matrix_pop();
203
204         if (gz->interaction_data) {
205                 GPU_matrix_push();
206                 GPU_matrix_mul(inter->init.matrix_final);
207
208                 if (align_view) {
209                         GPU_matrix_mul(matrix_align);
210                 }
211
212                 GPU_blend(true);
213                 move_geom_draw(gz, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f}, select, draw_options);
214                 GPU_blend(false);
215                 GPU_matrix_pop();
216         }
217 }
218
219 static void gizmo_move_draw_select(const bContext *C, wmGizmo *gz, int select_id)
220 {
221         GPU_select_load_id(select_id);
222         move3d_draw_intern(C, gz, true, false);
223 }
224
225 static void gizmo_move_draw(const bContext *C, wmGizmo *gz)
226 {
227         const bool is_modal = gz->state & WM_GIZMO_STATE_MODAL;
228         const bool is_highlight = (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0;
229
230         (void)is_modal;
231
232         GPU_blend(true);
233         move3d_draw_intern(C, gz, false, is_highlight);
234         GPU_blend(false);
235 }
236
237 static int gizmo_move_modal(
238         bContext *C, wmGizmo *gz, const wmEvent *event,
239         eWM_GizmoFlagTweak tweak_flag)
240 {
241         MoveInteraction *inter = gz->interaction_data;
242         if ((event->type != MOUSEMOVE) && (inter->prev.tweak_flag == tweak_flag)) {
243                 return OPERATOR_RUNNING_MODAL;
244         }
245         MoveGizmo3D *move = (MoveGizmo3D *)gz;
246         ARegion *ar = CTX_wm_region(C);
247
248         float prop_delta[3];
249         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) {
250                 move3d_get_translate(gz, event, ar, prop_delta);
251         }
252         else {
253                 float mval_proj_init[2], mval_proj_curr[2];
254                 if ((gizmo_window_project_2d(
255                          C, gz, inter->init.mval, 2, false, mval_proj_init) == false) ||
256                     (gizmo_window_project_2d(
257                          C, gz, (const float[2]){UNPACK2(event->mval)}, 2, false, mval_proj_curr) == false))
258                 {
259                         return OPERATOR_RUNNING_MODAL;
260                 }
261                 sub_v2_v2v2(prop_delta, mval_proj_curr, mval_proj_init);
262                 prop_delta[2] = 0.0f;
263         }
264
265         if (tweak_flag & WM_GIZMO_TWEAK_PRECISE) {
266                 mul_v3_fl(prop_delta, 0.1f);
267         }
268
269         add_v3_v3v3(move->prop_co, inter->init.prop_co, prop_delta);
270
271         if (tweak_flag & WM_GIZMO_TWEAK_SNAP) {
272                 if (inter->snap_context_v3d) {
273                         float dist_px = MVAL_MAX_PX_DIST * U.pixelsize;
274                         const float mval_fl[2] = {UNPACK2(event->mval)};
275                         float co[3];
276                         if (ED_transform_snap_object_project_view3d(
277                                 inter->snap_context_v3d,
278                                 (SCE_SNAP_MODE_VERTEX | SCE_SNAP_MODE_EDGE | SCE_SNAP_MODE_FACE),
279                                 &(const struct SnapObjectParams){
280                                     .snap_select = SNAP_ALL,
281                                     .use_object_edit_cage = true,
282                                     .use_occlusion_test = true,
283                                 },
284                                 mval_fl, &dist_px,
285                                 co, NULL))
286                         {
287                                 float matrix_space_inv[4][4];
288                                 invert_m4_m4(matrix_space_inv, gz->matrix_space);
289                                 mul_v3_m4v3(move->prop_co, matrix_space_inv, co);
290                         }
291                 }
292         }
293
294         /* set the property for the operator and call its modal function */
295         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
296         if (WM_gizmo_target_property_is_valid(gz_prop)) {
297                 WM_gizmo_target_property_float_set_array(C, gz, gz_prop, move->prop_co);
298         }
299         else {
300                 zero_v3(move->prop_co);
301         }
302
303         ED_region_tag_redraw(ar);
304
305         inter->prev.tweak_flag = tweak_flag;
306
307         return OPERATOR_RUNNING_MODAL;
308 }
309
310 static void gizmo_move_exit(bContext *C, wmGizmo *gz, const bool cancel)
311 {
312         MoveInteraction *inter = gz->interaction_data;
313         bool use_reset_value = false;
314         const float *reset_value = NULL;
315         if (cancel) {
316                 /* Set the property for the operator and call its modal function. */
317                 wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
318                 if (WM_gizmo_target_property_is_valid(gz_prop)) {
319                         use_reset_value = true;
320                         reset_value = inter->init.prop_co;
321                 }
322         }
323
324         if (use_reset_value) {
325                 wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
326                 if (WM_gizmo_target_property_is_valid(gz_prop)) {
327                         WM_gizmo_target_property_float_set_array(C, gz, gz_prop, reset_value);
328                 }
329         }
330
331         if (inter->snap_context_v3d) {
332                 ED_transform_snap_object_context_destroy(inter->snap_context_v3d);
333                 inter->snap_context_v3d = NULL;
334         }
335 }
336
337 static int gizmo_move_invoke(
338         bContext *C, wmGizmo *gz, const wmEvent *event)
339 {
340         const bool use_snap = RNA_boolean_get(gz->ptr, "use_snap");
341
342         MoveInteraction *inter = MEM_callocN(sizeof(MoveInteraction), __func__);
343         inter->init.mval[0] = event->mval[0];
344         inter->init.mval[1] = event->mval[1];
345
346 #if 0
347         copy_v3_v3(inter->init.prop_co, move->prop_co);
348 #else
349         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
350         if (WM_gizmo_target_property_is_valid(gz_prop)) {
351                 WM_gizmo_target_property_float_get_array(gz, gz_prop, inter->init.prop_co);
352         }
353 #endif
354
355         WM_gizmo_calc_matrix_final(gz, inter->init.matrix_final);
356
357         if (use_snap) {
358                 ScrArea *sa = CTX_wm_area(C);
359                 if (sa) {
360                         switch (sa->spacetype) {
361                                 case SPACE_VIEW3D:
362                                 {
363                                         inter->snap_context_v3d = ED_transform_snap_object_context_create_view3d(
364                                                 CTX_data_main(C), CTX_data_scene(C), CTX_data_depsgraph(C), 0,
365                                                 CTX_wm_region(C), CTX_wm_view3d(C));
366                                         break;
367                                 }
368                                 default:
369                                         /* Not yet supported. */
370                                         BLI_assert(0);
371                         }
372                 }
373         }
374
375         gz->interaction_data = inter;
376
377         return OPERATOR_RUNNING_MODAL;
378 }
379
380
381 static int gizmo_move_test_select(
382         bContext *C, wmGizmo *gz, const int mval[2])
383 {
384         float point_local[2];
385
386         if (gizmo_window_project_2d(
387                 C, gz, (const float[2]){UNPACK2(mval)}, 2, true, point_local) == false)
388         {
389                 return -1;
390         }
391
392         /* The 'gz->scale_final' is already applied when projecting. */
393         if (len_squared_v2(point_local) < 1.0f) {
394                 return 0;
395         }
396
397         return -1;
398 }
399
400 static void gizmo_move_property_update(wmGizmo *gz, wmGizmoProperty *gz_prop)
401 {
402         MoveGizmo3D *move = (MoveGizmo3D *)gz;
403         if (WM_gizmo_target_property_is_valid(gz_prop)) {
404                 WM_gizmo_target_property_float_get_array(gz, gz_prop, move->prop_co);
405         }
406         else {
407                 zero_v3(move->prop_co);
408         }
409 }
410
411 static int gizmo_move_cursor_get(wmGizmo *UNUSED(gz))
412 {
413         return BC_NSEW_SCROLLCURSOR;
414 }
415
416 /* -------------------------------------------------------------------- */
417 /** \name Move Gizmo API
418  *
419  * \{ */
420
421 static void GIZMO_GT_move_3d(wmGizmoType *gzt)
422 {
423         /* identifiers */
424         gzt->idname = "GIZMO_GT_move_3d";
425
426         /* api callbacks */
427         gzt->draw = gizmo_move_draw;
428         gzt->draw_select = gizmo_move_draw_select;
429         gzt->test_select = gizmo_move_test_select;
430         gzt->matrix_basis_get = gizmo_move_matrix_basis_get;
431         gzt->invoke = gizmo_move_invoke;
432         gzt->property_update = gizmo_move_property_update;
433         gzt->modal = gizmo_move_modal;
434         gzt->exit = gizmo_move_exit;
435         gzt->cursor_get = gizmo_move_cursor_get;
436
437         gzt->struct_size = sizeof(MoveGizmo3D);
438
439         /* rna */
440         static EnumPropertyItem rna_enum_draw_style[] = {
441                 {ED_GIZMO_MOVE_STYLE_RING_2D, "RING_2D", 0, "Ring", ""},
442                 {ED_GIZMO_MOVE_STYLE_CROSS_2D, "CROSS_2D", 0, "Ring", ""},
443                 {0, NULL, 0, NULL, NULL},
444         };
445         static EnumPropertyItem rna_enum_draw_options[] = {
446                 {ED_GIZMO_MOVE_DRAW_FLAG_FILL, "FILL", 0, "Filled", ""},
447                 {ED_GIZMO_MOVE_DRAW_FLAG_FILL_SELECT, "FILL_SELECT", 0, "Use fill for selection test", ""},
448                 {ED_GIZMO_MOVE_DRAW_FLAG_ALIGN_VIEW, "ALIGN_VIEW", 0, "Align View", ""},
449                 {0, NULL, 0, NULL, NULL},
450         };
451
452         RNA_def_enum(gzt->srna, "draw_style", rna_enum_draw_style, ED_GIZMO_MOVE_STYLE_RING_2D, "Draw Style", "");
453         RNA_def_enum_flag(gzt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
454         RNA_def_boolean(gzt->srna, "use_snap", false, "Use Snap", "");
455
456         WM_gizmotype_target_property_def(gzt, "offset", PROP_FLOAT, 3);
457 }
458
459 void ED_gizmotypes_move_3d(void)
460 {
461         WM_gizmotype_append(GIZMO_GT_move_3d);
462 }
463
464 /** \} */ // Move Gizmo API