Blender V4.3
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.h"
28#include "BLI_task.hh"
29#include "BLI_vector.hh"
30
32#include "DNA_object_types.h"
33#include "DNA_scene_types.h"
34
35#include "BKE_attribute.hh"
36#include "BKE_ccg.hh"
37#include "BKE_context.hh"
38#include "BKE_customdata.hh"
39#include "BKE_layer.hh"
40#include "BKE_mesh.hh"
41#include "BKE_mesh_fair.hh"
42#include "BKE_mesh_mapping.hh"
43#include "BKE_object.hh"
44#include "BKE_paint.hh"
45#include "BKE_pbvh_api.hh"
46#include "BKE_subdiv_ccg.hh"
47
48#include "DEG_depsgraph.hh"
49
50#include "WM_api.hh"
51#include "WM_types.hh"
52
53#include "ED_sculpt.hh"
54
55#include "mesh_brush_common.hh"
56#include "paint_hide.hh"
57#include "sculpt_automask.hh"
58#include "sculpt_boundary.hh"
59#include "sculpt_gesture.hh"
60#include "sculpt_intern.hh"
61#include "sculpt_islands.hh"
62#include "sculpt_undo.hh"
63
64#include "RNA_access.hh"
65#include "RNA_define.hh"
66
67#include "bmesh.hh"
68
70
71/* -------------------------------------------------------------------- */
76{
77 SculptSession &ss = *object.sculpt;
78 switch (bke::object::pbvh_get(object)->type()) {
81 Mesh &mesh = *static_cast<Mesh *>(object.data);
82 const bke::AttributeAccessor attributes = mesh.attributes();
83 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
85 const int max = threading::parallel_reduce(
86 face_sets.index_range(),
87 4096,
88 1,
89 [&](const IndexRange range, int max) {
90 for (const int id : face_sets.slice(range)) {
91 max = std::max(max, id);
92 }
93 return max;
94 },
95 [](const int a, const int b) { return std::max(a, b); });
96 return max + 1;
97 }
99 BMesh &bm = *ss.bm;
100 const int cd_offset = CustomData_get_offset_named(
101 &bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
102 if (cd_offset == -1) {
103 return 1;
104 }
105 int next_face_set = 1;
106 BMIter iter;
107 BMFace *f;
108 BM_ITER_MESH (f, &iter, &bm, BM_FACES_OF_MESH) {
109 const int fset = *static_cast<const int *>(POINTER_OFFSET(f->head.data, cd_offset));
110 next_face_set = std::max(next_face_set, fset);
111 }
112
113 return next_face_set + 1;
114 }
115 }
117 return 0;
118}
119
120void initialize_none_to_id(Mesh *mesh, const int new_id)
121{
122 bke::MutableAttributeAccessor attributes = mesh->attributes_for_write();
123 bke::SpanAttributeWriter<int> face_sets = attributes.lookup_for_write_span<int>(
124 ".sculpt_face_set");
125 if (!face_sets) {
126 return;
127 }
128
129 for (const int i : face_sets.span.index_range()) {
130 if (face_sets.span[i] == SCULPT_FACE_SET_NONE) {
131 face_sets.span[i] = new_id;
132 }
133 }
134 face_sets.finish();
135}
136
137int active_update_and_get(bContext *C, Object &ob, const float mval[2])
138{
139 if (!ob.sculpt) {
141 }
142
144 if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false)) {
146 }
147
148 return active_face_set_get(ob);
149}
150
152{
153 Mesh &mesh = *static_cast<Mesh *>(object.data);
154 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
155 if (attributes.contains(".sculpt_face_set")) {
156 return false;
157 }
158 attributes.add<int>(".sculpt_face_set",
159 bke::AttrDomain::Face,
161 mesh.face_sets_color_default = 1;
162 return true;
163}
164
166{
167 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
168 if (!attributes.contains(".sculpt_face_set")) {
169 attributes.add<int>(".sculpt_face_set",
170 bke::AttrDomain::Face,
172 mesh.face_sets_color_default = 1;
173 }
174 return attributes.lookup_or_add_for_write_span<int>(".sculpt_face_set", bke::AttrDomain::Face);
175}
176
178{
179 Mesh &mesh = *static_cast<Mesh *>(object.data);
180 SculptSession &ss = *object.sculpt;
181 BMesh &bm = *ss.bm;
182 if (!CustomData_has_layer_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set")) {
183 BM_data_layer_add_named(&bm, &bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
184 const int offset = CustomData_get_offset_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
185 if (offset == -1) {
186 return -1;
187 }
188 BMIter iter;
189 BMFace *face;
190 BM_ITER_MESH (face, &iter, &bm, BM_FACES_OF_MESH) {
191 BM_ELEM_CD_SET_INT(face, offset, 1);
192 }
193 mesh.face_sets_color_default = 1;
194 return offset;
195 }
196 return CustomData_get_offset_named(&bm.pdata, CD_PROP_INT32, ".sculpt_face_set");
197}
198
200{
201 const bke::AttributeAccessor attributes = mesh.attributes();
202 const VArray<int> attribute = *attributes.lookup_or_default(
203 ".sculpt_face_set", bke::AttrDomain::Face, 0);
204 Array<int> face_sets(attribute.size());
205 array_utils::copy(attribute, face_sets.as_mutable_span());
206 return face_sets;
207}
208
210 const Span<int> face_sets,
211 const bool unique,
212 const Span<int> verts,
213 const MutableSpan<float> factors)
214{
215 BLI_assert(verts.size() == factors.size());
216
217 for (const int i : verts.index_range()) {
218 if (unique == face_set::vert_has_unique_face_set(vert_to_face_map, face_sets, verts[i])) {
219 factors[i] = 0.0f;
220 }
221 }
222}
223
225 const Span<int> corner_verts,
226 const GroupedSpan<int> vert_to_face_map,
227 const Span<int> face_sets,
228 const SubdivCCG &subdiv_ccg,
229 const bool unique,
230 const Span<int> grids,
231 const MutableSpan<float> factors)
232{
233 const CCGKey key = BKE_subdiv_ccg_key_top_level(subdiv_ccg);
234 BLI_assert(grids.size() * key.grid_area == factors.size());
235
236 for (const int i : grids.index_range()) {
237 const int node_start = i * key.grid_area;
238 for (const int y : IndexRange(key.grid_size)) {
239 for (const int x : IndexRange(key.grid_size)) {
240 const int offset = CCG_grid_xy_to_index(key.grid_size, x, y);
241 const int node_vert = node_start + offset;
242 if (factors[node_vert] == 0.0f) {
243 continue;
244 }
245
246 SubdivCCGCoord coord{};
247 coord.grid_index = grids[i];
248 coord.x = x;
249 coord.y = y;
250 if (unique == face_set::vert_has_unique_face_set(
251 faces, corner_verts, vert_to_face_map, face_sets, subdiv_ccg, coord))
252 {
253 factors[node_vert] = 0.0f;
254 }
255 }
256 }
257 }
258}
259
261 const bool unique,
262 const Set<BMVert *, 0> &verts,
263 const MutableSpan<float> factors)
264{
265 BLI_assert(verts.size() == factors.size());
266
267 int i = 0;
268 for (const BMVert *vert : verts) {
269 if (unique == face_set::vert_has_unique_face_set(face_set_offset, *vert)) {
270 factors[i] = 0.0f;
271 }
272 i++;
273 }
274}
275
278/* -------------------------------------------------------------------- */
283static void face_sets_update(const Depsgraph &depsgraph,
284 Object &object,
285 const IndexMask &node_mask,
286 const FunctionRef<void(Span<int>, MutableSpan<int>)> calc_face_sets)
287{
288 SculptSession &ss = *object.sculpt;
289 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
290
292 *static_cast<Mesh *>(object.data));
293
294 struct TLS {
295 Vector<int> face_indices;
296 Vector<int> new_face_sets;
297 };
298
299 Array<bool> node_changed(pbvh.nodes_num(), false);
300
302 if (pbvh.type() == bke::pbvh::Type::Mesh) {
304 node_mask.foreach_index(GrainSize(1), [&](const int i) {
305 TLS &tls = all_tls.local();
306 const Span<int> faces = nodes[i].faces();
307
308 tls.new_face_sets.resize(faces.size());
309 MutableSpan<int> new_face_sets = tls.new_face_sets;
310 gather_data_mesh(face_sets.span.as_span(), faces, new_face_sets);
311 calc_face_sets(faces, new_face_sets);
312 if (array_utils::indexed_data_equal<int>(face_sets.span, faces, new_face_sets)) {
313 return;
314 }
315
316 undo::push_node(depsgraph, object, &nodes[i], undo::Type::FaceSet);
317 scatter_data_mesh(new_face_sets.as_span(), faces, face_sets.span);
318 node_changed[i] = true;
319 });
320 }
321 else if (pbvh.type() == bke::pbvh::Type::Grids) {
323 node_mask.foreach_index(GrainSize(1), [&](const int i) {
324 TLS &tls = all_tls.local();
325 const Span<int> faces = bke::pbvh::node_face_indices_calc_grids(
326 *ss.subdiv_ccg, nodes[i], tls.face_indices);
327
328 tls.new_face_sets.resize(faces.size());
329 MutableSpan<int> new_face_sets = tls.new_face_sets;
330 gather_data_mesh(face_sets.span.as_span(), faces, new_face_sets);
331 calc_face_sets(faces, new_face_sets);
332 if (array_utils::indexed_data_equal<int>(face_sets.span, faces, new_face_sets)) {
333 return;
334 }
335
336 undo::push_node(depsgraph, object, &nodes[i], undo::Type::FaceSet);
337 scatter_data_mesh(new_face_sets.as_span(), faces, face_sets.span);
338 node_changed[i] = true;
339 });
340 }
341
342 IndexMaskMemory memory;
343 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
344 face_sets.finish();
345}
346
347enum class CreateMode {
348 Masked = 0,
349 Visible = 1,
350 All = 2,
351 Selection = 3,
352};
353
354static void clear_face_sets(const Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
355{
356 Mesh &mesh = *static_cast<Mesh *>(object.data);
357 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
358 if (!attributes.contains(".sculpt_face_set")) {
359 return;
360 }
361 SculptSession &ss = *object.sculpt;
362 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
363
364 Array<bool> node_changed(pbvh.nodes_num(), false);
365
366 const int default_face_set = mesh.face_sets_color_default;
367 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
368 if (pbvh.type() == bke::pbvh::Type::Mesh) {
370 node_mask.foreach_index(GrainSize(1), [&](const int i) {
371 const Span<int> faces = nodes[i].faces();
372 if (std::any_of(faces.begin(), faces.end(), [&](const int face) {
373 return face_sets[face] != default_face_set;
374 }))
375 {
376 undo::push_node(depsgraph, object, &nodes[i], undo::Type::FaceSet);
377 node_changed[i] = true;
378 }
379 });
380 }
381 else if (pbvh.type() == bke::pbvh::Type::Grids) {
384 node_mask.foreach_index(GrainSize(1), [&](const int i) {
385 Vector<int> &face_indices = all_face_indices.local();
386 const Span<int> faces = bke::pbvh::node_face_indices_calc_grids(
387 *ss.subdiv_ccg, nodes[i], face_indices);
388 if (std::any_of(faces.begin(), faces.end(), [&](const int face) {
389 return face_sets[face] != default_face_set;
390 }))
391 {
392 undo::push_node(depsgraph, object, &nodes[i], undo::Type::FaceSet);
393 node_changed[i] = true;
394 }
395 });
396 }
397 IndexMaskMemory memory;
398 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
399 attributes.remove(".sculpt_face_set");
400}
401
403{
404 const Scene &scene = *CTX_data_scene(C);
405 Object &object = *CTX_data_active_object(C);
406 Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
407
408 const CreateMode mode = CreateMode(RNA_enum_get(op->ptr, "mode"));
409
410 const View3D *v3d = CTX_wm_view3d(C);
411 const Base *base = CTX_data_active_base(C);
412 if (!BKE_base_is_visible(v3d, base)) {
413 return OPERATOR_CANCELLED;
414 }
415
416 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
417 if (pbvh.type() == bke::pbvh::Type::BMesh) {
418 /* Dyntopo not supported. */
419 return OPERATOR_CANCELLED;
420 }
421
422 Mesh &mesh = *static_cast<Mesh *>(object.data);
423 const bke::AttributeAccessor attributes = mesh.attributes();
424
426
427 undo::push_begin(scene, object, op);
428
429 const int next_face_set = find_next_available_id(object);
430
431 IndexMaskMemory memory;
432 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
433 switch (mode) {
434 case CreateMode::Masked: {
435 if (pbvh.type() == bke::pbvh::Type::Mesh) {
436 const OffsetIndices faces = mesh.faces();
437 const Span<int> corner_verts = mesh.corner_verts();
438 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
439 bke::AttrDomain::Face);
440 const VArraySpan<float> mask = *attributes.lookup<float>(".sculpt_mask",
441 bke::AttrDomain::Point);
442 if (!mask.is_empty()) {
444 object,
445 node_mask,
446 [&](const Span<int> indices, MutableSpan<int> face_sets) {
447 for (const int i : indices.index_range()) {
448 if (!hide_poly.is_empty() && hide_poly[indices[i]]) {
449 continue;
450 }
451 const Span<int> face_verts = corner_verts.slice(faces[indices[i]]);
452 if (!std::any_of(face_verts.begin(),
453 face_verts.end(),
454 [&](const int vert) { return mask[vert] > 0.5f; }))
455 {
456 continue;
457 }
458 face_sets[i] = next_face_set;
459 }
460 });
461 }
462 }
463 else if (pbvh.type() == bke::pbvh::Type::Grids) {
464 const OffsetIndices<int> faces = mesh.faces();
465 const SculptSession &ss = *object.sculpt;
466 const SubdivCCG &subdiv_ccg = *ss.subdiv_ccg;
467 const int grid_area = subdiv_ccg.grid_area;
468 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
469 bke::AttrDomain::Face);
470 const Span<float> masks = subdiv_ccg.masks;
471 if (!masks.is_empty()) {
473 object,
474 node_mask,
475 [&](const Span<int> indices, MutableSpan<int> face_sets) {
476 for (const int i : indices.index_range()) {
477 if (!hide_poly.is_empty() && hide_poly[indices[i]]) {
478 continue;
479 }
480
481 const Span<float> face_masks = masks.slice(
482 bke::ccg::face_range(faces, grid_area, indices[i]));
483 if (!std::any_of(face_masks.begin(),
484 face_masks.end(),
485 [&](const float mask) { return mask > 0.5f; }))
486 {
487 continue;
488 }
489 face_sets[i] = next_face_set;
490 }
491 });
492 }
493 }
494 break;
495 }
496 case CreateMode::Visible: {
497 const VArray<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
498 switch (array_utils::booleans_mix_calc(hide_poly)) {
499 case array_utils::BooleanMix::None:
500 case array_utils::BooleanMix::AllTrue:
501 case array_utils::BooleanMix::AllFalse:
502 /* If all vertices in the sculpt are visible, remove face sets and update the default
503 * color. This way the new face set will be white, and it is a quick way of disabling all
504 * face sets and the performance hit of rendering the overlay. */
505 clear_face_sets(depsgraph, object, node_mask);
506 break;
507 case array_utils::BooleanMix::Mixed:
508 const VArraySpan<bool> hide_poly_span(hide_poly);
510 object,
511 node_mask,
512 [&](const Span<int> indices, MutableSpan<int> face_sets) {
513 for (const int i : indices.index_range()) {
514 if (!hide_poly_span[indices[i]]) {
515 face_sets[i] = next_face_set;
516 }
517 }
518 });
519 break;
520 }
521 break;
522 }
523 case CreateMode::All: {
525 object,
526 node_mask,
527 [&](const Span<int> /*indices*/, MutableSpan<int> face_sets) {
528 face_sets.fill(next_face_set);
529 });
530 break;
531 }
532 case CreateMode::Selection: {
533 const VArraySpan<bool> select_poly = *attributes.lookup_or_default<bool>(
534 ".select_poly", bke::AttrDomain::Face, false);
535 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
536 bke::AttrDomain::Face);
537
539 depsgraph, object, node_mask, [&](const Span<int> indices, MutableSpan<int> face_sets) {
540 for (const int i : indices.index_range()) {
541 if (select_poly[indices[i]]) {
542 if (!hide_poly.is_empty() && hide_poly[i]) {
543 continue;
544 }
545 face_sets[i] = next_face_set;
546 }
547 }
548 });
549
550 break;
551 }
552 }
553
554 undo::push_end(object);
555
557
558 return OPERATOR_FINISHED;
559}
560
562{
563 ot->name = "Create Face Set";
564 ot->idname = "SCULPT_OT_face_sets_create";
565 ot->description = "Create a new Face Set";
566
569
571
572 static EnumPropertyItem modes[] = {
573 {int(CreateMode::Masked),
574 "MASKED",
575 0,
576 "Face Set from Masked",
577 "Create a new Face Set from the masked faces"},
578 {int(CreateMode::Visible),
579 "VISIBLE",
580 0,
581 "Face Set from Visible",
582 "Create a new Face Set from the visible vertices"},
583 {int(CreateMode::All),
584 "ALL",
585 0,
586 "Face Set Full Mesh",
587 "Create an unique Face Set with all faces in the sculpt"},
588 {int(CreateMode::Selection),
589 "SELECTION",
590 0,
591 "Face Set from Edit Mode Selection",
592 "Create an Face Set corresponding to the Edit Mode face selection"},
593 {0, nullptr, 0, nullptr, nullptr},
594 };
595 RNA_def_enum(ot->srna, "mode", modes, int(CreateMode::Masked), "Mode", "");
596}
597
598enum class InitMode {
599 LooseParts = 0,
600 Materials = 1,
601 Normals = 2,
602 UVSeams = 3,
603 Creases = 4,
604 SharpEdges = 5,
605 BevelWeight = 6,
607};
608
609using FaceSetsFloodFillFn = FunctionRef<bool(int from_face, int edge, int to_face)>;
610
611static void init_flood_fill(Object &ob, const FaceSetsFloodFillFn &test_fn)
612{
613 SculptSession &ss = *ob.sculpt;
614 Mesh *mesh = static_cast<Mesh *>(ob.data);
615
616 BitVector<> visited_faces(mesh->faces_num, false);
617
619
620 const Span<int2> edges = mesh->edges();
621 const OffsetIndices faces = mesh->faces();
622 const Span<int> corner_edges = mesh->corner_edges();
623
624 if (ss.edge_to_face_map.is_empty()) {
625 ss.edge_to_face_map = bke::mesh::build_edge_to_face_map(
626 faces, corner_edges, edges.size(), ss.edge_to_face_offsets, ss.edge_to_face_indices);
627 }
628
629 const bke::AttributeAccessor attributes = mesh->attributes();
630 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
631 const Set<int> hidden_face_sets = gather_hidden_face_sets(hide_poly, face_sets.span);
632
633 int next_face_set = 1;
634
635 for (const int i : faces.index_range()) {
636 if (!hide_poly.is_empty() && hide_poly[i]) {
637 continue;
638 }
639 if (visited_faces[i]) {
640 continue;
641 }
642 std::queue<int> queue;
643
644 while (hidden_face_sets.contains(next_face_set)) {
645 next_face_set += 1;
646 }
647 face_sets.span[i] = next_face_set;
648 visited_faces[i].set(true);
649 queue.push(i);
650
651 while (!queue.empty()) {
652 const int face_i = queue.front();
653 queue.pop();
654
655 for (const int edge_i : corner_edges.slice(faces[face_i])) {
656 for (const int neighbor_i : ss.edge_to_face_map[edge_i]) {
657 if (neighbor_i == face_i) {
658 continue;
659 }
660 if (visited_faces[neighbor_i]) {
661 continue;
662 }
663 if (!hide_poly.is_empty() && hide_poly[neighbor_i]) {
664 continue;
665 }
666 if (!test_fn(face_i, edge_i, neighbor_i)) {
667 continue;
668 }
669
670 face_sets.span[neighbor_i] = next_face_set;
671 visited_faces[neighbor_i].set(true);
672 queue.push(neighbor_i);
673 }
674 }
675 }
676
677 next_face_set += 1;
678 }
679
680 face_sets.finish();
681}
682
683Set<int> gather_hidden_face_sets(const Span<bool> hide_poly, const Span<int> face_sets)
684{
685 if (hide_poly.is_empty()) {
686 return {};
687 }
688
689 Set<int> hidden_face_sets;
690 for (const int i : hide_poly.index_range()) {
691 if (hide_poly[i]) {
692 hidden_face_sets.add(face_sets[i]);
693 }
694 }
695
696 return hidden_face_sets;
697}
698
700{
701 const Scene &scene = *CTX_data_scene(C);
704
705 const InitMode mode = InitMode(RNA_enum_get(op->ptr, "mode"));
706
707 const View3D *v3d = CTX_wm_view3d(C);
708 const Base *base = CTX_data_active_base(C);
709 if (!BKE_base_is_visible(v3d, base)) {
710 return OPERATOR_CANCELLED;
711 }
712
714
715 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
716 /* Dyntopo not supported. */
717 if (pbvh.type() == bke::pbvh::Type::BMesh) {
718 return OPERATOR_CANCELLED;
719 }
720
721 IndexMaskMemory memory;
722 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
723 if (node_mask.is_empty()) {
724 return OPERATOR_CANCELLED;
725 }
726
727 undo::push_begin(scene, ob, op);
728 undo::push_nodes(*depsgraph, ob, node_mask, undo::Type::FaceSet);
729
730 const float threshold = RNA_float_get(op->ptr, "threshold");
731
732 Mesh *mesh = static_cast<Mesh *>(ob.data);
733 const bke::AttributeAccessor attributes = mesh->attributes();
734
735 switch (mode) {
736 case InitMode::LooseParts: {
737 const VArray<bool> hide_poly = *attributes.lookup_or_default<bool>(
738 ".hide_poly", bke::AttrDomain::Face, false);
739 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) {
740 return hide_poly[from_face] == hide_poly[to_face];
741 });
742 break;
743 }
744 case InitMode::Materials: {
746 const VArraySpan<int> material_indices = *attributes.lookup_or_default<int>(
747 "material_index", bke::AttrDomain::Face, 0);
748 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly",
749 bke::AttrDomain::Face);
750 for (const int i : IndexRange(mesh->faces_num)) {
751 if (!hide_poly.is_empty() && hide_poly[i]) {
752 continue;
753 }
754
755 /* In some cases material face set index could be same as hidden face set index
756 * A more robust implementation is needed to avoid this */
757 face_sets.span[i] = material_indices[i] + 1;
758 }
759
760 face_sets.finish();
761 break;
762 }
763 case InitMode::Normals: {
764 const Span<float3> face_normals = mesh->face_normals();
765 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
766 return std::abs(math::dot(face_normals[from_face], face_normals[to_face])) > threshold;
767 });
768 break;
769 }
770 case InitMode::UVSeams: {
771 const VArraySpan<bool> uv_seams = *mesh->attributes().lookup_or_default<bool>(
772 ".uv_seam", bke::AttrDomain::Edge, false);
774 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
775 return !uv_seams[edge];
776 });
777 break;
778 }
779 case InitMode::Creases: {
780 const VArraySpan<float> creases = *attributes.lookup_or_default<float>(
781 "crease_edge", bke::AttrDomain::Edge, 0.0f);
783 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
784 return creases[edge] < threshold;
785 });
786 break;
787 }
788 case InitMode::SharpEdges: {
789 const VArraySpan<bool> sharp_edges = *mesh->attributes().lookup_or_default<bool>(
790 "sharp_edge", bke::AttrDomain::Edge, false);
792 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
793 return !sharp_edges[edge];
794 });
795 break;
796 }
797 case InitMode::BevelWeight: {
798 const VArraySpan<float> bevel_weights = *attributes.lookup_or_default<float>(
799 "bevel_weight_edge", bke::AttrDomain::Edge, 0.0f);
801 [&](const int /*from_face*/, const int edge, const int /*to_face*/) -> bool {
802 return bevel_weights[edge] < threshold;
803 });
804 break;
805 }
806 case InitMode::FaceSetBoundaries: {
807 Array<int> face_sets_copy = duplicate_face_sets(*mesh);
808 init_flood_fill(ob, [&](const int from_face, const int /*edge*/, const int to_face) -> bool {
809 return face_sets_copy[from_face] == face_sets_copy[to_face];
810 });
811 break;
812 }
813 }
814
815 undo::push_end(ob);
816
817 pbvh.tag_face_sets_changed(node_mask);
818
820
821 return OPERATOR_FINISHED;
822}
823
825{
826 ot->name = "Init Face Sets";
827 ot->idname = "SCULPT_OT_face_sets_init";
828 ot->description = "Initializes all Face Sets in the mesh";
829
832
834
835 static EnumPropertyItem modes[] = {
836 {int(InitMode::LooseParts),
837 "LOOSE_PARTS",
838 0,
839 "Face Sets from Loose Parts",
840 "Create a Face Set per loose part in the mesh"},
841 {int(InitMode::Materials),
842 "MATERIALS",
843 0,
844 "Face Sets from Material Slots",
845 "Create a Face Set per Material Slot"},
846 {int(InitMode::Normals),
847 "NORMALS",
848 0,
849 "Face Sets from Mesh Normals",
850 "Create Face Sets for Faces that have similar normal"},
851 {int(InitMode::UVSeams),
852 "UV_SEAMS",
853 0,
854 "Face Sets from UV Seams",
855 "Create Face Sets using UV Seams as boundaries"},
856 {int(InitMode::Creases),
857 "CREASES",
858 0,
859 "Face Sets from Edge Creases",
860 "Create Face Sets using Edge Creases as boundaries"},
861 {int(InitMode::BevelWeight),
862 "BEVEL_WEIGHT",
863 0,
864 "Face Sets from Bevel Weight",
865 "Create Face Sets using Bevel Weights as boundaries"},
866 {int(InitMode::SharpEdges),
867 "SHARP_EDGES",
868 0,
869 "Face Sets from Sharp Edges",
870 "Create Face Sets using Sharp Edges as boundaries"},
871 {int(InitMode::FaceSetBoundaries),
872 "FACE_SET_BOUNDARIES",
873 0,
874 "Face Sets from Face Set Boundaries",
875 "Create a Face Set per isolated Face Set"},
876 {0, nullptr, 0, nullptr, nullptr},
877 };
878 RNA_def_enum(ot->srna, "mode", modes, int(InitMode::LooseParts), "Mode", "");
880 ot->srna,
881 "threshold",
882 0.5f,
883 0.0f,
884 1.0f,
885 "Threshold",
886 "Minimum value to consider a certain attribute a boundary when creating the Face Sets",
887 0.0f,
888 1.0f);
889}
890
891enum class VisibilityMode {
892 Toggle = 0,
893 ShowActive = 1,
894 HideActive = 2,
895};
896
897static void face_hide_update(const Depsgraph &depsgraph,
898 Object &object,
899 const IndexMask &node_mask,
900 const FunctionRef<void(Span<int>, MutableSpan<bool>)> calc_hide)
901{
902 SculptSession &ss = *object.sculpt;
903 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
904 Mesh &mesh = *static_cast<Mesh *>(object.data);
905 bke::MutableAttributeAccessor attributes = mesh.attributes_for_write();
906 bke::SpanAttributeWriter<bool> hide_poly = attributes.lookup_or_add_for_write_span<bool>(
907 ".hide_poly", bke::AttrDomain::Face);
908
909 struct TLS {
910 Vector<int> face_indices;
911 Vector<bool> new_hide;
912 };
913
914 Array<bool> node_changed(node_mask.min_array_size(), false);
915
917 if (pbvh.type() == bke::pbvh::Type::Mesh) {
919 node_mask.foreach_index(GrainSize(1), [&](const int i) {
920 TLS &tls = all_tls.local();
921 const Span<int> faces = nodes[i].faces();
922
923 tls.new_hide.resize(faces.size());
924 MutableSpan<bool> new_hide = tls.new_hide;
925 gather_data_mesh(hide_poly.span.as_span(), faces, new_hide);
926 calc_hide(faces, new_hide);
927 if (array_utils::indexed_data_equal<bool>(hide_poly.span, faces, new_hide)) {
928 return;
929 }
930
931 undo::push_node(depsgraph, object, &nodes[i], undo::Type::HideFace);
932 scatter_data_mesh(new_hide.as_span(), faces, hide_poly.span);
933 node_changed[i] = true;
934 });
935 }
936 else if (pbvh.type() == bke::pbvh::Type::Grids) {
938 node_mask.foreach_index(GrainSize(1), [&](const int i) {
939 TLS &tls = all_tls.local();
940 const Span<int> faces = bke::pbvh::node_face_indices_calc_grids(
941 *ss.subdiv_ccg, nodes[i], tls.face_indices);
942
943 tls.new_hide.resize(faces.size());
944 MutableSpan<bool> new_hide = tls.new_hide;
945 gather_data_mesh(hide_poly.span.as_span(), faces, new_hide);
946 calc_hide(faces, new_hide);
947 if (array_utils::indexed_data_equal<bool>(hide_poly.span, faces, new_hide)) {
948 return;
949 }
950
951 undo::push_node(depsgraph, object, &nodes[i], undo::Type::HideFace);
952 scatter_data_mesh(new_hide.as_span(), faces, hide_poly.span);
953 node_changed[i] = true;
954 });
955 }
956
957 hide_poly.finish();
958
959 IndexMaskMemory memory;
960 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
961 if (changed_nodes.is_empty()) {
962 return;
963 }
964 hide::sync_all_from_faces(object);
965 pbvh.tag_visibility_changed(node_mask);
966}
967
968static void show_all(Depsgraph &depsgraph, Object &object, const IndexMask &node_mask)
969{
970 switch (bke::object::pbvh_get(object)->type()) {
971 case bke::pbvh::Type::Mesh:
972 hide::mesh_show_all(depsgraph, object, node_mask);
973 break;
974 case bke::pbvh::Type::Grids:
975 hide::grids_show_all(depsgraph, object, node_mask);
976 break;
977 case bke::pbvh::Type::BMesh:
979 break;
980 }
981}
982
984{
985 const Scene &scene = *CTX_data_scene(C);
986 Object &object = *CTX_data_active_object(C);
987 SculptSession &ss = *object.sculpt;
988 Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
989
990 Mesh *mesh = BKE_object_get_original_mesh(&object);
992
993 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
994
995 if (pbvh.type() == bke::pbvh::Type::BMesh) {
996 /* Not supported for dyntopo. There is no active face. */
997 return OPERATOR_CANCELLED;
998 }
999
1000 const VisibilityMode mode = VisibilityMode(RNA_enum_get(op->ptr, "mode"));
1001 const int active_face_set = active_face_set_get(object);
1002
1003 undo::push_begin(scene, object, op);
1004
1005 IndexMaskMemory memory;
1006 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1007
1008 const bke::AttributeAccessor attributes = mesh->attributes();
1009 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1010 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1011 bke::AttrDomain::Face);
1012
1013 switch (mode) {
1014 case VisibilityMode::Toggle: {
1015 if (hide_poly.contains(true) || face_sets.is_empty()) {
1016 show_all(depsgraph, object, node_mask);
1017 }
1018 else {
1020 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1021 for (const int i : hide.index_range()) {
1022 hide[i] = face_sets[faces[i]] != active_face_set;
1023 }
1024 });
1025 }
1026 break;
1027 }
1028 case VisibilityMode::ShowActive:
1029 if (face_sets.is_empty()) {
1030 show_all(depsgraph, object, node_mask);
1031 }
1032 else {
1034 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1035 for (const int i : hide.index_range()) {
1036 if (face_sets[faces[i]] == active_face_set) {
1037 hide[i] = false;
1038 }
1039 }
1040 });
1041 }
1042 break;
1043 case VisibilityMode::HideActive:
1044 if (face_sets.is_empty()) {
1046 depsgraph, object, node_mask, [&](const Span<int> /*faces*/, MutableSpan<bool> hide) {
1047 hide.fill(true);
1048 });
1049 }
1050 else {
1052 depsgraph, object, node_mask, [&](const Span<int> faces, MutableSpan<bool> hide) {
1053 for (const int i : hide.index_range()) {
1054 if (face_sets[faces[i]] == active_face_set) {
1055 hide[i] = true;
1056 }
1057 }
1058 });
1059 }
1060 break;
1061 }
1062
1063 /* For modes that use the cursor active vertex, update the rotation origin for viewport
1064 * navigation. */
1065 if (ELEM(mode, VisibilityMode::Toggle, VisibilityMode::ShowActive)) {
1067 if (std::holds_alternative<std::monostate>(ss.active_vert())) {
1068 ups->last_stroke_valid = false;
1069 }
1070 else {
1071 float location[3];
1072 copy_v3_v3(location, ss.active_vert_position(depsgraph, object));
1073 mul_m4_v3(object.object_to_world().ptr(), location);
1074 copy_v3_v3(ups->average_stroke_accum, location);
1075 ups->average_stroke_counter = 1;
1076 ups->last_stroke_valid = true;
1077 }
1078 }
1079
1080 undo::push_end(object);
1081
1082 bke::pbvh::update_visibility(object, pbvh);
1083
1084 islands::invalidate(*object.sculpt);
1085 hide::tag_update_visibility(*C);
1086
1087 return OPERATOR_FINISHED;
1088}
1089
1090static int change_visibility_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1091{
1093
1094 const View3D *v3d = CTX_wm_view3d(C);
1095 const Base *base = CTX_data_active_base(C);
1096 if (!BKE_base_is_visible(v3d, base)) {
1097 return OPERATOR_CANCELLED;
1098 }
1099
1100 /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
1101 * cursor updates. */
1103 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1105 SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false);
1106
1107 return change_visibility_exec(C, op);
1108}
1109
1111{
1112 ot->name = "Face Sets Visibility";
1113 ot->idname = "SCULPT_OT_face_set_change_visibility";
1114 ot->description = "Change the visibility of the Face Sets of the sculpt";
1115
1119
1121
1122 static EnumPropertyItem modes[] = {
1123 {int(VisibilityMode::Toggle),
1124 "TOGGLE",
1125 0,
1126 "Toggle Visibility",
1127 "Hide all Face Sets except for the active one"},
1128 {int(VisibilityMode::ShowActive),
1129 "SHOW_ACTIVE",
1130 0,
1131 "Show Active Face Set",
1132 "Show Active Face Set"},
1133 {int(VisibilityMode::HideActive),
1134 "HIDE_ACTIVE",
1135 0,
1136 "Hide Active Face Sets",
1137 "Hide Active Face Sets"},
1138 {0, nullptr, 0, nullptr, nullptr},
1139 };
1140 RNA_def_enum(ot->srna, "mode", modes, int(VisibilityMode::Toggle), "Mode", "");
1141}
1142
1144{
1146
1147 const View3D *v3d = CTX_wm_view3d(C);
1148 const Base *base = CTX_data_active_base(C);
1149 if (!BKE_base_is_visible(v3d, base)) {
1150 return OPERATOR_CANCELLED;
1151 }
1152
1153 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1154
1155 /* Dyntopo not supported. */
1156 if (pbvh.type() == bke::pbvh::Type::BMesh) {
1157 return OPERATOR_CANCELLED;
1158 }
1159
1160 Mesh *mesh = static_cast<Mesh *>(ob.data);
1161 const bke::AttributeAccessor attributes = mesh->attributes();
1162
1163 if (!attributes.contains(".sculpt_face_set")) {
1164 return OPERATOR_CANCELLED;
1165 }
1166
1167 const VArray<int> face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
1168 const int random_index = clamp_i(mesh->faces_num * BLI_hash_int_01(mesh->face_sets_color_seed),
1169 0,
1170 max_ii(0, mesh->faces_num - 1));
1171 mesh->face_sets_color_default = face_sets[random_index];
1172
1173 mesh->face_sets_color_seed += 1;
1174
1175 IndexMaskMemory memory;
1176 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1177 pbvh.tag_face_sets_changed(node_mask);
1178
1180
1181 return OPERATOR_FINISHED;
1182}
1183
1185{
1186 ot->name = "Randomize Face Sets Colors";
1187 ot->idname = "SCULPT_OT_face_sets_randomize_colors";
1188 ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
1189
1192
1194}
1195
1196enum class EditMode {
1197 Grow = 0,
1198 Shrink = 1,
1199 DeleteGeometry = 2,
1200 FairPositions = 3,
1201 FairTangency = 4,
1202};
1203
1204static void edit_grow_shrink(const Depsgraph &depsgraph,
1205 const Scene &scene,
1206 Object &object,
1207 const EditMode mode,
1208 const int active_face_set_id,
1209 const bool modify_hidden,
1210 wmOperator *op)
1211{
1212 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1213 Mesh &mesh = *static_cast<Mesh *>(object.data);
1214 const OffsetIndices faces = mesh.faces();
1215 const Span<int> corner_verts = mesh.corner_verts();
1216 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1217 const bke::AttributeAccessor attributes = mesh.attributes();
1218
1219 BLI_assert(attributes.contains(".sculpt_face_set"));
1220
1221 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1222 Array<int> prev_face_sets = duplicate_face_sets(mesh);
1223
1224 undo::push_begin(scene, object, op);
1225
1226 IndexMaskMemory memory;
1227 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1229 depsgraph, object, node_mask, [&](const Span<int> indices, MutableSpan<int> face_sets) {
1230 for (const int i : indices.index_range()) {
1231 const int face = indices[i];
1232 if (!modify_hidden && !hide_poly.is_empty() && hide_poly[face]) {
1233 continue;
1234 }
1235 if (mode == EditMode::Grow) {
1236 for (const int vert : corner_verts.slice(faces[face])) {
1237 for (const int neighbor_face_index : vert_to_face_map[vert]) {
1238 if (neighbor_face_index == face) {
1239 continue;
1240 }
1241 if (prev_face_sets[neighbor_face_index] == active_face_set_id) {
1242 face_sets[i] = active_face_set_id;
1243 }
1244 }
1245 }
1246 }
1247 else {
1248 if (prev_face_sets[face] == active_face_set_id) {
1249 for (const int vert_i : corner_verts.slice(faces[face])) {
1250 for (const int neighbor_face_index : vert_to_face_map[vert_i]) {
1251 if (neighbor_face_index == face) {
1252 continue;
1253 }
1254 if (prev_face_sets[neighbor_face_index] != active_face_set_id) {
1255 face_sets[i] = prev_face_sets[neighbor_face_index];
1256 }
1257 }
1258 }
1259 }
1260 }
1261 }
1262 });
1263
1264 undo::push_end(object);
1265}
1266
1267static bool check_single_face_set(const Object &object, const bool check_visible_only)
1268{
1269 const Mesh &mesh = *static_cast<const Mesh *>(object.data);
1270 const bke::AttributeAccessor attributes = mesh.attributes();
1271 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1272 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1273 bke::AttrDomain::Face);
1274
1275 if (face_sets.is_empty()) {
1276 return true;
1277 }
1278 int first_face_set = SCULPT_FACE_SET_NONE;
1279 if (check_visible_only) {
1280 for (const int i : face_sets.index_range()) {
1281 if (!hide_poly.is_empty() && hide_poly[i]) {
1282 continue;
1283 }
1284 first_face_set = face_sets[i];
1285 break;
1286 }
1287 }
1288 else {
1289 first_face_set = face_sets[0];
1290 }
1291
1292 if (first_face_set == SCULPT_FACE_SET_NONE) {
1293 return true;
1294 }
1295
1296 for (const int i : face_sets.index_range()) {
1297 if (check_visible_only && !hide_poly.is_empty() && hide_poly[i]) {
1298 continue;
1299 }
1300 if (face_sets[i] != first_face_set) {
1301 return false;
1302 }
1303 }
1304 return true;
1305}
1306
1307static void delete_geometry(Object &ob, const int active_face_set_id, const bool modify_hidden)
1308{
1309 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1310 const bke::AttributeAccessor attributes = mesh.attributes();
1311 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1312 const VArraySpan<int> face_sets = *attributes.lookup<int>(".sculpt_face_set",
1313 bke::AttrDomain::Face);
1314
1315 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(&mesh);
1316 BMeshCreateParams create_params{};
1317 create_params.use_toolflags = true;
1318 BMesh *bm = BM_mesh_create(&allocsize, &create_params);
1319
1320 BMeshFromMeshParams convert_params{};
1321 convert_params.calc_vert_normal = true;
1322 convert_params.calc_face_normal = true;
1323 BM_mesh_bm_from_me(bm, &mesh, &convert_params);
1324
1328 BMIter iter;
1329 BMFace *f;
1330 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
1331 const int face_index = BM_elem_index_get(f);
1332 if (!modify_hidden && !hide_poly.is_empty() && hide_poly[face_index]) {
1333 continue;
1334 }
1335 BM_elem_flag_set(f, BM_ELEM_TAG, face_sets[face_index] == active_face_set_id);
1336 }
1339
1340 BMeshToMeshParams bmesh_to_mesh_params{};
1341 bmesh_to_mesh_params.calc_object_remap = false;
1342 BM_mesh_bm_to_me(nullptr, bm, &mesh, &bmesh_to_mesh_params);
1343
1345}
1346
1347static void edit_fairing(const Depsgraph &depsgraph,
1348 const Sculpt &sd,
1349 Object &ob,
1350 const int active_face_set_id,
1351 const eMeshFairingDepth fair_order,
1352 const float strength)
1353{
1354 SculptSession &ss = *ob.sculpt;
1355 Mesh &mesh = *static_cast<Mesh *>(ob.data);
1356 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1357 boundary::ensure_boundary_info(ob);
1358
1359 const PositionDeformData position_data(depsgraph, ob);
1360 const Span<float3> positions = position_data.eval;
1361 const GroupedSpan<int> vert_to_face_map = mesh.vert_to_face_map();
1362 const BitSpan boundary_verts = ss.vertex_info.boundary;
1363 const bke::AttributeAccessor attributes = mesh.attributes();
1364 const VArraySpan hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1365 const VArraySpan face_sets = *attributes.lookup<int>(".sculpt_face_set", bke::AttrDomain::Face);
1366
1367 Array<bool> fair_verts(positions.size(), false);
1368 for (const int vert : positions.index_range()) {
1369 if (boundary::vert_is_boundary(vert_to_face_map, hide_poly, boundary_verts, vert)) {
1370 continue;
1371 }
1372 if (!vert_has_face_set(vert_to_face_map, face_sets, vert, active_face_set_id)) {
1373 continue;
1374 }
1375 if (!vert_has_unique_face_set(vert_to_face_map, face_sets, vert)) {
1376 continue;
1377 }
1378 fair_verts[vert] = true;
1379 }
1380
1381 Array<float3> new_positions = positions;
1382 BKE_mesh_prefair_and_fair_verts(&mesh, new_positions, fair_verts.data(), fair_order);
1383
1384 struct LocalData {
1385 Vector<float3> translations;
1386 };
1387
1388 IndexMaskMemory memory;
1389 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1391
1393 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1394 LocalData &tls = all_tls.local();
1395 const Span<int> verts = nodes[i].verts();
1396 tls.translations.resize(verts.size());
1397 const MutableSpan<float3> translations = tls.translations;
1398 for (const int i : verts.index_range()) {
1399 translations[i] = new_positions[verts[i]] - positions[verts[i]];
1400 }
1401 scale_translations(translations, strength);
1402 clip_and_lock_translations(sd, ss, positions, verts, translations);
1403 position_data.deform(translations, verts);
1404 });
1405}
1406
1407static bool edit_is_operation_valid(const Object &object,
1408 const EditMode mode,
1409 const bool modify_hidden)
1410{
1411 const bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1412 if (pbvh.type() == bke::pbvh::Type::BMesh) {
1413 /* Dyntopo is not supported. */
1414 return false;
1415 }
1416
1417 if (mode == EditMode::DeleteGeometry) {
1418 if (pbvh.type() == bke::pbvh::Type::Grids) {
1419 /* Modification of base mesh geometry requires special remapping of multi-resolution
1420 * displacement, which does not happen here.
1421 * Disable delete operation. It can be supported in the future by doing similar displacement
1422 * data remapping as what happens in the mesh edit mode. */
1423 return false;
1424 }
1425 if (check_single_face_set(object, !modify_hidden)) {
1426 /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the
1427 * entire object. */
1428 return false;
1429 }
1430 }
1431
1432 if (ELEM(mode, EditMode::FairPositions, EditMode::FairTangency)) {
1433 if (pbvh.type() == bke::pbvh::Type::Grids) {
1434 /* TODO: Multi-resolution topology representation using grids and duplicates can't be used
1435 * directly by the fair algorithm. Multi-resolution topology needs to be exposed in a
1436 * different way or converted to a mesh for this operation. */
1437 return false;
1438 }
1439 }
1440
1441 if (ELEM(mode, EditMode::Grow, EditMode::Shrink)) {
1442 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1443 const Mesh &mesh = *static_cast<Mesh *>(object.data);
1444 const bke::AttributeAccessor attributes = mesh.attributes();
1445 if (!attributes.contains(".sculpt_face_set")) {
1446 /* If a mesh does not have the face set attribute, growing or shrinking the face set will
1447 * have no effect, exit early in this case. */
1448 return false;
1449 }
1450 }
1451 }
1452
1453 return true;
1454}
1455
1457 bContext *C, Object &ob, const int active_face_set, const bool modify_hidden, wmOperator *op)
1458{
1459 const Scene &scene = *CTX_data_scene(C);
1460 Mesh *mesh = static_cast<Mesh *>(ob.data);
1461 undo::geometry_begin(scene, ob, op);
1462 delete_geometry(ob, active_face_set, modify_hidden);
1463 undo::geometry_end(ob);
1468}
1469
1471 bContext *C, Object &ob, const int active_face_set, const EditMode mode, wmOperator *op)
1472{
1473 const Scene &scene = *CTX_data_scene(C);
1474 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1475 const Sculpt &sd = *CTX_data_tool_settings(C)->sculpt;
1476 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(ob);
1477 IndexMaskMemory memory;
1478 const IndexMask node_mask = bke::pbvh::all_leaf_nodes(pbvh, memory);
1479
1480 const float strength = RNA_float_get(op->ptr, "strength");
1481
1482 undo::push_begin(scene, ob, op);
1483 undo::push_nodes(depsgraph, ob, node_mask, undo::Type::Position);
1484
1485 pbvh.tag_positions_changed(node_mask);
1486
1487 switch (mode) {
1488 case EditMode::FairPositions:
1489 edit_fairing(depsgraph, sd, ob, active_face_set, MESH_FAIRING_DEPTH_POSITION, strength);
1490 break;
1491 case EditMode::FairTangency:
1492 edit_fairing(depsgraph, sd, ob, active_face_set, MESH_FAIRING_DEPTH_TANGENCY, strength);
1493 break;
1494 default:
1496 }
1497
1498 bke::pbvh::update_bounds(depsgraph, ob, pbvh);
1499 flush_update_step(C, UpdateType::Position);
1500 flush_update_done(C, ob, UpdateType::Position);
1501 undo::push_end(ob);
1502}
1503
1505{
1508 const EditMode mode = EditMode(RNA_enum_get(op->ptr, "mode"));
1509 const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
1510
1511 if (!edit_is_operation_valid(*ob, mode, modify_hidden)) {
1512 return false;
1513 }
1514
1516
1517 return true;
1518}
1519
1521{
1522 if (!edit_op_init(C, op)) {
1523 return OPERATOR_CANCELLED;
1524 }
1525
1526 const Scene &scene = *CTX_data_scene(C);
1527 const Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
1529
1530 const int active_face_set = RNA_int_get(op->ptr, "active_face_set");
1531 const EditMode mode = EditMode(RNA_enum_get(op->ptr, "mode"));
1532 const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
1533
1534 switch (mode) {
1535 case EditMode::DeleteGeometry:
1536 edit_modify_geometry(C, ob, active_face_set, modify_hidden, op);
1537 break;
1538 case EditMode::Grow:
1539 case EditMode::Shrink:
1540 edit_grow_shrink(depsgraph, scene, ob, mode, active_face_set, modify_hidden, op);
1541 break;
1542 case EditMode::FairPositions:
1543 case EditMode::FairTangency:
1544 edit_modify_coordinates(C, ob, active_face_set, mode, op);
1545 break;
1546 }
1547
1549
1550 return OPERATOR_FINISHED;
1551}
1552
1553static int edit_op_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1554{
1555 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1557
1558 const View3D *v3d = CTX_wm_view3d(C);
1559 const Base *base = CTX_data_active_base(C);
1560 if (!BKE_base_is_visible(v3d, base)) {
1561 return OPERATOR_CANCELLED;
1562 }
1563
1565
1566 /* Update the current active Face Set and Vertex as the operator can be used directly from the
1567 * tool without brush cursor. */
1569 const float mval_fl[2] = {float(event->mval[0]), float(event->mval[1])};
1570 if (!SCULPT_cursor_geometry_info_update(C, &sgi, mval_fl, false)) {
1571 /* The cursor is not over the mesh. Cancel to avoid editing the last updated Face Set ID. */
1572 return OPERATOR_CANCELLED;
1573 }
1574 RNA_int_set(op->ptr, "active_face_set", active_face_set_get(ob));
1575
1576 return edit_op_exec(C, op);
1577}
1578
1580{
1581 ot->name = "Edit Face Set";
1582 ot->idname = "SCULPT_OT_face_set_edit";
1583 ot->description = "Edits the current active Face Set";
1584
1586 ot->exec = edit_op_exec;
1588
1590
1591 PropertyRNA *prop = RNA_def_int(
1592 ot->srna, "active_face_set", 1, 0, INT_MAX, "Active Face Set", "", 0, 64);
1594
1595 static EnumPropertyItem modes[] = {
1596 {int(EditMode::Grow),
1597 "GROW",
1598 0,
1599 "Grow Face Set",
1600 "Grows the Face Sets boundary by one face based on mesh topology"},
1601 {int(EditMode::Shrink),
1602 "SHRINK",
1603 0,
1604 "Shrink Face Set",
1605 "Shrinks the Face Sets boundary by one face based on mesh topology"},
1606 {int(EditMode::DeleteGeometry),
1607 "DELETE_GEOMETRY",
1608 0,
1609 "Delete Geometry",
1610 "Deletes the faces that are assigned to the Face Set"},
1611 {int(EditMode::FairPositions),
1612 "FAIR_POSITIONS",
1613 0,
1614 "Fair Positions",
1615 "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
1616 "vertex positions"},
1617 {int(EditMode::FairTangency),
1618 "FAIR_TANGENCY",
1619 0,
1620 "Fair Tangency",
1621 "Creates a smooth as possible geometry patch from the Face Set minimizing changes in "
1622 "vertex tangents"},
1623 {0, nullptr, 0, nullptr, nullptr},
1624 };
1625 RNA_def_enum(ot->srna, "mode", modes, int(EditMode::Grow), "Mode", "");
1626 RNA_def_float(ot->srna, "strength", 1.0f, 0.0f, 1.0f, "Strength", "", 0.0f, 1.0f);
1627
1629 "modify_hidden",
1630 false,
1631 "Modify Hidden",
1632 "Apply the edit operation to hidden geometry");
1633}
1634
1637/* -------------------------------------------------------------------- */
1647
1648static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
1649{
1650 const Scene &scene = *CTX_data_scene(&C);
1651 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(&C);
1653 undo::push_begin(scene, *gesture_data.vc.obact, &op);
1654}
1655
1656static void gesture_apply_mesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
1657{
1658 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1659 const int new_face_set = face_set_operation->new_face_set_id;
1660 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
1661 Object &object = *gesture_data.vc.obact;
1662 Mesh &mesh = *static_cast<Mesh *>(object.data);
1663 bke::AttributeAccessor attributes = mesh.attributes();
1664 SculptSession &ss = *gesture_data.ss;
1665 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(object);
1666
1667 const Span<float3> positions = bke::pbvh::vert_positions_eval(depsgraph, object);
1668 const OffsetIndices<int> faces = mesh.faces();
1669 const Span<int> corner_verts = mesh.corner_verts();
1670 const VArraySpan<bool> hide_poly = *attributes.lookup<bool>(".hide_poly", bke::AttrDomain::Face);
1671 bke::SpanAttributeWriter<int> face_sets = face_set::ensure_face_sets_mesh(mesh);
1672
1673 struct TLS {
1674 Vector<int> face_indices;
1675 };
1676
1677 Array<bool> node_changed(pbvh.nodes_num(), false);
1678
1680 if (pbvh.type() == bke::pbvh::Type::Mesh) {
1682 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1683 undo::push_node(depsgraph, *gesture_data.vc.obact, &nodes[i], undo::Type::FaceSet);
1684 bool any_updated = false;
1685 for (const int face : nodes[i].faces()) {
1686 if (!hide_poly.is_empty() && hide_poly[face]) {
1687 continue;
1688 }
1689 const Span<int> face_verts = corner_verts.slice(faces[face]);
1690 const float3 face_center = bke::mesh::face_center_calc(positions, face_verts);
1691 const float3 face_normal = bke::mesh::face_normal_calc(positions, face_verts);
1692 if (!gesture::is_affected(gesture_data, face_center, face_normal)) {
1693 continue;
1694 }
1695 face_sets.span[face] = new_face_set;
1696 any_updated = true;
1697 }
1698 if (any_updated) {
1699 node_changed[i] = true;
1700 }
1701 });
1702 }
1703 else if (pbvh.type() == bke::pbvh::Type::Grids) {
1705 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1706 TLS &tls = all_tls.local();
1707 undo::push_node(depsgraph, *gesture_data.vc.obact, &nodes[i], undo::Type::FaceSet);
1708 const Span<int> node_faces = bke::pbvh::node_face_indices_calc_grids(
1709 *ss.subdiv_ccg, nodes[i], tls.face_indices);
1710
1711 bool any_updated = false;
1712 for (const int face : node_faces) {
1713 if (!hide_poly.is_empty() && hide_poly[face]) {
1714 continue;
1715 }
1716 const Span<int> face_verts = corner_verts.slice(faces[face]);
1717 const float3 face_center = bke::mesh::face_center_calc(positions, face_verts);
1718 const float3 face_normal = bke::mesh::face_normal_calc(positions, face_verts);
1719 if (!gesture::is_affected(gesture_data, face_center, face_normal)) {
1720 continue;
1721 }
1722 face_sets.span[face] = new_face_set;
1723 any_updated = true;
1724 }
1725 if (any_updated) {
1726 node_changed[i] = true;
1727 }
1728 });
1729 }
1730
1731 IndexMaskMemory memory;
1732 pbvh.tag_face_sets_changed(IndexMask::from_bools(node_changed, memory));
1733 face_sets.finish();
1734}
1735
1736static void gesture_apply_bmesh(gesture::GestureData &gesture_data, const IndexMask &node_mask)
1737{
1738 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1739 const Depsgraph &depsgraph = *gesture_data.vc.depsgraph;
1740 const int new_face_set = face_set_operation->new_face_set_id;
1741 SculptSession &ss = *gesture_data.ss;
1742 bke::pbvh::Tree &pbvh = *bke::object::pbvh_get(*gesture_data.vc.obact);
1744 BMesh *bm = ss.bm;
1745 const int offset = CustomData_get_offset_named(&bm->pdata, CD_PROP_INT32, ".sculpt_face_set");
1746
1747 Array<bool> node_changed(node_mask.min_array_size(), false);
1748
1749 node_mask.foreach_index(GrainSize(1), [&](const int i) {
1750 undo::push_node(depsgraph, *gesture_data.vc.obact, &nodes[i], undo::Type::FaceSet);
1751
1752 bool any_updated = false;
1753 for (BMFace *face : BKE_pbvh_bmesh_node_faces(&nodes[i])) {
1754 if (BM_elem_flag_test(face, BM_ELEM_HIDDEN)) {
1755 continue;
1756 }
1757 float3 center;
1758 BM_face_calc_center_median(face, center);
1759 if (!gesture::is_affected(gesture_data, center, face->no)) {
1760 continue;
1761 }
1762 BM_ELEM_CD_SET_INT(face, offset, new_face_set);
1763 any_updated = true;
1764 }
1765
1766 if (any_updated) {
1767 node_changed[i] = true;
1768 }
1769 });
1770
1771 IndexMaskMemory memory;
1772 const IndexMask changed_nodes = IndexMask::from_bools(node_changed, memory);
1773 if (changed_nodes.is_empty()) {
1774 return;
1775 }
1776 pbvh.tag_face_sets_changed(node_mask);
1777}
1778
1780{
1781 switch (bke::object::pbvh_get(*gesture_data.vc.obact)->type()) {
1782 case bke::pbvh::Type::Grids:
1783 case bke::pbvh::Type::Mesh:
1784 gesture_apply_mesh(gesture_data, gesture_data.node_mask);
1785 break;
1786 case bke::pbvh::Type::BMesh:
1787 gesture_apply_bmesh(gesture_data, gesture_data.node_mask);
1788 break;
1789 }
1790}
1791
1792static void gesture_end(bContext & /*C*/, gesture::GestureData &gesture_data)
1793{
1794 undo::push_end(*gesture_data.vc.obact);
1795}
1796
1797static void init_operation(gesture::GestureData &gesture_data, wmOperator & /*op*/)
1798{
1799 Object &object = *gesture_data.vc.obact;
1800 gesture_data.operation = reinterpret_cast<gesture::Operation *>(
1801 MEM_cnew<FaceSetOperation>(__func__));
1802
1803 FaceSetOperation *face_set_operation = (FaceSetOperation *)gesture_data.operation;
1804
1805 face_set_operation->op.begin = gesture_begin;
1807 face_set_operation->op.end = gesture_end;
1808
1809 face_set_operation->new_face_set_id = face_set::find_next_available_id(object);
1810}
1811
1812static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1813{
1814 const View3D *v3d = CTX_wm_view3d(C);
1815 const Base *base = CTX_data_active_base(C);
1816 if (!BKE_base_is_visible(v3d, base)) {
1817 return OPERATOR_CANCELLED;
1818 }
1819
1820 return WM_gesture_box_invoke(C, op, event);
1821}
1822
1824{
1825 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_box(C, op);
1826 if (!gesture_data) {
1827 return OPERATOR_CANCELLED;
1828 }
1829 init_operation(*gesture_data, *op);
1830 gesture::apply(*C, *gesture_data, *op);
1831 return OPERATOR_FINISHED;
1832}
1833
1834static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1835{
1836 const View3D *v3d = CTX_wm_view3d(C);
1837 const Base *base = CTX_data_active_base(C);
1838 if (!BKE_base_is_visible(v3d, base)) {
1839 return OPERATOR_CANCELLED;
1840 }
1841
1842 return WM_gesture_lasso_invoke(C, op, event);
1843}
1844
1846{
1847 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_lasso(C, op);
1848 if (!gesture_data) {
1849 return OPERATOR_CANCELLED;
1850 }
1851 init_operation(*gesture_data, *op);
1852 gesture::apply(*C, *gesture_data, *op);
1853 return OPERATOR_FINISHED;
1854}
1855
1856static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1857{
1858 const View3D *v3d = CTX_wm_view3d(C);
1859 const Base *base = CTX_data_active_base(C);
1860 if (!BKE_base_is_visible(v3d, base)) {
1861 return OPERATOR_CANCELLED;
1862 }
1863
1864 return WM_gesture_straightline_active_side_invoke(C, op, event);
1865}
1866
1868{
1869 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_line(C, op);
1870 if (!gesture_data) {
1871 return OPERATOR_CANCELLED;
1872 }
1873 init_operation(*gesture_data, *op);
1874 gesture::apply(*C, *gesture_data, *op);
1875 return OPERATOR_FINISHED;
1876}
1877
1878static int gesture_polyline_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1879{
1880 const View3D *v3d = CTX_wm_view3d(C);
1881 const Base *base = CTX_data_active_base(C);
1882 if (!BKE_base_is_visible(v3d, base)) {
1883 return OPERATOR_CANCELLED;
1884 }
1885
1886 return WM_gesture_polyline_invoke(C, op, event);
1887}
1888
1890{
1891 std::unique_ptr<gesture::GestureData> gesture_data = gesture::init_from_polyline(C, op);
1892 if (!gesture_data) {
1893 return OPERATOR_CANCELLED;
1894 }
1895 init_operation(*gesture_data, *op);
1896 gesture::apply(*C, *gesture_data, *op);
1897 return OPERATOR_FINISHED;
1898}
1899
1901{
1902 ot->name = "Face Set Lasso Gesture";
1903 ot->idname = "SCULPT_OT_face_set_polyline_gesture";
1904 ot->description = "Add a face set in a shape defined by the cursor";
1905
1909
1911
1913
1915 gesture::operator_properties(ot, gesture::ShapeType::Lasso);
1916}
1917
1919{
1920 ot->name = "Face Set Box Gesture";
1921 ot->idname = "SCULPT_OT_face_set_box_gesture";
1922 ot->description = "Add a face set in a rectangle defined by the cursor";
1923
1927
1929
1931
1933 gesture::operator_properties(ot, gesture::ShapeType::Box);
1934}
1935
1937{
1938 ot->name = "Face Set Lasso Gesture";
1939 ot->idname = "SCULPT_OT_face_set_lasso_gesture";
1940 ot->description = "Add a face set in a shape defined by the cursor";
1941
1945
1947
1949
1951 gesture::operator_properties(ot, gesture::ShapeType::Lasso);
1952}
1953
1955{
1956 ot->name = "Face Set Line Gesture";
1957 ot->idname = "SCULPT_OT_face_set_line_gesture";
1958 ot->description = "Add a face set to one side of a line defined by the cursor";
1959
1963
1965
1967
1969 gesture::operator_properties(ot, gesture::ShapeType::Line);
1970}
1973} // 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:77
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:341
void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool)
Definition paint.cc:2601
void BKE_sculptsession_free_pbvh(Object &object)
Definition paint.cc:2099
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:97
#define BLI_assert(a)
Definition BLI_assert.h:50
BLI_INLINE float BLI_hash_int_01(unsigned int k)
Definition BLI_hash.h:96
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)
void DEG_id_tag_update(ID *id, unsigned int flags)
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
@ CD_PROP_INT32
Object is a sort of wrapper for general info.
Read Guarded memory(de)allocation.
@ PROP_HIDDEN
Definition RNA_types.hh:239
@ OPTYPE_DEPENDS_ON_CURSOR
Definition WM_types.hh:198
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DATA
Definition WM_types.hh:475
#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 char *name)
#define BM_ITER_MESH(ele, iter, bm, itype)
@ BM_FACES_OF_MESH
ATTR_WARN_UNUSED_RESULT 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])
MutableSpan< T > as_mutable_span()
Definition BLI_array.hh:237
const T * data() const
Definition BLI_array.hh:301
constexpr int64_t size() const
Definition BLI_span.hh:494
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr Span< T > as_span() const
Definition BLI_span.hh:662
constexpr IndexRange index_range() const
Definition BLI_span.hh:671
bool add(const Key &key)
Definition BLI_set.hh:248
constexpr Span slice(int64_t start, int64_t size) const
Definition BLI_span.hh:138
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
constexpr bool contains(const T &value) const
Definition BLI_span.hh:278
bool add(const StringRef attribute_id, const AttrDomain domain, const eCustomDataType data_type, const AttributeInit &initializer)
void tag_positions_changed(const IndexMask &node_mask)
Definition pbvh.cc:549
Span< NodeT > nodes() const
int nodes_num() const
Definition pbvh.cc:504
void tag_face_sets_changed(const IndexMask &node_mask)
Definition pbvh.cc:579
void tag_visibility_changed(const IndexMask &node_mask)
Definition pbvh.cc:562
void deform(MutableSpan< float3 > translations, Span< int > verts) const
Definition sculpt.cc:7139
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
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 float verts[][3]
pbvh::Tree * pbvh_get(Object &object)
Definition paint.cc:2846
static int gesture_lasso_exec(bContext *C, wmOperator *op)
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 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 int change_visibility_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int gesture_polyline_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:294
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:254
void SCULPT_OT_face_set_lasso_gesture(wmOperatorType *ot)
static int gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void gesture_begin(bContext &C, wmOperator &op, gesture::GestureData &gesture_data)
static int gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void initialize_none_to_id(Mesh *mesh, int new_id)
static bool edit_op_init(bContext *C, wmOperator *op)
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)
void SCULPT_OT_face_sets_init(wmOperatorType *ot)
int active_update_and_get(bContext *C, Object &ob, const float mval_fl[2])
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)
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)
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 int create_op_exec(bContext *C, wmOperator *op)
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)
static int init_op_exec(bContext *C, wmOperator *op)
void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
static bool check_single_face_set(const Object &object, const bool check_visible_only)
static int edit_op_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static int edit_op_exec(bContext *C, wmOperator *op)
static int gesture_box_exec(bContext *C, wmOperator *op)
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 int change_visibility_exec(bContext *C, wmOperator *op)
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 void init_operation(gesture::GestureData &gesture_data, wmOperator &)
void SCULPT_OT_face_set_box_gesture(wmOperatorType *ot)
static int randomize_colors_exec(bContext *C, wmOperator *)
static int gesture_line_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static void edit_modify_coordinates(bContext *C, Object &ob, const int active_face_set, const EditMode mode, wmOperator *op)
static void init_flood_fill(Object &ob, const FaceSetsFloodFillFn &test_fn)
static int gesture_line_exec(bContext *C, wmOperator *op)
int active_face_set_get(const Object &object)
Definition sculpt.cc:198
static int gesture_polyline_exec(bContext *C, wmOperator *op)
static void edit_modify_geometry(bContext *C, Object &ob, const int active_face_set, const bool modify_hidden, wmOperator *op)
void flush_update_done(const bContext *C, Object &ob, UpdateType update_type)
Definition sculpt.cc:5055
void scale_translations(MutableSpan< float3 > translations, Span< float > factors)
Definition sculpt.cc:7210
void flush_update_step(bContext *C, UpdateType update_type)
Definition sculpt.cc:4960
void clip_and_lock_translations(const Sculpt &sd, const SculptSession &ss, Span< float3 > positions, Span< int > verts, MutableSpan< float3 > translations)
Definition sculpt.cc:7022
void scatter_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6122
void gather_data_mesh(Span< T > src, Span< int > indices, MutableSpan< T > dst)
Definition sculpt.cc:6082
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:153
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_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_cursor_geometry_info_update(bContext *C, SculptCursorGeometryInfo *out, const float mval[2], bool use_sampled_normal)
Definition sculpt.cc:4580
void SCULPT_vertex_random_access_ensure(Object &object)
Definition sculpt.cc:144
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
BMHeader head
void * data
CustomData pdata
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:458
SubdivCCG * subdiv_ccg
Definition BKE_paint.hh:405
ActiveVert active_vert() const
Definition paint.cc:2180
blender::Array< int > edge_to_face_offsets
Definition BKE_paint.hh:390
blender::float3 active_vert_position(const Depsgraph &depsgraph, const Object &object) const
Definition paint.cc:2224
blender::GroupedSpan< int > edge_to_face_map
Definition BKE_paint.hh:392
blender::Array< int > edge_to_face_indices
Definition BKE_paint.hh:391
blender::BitVector boundary
Definition BKE_paint.hh:351
blender::Array< float > masks
struct UnifiedPaintSettings unified_paint_settings
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 &)
int mval[2]
Definition WM_types.hh:728
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
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct PointerRNA * ptr
@ 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:4126
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)