Blender V5.0
paint_mask.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2012 by Nicholas Bishop. All rights reserved.
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "paint_mask.hh"
9
10#include <cstdlib>
11
12#include "MEM_guardedalloc.h"
13
14#include "DNA_object_types.h"
15
16#include "BLI_array_utils.hh"
17#include "BLI_bit_span_ops.hh"
19#include "BLI_span.hh"
20#include "BLI_vector.hh"
21
22#include "BKE_attribute.hh"
23#include "BKE_brush.hh"
24#include "BKE_ccg.hh"
25#include "BKE_context.hh"
26#include "BKE_mesh.hh"
27#include "BKE_multires.hh"
28#include "BKE_paint.hh"
29#include "BKE_paint_bvh.hh"
30#include "BKE_subdiv_ccg.hh"
31
32#include "RNA_access.hh"
33#include "RNA_define.hh"
34
35#include "WM_api.hh"
36#include "WM_types.hh"
37
38#include "ED_select_utils.hh"
39
40#include "bmesh.hh"
41
42#include "mesh_brush_common.hh"
43#include "paint_intern.hh"
44#include "sculpt_gesture.hh"
45#include "sculpt_hide.hh"
46#include "sculpt_intern.hh"
47#include "sculpt_undo.hh"
48
50
51/* -------------------------------------------------------------------- */
54
56{
57 const SculptSession &ss = *object.sculpt;
58 switch (bke::object::pbvh_get(object)->type()) {
60 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
61 const bke::AttributeAccessor attributes = mesh.attributes();
62 const VArray mask = *attributes.lookup_or_default<float>(
63 ".sculpt_mask", bke::AttrDomain::Point, 0.0f);
64 Array<float> result(mask.size());
65 mask.materialize(result);
66 return result;
67 }
69 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
70 if (subdiv_ccg.masks.is_empty()) {
71 return Array<float>(subdiv_ccg.positions.size(), 0.0f);
72 }
73 return subdiv_ccg.masks;
74 }
76 BMesh &bm = *ss.bm;
77 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
78 Array<float> result(bm.totvert);
79 if (offset == -1) {
80 result.fill(0.0f);
81 }
82 else {
83 vert_random_access_ensure(const_cast<Object &>(object));
84 for (const int i : result.index_range()) {
86 }
87 }
88 return result;
89 }
90 }
92 return {};
93}
94
95void mix_new_masks(const Span<float> new_masks, const float factor, const MutableSpan<float> masks)
96{
97 BLI_assert(new_masks.size() == masks.size());
98
99 for (const int i : masks.index_range()) {
100 masks[i] += (new_masks[i] - masks[i]) * factor;
101 }
102}
103
104void mix_new_masks(const Span<float> new_masks,
105 const Span<float> factors,
106 const MutableSpan<float> masks)
107{
108 BLI_assert(new_masks.size() == factors.size());
109 BLI_assert(new_masks.size() == masks.size());
110
111 for (const int i : masks.index_range()) {
112 masks[i] += (new_masks[i] - masks[i]) * factors[i];
113 }
114}
115
117{
118 for (float &mask : masks) {
119 mask = std::clamp(mask, 0.0f, 1.0f);
120 }
121}
122
124{
125 for (float &mask : masks) {
126 mask = 1.0f - mask;
127 }
128}
129
131 const Set<BMVert *, 0> &verts,
132 const MutableSpan<float> r_mask)
133{
134 BLI_assert(verts.size() == r_mask.size());
135
136 /* TODO: Avoid overhead of accessing attributes for every bke::pbvh::Tree node. */
137 const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
138 int i = 0;
139 for (const BMVert *vert : verts) {
140 r_mask[i] = (mask_offset == -1) ? 0.0f : BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
141 i++;
142 }
143}
144
145void gather_mask_grids(const SubdivCCG &subdiv_ccg,
146 const Span<int> grids,
147 const MutableSpan<float> r_mask)
148{
149 if (!subdiv_ccg.masks.is_empty()) {
150 gather_data_grids(subdiv_ccg, subdiv_ccg.masks.as_span(), grids, r_mask);
151 }
152 else {
153 r_mask.fill(0.0f);
154 }
155}
156
157void scatter_mask_grids(const Span<float> mask, SubdivCCG &subdiv_ccg, const Span<int> grids)
158{
159 scatter_data_grids(subdiv_ccg, mask, grids, subdiv_ccg.masks.as_mutable_span());
160}
161
163{
164 BLI_assert(verts.size() == mask.size());
165
166 const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
167 BLI_assert(mask_offset != -1);
168 int i = 0;
169 for (BMVert *vert : verts) {
170 BM_ELEM_CD_SET_FLOAT(vert, mask_offset, mask[i]);
171 i++;
172 }
173}
174
175static float average_masks(const int mask_offset, const Span<const BMVert *> verts)
176{
177 float sum = 0;
178 for (const BMVert *vert : verts) {
179 sum += BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
180 }
181 return sum / float(verts.size());
182}
183
184void average_neighbor_mask_bmesh(const int mask_offset,
185 const Set<BMVert *, 0> &verts,
186 const MutableSpan<float> new_masks)
187{
188 BMeshNeighborVerts neighbors;
189 int i = 0;
190 for (BMVert *vert : verts) {
191 new_masks[i] = average_masks(mask_offset, vert_neighbors_get_bmesh(*vert, neighbors));
192 i++;
193 }
194}
195
196void update_mask_mesh(const Depsgraph &depsgraph,
197 Object &object,
198 const IndexMask &node_mask,
199 FunctionRef<void(MutableSpan<float>, Span<int>)> update_fn)
200{
201 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
203
204 Mesh &mesh = *static_cast<Mesh *>(object.data);
205 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
206 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
208 ".sculpt_mask", bke::AttrDomain::Point);
209 if (!mask) {
210 return;
211 }
212
213 struct LocalData {
214 Vector<int> visible_verts;
216 };
217
218 Array<bool> node_changed(node_mask.min_array_size(), false);
219
221 node_mask.foreach_index(GrainSize(1), [&](const int i) {
222 LocalData &tls = all_tls.local();
223 const Span<int> verts = hide::node_visible_verts(nodes[i], hide_vert, tls.visible_verts);
224 tls.mask.resize(verts.size());
225 gather_data_mesh(mask.span.as_span(), verts, tls.mask.as_mutable_span());
226 update_fn(tls.mask, verts);
228 return;
229 }
233 node_changed[i] = true;
234 });
235
236 IndexMaskMemory memory;
237 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
238
239 mask.finish();
240}
241
243 const CCGKey &key,
244 const Span<int> grids,
245 const Span<float> values)
246{
247 BLI_assert(grids.size() * key.grid_area == values.size());
248
249 const IndexRange range = grids.index_range();
250 return std::all_of(range.begin(), range.end(), [&](const int i) {
251 return masks.slice(bke::ccg::grid_range(key, grids[i])) ==
252 values.slice(bke::ccg::grid_range(key, i));
253 return true;
254 });
255}
256
257bool mask_equals_array_bmesh(const int mask_offset,
258 const Set<BMVert *, 0> &verts,
259 const Span<float> values)
260{
261 BLI_assert(verts.size() == values.size());
262
263 int i = 0;
264 for (const BMVert *vert : verts) {
265 if (BM_ELEM_CD_GET_FLOAT(vert, mask_offset) != values[i]) {
266 return false;
267 }
268 i++;
269 }
270 return true;
271}
272
274
275/* -------------------------------------------------------------------- */
279
280/* The gesture API doesn't write to this enum type,
281 * it writes to eSelectOp from ED_select_utils.hh.
282 * We must thus map the modes here to the desired
283 * eSelectOp modes.
284 *
285 * Fixes #102349.
286 */
292
293static const EnumPropertyItem mode_items[] = {
295 "VALUE",
296 0,
297 "Value",
298 "Set mask to the level specified by the 'value' property"},
300 "VALUE_INVERSE",
301 0,
302 "Value Inverted",
303 "Set mask to the level specified by the inverted 'value' property"},
304 {int(FloodFillMode::InverseMeshValue), "INVERT", 0, "Invert", "Invert the mask"},
305 {0}};
306
308 const Span<bool> hide_vert,
310{
311 if (hide_vert.is_empty()) {
312 return {};
313 }
314 const Span<int> verts = node.verts();
316 return verts;
317 }
318 indices.resize(verts.size());
319 const int *end = std::copy_if(verts.begin(), verts.end(), indices.begin(), [&](const int vert) {
320 return hide_vert[vert];
321 });
322 indices.resize(end - indices.begin());
323 return indices;
324}
325
326static bool try_remove_mask_mesh(const Depsgraph &depsgraph,
327 Object &object,
328 const IndexMask &node_mask)
329{
330 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
332 Mesh &mesh = *static_cast<Mesh *>(object.data);
333 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
334 const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
335 if (mask.is_empty()) {
336 return true;
337 }
338
339 /* If there are any hidden vertices that shouldn't be affected with a mask value set, the
340 * attribute cannot be removed. This could also be done by building an IndexMask in the full
341 * vertex domain. */
342 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
344 const bool hidden_masked_verts = threading::parallel_reduce(
345 node_mask.index_range(),
346 1,
347 false,
348 [&](const IndexRange range, bool value) {
349 if (value) {
350 return value;
351 }
352 Vector<int> &index_data = all_index_data.local();
353 node_mask.slice(range).foreach_index([&](const int i) {
354 if (value) {
355 return;
356 }
357 const Span<int> verts = get_hidden_verts(nodes[i], hide_vert, index_data);
358 if (std::any_of(verts.begin(), verts.end(), [&](int i) { return mask[i] > 0.0f; })) {
359 value = true;
360 return;
361 }
362 });
363 return value;
364 },
365 std::logical_or());
366 if (hidden_masked_verts) {
367 return false;
368 }
369
370 IndexMaskMemory memory;
371 const IndexMask changed_nodes = IndexMask::from_predicate(
372 node_mask, GrainSize(1), memory, [&](const int i) {
373 const Span<int> verts = nodes[i].verts();
374 return std::any_of(
375 verts.begin(), verts.end(), [&](const int i) { return mask[i] != 0.0f; });
376 });
377
378 undo::push_nodes(depsgraph, object, changed_nodes, undo::Type::Mask);
379 attributes.remove(".sculpt_mask");
380 changed_nodes.foreach_index([&](const int i) {
381 BKE_pbvh_node_fully_masked_set(nodes[i], false);
383 });
384 pbvh.tag_masks_changed(changed_nodes);
385 return true;
386}
387
388static void fill_mask_mesh(const Depsgraph &depsgraph,
389 Object &object,
390 const float value,
391 const IndexMask &node_mask)
392{
393 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
395
396 Mesh &mesh = *static_cast<Mesh *>(object.data);
397 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
398 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
399 if (value == 0.0f) {
400 if (try_remove_mask_mesh(depsgraph, object, node_mask)) {
401 return;
402 }
403 }
404
406 ".sculpt_mask", bke::AttrDomain::Point);
407
408 Array<bool> node_changed(node_mask.min_array_size(), false);
409
411 node_mask.foreach_index(GrainSize(1), [&](const int i) {
412 Vector<int> &index_data = all_index_data.local();
413 const Span<int> verts = hide::node_visible_verts(nodes[i], hide_vert, index_data);
414 if (std::all_of(verts.begin(), verts.end(), [&](int i) { return mask.span[i] == value; })) {
415 return;
416 }
418 mask.span.fill_indices(verts, value);
419 node_changed[i] = true;
420 });
421
422 IndexMaskMemory memory;
423 const IndexMask changed_nodes = IndexMask::from_bools(node_mask, node_changed, memory);
424 pbvh.tag_masks_changed(changed_nodes);
425
426 mask.finish();
427 changed_nodes.foreach_index([&](const int i) {
428 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
430 });
431}
432
433static void fill_mask_grids(Main &bmain,
434 const Scene &scene,
435 Depsgraph &depsgraph,
436 Object &object,
437 const float value,
438 const IndexMask &node_mask)
439{
440 SculptSession &ss = *object.sculpt;
441
442 if (value == 0.0f && ss.subdiv_ccg->masks.is_empty()) {
443 /* NOTE: Deleting the mask array would be possible here. */
444 return;
445 }
446
447 MultiresModifierData &mmd = *BKE_sculpt_multires_active(&scene, &object);
448 BKE_sculpt_mask_layers_ensure(&depsgraph, &bmain, &object, &mmd);
449
450 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
452 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
453 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
454
455 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
456
457 Array<bool> node_changed(node_mask.min_array_size(), false);
458
459 MutableSpan<float> masks = subdiv_ccg.masks;
460 node_mask.foreach_index(GrainSize(1), [&](const int i) {
461 const Span<int> grid_indices = nodes[i].grids();
462 if (std::all_of(grid_indices.begin(), grid_indices.end(), [&](const int grid) {
463 const Span<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
464 return std::all_of(grid_masks.begin(), grid_masks.end(), [&](const float mask) {
465 return mask == value;
466 });
467 }))
468 {
469 return;
470 }
472
473 if (grid_hidden.is_empty()) {
474 for (const int grid : grid_indices) {
475 masks.slice(bke::ccg::grid_range(key, grid)).fill(value);
476 }
477 }
478 else {
479 for (const int grid : grid_indices) {
480 MutableSpan<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
481 bits::foreach_0_index(grid_hidden[grid], [&](const int i) { grid_masks[i] = value; });
482 }
483 }
484 node_changed[i] = true;
485 });
486
487 IndexMaskMemory memory;
488 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
489 if (node_changed.is_empty()) {
490 return;
491 }
492 pbvh.tag_masks_changed(changed_nodes);
494 changed_nodes.foreach_index([&](const int i) {
495 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
497 });
498}
499
500static void fill_mask_bmesh(const Depsgraph &depsgraph,
501 Object &object,
502 const float value,
503 const IndexMask &node_mask)
504{
505 SculptSession &ss = *object.sculpt;
506 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
508
509 BMesh &bm = *ss.bm;
510 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
511 if (value == 0.0f && offset == -1) {
512 return;
513 }
514 if (offset == -1) {
515 /* Mask is not dynamically added or removed for dynamic topology sculpting. */
517 return;
518 }
519
520 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
521
522 Array<bool> node_changed(node_mask.min_array_size(), false);
523
524 node_mask.foreach_index(GrainSize(1), [&](const int i) {
525 bool changed = false;
527 if (!BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
528 if (BM_ELEM_CD_GET_FLOAT(vert, offset) != value) {
529 BM_ELEM_CD_SET_FLOAT(vert, offset, value);
530 changed = true;
531 }
532 }
533 }
534 if (changed) {
535 node_changed[i] = true;
536 }
537 });
538 IndexMaskMemory memory;
539 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
540 if (node_changed.is_empty()) {
541 return;
542 }
543 pbvh.tag_masks_changed(changed_nodes);
544 changed_nodes.foreach_index([&](const int i) {
545 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
547 });
548}
549
550static void fill_mask(
551 Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object, const float value)
552{
553 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
554 IndexMaskMemory memory;
555 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
556 switch (pbvh.type()) {
558 fill_mask_mesh(depsgraph, object, value, node_mask);
559 break;
561 fill_mask_grids(bmain, scene, depsgraph, object, value, node_mask);
562 break;
564 fill_mask_bmesh(depsgraph, object, value, node_mask);
565 break;
566 }
567}
568
569static void invert_mask_grids(Main &bmain,
570 const Scene &scene,
571 Depsgraph &depsgraph,
572 Object &object,
573 const IndexMask &node_mask)
574{
575 SculptSession &ss = *object.sculpt;
576
577 MultiresModifierData &mmd = *BKE_sculpt_multires_active(&scene, &object);
578 BKE_sculpt_mask_layers_ensure(&depsgraph, &bmain, &object, &mmd);
579
580 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
581
582 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
584 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
585 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
586
587 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
588 MutableSpan<float> masks = subdiv_ccg.masks;
589 node_mask.foreach_index(GrainSize(1), [&](const int i) {
590 const Span<int> grid_indices = nodes[i].grids();
591 if (grid_hidden.is_empty()) {
592 for (const int grid : grid_indices) {
593 for (float &value : masks.slice(bke::ccg::grid_range(key, grid))) {
594 value = 1.0f - value;
595 }
596 }
597 }
598 else {
599 for (const int grid : grid_indices) {
600 MutableSpan<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
601 bits::foreach_0_index(grid_hidden[grid],
602 [&](const int i) { grid_masks[i] = 1.0f - grid_masks[i]; });
603 }
604 }
606 });
607 pbvh.tag_masks_changed(node_mask);
608
610}
611
612static void invert_mask_bmesh(const Depsgraph &depsgraph,
613 Object &object,
614 const IndexMask &node_mask)
615{
616 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
618 BMesh &bm = *object.sculpt->bm;
619 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
620 if (offset == -1) {
622 return;
623 }
624
625 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
626 node_mask.foreach_index(GrainSize(1), [&](const int i) {
628 if (!BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
629 BM_ELEM_CD_SET_FLOAT(vert, offset, 1.0f - BM_ELEM_CD_GET_FLOAT(vert, offset));
630 }
631 }
633 });
634 pbvh.tag_masks_changed(node_mask);
635}
636
637static void invert_mask(Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object)
638{
639 IndexMaskMemory memory;
640 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(*bke::object::pbvh_get(object), memory);
641 switch (bke::object::pbvh_get(object)->type()) {
644 depsgraph, object, node_mask, [&](MutableSpan<float> mask, const Span<int> verts) {
645 for (const int vert : verts) {
646 mask[vert] = 1.0f - mask[vert];
647 }
648 });
649 break;
651 invert_mask_grids(bmain, scene, depsgraph, object, node_mask);
652 break;
654 invert_mask_bmesh(depsgraph, object, node_mask);
655 break;
656 }
657}
658
660{
661 Main &bmain = *CTX_data_main(C);
662 const Scene &scene = *CTX_data_scene(C);
663 Object &object = *CTX_data_active_object(C);
665
666 const FloodFillMode mode = FloodFillMode(RNA_enum_get(op->ptr, "mode"));
667 const float value = RNA_float_get(op->ptr, "value");
668
670
671 undo::push_begin(scene, object, op);
672 switch (mode) {
674 fill_mask(bmain, scene, depsgraph, object, value);
675 break;
677 fill_mask(bmain, scene, depsgraph, object, 1.0f - value);
678 break;
680 invert_mask(bmain, scene, depsgraph, object);
681 break;
682 }
683
684 undo::push_end(object);
685
687
688 return OPERATOR_FINISHED;
689}
690
692{
693 /* Identifiers. */
694 ot->name = "Mask Flood Fill";
695 ot->idname = "PAINT_OT_mask_flood_fill";
696 ot->description = "Fill the whole mask with a given value, or invert its values";
697
698 /* API callbacks. */
699 ot->exec = mask_flood_fill_exec;
700 ot->poll = SCULPT_mode_poll;
701
702 ot->flag = OPTYPE_REGISTER;
703
704 /* RNA. */
705 RNA_def_enum(ot->srna, "mode", mode_items, int(FloodFillMode::Value), "Mode", nullptr);
707 ot->srna,
708 "value",
709 0.0f,
710 0.0f,
711 1.0f,
712 "Value",
713 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
714 0.0f,
715 1.0f);
716}
717
719
720/* -------------------------------------------------------------------- */
724
731
732static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
733{
734 const Scene &scene = *CTX_data_scene(&C);
737 undo::push_begin(scene, *gesture_data.vc.obact, &op);
738}
739
740static float mask_gesture_get_new_value(const float elem, FloodFillMode mode, float value)
741{
742 switch (mode) {
744 return value;
746 return 1.0f - value;
748 return 1.0f - elem;
749 }
751 return 0.0f;
752}
753
755{
756 const IndexMask &node_mask = gesture_data.node_mask;
757 const MaskOperation &op = *reinterpret_cast<const MaskOperation *>(gesture_data.operation);
758 Object &object = *gesture_data.vc.obact;
759 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
760 switch (bke::object::pbvh_get(object)->type()) {
762 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
765 depsgraph, object, node_mask, [&](MutableSpan<float> node_mask, const Span<int> verts) {
766 for (const int i : verts.index_range()) {
767 const int vert = verts[i];
768 if (gesture::is_affected(gesture_data, positions[vert], normals[vert])) {
769 node_mask[i] = mask_gesture_get_new_value(node_mask[i], op.mode, op.value);
770 }
771 }
772 });
773 break;
774 }
776 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
778 SubdivCCG &subdiv_ccg = *gesture_data.ss->subdiv_ccg;
779 const Span<float3> positions = subdiv_ccg.positions;
780 const Span<float3> normals = subdiv_ccg.normals;
781 MutableSpan<float> masks = subdiv_ccg.masks;
782 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
783 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
784
785 Array<bool> node_changed(node_mask.min_array_size(), false);
786
787 node_mask.foreach_index(GrainSize(1), [&](const int node_index) {
788 bke::pbvh::GridsNode &node = nodes[node_index];
789 bool any_changed = false;
790 for (const int grid : node.grids()) {
791 const int vert_start = grid * key.grid_area;
792 BKE_subdiv_ccg_foreach_visible_grid_vert(key, grid_hidden, grid, [&](const int i) {
793 const int vert = vert_start + i;
794 if (gesture::is_affected(gesture_data, positions[vert], normals[vert])) {
795 float &mask = masks[vert];
796 if (!any_changed) {
797 any_changed = true;
799 }
801 }
802 });
803 if (any_changed) {
804 bke::pbvh::node_update_mask_grids(key, masks, node);
805 node_changed[node_index] = true;
806 }
807 }
808 });
809
810 IndexMaskMemory memory;
811 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
812 break;
813 }
815 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
817 BMesh &bm = *gesture_data.ss->bm;
818 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
819
820 Array<bool> node_changed(node_mask.min_array_size(), false);
821
822 node_mask.foreach_index(GrainSize(1), [&](const int i) {
823 bool any_changed = false;
825 if (gesture::is_affected(gesture_data, vert->co, vert->no)) {
826 const float old_mask = BM_ELEM_CD_GET_FLOAT(vert, offset);
827 if (!any_changed) {
828 any_changed = true;
830 }
831 const float new_mask = mask_gesture_get_new_value(old_mask, op.mode, op.value);
832 BM_ELEM_CD_SET_FLOAT(vert, offset, new_mask);
833 }
834 }
835 if (any_changed) {
837 node_changed[i] = true;
838 }
839 });
840
841 IndexMaskMemory memory;
842 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
843 break;
844 }
845 }
846}
847
848static void gesture_end(bContext &C, gesture::GestureData &gesture_data)
849{
851 Object &object = *gesture_data.vc.obact;
852 if (bke::object::pbvh_get(object)->type() == bke::pbvh::Type::Grids) {
854 }
855 undo::push_end(object);
856}
857
858static void init_operation(bContext &C, gesture::GestureData &gesture_data, wmOperator &op)
859{
860 gesture_data.operation = reinterpret_cast<gesture::Operation *>(
862
863 MaskOperation *mask_operation = (MaskOperation *)gesture_data.operation;
864
865 Object *object = gesture_data.vc.obact;
866 MultiresModifierData *mmd = BKE_sculpt_multires_active(gesture_data.vc.scene, object);
868 CTX_data_depsgraph_pointer(&C), CTX_data_main(&C), gesture_data.vc.obact, mmd);
869
870 mask_operation->op.begin = gesture_begin;
872 mask_operation->op.end = gesture_end;
873
874 mask_operation->mode = FloodFillMode(RNA_enum_get(op.ptr, "mode"));
875 mask_operation->value = RNA_float_get(op.ptr, "value");
876}
877
879{
880 RNA_def_enum(ot->srna, "mode", mode_items, int(FloodFillMode::Value), "Mode", nullptr);
882 ot->srna,
883 "value",
884 1.0f,
885 0.0f,
886 1.0f,
887 "Value",
888 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
889 0.0f,
890 1.0f);
891}
892
894{
895 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
896 if (!gesture_data) {
897 return OPERATOR_CANCELLED;
898 }
899 init_operation(*C, *gesture_data, *op);
900 gesture::apply(*C, *gesture_data, *op);
901 return OPERATOR_FINISHED;
902}
903
905{
906 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
907 if (!gesture_data) {
908 return OPERATOR_CANCELLED;
909 }
910 init_operation(*C, *gesture_data, *op);
911 gesture::apply(*C, *gesture_data, *op);
912 return OPERATOR_FINISHED;
913}
914
916{
917 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
918 if (!gesture_data) {
919 return OPERATOR_CANCELLED;
920 }
921 init_operation(*C, *gesture_data, *op);
922 gesture::apply(*C, *gesture_data, *op);
923 return OPERATOR_FINISHED;
924}
925
927{
928 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
929 if (!gesture_data) {
930 return OPERATOR_CANCELLED;
931 }
932 init_operation(*C, *gesture_data, *op);
933 gesture::apply(*C, *gesture_data, *op);
934 return OPERATOR_FINISHED;
935}
936
938{
939 ot->name = "Mask Lasso Gesture";
940 ot->idname = "PAINT_OT_mask_lasso_gesture";
941 ot->description = "Mask within a shape defined by the cursor";
942
943 ot->invoke = WM_gesture_lasso_invoke;
944 ot->modal = WM_gesture_lasso_modal;
945 ot->exec = gesture_lasso_exec;
946
948
950
953
955}
956
958{
959 ot->name = "Mask Box Gesture";
960 ot->idname = "PAINT_OT_mask_box_gesture";
961 ot->description = "Mask within a rectangle defined by the cursor";
962
963 ot->invoke = WM_gesture_box_invoke;
964 ot->modal = WM_gesture_box_modal;
965 ot->exec = gesture_box_exec;
966
968
969 ot->flag = OPTYPE_REGISTER;
970
973
975}
976
978{
979 ot->name = "Mask Line Gesture";
980 ot->idname = "PAINT_OT_mask_line_gesture";
981 ot->description = "Mask to one side of a line defined by the cursor";
982
985 ot->exec = gesture_line_exec;
986
988
989 ot->flag = OPTYPE_REGISTER;
990
993
995}
996
998{
999 ot->name = "Mask Polyline Gesture";
1000 ot->idname = "PAINT_OT_mask_polyline_gesture";
1001 ot->description = "Mask within a shape defined by the cursor";
1002
1005 ot->exec = gesture_polyline_exec;
1006
1008
1010
1013
1015}
1016
1018
1019} // namespace blender::ed::sculpt_paint::mask
Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Main * CTX_data_main(const bContext *C)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
@ MULTIRES_COORDS_MODIFIED
void multires_mark_as_modified(Depsgraph *depsgraph, Object *object, MultiresModifiedFlags flags)
Definition multires.cc:247
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2513
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2797
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2806
A BVH for high poly meshes.
bool BKE_pbvh_node_fully_hidden_get(const blender::bke::pbvh::Node &node)
Definition pbvh.cc:1723
void BKE_pbvh_node_fully_unmasked_set(blender::bke::pbvh::Node &node, int fully_masked)
Definition pbvh.cc:1747
void BKE_pbvh_node_fully_masked_set(blender::bke::pbvh::Node &node, int fully_masked)
Definition pbvh.cc:1729
const blender::Set< BMVert *, 0 > & BKE_pbvh_bmesh_node_unique_verts(blender::bke::pbvh::BMeshNode *node)
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
void BKE_subdiv_ccg_foreach_visible_grid_vert(const CCGKey &key, const blender::BitGroupVector<> &grid_hidden, const int grid, const Fn &fn)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
@ CD_PROP_FLOAT
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_XOR
Read Guarded memory(de)allocation.
#define C
Definition RandGen.cpp:29
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define BM_ELEM_CD_SET_FLOAT(ele, offset, f)
#define BM_ELEM_CD_GET_FLOAT(ele, offset)
@ BM_ELEM_HIDDEN
#define BM_elem_flag_test(ele, hflag)
BMesh * bm
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
BPy_StructRNA * depsgraph
static T sum(const btAlignedObjectArray< T > &items)
Span< T > as_span() const
Definition BLI_array.hh:243
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
bool is_empty() const
Definition BLI_array.hh:264
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
bool is_empty() const
Definition BLI_array.hh:264
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr Iterator end() const
constexpr Iterator begin() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:573
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool is_empty() const
Definition BLI_span.hh:260
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
Span< NodeT > nodes() const
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:669
IndexMask slice(IndexRange range) const
void foreach_index(Fn &&fn) const
nullptr float
static ushort indices[]
static float verts[][3]
static float normals[][3]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
bool indexed_data_equal(const Span< T > all_values, const Span< int > indices, const Span< T > values)
void foreach_0_index(const BitSpanT &data, Fn &&fn)
IndexRange grid_range(const int grid_area, const int grid)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2628
void node_update_mask_bmesh(int mask_offset, BMeshNode &node)
Definition pbvh.cc:1479
void node_update_mask_mesh(Span< float > mask, MeshNode &node)
Definition pbvh.cc:1421
void node_update_mask_grids(const CCGKey &key, Span< float > masks, GridsNode &node)
Definition pbvh.cc:1449
Span< float3 > vert_normals_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1059
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1040
std::unique_ptr< GestureData > init_from_box(bContext *C, wmOperator *op)
void operator_properties(wmOperatorType *ot, ShapeType shapeType)
std::unique_ptr< GestureData > init_from_polyline(bContext *C, wmOperator *op)
std::unique_ptr< GestureData > init_from_line(bContext *C, const wmOperator *op)
void apply(bContext &C, GestureData &gesture_data, wmOperator &op)
std::unique_ptr< GestureData > init_from_lasso(bContext *C, wmOperator *op)
bool is_affected(const GestureData &gesture_data, const float3 &position, const float3 &normal)
Span< int > node_visible_verts(const bke::pbvh::MeshNode &node, const Span< bool > hide_vert, Vector< int > &indices)
bool mask_equals_array_bmesh(const int mask_offset, const Set< BMVert *, 0 > &verts, const Span< float > values)
void scatter_mask_grids(const Span< float > mask, SubdivCCG &subdiv_ccg, const Span< int > grids)
static void gesture_end(bContext &C, gesture::GestureData &gesture_data)
static float average_masks(const int mask_offset, const Span< const BMVert * > verts)
void PAINT_OT_mask_polyline_gesture(wmOperatorType *ot)
bool mask_equals_array_grids(const Span< float > masks, const CCGKey &key, const Span< int > grids, const Span< float > values)
void PAINT_OT_mask_line_gesture(wmOperatorType *ot)
void write_mask_mesh(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, FunctionRef< void(MutableSpan< float >, Span< int >)> write_fn)
static wmOperatorStatus gesture_polyline_exec(bContext *C, wmOperator *op)
static const EnumPropertyItem mode_items[]
static wmOperatorStatus mask_flood_fill_exec(bContext *C, wmOperator *op)
static void fill_mask_grids(Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object, const float value, const IndexMask &node_mask)
static void invert_mask_bmesh(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
void update_mask_mesh(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, FunctionRef< void(MutableSpan< float >, Span< int >)> update_fn)
static void init_operation(bContext &C, gesture::GestureData &gesture_data, wmOperator &op)
static bool try_remove_mask_mesh(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
void scatter_mask_bmesh(const Span< float > mask, const BMesh &bm, const Set< BMVert *, 0 > &verts)
static void gesture_apply_for_symmetry_pass(bContext &, gesture::GestureData &gesture_data)
void gather_mask_bmesh(const BMesh &bm, const Set< BMVert *, 0 > &verts, const MutableSpan< float > r_mask)
void mix_new_masks(const Span< float > new_masks, const float factor, const MutableSpan< float > masks)
Definition paint_mask.cc:95
static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
static wmOperatorStatus gesture_lasso_exec(bContext *C, wmOperator *op)
Array< float > duplicate_mask(const Object &object)
Definition paint_mask.cc:55
static Span< int > get_hidden_verts(const bke::pbvh::MeshNode &node, const Span< bool > hide_vert, Vector< int > &indices)
static void fill_mask(Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object, const float value)
void invert_mask(const MutableSpan< float > masks)
void gather_mask_grids(const SubdivCCG &subdiv_ccg, const Span< int > grids, const MutableSpan< float > r_mask)
static wmOperatorStatus gesture_line_exec(bContext *C, wmOperator *op)
static void fill_mask_bmesh(const Depsgraph &depsgraph, Object &object, const float value, const IndexMask &node_mask)
static float mask_gesture_get_new_value(const float elem, FloodFillMode mode, float value)
static void gesture_operator_properties(wmOperatorType *ot)
void PAINT_OT_mask_box_gesture(wmOperatorType *ot)
static void invert_mask_grids(Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
void PAINT_OT_mask_lasso_gesture(wmOperatorType *ot)
static wmOperatorStatus gesture_box_exec(bContext *C, wmOperator *op)
void clamp_mask(const MutableSpan< float > masks)
void PAINT_OT_mask_flood_fill(wmOperatorType *ot)
void average_neighbor_mask_bmesh(const int mask_offset, const Set< BMVert *, 0 > &verts, const MutableSpan< float > new_masks)
static void fill_mask_mesh(const Depsgraph &depsgraph, Object &object, const float value, const IndexMask &node_mask)
void push_nodes(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const Type type)
void push_node(const Depsgraph &depsgraph, const Object &object, const bke::pbvh::Node *node, const Type type)
void push_begin(const Scene &scene, Object &ob, const wmOperator *op)
void gather_data_grids(const SubdivCCG &subdiv_ccg, Span< T > src, Span< int > grids, MutableSpan< T > node_data)
Definition sculpt.cc:6405
void vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
Vector< BMVert *, 64 > BMeshNeighborVerts
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6435
Span< BMVert * > vert_neighbors_get_bmesh(BMVert &vert, BMeshNeighborVerts &r_neighbors)
Definition sculpt.cc:387
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6395
void scatter_data_grids(const SubdivCCG &subdiv_ccg, Span< T > node_data, Span< int > grids, MutableSpan< T > dst)
Definition sculpt.cc:6445
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
float RNA_float_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
bool SCULPT_mode_poll_view3d(bContext *C)
Definition sculpt.cc:3683
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3677
void SCULPT_tag_update_overlays(bContext *C)
Definition sculpt.cc:759
int grid_area
Definition BKE_ccg.hh:35
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:395
blender::Array< float > masks
blender::Array< blender::float3 > positions
Scene * scene
Definition ED_view3d.hh:73
Object * obact
Definition ED_view3d.hh:75
Depsgraph * depsgraph
Definition ED_view3d.hh:72
void(* end)(bContext &, GestureData &)
void(* begin)(bContext &, wmOperator &, GestureData &)
void(* apply_for_symmetry_pass)(bContext &, GestureData &)
struct PointerRNA * ptr
i
Definition text_draw.cc:230
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
wmOperatorType * ot
Definition wm_files.cc:4237
wmOperatorStatus WM_gesture_polyline_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_straightline_active_side_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
wmOperatorStatus WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_operator_properties_gesture_straightline(wmOperatorType *ot, int cursor)
void WM_operator_properties_border(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_gesture_polyline(wmOperatorType *ot)