2 * ***** BEGIN GPL LICENSE BLOCK *****
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.
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.
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.
18 * The Original Code is Copyright (C) 2012 by Nicholas Bishop
19 * All rights reserved.
21 * The Original Code is: all of this file.
25 * ***** END GPL LICENSE BLOCK *****
29 /** \file blender/editors/sculpt_paint/paint_mask.c
33 #include "MEM_guardedalloc.h"
35 #include "DNA_meshdata_types.h"
36 #include "DNA_object_types.h"
38 #include "BIF_glutil.h"
40 #include "BLI_bitmap_draw_2d.h"
41 #include "BLI_math_matrix.h"
42 #include "BLI_math_geom.h"
43 #include "BLI_utildefines.h"
44 #include "BLI_lasso_2d.h"
49 #include "BKE_context.h"
50 #include "BKE_DerivedMesh.h"
51 #include "BKE_multires.h"
52 #include "BKE_paint.h"
53 #include "BKE_subsurf.h"
55 #include "RNA_access.h"
56 #include "RNA_define.h"
61 #include "ED_screen.h"
62 #include "ED_sculpt.h"
63 #include "ED_view3d.h"
67 #include "paint_intern.h"
68 #include "sculpt_intern.h" /* for undo push */
72 static const EnumPropertyItem mode_items[] = {
73 {PAINT_MASK_FLOOD_VALUE, "VALUE", 0, "Value", "Set mask to the level specified by the 'value' property"},
74 {PAINT_MASK_FLOOD_VALUE_INVERSE, "VALUE_INVERSE", 0, "Value Inverted", "Set mask to the level specified by the inverted 'value' property"},
75 {PAINT_MASK_INVERT, "INVERT", 0, "Invert", "Invert the mask"},
79 static void mask_flood_fill_set_elem(float *elem,
80 PaintMaskFloodMode mode,
84 case PAINT_MASK_FLOOD_VALUE:
87 case PAINT_MASK_FLOOD_VALUE_INVERSE:
88 (*elem) = 1.0f - value;
90 case PAINT_MASK_INVERT:
91 (*elem) = 1.0f - (*elem);
96 typedef struct MaskTaskData {
102 PaintMaskFloodMode mode;
104 float (*clip_planes_final)[4];
107 static void mask_flood_fill_task_cb(
108 void *__restrict userdata,
110 const ParallelRangeTLS *__restrict UNUSED(tls))
112 MaskTaskData *data = userdata;
114 PBVHNode *node = data->nodes[i];
116 const PaintMaskFloodMode mode = data->mode;
117 const float value = data->value;
121 sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
123 BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) {
124 mask_flood_fill_set_elem(vi.mask, mode, value);
125 } BKE_pbvh_vertex_iter_end;
127 BKE_pbvh_node_mark_redraw(node);
129 BKE_pbvh_node_mark_normals_update(node);
132 static int mask_flood_fill_exec(bContext *C, wmOperator *op)
134 ARegion *ar = CTX_wm_region(C);
135 struct Scene *scene = CTX_data_scene(C);
136 Object *ob = CTX_data_active_object(C);
137 PaintMaskFloodMode mode;
143 Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
145 mode = RNA_enum_get(op->ptr, "mode");
146 value = RNA_float_get(op->ptr, "value");
148 BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true);
149 pbvh = ob->sculpt->pbvh;
150 multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
152 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
154 sculpt_undo_push_begin("Mask flood fill");
156 MaskTaskData data = {
157 .ob = ob, .pbvh = pbvh, .nodes = nodes, .multires = multires,
158 .mode = mode, .value = value,
161 ParallelRangeSettings settings;
162 BLI_parallel_range_settings_defaults(&settings);
163 settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT);
164 BLI_task_parallel_range(
165 0, totnode, &data, mask_flood_fill_task_cb,
169 multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
171 sculpt_undo_push_end(C);
176 ED_region_tag_redraw(ar);
178 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
180 return OPERATOR_FINISHED;
183 void PAINT_OT_mask_flood_fill(struct wmOperatorType *ot)
186 ot->name = "Mask Flood Fill";
187 ot->idname = "PAINT_OT_mask_flood_fill";
188 ot->description = "Fill the whole mask with a given value, or invert its values";
191 ot->exec = mask_flood_fill_exec;
192 ot->poll = sculpt_mode_poll;
194 ot->flag = OPTYPE_REGISTER;
197 RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
198 RNA_def_float(ot->srna, "value", 0, 0, 1, "Value",
199 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1);
202 /* Box select, operator is VIEW3D_OT_select_border, defined in view3d_select.c */
204 static bool is_effected(float planes[4][4], const float co[3])
206 return isect_point_planes_v3(planes, 4, co);
209 static void flip_plane(float out[4], const float in[4], const char symm)
211 if (symm & PAINT_SYMM_X)
215 if (symm & PAINT_SYMM_Y)
219 if (symm & PAINT_SYMM_Z)
227 static void mask_box_select_task_cb(
228 void *__restrict userdata,
230 const ParallelRangeTLS *__restrict UNUSED(tls))
232 MaskTaskData *data = userdata;
234 PBVHNode *node = data->nodes[i];
236 const PaintMaskFloodMode mode = data->mode;
237 const float value = data->value;
238 float (*clip_planes_final)[4] = data->clip_planes_final;
241 bool any_masked = false;
243 BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) {
244 if (is_effected(clip_planes_final, vi.co)) {
248 sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
250 BKE_pbvh_node_mark_redraw(node);
252 BKE_pbvh_node_mark_normals_update(node);
254 mask_flood_fill_set_elem(vi.mask, mode, value);
256 } BKE_pbvh_vertex_iter_end;
259 int ED_sculpt_mask_box_select(struct bContext *C, ViewContext *vc, const rcti *rect, bool select, bool UNUSED(extend))
261 Sculpt *sd = vc->scene->toolsettings->sculpt;
263 bglMats mats = {{0}};
264 float clip_planes[4][4];
265 float clip_planes_final[4][4];
266 ARegion *ar = vc->ar;
267 struct Scene *scene = vc->scene;
268 Object *ob = vc->obact;
269 PaintMaskFloodMode mode;
274 int totnode, symmpass;
275 int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
277 mode = PAINT_MASK_FLOOD_VALUE;
278 value = select ? 1.0 : 0.0;
280 /* transform the clip planes in object space */
281 view3d_get_transformation(vc->ar, vc->rv3d, vc->obact, &mats);
282 ED_view3d_clipping_calc(&bb, clip_planes, &mats, rect);
283 negate_m4(clip_planes);
285 BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true);
286 pbvh = ob->sculpt->pbvh;
287 multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
289 sculpt_undo_push_begin("Mask box fill");
291 for (symmpass = 0; symmpass <= symm; ++symmpass) {
294 (symm != 5 || symmpass != 3) &&
295 (symm != 6 || (symmpass != 3 && symmpass != 5))))
299 /* flip the planes symmetrically as needed */
301 flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
304 BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode);
306 MaskTaskData data = {
307 .ob = ob, .pbvh = pbvh, .nodes = nodes, .multires = multires,
308 .mode = mode, .value = value, .clip_planes_final = clip_planes_final,
311 ParallelRangeSettings settings;
312 BLI_parallel_range_settings_defaults(&settings);
313 settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT);
314 BLI_task_parallel_range(
315 0, totnode, &data, mask_box_select_task_cb,
324 multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
326 sculpt_undo_push_end(C);
328 ED_region_tag_redraw(ar);
330 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
332 return OPERATOR_FINISHED;
335 typedef struct LassoMaskData {
336 struct ViewContext *vc;
337 float projviewobjmat[4][4];
340 rcti rect; /* bounding box for scanfilling */
343 MaskTaskData task_data;
347 /* Lasso select. This could be defined as part of VIEW3D_OT_select_lasso, still the shortcuts conflict,
348 * so we will use a separate operator */
350 static bool is_effected_lasso(LassoMaskData *data, float co[3])
356 flip_v3_v3(co_final, co, data->symmpass);
357 /* first project point to 2d space */
358 ED_view3d_project_float_v2_m4(data->vc->ar, co_final, scr_co_f, data->projviewobjmat);
360 scr_co_s[0] = scr_co_f[0];
361 scr_co_s[1] = scr_co_f[1];
363 /* clip against screen, because lasso is limited to screen only */
364 if ((scr_co_s[0] < data->rect.xmin) ||
365 (scr_co_s[1] < data->rect.ymin) ||
366 (scr_co_s[0] >= data->rect.xmax) ||
367 (scr_co_s[1] >= data->rect.ymax))
372 scr_co_s[0] -= data->rect.xmin;
373 scr_co_s[1] -= data->rect.ymin;
375 return BLI_BITMAP_TEST_BOOL(data->px, scr_co_s[1] * data->width + scr_co_s[0]);
378 static void mask_lasso_px_cb(int x, int x_end, int y, void *user_data)
380 LassoMaskData *data = user_data;
381 int index = (y * data->width) + x;
382 int index_end = (y * data->width) + x_end;
384 BLI_BITMAP_ENABLE(data->px, index);
385 } while (++index != index_end);
388 static void mask_gesture_lasso_task_cb(
389 void *__restrict userdata,
391 const ParallelRangeTLS *__restrict UNUSED(tls))
393 LassoMaskData *lasso_data = userdata;
394 MaskTaskData *data = &lasso_data->task_data;
396 PBVHNode *node = data->nodes[i];
398 const PaintMaskFloodMode mode = data->mode;
399 const float value = data->value;
402 bool any_masked = false;
404 BKE_pbvh_vertex_iter_begin(data->pbvh, node, vi, PBVH_ITER_UNIQUE) {
405 if (is_effected_lasso(lasso_data, vi.co)) {
409 sculpt_undo_push_node(data->ob, node, SCULPT_UNDO_MASK);
411 BKE_pbvh_node_mark_redraw(node);
413 BKE_pbvh_node_mark_normals_update(node);
416 mask_flood_fill_set_elem(vi.mask, mode, value);
418 } BKE_pbvh_vertex_iter_end;
421 static int paint_mask_gesture_lasso_exec(bContext *C, wmOperator *op)
424 const int (*mcords)[2] = WM_gesture_lasso_path_to_array(C, op, &mcords_tot);
427 float clip_planes[4][4], clip_planes_final[4][4];
429 bglMats mats = {{0}};
433 struct Scene *scene = CTX_data_scene(C);
434 Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
435 int symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
438 int totnode, symmpass;
440 PaintMaskFloodMode mode = RNA_enum_get(op->ptr, "mode");
441 float value = RNA_float_get(op->ptr, "value");
443 /* Calculations of individual vertices are done in 2D screen space to diminish the amount of
444 * calculations done. Bounding box PBVH collision is not computed against enclosing rectangle
446 view3d_set_viewcontext(C, &vc);
447 view3d_get_transformation(vc.ar, vc.rv3d, vc.obact, &mats);
449 /* lasso data calculations */
452 ED_view3d_ob_project_mat_get(vc.rv3d, ob, data.projviewobjmat);
454 BLI_lasso_boundbox(&data.rect, mcords, mcords_tot);
455 data.width = data.rect.xmax - data.rect.xmin;
456 data.px = BLI_BITMAP_NEW(data.width * (data.rect.ymax - data.rect.ymin), __func__);
458 BLI_bitmap_draw_2d_poly_v2i_n(
459 data.rect.xmin, data.rect.ymin, data.rect.xmax, data.rect.ymax,
461 mask_lasso_px_cb, &data);
463 ED_view3d_clipping_calc(&bb, clip_planes, &mats, &data.rect);
464 negate_m4(clip_planes);
466 BKE_sculpt_update_mesh_elements(scene, sd, ob, false, true);
467 pbvh = ob->sculpt->pbvh;
468 multires = (BKE_pbvh_type(pbvh) == PBVH_GRIDS);
470 sculpt_undo_push_begin("Mask lasso fill");
472 for (symmpass = 0; symmpass <= symm; ++symmpass) {
473 if ((symmpass == 0) ||
475 (symm != 5 || symmpass != 3) &&
476 (symm != 6 || (symmpass != 3 && symmpass != 5))))
480 /* flip the planes symmetrically as needed */
482 flip_plane(clip_planes_final[j], clip_planes[j], symmpass);
485 data.symmpass = symmpass;
487 /* gather nodes inside lasso's enclosing rectangle (should greatly help with bigger meshes) */
488 BKE_pbvh_search_gather(pbvh, BKE_pbvh_node_planes_contain_AABB, clip_planes_final, &nodes, &totnode);
490 data.task_data.ob = ob;
491 data.task_data.pbvh = pbvh;
492 data.task_data.nodes = nodes;
493 data.task_data.multires = multires;
494 data.task_data.mode = mode;
495 data.task_data.value = value;
497 ParallelRangeSettings settings;
498 BLI_parallel_range_settings_defaults(&settings);
499 settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && (totnode > SCULPT_THREADED_LIMIT));
500 BLI_task_parallel_range(
501 0, totnode, &data, mask_gesture_lasso_task_cb,
510 multires_mark_as_modified(ob, MULTIRES_COORDS_MODIFIED);
512 sculpt_undo_push_end(C);
514 ED_region_tag_redraw(vc.ar);
515 MEM_freeN((void *)mcords);
518 WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
520 return OPERATOR_FINISHED;
522 return OPERATOR_PASS_THROUGH;
525 void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
527 ot->name = "Mask Lasso Gesture";
528 ot->idname = "PAINT_OT_mask_lasso_gesture";
529 ot->description = "Add mask within the lasso as you move the brush";
531 ot->invoke = WM_gesture_lasso_invoke;
532 ot->modal = WM_gesture_lasso_modal;
533 ot->exec = paint_mask_gesture_lasso_exec;
535 ot->poll = sculpt_mode_poll;
537 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
540 WM_operator_properties_gesture_lasso(ot);
542 RNA_def_enum(ot->srna, "mode", mode_items, PAINT_MASK_FLOOD_VALUE, "Mode", NULL);
543 RNA_def_float(ot->srna, "value", 1.0, 0, 1.0, "Value",
544 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked", 0, 1);