634b82674b5272612ea756d50016bf16e9d90271
[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 "BIF_glutil.h"
47
48 #include "ED_screen.h"
49 #include "ED_view3d.h"
50
51 #include "paint_intern.h"
52
53 #include <float.h>
54 #include <math.h>
55
56 typedef struct PaintStroke {
57         /* Cached values */
58         ViewContext vc;
59         bglMats mats;
60         Brush *brush;
61
62         float last_mouse_position[2];
63
64         /* Set whether any stroke step has yet occured
65            e.g. in sculpt mode, stroke doesn't start until cursor
66            passes over the mesh */
67         int stroke_started;
68
69         StrokeTestStart test_start;
70         StrokeUpdateStep update_step;
71         StrokeDone done;
72 } PaintStroke;
73
74 /* Put the location of the next stroke dot into the stroke RNA and apply it to the mesh */
75 static void paint_brush_stroke_add_step(bContext *C, wmOperator *op, wmEvent *event, float mouse[2])
76 {
77         PointerRNA itemptr;
78         float cur_depth, pressure = 1;
79         float center[3];
80         PaintStroke *stroke = op->customdata;
81
82         cur_depth = read_cached_depth(&stroke->vc, mouse[0], mouse[1]);
83         view3d_unproject(&stroke->mats, center, mouse[0], mouse[1], cur_depth); 
84
85         /* Tablet */
86         if(event->custom == EVT_DATA_TABLET) {
87                 wmTabletData *wmtab= event->customdata;
88                 if(wmtab->Active != EVT_TABLET_NONE)
89                         pressure= wmtab->Pressure;
90         }
91                                 
92         /* Add to stroke */
93         RNA_collection_add(op->ptr, "stroke", &itemptr);
94         RNA_float_set_array(&itemptr, "location", center);
95         RNA_float_set_array(&itemptr, "mouse", mouse);
96         RNA_boolean_set(&itemptr, "flip", event->shift);
97         RNA_float_set(&itemptr, "pressure", pressure);
98
99         stroke->last_mouse_position[0] = mouse[0];
100         stroke->last_mouse_position[1] = mouse[1];
101
102         stroke->update_step(C, stroke, &itemptr);
103 }
104
105 /* Returns zero if no sculpt changes should be made, non-zero otherwise */
106 static int paint_smooth_stroke(PaintStroke *stroke, float output[2], wmEvent *event)
107 {
108         output[0] = event->x;
109         output[1] = event->y;
110
111         if(stroke->brush->flag & BRUSH_SMOOTH_STROKE && stroke->brush->sculpt_tool != SCULPT_TOOL_GRAB) {
112                 float u = stroke->brush->smooth_stroke_factor, v = 1.0 - u;
113                 float dx = stroke->last_mouse_position[0] - event->x, dy = stroke->last_mouse_position[1] - event->y;
114
115                 /* If the mouse is moving within the radius of the last move,
116                    don't update the mouse position. This allows sharp turns. */
117                 if(dx*dx + dy*dy < stroke->brush->smooth_stroke_radius * stroke->brush->smooth_stroke_radius)
118                         return 0;
119
120                 output[0] = event->x * v + stroke->last_mouse_position[0] * u;
121                 output[1] = event->y * v + stroke->last_mouse_position[1] * u;
122         }
123
124         return 1;
125 }
126
127 /* Returns zero if the stroke dots should not be spaced, non-zero otherwise */
128 static int paint_space_stroke_enabled(Brush *br)
129 {
130         return (br->flag & BRUSH_SPACE) && !(br->flag & BRUSH_ANCHORED) && (br->sculpt_tool != SCULPT_TOOL_GRAB);
131 }
132
133 /* For brushes with stroke spacing enabled, moves mouse in steps
134    towards the final mouse location. */
135 static int paint_space_stroke(bContext *C, wmOperator *op, wmEvent *event, const float final_mouse[2])
136 {
137         PaintStroke *stroke = op->customdata;
138         int cnt = 0;
139
140         if(paint_space_stroke_enabled(stroke->brush)) {
141                 float mouse[2] = {stroke->last_mouse_position[0], stroke->last_mouse_position[1]};
142                 float vec[2] = {final_mouse[0] - mouse[0], final_mouse[1] - mouse[1]};
143                 float length, scale;
144                 int steps = 0, i;
145
146                 /* Normalize the vector between the last stroke dot and the goal */
147                 length = sqrt(vec[0]*vec[0] + vec[1]*vec[1]);
148
149                 if(length > FLT_EPSILON) {
150                         scale = stroke->brush->spacing / length;
151                         vec[0] *= scale;
152                         vec[1] *= scale;
153
154                         steps = (int)(length / stroke->brush->spacing);
155                         for(i = 0; i < steps; ++i, ++cnt) {
156                                 mouse[0] += vec[0];
157                                 mouse[1] += vec[1];
158                                 paint_brush_stroke_add_step(C, op, event, mouse);
159                         }
160                 }
161         }
162
163         return cnt;
164 }
165
166 /**** Public API ****/
167
168 PaintStroke *paint_stroke_new(bContext *C, StrokeTestStart test_start,
169                               StrokeUpdateStep update_step, StrokeDone done)
170 {
171         PaintStroke *stroke = MEM_callocN(sizeof(PaintStroke), "PaintStroke");
172
173         stroke->brush = paint_brush(paint_get_active(CTX_data_scene(C)));
174         view3d_set_viewcontext(C, &stroke->vc);
175         view3d_get_transformation(&stroke->vc, stroke->vc.obact, &stroke->mats);
176
177         stroke->test_start = test_start;
178         stroke->update_step = update_step;
179         stroke->done = done;
180
181         return stroke;
182 }
183
184 int paint_stroke_modal(bContext *C, wmOperator *op, wmEvent *event)
185 {
186         ARegion *ar = CTX_wm_region(C);
187         PaintStroke *stroke = op->customdata;
188         float mouse[2];
189
190         if(!stroke->stroke_started) {
191                 stroke->last_mouse_position[0] = event->x;
192                 stroke->last_mouse_position[1] = event->y;
193                 stroke->stroke_started = stroke->test_start(C, op, event);
194                 ED_region_tag_redraw(ar);
195         }
196
197         if(stroke->stroke_started) {
198                 if(paint_smooth_stroke(stroke, mouse, event)) {
199                         if(paint_space_stroke_enabled(stroke->brush)) {
200                                 if(!paint_space_stroke(C, op, event, mouse))
201                                         ED_region_tag_redraw(ar);
202                         }
203                         else
204                                 paint_brush_stroke_add_step(C, op, event, mouse);
205                 }
206                 else
207                         ED_region_tag_redraw(ar);
208         }
209
210         /* TODO: fix hardcoded event here */
211         if(event->type == LEFTMOUSE && event->val == 0) {
212                 stroke->done(C, stroke);
213                 MEM_freeN(stroke);
214                 return OPERATOR_FINISHED;
215         }
216         else
217                 return OPERATOR_RUNNING_MODAL;
218 }
219
220 ViewContext *paint_stroke_view_context(PaintStroke *stroke)
221 {
222         return &stroke->vc;
223 }
224