Support for symmetrical box masking in sculpt mode.
[blender.git] / source / blender / editors / sculpt_paint / paint_mask.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) 2012 by Nicholas Bishop
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s):
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  *
27  */
28
29 /** \file blender/editors/sculpt_paint/paint_mask.c
30  *  \ingroup edsculpt
31  */
32
33 #include "MEM_guardedalloc.h"
34
35 #include "DNA_mesh_types.h"
36 #include "DNA_meshdata_types.h"
37 #include "DNA_object_types.h"
38
39 #include "BIF_glutil.h"
40
41 #include "BLI_math_matrix.h"
42 #include "BLI_math_geom.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_lasso.h"
45
46 #include "BKE_pbvh.h"
47 #include "BKE_ccg.h"
48 #include "BKE_context.h"
49 #include "BKE_DerivedMesh.h"
50 #include "BKE_multires.h"
51 #include "BKE_paint.h"
52 #include "BKE_subsurf.h"
53
54 #include "RNA_access.h"
55 #include "RNA_define.h"
56
57 #include "WM_api.h"
58 #include "WM_types.h"
59
60 #include "ED_screen.h"
61 #include "ED_sculpt.h"
62 #include "ED_view3d.h"
63
64 #include "bmesh.h"
65
66 #include "paint_intern.h"
67 #include "sculpt_intern.h" /* for undo push */
68
69 #include <stdlib.h>
70
71 static void mask_flood_fill_set_elem(float *elem,
72                                      PaintMaskFloodMode mode,
73                                      float value)
74 {
75         switch (mode) {
76                 case PAINT_MASK_FLOOD_VALUE:
77                         (*elem) = value;
78                         break;
79                 case PAINT_MASK_INVERT:
80                         (*elem) = 1.0f - (*elem);
81                         break;
82         }
83 }
84
85 static int mask_flood_fill_exec(bContext *C, wmOperator *op)
86 {
87         ARegion *ar = CTX_wm_region(C);
88         struct Scene *scene = CTX_data_scene(C);
89         Object *ob = CTX_data_active_object(C);
90         struct MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
91         PaintMaskFloodMode mode;
92         float value;
93         DerivedMesh *dm;
94         PBVH *pbvh;
95         PBVHNode **nodes;
96         int totnode, i;
97
98         mode = RNA_enum_get(op->ptr, "mode");
99         value = RNA_float_get(op->ptr, "value");
100
101         ED_sculpt_mask_layers_ensure(ob, mmd);
102
103         dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
104         pbvh = dm->getPBVH(ob, dm);
105         ob->sculpt->pbvh = pbvh;
106
107         BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
108
109         sculpt_undo_push_begin("Mask flood fill");
110
111         for (i = 0; i < totnode; i++) {
112                 PBVHVertexIter vi;
113
114                 sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK);
115
116                 BKE_pbvh_vertex_iter_begin(pbvh, nodes[i], vi, PBVH_ITER_UNIQUE) {
117                         mask_flood_fill_set_elem(vi.mask, mode, value);
118                 } BKE_pbvh_vertex_iter_end;
119                 
120                 BKE_pbvh_node_mark_update(nodes[i]);
121                 if (BKE_pbvh_type(pbvh) == PBVH_GRIDS)
122                         multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
123         }
124         
125         sculpt_undo_push_end();
126
127         if (nodes)
128                 MEM_freeN(nodes);
129
130         ED_region_tag_redraw(ar);
131
132         return OPERATOR_FINISHED;
133 }
134
135 void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
136 {
137         static EnumPropertyItem mode_items[] = {
138                 {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"},
139                 {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"},
140                 {0}};
141
142         /* identifiers */
143         ot->name = "Mask Flood Fill";
144         ot->idname = "PAINT_OT_mask_flood_fill";
145         ot->description = "Fill the whole mask with a given value, or invert its values";
146
147         /* api callbacks */
148         ot->exec = mask_flood_fill_exec;
149         ot->poll = sculpt_mode_poll;
150
151         ot->flag = OPTYPE_REGISTER;
152
153         /* rna */
154         RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
155         RNA_def_float(ot->srna, "value", 0, 0, 1, "Value",
156                       "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1);
157 }
158
159 /* Box select, operator is VIEW3D_OT_select_border, defined in view3d_select.c */
160
161 static int is_effected(float planes[4][4], const float co[3])
162 {
163         return isect_point_planes_v3(planes, 4, co);
164 }
165
166 static void flip_plane(float out[4], const float in[4], const char symm)
167 {
168         if (symm & SCULPT_SYMM_X)
169                 out[0] = -in[0];
170         else
171                 out[0] = in[0];
172         if (symm & SCULPT_SYMM_Y)
173                 out[1] = -in[1];
174         else
175                 out[1] = in[1];
176         if (symm & SCULPT_SYMM_Z)
177                 out[2] = -in[2];
178         else
179                 out[2] = in[2];
180
181         out[3] = in[3];
182 }
183
184 int do_sculpt_mask_box_select(ViewContext *vc, rcti *rect, bool select, bool UNUSED(extend))
185 {
186         Sculpt *sd = vc->scene->toolsettings->sculpt;
187         BoundBox bb;
188         bglMats mats = {{0}};
189         float clip_planes[4][4];
190         float clip_planes_final[4][4];
191         ARegion *ar = vc->ar;
192         struct Scene *scene = vc->scene;
193         Object *ob = vc->obact;
194         struct MultiresModifierData *mmd = sculpt_multires_active(scene, ob);
195         PaintMaskFloodMode mode;
196         float value;
197         DerivedMesh *dm;
198         PBVH *pbvh;
199         PBVHNode **nodes;
200         int totnode, i, symmpass;
201         int symm = sd->flags & 7;
202
203         mode = PAINT_MASK_FLOOD_VALUE;
204         value = select ? 1.0 : 0.0;
205
206         /* transform the clip planes in object space */
207         view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, &mats);
208         ED_view3d_clipping_calc(&bb, clip_planes, &mats, rect);
209         mul_m4_fl(clip_planes, -1.0f);
210
211         ED_sculpt_mask_layers_ensure(ob, mmd);
212
213         dm = mesh_get_derived_final(scene, ob, CD_MASK_BAREMESH);
214         pbvh = dm->getPBVH(ob, dm);
215         ob->sculpt->pbvh = pbvh;
216
217         sculpt_undo_push_begin("Mask box fill");
218
219         for (symmpass = 0; symmpass <= symm; ++symmpass) {
220                 if (symmpass == 0 ||
221                         (symm & symmpass &&
222                          (symm != 5 || symmpass != 3) &&
223                          (symm != 6 || (symmpass != 3 && symmpass != 5))))
224                 {
225                         int j = 0;
226
227                         /* flip the planes symmetrically as needed */
228                         for (; j < 4; j++) {
229                                 flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
230                         }
231
232                         BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode);
233
234 #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
235                         for (i = 0; i < totnode; i++) {
236                                 PBVHVertexIter vi;
237
238                                 sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK);
239
240                                 BKE_pbvh_vertex_iter_begin(pbvh, nodes[i], vi, PBVH_ITER_UNIQUE) {
241                                         if (is_effected(clip_planes_final, vi.co))
242                                                 mask_flood_fill_set_elem(vi.mask, mode, value);
243                                 } BKE_pbvh_vertex_iter_end;
244
245                                 BKE_pbvh_node_mark_update(nodes[i]);
246                                 if (BKE_pbvh_type(pbvh) == PBVH_GRIDS)
247                                         multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
248                         }
249
250                         if (nodes)
251                                 MEM_freeN(nodes);
252                 }
253         }
254
255         sculpt_undo_push_end();
256
257         ED_region_tag_redraw(ar);
258
259         return OPERATOR_FINISHED;
260 }
261
262 typedef struct LassoMaskData {
263         struct ViewContext *vc;
264         float projviewobjmat[4][4];
265         bool *px;
266         int width;
267         rcti rect; /* bounding box for scanfilling */
268 } LassoMaskData;
269
270
271 /* Lasso select. This could be defined as part of VIEW3D_OT_select_lasso, still the shortcuts conflict,
272  * so we will use a separate operator */
273
274 static bool is_effected_lasso(LassoMaskData *data, float co[3])
275 {
276         float scr_co_f[2];
277         short scr_co_s[2];
278
279         /* first project point to 2d space */
280         ED_view3d_project_float_v2_m4(data->vc->ar, co, scr_co_f, data->projviewobjmat);
281
282         scr_co_s[0] = scr_co_f[0];
283         scr_co_s[1] = scr_co_f[1];
284
285         /* clip against screen, because lasso is limited to screen only */
286         if (scr_co_s[0] < data->rect.xmin || scr_co_s[1] < data->rect.ymin || scr_co_s[0] >= data->rect.xmax || scr_co_s[1] >= data->rect.ymax)
287                 return false;
288
289         scr_co_s[0] -= data->rect.xmin;
290         scr_co_s[1] -= data->rect.ymin;
291
292         return data->px[scr_co_s[1] * data->width + scr_co_s[0]];
293 }
294
295 static void mask_lasso_px_cb(int x, int y, void *user_data)
296 {
297         struct LassoMaskData *data = user_data;
298         data->px[(y * data->width) + x] = true;
299 }
300
301 static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
302 {
303         int mcords_tot;
304         int (*mcords)[2] = (int (*)[2])WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
305
306         if (mcords) {
307                 float clip_planes[4][4];
308                 BoundBox bb;
309                 bglMats mats = {{0}};
310                 Object *ob;
311                 ViewContext vc;
312                 LassoMaskData data;
313 #ifdef _OPENMP
314                 Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
315 #endif
316                 struct MultiresModifierData *mmd;
317                 DerivedMesh *dm;
318                 PBVH *pbvh;
319                 PBVHNode **nodes;
320                 int totnode, i;
321                 PaintMaskFloodMode mode = PAINT_MASK_FLOOD_VALUE;
322                 bool select = true; /* TODO: see how to implement deselection */
323                 float value = select ? 1.0 : 0.0;
324
325                 /* Calculations of individual vertices are done in 2D screen space to diminish the amount of
326                  * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle
327                  * of lasso */
328                 view3d_set_viewcontext(C, &vc);
329                 view3d_get_transformation(vc.ar, vc.rv3d, vc.obact, &mats);
330
331                 /* lasso data calculations */
332                 data.vc = &vc;
333                 ob = vc.obact;
334                 ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat);
335
336                 BLI_lasso_boundbox(&data.rect, (const int (*)[2])mcords, mcords_tot);
337                 data.width = data.rect.xmax - data.rect.xmin;
338                 data.px = MEM_callocN(sizeof(*data.px) * data.width * (data.rect.ymax - data.rect.ymin), "lasso_mask_pixel_buffer");
339
340                 fill_poly_v2i_n(
341                        data.rect.xmin, data.rect.ymin, data.rect.xmax, data.rect.ymax,
342                        (const int (*)[2])mcords, mcords_tot,
343                        mask_lasso_px_cb, &data);
344
345                 ED_view3d_clipping_calc(&bb, clip_planes, &mats, &data.rect);
346                 mul_m4_fl(clip_planes, -1.0f);
347
348                 mmd = sculpt_multires_active(vc.scene, ob);
349                 ED_sculpt_mask_layers_ensure(ob, mmd);
350                 dm = mesh_get_derived_final(vc.scene, ob, CD_MASK_BAREMESH);
351                 pbvh = dm->getPBVH(ob, dm);
352                 ob->sculpt->pbvh = pbvh;
353
354                 /* gather nodes inside lasso's enclosing rectangle (should greatly help with bigger meshes) */
355                 BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes, &nodes, &totnode);
356
357                 sculpt_undo_push_begin("Mask lasso fill");
358
359 #pragma omp parallel for schedule(guided) if (sd->flags & SCULPT_USE_OPENMP)
360                 for (i = 0; i < totnode; i++) {
361                         PBVHVertexIter vi;
362
363                         sculpt_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK);
364
365                         BKE_pbvh_vertex_iter_begin(pbvh, nodes[i], vi, PBVH_ITER_UNIQUE) {
366                                 if (is_effected_lasso(&data, vi.co))
367                                         mask_flood_fill_set_elem(vi.mask, mode, value);
368                         } BKE_pbvh_vertex_iter_end;
369
370                         BKE_pbvh_node_mark_update(nodes[i]);
371                         if (BKE_pbvh_type(pbvh) == PBVH_GRIDS)
372                                 multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
373                 }
374
375                 sculpt_undo_push_end();
376
377                 if (nodes)
378                         MEM_freeN(nodes);
379
380                 ED_region_tag_redraw(vc.ar);
381                 MEM_freeN((void *)mcords);
382                 MEM_freeN(data.px);
383
384                 return OPERATOR_FINISHED;
385         }
386         return OPERATOR_PASS_THROUGH;
387 }
388
389 void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
390 {
391         PropertyRNA *prop;
392
393         ot->name = "Mask Lasso Gesture";
394         ot->idname = "PAINT_OT_mask_lasso_gesture";
395         ot->description = "Add mask within the lasso as you move the pointer";
396
397         ot->invoke = WM_gesture_lasso_invoke;
398         ot->modal = WM_gesture_lasso_modal;
399         ot->exec = paint_mask_gesture_lasso_exec;
400
401         ot->poll = sculpt_mode_poll;
402
403         ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
404
405         prop = RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
406         RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
407 }