Cleanup: rename gizmo access functions
[blender.git] / source / blender / editors / gizmo_library / gizmo_types / arrow3d_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  * The Original Code is Copyright (C) 2014 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation
22  *
23  * ***** END GPL LICENSE BLOCK *****
24  */
25
26 /** \file arrow3d_gizmo.c
27  *  \ingroup wm
28  *
29  * \name Arrow Gizmo
30  *
31  * 3D Gizmo
32  *
33  * \brief Simple arrow gizmo which is dragged into a certain direction.
34  * The arrow head can have varying shapes, e.g. cone, box, etc.
35  *
36  * - `matrix[0]` is derived from Y and Z.
37  * - `matrix[1]` is 'up' for gizmo types that have an up.
38  * - `matrix[2]` is the arrow direction (for all arrowes).
39  */
40
41 #include "BIF_gl.h"
42
43 #include "BLI_math.h"
44
45 #include "DNA_view3d_types.h"
46
47 #include "BKE_context.h"
48
49 #include "GPU_draw.h"
50 #include "GPU_immediate.h"
51 #include "GPU_immediate_util.h"
52 #include "GPU_matrix.h"
53 #include "GPU_select.h"
54 #include "GPU_state.h"
55
56 #include "MEM_guardedalloc.h"
57
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60
61 #include "WM_types.h"
62 #include "WM_api.h"
63
64 #include "ED_view3d.h"
65 #include "ED_screen.h"
66 #include "ED_gizmo_library.h"
67
68 /* own includes */
69 #include "../gizmo_geometry.h"
70 #include "../gizmo_library_intern.h"
71
72 /* to use custom arrows exported to geom_arrow_gizmo.c */
73 //#define USE_GIZMO_CUSTOM_ARROWS
74
75 typedef struct ArrowGizmo3D {
76         wmGizmo gizmo;
77         GizmoCommonData data;
78 } ArrowGizmo3D;
79
80
81 /* -------------------------------------------------------------------- */
82
83 static void gizmo_arrow_matrix_basis_get(const wmGizmo *gz, float r_matrix[4][4])
84 {
85         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
86
87         copy_m4_m4(r_matrix, arrow->gizmo.matrix_basis);
88         madd_v3_v3fl(r_matrix[3], arrow->gizmo.matrix_basis[2], arrow->data.offset);
89 }
90
91 static void arrow_draw_geom(const ArrowGizmo3D *arrow, const bool select, const float color[4])
92 {
93         uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 3, GPU_FETCH_FLOAT);
94         bool unbind_shader = true;
95         const int draw_style = RNA_enum_get(arrow->gizmo.ptr, "draw_style");
96         const int draw_options = RNA_enum_get(arrow->gizmo.ptr, "draw_options");
97
98         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
99
100         if (draw_style == ED_GIZMO_ARROW_STYLE_CROSS) {
101                 immUniformColor4fv(color);
102
103                 immBegin(GPU_PRIM_LINES, 4);
104                 immVertex3f(pos, -1.0f,  0.0f, 0.0f);
105                 immVertex3f(pos,  1.0f,  0.0f, 0.0f);
106                 immVertex3f(pos,  0.0f, -1.0f, 0.0f);
107                 immVertex3f(pos,  0.0f,  1.0f, 0.0f);
108                 immEnd();
109         }
110         else if (draw_style == ED_GIZMO_ARROW_STYLE_CONE) {
111                 float aspect[2];
112                 RNA_float_get_array(arrow->gizmo.ptr, "aspect", aspect);
113                 const float unitx = aspect[0];
114                 const float unity = aspect[1];
115                 const float vec[4][3] = {
116                         {-unitx, -unity, 0},
117                         { unitx, -unity, 0},
118                         { unitx,  unity, 0},
119                         {-unitx,  unity, 0},
120                 };
121
122                 GPU_line_width(arrow->gizmo.line_width);
123                 wm_gizmo_vec_draw(color, vec, ARRAY_SIZE(vec), pos, GPU_PRIM_LINE_LOOP);
124         }
125         else {
126 #ifdef USE_GIZMO_CUSTOM_ARROWS
127                 wm_gizmo_geometryinfo_draw(&wm_gizmo_geom_data_arrow, select, color);
128 #else
129                 const float arrow_length = RNA_float_get(arrow->gizmo.ptr, "length");
130
131                 const float vec[2][3] = {
132                         {0.0f, 0.0f, 0.0f},
133                         {0.0f, 0.0f, arrow_length},
134                 };
135
136                 if (draw_options & ED_GIZMO_ARROW_DRAW_FLAG_STEM) {
137                         GPU_line_width(arrow->gizmo.line_width);
138                         wm_gizmo_vec_draw(color, vec, ARRAY_SIZE(vec), pos, GPU_PRIM_LINE_STRIP);
139                 }
140                 else {
141                         immUniformColor4fv(color);
142                 }
143
144                 /* *** draw arrow head *** */
145
146                 GPU_matrix_push();
147
148                 if (draw_style == ED_GIZMO_ARROW_STYLE_BOX) {
149                         const float size = 0.05f;
150
151                         /* translate to line end with some extra offset so box starts exactly where line ends */
152                         GPU_matrix_translate_3f(0.0f, 0.0f, arrow_length + size);
153                         /* scale down to box size */
154                         GPU_matrix_scale_3f(size, size, size);
155
156                         /* draw cube */
157                         immUnbindProgram();
158                         unbind_shader = false;
159                         wm_gizmo_geometryinfo_draw(&wm_gizmo_geom_data_cube, select, color);
160                 }
161                 else {
162                         BLI_assert(draw_style == ED_GIZMO_ARROW_STYLE_NORMAL);
163
164                         const float len = 0.25f;
165                         const float width = 0.06f;
166
167                         /* translate to line end */
168                         GPU_matrix_translate_3f(0.0f, 0.0f, arrow_length);
169
170                         imm_draw_circle_fill_3d(pos, 0.0, 0.0, width, 8);
171                         imm_draw_cylinder_fill_3d(pos, width, 0.0, len, 8, 1);
172                 }
173
174                 GPU_matrix_pop();
175 #endif  /* USE_GIZMO_CUSTOM_ARROWS */
176         }
177
178         if (unbind_shader) {
179                 immUnbindProgram();
180         }
181 }
182
183 static void arrow_draw_intern(ArrowGizmo3D *arrow, const bool select, const bool highlight)
184 {
185         wmGizmo *gz = &arrow->gizmo;
186         float color[4];
187         float matrix_final[4][4];
188
189         gizmo_color_get(gz, highlight, color);
190
191         WM_gizmo_calc_matrix_final(gz, matrix_final);
192
193         GPU_matrix_push();
194         GPU_matrix_mul(matrix_final);
195         GPU_blend(true);
196         arrow_draw_geom(arrow, select, color);
197         GPU_blend(false);
198
199         GPU_matrix_pop();
200
201         if (gz->interaction_data) {
202                 GizmoInteraction *inter = gz->interaction_data;
203
204                 GPU_matrix_push();
205                 GPU_matrix_mul(inter->init_matrix_final);
206
207
208                 GPU_blend(true);
209                 arrow_draw_geom(arrow, select, (const float[4]){0.5f, 0.5f, 0.5f, 0.5f});
210                 GPU_blend(false);
211
212                 GPU_matrix_pop();
213         }
214 }
215
216 static void gizmo_arrow_draw_select(
217         const bContext *UNUSED(C), wmGizmo *gz,
218         int select_id)
219 {
220         GPU_select_load_id(select_id);
221         arrow_draw_intern((ArrowGizmo3D *)gz, true, false);
222 }
223
224 static void gizmo_arrow_draw(const bContext *UNUSED(C), wmGizmo *gz)
225 {
226         arrow_draw_intern((ArrowGizmo3D *)gz, false, (gz->state & WM_GIZMO_STATE_HIGHLIGHT) != 0);
227 }
228
229 /**
230  * Calculate arrow offset independent from prop min value,
231  * meaning the range will not be offset by min value first.
232  */
233 static int gizmo_arrow_modal(
234         bContext *C, wmGizmo *gz, const wmEvent *event,
235         eWM_GizmoFlagTweak tweak_flag)
236 {
237         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
238         GizmoInteraction *inter = gz->interaction_data;
239         View3D *v3d = CTX_wm_view3d(C);
240         ARegion *ar = CTX_wm_region(C);
241         RegionView3D *rv3d = ar->regiondata;
242
243         float offset[3];
244         float facdir = 1.0f;
245
246         /* (src, dst) */
247         struct {
248                 float mval[2];
249                 float ray_origin[3], ray_direction[3];
250                 float location[3];
251         } proj[2] = {
252                 {.mval = {UNPACK2(inter->init_mval)}},
253                 {.mval = {UNPACK2(event->mval)}},
254         };
255
256         float arrow_co[3];
257         float arrow_no[3];
258         copy_v3_v3(arrow_co, inter->init_matrix_basis[3]);
259         normalize_v3_v3(arrow_no, arrow->gizmo.matrix_basis[2]);
260
261         int ok = 0;
262
263         for (int j = 0; j < 2; j++) {
264                 if (ED_view3d_win_to_ray(
265                             CTX_data_depsgraph(C),
266                             ar, v3d, proj[j].mval,
267                             proj[j].ray_origin, proj[j].ray_direction, false))
268                 {
269                         /* Force Y axis if we're view aligned */
270                         if (j == 0) {
271                                 if (RAD2DEGF(acosf(dot_v3v3(proj[j].ray_direction, arrow->gizmo.matrix_basis[2]))) < 5.0f) {
272                                         normalize_v3_v3(arrow_no, rv3d->viewinv[1]);
273                                 }
274                         }
275
276                         float arrow_no_proj[3];
277                         project_plane_v3_v3v3(arrow_no_proj, arrow_no, proj[j].ray_direction);
278
279                         normalize_v3(arrow_no_proj);
280
281                         float plane[4];
282                         plane_from_point_normal_v3(plane, proj[j].ray_origin, arrow_no_proj);
283
284                         float lambda;
285                         if (isect_ray_plane_v3(arrow_co, arrow_no, plane, &lambda, false)) {
286                                 madd_v3_v3v3fl(proj[j].location, arrow_co, arrow_no, lambda);
287                                 ok++;
288                         }
289                 }
290         }
291
292         if (ok != 2) {
293                 return OPERATOR_RUNNING_MODAL;
294         }
295
296         sub_v3_v3v3(offset, proj[1].location, proj[0].location);
297         facdir = dot_v3v3(arrow_no, offset) < 0.0f ? -1 : 1;
298
299         GizmoCommonData *data = &arrow->data;
300         const float ofs_new = facdir * len_v3(offset);
301
302         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
303
304         /* set the property for the operator and call its modal function */
305         if (WM_gizmo_target_property_is_valid(gz_prop)) {
306                 const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
307                 const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
308                 const bool inverted = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_INVERTED) != 0;
309                 const bool use_precision = (tweak_flag & WM_GIZMO_TWEAK_PRECISE) != 0;
310                 float value = gizmo_value_from_offset(data, inter, ofs_new, constrained, inverted, use_precision);
311
312                 WM_gizmo_target_property_float_set(C, gz, gz_prop, value);
313                 /* get clamped value */
314                 value = WM_gizmo_target_property_float_get(gz, gz_prop);
315
316                 data->offset = gizmo_offset_from_value(data, value, constrained, inverted);
317         }
318         else {
319                 data->offset = ofs_new;
320         }
321
322         /* tag the region for redraw */
323         ED_region_tag_redraw(ar);
324         WM_event_add_mousemove(C);
325
326         return OPERATOR_RUNNING_MODAL;
327 }
328
329 static void gizmo_arrow_setup(wmGizmo *gz)
330 {
331         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
332
333         arrow->gizmo.flag |= WM_GIZMO_DRAW_MODAL;
334
335         arrow->data.range_fac = 1.0f;
336 }
337
338 static int gizmo_arrow_invoke(
339         bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event)
340 {
341         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
342         GizmoInteraction *inter = MEM_callocN(sizeof(GizmoInteraction), __func__);
343         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
344
345         /* Some gizmos don't use properties. */
346         if (WM_gizmo_target_property_is_valid(gz_prop)) {
347                 inter->init_value = WM_gizmo_target_property_float_get(gz, gz_prop);
348         }
349
350         inter->init_offset = arrow->data.offset;
351
352         inter->init_mval[0] = event->mval[0];
353         inter->init_mval[1] = event->mval[1];
354
355         gizmo_arrow_matrix_basis_get(gz, inter->init_matrix_basis);
356         WM_gizmo_calc_matrix_final(gz, inter->init_matrix_final);
357
358         gz->interaction_data = inter;
359
360         return OPERATOR_RUNNING_MODAL;
361 }
362
363 static void gizmo_arrow_property_update(wmGizmo *gz, wmGizmoProperty *gz_prop)
364 {
365         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
366         const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
367         const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
368         const bool inverted = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_INVERTED) != 0;
369         gizmo_property_data_update(gz, &arrow->data, gz_prop, constrained, inverted);
370 }
371
372 static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel)
373 {
374         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
375         GizmoCommonData *data = &arrow->data;
376         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
377         const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop);
378
379         if (!cancel) {
380                 /* Assign incase applying the opetration needs an updated offset
381                  * editmesh bisect needs this. */
382                 if (is_prop_valid) {
383                         data->offset = WM_gizmo_target_property_float_get(gz, gz_prop);
384                 }
385                 return;
386         }
387
388         GizmoInteraction *inter = gz->interaction_data;
389         if (is_prop_valid) {
390                 gizmo_property_value_reset(C, gz, inter, gz_prop);
391         }
392         data->offset = inter->init_offset;
393 }
394
395
396 /* -------------------------------------------------------------------- */
397 /** \name Arrow Gizmo API
398  *
399  * \{ */
400
401 /**
402  * Define a custom property UI range
403  *
404  * \note Needs to be called before WM_gizmo_target_property_def_rna!
405  */
406 void ED_gizmo_arrow3d_set_ui_range(wmGizmo *gz, const float min, const float max)
407 {
408         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
409
410         BLI_assert(min < max);
411         BLI_assert(!(WM_gizmo_target_property_is_valid(WM_gizmo_target_property_find(gz, "offset")) &&
412                      "Make sure this function is called before WM_gizmo_target_property_def_rna"));
413
414         arrow->data.range = max - min;
415         arrow->data.min = min;
416         arrow->data.flag |= GIZMO_CUSTOM_RANGE_SET;
417 }
418
419 /**
420  * Define a custom factor for arrow min/max distance
421  *
422  * \note Needs to be called before WM_gizmo_target_property_def_rna!
423  */
424 void ED_gizmo_arrow3d_set_range_fac(wmGizmo *gz, const float range_fac)
425 {
426         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
427         BLI_assert(!(WM_gizmo_target_property_is_valid(WM_gizmo_target_property_find(gz, "offset")) &&
428                      "Make sure this function is called before WM_gizmo_target_property_def_rna"));
429
430         arrow->data.range_fac = range_fac;
431 }
432
433 static void GIZMO_GT_arrow_3d(wmGizmoType *gzt)
434 {
435         /* identifiers */
436         gzt->idname = "GIZMO_GT_arrow_3d";
437
438         /* api callbacks */
439         gzt->draw = gizmo_arrow_draw;
440         gzt->draw_select = gizmo_arrow_draw_select;
441         gzt->matrix_basis_get = gizmo_arrow_matrix_basis_get;
442         gzt->modal = gizmo_arrow_modal;
443         gzt->setup = gizmo_arrow_setup;
444         gzt->invoke = gizmo_arrow_invoke;
445         gzt->property_update = gizmo_arrow_property_update;
446         gzt->exit = gizmo_arrow_exit;
447
448         gzt->struct_size = sizeof(ArrowGizmo3D);
449
450         /* rna */
451         static EnumPropertyItem rna_enum_draw_style_items[] = {
452                 {ED_GIZMO_ARROW_STYLE_NORMAL, "NORMAL", 0, "Normal", ""},
453                 {ED_GIZMO_ARROW_STYLE_CROSS, "CROSS", 0, "Cross", ""},
454                 {ED_GIZMO_ARROW_STYLE_BOX, "BOX", 0, "Box", ""},
455                 {ED_GIZMO_ARROW_STYLE_CONE, "CONE", 0, "Cone", ""},
456                 {0, NULL, 0, NULL, NULL}
457         };
458         static EnumPropertyItem rna_enum_draw_options_items[] = {
459                 {ED_GIZMO_ARROW_DRAW_FLAG_STEM, "STEM", 0, "Stem", ""},
460                 {0, NULL, 0, NULL, NULL}
461         };
462         static EnumPropertyItem rna_enum_transform_items[] = {
463                 {ED_GIZMO_ARROW_XFORM_FLAG_INVERTED, "INVERT", 0, "Inverted", ""},
464                 {ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED, "CONSTRAIN", 0, "Constrained", ""},
465                 {0, NULL, 0, NULL, NULL}
466         };
467
468         RNA_def_enum(
469                 gzt->srna, "draw_style", rna_enum_draw_style_items,
470                 ED_GIZMO_ARROW_STYLE_NORMAL,
471                 "Draw Style", "");
472         RNA_def_enum_flag(
473                 gzt->srna, "draw_options", rna_enum_draw_options_items,
474                 ED_GIZMO_ARROW_DRAW_FLAG_STEM,
475                 "Draw Options", "");
476         RNA_def_enum_flag(
477                 gzt->srna, "transform", rna_enum_transform_items,
478                 0,
479                 "Transform", "");
480
481         RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX);
482         RNA_def_float_vector(gzt->srna, "aspect", 2, NULL, 0, FLT_MAX, "Aspect", "Cone/box style only", 0.0f, FLT_MAX);
483
484         WM_gizmotype_target_property_def(gzt, "offset", PROP_FLOAT, 1);
485 }
486
487 void ED_gizmotypes_arrow_3d(void)
488 {
489         WM_gizmotype_append(GIZMO_GT_arrow_3d);
490 }
491
492 /** \} */