Cleanup: remove flag from GizmoCommonData
[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 edgizmolib
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         if (event->type != MOUSEMOVE) {
238                 return OPERATOR_RUNNING_MODAL;
239         }
240         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
241         GizmoInteraction *inter = gz->interaction_data;
242         ARegion *ar = CTX_wm_region(C);
243         RegionView3D *rv3d = ar->regiondata;
244
245         float offset[3];
246         float facdir = 1.0f;
247
248         /* (src, dst) */
249         struct {
250                 float mval[2];
251                 float ray_origin[3], ray_direction[3];
252                 float location[3];
253         } proj[2] = {
254                 {.mval = {UNPACK2(inter->init_mval)}},
255                 {.mval = {UNPACK2(event->mval)}},
256         };
257
258         float arrow_co[3];
259         float arrow_no[3];
260         copy_v3_v3(arrow_co, inter->init_matrix_basis[3]);
261         normalize_v3_v3(arrow_no, arrow->gizmo.matrix_basis[2]);
262
263         int ok = 0;
264
265         for (int j = 0; j < 2; j++) {
266                 ED_view3d_win_to_ray(
267                         ar, proj[j].mval,
268                         proj[j].ray_origin, proj[j].ray_direction);
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         if (ok != 2) {
292                 return OPERATOR_RUNNING_MODAL;
293         }
294
295         sub_v3_v3v3(offset, proj[1].location, proj[0].location);
296         facdir = dot_v3v3(arrow_no, offset) < 0.0f ? -1 : 1;
297
298         GizmoCommonData *data = &arrow->data;
299         const float ofs_new = facdir * len_v3(offset);
300
301         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
302
303         /* set the property for the operator and call its modal function */
304         if (WM_gizmo_target_property_is_valid(gz_prop)) {
305                 const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
306                 const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
307                 const bool inverted = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_INVERTED) != 0;
308                 const bool use_precision = (tweak_flag & WM_GIZMO_TWEAK_PRECISE) != 0;
309                 float value = gizmo_value_from_offset(data, inter, ofs_new, constrained, inverted, use_precision);
310
311                 WM_gizmo_target_property_float_set(C, gz, gz_prop, value);
312                 /* get clamped value */
313                 value = WM_gizmo_target_property_float_get(gz, gz_prop);
314
315                 data->offset = gizmo_offset_from_value(data, value, constrained, inverted);
316         }
317         else {
318                 data->offset = ofs_new;
319         }
320
321         /* tag the region for redraw */
322         ED_region_tag_redraw(ar);
323         WM_event_add_mousemove(C);
324
325         return OPERATOR_RUNNING_MODAL;
326 }
327
328 static void gizmo_arrow_setup(wmGizmo *gz)
329 {
330         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
331
332         arrow->gizmo.flag |= WM_GIZMO_DRAW_MODAL;
333
334         arrow->data.range_fac = 1.0f;
335 }
336
337 static int gizmo_arrow_invoke(
338         bContext *UNUSED(C), wmGizmo *gz, const wmEvent *event)
339 {
340         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
341         GizmoInteraction *inter = MEM_callocN(sizeof(GizmoInteraction), __func__);
342         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
343
344         /* Some gizmos don't use properties. */
345         if (WM_gizmo_target_property_is_valid(gz_prop)) {
346                 inter->init_value = WM_gizmo_target_property_float_get(gz, gz_prop);
347         }
348
349         inter->init_offset = arrow->data.offset;
350
351         inter->init_mval[0] = event->mval[0];
352         inter->init_mval[1] = event->mval[1];
353
354         gizmo_arrow_matrix_basis_get(gz, inter->init_matrix_basis);
355         WM_gizmo_calc_matrix_final(gz, inter->init_matrix_final);
356
357         gz->interaction_data = inter;
358
359         return OPERATOR_RUNNING_MODAL;
360 }
361
362 static void gizmo_arrow_property_update(wmGizmo *gz, wmGizmoProperty *gz_prop)
363 {
364         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
365         const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
366         const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
367         const bool inverted = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_INVERTED) != 0;
368         gizmo_property_data_update(gz, &arrow->data, gz_prop, constrained, inverted);
369 }
370
371 static void gizmo_arrow_exit(bContext *C, wmGizmo *gz, const bool cancel)
372 {
373         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
374         GizmoCommonData *data = &arrow->data;
375         wmGizmoProperty *gz_prop = WM_gizmo_target_property_find(gz, "offset");
376         const bool is_prop_valid = WM_gizmo_target_property_is_valid(gz_prop);
377
378         if (!cancel) {
379                 /* Assign incase applying the opetration needs an updated offset
380                  * editmesh bisect needs this. */
381                 if (is_prop_valid) {
382                         const int transform_flag = RNA_enum_get(arrow->gizmo.ptr, "transform");
383                         const bool constrained = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED) != 0;
384                         const bool inverted = (transform_flag & ED_GIZMO_ARROW_XFORM_FLAG_INVERTED) != 0;
385                         const float value = WM_gizmo_target_property_float_get(gz, gz_prop);
386                         data->offset = gizmo_offset_from_value(data, value, constrained, inverted);
387                 }
388                 return;
389         }
390
391         GizmoInteraction *inter = gz->interaction_data;
392         if (is_prop_valid) {
393                 gizmo_property_value_reset(C, gz, inter, gz_prop);
394         }
395         data->offset = inter->init_offset;
396 }
397
398
399 /* -------------------------------------------------------------------- */
400 /** \name Arrow Gizmo API
401  *
402  * \{ */
403
404 /**
405  * Define a custom property UI range
406  *
407  * \note Needs to be called before WM_gizmo_target_property_def_rna!
408  */
409 void ED_gizmo_arrow3d_set_ui_range(wmGizmo *gz, const float min, const float max)
410 {
411         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
412
413         BLI_assert(min < max);
414         BLI_assert(!(WM_gizmo_target_property_is_valid(WM_gizmo_target_property_find(gz, "offset")) &&
415                      "Make sure this function is called before WM_gizmo_target_property_def_rna"));
416
417         arrow->data.range = max - min;
418         arrow->data.min = min;
419         arrow->data.max = max;
420         arrow->data.is_custom_range_set = true;
421 }
422
423 /**
424  * Define a custom factor for arrow min/max distance
425  *
426  * \note Needs to be called before WM_gizmo_target_property_def_rna!
427  */
428 void ED_gizmo_arrow3d_set_range_fac(wmGizmo *gz, const float range_fac)
429 {
430         ArrowGizmo3D *arrow = (ArrowGizmo3D *)gz;
431         BLI_assert(!(WM_gizmo_target_property_is_valid(WM_gizmo_target_property_find(gz, "offset")) &&
432                      "Make sure this function is called before WM_gizmo_target_property_def_rna"));
433
434         arrow->data.range_fac = range_fac;
435 }
436
437 static void GIZMO_GT_arrow_3d(wmGizmoType *gzt)
438 {
439         /* identifiers */
440         gzt->idname = "GIZMO_GT_arrow_3d";
441
442         /* api callbacks */
443         gzt->draw = gizmo_arrow_draw;
444         gzt->draw_select = gizmo_arrow_draw_select;
445         gzt->matrix_basis_get = gizmo_arrow_matrix_basis_get;
446         gzt->modal = gizmo_arrow_modal;
447         gzt->setup = gizmo_arrow_setup;
448         gzt->invoke = gizmo_arrow_invoke;
449         gzt->property_update = gizmo_arrow_property_update;
450         gzt->exit = gizmo_arrow_exit;
451
452         gzt->struct_size = sizeof(ArrowGizmo3D);
453
454         /* rna */
455         static EnumPropertyItem rna_enum_draw_style_items[] = {
456                 {ED_GIZMO_ARROW_STYLE_NORMAL, "NORMAL", 0, "Normal", ""},
457                 {ED_GIZMO_ARROW_STYLE_CROSS, "CROSS", 0, "Cross", ""},
458                 {ED_GIZMO_ARROW_STYLE_BOX, "BOX", 0, "Box", ""},
459                 {ED_GIZMO_ARROW_STYLE_CONE, "CONE", 0, "Cone", ""},
460                 {0, NULL, 0, NULL, NULL}
461         };
462         static EnumPropertyItem rna_enum_draw_options_items[] = {
463                 {ED_GIZMO_ARROW_DRAW_FLAG_STEM, "STEM", 0, "Stem", ""},
464                 {0, NULL, 0, NULL, NULL}
465         };
466         static EnumPropertyItem rna_enum_transform_items[] = {
467                 {ED_GIZMO_ARROW_XFORM_FLAG_INVERTED, "INVERT", 0, "Inverted", ""},
468                 {ED_GIZMO_ARROW_XFORM_FLAG_CONSTRAINED, "CONSTRAIN", 0, "Constrained", ""},
469                 {0, NULL, 0, NULL, NULL}
470         };
471
472         RNA_def_enum(
473                 gzt->srna, "draw_style", rna_enum_draw_style_items,
474                 ED_GIZMO_ARROW_STYLE_NORMAL,
475                 "Draw Style", "");
476         RNA_def_enum_flag(
477                 gzt->srna, "draw_options", rna_enum_draw_options_items,
478                 ED_GIZMO_ARROW_DRAW_FLAG_STEM,
479                 "Draw Options", "");
480         RNA_def_enum_flag(
481                 gzt->srna, "transform", rna_enum_transform_items,
482                 0,
483                 "Transform", "");
484
485         RNA_def_float(gzt->srna, "length", 1.0f, 0.0f, FLT_MAX, "Arrow Line Length", "", 0.0f, FLT_MAX);
486         RNA_def_float_vector(gzt->srna, "aspect", 2, NULL, 0, FLT_MAX, "Aspect", "Cone/box style only", 0.0f, FLT_MAX);
487
488         WM_gizmotype_target_property_def(gzt, "offset", PROP_FLOAT, 1);
489 }
490
491 void ED_gizmotypes_arrow_3d(void)
492 {
493         WM_gizmotype_append(GIZMO_GT_arrow_3d);
494 }
495
496 /** \} */