Manipulator: grab3d option to align to view
[blender.git] / source / blender / editors / manipulator_library / manipulator_types / grab3d_manipulator.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 grab3d_manipulator.c
22  *  \ingroup wm
23  *
24  * \name Grab Manipulator
25  *
26  * 3D Manipulator, also works in 2D views.
27  *
28  * \brief Simple manipulator to grab and translate.
29  *
30  * - `matrix[0]` is derived from Y and Z.
31  * - `matrix[1]` currently not used.
32  * - `matrix[2]` is the widget direction (for all manipulators).
33  *
34  */
35
36 #include "BIF_gl.h"
37 #include "BIF_glutil.h"
38
39 #include "BKE_context.h"
40
41 #include "BLI_math.h"
42
43 #include "ED_screen.h"
44 #include "ED_view3d.h"
45 #include "ED_manipulator_library.h"
46
47 #include "GPU_select.h"
48
49 #include "GPU_matrix.h"
50
51 #include "GPU_immediate.h"
52 #include "GPU_immediate_util.h"
53
54 #include "MEM_guardedalloc.h"
55
56 #include "RNA_access.h"
57 #include "RNA_define.h"
58
59 #include "WM_api.h"
60 #include "WM_types.h"
61
62 /* own includes */
63 #include "../manipulator_geometry.h"
64 #include "../manipulator_library_intern.h"
65
66 static void manipulator_grab_modal(
67         bContext *C, wmManipulator *mpr, const wmEvent *event,
68         eWM_ManipulatorTweak tweak_flag);
69
70 typedef struct GrabInteraction {
71         float init_mval[2];
72
73         /* only for when using properties */
74         float init_prop_co[3];
75
76         float init_matrix_basis[4][4];
77         float init_scale_final;
78
79         /* final output values, used for drawing */
80         struct {
81                 float co_final[3];
82         } output;
83 } GrabInteraction;
84
85 #define DIAL_RESOLUTION 32
86
87 /* -------------------------------------------------------------------- */
88
89 static void grab_geom_draw(
90         const wmManipulator *mpr, const float color[4], const bool select, const int draw_options)
91 {
92 #ifdef USE_MANIPULATOR_CUSTOM_DIAL
93         UNUSED_VARS(grab3d, col, axis_modal_mat);
94         wm_manipulator_geometryinfo_draw(&wm_manipulator_geom_data_grab3d, select);
95 #else
96         const int draw_style = RNA_enum_get(mpr->ptr, "draw_style");
97         const bool filled = (draw_options & ED_MANIPULATOR_GRAB_DRAW_FLAG_FILL) != 0;
98
99         glLineWidth(mpr->line_width);
100
101         Gwn_VertFormat *format = immVertexFormat();
102         uint pos = GWN_vertformat_attr_add(format, "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
103
104         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
105
106         immUniformColor4fv(color);
107
108         if (draw_style == ED_MANIPULATOR_GRAB_STYLE_RING_2D) {
109                 if (filled) {
110                         imm_draw_circle_fill(pos, 0, 0, 1.0f, DIAL_RESOLUTION);
111                 }
112                 else {
113                         imm_draw_circle_wire(pos, 0, 0, 1.0f, DIAL_RESOLUTION);
114                 }
115         }
116         else if (draw_style == ED_MANIPULATOR_GRAB_STYLE_CROSS_2D) {
117                 immBegin(GWN_PRIM_LINES, 4);
118                 immVertex2f(pos,  1.0f,  1.0f);
119                 immVertex2f(pos, -1.0f, -1.0f);
120
121                 immVertex2f(pos, -1.0f,  1.0f);
122                 immVertex2f(pos,  1.0f, -1.0f);
123                 immEnd();
124         }
125         else {
126                 BLI_assert(0);
127         }
128
129         immUnbindProgram();
130
131         UNUSED_VARS(select);
132 #endif
133 }
134
135 static void grab3d_get_translate(
136         const wmManipulator *mpr, const wmEvent *event, const ARegion *ar,
137         float co_delta[3])
138 {
139         GrabInteraction *inter = mpr->interaction_data;
140         const float mval_delta[2] = {
141             event->mval[0] - inter->init_mval[0],
142             event->mval[1] - inter->init_mval[1],
143         };
144
145         RegionView3D *rv3d = ar->regiondata;
146         const float *co_ref = inter->init_prop_co;
147         const float zfac = ED_view3d_calc_zfac(rv3d, co_ref, NULL);
148
149         ED_view3d_win_to_delta(ar, mval_delta, co_delta, zfac);
150 }
151
152 static void grab3d_draw_intern(
153         const bContext *C, wmManipulator *mpr,
154         const bool select, const bool highlight)
155 {
156         const int draw_options = RNA_enum_get(mpr->ptr, "draw_options");
157         const bool align_view = (draw_options & ED_MANIPULATOR_GRAB_DRAW_FLAG_ALIGN_VIEW) != 0;
158         float color[4];
159         float matrix_final[4][4];
160         float matrix_align[4][4];
161
162         manipulator_color_get(mpr, highlight, color);
163
164         WM_manipulator_calc_matrix_final(mpr, matrix_final);
165
166         gpuPushMatrix();
167         gpuMultMatrix(matrix_final);
168
169         if (align_view) {
170                 float matrix_final_unit[4][4];
171                 RegionView3D *rv3d = CTX_wm_region_view3d(C);
172                 normalize_m4_m4(matrix_final_unit, matrix_final);
173                 mul_m4_m4m4(matrix_align, rv3d->viewmat, matrix_final_unit);
174                 zero_v3(matrix_align[3]);
175                 transpose_m4(matrix_align);
176                 gpuMultMatrix(matrix_align);
177         }
178
179         glEnable(GL_BLEND);
180         grab_geom_draw(mpr, color, select, draw_options);
181         glDisable(GL_BLEND);
182         gpuPopMatrix();
183
184         if (mpr->interaction_data) {
185                 GrabInteraction *inter = mpr->interaction_data;
186
187                 WM_manipulator_calc_matrix_final_params(
188                         mpr, &((struct WM_ManipulatorMatrixParams) {
189                             .matrix_basis = inter->init_matrix_basis,
190                             .scale_final = &inter->init_scale_final,
191                         }), matrix_final);
192
193                 gpuPushMatrix();
194                 gpuMultMatrix(matrix_final);
195
196                 if (align_view) {
197                         gpuMultMatrix(matrix_align);
198                 }
199
200                 glEnable(GL_BLEND);
201                 grab_geom_draw(mpr, (const float [4]){0.5f, 0.5f, 0.5f, 0.5f}, select, draw_options);
202                 glDisable(GL_BLEND);
203                 gpuPopMatrix();
204         }
205 }
206
207 static void manipulator_grab_draw_select(const bContext *C, wmManipulator *mpr, int select_id)
208 {
209         GPU_select_load_id(select_id);
210         grab3d_draw_intern(C, mpr, true, false);
211 }
212
213 static void manipulator_grab_draw(const bContext *C, wmManipulator *mpr)
214 {
215         const bool is_modal = mpr->state & WM_MANIPULATOR_STATE_MODAL;
216         const bool is_highlight = (mpr->state & WM_MANIPULATOR_STATE_HIGHLIGHT) != 0;
217
218         (void)is_modal;
219
220         glEnable(GL_BLEND);
221         grab3d_draw_intern(C, mpr, false, is_highlight);
222         glDisable(GL_BLEND);
223 }
224
225 static void manipulator_grab_modal(
226         bContext *C, wmManipulator *mpr, const wmEvent *event,
227         eWM_ManipulatorTweak UNUSED(tweak_flag))
228 {
229         GrabInteraction *inter = mpr->interaction_data;
230
231         if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D) {
232                 grab3d_get_translate(mpr, event, CTX_wm_region(C), mpr->matrix_basis[3]);
233         }
234         else {
235                 float mval_proj_init[2], mval_proj_curr[2];
236                 if ((manipulator_window_project_2d(
237                          C, mpr, inter->init_mval, 2, false, mval_proj_init) == false) ||
238                     (manipulator_window_project_2d(
239                          C, mpr, (const float[2]){UNPACK2(event->mval)}, 2, false, mval_proj_curr) == false))
240                 {
241                         return;
242                 }
243                 sub_v2_v2v2(mpr->matrix_basis[3], mval_proj_curr, mval_proj_init);
244                 mpr->matrix_basis[3][2] = 0.0f;
245         }
246
247         add_v3_v3v3(inter->output.co_final, inter->init_prop_co, mpr->matrix_basis[3]);
248
249         /* set the property for the operator and call its modal function */
250         wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_find(mpr, "offset");
251         if (WM_manipulator_target_property_is_valid(mpr_prop)) {
252                 WM_manipulator_target_property_value_set_array(C, mpr, mpr_prop, inter->output.co_final);
253         }
254 }
255
256 static void manipulator_grab_invoke(
257         bContext *UNUSED(C), wmManipulator *mpr, const wmEvent *event)
258 {
259         GrabInteraction *inter = MEM_callocN(sizeof(GrabInteraction), __func__);
260
261         inter->init_mval[0] = event->mval[0];
262         inter->init_mval[1] = event->mval[1];
263
264         copy_m4_m4(inter->init_matrix_basis, mpr->matrix_basis);
265         inter->init_scale_final = mpr->scale_final;
266
267         wmManipulatorProperty *mpr_prop = WM_manipulator_target_property_find(mpr, "offset");
268         if (WM_manipulator_target_property_is_valid(mpr_prop)) {
269                 WM_manipulator_target_property_value_get_array(mpr, mpr_prop, inter->init_prop_co);
270         }
271
272         mpr->interaction_data = inter;
273 }
274
275
276 static int manipulator_grab_test_select(
277         bContext *C, wmManipulator *mpr, const wmEvent *event)
278 {
279         float point_local[2];
280
281         if (manipulator_window_project_2d(
282                 C, mpr, (const float[2]){UNPACK2(event->mval)}, 2, true, point_local) == false)
283         {
284                 return 0;
285         }
286
287         if (len_squared_v2(point_local) < SQUARE(mpr->scale_final)) {
288                 return true;
289         }
290
291         return 0;
292 }
293
294 static void manipulator_grab_property_update(wmManipulator *mpr, wmManipulatorProperty *mpr_prop)
295 {
296         WM_manipulator_target_property_value_get_array(mpr, mpr_prop, mpr->matrix_basis[3]);
297 }
298
299 static int manipulator_grab_cursor_get(wmManipulator *UNUSED(mpr))
300 {
301         return BC_HANDCURSOR;
302 }
303
304 /* -------------------------------------------------------------------- */
305 /** \name Grab Manipulator API
306  *
307  * \{ */
308
309 static void MANIPULATOR_WT_grab_3d(wmManipulatorType *wt)
310 {
311         /* identifiers */
312         wt->idname = "MANIPULATOR_WT_grab_3d";
313
314         /* api callbacks */
315         wt->draw = manipulator_grab_draw;
316         wt->draw_select = manipulator_grab_draw_select;
317         wt->test_select = manipulator_grab_test_select;
318         wt->invoke = manipulator_grab_invoke;
319         wt->property_update = manipulator_grab_property_update;
320         wt->modal = manipulator_grab_modal;
321         wt->cursor_get = manipulator_grab_cursor_get;
322
323         wt->struct_size = sizeof(wmManipulator);
324
325         /* rna */
326         static EnumPropertyItem rna_enum_draw_style[] = {
327                 {ED_MANIPULATOR_GRAB_STYLE_RING_2D, "RING_2D", 0, "Ring", ""},
328                 {ED_MANIPULATOR_GRAB_STYLE_CROSS_2D, "CROSS_2D", 0, "Ring", ""},
329                 {0, NULL, 0, NULL, NULL}
330         };
331         static EnumPropertyItem rna_enum_draw_options[] = {
332                 {ED_MANIPULATOR_GRAB_DRAW_FLAG_FILL, "FILL", 0, "Filled", ""},
333                 {ED_MANIPULATOR_GRAB_DRAW_FLAG_ALIGN_VIEW, "ALIGN_VIEW", 0, "Align View", ""},
334                 {0, NULL, 0, NULL, NULL}
335         };
336
337         RNA_def_enum(wt->srna, "draw_style", rna_enum_draw_style, ED_MANIPULATOR_GRAB_STYLE_RING_2D, "Draw Style", "");
338         RNA_def_enum_flag(wt->srna, "draw_options", rna_enum_draw_options, 0, "Draw Options", "");
339
340         WM_manipulatortype_target_property_def(wt, "offset", PROP_FLOAT, 3);
341 }
342
343 void ED_manipulatortypes_grab_3d(void)
344 {
345         WM_manipulatortype_append(MANIPULATOR_WT_grab_3d);
346 }
347
348 /** \} */ // Grab Manipulator API