1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2020 Blender Foundation.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup edsculpt
22 */
23
24 #include "MEM_guardedalloc.h"
25
26 #include "BLI_blenlib.h"
27 #include "BLI_hash.h"
28 #include "BLI_math.h"
29 #include "BLI_task.h"
30
31 #include "DNA_brush_types.h"
32 #include "DNA_customdata_types.h"
33 #include "DNA_mesh_types.h"
34 #include "DNA_meshdata_types.h"
35 #include "DNA_object_types.h"
36 #include "DNA_scene_types.h"
37
38 #include "BKE_brush.h"
39 #include "BKE_ccg.h"
40 #include "BKE_colortools.h"
41 #include "BKE_context.h"
42 #include "BKE_customdata.h"
43 #include "BKE_mesh.h"
44 #include "BKE_mesh_mapping.h"
45 #include "BKE_multires.h"
46 #include "BKE_node.h"
47 #include "BKE_object.h"
48 #include "BKE_paint.h"
49 #include "BKE_pbvh.h"
50 #include "BKE_scene.h"
51
52 #include "DEG_depsgraph.h"
53
54 #include "WM_api.h"
55 #include "WM_message.h"
56 #include "WM_toolsystem.h"
57 #include "WM_types.h"
58
59 #include "ED_object.h"
60 #include "ED_screen.h"
61 #include "ED_sculpt.h"
62 #include "ED_view3d.h"
63 #include "paint_intern.h"
64 #include "sculpt_intern.h"
65
66 #include "RNA_access.h"
67 #include "RNA_define.h"
68
69 #include "bmesh.h"
70
71 #include <math.h>
72 #include <stdlib.h>
73
74 /* Utils. */
ED_sculpt_face_sets_find_next_available_id(struct Mesh * mesh)75 int ED_sculpt_face_sets_find_next_available_id(struct Mesh *mesh)
76 {
77 int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
78 if (!face_sets) {
79 return SCULPT_FACE_SET_NONE;
80 }
81
82 int next_face_set_id = 0;
83 for (int i = 0; i < mesh->totpoly; i++) {
84 next_face_set_id = max_ii(next_face_set_id, abs(face_sets[i]));
85 }
86 next_face_set_id++;
87
88 return next_face_set_id;
89 }
90
ED_sculpt_face_sets_initialize_none_to_id(struct Mesh * mesh,const int new_id)91 void ED_sculpt_face_sets_initialize_none_to_id(struct Mesh *mesh, const int new_id)
92 {
93 int *face_sets = CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS);
94 if (!face_sets) {
95 return;
96 }
97
98 for (int i = 0; i < mesh->totpoly; i++) {
99 if (face_sets[i] == SCULPT_FACE_SET_NONE) {
100 face_sets[i] = new_id;
101 }
102 }
103 }
104
ED_sculpt_face_sets_active_update_and_get(bContext * C,Object * ob,const float mval[2])105 int ED_sculpt_face_sets_active_update_and_get(bContext *C, Object *ob, const float mval[2])
106 {
107 SculptSession *ss = ob->sculpt;
108 if (!ss) {
109 return SCULPT_FACE_SET_NONE;
110 }
111
112 SculptCursorGeometryInfo gi;
113 if (!SCULPT_cursor_geometry_info_update(C, &gi, mval, false)) {
114 return SCULPT_FACE_SET_NONE;
115 }
116
117 return SCULPT_active_face_set_get(ss);
118 }
119
120 /* Draw Face Sets Brush. */
121
do_draw_face_sets_brush_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict tls)122 static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
123 const int n,
124 const TaskParallelTLS *__restrict tls)
125 {
126 SculptThreadedTaskData *data = userdata;
127 SculptSession *ss = data->ob->sculpt;
128 const Brush *brush = data->brush;
129 const float bstrength = ss->cache->bstrength;
130
131 PBVHVertexIter vd;
132
133 SculptBrushTest test;
134 SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
135 ss, &test, data->brush->falloff_shape);
136 const int thread_id = BLI_task_parallel_thread_id(tls);
137
138 MVert *mvert = SCULPT_mesh_deformed_mverts_get(ss);
139
140 BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
141 {
142 if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
143 MeshElemMap *vert_map = &ss->pmap[vd.index];
144 for (int j = 0; j < ss->pmap[vd.index].count; j++) {
145 const MPoly *p = &ss->mpoly[vert_map->indices[j]];
146
147 float poly_center[3];
148 BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], mvert, poly_center);
149
150 if (sculpt_brush_test_sq_fn(&test, poly_center)) {
151 const float fade = bstrength * SCULPT_brush_strength_factor(ss,
152 brush,
153 vd.co,
154 sqrtf(test.dist),
155 vd.no,
156 vd.fno,
157 vd.mask ? *vd.mask : 0.0f,
158 vd.index,
159 thread_id);
160
161 if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
162 ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
163 }
164 }
165 }
166 }
167
168 else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
169 {
170 if (sculpt_brush_test_sq_fn(&test, vd.co)) {
171 const float fade = bstrength * SCULPT_brush_strength_factor(ss,
172 brush,
173 vd.co,
174 sqrtf(test.dist),
175 vd.no,
176 vd.fno,
177 vd.mask ? *vd.mask : 0.0f,
178 vd.index,
179 thread_id);
180
181 if (fade > 0.05f) {
182 SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
183 }
184 }
185 }
186 }
187 }
188 BKE_pbvh_vertex_iter_end;
189 }
190
do_relax_face_sets_brush_task_cb_ex(void * __restrict userdata,const int n,const TaskParallelTLS * __restrict tls)191 static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
192 const int n,
193 const TaskParallelTLS *__restrict tls)
194 {
195 SculptThreadedTaskData *data = userdata;
196 SculptSession *ss = data->ob->sculpt;
197 const Brush *brush = data->brush;
198 float bstrength = ss->cache->bstrength;
199
200 PBVHVertexIter vd;
201
202 SculptBrushTest test;
203 SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
204 ss, &test, data->brush->falloff_shape);
205
206 const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
207 /* This operations needs a stregth tweak as the relax deformation is too weak by default. */
208 if (relax_face_sets) {
209 bstrength *= 2.0f;
210 }
211
212 const int thread_id = BLI_task_parallel_thread_id(tls);
213
214 BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
215 {
216 if (sculpt_brush_test_sq_fn(&test, vd.co)) {
217 if (relax_face_sets != SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
218 const float fade = bstrength * SCULPT_brush_strength_factor(ss,
219 brush,
220 vd.co,
221 sqrtf(test.dist),
222 vd.no,
223 vd.fno,
224 vd.mask ? *vd.mask : 0.0f,
225 vd.index,
226 thread_id);
227
228 SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
229 if (vd.mvert) {
230 vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
231 }
232 }
233 }
234 }
235 BKE_pbvh_vertex_iter_end;
236 }
237
SCULPT_do_draw_face_sets_brush(Sculpt * sd,Object * ob,PBVHNode ** nodes,int totnode)238 void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
239 {
240 SculptSession *ss = ob->sculpt;
241 Brush *brush = BKE_paint_brush(&sd->paint);
242
243 BKE_curvemapping_init(brush->curve);
244
245 /* Threaded loop over nodes. */
246 SculptThreadedTaskData data = {
247 .sd = sd,
248 .ob = ob,
249 .brush = brush,
250 .nodes = nodes,
251 };
252
253 TaskParallelSettings settings;
254 BKE_pbvh_parallel_range_settings(&settings, true, totnode);
255 if (ss->cache->alt_smooth) {
256 SCULPT_boundary_info_ensure(ob);
257 for (int i = 0; i < 4; i++) {
258 BLI_task_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
259 }
260 }
261 else {
262 BLI_task_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
263 }
264 }
265
266 /* Face Sets Operators */
267
268 typedef enum eSculptFaceGroupsCreateModes {
269 SCULPT_FACE_SET_MASKED = 0,
270 SCULPT_FACE_SET_VISIBLE = 1,
271 SCULPT_FACE_SET_ALL = 2,
272 SCULPT_FACE_SET_SELECTION = 3,
273 } eSculptFaceGroupsCreateModes;
274
275 static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
276 {
277 SCULPT_FACE_SET_MASKED,
278 "MASKED",
279 0,
280 "Face Set from Masked",
281 "Create a new Face Set from the masked faces",
282 },
283 {
284 SCULPT_FACE_SET_VISIBLE,
285 "VISIBLE",
286 0,
287 "Face Set from Visible",
288 "Create a new Face Set from the visible vertices",
289 },
290 {
291 SCULPT_FACE_SET_ALL,
292 "ALL",
293 0,
294 "Face Set Full Mesh",
295 "Create an unique Face Set with all faces in the sculpt",
296 },
297 {
298 SCULPT_FACE_SET_SELECTION,
299 "SELECTION",
300 0,
301 "Face Set from Edit Mode Selection",
302 "Create an Face Set corresponding to the Edit Mode face selection",
303 },
304 {0, NULL, 0, NULL, NULL},
305 };
306
sculpt_face_set_create_exec(bContext * C,wmOperator * op)307 static int sculpt_face_set_create_exec(bContext *C, wmOperator *op)
308 {
309 Object *ob = CTX_data_active_object(C);
310 SculptSession *ss = ob->sculpt;
311 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
312
313 const int mode = RNA_enum_get(op->ptr, "mode");
314
315 /* Dyntopo not suported. */
316 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
317 return OPERATOR_CANCELLED;
318 }
319
320 BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED, false);
321
322 const int tot_vert = SCULPT_vertex_count_get(ss);
323 float threshold = 0.5f;
324
325 PBVH *pbvh = ob->sculpt->pbvh;
326 PBVHNode **nodes;
327 int totnode;
328 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
329
330 if (!nodes) {
331 return OPERATOR_CANCELLED;
332 }
333
334 SCULPT_undo_push_begin("face set change");
335 SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
336
337 const int next_face_set = SCULPT_face_set_next_available_get(ss);
338
339 if (mode == SCULPT_FACE_SET_MASKED) {
340 for (int i = 0; i < tot_vert; i++) {
341 if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
342 SCULPT_vertex_face_set_set(ss, i, next_face_set);
343 }
344 }
345 }
346
347 if (mode == SCULPT_FACE_SET_VISIBLE) {
348
349 /* If all vertices in the sculpt are visible, create the new face set and update the default
350 * color. This way the new face set will be white, which is a quick way of disabling all face
351 * sets and the performance hit of rendering the overlay. */
352 bool all_visible = true;
353 for (int i = 0; i < tot_vert; i++) {
354 if (!SCULPT_vertex_visible_get(ss, i)) {
355 all_visible = false;
356 break;
357 }
358 }
359
360 if (all_visible) {
361 Mesh *mesh = ob->data;
362 mesh->face_sets_color_default = next_face_set;
363 BKE_pbvh_face_sets_color_set(
364 ss->pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
365 }
366
367 for (int i = 0; i < tot_vert; i++) {
368 if (SCULPT_vertex_visible_get(ss, i)) {
369 SCULPT_vertex_face_set_set(ss, i, next_face_set);
370 }
371 }
372 }
373
374 if (mode == SCULPT_FACE_SET_ALL) {
375 for (int i = 0; i < tot_vert; i++) {
376 SCULPT_vertex_face_set_set(ss, i, next_face_set);
377 }
378 }
379
380 if (mode == SCULPT_FACE_SET_SELECTION) {
381 Mesh *mesh = ob->data;
382 BMesh *bm;
383 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
384 bm = BM_mesh_create(&allocsize,
385 &((struct BMeshCreateParams){
386 .use_toolflags = true,
387 }));
388
389 BM_mesh_bm_from_me(bm,
390 mesh,
391 (&(struct BMeshFromMeshParams){
392 .calc_face_normal = true,
393 }));
394
395 BMIter iter;
396 BMFace *f;
397 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
398 if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
399 ss->face_sets[BM_elem_index_get(f)] = next_face_set;
400 }
401 }
402 BM_mesh_free(bm);
403 }
404
405 for (int i = 0; i < totnode; i++) {
406 BKE_pbvh_node_mark_redraw(nodes[i]);
407 }
408
409 MEM_SAFE_FREE(nodes);
410
411 SCULPT_undo_push_end();
412
413 SCULPT_tag_update_overlays(C);
414
415 return OPERATOR_FINISHED;
416 }
417
SCULPT_OT_face_sets_create(wmOperatorType * ot)418 void SCULPT_OT_face_sets_create(wmOperatorType *ot)
419 {
420 /* identifiers */
421 ot->name = "Create Face Set";
422 ot->idname = "SCULPT_OT_face_sets_create";
423 ot->description = "Create a new Face Set";
424
425 /* api callbacks */
426 ot->exec = sculpt_face_set_create_exec;
427 ot->poll = SCULPT_mode_poll;
428
429 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
430
431 RNA_def_enum(
432 ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", "");
433 }
434
435 typedef enum eSculptFaceSetsInitMode {
436 SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0,
437 SCULPT_FACE_SETS_FROM_MATERIALS = 1,
438 SCULPT_FACE_SETS_FROM_NORMALS = 2,
439 SCULPT_FACE_SETS_FROM_UV_SEAMS = 3,
440 SCULPT_FACE_SETS_FROM_CREASES = 4,
441 SCULPT_FACE_SETS_FROM_SHARP_EDGES = 5,
442 SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6,
443 SCULPT_FACE_SETS_FROM_FACE_MAPS = 7,
444 } eSculptFaceSetsInitMode;
445
446 static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
447 {
448 SCULPT_FACE_SETS_FROM_LOOSE_PARTS,
449 "LOOSE_PARTS",
450 0,
451 "Face Sets from Loose Parts",
452 "Create a Face Set per loose part in the mesh",
453 },
454 {
455 SCULPT_FACE_SETS_FROM_MATERIALS,
456 "MATERIALS",
457 0,
458 "Face Sets from Material Slots",
459 "Create a Face Set per Material Slot",
460 },
461 {
462 SCULPT_FACE_SETS_FROM_NORMALS,
463 "NORMALS",
464 0,
465 "Face Sets from Mesh Normals",
466 "Create Face Sets for Faces that have similar normal",
467 },
468 {
469 SCULPT_FACE_SETS_FROM_UV_SEAMS,
470 "UV_SEAMS",
471 0,
472 "Face Sets from UV Seams",
473 "Create Face Sets using UV Seams as boundaries",
474 },
475 {
476 SCULPT_FACE_SETS_FROM_CREASES,
477 "CREASES",
478 0,
479 "Face Sets from Edge Creases",
480 "Create Face Sets using Edge Creases as boundaries",
481 },
482 {
483 SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT,
484 "BEVEL_WEIGHT",
485 0,
486 "Face Sets from Bevel Weight",
487 "Create Face Sets using Bevel Weights as boundaries",
488 },
489 {
490 SCULPT_FACE_SETS_FROM_SHARP_EDGES,
491 "SHARP_EDGES",
492 0,
493 "Face Sets from Sharp Edges",
494 "Create Face Sets using Sharp Edges as boundaries",
495 },
496 {
497 SCULPT_FACE_SETS_FROM_FACE_MAPS,
498 "FACE_MAPS",
499 0,
500 "Face Sets from Face Maps",
501 "Create a Face Set per Face Map",
502 },
503 {0, NULL, 0, NULL, NULL},
504 };
505
506 typedef bool (*face_sets_flood_fill_test)(
507 BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
508
sculpt_face_sets_init_loose_parts_test(BMesh * UNUSED (bm),BMFace * UNUSED (from_f),BMEdge * UNUSED (from_e),BMFace * UNUSED (to_f),const float UNUSED (threshold))509 static bool sculpt_face_sets_init_loose_parts_test(BMesh *UNUSED(bm),
510 BMFace *UNUSED(from_f),
511 BMEdge *UNUSED(from_e),
512 BMFace *UNUSED(to_f),
513 const float UNUSED(threshold))
514 {
515 return true;
516 }
517
sculpt_face_sets_init_normals_test(BMesh * UNUSED (bm),BMFace * from_f,BMEdge * UNUSED (from_e),BMFace * to_f,const float threshold)518 static bool sculpt_face_sets_init_normals_test(
519 BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
520 {
521 return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
522 }
523
sculpt_face_sets_init_uv_seams_test(BMesh * UNUSED (bm),BMFace * UNUSED (from_f),BMEdge * from_e,BMFace * UNUSED (to_f),const float UNUSED (threshold))524 static bool sculpt_face_sets_init_uv_seams_test(BMesh *UNUSED(bm),
525 BMFace *UNUSED(from_f),
526 BMEdge *from_e,
527 BMFace *UNUSED(to_f),
528 const float UNUSED(threshold))
529 {
530 return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
531 }
532
sculpt_face_sets_init_crease_test(BMesh * bm,BMFace * UNUSED (from_f),BMEdge * from_e,BMFace * UNUSED (to_f),const float threshold)533 static bool sculpt_face_sets_init_crease_test(
534 BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
535 {
536 return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
537 }
538
sculpt_face_sets_init_bevel_weight_test(BMesh * bm,BMFace * UNUSED (from_f),BMEdge * from_e,BMFace * UNUSED (to_f),const float threshold)539 static bool sculpt_face_sets_init_bevel_weight_test(
540 BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
541 {
542 return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
543 }
544
sculpt_face_sets_init_sharp_edges_test(BMesh * UNUSED (bm),BMFace * UNUSED (from_f),BMEdge * from_e,BMFace * UNUSED (to_f),const float UNUSED (threshold))545 static bool sculpt_face_sets_init_sharp_edges_test(BMesh *UNUSED(bm),
546 BMFace *UNUSED(from_f),
547 BMEdge *from_e,
548 BMFace *UNUSED(to_f),
549 const float UNUSED(threshold))
550 {
551 return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
552 }
553
sculpt_face_sets_init_flood_fill(Object * ob,face_sets_flood_fill_test test,const float threshold)554 static void sculpt_face_sets_init_flood_fill(Object *ob,
555 face_sets_flood_fill_test test,
556 const float threshold)
557 {
558 SculptSession *ss = ob->sculpt;
559 Mesh *mesh = ob->data;
560 BMesh *bm;
561 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
562 bm = BM_mesh_create(&allocsize,
563 &((struct BMeshCreateParams){
564 .use_toolflags = true,
565 }));
566
567 BM_mesh_bm_from_me(bm,
568 mesh,
569 (&(struct BMeshFromMeshParams){
570 .calc_face_normal = true,
571 }));
572
573 BLI_bitmap *visited_faces = BLI_BITMAP_NEW(mesh->totpoly, "visited faces");
574 const int totfaces = mesh->totpoly;
575
576 int *face_sets = ss->face_sets;
577
578 BM_mesh_elem_table_init(bm, BM_FACE);
579 BM_mesh_elem_table_ensure(bm, BM_FACE);
580
581 int next_face_set = 1;
582
583 for (int i = 0; i < totfaces; i++) {
584 if (!BLI_BITMAP_TEST(visited_faces, i)) {
585 GSQueue *queue;
586 queue = BLI_gsqueue_new(sizeof(int));
587
588 face_sets[i] = next_face_set;
589 BLI_BITMAP_ENABLE(visited_faces, i);
590 BLI_gsqueue_push(queue, &i);
591
592 while (!BLI_gsqueue_is_empty(queue)) {
593 int from_f;
594 BLI_gsqueue_pop(queue, &from_f);
595
596 BMFace *f, *f_neighbor;
597 BMEdge *ed;
598 BMIter iter_a, iter_b;
599
600 f = BM_face_at_index(bm, from_f);
601
602 BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
603 BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
604 if (f_neighbor != f) {
605 int neighbor_face_index = BM_elem_index_get(f_neighbor);
606 if (!BLI_BITMAP_TEST(visited_faces, neighbor_face_index)) {
607 if (test(bm, f, ed, f_neighbor, threshold)) {
608 face_sets[neighbor_face_index] = next_face_set;
609 BLI_BITMAP_ENABLE(visited_faces, neighbor_face_index);
610 BLI_gsqueue_push(queue, &neighbor_face_index);
611 }
612 }
613 }
614 }
615 }
616 }
617
618 next_face_set += 1;
619
620 BLI_gsqueue_free(queue);
621 }
622 }
623
624 MEM_SAFE_FREE(visited_faces);
625
626 BM_mesh_free(bm);
627 }
628
sculpt_face_sets_init_loop(Object * ob,const int mode)629 static void sculpt_face_sets_init_loop(Object *ob, const int mode)
630 {
631 Mesh *mesh = ob->data;
632 SculptSession *ss = ob->sculpt;
633 BMesh *bm;
634 const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
635 bm = BM_mesh_create(&allocsize,
636 &((struct BMeshCreateParams){
637 .use_toolflags = true,
638 }));
639
640 BM_mesh_bm_from_me(bm,
641 mesh,
642 (&(struct BMeshFromMeshParams){
643 .calc_face_normal = true,
644 }));
645 BMIter iter;
646 BMFace *f;
647
648 const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
649
650 BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
651 if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
652 ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
653 }
654 else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
655 if (cd_fmaps_offset != -1) {
656 ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
657 }
658 else {
659 ss->face_sets[BM_elem_index_get(f)] = 1;
660 }
661 }
662 }
663 BM_mesh_free(bm);
664 }
665
sculpt_face_set_init_exec(bContext * C,wmOperator * op)666 static int sculpt_face_set_init_exec(bContext *C, wmOperator *op)
667 {
668 Object *ob = CTX_data_active_object(C);
669 SculptSession *ss = ob->sculpt;
670 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
671
672 const int mode = RNA_enum_get(op->ptr, "mode");
673
674 /* Dyntopo not supported. */
675 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
676 return OPERATOR_CANCELLED;
677 }
678
679 BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
680
681 PBVH *pbvh = ob->sculpt->pbvh;
682 PBVHNode **nodes;
683 int totnode;
684 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
685
686 if (!nodes) {
687 return OPERATOR_CANCELLED;
688 }
689
690 SCULPT_undo_push_begin("face set change");
691 SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
692
693 const float threshold = RNA_float_get(op->ptr, "threshold");
694
695 switch (mode) {
696 case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
697 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
698 break;
699 case SCULPT_FACE_SETS_FROM_MATERIALS:
700 sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
701 break;
702 case SCULPT_FACE_SETS_FROM_NORMALS:
703 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
704 break;
705 case SCULPT_FACE_SETS_FROM_UV_SEAMS:
706 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
707 break;
708 case SCULPT_FACE_SETS_FROM_CREASES:
709 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
710 break;
711 case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
712 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
713 break;
714 case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
715 sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
716 break;
717 case SCULPT_FACE_SETS_FROM_FACE_MAPS:
718 sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
719 break;
720 }
721
722 SCULPT_undo_push_end();
723
724 /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
725 SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
726
727 for (int i = 0; i < totnode; i++) {
728 BKE_pbvh_node_mark_update_visibility(nodes[i]);
729 }
730
731 BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
732
733 MEM_SAFE_FREE(nodes);
734
735 if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
736 BKE_mesh_flush_hidden_from_verts(ob->data);
737 }
738
739 SCULPT_tag_update_overlays(C);
740
741 return OPERATOR_FINISHED;
742 }
743
SCULPT_OT_face_sets_init(wmOperatorType * ot)744 void SCULPT_OT_face_sets_init(wmOperatorType *ot)
745 {
746 /* identifiers */
747 ot->name = "Init Face Sets";
748 ot->idname = "SCULPT_OT_face_sets_init";
749 ot->description = "Initializes all Face Sets in the mesh";
750
751 /* api callbacks */
752 ot->exec = sculpt_face_set_init_exec;
753 ot->poll = SCULPT_mode_poll;
754
755 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
756
757 RNA_def_enum(
758 ot->srna, "mode", prop_sculpt_face_sets_init_types, SCULPT_FACE_SET_MASKED, "Mode", "");
759 RNA_def_float(
760 ot->srna,
761 "threshold",
762 0.5f,
763 0.0f,
764 1.0f,
765 "Threshold",
766 "Minimum value to consider a certain attribute a boundary when creating the Face Sets",
767 0.0f,
768 1.0f);
769 }
770
771 typedef enum eSculptFaceGroupVisibilityModes {
772 SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0,
773 SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1,
774 SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
775 SCULPT_FACE_SET_VISIBILITY_INVERT = 3,
776 SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4,
777 } eSculptFaceGroupVisibilityModes;
778
779 static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
780 {
781 SCULPT_FACE_SET_VISIBILITY_TOGGLE,
782 "TOGGLE",
783 0,
784 "Toggle Visibility",
785 "Hide all Face Sets except for the active one",
786 },
787 {
788 SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE,
789 "SHOW_ACTIVE",
790 0,
791 "Show Active Face Set",
792 "Show Active Face Set",
793 },
794 {
795 SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE,
796 "HIDE_ACTIVE",
797 0,
798 "Hide Active Face Sets",
799 "Hide Active Face Sets",
800 },
801 {
802 SCULPT_FACE_SET_VISIBILITY_INVERT,
803 "INVERT",
804 0,
805 "Invert Face Set Visibility",
806 "Invert Face Set Visibility",
807 },
808 {
809 SCULPT_FACE_SET_VISIBILITY_SHOW_ALL,
810 "SHOW_ALL",
811 0,
812 "Show All Face Sets",
813 "Show All Face Sets",
814 },
815 {0, NULL, 0, NULL, NULL},
816 };
817
sculpt_face_sets_change_visibility_exec(bContext * C,wmOperator * op)818 static int sculpt_face_sets_change_visibility_exec(bContext *C, wmOperator *op)
819 {
820 Object *ob = CTX_data_active_object(C);
821 SculptSession *ss = ob->sculpt;
822 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
823
824 /* Dyntopo not supported. */
825 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
826 return OPERATOR_CANCELLED;
827 }
828
829 BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
830
831 const int tot_vert = SCULPT_vertex_count_get(ss);
832 const int mode = RNA_enum_get(op->ptr, "mode");
833 const int active_face_set = SCULPT_active_face_set_get(ss);
834
835 SCULPT_undo_push_begin("Hide area");
836
837 PBVH *pbvh = ob->sculpt->pbvh;
838 PBVHNode **nodes;
839 int totnode;
840
841 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
842
843 if (totnode == 0) {
844 MEM_SAFE_FREE(nodes);
845 return OPERATOR_CANCELLED;
846 }
847
848 SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
849
850 if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) {
851 bool hidden_vertex = false;
852
853 /* This can fail with regular meshes with non-manifold geometry as the visibility state can't
854 * be synced from face sets to non-manifold vertices. */
855 if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
856 for (int i = 0; i < tot_vert; i++) {
857 if (!SCULPT_vertex_visible_get(ss, i)) {
858 hidden_vertex = true;
859 break;
860 }
861 }
862 }
863
864 for (int i = 0; i < ss->totfaces; i++) {
865 if (ss->face_sets[i] <= 0) {
866 hidden_vertex = true;
867 break;
868 }
869 }
870
871 if (hidden_vertex) {
872 SCULPT_face_sets_visibility_all_set(ss, true);
873 }
874 else {
875 SCULPT_face_sets_visibility_all_set(ss, false);
876 SCULPT_face_set_visibility_set(ss, active_face_set, true);
877 }
878 }
879
880 if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
881 SCULPT_face_sets_visibility_all_set(ss, true);
882 }
883
884 if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
885 SCULPT_face_sets_visibility_all_set(ss, false);
886 SCULPT_face_set_visibility_set(ss, active_face_set, true);
887 }
888
889 if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
890 SCULPT_face_set_visibility_set(ss, active_face_set, false);
891 }
892
893 if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
894 SCULPT_face_sets_visibility_invert(ss);
895 }
896
897 /* For modes that use the cursor active vertex, update the rotation origin for viewport
898 * navigation. */
899 if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
900 UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
901 float location[3];
902 copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
903 mul_m4_v3(ob->obmat, location);
904 copy_v3_v3(ups->average_stroke_accum, location);
905 ups->average_stroke_counter = 1;
906 ups->last_stroke_valid = true;
907 }
908
909 /* Sync face sets visibility and vertex visibility. */
910 SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
911
912 SCULPT_undo_push_end();
913
914 for (int i = 0; i < totnode; i++) {
915 BKE_pbvh_node_mark_update_visibility(nodes[i]);
916 }
917
918 BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
919
920 MEM_SAFE_FREE(nodes);
921
922 if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
923 BKE_mesh_flush_hidden_from_verts(ob->data);
924 }
925
926 SCULPT_tag_update_overlays(C);
927
928 return OPERATOR_FINISHED;
929 }
930
sculpt_face_sets_change_visibility_invoke(bContext * C,wmOperator * op,const wmEvent * event)931 static int sculpt_face_sets_change_visibility_invoke(bContext *C,
932 wmOperator *op,
933 const wmEvent *event)
934 {
935 Object *ob = CTX_data_active_object(C);
936 SculptSession *ss = ob->sculpt;
937
938 /* Update the active vertex and Face Set using the cursor position to avoid relying on the paint
939 * cursor updates. */
940 SculptCursorGeometryInfo sgi;
941 float mouse[2];
942 mouse[0] = event->mval[0];
943 mouse[1] = event->mval[1];
944 SCULPT_vertex_random_access_ensure(ss);
945 SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
946
947 return sculpt_face_sets_change_visibility_exec(C, op);
948 }
949
SCULPT_OT_face_sets_change_visibility(wmOperatorType * ot)950 void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
951 {
952 /* Identifiers. */
953 ot->name = "Face Sets Visibility";
954 ot->idname = "SCULPT_OT_face_set_change_visibility";
955 ot->description = "Change the visibility of the Face Sets of the sculpt";
956
957 /* Api callbacks. */
958 ot->exec = sculpt_face_sets_change_visibility_exec;
959 ot->invoke = sculpt_face_sets_change_visibility_invoke;
960 ot->poll = SCULPT_mode_poll;
961
962 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
963
964 RNA_def_enum(ot->srna,
965 "mode",
966 prop_sculpt_face_sets_change_visibility_types,
967 SCULPT_FACE_SET_VISIBILITY_TOGGLE,
968 "Mode",
969 "");
970 }
971
sculpt_face_sets_randomize_colors_exec(bContext * C,wmOperator * UNUSED (op))972 static int sculpt_face_sets_randomize_colors_exec(bContext *C, wmOperator *UNUSED(op))
973 {
974
975 Object *ob = CTX_data_active_object(C);
976 SculptSession *ss = ob->sculpt;
977
978 /* Dyntopo not supported. */
979 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
980 return OPERATOR_CANCELLED;
981 }
982
983 PBVH *pbvh = ob->sculpt->pbvh;
984 PBVHNode **nodes;
985 int totnode;
986 Mesh *mesh = ob->data;
987
988 mesh->face_sets_color_seed += 1;
989 if (ss->face_sets) {
990 const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
991 0,
992 max_ii(0, ss->totfaces - 1));
993 mesh->face_sets_color_default = ss->face_sets[random_index];
994 }
995 BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
996
997 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
998 for (int i = 0; i < totnode; i++) {
999 BKE_pbvh_node_mark_redraw(nodes[i]);
1000 }
1001
1002 MEM_SAFE_FREE(nodes);
1003
1004 SCULPT_tag_update_overlays(C);
1005
1006 return OPERATOR_FINISHED;
1007 }
1008
SCULPT_OT_face_sets_randomize_colors(wmOperatorType * ot)1009 void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
1010 {
1011 /* Identifiers. */
1012 ot->name = "Randomize Face Sets Colors";
1013 ot->idname = "SCULPT_OT_face_sets_randomize_colors";
1014 ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
1015
1016 /* Api callbacks. */
1017 ot->exec = sculpt_face_sets_randomize_colors_exec;
1018 ot->poll = SCULPT_mode_poll;
1019
1020 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1021 }
1022
1023 typedef enum eSculptFaceSetEditMode {
1024 SCULPT_FACE_SET_EDIT_GROW = 0,
1025 SCULPT_FACE_SET_EDIT_SHRINK = 1,
1026 } eSculptFaceSetEditMode;
1027
1028 static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
1029 {
1030 SCULPT_FACE_SET_EDIT_GROW,
1031 "GROW",
1032 0,
1033 "Grow Face Set",
1034 "Grows the Face Sets boundary by one face based on mesh topology",
1035 },
1036 {
1037 SCULPT_FACE_SET_EDIT_SHRINK,
1038 "SHRINK",
1039 0,
1040 "Shrink Face Set",
1041 "Shrinks the Face Sets boundary by one face based on mesh topology",
1042 },
1043 {0, NULL, 0, NULL, NULL},
1044 };
1045
sculpt_face_set_grow(Object * ob,SculptSession * ss,const int * prev_face_sets,const int active_face_set_id,const bool modify_hidden)1046 static void sculpt_face_set_grow(Object *ob,
1047 SculptSession *ss,
1048 const int *prev_face_sets,
1049 const int active_face_set_id,
1050 const bool modify_hidden)
1051 {
1052 Mesh *mesh = BKE_mesh_from_object(ob);
1053 for (int p = 0; p < mesh->totpoly; p++) {
1054 if (!modify_hidden && prev_face_sets[p] <= 0) {
1055 continue;
1056 }
1057 const MPoly *c_poly = &mesh->mpoly[p];
1058 for (int l = 0; l < c_poly->totloop; l++) {
1059 const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
1060 const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
1061 for (int i = 0; i < vert_map->count; i++) {
1062 const int neighbor_face_index = vert_map->indices[i];
1063 if (neighbor_face_index == p) {
1064 continue;
1065 }
1066 if (abs(prev_face_sets[neighbor_face_index]) == active_face_set_id) {
1067 ss->face_sets[p] = active_face_set_id;
1068 }
1069 }
1070 }
1071 }
1072 }
1073
sculpt_face_set_shrink(Object * ob,SculptSession * ss,const int * prev_face_sets,const int active_face_set_id,const bool modify_hidden)1074 static void sculpt_face_set_shrink(Object *ob,
1075 SculptSession *ss,
1076 const int *prev_face_sets,
1077 const int active_face_set_id,
1078 const bool modify_hidden)
1079 {
1080 Mesh *mesh = BKE_mesh_from_object(ob);
1081 for (int p = 0; p < mesh->totpoly; p++) {
1082 if (!modify_hidden && prev_face_sets[p] <= 0) {
1083 continue;
1084 }
1085 if (abs(prev_face_sets[p]) == active_face_set_id) {
1086 const MPoly *c_poly = &mesh->mpoly[p];
1087 for (int l = 0; l < c_poly->totloop; l++) {
1088 const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
1089 const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
1090 for (int i = 0; i < vert_map->count; i++) {
1091 const int neighbor_face_index = vert_map->indices[i];
1092 if (neighbor_face_index == p) {
1093 continue;
1094 }
1095 if (abs(prev_face_sets[neighbor_face_index]) != active_face_set_id) {
1096 ss->face_sets[p] = prev_face_sets[neighbor_face_index];
1097 }
1098 }
1099 }
1100 }
1101 }
1102 }
1103
sculpt_face_set_apply_edit(Object * ob,const int active_face_set_id,const int mode,const bool modify_hidden)1104 static void sculpt_face_set_apply_edit(Object *ob,
1105 const int active_face_set_id,
1106 const int mode,
1107 const bool modify_hidden)
1108 {
1109 SculptSession *ss = ob->sculpt;
1110
1111 int *prev_face_sets = MEM_dupallocN(ss->face_sets);
1112
1113 switch (mode) {
1114 case SCULPT_FACE_SET_EDIT_GROW:
1115 sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
1116 break;
1117 case SCULPT_FACE_SET_EDIT_SHRINK:
1118 sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden);
1119 break;
1120 }
1121
1122 MEM_SAFE_FREE(prev_face_sets);
1123 }
1124
sculpt_face_set_edit_invoke(bContext * C,wmOperator * op,const wmEvent * event)1125 static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event)
1126 {
1127 Object *ob = CTX_data_active_object(C);
1128 SculptSession *ss = ob->sculpt;
1129 Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
1130
1131 const int mode = RNA_enum_get(op->ptr, "mode");
1132
1133 /* Dyntopo not supported. */
1134 if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
1135 return OPERATOR_CANCELLED;
1136 }
1137
1138 /* Ignore other events to avoid repeated operations. */
1139 if (event->val != KM_PRESS) {
1140 return OPERATOR_CANCELLED;
1141 }
1142
1143 BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false);
1144
1145 /* Update the current active Face Set and Vertex as the operator can be used directly from the
1146 * tool without brush cursor. */
1147 SculptCursorGeometryInfo sgi;
1148 float mouse[2];
1149 mouse[0] = event->mval[0];
1150 mouse[1] = event->mval[1];
1151 SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
1152
1153 PBVH *pbvh = ob->sculpt->pbvh;
1154 PBVHNode **nodes;
1155 int totnode;
1156 BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
1157
1158 if (!nodes) {
1159 return OPERATOR_CANCELLED;
1160 }
1161
1162 SCULPT_undo_push_begin("face set edit");
1163 SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
1164
1165 const int active_face_set = SCULPT_active_face_set_get(ss);
1166 const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden");
1167
1168 sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden);
1169
1170 SCULPT_undo_push_end();
1171
1172 /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
1173 SCULPT_visibility_sync_all_face_sets_to_vertices(ob);
1174
1175 for (int i = 0; i < totnode; i++) {
1176 BKE_pbvh_node_mark_update_visibility(nodes[i]);
1177 }
1178
1179 BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
1180
1181 MEM_SAFE_FREE(nodes);
1182
1183 if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
1184 BKE_mesh_flush_hidden_from_verts(ob->data);
1185 }
1186
1187 SCULPT_tag_update_overlays(C);
1188
1189 return OPERATOR_FINISHED;
1190 }
1191
SCULPT_OT_face_sets_edit(struct wmOperatorType * ot)1192 void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot)
1193 {
1194 /* Identifiers. */
1195 ot->name = "Edit Face Set";
1196 ot->idname = "SCULPT_OT_face_set_edit";
1197 ot->description = "Edits the current active Face Set";
1198
1199 /* Api callbacks. */
1200 ot->invoke = sculpt_face_set_edit_invoke;
1201 ot->poll = SCULPT_mode_poll;
1202
1203 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1204
1205 RNA_def_enum(
1206 ot->srna, "mode", prop_sculpt_face_sets_edit_types, SCULPT_FACE_SET_EDIT_GROW, "Mode", "");
1207 ot->prop = RNA_def_boolean(ot->srna,
1208 "modify_hidden",
1209 true,
1210 "Modify Hidden",
1211 "Apply the edit operation to hidden Face Sets");
1212 }
1213