Blender V5.0
sculpt_face_set.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2020 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8#include "sculpt_face_set.hh"
9
10#include <cmath>
11#include <cstdlib>
12#include <queue>
13
14#include "MEM_guardedalloc.h"
15
16#include "BLI_array.hh"
17#include "BLI_array_utils.hh"
18#include "BLI_bit_vector.hh"
20#include "BLI_function_ref.hh"
21#include "BLI_hash.h"
22#include "BLI_math_matrix.h"
23#include "BLI_math_vector.h"
24#include "BLI_math_vector.hh"
26#include "BLI_span.hh"
27#include "BLI_task.hh"
28#include "BLI_vector.hh"
29
30#include "BLT_translation.hh"
31
33#include "DNA_object_types.h"
34#include "DNA_scene_types.h"
35
36#include "BKE_attribute.hh"
37#include "BKE_ccg.hh"
38#include "BKE_context.hh"
39#include "BKE_customdata.hh"
40#include "BKE_layer.hh"
41#include "BKE_mesh.hh"
42#include "BKE_mesh_fair.hh"
43#include "BKE_mesh_mapping.hh"
44#include "BKE_object.hh"
45#include "BKE_paint.hh"
46#include "BKE_paint_bvh.hh"
47#include "BKE_paint_types.hh"
48#include "BKE_subdiv_ccg.hh"
49
50#include "DEG_depsgraph.hh"
51
52#include "WM_api.hh"
53#include "WM_types.hh"
54
55#include "ED_sculpt.hh"
56
57#include "mesh_brush_common.hh"
58#include "paint_hide.hh"
59#include "sculpt_boundary.hh"
60#include "sculpt_gesture.hh"
61#include "sculpt_intern.hh"
62#include "sculpt_islands.hh"
63#include "sculpt_undo.hh"
64
65#include "RNA_access.hh"
66#include "RNA_define.hh"
67
68#include "bmesh.hh"
69
71
72/* -------------------------------------------------------------------- */
75
77{
78 SculptSession &ss = *object.sculpt;
79 switch (bke::object::pbvh_get(object)->type()) {
82 Mesh &mesh = *static_cast<Mesh *>(object.data);
83 const bke::AttributeAccessor attributes = mesh.attributes();
84 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
87 face_sets.index_range(),
88 4096,
89 1,
90 [&](const IndexRange range, int max) {
91 for (const int id : face_sets.slice(range)) {
92 max = std::max(max, id);
93 }
94 return max;
95 },
96 [](const int a, const int b) { return std::max(a, b); });
97 return max + 1;
98 }
100 BMesh &bm = *ss.bm;
101 const int cd_offset = CustomData_get_offset_named(
102 &bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
103 if (cd_offset == -1) {
104 /* Default face set ID is 1, so the next available id should never be 1 */
105 return 2;
106 }
107 int next_face_set = 1;
108 BMIter iter;
109 BMFace *f;
110 BM_ITER_MESH (f, &iter, &bm, BM_FACES_OF_MESH) {
111 const int fset = *static_cast<const int *>(POINTER_OFFSET(f->head.data, cd_offset));
112 next_face_set = std::max(next_face_set, fset);
113 }
114
115 return next_face_set + 1;
116 }
117 }
119 return 0;
120}
121
122void initialize_none_to_id(Mesh *mesh, const int new_id)
123{
124 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
125 bke::SpanAttributeWriter<int> face_sets = attributes.lookup_for_write_span<int>(
126 ".sculpt_face_set");
127 if (!face_sets) {
128 return;
129 }
130
131 for (const int i : face_sets.span.index_range()) {
132 if (face_sets.span[i] == SCULPT_FACE_SET_NONE) {
133 face_sets.span[i] = new_id;
134 }
135 }
136 face_sets.finish();
137}
138
139int active_update_and_get(bContext *C, Object &ob, const float mval[2])
140{
141 if (!ob.sculpt) {
143 }
144
146 if (!cursor_geometry_info_update(C, &gi, mval, false)) {
148 }
149
150 return active_face_set_get(ob);
151}
152
154{
155 Mesh &mesh = *static_cast<Mesh *>(object.data);
156 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
157 if (attributes.contains(".sculpt_face_set")) {
158 return false;
159 }
160 attributes.add<int>(".sculpt_face_set",
163 mesh.face_sets_color_default = 1;
164 return true;
165}
166
168{
169 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
170 if (!attributes.contains(".sculpt_face_set")) {
171 attributes.add<int>(".sculpt_face_set",
174 mesh.face_sets_color_default = 1;
175 }
176 return attributes.lookup_or_add_for_write_span<int>(".sculpt_face_set", bke::AttrDomain::Face);
177}
178
180{
181 Mesh &mesh = *static_cast<Mesh *>(object.data);
182 SculptSession &ss = *object.sculpt;
183 BMesh &bm = *ss.bm;
184 if (!CustomData_has_layer_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set")) {
185 BM_data_layer_add_named(&bm, &bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
186 const int offset = CustomData_get_offset_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
187 if (offset == -1) {
188 return -1;
189 }
190 BMIter iter;
191 BMFace *face;
192 BM_ITER_MESH (face, &iter, &bm, BM_FACES_OF_MESH) {
193 BM_ELEM_CD_SET_INT(face, offset, 1);
194 }
195 mesh.face_sets_color_default = 1;
196 return offset;
197 }
198 return CustomData_get_offset_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
199}
200
202{
203 const bke::AttributeAccessor attributes = mesh.attributes();
204 const VArray<int> attribute = *attributes.lookup_or_default(
205 ".sculpt_face_set", bke::AttrDomain::Face, 0);
206 Array<int> face_sets(attribute.size());
207 array_utils::copy(attribute, face_sets.as_mutable_span());
208 return face_sets;
209}
210
212 const Span<int> face_sets,
213 const bool unique,
214 const Span<int> verts,
215 const MutableSpan<float> factors)
216{
217 BLI_assert(verts.size() == factors.size());
218
219 for (const int i : verts.index_range()) {
220 if (unique == face_set::vert_has_unique_face_set(vert_to_face_map, face_sets, verts[i])) {
221 factors[i] = 0.0f;
222 }
223 }
224}
225
227 const Span<int> corner_verts,
228 const GroupedSpan<int> vert_to_face_map,
229 const Span<int> face_sets,
230 const SubdivCCG &subdiv_ccg,
231 const bool unique,
232 const Span<int> grids,
233 const MutableSpan<float> factors)
234{
235 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
236 BLI_assert(grids.size() * key.grid_area == factors.size());
237
238 for (const int i : grids.index_range()) {
239 const int node_start = i * key.grid_area;
240 for (const int y : IndexRange(key.grid_size)) {
241 for (const int x : IndexRange(key.grid_size)) {
242 const int offset = CCG_grid_xy_to_index(key.grid_size, x, y);
243 const int node_vert = node_start + offset;
244 if (factors[node_vert] == 0.0f) {
245 continue;
246 }
247
248 SubdivCCGCoord coord{};
249 coord.grid_index = grids[i];
250 coord.x = x;
251 coord.y = y;
253 faces, corner_verts, vert_to_face_map, face_sets, subdiv_ccg, coord))
254 {
255 factors[node_vert] = 0.0f;
256 }
257 }
258 }
259 }
260}
261
263 const bool unique,
264 const Set<BMVert *, 0> &verts,
265 const MutableSpan<float> factors)
266{
267 BLI_assert(verts.size() == factors.size());
268
269 int i = 0;
270 for (const BMVert *vert : verts) {
271 if (unique == face_set::vert_has_unique_face_set(face_set_offset, *vert)) {
272 factors[i] = 0.0f;
273 }
274 i++;
275 }
276}
277
279
280/* -------------------------------------------------------------------- */
284
285static void face_sets_update(const Depsgraph &depsgraph,
286 Object &object,
287 const IndexMask &node_mask,
288 const FunctionRef<void(Span<int>, MutableSpan<int>)> calc_face_sets)
289{
290 SculptSession &ss = *object.sculpt;
291 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
292
294 *static_cast<Mesh *>(object.data));
295
296 struct TLS {
297 Vector<int> face_indices;
298 Vector<int> new_face_sets;
299 };
300
301 Array<bool> node_changed(pbvh.nodes_num(), false);
302
304 if (pbvh.type() == bke::pbvh::Type::Mesh) {
306 node_mask.foreach_index(GrainSize(1), [&](const int i) {
307 TLS &tls = all_tls.local();
308 const Span<int> faces = nodes[i].faces();
309
310 tls.new_face_sets.resize(faces.size());
311 MutableSpan<int> new_face_sets = tls.new_face_sets;
312 gather_data_mesh(face_sets.span.as_span(), faces, new_face_sets);
313 calc_face_sets(faces, new_face_sets);
314 if (array_utils::indexed_data_equal<int>(face_sets.span, faces, new_face_sets)) {
315 return;
316 }
317
319 scatter_data_mesh(new_face_sets.as_span(), faces, face_sets.span);
320 node_changed[i] = true;
321 });
322 }
323 else if (pbvh.type() == bke::pbvh::Type::Grids) {
325 node_mask.foreach_index(GrainSize(1), [&](const int i) {
326 TLS &tls = all_tls.local();
328 *ss.subdiv_ccg, nodes[i], tls.face_indices);
329
330 tls.new_face_sets.resize(faces.size());
331 MutableSpan<int> new_face_sets = tls.new_face_sets;
332 gather_data_mesh(face_sets.span.as_span(), faces, new_face_sets);
333 calc_face_sets(faces, new_face_sets);
334 if (array_utils::indexed_data_equal<int>(face_sets.span, faces, new_face_sets)) {
335 return;
336 }
337
339 scatter_data_mesh(new_face_sets.as_span(), faces, face_sets.span);
340 node_changed[i] = true;
341 });
342 }
343
344 IndexMaskMemory memory;
345 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
346 face_sets.finish();
347}
348
349enum class CreateMode {
352 All = 2,
353 Selection = 3,
354};
355
356static void clear_face_sets(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
357{
358 Mesh &mesh = *static_cast<Mesh *>(object.data);
359 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
360 if (!attributes.contains(".sculpt_face_set")) {
361 return;
362 }
363 SculptSession &ss = *object.sculpt;
364 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
365
366 Array<bool> node_changed(pbvh.nodes_num(), false);
367
368 const int default_face_set = mesh.face_sets_color_default;
369 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
370 if (pbvh.type() == bke::pbvh::Type::Mesh) {
372 node_mask.foreach_index(GrainSize(1), [&](const int i) {
373 const Span<int> faces = nodes[i].faces();
374 if (std::any_of(faces.begin(), faces.end(), [&](const int face) {
375 return face_sets[face] != default_face_set;
376 }))
377 {
379 node_changed[i] = true;
380 }
381 });
382 }
383 else if (pbvh.type() == bke::pbvh::Type::Grids) {
386 node_mask.foreach_index(GrainSize(1), [&](const int i) {
387 Vector<int> &face_indices = all_face_indices.local();
389 *ss.subdiv_ccg, nodes[i], face_indices);
390 if (std::any_of(faces.begin(), faces.end(), [&](const int face) {
391 return face_sets[face] != default_face_set;
392 }))
393 {
395 node_changed[i] = true;
396 }
397 });
398 }
399 IndexMaskMemory memory;
400 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
401 attributes.remove(".sculpt_face_set");
402}
403
405{
406 const Scene &scene = *CTX_data_scene(C);
407 Object &object = *CTX_data_active_object(C);
409
410 const CreateMode mode = CreateMode(RNA_enum_get(op->ptr, "mode"));
411
412 const View3D *v3d = CTX_wm_view3d(C);
413 const Base *base = CTX_data_active_base(C);
414 if (!BKE_base_is_visible(v3d, base)) {
415 return OPERATOR_CANCELLED;
416 }
417
418 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
419 if (pbvh.type() == bke::pbvh::Type::BMesh) {
420 /* Dyntopo not supported. */
421 return OPERATOR_CANCELLED;
422 }
423
424 Mesh &mesh = *static_cast<Mesh *>(object.data);
425 const bke::AttributeAccessor attributes = mesh.attributes();
426
428
429 undo::push_begin(scene, object, op);
430
431 const int next_face_set = find_next_available_id(object);
432
433 IndexMaskMemory memory;
434 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
435 switch (mode) {
436 case CreateMode::Masked: {
437 if (pbvh.type() == bke::pbvh::Type::Mesh) {
438 const OffsetIndices faces = mesh.faces();
439 const Span<int> corner_verts = mesh.corner_verts();
440 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
442 const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask",
444 if (!mask.is_empty()) {
446 object,
447 node_mask,
448 [&](const Span<int> indices, MutableSpan<int> face_sets) {
449 for (const int i : indices.index_range()) {
450 if (!hide_poly.is_empty() && hide_poly[indices[i]]) {
451 continue;
452 }
453 const Span<int> face_verts = corner_verts.slice(faces[indices[i]]);
454 if (!std::any_of(face_verts.begin(),
455 face_verts.end(),
456 [&](const int vert) { return mask[vert] > 0.5f; }))
457 {
458 continue;
459 }
460 face_sets[i] = next_face_set;
461 }
462 });
463 }
464 }
465 else if (pbvh.type() == bke::pbvh::Type::Grids) {
466 const OffsetIndices<int> faces = mesh.faces();
467 const SculptSession &ss = *object.sculpt;
468 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
469 const int grid_area = subdiv_ccg.grid_area;
470 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
472 const Span<float> masks = subdiv_ccg.masks;
473 if (!masks.is_empty()) {
475 object,
476 node_mask,
477 [&](const Span<int> indices, MutableSpan<int> face_sets) {
478 for (const int i : indices.index_range()) {
479 if (!hide_poly.is_empty() && hide_poly[indices[i]]) {
480 continue;
481 }
482
483 const Span<float> face_masks = masks.slice(
484 bke::ccg::face_range(faces, grid_area, indices[i]));
485 if (!std::any_of(face_masks.begin(),
486 face_masks.end(),
487 [&](const float mask) { return mask > 0.5f; }))
488 {
489 continue;
490 }
491 face_sets[i] = next_face_set;
492 }
493 });
494 }
495 }
496 break;
497 }
498 case CreateMode::Visible: {
499 const VArray<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
500 switch (array_utils::booleans_mix_calc(hide_poly)) {
504 /* If all vertices in the sculpt are visible, remove face sets and update the default
505 * color. This way the new face set will be white, and it is a quick way of disabling all
506 * face sets and the performance hit of rendering the overlay. */
507 clear_face_sets(depsgraph, object, node_mask);
508 break;
510 const VArraySpan<bool> hide_poly_span(hide_poly);
512 object,
513 node_mask,
514 [&](const Span<int> indices, MutableSpan<int> face_sets) {
515 for (const int i : indices.index_range()) {
516 if (!hide_poly_span[indices[i]]) {
517 face_sets[i] = next_face_set;
518 }
519 }
520 });
521 break;
522 }
523 break;
524 }
525 case CreateMode::All: {
527 object,
528 node_mask,
529 [&](const Span<int> /*indices*/, MutableSpan<int> face_sets) {
530 face_sets.fill(next_face_set);
531 });
532 break;
533 }
535 const VArraySpan<bool> select_poly = *attributes.lookup_or_default<bool>(
536 ".select_poly", bke::AttrDomain::Face, false);
537 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
539
541 depsgraph, object, node_mask, [&](const Span<int> indices, MutableSpan<int> face_sets) {
542 for (const int i : indices.index_range()) {
543 if (select_poly[indices[i]]) {
544 if (!hide_poly.is_empty() && hide_poly[i]) {
545 continue;
546 }
547 face_sets[i] = next_face_set;
548 }
549 }
550 });
551
552 break;
553 }
554 }
555
556 undo::push_end(object);
557
559
560 return OPERATOR_FINISHED;
561}
562
564{
565 ot->name = "Create Face Set";
566 ot->idname = "SCULPT_OT_face_sets_create";
567 ot->description = "Create a new Face Set";
568
569 ot->exec = create_op_exec;
570 ot->poll = SCULPT_mode_poll;
571
573
574 static EnumPropertyItem modes[] = {
575 {int(CreateMode::Masked),
576 "MASKED",
577 0,
578 "Face Set from Masked",
579 "Create a new Face Set from the masked faces"},
581 "VISIBLE",
582 0,
583 "Face Set from Visible",
584 "Create a new Face Set from the visible vertices"},
585 {int(CreateMode::All),
586 "ALL",
587 0,
588 "Face Set Full Mesh",
589 "Create an unique Face Set with all faces in the sculpt"},
591 "SELECTION",
592 0,
593 "Face Set from Edit Mode Selection",
594 "Create an Face Set corresponding to the Edit Mode face selection"},
595 {0, nullptr, 0, nullptr, nullptr},
596 };
597 RNA_def_enum(ot->srna, "mode", modes, int(CreateMode::Masked), "Mode", "");
598}
599
610
611using FaceSetsFloodFillFn = FunctionRef<bool(int from_face, int edge, int to_face)>;
612
613static void init_flood_fill(Object &ob, const FaceSetsFloodFillFn &test_fn)
614{
615 SculptSession &ss = *ob.sculpt;
616 Mesh *mesh = static_cast<Mesh *>(ob.data);
617
618 BitVector<> visited_faces(mesh->faces_num, false);
619
621
622 const Span<int2> edges = mesh->edges();
623 const OffsetIndices faces = mesh->faces();
624 const Span<int> corner_edges = mesh->corner_edges();
625
626 if (ss.edge_to_face_map.is_empty()) {
628 faces, corner_edges, edges.size(), ss.edge_to_face_offsets, ss.edge_to_face_indices);
629 }
630
631 const bke::AttributeAccessor attributes = mesh->attributes();
632 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
633 const Set<int> hidden_face_sets = gather_hidden_face_sets(hide_poly, face_sets.span);
634
635 int next_face_set = 1;
636
637 for (const int i : faces.index_range()) {
638 if (!hide_poly.is_empty() && hide_poly[i]) {
639 continue;
640 }
641 if (visited_faces[i]) {
642 continue;
643 }
644 std::queue<int> queue;
645
646 while (hidden_face_sets.contains(next_face_set)) {
647 next_face_set += 1;
648 }
649 face_sets.span[i] = next_face_set;
650 visited_faces[i].set(true);
651 queue.push(i);
652
653 while (!queue.empty()) {
654 const int face_i = queue.front();
655 queue.pop();
656
657 for (const int edge_i : corner_edges.slice(faces[face_i])) {
658 for (const int neighbor_i : ss.edge_to_face_map[edge_i]) {
659 if (neighbor_i == face_i) {
660 continue;
661 }
662 if (visited_faces[neighbor_i]) {
663 continue;
664 }
665 if (!hide_poly.is_empty() && hide_poly[neighbor_i]) {
666 continue;
667 }
668 if (!test_fn(face_i, edge_i, neighbor_i)) {
669 continue;
670 }
671
672 face_sets.span[neighbor_i] = next_face_set;
673 visited_faces[neighbor_i].set(true);
674 queue.push(neighbor_i);
675 }
676 }
677 }
678
679 next_face_set += 1;
680 }
681
682 face_sets.finish();
683}
684
685Set<int> gather_hidden_face_sets(const Span<bool> hide_poly, const Span<int> face_sets)
686{
687 if (hide_poly.is_empty()) {
688 return {};
689 }
690
691 Set<int> hidden_face_sets;
692 for (const int i : hide_poly.index_range()) {
693 if (hide_poly[i]) {
694 hidden_face_sets.add(face_sets[i]);
695 }
696 }
697
698 return hidden_face_sets;
699}
700
702{
703 const Scene &scene = *CTX_data_scene(C);
706
707 const InitMode mode = InitMode(RNA_enum_get(op->ptr, "mode"));
708
709 const View3D *v3d = CTX_wm_view3d(C);
710 const Base *base = CTX_data_active_base(C);
711 if (!BKE_base_is_visible(v3d, base)) {
712 return OPERATOR_CANCELLED;
713 }
714
716
718 /* Dyntopo not supported. */
719 if (pbvh.type() == bke::pbvh::Type::BMesh) {
720 return OPERATOR_CANCELLED;
721 }
722
723 IndexMaskMemory memory;
724 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
725 if (node_mask.is_empty()) {
726 return OPERATOR_CANCELLED;
727 }
728
729 undo::push_begin(scene, ob, op);
731
732 const float threshold = RNA_float_get(op->ptr, "threshold");
733
734 Mesh *mesh = static_cast<Mesh *>(ob.data);
735 const bke::AttributeAccessor attributes = mesh->attributes();
736
737 switch (mode) {
739 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
740 ".hide_poly", bke::AttrDomain::Face, false);
741 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) {
742 return hide_poly[from_face] == hide_poly[to_face];
743 });
744 break;
745 }
746 case InitMode::Materials: {
748 const VArraySpan<int> material_indices = *attributes.lookup_or_default<int>(
749 "material_index", bke::AttrDomain::Face, 0);
750 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
752 for (const int i : IndexRange(mesh->faces_num)) {
753 if (!hide_poly.is_empty() && hide_poly[i]) {
754 continue;
755 }
756
757 /* In some cases material face set index could be same as hidden face set index
758 * A more robust implementation is needed to avoid this */
759 face_sets.span[i] = material_indices[i] + 1;
760 }
761
762 face_sets.finish();
763 break;
764 }
765 case InitMode::Normals: {
766 const Span<float3> face_normals = mesh->face_normals();
767 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
768 return std::abs(math::dot(face_normals[from_face], face_normals[to_face])) > threshold;
769 });
770 break;
771 }
772 case InitMode::UVSeams: {
773 const VArraySpan<bool> uv_seams = *mesh->attributes().lookup_or_default<bool>(
774 "uv_seam", bke::AttrDomain::Edge, false);
776 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
777 return !uv_seams[edge];
778 });
779 break;
780 }
781 case InitMode::Creases: {
782 const VArraySpan<float> creases = *attributes.lookup_or_default<float>(
783 "crease_edge", bke::AttrDomain::Edge, 0.0f);
785 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
786 return creases[edge] < threshold;
787 });
788 break;
789 }
791 const VArraySpan<bool> sharp_edges = *mesh->attributes().lookup_or_default<bool>(
792 "sharp_edge", bke::AttrDomain::Edge, false);
794 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
795 return !sharp_edges[edge];
796 });
797 break;
798 }
800 const VArraySpan<float> bevel_weights = *attributes.lookup_or_default<float>(
801 "bevel_weight_edge", bke::AttrDomain::Edge, 0.0f);
803 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
804 return bevel_weights[edge] < threshold;
805 });
806 break;
807 }
809 Array<int> face_sets_copy = duplicate_face_sets(*mesh);
810 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
811 return face_sets_copy[from_face] == face_sets_copy[to_face];
812 });
813 break;
814 }
815 }
816
817 undo::push_end(ob);
818
819 pbvh.tag_face_sets_changed(node_mask);
820
822
823 return OPERATOR_FINISHED;
824}
825
827{
828 ot->name = "Init Face Sets";
829 ot->idname = "SCULPT_OT_face_sets_init";
830 ot->description = "Initializes all Face Sets in the mesh";
831
832 ot->exec = init_op_exec;
833 ot->poll = SCULPT_mode_poll;
834
836
837 static EnumPropertyItem modes[] = {
839 "LOOSE_PARTS",
840 0,
841 "Face Sets from Loose Parts",
842 "Create a Face Set per loose part in the mesh"},
844 "MATERIALS",
845 0,
846 "Face Sets from Material Slots",
847 "Create a Face Set per Material Slot"},
848 {int(InitMode::Normals),
849 "NORMALS",
850 0,
851 "Face Sets from Mesh Normals",
852 "Create Face Sets for Faces that have similar normal"},
853 {int(InitMode::UVSeams),
854 "UV_SEAMS",
855 0,
856 "Face Sets from UV Seams",
857 "Create Face Sets using UV Seams as boundaries"},
858 {int(InitMode::Creases),
859 "CREASES",
860 0,
861 "Face Sets from Edge Creases",
862 "Create Face Sets using Edge Creases as boundaries"},
864 "BEVEL_WEIGHT",
865 0,
866 "Face Sets from Bevel Weight",
867 "Create Face Sets using Bevel Weights as boundaries"},
869 "SHARP_EDGES",
870 0,
871 "Face Sets from Sharp Edges",
872 "Create Face Sets using Sharp Edges as boundaries"},
874 "FACE_SET_BOUNDARIES",
875 0,
876 "Face Sets from Face Set Boundaries",
877 "Create a Face Set per isolated Face Set"},
878 {0, nullptr, 0, nullptr, nullptr},
879 };
880 RNA_def_enum(ot->srna, "mode", modes, int(InitMode::LooseParts), "Mode", "");
882 ot->srna,
883 "threshold",
884 0.5f,
885 0.0f,
886 1.0f,
887 "Threshold",
888 "Minimum value to consider a certain attribute a boundary when creating the Face Sets",
889 0.0f,
890 1.0f);
891}
892
893enum class VisibilityMode {
897};
898
899static void face_hide_update(const Depsgraph &depsgraph,
900 Object &object,
901 const IndexMask &node_mask,
902 const FunctionRef<void(Span<int>, MutableSpan<bool>)> calc_hide)
903{
904 SculptSession &ss = *object.sculpt;
905 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
906 Mesh &mesh = *static_cast<Mesh *>(object.data);
907 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
909 ".hide_poly", bke::AttrDomain::Face);
910
911 struct TLS {
912 Vector<int> face_indices;
913 Vector<bool> new_hide;
914 };
915
916 Array<bool> node_changed(node_mask.min_array_size(), false);
917
919 if (pbvh.type() == bke::pbvh::Type::Mesh) {
921 node_mask.foreach_index(GrainSize(1), [&](const int i) {
922 TLS &tls = all_tls.local();
923 const Span<int> faces = nodes[i].faces();
924
925 tls.new_hide.resize(faces.size());
926 MutableSpan<bool> new_hide = tls.new_hide;
927 gather_data_mesh(hide_poly.span.as_span(), faces, new_hide);
928 calc_hide(faces, new_hide);
929 if (array_utils::indexed_data_equal<bool>(hide_poly.span, faces, new_hide)) {
930 return;
931 }
932
934 scatter_data_mesh(new_hide.as_span(), faces, hide_poly.span);
935 node_changed[i] = true;
936 });
937 }
938 else if (pbvh.type() == bke::pbvh::Type::Grids) {
940 node_mask.foreach_index(GrainSize(1), [&](const int i) {
941 TLS &tls = all_tls.local();
943 *ss.subdiv_ccg, nodes[i], tls.face_indices);
944
945 tls.new_hide.resize(faces.size());
946 MutableSpan<bool> new_hide = tls.new_hide;
947 gather_data_mesh(hide_poly.span.as_span(), faces, new_hide);
948 calc_hide(faces, new_hide);
949 if (array_utils::indexed_data_equal<bool>(hide_poly.span, faces, new_hide)) {
950 return;
951 }
952
954 scatter_data_mesh(new_hide.as_span(), faces, hide_poly.span);
955 node_changed[i] = true;
956 });
957 }
958
959 hide_poly.finish();
960
961 IndexMaskMemory memory;
962 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
963 if (changed_nodes.is_empty()) {
964 return;
965 }
967 pbvh.tag_visibility_changed(node_mask);
968}
969
970static void show_all(Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
971{
972 switch (bke::object::pbvh_get(object)->type()) {
974 hide::mesh_show_all(depsgraph, object, node_mask);
975 break;
977 hide::grids_show_all(depsgraph, object, node_mask);
978 break;
981 break;
982 }
983}
984
986{
987 const Scene &scene = *CTX_data_scene(C);
988 Object &object = *CTX_data_active_object(C);
989 SculptSession &ss = *object.sculpt;
991
994
995 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
996
997 if (pbvh.type() == bke::pbvh::Type::BMesh) {
998 /* Not supported for dyntopo. There is no active face. */
999 return OPERATOR_CANCELLED;
1000 }
1001
1002 const VisibilityMode mode = VisibilityMode(RNA_enum_get(op->ptr, "mode"));
1003 const int active_face_set = active_face_set_get(object);
1004
1005 undo::push_begin(scene, object, op);
1006
1007 IndexMaskMemory memory;
1008 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1009
1010 const bke::AttributeAccessor attributes = mesh->attributes();
1011 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1012 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1014
1015 switch (mode) {
1017 if (hide_poly.contains(true) || face_sets.is_empty()) {
1018 show_all(depsgraph, object, node_mask);
1019 }
1020 else {
1022 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1023 for (const int i : hide.index_range()) {
1024 hide[i] = face_sets[faces[i]] != active_face_set;
1025 }
1026 });
1027 }
1028 break;
1029 }
1031 if (face_sets.is_empty()) {
1032 show_all(depsgraph, object, node_mask);
1033 }
1034 else {
1036 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1037 for (const int i : hide.index_range()) {
1038 if (face_sets[faces[i]] == active_face_set) {
1039 hide[i] = false;
1040 }
1041 }
1042 });
1043 }
1044 break;
1046 if (face_sets.is_empty()) {
1048 depsgraph, object, node_mask, [&](const Span<int> /*faces*/, MutableSpan<bool> hide) {
1049 hide.fill(true);
1050 });
1051 }
1052 else {
1054 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1055 for (const int i : hide.index_range()) {
1056 if (face_sets[faces[i]] == active_face_set) {
1057 hide[i] = true;
1058 }
1059 }
1060 });
1061 }
1062 break;
1063 }
1064
1065 /* For modes that use the cursor active vertex, update the rotation origin for viewport
1066 * navigation. */
1069 bke::PaintRuntime *paint_runtime = paint->runtime;
1070 if (std::holds_alternative<std::monostate>(ss.active_vert())) {
1071 paint_runtime->last_stroke_valid = false;
1072 }
1073 else {
1074 float location[3];
1075 copy_v3_v3(location, ss.active_vert_position(depsgraph, object));
1076 mul_m4_v3(object.object_to_world().ptr(), location);
1077 copy_v3_v3(paint_runtime->average_stroke_accum, location);
1078 paint_runtime->average_stroke_counter = 1;
1079 paint_runtime->last_stroke_valid = true;
1080 }
1081 }
1082
1083 undo::push_end(object);
1084
1085 pbvh.update_visibility(object);
1086
1087 islands::invalidate(*object.sculpt);
1089
1090 return OPERATOR_FINISHED;
1091}
1092
1094{
1096
1097 const View3D *v3d = CTX_wm_view3d(C);
1098 const Base *base = CTX_data_active_base(C);
1099 if (!BKE_base_is_visible(v3d, base)) {
1100 return OPERATOR_CANCELLED;
1101 }
1102
1103 /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
1104 * cursor updates. */
1106 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1108 cursor_geometry_info_update(C, &cgi, mval_fl, false);
1109
1110 return change_visibility_exec(C, op);
1111}
1112
1114{
1115 ot->name = "Face Sets Visibility";
1116 ot->idname = "SCULPT_OT_face_set_change_visibility";
1117 ot->description = "Change the visibility of the Face Sets of the sculpt";
1118
1119 ot->exec = change_visibility_exec;
1120 ot->invoke = change_visibility_invoke;
1121 ot->poll = SCULPT_mode_poll;
1122
1124
1125 static EnumPropertyItem modes[] = {
1127 "TOGGLE",
1128 0,
1129 "Toggle Visibility",
1130 "Hide all Face Sets except for the active one"},
1132 "SHOW_ACTIVE",
1133 0,
1134 "Show Active Face Set",
1135 "Show Active Face Set"},
1137 "HIDE_ACTIVE",
1138 0,
1139 "Hide Active Face Sets",
1140 "Hide Active Face Sets"},
1141 {0, nullptr, 0, nullptr, nullptr},
1142 };
1143 RNA_def_enum(ot->srna, "mode", modes, int(VisibilityMode::Toggle), "Mode", "");
1144}
1145
1147{
1149
1150 const View3D *v3d = CTX_wm_view3d(C);
1151 const Base *base = CTX_data_active_base(C);
1152 if (!BKE_base_is_visible(v3d, base)) {
1153 return OPERATOR_CANCELLED;
1154 }
1155
1157
1158 /* Dyntopo not supported. */
1159 if (pbvh.type() == bke::pbvh::Type::BMesh) {
1160 return OPERATOR_CANCELLED;
1161 }
1162
1163 Mesh *mesh = static_cast<Mesh *>(ob.data);
1164 const bke::AttributeAccessor attributes = mesh->attributes();
1165
1166 if (!attributes.contains(".sculpt_face_set")) {
1167 return OPERATOR_CANCELLED;
1168 }
1169
1170 const VArray<int> face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
1171 const int random_index = clamp_i(mesh->faces_num * BLI_hash_int_01(mesh->face_sets_color_seed),
1172 0,
1173 max_ii(0, mesh->faces_num - 1));
1174 mesh->face_sets_color_default = face_sets[random_index];
1175
1176 mesh->face_sets_color_seed += 1;
1177
1178 IndexMaskMemory memory;
1179 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1180 pbvh.tag_face_sets_changed(node_mask);
1181
1183
1184 return OPERATOR_FINISHED;
1185}
1186
1188{
1189 ot->name = "Randomize Face Sets Colors";
1190 ot->idname = "SCULPT_OT_face_sets_randomize_colors";
1191 ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
1192
1193 ot->exec = randomize_colors_exec;
1194 ot->poll = SCULPT_mode_poll;
1195
1196 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1197}
1198
1206
1207static void edit_grow_shrink(const Depsgraph &depsgraph,
1208 const Scene &scene,
1209 Object &object,
1210 const EditMode mode,
1211 const int active_face_set_id,
1212 const bool modify_hidden,
1213 wmOperator *op)
1214{
1215 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1216 Mesh &mesh = *static_cast<Mesh *>(object.data);
1217 const OffsetIndices faces = mesh.faces();
1218 const Span<int> corner_verts = mesh.corner_verts();
1219 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1220 const bke::AttributeAccessor attributes = mesh.attributes();
1221
1222 BLI_assert(attributes.contains(".sculpt_face_set"));
1223
1224 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1225 Array<int> prev_face_sets = duplicate_face_sets(mesh);
1226
1227 undo::push_begin(scene, object, op);
1228
1229 IndexMaskMemory memory;
1230 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1232 depsgraph, object, node_mask, [&](const Span<int> indices, MutableSpan<int> face_sets) {
1233 for (const int i : indices.index_range()) {
1234 const int face = indices[i];
1235 if (!modify_hidden && !hide_poly.is_empty() && hide_poly[face]) {
1236 continue;
1237 }
1238 if (mode == EditMode::Grow) {
1239 for (const int vert : corner_verts.slice(faces[face])) {
1240 for (const int neighbor_face_index : vert_to_face_map[vert]) {
1241 if (neighbor_face_index == face) {
1242 continue;
1243 }
1244 if (prev_face_sets[neighbor_face_index] == active_face_set_id) {
1245 face_sets[i] = active_face_set_id;
1246 }
1247 }
1248 }
1249 }
1250 else {
1251 if (prev_face_sets[face] == active_face_set_id) {
1252 for (const int vert_i : corner_verts.slice(faces[face])) {
1253 for (const int neighbor_face_index : vert_to_face_map[vert_i]) {
1254 if (neighbor_face_index == face) {
1255 continue;
1256 }
1257 if (prev_face_sets[neighbor_face_index] != active_face_set_id) {
1258 face_sets[i] = prev_face_sets[neighbor_face_index];
1259 }
1260 }
1261 }
1262 }
1263 }
1264 }
1265 });
1266
1267 undo::push_end(object);
1268}
1269
1270static bool check_single_face_set(const Object &object, const bool check_visible_only)
1271{
1272 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1273 const bke::AttributeAccessor attributes = mesh.attributes();
1274 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1275 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1277
1278 if (face_sets.is_empty()) {
1279 return true;
1280 }
1281 int first_face_set = SCULPT_FACE_SET_NONE;
1282 if (check_visible_only) {
1283 for (const int i : face_sets.index_range()) {
1284 if (!hide_poly.is_empty() && hide_poly[i]) {
1285 continue;
1286 }
1287 first_face_set = face_sets[i];
1288 break;
1289 }
1290 }
1291 else {
1292 first_face_set = face_sets[0];
1293 }
1294
1295 if (first_face_set == SCULPT_FACE_SET_NONE) {
1296 return true;
1297 }
1298
1299 for (const int i : face_sets.index_range()) {
1300 if (check_visible_only && !hide_poly.is_empty() && hide_poly[i]) {
1301 continue;
1302 }
1303 if (face_sets[i] != first_face_set) {
1304 return false;
1305 }
1306 }
1307 return true;
1308}
1309
1310static void delete_geometry(Object &ob, const int active_face_set_id, const bool modify_hidden)
1311{
1312 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1313 const bke::AttributeAccessor attributes = mesh.attributes();
1314 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1315 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1317
1318 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&mesh);
1319 BMeshCreateParams create_params{};
1320 create_params.use_toolflags = true;
1321 BMesh *bm = BM_mesh_create(&allocsize, &create_params);
1322
1323 BMeshFromMeshParams convert_params{};
1324 convert_params.calc_vert_normal = true;
1325 convert_params.calc_face_normal = true;
1326 BM_mesh_bm_from_me(bm, &mesh, &convert_params);
1327
1331 BMIter iter;
1332 BMFace *f;
1333 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1334 const int face_index = BM_elem_index_get(f);
1335 if (!modify_hidden && !hide_poly.is_empty() && hide_poly[face_index]) {
1336 continue;
1337 }
1338 BM_elem_flag_set(f, BM_ELEM_TAG, face_sets[face_index] == active_face_set_id);
1339 }
1342
1343 BMeshToMeshParams bmesh_to_mesh_params{};
1344 bmesh_to_mesh_params.calc_object_remap = false;
1345 BM_mesh_bm_to_me(nullptr, bm, &mesh, &bmesh_to_mesh_params);
1346
1348}
1349
1350static void edit_fairing(const Depsgraph &depsgraph,
1351 const Sculpt &sd,
1352 Object &ob,
1353 const int active_face_set_id,
1354 const eMeshFairingDepth fair_order,
1355 const float strength)
1356{
1357 SculptSession &ss = *ob.sculpt;
1358 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1361
1362 const PositionDeformData position_data(depsgraph, ob);
1363 const Span<float3> positions = position_data.eval;
1364 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1365 const BitSpan boundary_verts = ss.vertex_info.boundary;
1366 const bke::AttributeAccessor attributes = mesh.attributes();
1367 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1368 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
1369
1370 Array<bool> fair_verts(positions.size(), false);
1371 for (const int vert : positions.index_range()) {
1372 if (boundary::vert_is_boundary(vert_to_face_map, hide_poly, boundary_verts, vert)) {
1373 continue;
1374 }
1375 if (!vert_has_face_set(vert_to_face_map, face_sets, vert, active_face_set_id)) {
1376 continue;
1377 }
1378 if (!vert_has_unique_face_set(vert_to_face_map, face_sets, vert)) {
1379 continue;
1380 }
1381 fair_verts[vert] = true;
1382 }
1383
1384 Array<float3> new_positions = positions;
1385 BKE_mesh_prefair_and_fair_verts(&mesh, new_positions, fair_verts.data(), fair_order);
1386
1387 struct LocalData {
1388 Vector<float3> translations;
1389 };
1390
1391 IndexMaskMemory memory;
1392 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1394
1396 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1397 LocalData &tls = all_tls.local();
1398 const Span<int> verts = nodes[i].verts();
1399 tls.translations.resize(verts.size());
1400 const MutableSpan<float3> translations = tls.translations;
1401 for (const int i : verts.index_range()) {
1402 translations[i] = new_positions[verts[i]] - positions[verts[i]];
1403 }
1404 scale_translations(translations, strength);
1405 clip_and_lock_translations(sd, ss, positions, verts, translations);
1406 position_data.deform(translations, verts);
1407 });
1408}
1409
1410static bool edit_is_operation_valid(const Object &object,
1411 const EditMode mode,
1412 const bool modify_hidden)
1413{
1414 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1415 if (pbvh.type() == bke::pbvh::Type::BMesh) {
1416 /* Dyntopo is not supported. */
1417 return false;
1418 }
1419
1420 if (mode == EditMode::DeleteGeometry) {
1421 if (pbvh.type() == bke::pbvh::Type::Grids) {
1422 /* Modification of base mesh geometry requires special remapping of multi-resolution
1423 * displacement, which does not happen here.
1424 * Disable delete operation. It can be supported in the future by doing similar displacement
1425 * data remapping as what happens in the mesh edit mode. */
1426 return false;
1427 }
1428 if (check_single_face_set(object, !modify_hidden)) {
1429 /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the
1430 * entire object. */
1431 return false;
1432 }
1433 }
1434
1436 if (pbvh.type() == bke::pbvh::Type::Grids) {
1437 /* TODO: Multi-resolution topology representation using grids and duplicates can't be used
1438 * directly by the fair algorithm. Multi-resolution topology needs to be exposed in a
1439 * different way or converted to a mesh for this operation. */
1440 return false;
1441 }
1442 }
1443
1444 if (ELEM(mode, EditMode::Grow, EditMode::Shrink)) {
1445 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1446 const Mesh &mesh = *static_cast<Mesh *>(object.data);
1447 const bke::AttributeAccessor attributes = mesh.attributes();
1448 if (!attributes.contains(".sculpt_face_set")) {
1449 /* If a mesh does not have the face set attribute, growing or shrinking the face set will
1450 * have no effect, exit early in this case. */
1451 return false;
1452 }
1453 }
1454 }
1455
1456 return true;
1457}
1458
1460 bContext *C, Object &ob, const int active_face_set, const bool modify_hidden, wmOperator *op)
1461{
1462 const Scene &scene = *CTX_data_scene(C);
1463 Mesh *mesh = static_cast<Mesh *>(ob.data);
1464 undo::geometry_begin(scene, ob, op);
1465 delete_geometry(ob, active_face_set, modify_hidden);
1471}
1472
1474 bContext *C, Object &ob, const int active_face_set, const EditMode mode, wmOperator *op)
1475{
1476 const Scene &scene = *CTX_data_scene(C);
1477 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1478 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1480 IndexMaskMemory memory;
1481 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1482
1483 const float strength = RNA_float_get(op->ptr, "strength");
1484
1485 undo::push_begin(scene, ob, op);
1487
1488 switch (mode) {
1490 edit_fairing(depsgraph, sd, ob, active_face_set, MESH_FAIRING_DEPTH_POSITION, strength);
1491 break;
1493 edit_fairing(depsgraph, sd, ob, active_face_set, MESH_FAIRING_DEPTH_TANGENCY, strength);
1494 break;
1495 default:
1497 }
1498
1499 pbvh.tag_positions_changed(node_mask);
1500 pbvh.update_bounds(depsgraph, ob);
1503 undo::push_end(ob);
1504}
1505
1507{
1510 const EditMode mode = EditMode(RNA_enum_get(op->ptr, "mode"));
1511 const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
1512
1513 if (!edit_is_operation_valid(*ob, mode, modify_hidden)) {
1514 return false;
1515 }
1516
1518
1519 return true;
1520}
1521
1523{
1524 if (!edit_op_init(C, op)) {
1525 return OPERATOR_CANCELLED;
1526 }
1527
1528 const Scene &scene = *CTX_data_scene(C);
1529 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1531
1532 const int active_face_set = RNA_int_get(op->ptr, "active_face_set");
1533 const EditMode mode = EditMode(RNA_enum_get(op->ptr, "mode"));
1534 const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
1535
1536 switch (mode) {
1538 edit_modify_geometry(C, ob, active_face_set, modify_hidden, op);
1539 break;
1540 case EditMode::Grow:
1541 case EditMode::Shrink:
1542 edit_grow_shrink(depsgraph, scene, ob, mode, active_face_set, modify_hidden, op);
1543 break;
1546 edit_modify_coordinates(C, ob, active_face_set, mode, op);
1547 break;
1548 }
1549
1551
1552 return OPERATOR_FINISHED;
1553}
1554
1556{
1559
1560 const View3D *v3d = CTX_wm_view3d(C);
1561 const Base *base = CTX_data_active_base(C);
1562 if (!BKE_base_is_visible(v3d, base)) {
1563 return OPERATOR_CANCELLED;
1564 }
1565
1567
1568 /* Update the current active Face Set and Vertex as the operator can be used directly from the
1569 * tool without brush cursor. */
1571 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1572 if (!cursor_geometry_info_update(C, &cgi, mval_fl, false)) {
1573 /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
1574 return OPERATOR_CANCELLED;
1575 }
1576 RNA_int_set(op->ptr, "active_face_set", active_face_set_get(ob));
1577
1578 return edit_op_exec(C, op);
1579}
1580
1582{
1583 ot->name = "Edit Face Set";
1584 ot->idname = "SCULPT_OT_face_set_edit";
1585 ot->description = "Edits the current active Face Set";
1586
1587 ot->invoke = edit_op_invoke;
1588 ot->exec = edit_op_exec;
1589 ot->poll = SCULPT_mode_poll;
1590
1592
1593 PropertyRNA *prop = RNA_def_int(
1594 ot->srna, "active_face_set", 1, 0, INT_MAX, "Active Face Set", "", 0, 64);
1596
1597 static EnumPropertyItem modes[] = {
1598 {int(EditMode::Grow),
1599 "GROW",
1600 0,
1601 "Grow Face Set",
1602 "Grows the Face Sets boundary by one face based on mesh topology"},
1603 {int(EditMode::Shrink),
1604 "SHRINK",
1605 0,
1606 "Shrink Face Set",
1607 "Shrinks the Face Sets boundary by one face based on mesh topology"},
1609 "DELETE_GEOMETRY",
1610 0,
1611 "Delete Geometry",
1612 "Deletes the faces that are assigned to the Face Set"},
1614 "FAIR_POSITIONS",
1615 0,
1616 "Fair Positions",
1617 "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
1618 "vertex positions"},
1620 "FAIR_TANGENCY",
1621 0,
1622 "Fair Tangency",
1623 "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
1624 "vertex tangents"},
1625 {0, nullptr, 0, nullptr, nullptr},
1626 };
1627 RNA_def_enum(ot->srna, "mode", modes, int(EditMode::Grow), "Mode", "");
1628 prop = RNA_def_float(ot->srna, "strength", 1.0f, 0.0f, 1.0f, "Strength", "", 0.0f, 1.0f);
1630
1631 ot->prop = RNA_def_boolean(ot->srna,
1632 "modify_hidden",
1633 false,
1634 "Modify Hidden",
1635 "Apply the edit operation to hidden geometry");
1636}
1637
1639
1640/* -------------------------------------------------------------------- */
1644
1650
1651static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
1652{
1653 const Scene &scene = *CTX_data_scene(&C);
1654 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
1656 undo::push_begin(scene, *gesture_data.vc.obact, &op);
1657}
1658
1659static void gesture_apply_mesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
1660{
1661 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1662 const int new_face_set = face_set_operation->new_face_set_id;
1663 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
1664 Object &object = *gesture_data.vc.obact;
1665 Mesh &mesh = *static_cast<Mesh *>(object.data);
1666 bke::AttributeAccessor attributes = mesh.attributes();
1667 SculptSession &ss = *gesture_data.ss;
1668 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1669
1670 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
1671 const OffsetIndices<int> faces = mesh.faces();
1672 const Span<int> corner_verts = mesh.corner_verts();
1673 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1675
1676 struct TLS {
1677 Vector<int> face_indices;
1678 };
1679
1680 Array<bool> node_changed(pbvh.nodes_num(), false);
1681
1683 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1685 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1687 bool any_updated = false;
1688 for (const int face : nodes[i].faces()) {
1689 if (!hide_poly.is_empty() && hide_poly[face]) {
1690 continue;
1691 }
1692 const Span<int> face_verts = corner_verts.slice(faces[face]);
1693 const float3 face_center = bke::mesh::face_center_calc(positions, face_verts);
1694 const float3 face_normal = bke::mesh::face_normal_calc(positions, face_verts);
1695 if (!gesture::is_affected(gesture_data, face_center, face_normal)) {
1696 continue;
1697 }
1698 face_sets.span[face] = new_face_set;
1699 any_updated = true;
1700 }
1701 if (any_updated) {
1702 node_changed[i] = true;
1703 }
1704 });
1705 }
1706 else if (pbvh.type() == bke::pbvh::Type::Grids) {
1708 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1709 TLS &tls = all_tls.local();
1712 *ss.subdiv_ccg, nodes[i], tls.face_indices);
1713
1714 bool any_updated = false;
1715 for (const int face : node_faces) {
1716 if (!hide_poly.is_empty() && hide_poly[face]) {
1717 continue;
1718 }
1719 const Span<int> face_verts = corner_verts.slice(faces[face]);
1720 const float3 face_center = bke::mesh::face_center_calc(positions, face_verts);
1721 const float3 face_normal = bke::mesh::face_normal_calc(positions, face_verts);
1722 if (!gesture::is_affected(gesture_data, face_center, face_normal)) {
1723 continue;
1724 }
1725 face_sets.span[face] = new_face_set;
1726 any_updated = true;
1727 }
1728 if (any_updated) {
1729 node_changed[i] = true;
1730 }
1731 });
1732 }
1733
1734 IndexMaskMemory memory;
1735 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
1736 face_sets.finish();
1737}
1738
1739static void gesture_apply_bmesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
1740{
1741 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1742 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
1743 const int new_face_set = face_set_operation->new_face_set_id;
1744 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*gesture_data.vc.obact);
1746 const int offset = face_set::ensure_face_sets_bmesh(*gesture_data.vc.obact);
1747
1748 Array<bool> node_changed(node_mask.min_array_size(), false);
1749
1750 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1752
1753 bool any_updated = false;
1754 for (BMFace *face : BKE_pbvh_bmesh_node_faces(&nodes[i])) {
1755 if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
1756 continue;
1757 }
1758 float3 center;
1759 BM_face_calc_center_median(face, center);
1760 if (!gesture::is_affected(gesture_data, center, face->no)) {
1761 continue;
1762 }
1763 BM_ELEM_CD_SET_INT(face, offset, new_face_set);
1764 any_updated = true;
1765 }
1766
1767 if (any_updated) {
1768 node_changed[i] = true;
1769 }
1770 });
1771
1772 IndexMaskMemory memory;
1773 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
1774 if (changed_nodes.is_empty()) {
1775 return;
1776 }
1777 pbvh.tag_face_sets_changed(node_mask);
1778}
1779
1781{
1782 switch (bke::object::pbvh_get(*gesture_data.vc.obact)->type()) {
1785 gesture_apply_mesh(gesture_data, gesture_data.node_mask);
1786 break;
1788 gesture_apply_bmesh(gesture_data, gesture_data.node_mask);
1789 break;
1790 }
1791}
1792
1793static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
1794{
1795 undo::push_end(*gesture_data.vc.obact);
1796}
1797
1798static void init_operation(gesture::GestureData &gesture_data, wmOperator & /*op*/)
1799{
1800 Object &object = *gesture_data.vc.obact;
1801 gesture_data.operation = reinterpret_cast<gesture::Operation *>(
1803
1804 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1805
1806 face_set_operation->op.begin = gesture_begin;
1808 face_set_operation->op.end = gesture_end;
1809
1810 face_set_operation->new_face_set_id = face_set::find_next_available_id(object);
1811}
1812
1814{
1815 const View3D *v3d = CTX_wm_view3d(C);
1816 const Base *base = CTX_data_active_base(C);
1817 if (!BKE_base_is_visible(v3d, base)) {
1818 return OPERATOR_CANCELLED;
1819 }
1820
1821 return WM_gesture_box_invoke(C, op, event);
1822}
1823
1825{
1826 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
1827 if (!gesture_data) {
1828 return OPERATOR_CANCELLED;
1829 }
1830 init_operation(*gesture_data, *op);
1831 gesture::apply(*C, *gesture_data, *op);
1832 return OPERATOR_FINISHED;
1833}
1834
1836{
1837 const View3D *v3d = CTX_wm_view3d(C);
1838 const Base *base = CTX_data_active_base(C);
1839 if (!BKE_base_is_visible(v3d, base)) {
1840 return OPERATOR_CANCELLED;
1841 }
1842
1843 return WM_gesture_lasso_invoke(C, op, event);
1844}
1845
1847{
1848 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
1849 if (!gesture_data) {
1850 return OPERATOR_CANCELLED;
1851 }
1852 init_operation(*gesture_data, *op);
1853 gesture::apply(*C, *gesture_data, *op);
1854 return OPERATOR_FINISHED;
1855}
1856
1858{
1859 const View3D *v3d = CTX_wm_view3d(C);
1860 const Base *base = CTX_data_active_base(C);
1861 if (!BKE_base_is_visible(v3d, base)) {
1862 return OPERATOR_CANCELLED;
1863 }
1864
1866}
1867
1869{
1870 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
1871 if (!gesture_data) {
1872 return OPERATOR_CANCELLED;
1873 }
1874 init_operation(*gesture_data, *op);
1875 gesture::apply(*C, *gesture_data, *op);
1876 return OPERATOR_FINISHED;
1877}
1878
1880{
1881 const View3D *v3d = CTX_wm_view3d(C);
1882 const Base *base = CTX_data_active_base(C);
1883 if (!BKE_base_is_visible(v3d, base)) {
1884 return OPERATOR_CANCELLED;
1885 }
1886
1887 return WM_gesture_polyline_invoke(C, op, event);
1888}
1889
1891{
1892 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
1893 if (!gesture_data) {
1894 return OPERATOR_CANCELLED;
1895 }
1896 init_operation(*gesture_data, *op);
1897 gesture::apply(*C, *gesture_data, *op);
1898 return OPERATOR_FINISHED;
1899}
1900
1902{
1903 ot->name = "Face Set Lasso Gesture";
1904 ot->idname = "SCULPT_OT_face_set_polyline_gesture";
1905 ot->description = "Add a face set in a shape defined by the cursor";
1906
1907 ot->invoke = gesture_polyline_invoke;
1909 ot->exec = gesture_polyline_exec;
1910
1912
1914
1917}
1918
1920{
1921 ot->name = "Face Set Box Gesture";
1922 ot->idname = "SCULPT_OT_face_set_box_gesture";
1923 ot->description = "Add a face set in a rectangle defined by the cursor";
1924
1925 ot->invoke = gesture_box_invoke;
1926 ot->modal = WM_gesture_box_modal;
1927 ot->exec = gesture_box_exec;
1928
1930
1931 ot->flag = OPTYPE_REGISTER;
1932
1935}
1936
1938{
1939 ot->name = "Face Set Lasso Gesture";
1940 ot->idname = "SCULPT_OT_face_set_lasso_gesture";
1941 ot->description = "Add a face set in a shape defined by the cursor";
1942
1943 ot->invoke = gesture_lasso_invoke;
1944 ot->modal = WM_gesture_lasso_modal;
1945 ot->exec = gesture_lasso_exec;
1946
1948
1950
1953}
1954
1956{
1957 ot->name = "Face Set Line Gesture";
1958 ot->idname = "SCULPT_OT_face_set_line_gesture";
1959 ot->description = "Add a face set to one side of a line defined by the cursor";
1960
1961 ot->invoke = gesture_line_invoke;
1963 ot->exec = gesture_line_exec;
1964
1966
1967 ot->flag = OPTYPE_REGISTER;
1968
1971}
1972
1973
1974} // namespace blender::ed::sculpt_paint::face_set
int CCG_grid_xy_to_index(const int grid_size, const int x, const int y)
Definition BKE_ccg.hh:73
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)
Base * CTX_data_active_base(const bContext *C)
ToolSettings * CTX_data_tool_settings(const bContext *C)
View3D * CTX_wm_view3d(const bContext *C)
CustomData interface, see also DNA_customdata_types.h.
bool CustomData_has_layer_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
int CustomData_get_offset_named(const CustomData *data, eCustomDataType type, blender::StringRef name)
bool BKE_base_is_visible(const View3D *v3d, const Base *base)
void BKE_mesh_batch_cache_dirty_tag(Mesh *mesh, eMeshBatchDirtyMode mode)
@ BKE_MESH_BATCH_DIRTY_ALL
Definition BKE_mesh.h:38
eMeshFairingDepth
@ MESH_FAIRING_DEPTH_POSITION
@ MESH_FAIRING_DEPTH_TANGENCY
void BKE_mesh_prefair_and_fair_verts(Mesh *mesh, blender::MutableSpan< blender::float3 > deform_vert_positions, const bool affected_verts[], eMeshFairingDepth depth)
General operations, lookup, etc. for blender objects.
Mesh * BKE_object_get_original_mesh(const Object *object)
#define SCULPT_FACE_SET_NONE
Definition BKE_paint.hh:324
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2797
Paint * BKE_paint_get_active_from_context(const bContext *C)
Definition paint.cc:476
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2286
A BVH for high poly meshes.
const blender::Set< BMFace *, 0 > & BKE_pbvh_bmesh_node_faces(blender::bke::pbvh::BMeshNode *node)
CCGKey BKE_subdiv_ccg_key_top_level(const SubdivCCG &subdiv_ccg)
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition BLI_hash.h:92
MINLINE int max_ii(int a, int b)
MINLINE int clamp_i(int value, int min, int max)
void mul_m4_v3(const float M[4][4], float r[3])
MINLINE void copy_v3_v3(float r[3], const float a[3])
#define ELEM(...)
#define POINTER_OFFSET(v, ofs)
#define BLT_I18NCONTEXT_AMOUNT
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
Read Guarded memory(de)allocation.
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DATA
Definition WM_types.hh:509
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:218
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define BM_ELEM_CD_SET_INT(ele, offset, f)
@ BM_ELEM_HIDDEN
@ BM_ELEM_TAG
void BM_mesh_delete_hflag_context(BMesh *bm, const char hflag, const int type)
#define BM_elem_index_get(ele)
#define BM_elem_flag_set(ele, hflag, val)
#define BM_elem_flag_test(ele, hflag)
void BM_data_layer_add_named(BMesh *bm, CustomData *data, int type, const StringRef name)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
BMesh const char void * data
BMesh * bm
void BM_mesh_elem_hflag_disable_all(BMesh *bm, const char htype, const char hflag, const bool respecthide)
void BM_mesh_free(BMesh *bm)
BMesh Free Mesh.
void BM_mesh_elem_table_ensure(BMesh *bm, const char htype)
void BM_mesh_elem_table_init(BMesh *bm, const char htype)
BMesh * BM_mesh_create(const BMAllocTemplate *allocsize, const BMeshCreateParams *params)
BMesh Make Mesh.
#define BMALLOC_TEMPLATE_FROM_ME(...)
void BM_mesh_bm_from_me(BMesh *bm, const Mesh *mesh, const BMeshFromMeshParams *params)
void BM_mesh_bm_to_me(Main *bmain, BMesh *bm, Mesh *mesh, const BMeshToMeshParams *params)
#define BM_FACE
#define BM_EDGE
#define BM_VERT
void BM_face_calc_center_median(const BMFace *f, float r_cent[3])
BPy_StructRNA * depsgraph
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:248
const T * data() const
Definition BLI_array.hh:312
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr void fill(const T &value) const
Definition BLI_span.hh:517
constexpr Span< T > as_span() const
Definition BLI_span.hh:661
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:137
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
constexpr bool contains(const T &value) const
Definition BLI_span.hh:277
static VArray from_single(T value, const int64_t size)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
bool contains(StringRef attribute_id) const
GAttributeReader lookup(const StringRef attribute_id) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
GSpanAttributeWriter lookup_for_write_span(StringRef attribute_id)
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:635
Span< NodeT > nodes() const
void update_bounds(const Depsgraph &depsgraph, const Object &object)
Definition pbvh.cc:1386
int nodes_num() const
Definition pbvh.cc:590
void tag_face_sets_changed(const IndexMask &node_mask)
Definition pbvh.cc:662
void tag_visibility_changed(const IndexMask &node_mask)
Definition pbvh.cc:646
void update_visibility(const Object &object)
Definition pbvh.cc:1579
void foreach_index(Fn &&fn) const
nullptr float
static ushort indices[]
static float verts[][3]
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
static char faces[256]
void copy(const GVArray &src, GMutableSpan dst, int64_t grain_size=4096)
bool indexed_data_equal(const Span< T > all_values, const Span< int > indices, const Span< T > values)
BooleanMix booleans_mix_calc(const VArray< bool > &varray, IndexRange range_to_check)
IndexRange face_range(const OffsetIndices< int > faces, const int grid_area, const int face)
float3 face_normal_calc(Span< float3 > vert_positions, Span< int > face_verts)
GroupedSpan< int > build_edge_to_face_map(OffsetIndices< int > faces, Span< int > corner_edges, int edges_num, Array< int > &r_offsets, Array< int > &r_indices)
float3 face_center_calc(Span< float3 > vert_positions, Span< int > face_verts)
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:3052
IndexMask all_leaf_nodes(const Tree &pbvh, IndexMaskMemory &memory)
Definition pbvh.cc:2628
Span< int > node_face_indices_calc_grids(const SubdivCCG &subdiv_ccg, const GridsNode &node, Vector< int > &faces)
Definition pbvh.cc:1767
Span< float3 > vert_positions_eval(const Depsgraph &depsgraph, const Object &object_orig)
Definition pbvh.cc:1040
bool vert_is_boundary(const GroupedSpan< int > vert_to_face_map, const Span< bool > hide_poly, const BitSpan boundary, const int vert)
Definition sculpt.cc:483
void ensure_boundary_info(Object &object)
Definition sculpt.cc:6073
static void face_hide_update(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const FunctionRef< void(Span< int >, MutableSpan< bool >)> calc_hide)
static bool edit_is_operation_valid(const Object &object, const EditMode mode, const bool modify_hidden)
void filter_verts_with_unique_face_sets_mesh(const GroupedSpan< int > vert_to_face_map, const Span< int > face_sets, const bool unique, const Span< int > verts, const MutableSpan< float > factors)
static void gesture_apply_for_symmetry_pass(bContext &, gesture::GestureData &gesture_data)
static wmOperatorStatus init_op_exec(bContext *C, wmOperator *op)
static wmOperatorStatus gesture_box_exec(bContext *C, wmOperator *op)
static void edit_fairing(const Depsgraph &depsgraph, const Sculpt &sd, Object &ob, const int active_face_set_id, const eMeshFairingDepth fair_order, const float strength)
static wmOperatorStatus gesture_line_exec(bContext *C, wmOperator *op)
static wmOperatorStatus change_visibility_invoke(bContext *C, wmOperator *op, const wmEvent *event)
bool vert_has_unique_face_set(const GroupedSpan< int > vert_to_face_map, const Span< int > face_sets, int vert)
Definition sculpt.cc:292
bool vert_has_face_set(const GroupedSpan< int > vert_to_face_map, const Span< int > face_sets, const int vert, const int face_set)
Definition sculpt.cc:252
void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
void initialize_none_to_id(Mesh *mesh, int new_id)
static bool edit_op_init(bContext *C, wmOperator *op)
static wmOperatorStatus randomize_colors_exec(bContext *C, wmOperator *)
void SCULPT_OT_face_set_line_gesture(wmOperatorType *ot)
void SCULPT_OT_face_set_polyline_gesture(wmOperatorType *ot)
static void clear_face_sets(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
static void delete_geometry(Object &ob, const int active_face_set_id, const bool modify_hidden)
static wmOperatorStatus change_visibility_exec(bContext *C, wmOperator *op)
void SCULPT_OT_face_sets_init(wmOperatorType *ot)
int active_update_and_get(bContext *C, Object &ob, const float mval_fl[2])
static wmOperatorStatus gesture_lasso_exec(bContext *C, wmOperator *op)
static void edit_grow_shrink(const Depsgraph &depsgraph, const Scene &scene, Object &object, const EditMode mode, const int active_face_set_id, const bool modify_hidden, wmOperator *op)
static wmOperatorStatus edit_op_exec(bContext *C, wmOperator *op)
void filter_verts_with_unique_face_sets_bmesh(int face_set_offset, const bool unique, const Set< BMVert *, 0 > &verts, const MutableSpan< float > factors)
static void show_all(Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
Array< int > duplicate_face_sets(const Mesh &mesh)
static wmOperatorStatus gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
FunctionRef< bool(int from_face, int edge, int to_face)> FaceSetsFloodFillFn
void SCULPT_OT_face_sets_create(wmOperatorType *ot)
Set< int > gather_hidden_face_sets(const Span< bool > hide_poly, const Span< int > face_sets)
static void gesture_apply_mesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
void SCULPT_OT_face_set_change_visibility(wmOperatorType *ot)
void filter_verts_with_unique_face_sets_grids(const OffsetIndices< int > faces, const Span< int > corner_verts, const GroupedSpan< int > vert_to_face_map, const Span< int > face_sets, const SubdivCCG &subdiv_ccg, const bool unique, const Span< int > grids, const MutableSpan< float > factors)
void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
static bool check_single_face_set(const Object &object, const bool check_visible_only)
bke::SpanAttributeWriter< int > ensure_face_sets_mesh(Mesh &mesh)
static void face_sets_update(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const FunctionRef< void(Span< int >, MutableSpan< int >)> calc_face_sets)
static wmOperatorStatus gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static wmOperatorStatus gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_face_sets_edit(wmOperatorType *ot)
static void gesture_end(bContext &, gesture::GestureData &gesture_data)
static void gesture_apply_bmesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
static wmOperatorStatus create_op_exec(bContext *C, wmOperator *op)
static void init_operation(gesture::GestureData &gesture_data, wmOperator &)
static wmOperatorStatus edit_op_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
static void edit_modify_coordinates(bContext *C, Object &ob, const int active_face_set, const EditMode mode, wmOperator *op)
static wmOperatorStatus gesture_polyline_exec(bContext *C, wmOperator *op)
static void init_flood_fill(Object &ob, const FaceSetsFloodFillFn &test_fn)
static wmOperatorStatus gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int active_face_set_get(const Object &object)
Definition sculpt.cc:196
static void edit_modify_geometry(bContext *C, Object &ob, const int active_face_set, const bool modify_hidden, wmOperator *op)
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)
void tag_update_visibility(const bContext &C)
void grids_show_all(Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
void mesh_show_all(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
void sync_all_from_faces(Object &object)
Definition paint_hide.cc:55
void invalidate(SculptSession &ss)
Definition sculpt.cc:6199
void push_nodes(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask, const Type type)
void geometry_begin(const Scene &scene, Object &ob, const wmOperator *op)
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 vert_random_access_ensure(Object &object)
Definition sculpt.cc:141
void scale_translations(MutableSpan< float3 > translations, Span< float > factors)
Definition sculpt.cc:7495
void clip_and_lock_translations(const Sculpt &sd, const SculptSession &ss, Span< float3 > positions, Span< int > verts, MutableSpan< float3 > translations)
Definition sculpt.cc:7335
bool cursor_geometry_info_update(bContext *C, CursorGeometryInfo *out, const float2 &mval, const bool use_sampled_normal)
Definition sculpt.cc:4685
void flush_update_done(const bContext *C, Object &ob, const UpdateType update_type)
Definition sculpt.cc:5146
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6435
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6395
void flush_update_step(const bContext *C, const UpdateType update_type)
Definition sculpt.cc:5098
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
Value parallel_reduce(IndexRange range, int64_t grain_size, const Value &identity, const Function &function, const Reduction &reduction)
Definition BLI_task.hh:151
VecBase< float, 3 > float3
void RNA_int_set(PointerRNA *ptr, const char *name, int value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_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)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_translation_context(PropertyRNA *prop, const char *context)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
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
BMHeader head
void * data
int grid_size
Definition BKE_ccg.hh:33
int grid_area
Definition BKE_ccg.hh:35
struct SculptSession * sculpt
SculptVertexInfo vertex_info
Definition BKE_paint.hh:462
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:395
ActiveVert active_vert() const
Definition paint.cc:2367
blender::Array< int > edge_to_face_offsets
Definition BKE_paint.hh:380
blender::float3 active_vert_position(const Depsgraph &depsgraph, const Object &object) const
Definition paint.cc:2403
blender::GroupedSpan< int > edge_to_face_map
Definition BKE_paint.hh:382
blender::Array< int > edge_to_face_indices
Definition BKE_paint.hh:381
blender::BitVector boundary
Definition BKE_paint.hh:334
blender::Array< float > masks
Object * obact
Definition ED_view3d.hh:75
Depsgraph * depsgraph
Definition ED_view3d.hh:72
blender::float3 average_stroke_accum
void(* end)(bContext &, GestureData &)
void(* begin)(bContext &, wmOperator &, GestureData &)
void(* apply_for_symmetry_pass)(bContext &, GestureData &)
int mval[2]
Definition WM_types.hh:763
struct PointerRNA * ptr
i
Definition text_draw.cc:230
max
Definition text_draw.cc:251
@ WM_CURSOR_EDIT
Definition wm_cursors.hh:19
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
PointerRNA * ptr
Definition wm_files.cc:4238
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)