use COMP instead of GL enum to construct vertex format
[blender.git] / source / blender / windowmanager / manipulators / intern / manipulator_library / dial_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  * 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 blender/windowmanager/manipulators/intern/manipulator_library/dial_manipulator.c
27  *  \ingroup wm
28  *
29  * \name Dial Manipulator
30  *
31  * 3D Manipulator
32  *
33  * \brief Circle shaped manipulator for circular interaction.
34  * Currently no own handling, use with operator only.
35  */
36
37 #include "BIF_gl.h"
38 #include "BIF_glutil.h"
39
40 #include "BKE_context.h"
41
42 #include "BLI_math.h"
43
44 #include "DNA_manipulator_types.h"
45
46 #include "ED_screen.h"
47 #include "ED_view3d.h"
48
49 #include "GPU_select.h"
50
51 #include "GPU_matrix.h"
52
53 #include "GPU_immediate.h"
54 #include "GPU_immediate_util.h"
55
56 #include "MEM_guardedalloc.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60
61 /* own includes */
62 #include "WM_manipulator_types.h"
63 #include "WM_manipulator_library.h"
64 #include "wm_manipulator_wmapi.h"
65 #include "wm_manipulator_intern.h"
66 #include "manipulator_geometry.h"
67 #include "manipulator_library_intern.h"
68
69 /* to use custom dials exported to geom_dial_manipulator.c */
70 // #define USE_MANIPULATOR_CUSTOM_DIAL
71
72 typedef struct DialManipulator {
73         wmManipulator manipulator;
74         int style;
75         float direction[3];
76 } DialManipulator;
77
78 typedef struct DialInteraction {
79         float init_mval[2];
80
81         /* cache the last angle to detect rotations bigger than -/+ PI */
82         float last_angle;
83         /* number of full rotations */
84         int rotations;
85 } DialInteraction;
86
87 #define DIAL_WIDTH       1.0f
88 #define DIAL_RESOLUTION 32
89
90 /* -------------------------------------------------------------------- */
91
92 static void dial_geom_draw(
93         const DialManipulator *dial, const float col[4], const bool select,
94         float axis_modal_mat[4][4], float clip_plane[4])
95 {
96 #ifdef USE_MANIPULATOR_CUSTOM_DIAL
97         UNUSED_VARS(dial, col, axis_modal_mat, clip_plane);
98         wm_manipulator_geometryinfo_draw(&wm_manipulator_geom_data_dial, select);
99 #else
100         const bool filled = (dial->style == MANIPULATOR_DIAL_STYLE_RING_FILLED);
101
102         glLineWidth(dial->manipulator.line_width);
103
104         VertexFormat *format = immVertexFormat();
105         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
106
107         if (clip_plane) {
108                 immBindBuiltinProgram(GPU_SHADER_3D_CLIPPED_UNIFORM_COLOR);
109                 float clip_plane_f[4] = {clip_plane[0], clip_plane[1], clip_plane[2], clip_plane[3]};
110                 immUniform4fv("ClipPlane", clip_plane_f);
111                 immUniformMatrix4fv("ModelMatrix", axis_modal_mat);
112         }
113         else {
114                 immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
115         }
116
117         immUniformColor4fv(col);
118
119         if (filled) {
120                 imm_draw_circle_fill(pos, 0, 0, 1.0, DIAL_RESOLUTION);
121         }
122         else {
123                 imm_draw_circle_wire(pos, 0, 0, 1.0, DIAL_RESOLUTION);
124         }
125
126         immUnbindProgram();
127
128         UNUSED_VARS(select);
129 #endif
130 }
131
132 /**
133  * Draws a line from (0, 0, 0) to \a co_outer, at \a angle.
134  */
135 static void dial_ghostarc_draw_helpline(const float angle, const float co_outer[3], const float col[4])
136 {
137         glLineWidth(1.0f);
138
139         gpuPushMatrix();
140         gpuRotate3f(RAD2DEGF(angle), 0.0f, 0.0f, -1.0f);
141
142         unsigned int pos = VertexFormat_add_attrib(immVertexFormat(), "pos", COMP_F32, 3, KEEP_FLOAT);
143
144         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
145
146         immUniformColor4fv(col);
147
148         immBegin(PRIM_LINE_STRIP, 2);
149         immVertex3f(pos, 0.0f, 0.0f, 0.0f);
150         immVertex3fv(pos, co_outer);
151         immEnd();
152
153         immUnbindProgram();
154
155         gpuPopMatrix();
156 }
157
158 static void dial_ghostarc_draw(
159         const DialManipulator *dial, const float angle_ofs, const float angle_delta, const float color[4])
160 {
161         const float width_inner = DIAL_WIDTH - dial->manipulator.line_width * 0.5f / U.manipulator_scale;
162
163         VertexFormat *format = immVertexFormat();
164         unsigned int pos = VertexFormat_add_attrib(format, "pos", COMP_F32, 2, KEEP_FLOAT);
165         immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR);
166         immUniformColor4fv(color);
167         imm_draw_disk_partial_fill(
168                 pos, 0, 0, 0.0, width_inner, DIAL_RESOLUTION, RAD2DEGF(angle_ofs), RAD2DEGF(angle_delta));
169         immUnbindProgram();
170 }
171
172 static void dial_ghostarc_get_angles(
173         const DialManipulator *dial, const wmEvent *event, const ARegion *ar,
174         float mat[4][4], const float co_outer[3],
175         float *r_start, float *r_delta)
176 {
177         DialInteraction *inter = dial->manipulator.interaction_data;
178         const RegionView3D *rv3d = ar->regiondata;
179         const float mval[2] = {event->x - ar->winrct.xmin, event->y - ar->winrct.ymin};
180         bool inv = false;
181
182         /* we might need to invert the direction of the angles */
183         float view_vec[3], axis_vec[3];
184         ED_view3d_global_to_vector(rv3d, rv3d->twmat[3], view_vec);
185         normalize_v3_v3(axis_vec, dial->direction);
186         if (dot_v3v3(view_vec, axis_vec) < 0.0f) {
187                 inv = true;
188         }
189
190         float co[3], origin2d[2], co2d[2];
191         mul_v3_project_m4_v3(co, mat, co_outer);
192         /* project 3d coordinats to 2d viewplane */
193         ED_view3d_project_float_global(ar, dial->manipulator.origin, origin2d, V3D_PROJ_TEST_NOP);
194         ED_view3d_project_float_global(ar, co, co2d, V3D_PROJ_TEST_NOP);
195
196         /* convert to manipulator relative space */
197         float rel_initmval[2], rel_mval[2], rel_co[2];
198         sub_v2_v2v2(rel_initmval, inter->init_mval, origin2d);
199         sub_v2_v2v2(rel_mval, mval, origin2d);
200         sub_v2_v2v2(rel_co, co2d, origin2d);
201
202         /* return angles */
203         const float start = angle_signed_v2v2(rel_co, rel_initmval) * (inv ? -1 : 1);
204         const float delta = angle_signed_v2v2(rel_initmval, rel_mval) * (inv ? -1 : 1);
205
206         /* Change of sign, we passed the 180 degree threshold. This means we need to add a turn
207          * to distinguish between transition from 0 to -1 and -PI to +PI, use comparison with PI/2.
208          * Logic taken from BLI_dial_angle */
209         if ((delta * inter->last_angle < 0.0f) &&
210             (fabsf(inter->last_angle) > (float)M_PI_2))
211         {
212                 if (inter->last_angle < 0.0f)
213                         inter->rotations--;
214                 else
215                         inter->rotations++;
216         }
217         inter->last_angle = delta;
218
219         *r_start = start;
220         *r_delta = fmod(delta + 2.0f * (float)M_PI * inter->rotations, 2 * (float)M_PI);
221 }
222
223 static void dial_draw_intern(
224         const bContext *C, DialManipulator *dial,
225         const bool select, const bool highlight, float clip_plane[4])
226 {
227         float rot[3][3];
228         float mat[4][4];
229         const float up[3] = {0.0f, 0.0f, 1.0f};
230         float col[4];
231
232         BLI_assert(CTX_wm_area(C)->spacetype == SPACE_VIEW3D);
233
234         manipulator_color_get(&dial->manipulator, highlight, col);
235
236         rotation_between_vecs_to_mat3(rot, up, dial->direction);
237         copy_m4_m3(mat, rot);
238         copy_v3_v3(mat[3], dial->manipulator.origin);
239         mul_mat3_m4_fl(mat, dial->manipulator.scale);
240
241         gpuPushMatrix();
242         gpuMultMatrix3D(mat);
243         gpuTranslate3fv(dial->manipulator.offset);
244
245         /* draw rotation indicator arc first */
246         if ((dial->manipulator.flag & WM_MANIPULATOR_DRAW_VALUE) && (dial->manipulator.state & WM_MANIPULATOR_ACTIVE)) {
247                 wmWindow *win = CTX_wm_window(C);
248                 const float co_outer[4] = {0.0f, DIAL_WIDTH, 0.0f}; /* coordinate at which the arc drawing will be started */
249                 float angle_ofs, angle_delta;
250
251                 dial_ghostarc_get_angles(dial, win->eventstate, CTX_wm_region(C), mat, co_outer, &angle_ofs, &angle_delta);
252                 /* draw! */
253                 dial_ghostarc_draw(dial, angle_ofs, angle_delta, (const float [4]){0.8f, 0.8f, 0.8f, 0.4f});
254
255                 dial_ghostarc_draw_helpline(angle_ofs, co_outer, col); /* starting position */
256                 dial_ghostarc_draw_helpline(angle_ofs + angle_delta, co_outer, col); /* starting position + current value */
257         }
258
259         /* draw actual dial manipulator */
260         dial_geom_draw(dial, col, select, mat, clip_plane);
261
262         gpuPopMatrix();
263 }
264
265 static void manipulator_dial_render_3d_intersect(const bContext *C, wmManipulator *manipulator, int selectionbase)
266 {
267         DialManipulator *dial = (DialManipulator *)manipulator;
268         float clip_plane_buf[4];
269         float *clip_plane = (dial->style == MANIPULATOR_DIAL_STYLE_RING_CLIPPED) ? clip_plane_buf : NULL;
270
271         /* enable clipping if needed */
272         if (clip_plane) {
273                 ARegion *ar = CTX_wm_region(C);
274                 RegionView3D *rv3d = ar->regiondata;
275
276                 copy_v3_v3(clip_plane, rv3d->viewinv[2]);
277                 clip_plane[3] = -dot_v3v3(rv3d->viewinv[2], manipulator->origin);
278                 glEnable(GL_CLIP_DISTANCE0);
279         }
280
281         GPU_select_load_id(selectionbase);
282         dial_draw_intern(C, dial, true, false, clip_plane);
283
284         if (clip_plane) {
285                 glDisable(GL_CLIP_DISTANCE0);
286         }
287 }
288
289 static void manipulator_dial_draw(const bContext *C, wmManipulator *manipulator)
290 {
291         DialManipulator *dial = (DialManipulator *)manipulator;
292         const bool active = manipulator->state & WM_MANIPULATOR_ACTIVE;
293         const bool highlight = (manipulator->state & WM_MANIPULATOR_HIGHLIGHT) != 0;
294         float clip_plane_buf[4];
295         float *clip_plane = (!active && dial->style == MANIPULATOR_DIAL_STYLE_RING_CLIPPED) ? clip_plane_buf : NULL;
296
297         /* enable clipping if needed */
298         if (clip_plane) {
299                 ARegion *ar = CTX_wm_region(C);
300                 RegionView3D *rv3d = ar->regiondata;
301
302                 copy_v3_v3(clip_plane, rv3d->viewinv[2]);
303                 clip_plane[3] = -dot_v3v3(rv3d->viewinv[2], manipulator->origin);
304                 clip_plane[3] -= 0.02 * dial->manipulator.scale;
305
306                 glEnable(GL_CLIP_DISTANCE0);
307         }
308
309         glEnable(GL_BLEND);
310         dial_draw_intern(C, dial, false, highlight, clip_plane);
311         glDisable(GL_BLEND);
312
313         if (clip_plane) {
314                 glDisable(GL_CLIP_DISTANCE0);
315         }
316 }
317
318 static int manipulator_dial_invoke(bContext *UNUSED(C), const wmEvent *event, wmManipulator *manipulator)
319 {
320         DialInteraction *inter = MEM_callocN(sizeof(DialInteraction), __func__);
321
322         inter->init_mval[0] = event->mval[0];
323         inter->init_mval[1] = event->mval[1];
324
325         manipulator->interaction_data = inter;
326
327         return OPERATOR_RUNNING_MODAL;
328 }
329
330
331 /* -------------------------------------------------------------------- */
332 /** \name Dial Manipulator API
333  *
334  * \{ */
335
336 wmManipulator *MANIPULATOR_dial_new(wmManipulatorGroup *mgroup, const char *name, const int style)
337 {
338         DialManipulator *dial = MEM_callocN(sizeof(DialManipulator), name);
339         const float dir_default[3] = {0.0f, 0.0f, 1.0f};
340
341         dial->manipulator.draw = manipulator_dial_draw;
342         dial->manipulator.intersect = NULL;
343         dial->manipulator.render_3d_intersection = manipulator_dial_render_3d_intersect;
344         dial->manipulator.invoke = manipulator_dial_invoke;
345
346         dial->style = style;
347
348         /* defaults */
349         copy_v3_v3(dial->direction, dir_default);
350
351         wm_manipulator_register(mgroup, &dial->manipulator, name);
352
353         return (wmManipulator *)dial;
354 }
355
356 /**
357  * Define up-direction of the dial manipulator
358  */
359 void MANIPULATOR_dial_set_up_vector(wmManipulator *manipulator, const float direction[3])
360 {
361         DialManipulator *dial = (DialManipulator *)manipulator;
362
363         copy_v3_v3(dial->direction, direction);
364         normalize_v3(dial->direction);
365 }
366
367 /** \} */ // Dial Manipulator API
368
369
370 /* -------------------------------------------------------------------- */
371
372 void fix_linking_manipulator_dial(void)
373 {
374         (void)0;
375 }