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