Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPablo Dobarro <pablodp606@gmail.com>2020-04-03 20:16:49 +0300
committerPablo Dobarro <pablodp606@gmail.com>2020-04-03 20:17:28 +0300
commitcfc8d73546239bf2e66f5ecf677374be4efb61ce (patch)
tree93fa0f828cd6c693119ba71fcff454a90dd35623
parent63922c5056d16cc91c30ec3476a28d523adbdbea (diff)
Cleanup: Move all Face Set functionality to its own file
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt1
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c918
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_face_set.c975
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h49
4 files changed, 1028 insertions, 915 deletions
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index de9e70f8e06..fda9594835d 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -60,6 +60,7 @@ set(SRC
paint_vertex_weight_utils.c
sculpt.c
sculpt_cloth.c
+ sculpt_face_set.c
sculpt_multiplane_scrape.c
sculpt_pose.c
sculpt_undo.c
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index d30dce666c4..490ec336ccc 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -216,7 +216,7 @@ static void SCULPT_active_vertex_normal_get(SculptSession *ss, float normal[3])
/* Sculpt Face Sets and Visibility. */
-static int SCULPT_active_face_set_get(SculptSession *ss)
+int SCULPT_active_face_set_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -232,7 +232,7 @@ static int SCULPT_active_face_set_get(SculptSession *ss)
return SCULPT_FACE_SET_NONE;
}
-static void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
+void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -247,7 +247,7 @@ static void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible
}
}
-static bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -260,7 +260,7 @@ static bool SCULPT_vertex_visible_get(SculptSession *ss, int index)
return true;
}
-static void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
+void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -281,7 +281,7 @@ static void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool
}
}
-static void SCULPT_face_sets_visibility_invert(SculptSession *ss)
+void SCULPT_face_sets_visibility_invert(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -295,7 +295,7 @@ static void SCULPT_face_sets_visibility_invert(SculptSession *ss)
}
}
-static void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
+void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -322,7 +322,7 @@ static void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible)
}
}
-static bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -342,7 +342,7 @@ static bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
return true;
}
-static bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -366,7 +366,7 @@ static bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index
return true;
}
-static void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
+void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
@@ -526,7 +526,7 @@ bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
return false;
}
-static int SCULPT_face_set_next_available_get(SculptSession *ss)
+int SCULPT_face_set_next_available_get(SculptSession *ss)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES:
@@ -3676,155 +3676,6 @@ static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
BKE_pbvh_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
}
-static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float bstrength = ss->cache->bstrength;
-
- PBVHVertexIter vd;
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, data->brush->falloff_shape);
-
- BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
- {
- if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
- MeshElemMap *vert_map = &ss->pmap[vd.index];
- for (int j = 0; j < ss->pmap[vd.index].count; j++) {
- const MPoly *p = &ss->mpoly[vert_map->indices[j]];
-
- float poly_center[3];
- BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], ss->mvert, poly_center);
-
- if (sculpt_brush_test_sq_fn(&test, poly_center)) {
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- tls->thread_id);
-
- if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
- ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
- }
- }
- }
- }
-
- else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
- {
- if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- tls->thread_id);
-
- if (fade > 0.05f) {
- SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
- }
- }
- }
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- float bstrength = ss->cache->bstrength;
-
- PBVHVertexIter vd;
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, data->brush->falloff_shape);
-
- const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
- /* This operations needs a stregth tweak as the relax deformation is too weak by default. */
- if (relax_face_sets) {
- bstrength *= 2.0f;
- }
-
- BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
- {
- if (sculpt_brush_test_sq_fn(&test, vd.co)) {
- if (relax_face_sets != SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- tls->thread_id);
-
- SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 &&
- ss->cache->radial_symmetry_pass == 0) {
- if (ss->cache->invert) {
- /* When inverting the brush, pick the paint face mask ID from the mesh. */
- ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
- }
- else {
- /* By default create a new Face Sets. */
- ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss);
- }
- }
-
- BKE_curvemapping_initialize(brush->curve);
-
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- PBVHParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
- if (ss->cache->alt_smooth) {
- for (int i = 0; i < 4; i++) {
- BKE_pbvh_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
- }
- }
- else {
- BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
- }
-}
static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
const int n,
@@ -6405,7 +6256,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
SCULPT_do_cloth_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_DRAW_FACE_SETS:
- do_draw_face_sets_brush(sd, ob, nodes, totnode);
+ SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
break;
}
@@ -11259,753 +11110,6 @@ static void SCULPT_OT_set_pivot_position(wmOperatorType *ot)
10000.0f);
}
-typedef enum eSculptFaceGroupsCreateModes {
- SCULPT_FACE_SET_MASKED = 0,
- SCULPT_FACE_SET_VISIBLE = 1,
- SCULPT_FACE_SET_ALL = 2,
- SCULPT_FACE_SET_SELECTION = 3,
-} eSculptFaceGroupsCreateModes;
-
-static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
- {
- SCULPT_FACE_SET_MASKED,
- "MASKED",
- 0,
- "Face Set From Masked",
- "Create a new Face Set from the masked faces",
- },
- {
- SCULPT_FACE_SET_VISIBLE,
- "VISIBLE",
- 0,
- "Face Set From Visible",
- "Create a new Face Set from the visible vertices",
- },
- {
- SCULPT_FACE_SET_ALL,
- "ALL",
- 0,
- "Face Set Full Mesh",
- "Create an unique Face Set with all faces in the sculpt",
- },
- {
- SCULPT_FACE_SET_SELECTION,
- "SELECTION",
- 0,
- "Face Set From Edit Mode Selection",
- "Create an Face Set corresponding to the Edit Mode face selection",
- },
- {0, NULL, 0, NULL, NULL},
-};
-
-static int sculpt_face_set_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
- ARegion *region = CTX_wm_region(C);
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
-
- const int mode = RNA_enum_get(op->ptr, "mode");
-
- /* Dyntopo not suported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
-
- const int tot_vert = SCULPT_vertex_count_get(ss);
- float threshold = 0.5f;
-
- PBVH *pbvh = ob->sculpt->pbvh;
- PBVHNode **nodes;
- int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
-
- if (!nodes) {
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_undo_push_begin("face set change");
- SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
-
- const int next_face_set = SCULPT_face_set_next_available_get(ss);
-
- if (mode == SCULPT_FACE_SET_MASKED) {
- for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
- }
- }
- }
-
- if (mode == SCULPT_FACE_SET_VISIBLE) {
- for (int i = 0; i < tot_vert; i++) {
- if (SCULPT_vertex_visible_get(ss, i)) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
- }
- }
- }
-
- if (mode == SCULPT_FACE_SET_ALL) {
- for (int i = 0; i < tot_vert; i++) {
- SCULPT_vertex_face_set_set(ss, i, next_face_set);
- }
- }
-
- if (mode == SCULPT_FACE_SET_SELECTION) {
- Mesh *mesh = ob->data;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- BMIter iter;
- BMFace *f;
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
- ss->face_sets[BM_elem_index_get(f)] = next_face_set;
- }
- }
- BM_mesh_free(bm);
- }
-
- for (int i = 0; i < totnode; i++) {
- BKE_pbvh_node_mark_redraw(nodes[i]);
- }
-
- MEM_SAFE_FREE(nodes);
-
- SCULPT_undo_push_end();
-
- ED_region_tag_redraw(region);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_face_sets_create(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Create Face Set";
- ot->idname = "SCULPT_OT_face_sets_create";
- ot->description = "Create a new Face Set";
-
- /* api callbacks */
- ot->invoke = sculpt_face_set_create_invoke;
- ot->poll = SCULPT_mode_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(
- ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", "");
-}
-
-typedef enum eSculptFaceSetsInitMode {
- SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0,
- SCULPT_FACE_SETS_FROM_MATERIALS = 1,
- SCULPT_FACE_SETS_FROM_NORMALS = 2,
- SCULPT_FACE_SETS_FROM_UV_SEAMS = 3,
- SCULPT_FACE_SETS_FROM_CREASES = 4,
- SCULPT_FACE_SETS_FROM_SHARP_EDGES = 5,
- SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6,
- SCULPT_FACE_SETS_FROM_FACE_MAPS = 7,
-} eSculptFaceSetsInitMode;
-
-static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
- {
- SCULPT_FACE_SETS_FROM_LOOSE_PARTS,
- "LOOSE_PARTS",
- 0,
- "Face Sets From Loose Parts",
- "Create a Face Set per loose part in the mesh",
- },
- {
- SCULPT_FACE_SETS_FROM_MATERIALS,
- "MATERIALS",
- 0,
- "Face Sets From Material Slots",
- "Create a Face Set per Material Slot",
- },
- {
- SCULPT_FACE_SETS_FROM_NORMALS,
- "NORMALS",
- 0,
- "Face Sets From Mesh Normals",
- "Create Face Sets for Faces that have similar normal",
- },
- {
- SCULPT_FACE_SETS_FROM_UV_SEAMS,
- "UV_SEAMS",
- 0,
- "Face Sets From UV Seams",
- "Create Face Sets using UV Seams as boundaries",
- },
- {
- SCULPT_FACE_SETS_FROM_CREASES,
- "CREASES",
- 0,
- "Face Sets From Edge Creases",
- "Create Face Sets using Edge Creases as boundaries",
- },
- {
- SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT,
- "BEVEL_WEIGHT",
- 0,
- "Face Sets From Bevel Weight",
- "Create Face Sets using Bevel Weights as boundaries",
- },
- {
- SCULPT_FACE_SETS_FROM_SHARP_EDGES,
- "SHARP_EDGES",
- 0,
- "Face Sets From Sharp Edges",
- "Create Face Sets using Sharp Edges as boundaries",
- },
- {
- SCULPT_FACE_SETS_FROM_FACE_MAPS,
- "FACE_MAPS",
- 0,
- "Face Sets From Face Maps",
- "Create a Face Set per Face Map",
- },
- {0, NULL, 0, NULL, NULL},
-};
-
-typedef bool (*face_sets_flood_fill_test)(
- BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
-
-static bool 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))
-{
- return true;
-}
-
-static bool sculpt_face_sets_init_normals_test(
- BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
-{
- return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
-}
-
-static bool 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))
-{
- return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
-}
-
-static bool sculpt_face_sets_init_crease_test(
- BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
-{
- return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
-}
-
-static bool sculpt_face_sets_init_bevel_weight_test(
- BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
-{
- return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
-}
-
-static bool 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))
-{
- return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
-}
-
-static void sculpt_face_sets_init_flood_fill(Object *ob,
- face_sets_flood_fill_test test,
- const float threshold)
-{
- SculptSession *ss = ob->sculpt;
- Mesh *mesh = ob->data;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
-
- bool *visited_faces = MEM_callocN(sizeof(bool) * mesh->totpoly, "visited faces");
- const int totfaces = mesh->totpoly;
-
- int *face_sets = ss->face_sets;
-
- BM_mesh_elem_table_init(bm, BM_FACE);
- BM_mesh_elem_table_ensure(bm, BM_FACE);
-
- int next_face_set = 1;
-
- for (int i = 0; i < totfaces; i++) {
- if (!visited_faces[i]) {
- GSQueue *queue;
- queue = BLI_gsqueue_new(sizeof(int));
-
- face_sets[i] = next_face_set;
- visited_faces[i] = true;
- BLI_gsqueue_push(queue, &i);
-
- while (!BLI_gsqueue_is_empty(queue)) {
- int from_f;
- BLI_gsqueue_pop(queue, &from_f);
-
- BMFace *f, *f_neighbor;
- BMEdge *ed;
- BMIter iter_a, iter_b;
-
- f = BM_face_at_index(bm, from_f);
-
- BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
- BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
- if (f_neighbor != f) {
- int neighbor_face_index = BM_elem_index_get(f_neighbor);
- if (!visited_faces[neighbor_face_index]) {
- if (test(bm, f, ed, f_neighbor, threshold)) {
- face_sets[neighbor_face_index] = next_face_set;
- visited_faces[neighbor_face_index] = true;
- BLI_gsqueue_push(queue, &neighbor_face_index);
- }
- }
- }
- }
- }
- }
-
- next_face_set += 1;
-
- BLI_gsqueue_free(queue);
- }
- }
-
- MEM_SAFE_FREE(visited_faces);
-
- BM_mesh_free(bm);
-}
-
-static void sculpt_face_sets_init_loop(Object *ob, const int mode)
-{
- Mesh *mesh = ob->data;
- SculptSession *ss = ob->sculpt;
- BMesh *bm;
- const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
- bm = BM_mesh_create(&allocsize,
- &((struct BMeshCreateParams){
- .use_toolflags = true,
- }));
-
- BM_mesh_bm_from_me(bm,
- mesh,
- (&(struct BMeshFromMeshParams){
- .calc_face_normal = true,
- }));
- BMIter iter;
- BMFace *f;
-
- const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
-
- BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
- if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
- ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
- }
- else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
- if (cd_fmaps_offset != -1) {
- ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
- }
- else {
- ss->face_sets[BM_elem_index_get(f)] = 1;
- }
- }
- }
- BM_mesh_free(bm);
-}
-
-static int sculpt_face_set_init_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
- ARegion *region = CTX_wm_region(C);
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
-
- const int mode = RNA_enum_get(op->ptr, "mode");
-
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
-
- PBVH *pbvh = ob->sculpt->pbvh;
- PBVHNode **nodes;
- int totnode;
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
-
- if (!nodes) {
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_undo_push_begin("face set change");
- SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
-
- const float threshold = RNA_float_get(op->ptr, "threshold");
-
- switch (mode) {
- case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_MATERIALS:
- sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
- break;
- case SCULPT_FACE_SETS_FROM_NORMALS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_UV_SEAMS:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_CREASES:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
- sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
- break;
- case SCULPT_FACE_SETS_FROM_FACE_MAPS:
- sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
- break;
- }
-
- SCULPT_undo_push_end();
-
- /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
- SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
-
- for (int i = 0; i < totnode; i++) {
- BKE_pbvh_node_mark_update_visibility(nodes[i]);
- }
-
- BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
-
- MEM_SAFE_FREE(nodes);
-
- if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
- }
-
- ED_region_tag_redraw(region);
- DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
-
- View3D *v3d = CTX_wm_view3d(C);
- if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- }
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_face_sets_init(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Init Face Sets";
- ot->idname = "SCULPT_OT_face_sets_init";
- ot->description = "Initializes all Face Sets in the mesh";
-
- /* api callbacks */
- ot->invoke = sculpt_face_set_init_invoke;
- ot->poll = SCULPT_mode_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(
- ot->srna, "mode", prop_sculpt_face_sets_init_types, SCULPT_FACE_SET_MASKED, "Mode", "");
- RNA_def_float(
- ot->srna,
- "threshold",
- 0.5f,
- 0.0f,
- 1.0f,
- "Threshold",
- "Minimum value to consider a certain atribute a boundary when creating the Face Sets",
- 0.0f,
- 1.0f);
-}
-
-typedef enum eSculptFaceGroupVisibilityModes {
- SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0,
- SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1,
- SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
- SCULPT_FACE_SET_VISIBILITY_INVERT = 3,
- SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4,
-} eSculptFaceGroupVisibilityModes;
-
-static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
- {
- SCULPT_FACE_SET_VISIBILITY_TOGGLE,
- "TOGGLE",
- 0,
- "Toggle Visibility",
- "Hide all Face Sets except for the active one",
- },
- {
- SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE,
- "SHOW_ACTIVE",
- 0,
- "Show Active Face Set",
- "Show Active Face Set",
- },
- {
- SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE,
- "HIDE_ACTIVE",
- 0,
- "Hide Active Face Sets",
- "Hide Active Face Sets",
- },
- {
- SCULPT_FACE_SET_VISIBILITY_INVERT,
- "INVERT",
- 0,
- "Invert Face Set Visibility",
- "Invert Face Set Visibility",
- },
- {
- SCULPT_FACE_SET_VISIBILITY_SHOW_ALL,
- "SHOW_ALL",
- 0,
- "Show All Face Sets",
- "Show All Face Sets",
- },
- {0, NULL, 0, NULL, NULL},
-};
-
-static int sculpt_face_sets_change_visibility_invoke(bContext *C,
- wmOperator *op,
- const wmEvent *UNUSED(event))
-{
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
- ARegion *region = CTX_wm_region(C);
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
-
- /* Dyntopo not supported. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
- return OPERATOR_CANCELLED;
- }
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
-
- const int tot_vert = SCULPT_vertex_count_get(ss);
- const int mode = RNA_enum_get(op->ptr, "mode");
- const int active_face_set = SCULPT_active_face_set_get(ss);
-
- SCULPT_undo_push_begin("Hide area");
-
- PBVH *pbvh = ob->sculpt->pbvh;
- PBVHNode **nodes;
- int totnode;
-
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
-
- if (totnode == 0) {
- MEM_SAFE_FREE(nodes);
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
-
- if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) {
- bool hidden_vertex = false;
-
- /* This can fail with regular meshes with non-manifold geometry as the visibility state can't
- * be synced from face sets to non-manifold vertices. */
- if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
- for (int i = 0; i < tot_vert; i++) {
- if (!SCULPT_vertex_visible_get(ss, i)) {
- hidden_vertex = true;
- break;
- }
- }
- }
-
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] <= 0) {
- hidden_vertex = true;
- break;
- }
- }
-
- if (hidden_vertex) {
- SCULPT_face_sets_visibility_all_set(ss, true);
- }
- else {
- SCULPT_face_sets_visibility_all_set(ss, false);
- SCULPT_face_set_visibility_set(ss, active_face_set, true);
- }
- }
-
- if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
- SCULPT_face_sets_visibility_all_set(ss, true);
- }
-
- if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
- SCULPT_face_sets_visibility_all_set(ss, false);
- SCULPT_face_set_visibility_set(ss, active_face_set, true);
- for (int i = 0; i < tot_vert; i++) {
- SCULPT_vertex_visible_set(ss,
- i,
- SCULPT_vertex_visible_get(ss, i) &&
- SCULPT_vertex_has_face_set(ss, i, active_face_set));
- }
- }
-
- if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
- SCULPT_face_set_visibility_set(ss, active_face_set, false);
- }
-
- if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
- SCULPT_face_sets_visibility_invert(ss);
- }
-
- /* For modes that use the cursor active vertex, update the rotation origin for viewport
- * navigation. */
- if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
- UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
- float location[3];
- copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
- mul_m4_v3(ob->obmat, location);
- copy_v3_v3(ups->average_stroke_accum, location);
- ups->average_stroke_counter = 1;
- ups->last_stroke_valid = true;
- }
-
- /* Sync face sets visibility and vertex visibility. */
- SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
-
- SCULPT_undo_push_end();
-
- for (int i = 0; i < totnode; i++) {
- BKE_pbvh_node_mark_update_visibility(nodes[i]);
- }
-
- BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
-
- MEM_SAFE_FREE(nodes);
-
- if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
- BKE_mesh_flush_hidden_from_verts(ob->data);
- }
-
- ED_region_tag_redraw(region);
- DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
-
- View3D *v3d = CTX_wm_view3d(C);
- if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- }
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Face Sets Visibility";
- ot->idname = "SCULPT_OT_face_set_change_visibility";
- ot->description = "Change the visibility of the Face Sets of the sculpt";
-
- /* Api callbacks. */
- ot->invoke = sculpt_face_sets_change_visibility_invoke;
- ot->poll = SCULPT_mode_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- RNA_def_enum(ot->srna,
- "mode",
- prop_sculpt_face_sets_change_visibility_types,
- SCULPT_FACE_SET_VISIBILITY_TOGGLE,
- "Mode",
- "");
-}
-
-static int sculpt_face_sets_randomize_colors_invoke(bContext *C,
- wmOperator *UNUSED(op),
- const wmEvent *UNUSED(event))
-{
-
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
- ARegion *region = CTX_wm_region(C);
-
- /* Dyntopo and Multires not supported for now. */
- if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
- return OPERATOR_CANCELLED;
- }
-
- PBVH *pbvh = ob->sculpt->pbvh;
- PBVHNode **nodes;
- int totnode;
- Mesh *mesh = ob->data;
-
- mesh->face_sets_color_seed += 1;
- if (ss->face_sets) {
- const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
- 0,
- max_ii(0, ss->totfaces - 1));
- mesh->face_sets_color_default = ss->face_sets[random_index];
- }
- BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
-
- BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
- for (int i = 0; i < totnode; i++) {
- BKE_pbvh_node_mark_redraw(nodes[i]);
- }
-
- MEM_SAFE_FREE(nodes);
-
- View3D *v3d = CTX_wm_view3d(C);
- if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
- DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
- }
-
- ED_region_tag_redraw(region);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Randomize Face Sets Colors";
- ot->idname = "SCULPT_OT_face_sets_randomize_colors";
- ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
-
- /* Api callbacks. */
- ot->invoke = sculpt_face_sets_randomize_colors_invoke;
- ot->poll = SCULPT_mode_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
void ED_operatortypes_sculpt(void)
{
diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.c b/source/blender/editors/sculpt_paint/sculpt_face_set.c
new file mode 100644
index 00000000000..a1bf2f7d7bd
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_face_set.c
@@ -0,0 +1,975 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2020 Blender Foundation.
+ * All rights reserved.
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_math.h"
+#include "BLI_hash.h"
+#include "BLI_task.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_ccg.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_customdata.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_multires.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_scene.h"
+
+#include "DEG_depsgraph.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "ED_view3d.h"
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "bmesh.h"
+
+#include <math.h>
+#include <stdlib.h>
+
+/* Draw Face Sets Brush. */
+
+static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ MeshElemMap *vert_map = &ss->pmap[vd.index];
+ for (int j = 0; j < ss->pmap[vd.index].count; j++) {
+ const MPoly *p = &ss->mpoly[vert_map->indices[j]];
+
+ float poly_center[3];
+ BKE_mesh_calc_poly_center(p, &ss->mloop[p->loopstart], ss->mvert, poly_center);
+
+ if (sculpt_brush_test_sq_fn(&test, poly_center)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ tls->thread_id);
+
+ if (fade > 0.05f && ss->face_sets[vert_map->indices[j]] > 0) {
+ ss->face_sets[vert_map->indices[j]] = abs(ss->cache->paint_face_set);
+ }
+ }
+ }
+ }
+
+ else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ tls->thread_id);
+
+ if (fade > 0.05f) {
+ SCULPT_vertex_face_set_set(ss, vd.index, ss->cache->paint_face_set);
+ }
+ }
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+
+ const bool relax_face_sets = !(ss->cache->iteration_count % 3 == 0);
+ /* This operations needs a stregth tweak as the relax deformation is too weak by default. */
+ if (relax_face_sets) {
+ bstrength *= 2.0f;
+ }
+
+ BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+ {
+ if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+ if (relax_face_sets != SCULPT_vertex_has_unique_face_set(ss, vd.index)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ tls->thread_id);
+
+ SCULPT_relax_vertex(ss, &vd, fade * bstrength, relax_face_sets, vd.co);
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (ss->cache->first_time && ss->cache->mirror_symmetry_pass == 0 &&
+ ss->cache->radial_symmetry_pass == 0) {
+ if (ss->cache->invert) {
+ /* When inverting the brush, pick the paint face mask ID from the mesh. */
+ ss->cache->paint_face_set = SCULPT_active_face_set_get(ss);
+ }
+ else {
+ /* By default create a new Face Sets. */
+ ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss);
+ }
+ }
+
+ BKE_curvemapping_initialize(brush->curve);
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ PBVHParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ if (ss->cache->alt_smooth) {
+ for (int i = 0; i < 4; i++) {
+ BKE_pbvh_parallel_range(0, totnode, &data, do_relax_face_sets_brush_task_cb_ex, &settings);
+ }
+ }
+ else {
+ BKE_pbvh_parallel_range(0, totnode, &data, do_draw_face_sets_brush_task_cb_ex, &settings);
+ }
+}
+
+
+/* Face Sets Operators */
+
+typedef enum eSculptFaceGroupsCreateModes {
+ SCULPT_FACE_SET_MASKED = 0,
+ SCULPT_FACE_SET_VISIBLE = 1,
+ SCULPT_FACE_SET_ALL = 2,
+ SCULPT_FACE_SET_SELECTION = 3,
+} eSculptFaceGroupsCreateModes;
+
+static EnumPropertyItem prop_sculpt_face_set_create_types[] = {
+ {
+ SCULPT_FACE_SET_MASKED,
+ "MASKED",
+ 0,
+ "Face Set From Masked",
+ "Create a new Face Set from the masked faces",
+ },
+ {
+ SCULPT_FACE_SET_VISIBLE,
+ "VISIBLE",
+ 0,
+ "Face Set From Visible",
+ "Create a new Face Set from the visible vertices",
+ },
+ {
+ SCULPT_FACE_SET_ALL,
+ "ALL",
+ 0,
+ "Face Set Full Mesh",
+ "Create an unique Face Set with all faces in the sculpt",
+ },
+ {
+ SCULPT_FACE_SET_SELECTION,
+ "SELECTION",
+ 0,
+ "Face Set From Edit Mode Selection",
+ "Create an Face Set corresponding to the Edit Mode face selection",
+ },
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sculpt_face_set_create_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+
+ /* Dyntopo not suported. */
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, mode == SCULPT_FACE_SET_MASKED);
+
+ const int tot_vert = SCULPT_vertex_count_get(ss);
+ float threshold = 0.5f;
+
+ PBVH *pbvh = ob->sculpt->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+
+ if (!nodes) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_undo_push_begin("face set change");
+ SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
+
+ const int next_face_set = SCULPT_face_set_next_available_get(ss);
+
+ if (mode == SCULPT_FACE_SET_MASKED) {
+ for (int i = 0; i < tot_vert; i++) {
+ if (SCULPT_vertex_mask_get(ss, i) >= threshold && SCULPT_vertex_visible_get(ss, i)) {
+ SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ }
+ }
+ }
+
+ if (mode == SCULPT_FACE_SET_VISIBLE) {
+ for (int i = 0; i < tot_vert; i++) {
+ if (SCULPT_vertex_visible_get(ss, i)) {
+ SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ }
+ }
+ }
+
+ if (mode == SCULPT_FACE_SET_ALL) {
+ for (int i = 0; i < tot_vert; i++) {
+ SCULPT_vertex_face_set_set(ss, i, next_face_set);
+ }
+ }
+
+ if (mode == SCULPT_FACE_SET_SELECTION) {
+ Mesh *mesh = ob->data;
+ BMesh *bm;
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+
+ BMIter iter;
+ BMFace *f;
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (BM_elem_flag_test(f, BM_ELEM_SELECT)) {
+ ss->face_sets[BM_elem_index_get(f)] = next_face_set;
+ }
+ }
+ BM_mesh_free(bm);
+ }
+
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_redraw(nodes[i]);
+ }
+
+ MEM_SAFE_FREE(nodes);
+
+ SCULPT_undo_push_end();
+
+ ED_region_tag_redraw(region);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCULPT_OT_face_sets_create(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Create Face Set";
+ ot->idname = "SCULPT_OT_face_sets_create";
+ ot->description = "Create a new Face Set";
+
+ /* api callbacks */
+ ot->invoke = sculpt_face_set_create_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(
+ ot->srna, "mode", prop_sculpt_face_set_create_types, SCULPT_FACE_SET_MASKED, "Mode", "");
+}
+
+typedef enum eSculptFaceSetsInitMode {
+ SCULPT_FACE_SETS_FROM_LOOSE_PARTS = 0,
+ SCULPT_FACE_SETS_FROM_MATERIALS = 1,
+ SCULPT_FACE_SETS_FROM_NORMALS = 2,
+ SCULPT_FACE_SETS_FROM_UV_SEAMS = 3,
+ SCULPT_FACE_SETS_FROM_CREASES = 4,
+ SCULPT_FACE_SETS_FROM_SHARP_EDGES = 5,
+ SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT = 6,
+ SCULPT_FACE_SETS_FROM_FACE_MAPS = 7,
+} eSculptFaceSetsInitMode;
+
+static EnumPropertyItem prop_sculpt_face_sets_init_types[] = {
+ {
+ SCULPT_FACE_SETS_FROM_LOOSE_PARTS,
+ "LOOSE_PARTS",
+ 0,
+ "Face Sets From Loose Parts",
+ "Create a Face Set per loose part in the mesh",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_MATERIALS,
+ "MATERIALS",
+ 0,
+ "Face Sets From Material Slots",
+ "Create a Face Set per Material Slot",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_NORMALS,
+ "NORMALS",
+ 0,
+ "Face Sets From Mesh Normals",
+ "Create Face Sets for Faces that have similar normal",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_UV_SEAMS,
+ "UV_SEAMS",
+ 0,
+ "Face Sets From UV Seams",
+ "Create Face Sets using UV Seams as boundaries",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_CREASES,
+ "CREASES",
+ 0,
+ "Face Sets From Edge Creases",
+ "Create Face Sets using Edge Creases as boundaries",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT,
+ "BEVEL_WEIGHT",
+ 0,
+ "Face Sets From Bevel Weight",
+ "Create Face Sets using Bevel Weights as boundaries",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_SHARP_EDGES,
+ "SHARP_EDGES",
+ 0,
+ "Face Sets From Sharp Edges",
+ "Create Face Sets using Sharp Edges as boundaries",
+ },
+ {
+ SCULPT_FACE_SETS_FROM_FACE_MAPS,
+ "FACE_MAPS",
+ 0,
+ "Face Sets From Face Maps",
+ "Create a Face Set per Face Map",
+ },
+ {0, NULL, 0, NULL, NULL},
+};
+
+typedef bool (*face_sets_flood_fill_test)(
+ BMesh *bm, BMFace *from_f, BMEdge *from_e, BMFace *to_f, const float threshold);
+
+static bool 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))
+{
+ return true;
+}
+
+static bool sculpt_face_sets_init_normals_test(
+ BMesh *UNUSED(bm), BMFace *from_f, BMEdge *UNUSED(from_e), BMFace *to_f, const float threshold)
+{
+ return fabsf(dot_v3v3(from_f->no, to_f->no)) > threshold;
+}
+
+static bool 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))
+{
+ return !BM_elem_flag_test(from_e, BM_ELEM_SEAM);
+}
+
+static bool sculpt_face_sets_init_crease_test(
+ BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
+{
+ return BM_elem_float_data_get(&bm->edata, from_e, CD_CREASE) < threshold;
+}
+
+static bool sculpt_face_sets_init_bevel_weight_test(
+ BMesh *bm, BMFace *UNUSED(from_f), BMEdge *from_e, BMFace *UNUSED(to_f), const float threshold)
+{
+ return BM_elem_float_data_get(&bm->edata, from_e, CD_BWEIGHT) < threshold;
+}
+
+static bool 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))
+{
+ return BM_elem_flag_test(from_e, BM_ELEM_SMOOTH);
+}
+
+static void sculpt_face_sets_init_flood_fill(Object *ob,
+ face_sets_flood_fill_test test,
+ const float threshold)
+{
+ SculptSession *ss = ob->sculpt;
+ Mesh *mesh = ob->data;
+ BMesh *bm;
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+
+ bool *visited_faces = MEM_callocN(sizeof(bool) * mesh->totpoly, "visited faces");
+ const int totfaces = mesh->totpoly;
+
+ int *face_sets = ss->face_sets;
+
+ BM_mesh_elem_table_init(bm, BM_FACE);
+ BM_mesh_elem_table_ensure(bm, BM_FACE);
+
+ int next_face_set = 1;
+
+ for (int i = 0; i < totfaces; i++) {
+ if (!visited_faces[i]) {
+ GSQueue *queue;
+ queue = BLI_gsqueue_new(sizeof(int));
+
+ face_sets[i] = next_face_set;
+ visited_faces[i] = true;
+ BLI_gsqueue_push(queue, &i);
+
+ while (!BLI_gsqueue_is_empty(queue)) {
+ int from_f;
+ BLI_gsqueue_pop(queue, &from_f);
+
+ BMFace *f, *f_neighbor;
+ BMEdge *ed;
+ BMIter iter_a, iter_b;
+
+ f = BM_face_at_index(bm, from_f);
+
+ BM_ITER_ELEM (ed, &iter_a, f, BM_EDGES_OF_FACE) {
+ BM_ITER_ELEM (f_neighbor, &iter_b, ed, BM_FACES_OF_EDGE) {
+ if (f_neighbor != f) {
+ int neighbor_face_index = BM_elem_index_get(f_neighbor);
+ if (!visited_faces[neighbor_face_index]) {
+ if (test(bm, f, ed, f_neighbor, threshold)) {
+ face_sets[neighbor_face_index] = next_face_set;
+ visited_faces[neighbor_face_index] = true;
+ BLI_gsqueue_push(queue, &neighbor_face_index);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ next_face_set += 1;
+
+ BLI_gsqueue_free(queue);
+ }
+ }
+
+ MEM_SAFE_FREE(visited_faces);
+
+ BM_mesh_free(bm);
+}
+
+static void sculpt_face_sets_init_loop(Object *ob, const int mode)
+{
+ Mesh *mesh = ob->data;
+ SculptSession *ss = ob->sculpt;
+ BMesh *bm;
+ const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh);
+ bm = BM_mesh_create(&allocsize,
+ &((struct BMeshCreateParams){
+ .use_toolflags = true,
+ }));
+
+ BM_mesh_bm_from_me(bm,
+ mesh,
+ (&(struct BMeshFromMeshParams){
+ .calc_face_normal = true,
+ }));
+ BMIter iter;
+ BMFace *f;
+
+ const int cd_fmaps_offset = CustomData_get_offset(&bm->pdata, CD_FACEMAP);
+
+ BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
+ if (mode == SCULPT_FACE_SETS_FROM_MATERIALS) {
+ ss->face_sets[BM_elem_index_get(f)] = (int)(f->mat_nr + 1);
+ }
+ else if (mode == SCULPT_FACE_SETS_FROM_FACE_MAPS) {
+ if (cd_fmaps_offset != -1) {
+ ss->face_sets[BM_elem_index_get(f)] = BM_ELEM_CD_GET_INT(f, cd_fmaps_offset) + 2;
+ }
+ else {
+ ss->face_sets[BM_elem_index_get(f)] = 1;
+ }
+ }
+ }
+ BM_mesh_free(bm);
+}
+
+static int sculpt_face_set_init_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+
+ const int mode = RNA_enum_get(op->ptr, "mode");
+
+ /* Dyntopo not supported. */
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false);
+
+ PBVH *pbvh = ob->sculpt->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+
+ if (!nodes) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_undo_push_begin("face set change");
+ SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
+
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+
+ switch (mode) {
+ case SCULPT_FACE_SETS_FROM_LOOSE_PARTS:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_loose_parts_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_MATERIALS:
+ sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_MATERIALS);
+ break;
+ case SCULPT_FACE_SETS_FROM_NORMALS:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_normals_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_UV_SEAMS:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_uv_seams_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_CREASES:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_crease_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_SHARP_EDGES:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_sharp_edges_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_BEVEL_WEIGHT:
+ sculpt_face_sets_init_flood_fill(ob, sculpt_face_sets_init_bevel_weight_test, threshold);
+ break;
+ case SCULPT_FACE_SETS_FROM_FACE_MAPS:
+ sculpt_face_sets_init_loop(ob, SCULPT_FACE_SETS_FROM_FACE_MAPS);
+ break;
+ }
+
+ SCULPT_undo_push_end();
+
+ /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */
+ SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
+
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update_visibility(nodes[i]);
+ }
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
+
+ MEM_SAFE_FREE(nodes);
+
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
+ BKE_mesh_flush_hidden_from_verts(ob->data);
+ }
+
+ ED_region_tag_redraw(region);
+ DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+
+ View3D *v3d = CTX_wm_view3d(C);
+ if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void SCULPT_OT_face_sets_init(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Init Face Sets";
+ ot->idname = "SCULPT_OT_face_sets_init";
+ ot->description = "Initializes all Face Sets in the mesh";
+
+ /* api callbacks */
+ ot->invoke = sculpt_face_set_init_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(
+ ot->srna, "mode", prop_sculpt_face_sets_init_types, SCULPT_FACE_SET_MASKED, "Mode", "");
+ RNA_def_float(
+ ot->srna,
+ "threshold",
+ 0.5f,
+ 0.0f,
+ 1.0f,
+ "Threshold",
+ "Minimum value to consider a certain atribute a boundary when creating the Face Sets",
+ 0.0f,
+ 1.0f);
+}
+
+typedef enum eSculptFaceGroupVisibilityModes {
+ SCULPT_FACE_SET_VISIBILITY_TOGGLE = 0,
+ SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE = 1,
+ SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE = 2,
+ SCULPT_FACE_SET_VISIBILITY_INVERT = 3,
+ SCULPT_FACE_SET_VISIBILITY_SHOW_ALL = 4,
+} eSculptFaceGroupVisibilityModes;
+
+static EnumPropertyItem prop_sculpt_face_sets_change_visibility_types[] = {
+ {
+ SCULPT_FACE_SET_VISIBILITY_TOGGLE,
+ "TOGGLE",
+ 0,
+ "Toggle Visibility",
+ "Hide all Face Sets except for the active one",
+ },
+ {
+ SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE,
+ "SHOW_ACTIVE",
+ 0,
+ "Show Active Face Set",
+ "Show Active Face Set",
+ },
+ {
+ SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE,
+ "HIDE_ACTIVE",
+ 0,
+ "Hide Active Face Sets",
+ "Hide Active Face Sets",
+ },
+ {
+ SCULPT_FACE_SET_VISIBILITY_INVERT,
+ "INVERT",
+ 0,
+ "Invert Face Set Visibility",
+ "Invert Face Set Visibility",
+ },
+ {
+ SCULPT_FACE_SET_VISIBILITY_SHOW_ALL,
+ "SHOW_ALL",
+ 0,
+ "Show All Face Sets",
+ "Show All Face Sets",
+ },
+ {0, NULL, 0, NULL, NULL},
+};
+
+static int sculpt_face_sets_change_visibility_invoke(bContext *C,
+ wmOperator *op,
+ const wmEvent *UNUSED(event))
+{
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ ARegion *region = CTX_wm_region(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+
+ /* Dyntopo not supported. */
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true);
+
+ const int tot_vert = SCULPT_vertex_count_get(ss);
+ const int mode = RNA_enum_get(op->ptr, "mode");
+ const int active_face_set = SCULPT_active_face_set_get(ss);
+
+ SCULPT_undo_push_begin("Hide area");
+
+ PBVH *pbvh = ob->sculpt->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+
+ if (totnode == 0) {
+ MEM_SAFE_FREE(nodes);
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
+
+ if (mode == SCULPT_FACE_SET_VISIBILITY_TOGGLE) {
+ bool hidden_vertex = false;
+
+ /* This can fail with regular meshes with non-manifold geometry as the visibility state can't
+ * be synced from face sets to non-manifold vertices. */
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ for (int i = 0; i < tot_vert; i++) {
+ if (!SCULPT_vertex_visible_get(ss, i)) {
+ hidden_vertex = true;
+ break;
+ }
+ }
+ }
+
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] <= 0) {
+ hidden_vertex = true;
+ break;
+ }
+ }
+
+ if (hidden_vertex) {
+ SCULPT_face_sets_visibility_all_set(ss, true);
+ }
+ else {
+ SCULPT_face_sets_visibility_all_set(ss, false);
+ SCULPT_face_set_visibility_set(ss, active_face_set, true);
+ }
+ }
+
+ if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ALL) {
+ SCULPT_face_sets_visibility_all_set(ss, true);
+ }
+
+ if (mode == SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE) {
+ SCULPT_face_sets_visibility_all_set(ss, false);
+ SCULPT_face_set_visibility_set(ss, active_face_set, true);
+ for (int i = 0; i < tot_vert; i++) {
+ SCULPT_vertex_visible_set(ss,
+ i,
+ SCULPT_vertex_visible_get(ss, i) &&
+ SCULPT_vertex_has_face_set(ss, i, active_face_set));
+ }
+ }
+
+ if (mode == SCULPT_FACE_SET_VISIBILITY_HIDE_ACTIVE) {
+ SCULPT_face_set_visibility_set(ss, active_face_set, false);
+ }
+
+ if (mode == SCULPT_FACE_SET_VISIBILITY_INVERT) {
+ SCULPT_face_sets_visibility_invert(ss);
+ }
+
+ /* For modes that use the cursor active vertex, update the rotation origin for viewport
+ * navigation. */
+ if (ELEM(mode, SCULPT_FACE_SET_VISIBILITY_TOGGLE, SCULPT_FACE_SET_VISIBILITY_SHOW_ACTIVE)) {
+ UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings;
+ float location[3];
+ copy_v3_v3(location, SCULPT_active_vertex_co_get(ss));
+ mul_m4_v3(ob->obmat, location);
+ copy_v3_v3(ups->average_stroke_accum, location);
+ ups->average_stroke_counter = 1;
+ ups->last_stroke_valid = true;
+ }
+
+ /* Sync face sets visibility and vertex visibility. */
+ SCULPT_visibility_sync_all_face_sets_to_vertices(ss);
+
+ SCULPT_undo_push_end();
+
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_update_visibility(nodes[i]);
+ }
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility);
+
+ MEM_SAFE_FREE(nodes);
+
+ if (BKE_pbvh_type(pbvh) == PBVH_FACES) {
+ BKE_mesh_flush_hidden_from_verts(ob->data);
+ }
+
+ ED_region_tag_redraw(region);
+ DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+
+ View3D *v3d = CTX_wm_view3d(C);
+ if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+ return OPERATOR_FINISHED;
+}
+
+void SCULPT_OT_face_sets_change_visibility(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Face Sets Visibility";
+ ot->idname = "SCULPT_OT_face_set_change_visibility";
+ ot->description = "Change the visibility of the Face Sets of the sculpt";
+
+ /* Api callbacks. */
+ ot->invoke = sculpt_face_sets_change_visibility_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_enum(ot->srna,
+ "mode",
+ prop_sculpt_face_sets_change_visibility_types,
+ SCULPT_FACE_SET_VISIBILITY_TOGGLE,
+ "Mode",
+ "");
+}
+
+static int sculpt_face_sets_randomize_colors_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(event))
+{
+
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+ ARegion *region = CTX_wm_region(C);
+
+ /* Dyntopo and Multires not supported for now. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
+ PBVH *pbvh = ob->sculpt->pbvh;
+ PBVHNode **nodes;
+ int totnode;
+ Mesh *mesh = ob->data;
+
+ mesh->face_sets_color_seed += 1;
+ if (ss->face_sets) {
+ const int random_index = clamp_i(ss->totfaces * BLI_hash_int_01(mesh->face_sets_color_seed),
+ 0,
+ max_ii(0, ss->totfaces - 1));
+ mesh->face_sets_color_default = ss->face_sets[random_index];
+ }
+ BKE_pbvh_face_sets_color_set(pbvh, mesh->face_sets_color_seed, mesh->face_sets_color_default);
+
+ BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode);
+ for (int i = 0; i < totnode; i++) {
+ BKE_pbvh_node_mark_redraw(nodes[i]);
+ }
+
+ MEM_SAFE_FREE(nodes);
+
+ View3D *v3d = CTX_wm_view3d(C);
+ if (!BKE_sculptsession_use_pbvh_draw(ob, v3d)) {
+ DEG_id_tag_update(&ob->id, ID_RECALC_SHADING);
+ }
+
+ ED_region_tag_redraw(region);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Randomize Face Sets Colors";
+ ot->idname = "SCULPT_OT_face_sets_randomize_colors";
+ ot->description = "Generates a new set of random colors to render the Face Sets in the viewport";
+
+ /* Api callbacks. */
+ ot->invoke = sculpt_face_sets_randomize_colors_invoke;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 60b51ce17fd..ba14c9ff011 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -128,6 +128,33 @@ void SCULPT_vertex_neighbors_get(struct SculptSession *ss,
int SCULPT_active_vertex_get(SculptSession *ss);
const float *SCULPT_active_vertex_co_get(SculptSession *ss);
+/* Sculpt Visibility API */
+
+void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible);
+bool SCULPT_vertex_visible_get(SculptSession *ss, int index);
+
+void SCULPT_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss);
+void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
+
+/* Face Sets API */
+
+int SCULPT_active_face_set_get(SculptSession *ss);
+int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
+void SCULPT_vertex_face_set_set(SculptSession *ss, int index, int face_set);
+
+bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
+bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
+
+int SCULPT_face_set_next_available_get(SculptSession *ss);
+
+void SCULPT_face_set_visibility_set(SculptSession *ss, int face_set, bool visible);
+bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index);
+bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index);
+
+void SCULPT_face_sets_visibility_invert(SculptSession *ss);
+void SCULPT_face_sets_visibility_all_set(SculptSession *ss, bool visible);
+
+
/* Sculpt Original Data */
typedef struct {
struct BMLog *bm_log;
@@ -146,11 +173,6 @@ typedef struct {
void SCULPT_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node);
void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter);
-/* Face Sets */
-int SCULPT_vertex_face_set_get(SculptSession *ss, int index);
-bool SCULPT_vertex_has_face_set(SculptSession *ss, int index, int face_set);
-bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index);
-
/* Dynamic topology */
void sculpt_pbvh_clear(Object *ob);
void sculpt_dyntopo_node_layers_add(struct SculptSession *ss);
@@ -274,6 +296,8 @@ void SCULPT_multiplane_scrape_preview_draw(const uint gpuattr,
SculptSession *ss,
const float outline_col[3],
const float outline_alpha);
+/* Draw Face Sets Brush. */
+void SCULPT_do_draw_face_sets_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode);
/* Slide/Relax */
void SCULPT_relax_vertex(struct SculptSession *ss,
@@ -282,9 +306,8 @@ void SCULPT_relax_vertex(struct SculptSession *ss,
bool filter_boundary_face_sets,
float *r_final_pos);
-/* Sculpt Visibility API */
-void SCULPT_visibility_sync_all_face_sets_to_vertices(struct SculptSession *ss);
-void SCULPT_visibility_sync_all_vertex_to_face_sets(struct SculptSession *ss);
+
+
/* Undo */
@@ -737,4 +760,14 @@ bool SCULPT_get_redraw_rect(struct ARegion *region,
Object *ob,
rcti *rect);
+
+/* Operators */
+
+/* Face Sets */
+void SCULPT_OT_face_sets_randomize_colors(struct wmOperatorType *ot);
+void SCULPT_OT_face_sets_change_visibility(struct wmOperatorType *ot);
+void SCULPT_OT_face_sets_init(struct wmOperatorType *ot);
+void SCULPT_OT_face_sets_create(struct wmOperatorType *ot);
+
+
#endif