Draw: Fix crash when tryign to get procedural textures with modifier disabled
[blender.git] / source / blender / windowmanager / intern / wm_gesture.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) 2008 Blender Foundation.
19  * All rights reserved.
20  *
21  *
22  * Contributor(s): Blender Foundation
23  *
24  * ***** END GPL LICENSE BLOCK *****
25  */
26
27 /** \file blender/windowmanager/intern/wm_gesture.c
28  *  \ingroup wm
29  *
30  * Gestures (cursor motions) creating, evaluating and drawing, shared between operators.
31  */
32
33 #include "DNA_screen_types.h"
34 #include "DNA_vec_types.h"
35 #include "DNA_userdef_types.h"
36 #include "DNA_windowmanager_types.h"
37
38 #include "MEM_guardedalloc.h"
39
40 #include "BLI_bitmap_draw_2d.h"
41 #include "BLI_blenlib.h"
42 #include "BLI_math.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_lasso_2d.h"
45
46 #include "BKE_context.h"
47
48 #include "WM_api.h"
49 #include "WM_types.h"
50
51 #include "wm.h"
52 #include "wm_draw.h"
53
54 #include "GPU_immediate.h"
55 #include "GPU_immediate_util.h"
56
57 #include "BIF_glutil.h"
58
59
60 /* context checked on having screen, window and area */
61 wmGesture *WM_gesture_new(bContext *C, const wmEvent *event, int type)
62 {
63         wmGesture *gesture = MEM_callocN(sizeof(wmGesture), "new gesture");
64         wmWindow *window = CTX_wm_window(C);
65         ARegion *ar = CTX_wm_region(C);
66         
67         BLI_addtail(&window->gesture, gesture);
68         
69         gesture->type = type;
70         gesture->event_type = event->type;
71         gesture->winrct = ar->winrct;
72         gesture->userdata_free = true;   /* Free if userdata is set. */
73         gesture->modal_state = GESTURE_MODAL_NOP;
74         
75         if (ELEM(type, WM_GESTURE_RECT, WM_GESTURE_CROSS_RECT, WM_GESTURE_TWEAK,
76                   WM_GESTURE_CIRCLE, WM_GESTURE_STRAIGHTLINE))
77         {
78                 rcti *rect = MEM_callocN(sizeof(rcti), "gesture rect new");
79                 
80                 gesture->customdata = rect;
81                 rect->xmin = event->x - gesture->winrct.xmin;
82                 rect->ymin = event->y - gesture->winrct.ymin;
83                 if (type == WM_GESTURE_CIRCLE) {
84                         /* caller is responsible for initializing 'xmax' to radius. */
85                 }
86                 else {
87                         rect->xmax = event->x - gesture->winrct.xmin;
88                         rect->ymax = event->y - gesture->winrct.ymin;
89                 }
90         }
91         else if (ELEM(type, WM_GESTURE_LINES, WM_GESTURE_LASSO)) {
92                 short *lasso;
93                 gesture->points_alloc = 1024;
94                 gesture->customdata = lasso = MEM_mallocN(sizeof(short[2]) * gesture->points_alloc, "lasso points");
95                 lasso[0] = event->x - gesture->winrct.xmin;
96                 lasso[1] = event->y - gesture->winrct.ymin;
97                 gesture->points = 1;
98         }
99         
100         return gesture;
101 }
102
103 void WM_gesture_end(bContext *C, wmGesture *gesture)
104 {
105         wmWindow *win = CTX_wm_window(C);
106         
107         if (win->tweak == gesture)
108                 win->tweak = NULL;
109         BLI_remlink(&win->gesture, gesture);
110         MEM_freeN(gesture->customdata);
111         if (gesture->userdata && gesture->userdata_free) {
112                 MEM_freeN(gesture->userdata);
113         }
114         MEM_freeN(gesture);
115 }
116
117 void WM_gestures_remove(bContext *C)
118 {
119         wmWindow *win = CTX_wm_window(C);
120         
121         while (win->gesture.first)
122                 WM_gesture_end(C, win->gesture.first);
123 }
124
125
126 /* tweak and line gestures */
127 int wm_gesture_evaluate(wmGesture *gesture)
128 {
129         if (gesture->type == WM_GESTURE_TWEAK) {
130                 rcti *rect = gesture->customdata;
131                 int dx = BLI_rcti_size_x(rect);
132                 int dy = BLI_rcti_size_y(rect);
133                 if (abs(dx) + abs(dy) > U.tweak_threshold) {
134                         int theta = round_fl_to_int(4.0f * atan2f((float)dy, (float)dx) / (float)M_PI);
135                         int val = EVT_GESTURE_W;
136
137                         if (theta == 0) val = EVT_GESTURE_E;
138                         else if (theta == 1) val = EVT_GESTURE_NE;
139                         else if (theta == 2) val = EVT_GESTURE_N;
140                         else if (theta == 3) val = EVT_GESTURE_NW;
141                         else if (theta == -1) val = EVT_GESTURE_SE;
142                         else if (theta == -2) val = EVT_GESTURE_S;
143                         else if (theta == -3) val = EVT_GESTURE_SW;
144                         
145 #if 0
146                         /* debug */
147                         if (val == 1) printf("tweak north\n");
148                         if (val == 2) printf("tweak north-east\n");
149                         if (val == 3) printf("tweak east\n");
150                         if (val == 4) printf("tweak south-east\n");
151                         if (val == 5) printf("tweak south\n");
152                         if (val == 6) printf("tweak south-west\n");
153                         if (val == 7) printf("tweak west\n");
154                         if (val == 8) printf("tweak north-west\n");
155 #endif
156                         return val;
157                 }
158         }
159         return 0;
160 }
161
162
163 /* ******************* gesture draw ******************* */
164
165 static void wm_gesture_draw_line(wmGesture *gt)
166 {
167         rcti *rect = (rcti *)gt->customdata;
168
169         uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
170
171         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
172
173         float viewport_size[4];
174         glGetFloatv(GL_VIEWPORT, viewport_size);
175         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
176
177         immUniform1i("num_colors", 2);  /* "advanced" mode */
178         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
179         immUniform1f("dash_width", 8.0f);
180
181         float xmin = (float)rect->xmin;
182         float ymin = (float)rect->ymin;
183
184         immBegin(GWN_PRIM_LINES, 2);
185         immVertex2f(shdr_pos, xmin, ymin);
186         immVertex2f(shdr_pos, (float)rect->xmax, (float)rect->ymax);
187         immEnd();
188
189         immUnbindProgram();
190 }
191
192 static void wm_gesture_draw_rect(wmGesture *gt)
193 {
194         rcti *rect = (rcti *)gt->customdata;
195
196         uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_I32, 2, GWN_FETCH_INT_TO_FLOAT);
197
198         glEnable(GL_BLEND);
199
200         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
201         immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
202
203         immRecti(shdr_pos, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
204
205         immUnbindProgram();
206
207         glDisable(GL_BLEND);
208
209         shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
210
211         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
212
213         float viewport_size[4];
214         glGetFloatv(GL_VIEWPORT, viewport_size);
215         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
216
217         immUniform1i("num_colors", 2);  /* "advanced" mode */
218         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
219         immUniform1f("dash_width", 8.0f);
220
221         imm_draw_box_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, (float)rect->ymax);
222
223         immUnbindProgram();
224
225         // wm_gesture_draw_line(gt); // draws a diagonal line in the lined box to test wm_gesture_draw_line
226 }
227
228 static void wm_gesture_draw_circle(wmGesture *gt)
229 {
230         rcti *rect = (rcti *)gt->customdata;
231
232         glEnable(GL_BLEND);
233
234         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
235
236         immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR);
237
238         immUniformColor4f(1.0f, 1.0f, 1.0f, 0.05f);
239         imm_draw_circle_fill_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
240
241         immUnbindProgram();
242
243         glDisable(GL_BLEND);
244
245         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
246
247         float viewport_size[4];
248         glGetFloatv(GL_VIEWPORT, viewport_size);
249         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
250
251         immUniform1i("num_colors", 2);  /* "advanced" mode */
252         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
253         immUniform1f("dash_width", 4.0f);
254
255         imm_draw_circle_wire_2d(shdr_pos, (float)rect->xmin, (float)rect->ymin, (float)rect->xmax, 40);
256
257         immUnbindProgram();
258 }
259
260 struct LassoFillData {
261         unsigned char *px;
262         int width;
263 };
264
265 static void draw_filled_lasso_px_cb(int x, int x_end, int y, void *user_data)
266 {
267         struct LassoFillData *data = user_data;
268         unsigned char *col = &(data->px[(y * data->width) + x]);
269         memset(col, 0x10, x_end - x);
270 }
271
272 static void draw_filled_lasso(wmGesture *gt)
273 {
274         const short *lasso = (short *)gt->customdata;
275         const int tot = gt->points;
276         int (*moves)[2] = MEM_mallocN(sizeof(*moves) * (tot + 1), __func__);
277         int i;
278         rcti rect;
279         float red[4] = {1.0f, 0.0f, 0.0f, 0.0f};
280
281         for (i = 0; i < tot; i++, lasso += 2) {
282                 moves[i][0] = lasso[0];
283                 moves[i][1] = lasso[1];
284         }
285
286         BLI_lasso_boundbox(&rect, (const int (*)[2])moves, tot);
287
288         BLI_rcti_translate(&rect, gt->winrct.xmin, gt->winrct.ymin);
289         BLI_rcti_isect(&gt->winrct, &rect, &rect);
290         BLI_rcti_translate(&rect, -gt->winrct.xmin, -gt->winrct.ymin);
291
292         /* highly unlikely this will fail, but could crash if (tot == 0) */
293         if (BLI_rcti_is_empty(&rect) == false) {
294                 const int w = BLI_rcti_size_x(&rect);
295                 const int h = BLI_rcti_size_y(&rect);
296                 unsigned char *pixel_buf = MEM_callocN(sizeof(*pixel_buf) * w * h, __func__);
297                 struct LassoFillData lasso_fill_data = {pixel_buf, w};
298
299                 BLI_bitmap_draw_2d_poly_v2i_n(
300                        rect.xmin, rect.ymin, rect.xmax, rect.ymax,
301                        (const int (*)[2])moves, tot,
302                        draw_filled_lasso_px_cb, &lasso_fill_data);
303
304                 /* Additive Blending */
305                 glEnable(GL_BLEND);
306                 glBlendFunc(GL_ONE, GL_ONE);
307
308                 GLint unpack_alignment;
309                 glGetIntegerv(GL_UNPACK_ALIGNMENT, &unpack_alignment);
310
311                 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
312
313                 IMMDrawPixelsTexState state = immDrawPixelsTexSetup(GPU_SHADER_2D_IMAGE_SHUFFLE_COLOR);
314                 GPU_shader_bind(state.shader);
315                 GPU_shader_uniform_vector(state.shader, GPU_shader_get_uniform(state.shader, "shuffle"), 4, 1, red);
316
317                 immDrawPixelsTex(&state, rect.xmin, rect.ymin, w, h, GL_RED, GL_UNSIGNED_BYTE, GL_NEAREST, pixel_buf, 1.0f, 1.0f, NULL);
318
319                 GPU_shader_unbind();
320
321                 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack_alignment);
322
323                 MEM_freeN(pixel_buf);
324
325                 glDisable(GL_BLEND);
326                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
327         }
328
329         MEM_freeN(moves);
330 }
331
332
333 static void wm_gesture_draw_lasso(wmGesture *gt, bool filled)
334 {
335         const short *lasso = (short *)gt->customdata;
336         int i;
337
338         if (filled) {
339                 draw_filled_lasso(gt);
340         }
341
342         const int numverts = gt->points;
343
344         /* Nothing to draw, do early output. */
345         if (numverts < 2) {
346                 return;
347         }
348
349         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
350
351         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
352
353         float viewport_size[4];
354         glGetFloatv(GL_VIEWPORT, viewport_size);
355         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
356
357         immUniform1i("num_colors", 2);  /* "advanced" mode */
358         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
359         immUniform1f("dash_width", 2.0f);
360
361         immBegin((gt->type == WM_GESTURE_LASSO) ? GWN_PRIM_LINE_LOOP : GWN_PRIM_LINE_STRIP, numverts);
362
363         for (i = 0; i < gt->points; i++, lasso += 2) {
364                 immVertex2f(shdr_pos, (float)lasso[0], (float)lasso[1]);
365         }
366
367         immEnd();
368
369         immUnbindProgram();
370 }
371
372 static void wm_gesture_draw_cross(wmWindow *win, wmGesture *gt)
373 {
374         rcti *rect = (rcti *)gt->customdata;
375         const int winsize_x = WM_window_pixels_x(win);
376         const int winsize_y = WM_window_pixels_y(win);
377
378         float x1, x2, y1, y2;
379
380         const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 2, GWN_FETCH_FLOAT);
381
382         immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR);
383
384         float viewport_size[4];
385         glGetFloatv(GL_VIEWPORT, viewport_size);
386         immUniform2f("viewport_size", viewport_size[2], viewport_size[3]);
387
388         immUniform1i("num_colors", 2);  /* "advanced" mode */
389         immUniformArray4fv("colors", (float *)(float[][4]){{0.4f, 0.4f, 0.4f, 1.0f}, {1.0f, 1.0f, 1.0f, 1.0f}}, 2);
390         immUniform1f("dash_width", 8.0f);
391
392         immBegin(GWN_PRIM_LINES, 4);
393
394         x1 = (float)(rect->xmin - winsize_x);
395         y1 = (float)rect->ymin;
396         x2 = (float)(rect->xmin + winsize_x);
397         y2 = y1;
398
399         immVertex2f(shdr_pos, x1, y1);
400         immVertex2f(shdr_pos, x2, y2);
401
402         x1 = (float)rect->xmin;
403         y1 = (float)(rect->ymin - winsize_y);
404         x2 = x1;
405         y2 = (float)(rect->ymin + winsize_y);
406
407         immVertex2f(shdr_pos, x1, y1);
408         immVertex2f(shdr_pos, x2, y2);
409
410         immEnd();
411
412         immUnbindProgram();
413 }
414
415 /* called in wm_draw.c */
416 void wm_gesture_draw(wmWindow *win)
417 {
418         wmGesture *gt = (wmGesture *)win->gesture.first;
419
420         glLineWidth(1.0f);
421         for (; gt; gt = gt->next) {
422                 /* all in subwindow space */
423                 wmViewport(&gt->winrct);
424                 
425                 if (gt->type == WM_GESTURE_RECT)
426                         wm_gesture_draw_rect(gt);
427 //              else if (gt->type == WM_GESTURE_TWEAK)
428 //                      wm_gesture_draw_line(gt);
429                 else if (gt->type == WM_GESTURE_CIRCLE)
430                         wm_gesture_draw_circle(gt);
431                 else if (gt->type == WM_GESTURE_CROSS_RECT) {
432                         if (gt->is_active) {
433                                 wm_gesture_draw_rect(gt);
434                         }
435                         else {
436                                 wm_gesture_draw_cross(win, gt);
437                         }
438                 }
439                 else if (gt->type == WM_GESTURE_LINES)
440                         wm_gesture_draw_lasso(gt, false);
441                 else if (gt->type == WM_GESTURE_LASSO)
442                         wm_gesture_draw_lasso(gt, true);
443                 else if (gt->type == WM_GESTURE_STRAIGHTLINE)
444                         wm_gesture_draw_line(gt);
445         }
446 }
447
448 void wm_gesture_tag_redraw(bContext *C)
449 {
450         bScreen *screen = CTX_wm_screen(C);
451         
452         if (screen)
453                 screen->do_draw_gesture = true;
454 }