Blender V4.3
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_math_base.hh"
20#include "BLI_span.hh"
21#include "BLI_vector.hh"
22
23#include "BKE_attribute.hh"
24#include "BKE_brush.hh"
25#include "BKE_ccg.hh"
26#include "BKE_context.hh"
27#include "BKE_mesh.hh"
28#include "BKE_multires.hh"
29#include "BKE_paint.hh"
30#include "BKE_pbvh_api.hh"
31#include "BKE_subdiv_ccg.hh"
32#include "BKE_subsurf.hh"
33
34#include "RNA_access.hh"
35#include "RNA_define.hh"
36
37#include "WM_api.hh"
38#include "WM_types.hh"
39
40#include "ED_select_utils.hh"
41
42#include "bmesh.hh"
43
44#include "mesh_brush_common.hh"
45#include "paint_intern.hh"
46#include "sculpt_automask.hh"
47#include "sculpt_gesture.hh"
48#include "sculpt_hide.hh"
49#include "sculpt_intern.hh"
50#include "sculpt_undo.hh"
51
53
54/* -------------------------------------------------------------------- */
59{
60 const SculptSession &ss = *object.sculpt;
61 switch (bke::object::pbvh_get(object)->type()) {
63 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
64 const bke::AttributeAccessor attributes = mesh.attributes();
65 const VArray mask = *attributes.lookup_or_default<float>(
66 ".sculpt_mask", bke::AttrDomain::Point, 0.0f);
67 Array<float> result(mask.size());
68 mask.materialize(result);
69 return result;
70 }
72 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
73 if (subdiv_ccg.masks.is_empty()) {
74 return Array<float>(subdiv_ccg.positions.size(), 0.0f);
75 }
76 return subdiv_ccg.masks;
77 }
79 BMesh &bm = *ss.bm;
80 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
82 if (offset == -1) {
83 result.fill(0.0f);
84 }
85 else {
87 for (const int i : result.index_range()) {
88 result[i] = BM_ELEM_CD_GET_FLOAT(BM_vert_at_index(&bm, i), offset);
89 }
90 }
91 return result;
92 }
93 }
95 return {};
96}
97
98void mix_new_masks(const Span<float> new_masks,
99 const Span<float> factors,
100 const MutableSpan<float> masks)
101{
102 BLI_assert(new_masks.size() == factors.size());
103 BLI_assert(new_masks.size() == masks.size());
104
105 for (const int i : masks.index_range()) {
106 masks[i] += (new_masks[i] - masks[i]) * factors[i];
107 }
108}
109
111{
112 for (float &mask : masks) {
113 mask = std::clamp(mask, 0.0f, 1.0f);
114 }
115}
116
118{
119 for (float &mask : masks) {
120 mask = 1.0f - mask;
121 }
122}
123
125 const Set<BMVert *, 0> &verts,
126 const MutableSpan<float> r_mask)
127{
128 BLI_assert(verts.size() == r_mask.size());
129
130 /* TODO: Avoid overhead of accessing attributes for every bke::pbvh::Tree node. */
131 const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
132 int i = 0;
133 for (const BMVert *vert : verts) {
134 r_mask[i] = (mask_offset == -1) ? 0.0f : BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
135 i++;
136 }
137}
138
139void gather_mask_grids(const SubdivCCG &subdiv_ccg,
140 const Span<int> grids,
141 const MutableSpan<float> r_mask)
142{
143 if (!subdiv_ccg.masks.is_empty()) {
144 gather_data_grids(subdiv_ccg, subdiv_ccg.masks.as_span(), grids, r_mask);
145 }
146 else {
147 r_mask.fill(0.0f);
148 }
149}
150
151void scatter_mask_grids(const Span<float> mask, SubdivCCG &subdiv_ccg, const Span<int> grids)
152{
153 scatter_data_grids(subdiv_ccg, mask, grids, subdiv_ccg.masks.as_mutable_span());
154}
155
157{
158 BLI_assert(verts.size() == mask.size());
159
160 const int mask_offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
161 BLI_assert(mask_offset != -1);
162 int i = 0;
163 for (BMVert *vert : verts) {
164 BM_ELEM_CD_SET_FLOAT(vert, mask_offset, mask[i]);
165 i++;
166 }
167}
168
169static float average_masks(const int mask_offset, const Span<const BMVert *> verts)
170{
171 float sum = 0;
172 for (const BMVert *vert : verts) {
173 sum += BM_ELEM_CD_GET_FLOAT(vert, mask_offset);
174 }
175 return sum / float(verts.size());
176}
177
178void average_neighbor_mask_bmesh(const int mask_offset,
179 const Set<BMVert *, 0> &verts,
180 const MutableSpan<float> new_masks)
181{
182 Vector<BMVert *, 64> neighbors;
183 int i = 0;
184 for (BMVert *vert : verts) {
185 new_masks[i] = average_masks(mask_offset, vert_neighbors_get_bmesh(*vert, neighbors));
186 i++;
187 }
188}
189
190void update_mask_mesh(const Depsgraph &depsgraph,
191 Object &object,
192 const IndexMask &node_mask,
193 FunctionRef<void(MutableSpan<float>, Span<int>)> update_fn)
194{
195 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
197
198 Mesh &mesh = *static_cast<Mesh *>(object.data);
199 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
200 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
201 bke::SpanAttributeWriter<float> mask = attributes.lookup_or_add_for_write_span<float>(
202 ".sculpt_mask", bke::AttrDomain::Point);
203 if (!mask) {
204 return;
205 }
206
207 struct LocalData {
208 Vector<int> visible_verts;
210 };
211
212 Array<bool> node_changed(node_mask.min_array_size(), false);
213
215 node_mask.foreach_index(GrainSize(1), [&](const int i) {
216 LocalData &tls = all_tls.local();
217 const Span<int> verts = hide::node_visible_verts(nodes[i], hide_vert, tls.visible_verts);
218 tls.mask.resize(verts.size());
219 gather_data_mesh(mask.span.as_span(), verts, tls.mask.as_mutable_span());
220 update_fn(tls.mask, verts);
222 return;
223 }
224 undo::push_node(depsgraph, object, &nodes[i], undo::Type::Mask);
225 scatter_data_mesh(tls.mask.as_span(), verts, mask.span);
226 bke::pbvh::node_update_mask_mesh(mask.span, nodes[i]);
227 node_changed[i] = true;
228 });
229
230 IndexMaskMemory memory;
231 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
232
233 mask.finish();
234}
235
237 const CCGKey &key,
238 const Span<int> grids,
239 const Span<float> values)
240{
241 BLI_assert(grids.size() * key.grid_area == values.size());
242
243 const IndexRange range = grids.index_range();
244 return std::all_of(range.begin(), range.end(), [&](const int i) {
245 return masks.slice(bke::ccg::grid_range(key, grids[i])) ==
246 values.slice(bke::ccg::grid_range(key, i));
247 return true;
248 });
249}
250
251bool mask_equals_array_bmesh(const int mask_offset,
252 const Set<BMVert *, 0> &verts,
253 const Span<float> values)
254{
255 BLI_assert(verts.size() == values.size());
256
257 int i = 0;
258 for (const BMVert *vert : verts) {
259 if (BM_ELEM_CD_GET_FLOAT(vert, mask_offset) != values[i]) {
260 return false;
261 }
262 i++;
263 }
264 return true;
265}
266
269/* -------------------------------------------------------------------- */
274/* The gesture API doesn't write to this enum type,
275 * it writes to eSelectOp from ED_select_utils.hh.
276 * We must thus map the modes here to the desired
277 * eSelectOp modes.
278 *
279 * Fixes #102349.
280 */
281enum class FloodFillMode {
282 Value = SEL_OP_SUB,
285};
286
287static const EnumPropertyItem mode_items[] = {
289 "VALUE",
290 0,
291 "Value",
292 "Set mask to the level specified by the 'value' property"},
294 "VALUE_INVERSE",
295 0,
296 "Value Inverted",
297 "Set mask to the level specified by the inverted 'value' property"},
298 {int(FloodFillMode::InverseMeshValue), "INVERT", 0, "Invert", "Invert the mask"},
299 {0}};
300
302 const Span<bool> hide_vert,
303 Vector<int> &indices)
304{
305 if (hide_vert.is_empty()) {
306 return {};
307 }
308 const Span<int> verts = node.verts();
310 return verts;
311 }
312 indices.resize(verts.size());
313 const int *end = std::copy_if(verts.begin(), verts.end(), indices.begin(), [&](const int vert) {
314 return hide_vert[vert];
315 });
316 indices.resize(end - indices.begin());
317 return indices;
318}
319
320static bool try_remove_mask_mesh(const Depsgraph &depsgraph,
321 Object &object,
322 const IndexMask &node_mask)
323{
324 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
326 Mesh &mesh = *static_cast<Mesh *>(object.data);
327 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
328 const VArraySpan mask = *attributes.lookup<float>(".sculpt_mask", bke::AttrDomain::Point);
329 if (mask.is_empty()) {
330 return true;
331 }
332
333 /* If there are any hidden vertices that shouldn't be affected with a mask value set, the
334 * attribute cannot be removed. This could also be done by building an IndexMask in the full
335 * vertex domain. */
336 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
338 const bool hidden_masked_verts = threading::parallel_reduce(
339 node_mask.index_range(),
340 1,
341 false,
342 [&](const IndexRange range, bool value) {
343 if (value) {
344 return value;
345 }
346 Vector<int> &index_data = all_index_data.local();
347 node_mask.slice(range).foreach_index([&](const int i) {
348 if (value) {
349 return;
350 }
351 const Span<int> verts = get_hidden_verts(nodes[i], hide_vert, index_data);
352 if (std::any_of(verts.begin(), verts.end(), [&](int i) { return mask[i] > 0.0f; })) {
353 value = true;
354 return;
355 }
356 });
357 return value;
358 },
359 std::logical_or());
360 if (hidden_masked_verts) {
361 return false;
362 }
363
364 IndexMaskMemory memory;
365 const IndexMask changed_nodes = IndexMask::from_predicate(
366 node_mask, GrainSize(1), memory, [&](const int i) {
367 const Span<int> verts = nodes[i].verts();
368 return std::any_of(
369 verts.begin(), verts.end(), [&](const int i) { return mask[i] != 0.0f; });
370 });
371
372 undo::push_nodes(depsgraph, object, changed_nodes, undo::Type::Mask);
373 attributes.remove(".sculpt_mask");
374 changed_nodes.foreach_index([&](const int i) {
375 BKE_pbvh_node_fully_masked_set(nodes[i], false);
376 BKE_pbvh_node_fully_unmasked_set(nodes[i], true);
377 });
378 pbvh.tag_masks_changed(changed_nodes);
379 return true;
380}
381
382static void fill_mask_mesh(const Depsgraph &depsgraph,
383 Object &object,
384 const float value,
385 const IndexMask &node_mask)
386{
387 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
389
390 Mesh &mesh = *static_cast<Mesh *>(object.data);
391 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
392 const VArraySpan hide_vert = *attributes.lookup<bool>(".hide_vert", bke::AttrDomain::Point);
393 if (value == 0.0f) {
394 if (try_remove_mask_mesh(depsgraph, object, node_mask)) {
395 return;
396 }
397 }
398
399 bke::SpanAttributeWriter<float> mask = attributes.lookup_or_add_for_write_span<float>(
400 ".sculpt_mask", bke::AttrDomain::Point);
401
402 Array<bool> node_changed(node_mask.min_array_size(), false);
403
405 node_mask.foreach_index(GrainSize(1), [&](const int i) {
406 Vector<int> &index_data = all_index_data.local();
407 const Span<int> verts = hide::node_visible_verts(nodes[i], hide_vert, index_data);
408 if (std::all_of(verts.begin(), verts.end(), [&](int i) { return mask.span[i] == value; })) {
409 return;
410 }
411 undo::push_node(depsgraph, object, &nodes[i], undo::Type::Mask);
412 mask.span.fill_indices(verts, value);
413 node_changed[i] = true;
414 });
415
416 IndexMaskMemory memory;
417 const IndexMask changed_nodes = IndexMask::from_bools(node_mask, node_changed, memory);
418 pbvh.tag_masks_changed(changed_nodes);
419
420 mask.finish();
421 changed_nodes.foreach_index([&](const int i) {
422 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
423 BKE_pbvh_node_fully_unmasked_set(nodes[i], value == 0.0f);
424 });
425}
426
427static void fill_mask_grids(Main &bmain,
428 const Scene &scene,
429 Depsgraph &depsgraph,
430 Object &object,
431 const float value,
432 const IndexMask &node_mask)
433{
434 SculptSession &ss = *object.sculpt;
435
436 if (value == 0.0f && ss.subdiv_ccg->masks.is_empty()) {
437 /* NOTE: Deleting the mask array would be possible here. */
438 return;
439 }
440
441 MultiresModifierData &mmd = *BKE_sculpt_multires_active(&scene, &object);
442 BKE_sculpt_mask_layers_ensure(&depsgraph, &bmain, &object, &mmd);
443
444 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
446 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
447 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
448
449 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
450
451 Array<bool> node_changed(node_mask.min_array_size(), false);
452
453 MutableSpan<float> masks = subdiv_ccg.masks;
454 node_mask.foreach_index(GrainSize(1), [&](const int i) {
455 const Span<int> grid_indices = nodes[i].grids();
456 if (std::all_of(grid_indices.begin(), grid_indices.end(), [&](const int grid) {
457 const Span<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
458 return std::all_of(grid_masks.begin(), grid_masks.end(), [&](const float mask) {
459 return mask == value;
460 });
461 }))
462 {
463 return;
464 }
465 undo::push_node(depsgraph, object, &nodes[i], undo::Type::Mask);
466
467 if (grid_hidden.is_empty()) {
468 for (const int grid : grid_indices) {
469 masks.slice(bke::ccg::grid_range(key, grid)).fill(value);
470 }
471 }
472 else {
473 for (const int grid : grid_indices) {
474 MutableSpan<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
475 bits::foreach_0_index(grid_hidden[grid], [&](const int i) { grid_masks[i] = value; });
476 }
477 }
478 node_changed[i] = true;
479 });
480
481 IndexMaskMemory memory;
482 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
483 if (node_changed.is_empty()) {
484 return;
485 }
486 pbvh.tag_masks_changed(changed_nodes);
488 changed_nodes.foreach_index([&](const int i) {
489 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
490 BKE_pbvh_node_fully_unmasked_set(nodes[i], value == 0.0f);
491 });
492}
493
494static void fill_mask_bmesh(const Depsgraph &depsgraph,
495 Object &object,
496 const float value,
497 const IndexMask &node_mask)
498{
499 SculptSession &ss = *object.sculpt;
500 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
502
503 BMesh &bm = *ss.bm;
504 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
505 if (value == 0.0f && offset == -1) {
506 return;
507 }
508 if (offset == -1) {
509 /* Mask is not dynamically added or removed for dynamic topology sculpting. */
511 return;
512 }
513
514 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
515
516 Array<bool> node_changed(node_mask.min_array_size(), false);
517
518 node_mask.foreach_index(GrainSize(1), [&](const int i) {
519 bool changed = false;
520 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&nodes[i])) {
521 if (!BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
522 if (BM_ELEM_CD_GET_FLOAT(vert, offset) != value) {
523 BM_ELEM_CD_SET_FLOAT(vert, offset, value);
524 changed = true;
525 }
526 }
527 }
528 if (changed) {
529 node_changed[i] = true;
530 }
531 });
532 IndexMaskMemory memory;
533 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
534 if (node_changed.is_empty()) {
535 return;
536 }
537 pbvh.tag_masks_changed(changed_nodes);
538 changed_nodes.foreach_index([&](const int i) {
539 BKE_pbvh_node_fully_masked_set(nodes[i], value == 1.0f);
540 BKE_pbvh_node_fully_unmasked_set(nodes[i], value == 0.0f);
541 });
542}
543
544static void fill_mask(
545 Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object, const float value)
546{
547 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
548 IndexMaskMemory memory;
549 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
550 switch (pbvh.type()) {
551 case bke::pbvh::Type::Mesh:
552 fill_mask_mesh(depsgraph, object, value, node_mask);
553 break;
554 case bke::pbvh::Type::Grids:
555 fill_mask_grids(bmain, scene, depsgraph, object, value, node_mask);
556 break;
557 case bke::pbvh::Type::BMesh:
558 fill_mask_bmesh(depsgraph, object, value, node_mask);
559 break;
560 }
561}
562
563static void invert_mask_grids(Main &bmain,
564 const Scene &scene,
565 Depsgraph &depsgraph,
566 Object &object,
567 const IndexMask &node_mask)
568{
569 SculptSession &ss = *object.sculpt;
570
571 MultiresModifierData &mmd = *BKE_sculpt_multires_active(&scene, &object);
572 BKE_sculpt_mask_layers_ensure(&depsgraph, &bmain, &object, &mmd);
573
574 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
575
576 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
578 SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
579 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
580
581 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
582 MutableSpan<float> masks = subdiv_ccg.masks;
583 node_mask.foreach_index(GrainSize(1), [&](const int i) {
584 const Span<int> grid_indices = nodes[i].grids();
585 if (grid_hidden.is_empty()) {
586 for (const int grid : grid_indices) {
587 for (float &value : masks.slice(bke::ccg::grid_range(key, grid))) {
588 value = 1.0f - value;
589 }
590 }
591 }
592 else {
593 for (const int grid : grid_indices) {
594 MutableSpan<float> grid_masks = masks.slice(bke::ccg::grid_range(key, grid));
595 bits::foreach_0_index(grid_hidden[grid],
596 [&](const int i) { grid_masks[i] = 1.0f - grid_masks[i]; });
597 }
598 }
599 bke::pbvh::node_update_mask_grids(key, masks, nodes[i]);
600 });
601 pbvh.tag_masks_changed(node_mask);
602
604}
605
606static void invert_mask_bmesh(const Depsgraph &depsgraph,
607 Object &object,
608 const IndexMask &node_mask)
609{
610 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
612 BMesh &bm = *object.sculpt->bm;
613 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
614 if (offset == -1) {
616 return;
617 }
618
619 undo::push_nodes(depsgraph, object, node_mask, undo::Type::Mask);
620 node_mask.foreach_index(GrainSize(1), [&](const int i) {
621 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&nodes[i])) {
622 if (!BM_elem_flag_test(vert, BM_ELEM_HIDDEN)) {
623 BM_ELEM_CD_SET_FLOAT(vert, offset, 1.0f - BM_ELEM_CD_GET_FLOAT(vert, offset));
624 }
625 }
626 bke::pbvh::node_update_mask_bmesh(offset, nodes[i]);
627 });
628 pbvh.tag_masks_changed(node_mask);
629}
630
631static void invert_mask(Main &bmain, const Scene &scene, Depsgraph &depsgraph, Object &object)
632{
633 IndexMaskMemory memory;
634 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(*bke::object::pbvh_get(object), memory);
635 switch (bke::object::pbvh_get(object)->type()) {
636 case bke::pbvh::Type::Mesh:
638 depsgraph, object, node_mask, [&](MutableSpan<float> mask, const Span<int> verts) {
639 for (const int vert : verts) {
640 mask[vert] = 1.0f - mask[vert];
641 }
642 });
643 break;
644 case bke::pbvh::Type::Grids:
645 invert_mask_grids(bmain, scene, depsgraph, object, node_mask);
646 break;
647 case bke::pbvh::Type::BMesh:
648 invert_mask_bmesh(depsgraph, object, node_mask);
649 break;
650 }
651}
652
654{
655 Main &bmain = *CTX_data_main(C);
656 const Scene &scene = *CTX_data_scene(C);
657 Object &object = *CTX_data_active_object(C);
659
660 const FloodFillMode mode = FloodFillMode(RNA_enum_get(op->ptr, "mode"));
661 const float value = RNA_float_get(op->ptr, "value");
662
664
665 undo::push_begin(scene, object, op);
666 switch (mode) {
667 case FloodFillMode::Value:
668 fill_mask(bmain, scene, depsgraph, object, value);
669 break;
670 case FloodFillMode::InverseValue:
671 fill_mask(bmain, scene, depsgraph, object, 1.0f - value);
672 break;
673 case FloodFillMode::InverseMeshValue:
674 invert_mask(bmain, scene, depsgraph, object);
675 break;
676 }
677
678 undo::push_end(object);
679
681
682 return OPERATOR_FINISHED;
683}
684
686{
687 /* Identifiers. */
688 ot->name = "Mask Flood Fill";
689 ot->idname = "PAINT_OT_mask_flood_fill";
690 ot->description = "Fill the whole mask with a given value, or invert its values";
691
692 /* API callbacks. */
695
697
698 /* RNA. */
699 RNA_def_enum(ot->srna, "mode", mode_items, int(FloodFillMode::Value), "Mode", nullptr);
701 ot->srna,
702 "value",
703 0.0f,
704 0.0f,
705 1.0f,
706 "Value",
707 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
708 0.0f,
709 1.0f);
710}
711
714/* -------------------------------------------------------------------- */
725
726static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
727{
728 const Scene &scene = *CTX_data_scene(&C);
729 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
731 undo::push_begin(scene, *gesture_data.vc.obact, &op);
732}
733
734static float mask_gesture_get_new_value(const float elem, FloodFillMode mode, float value)
735{
736 switch (mode) {
737 case FloodFillMode::Value:
738 return value;
739 case FloodFillMode::InverseValue:
740 return 1.0f - value;
741 case FloodFillMode::InverseMeshValue:
742 return 1.0f - elem;
743 }
745 return 0.0f;
746}
747
749{
750 const IndexMask &node_mask = gesture_data.node_mask;
751 const MaskOperation &op = *reinterpret_cast<const MaskOperation *>(gesture_data.operation);
752 Object &object = *gesture_data.vc.obact;
753 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
754 switch (bke::object::pbvh_get(object)->type()) {
755 case bke::pbvh::Type::Mesh: {
756 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
757 const Span<float3> normals = bke::pbvh::vert_normals_eval(depsgraph, object);
758 update_mask_mesh(
759 depsgraph, object, node_mask, [&](MutableSpan<float> node_mask, const Span<int> verts) {
760 for (const int i : verts.index_range()) {
761 const int vert = verts[i];
762 if (gesture::is_affected(gesture_data, positions[vert], normals[vert])) {
763 node_mask[i] = mask_gesture_get_new_value(node_mask[i], op.mode, op.value);
764 }
765 }
766 });
767 break;
768 }
769 case bke::pbvh::Type::Grids: {
770 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
772 SubdivCCG &subdiv_ccg = *gesture_data.ss->subdiv_ccg;
773 const Span<float3> positions = subdiv_ccg.positions;
774 const Span<float3> normals = subdiv_ccg.normals;
775 MutableSpan<float> masks = subdiv_ccg.masks;
776 const BitGroupVector<> &grid_hidden = subdiv_ccg.grid_hidden;
777 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
778
779 Array<bool> node_changed(node_mask.min_array_size(), false);
780
781 node_mask.foreach_index(GrainSize(1), [&](const int node_index) {
782 bke::pbvh::GridsNode &node = nodes[node_index];
783 bool any_changed = false;
784 for (const int grid : node.grids()) {
785 const int vert_start = grid * key.grid_area;
786 BKE_subdiv_ccg_foreach_visible_grid_vert(key, grid_hidden, grid, [&](const int i) {
787 const int vert = vert_start + i;
788 if (gesture::is_affected(gesture_data, positions[vert], normals[vert])) {
789 float &mask = masks[vert];
790 if (!any_changed) {
791 any_changed = true;
792 undo::push_node(depsgraph, object, &node, undo::Type::Mask);
793 }
794 mask = mask_gesture_get_new_value(mask, op.mode, op.value);
795 }
796 });
797 if (any_changed) {
798 bke::pbvh::node_update_mask_grids(key, masks, node);
799 node_changed[node_index] = true;
800 }
801 }
802 });
803
804 IndexMaskMemory memory;
805 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
806 break;
807 }
808 case bke::pbvh::Type::BMesh: {
809 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
811 BMesh &bm = *gesture_data.ss->bm;
812 const int offset = CustomData_get_offset_named(&bm.vdata, CD_PROP_FLOAT, ".sculpt_mask");
813
814 Array<bool> node_changed(node_mask.min_array_size(), false);
815
816 node_mask.foreach_index(GrainSize(1), [&](const int i) {
817 bool any_changed = false;
818 for (BMVert *vert : BKE_pbvh_bmesh_node_unique_verts(&nodes[i])) {
819 if (gesture::is_affected(gesture_data, vert->co, vert->no)) {
820 const float old_mask = BM_ELEM_CD_GET_FLOAT(vert, offset);
821 if (!any_changed) {
822 any_changed = true;
823 undo::push_node(depsgraph, object, &nodes[i], undo::Type::Mask);
824 }
825 const float new_mask = mask_gesture_get_new_value(old_mask, op.mode, op.value);
826 BM_ELEM_CD_SET_FLOAT(vert, offset, new_mask);
827 }
828 }
829 if (any_changed) {
830 bke::pbvh::node_update_mask_bmesh(offset, nodes[i]);
831 node_changed[i] = true;
832 }
833 });
834
835 IndexMaskMemory memory;
836 pbvh.tag_masks_changed(IndexMask::from_bools(node_changed, memory));
837 break;
838 }
839 }
840}
841
842static void gesture_end(bContext &C, gesture::GestureData &gesture_data)
843{
844 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
845 Object &object = *gesture_data.vc.obact;
846 if (bke::object::pbvh_get(object)->type() == bke::pbvh::Type::Grids) {
848 }
849 undo::push_end(object);
850}
851
852static void init_operation(bContext &C, gesture::GestureData &gesture_data, wmOperator &op)
853{
854 gesture_data.operation = reinterpret_cast<gesture::Operation *>(
855 MEM_cnew<MaskOperation>(__func__));
856
857 MaskOperation *mask_operation = (MaskOperation *)gesture_data.operation;
858
859 Object *object = gesture_data.vc.obact;
860 MultiresModifierData *mmd = BKE_sculpt_multires_active(gesture_data.vc.scene, object);
862 CTX_data_depsgraph_pointer(&C), CTX_data_main(&C), gesture_data.vc.obact, mmd);
863
864 mask_operation->op.begin = gesture_begin;
866 mask_operation->op.end = gesture_end;
867
868 mask_operation->mode = FloodFillMode(RNA_enum_get(op.ptr, "mode"));
869 mask_operation->value = RNA_float_get(op.ptr, "value");
870}
871
873{
874 RNA_def_enum(ot->srna, "mode", mode_items, int(FloodFillMode::Value), "Mode", nullptr);
876 ot->srna,
877 "value",
878 1.0f,
879 0.0f,
880 1.0f,
881 "Value",
882 "Mask level to use when mode is 'Value'; zero means no masking and one is fully masked",
883 0.0f,
884 1.0f);
885}
886
888{
889 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
890 if (!gesture_data) {
891 return OPERATOR_CANCELLED;
892 }
893 init_operation(*C, *gesture_data, *op);
894 gesture::apply(*C, *gesture_data, *op);
895 return OPERATOR_FINISHED;
896}
897
899{
900 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
901 if (!gesture_data) {
902 return OPERATOR_CANCELLED;
903 }
904 init_operation(*C, *gesture_data, *op);
905 gesture::apply(*C, *gesture_data, *op);
906 return OPERATOR_FINISHED;
907}
908
910{
911 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
912 if (!gesture_data) {
913 return OPERATOR_CANCELLED;
914 }
915 init_operation(*C, *gesture_data, *op);
916 gesture::apply(*C, *gesture_data, *op);
917 return OPERATOR_FINISHED;
918}
919
921{
922 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
923 if (!gesture_data) {
924 return OPERATOR_CANCELLED;
925 }
926 init_operation(*C, *gesture_data, *op);
927 gesture::apply(*C, *gesture_data, *op);
928 return OPERATOR_FINISHED;
929}
930
932{
933 ot->name = "Mask Lasso Gesture";
934 ot->idname = "PAINT_OT_mask_lasso_gesture";
935 ot->description = "Mask within a shape defined by the cursor";
936
940
942
944
946 gesture::operator_properties(ot, gesture::ShapeType::Lasso);
947
949}
950
952{
953 ot->name = "Mask Box Gesture";
954 ot->idname = "PAINT_OT_mask_box_gesture";
955 ot->description = "Mask within a rectangle defined by the cursor";
956
960
962
964
966 gesture::operator_properties(ot, gesture::ShapeType::Box);
967
969}
970
972{
973 ot->name = "Mask Line Gesture";
974 ot->idname = "PAINT_OT_mask_line_gesture";
975 ot->description = "Mask to one side of a line defined by the cursor";
976
980
982
984
986 gesture::operator_properties(ot, gesture::ShapeType::Line);
987
989}
990
992{
993 ot->name = "Mask Polyline Gesture";
994 ot->idname = "PAINT_OT_mask_polyline_gesture";
995 ot->description = "Mask within a shape defined by the cursor";
996
1000
1002
1004
1006 gesture::operator_properties(ot, gesture::ShapeType::Lasso);
1007
1009}
1010
1013} // 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)
void multires_mark_as_modified(Depsgraph *depsgraph, Object *object, MultiresModifiedFlags flags)
Definition multires.cc:373
MultiresModifierData * BKE_sculpt_multires_active(const Scene *scene, Object *ob)
Definition paint.cc:2316
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd)
Definition paint.cc:2610
A BVH for high poly meshes.
bool BKE_pbvh_node_fully_hidden_get(const blender::bke::pbvh::Node &node)
Definition pbvh.cc:1542
void BKE_pbvh_node_fully_unmasked_set(blender::bke::pbvh::Node &node, int fully_masked)
Definition pbvh.cc:1564
void BKE_pbvh_node_fully_masked_set(blender::bke::pbvh::Node &node, int fully_masked)
Definition pbvh.cc:1547
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)
@ MULTIRES_COORDS_MODIFIED
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
@ CD_PROP_FLOAT
Object is a sort of wrapper for general info.
@ SEL_OP_ADD
@ SEL_OP_SUB
@ SEL_OP_XOR
Read Guarded memory(de)allocation.
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#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)
ATTR_WARN_UNUSED_RESULT BMesh * bm
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
BLI_INLINE BMVert * BM_vert_at_index(BMesh *bm, const int index)
#define BM_VERT
static T sum(const btAlignedObjectArray< T > &items)
Span< T > as_span() const
Definition BLI_array.hh:232
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
bool is_empty() const
Definition BLI_array.hh:253
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
Definition BLI_span.hh:574
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr bool is_empty() const
Definition BLI_span.hh:261
void resize(const int64_t new_size)
MutableSpan< T > as_mutable_span()
Span< T > as_span() const
Span< NodeT > nodes() const
void tag_masks_changed(const IndexMask &node_mask)
Definition pbvh.cc:586
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
IndexMask slice(IndexRange range) const
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
const Depsgraph * depsgraph
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static ushort indices[]
static float verts[][3]
bool indexed_data_equal(const Span< T > all_values, const Span< int > indices, const Span< T > values)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
void node_update_mask_mesh(Span< float > mask, MeshNode &node)
Definition pbvh.cc:1219
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 int gesture_box_exec(bContext *C, wmOperator *op)
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 const EnumPropertyItem mode_items[]
static int gesture_polyline_exec(bContext *C, wmOperator *op)
void mix_new_masks(const Span< float > new_masks, const Span< float > factors, const MutableSpan< float > masks)
Definition paint_mask.cc:98
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 int gesture_lasso_exec(bContext *C, 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)
static int mask_flood_fill_exec(bContext *C, wmOperator *op)
static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
Array< float > duplicate_mask(const Object &object)
Definition paint_mask.cc:58
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 int 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)
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, Type type)
void gather_data_grids(const SubdivCCG &subdiv_ccg, Span< T > src, Span< int > grids, MutableSpan< T > node_data)
Definition sculpt.cc:6092
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6122
Span< BMVert * > vert_neighbors_get_bmesh(BMVert &vert, Vector< BMVert *, 64 > &r_neighbors)
Definition sculpt.cc:389
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6082
void scatter_data_grids(const SubdivCCG &subdiv_ccg, Span< T > node_data, Span< int > grids, MutableSpan< T > dst)
Definition sculpt.cc:6132
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:153
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:3566
bool SCULPT_mode_poll(bContext *C)
Definition sculpt.cc:3560
void SCULPT_tag_update_overlays(bContext *C)
Definition sculpt.cc:735
int totvert
CustomData vdata
int grid_area
Definition BKE_ccg.hh:35
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
blender::Array< float > masks
blender::Array< blender::float3 > positions
Scene * scene
Definition ED_view3d.hh:69
Object * obact
Definition ED_view3d.hh:71
Depsgraph * depsgraph
Definition ED_view3d.hh:68
void(* end)(bContext &, GestureData &)
void(* begin)(bContext &, wmOperator &, GestureData &)
void(* apply_for_symmetry_pass)(bContext &, GestureData &)
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* modal)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1036
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
wmOperatorType * ot
Definition wm_files.cc:4125
int WM_gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_polyline_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_oneshot_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_straightline_active_side_invoke(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)