fix for middle mouse up events not working with modal keymaps (used for fly mode).
[blender.git] / source / blender / editors / sculpt_paint / paint_stroke.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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
17  *
18  * The Original Code is Copyright (C) 2009 by Nicholas Bishop
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): none yet.
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  *
27  */
28
29 #include "MEM_guardedalloc.h"
30
31 #include "DNA_brush_types.h"
32 #include "DNA_object_types.h"
33 #include "DNA_scene_types.h"
34 #include "DNA_screen_types.h"
35
36 #include "RNA_access.h"
37
38 #include "BKE_context.h"
39 #include "BKE_paint.h"
40
41 #include "WM_api.h"
42 #include "WM_types.h"
43
44 #include "BLI_arithb.h"
45
46 #include "PIL_time.h"
47
48 #include "BIF_gl.h"
49 #include "BIF_glutil.h"
50
51 #include "ED_screen.h"
52 #include "ED_view3d.h"
53
54 #include "paint_intern.h"
55
56 #include <float.h>
57 #include <math.h>
58
59 typedef struct PaintStroke {
60         void *mode_data;
61         void *smooth_stroke_cursor;
62         wmTimer *timer;
63
64         /* Cached values */
65         ViewContext vc;
66         bglMats mats;
67         Brush *brush;
68
69         float last_mouse_position[2];
70
71         /* Set whether any stroke step has yet occured
72            e.g. in sculpt mode, stroke doesn't start until cursor
73            passes over the mesh */
74         int stroke_started;
75
76         StrokeTestStart test_start;
77         StrokeUpdateStep update_step;
78         StrokeDone done;
79 } PaintStroke;
80
81 /*** Cursor ***/
82 static void paint_draw_smooth_stroke(bContext *C, int x, int y, void *customdata) 
83 {
84         Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C)));
85         PaintStroke *stroke = customdata;
86
87         glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
88         glEnable(GL_LINE_SMOOTH);
89         glEnable(GL_BLEND);
90
91         if(stroke && brush && (brush->flag & BRUSH_SMOOTH_STROKE)) {
92                 ARegion *ar = CTX_wm_region(C);
93                 sdrawline(x, y, (int)stroke->last_mouse_position[0] - ar->winrct.xmin,
94                           (int)stroke->last_mouse_position[1] - ar->winrct.ymin);
95         }
96
97         glDisable(GL_BLEND);
98         glDisable(GL_LINE_SMOOTH);
99 }
100
101 static void paint_draw_cursor(bContext *C, int x, int y, void *customdata)
102 {
103         Brush *brush = paint_brush(paint_get_active(CTX_data_scene(C)));
104
105         glColor4ubv(paint_get_active(CTX_data_scene(C))->paint_cursor_col);
106         glEnable(GL_LINE_SMOOTH);
107         glEnable(GL_BLEND);
108
109         glTranslatef((float)x, (float)y, 0.0f);
110         glutil_draw_lined_arc(0.0, M_PI*2.0, brush->size, 40);
111         glTranslatef((float)-x, (float)-y, 0.0f);
112
113         glDisable(GL_BLEND);
114         glDisable(GL_LINE_SMOOTH);
115 }
116
117 /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
118 static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
119 {
120         PointerRNA itemptr;
121         float cur_depth, pressure = 1;
122         float center[3];
123         PaintStroke *stroke = op->customdata;
124
125         cur_depth = read_cached_depth(&stroke->vc, mouse[0], mouse[1]);
126         view3d_unproject(&stroke->mats, center, mouse[0], mouse[1], cur_depth); 
127
128         /* Tablet */
129         if(event->custom == EVT_DATA_TABLET) {
130                 wmTabletData *wmtab= event->customdata;
131                 if(wmtab->Active != EVT_TABLET_NONE)
132                         pressure= wmtab->Pressure;
133         }
134                                 
135         /* Add to stroke */
136         RNA_collection_add(op->ptr, "stroke", &itemptr);
137         RNA_float_set_array(&itemptr, "location", center);
138         RNA_float_set_array(&itemptr, "mouse", mouse);
139         RNA_boolean_set(&itemptr, "flip", event->shift);
140         RNA_float_set(&itemptr, "pressure", pressure);
141
142         stroke->last_mouse_position[0] = mouse[0];
143         stroke->last_mouse_position[1] = mouse[1];
144
145         stroke->update_step(C, stroke, &itemptr);
146 }
147
148 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
149 static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
150 {
151         output[0] = event->x;
152         output[1] = event->y;
153
154         if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
155                 float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
156                 float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
157
158                 /* If the mouse is moving within the radius of the last move,
159                    don't update the mouse position. This allows sharp turns. */
160                 if(dx*dx + dy*dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius)
161                         return 0;
162
163                 output[0] = event->x * v + stroke->last_mouse_position[0] * u;
164                 output[1] = event->y * v + stroke->last_mouse_position[1] * u;
165         }
166
167         return 1;
168 }
169
170 /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
171 static int paint_space_stroke_enabled(Brush *br)
172 {
173         return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
174 }
175
176 /* For brushes with stroke spacing enabled, moves mouse in steps
177    towards the final mouse location. */
178 static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
179 {
180         PaintStroke *stroke = op->customdata;
181         int cnt = 0;
182
183         if(paint_space_stroke_enabled(stroke->brush)) {
184                 float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
185                 float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
186                 float length, scale;
187                 int steps = 0, i;
188
189                 /* Normalize the vector between the last stroke dot and the goal */
190                 length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
191
192                 if(length > FLT_EPSILON) {
193                         scale = stroke->brush->spacing / length;
194                         vec[0] *= scale;
195                         vec[1] *= scale;
196
197                         steps = (int)(length / stroke->brush->spacing);
198                         for(i = 0; i < steps; ++i, ++cnt) {
199                                 mouse[0] += vec[0];
200                                 mouse[1] += vec[1];
201                                 paint_brush_stroke_add_step(C, op, event, mouse);
202                         }
203                 }
204         }
205
206         return cnt;
207 }
208
209 /**** Public API ****/
210
211 PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start,
212                               StrokeUpdateStep update_step, StrokeDone done)
213 {
214         PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
215
216         stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C)));
217         view3d_set_viewcontext(C, &stroke->vc);
218         view3d_get_transformation(&stroke->vc, stroke->vc.obact, &stroke->mats);
219
220         stroke->test_start = test_start;
221         stroke->update_step = update_step;
222         stroke->done = done;
223
224         return stroke;
225 }
226
227 int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
228 {
229         ARegion *ar = CTX_wm_region(C);
230         PaintStroke *stroke = op->customdata;
231         float mouse[2];
232
233         if(event->type == TIMER && (event->customdata != stroke->timer))
234                 return OPERATOR_RUNNING_MODAL;
235
236         if(!stroke->stroke_started) {
237                 stroke->last_mouse_position[0] = event->x;
238                 stroke->last_mouse_position[1] = event->y;
239                 stroke->stroke_started = stroke->test_start(C, op, event);
240
241                 if(stroke->stroke_started) {
242                         stroke->smooth_stroke_cursor =
243                                 WM_paint_cursor_activate(CTX_wm_manager(C), paint_poll, paint_draw_smooth_stroke, stroke);
244
245                         if(stroke->brush->flag & BRUSH_AIRBRUSH)
246                                 stroke->timer = WM_event_add_window_timer(CTX_wm_window(C), TIMER, stroke->brush->rate);
247                 }
248
249                 ED_region_tag_redraw(ar);
250         }
251
252         if(stroke->stroke_started) {
253                 if(paint_smooth_stroke(stroke, mouse, event)) {
254                         if(paint_space_stroke_enabled(stroke->brush)) {
255                                 if(!paint_space_stroke(C, op, event, mouse))
256                                         ED_region_tag_redraw(ar);
257                         }
258                         else
259                                 paint_brush_stroke_add_step(C, op, event, mouse);
260                 }
261                 else
262                         ED_region_tag_redraw(ar);
263         }
264
265         /* TODO: fix hardcoded event here */
266         if(event->type == LEFTMOUSE && event->val == KM_RELEASE) {
267                 /* Exit stroke, free data */
268
269                 if(stroke->smooth_stroke_cursor)
270                         WM_paint_cursor_end(CTX_wm_manager(C), stroke->smooth_stroke_cursor);
271
272                 if(stroke->timer)
273                         WM_event_remove_window_timer(CTX_wm_window(C), stroke->timer);
274
275                 stroke->done(C, stroke);
276                 MEM_freeN(stroke);
277                 return OPERATOR_FINISHED;
278         }
279         else
280                 return OPERATOR_RUNNING_MODAL;
281 }
282
283 int paint_stroke_exec(bContext *C, wmOperator *op)
284 {
285         PaintStroke *stroke = op->customdata;
286
287         RNA_BEGIN(op->ptr, itemptr, "stroke") {
288                 stroke->update_step(C, stroke, &itemptr);
289         }
290         RNA_END;
291
292         MEM_freeN(stroke);
293         op->customdata = NULL;
294
295         return OPERATOR_FINISHED;
296 }
297
298 ViewContext *paint_stroke_view_context(PaintStroke *stroke)
299 {
300         return &stroke->vc;
301 }
302
303 void *paint_stroke_mode_data(struct PaintStroke *stroke)
304 {
305         return stroke->mode_data;
306 }
307
308 void paint_stroke_set_mode_data(PaintStroke *stroke, void *mode_data)
309 {
310         stroke->mode_data = mode_data;
311 }
312
313 int paint_poll(bContext *C)
314 {
315         Paint *p = paint_get_active(CTX_data_scene(C));
316         Object *ob = CTX_data_active_object(C);
317
318         return p && ob && paint_brush(p) &&
319                 CTX_wm_area(C)->spacetype == SPACE_VIEW3D &&
320                 CTX_wm_region(C)->regiontype == RGN_TYPE_WINDOW;
321 }
322
323 void paint_cursor_start(bContext *C, int (*poll)(bContext *C))
324 {
325         Paint *p = paint_get_active(CTX_data_scene(C));
326
327         if(p && !p->paint_cursor)
328                 p->paint_cursor = WM_paint_cursor_activate(CTX_wm_manager(C), poll, paint_draw_cursor, NULL);
329 }
330