Sculpt: Face Set Edit Operator

This operator performs an edit operation in the active face set defined
by the cursor position and updates the visibility. For now, it has a
Grow and Shrink operations, similar to Select More/Less in edit mode or
to the mask filter Grow/Shrink modes. More operations can be added in
the future.

In multires, this updates the visibility of an entire face from the base
mesh at once, which makes it very convenient to edit the visible area
without manipulating the face set directly.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D7367
This commit is contained in:
Pablo Dobarro 2020-06-04 00:23:29 +02:00
parent 77789a1904
commit cb9de95d61
Notes: blender-bot 2023-02-14 03:29:37 +01:00
Referenced by commit eb2c26bd38, Fix use of operator flag as boolean
6 changed files with 183 additions and 0 deletions

View File

@ -4318,6 +4318,10 @@ def km_sculpt(params):
{"properties": [("mode", 'SHOW_ALL')]}),
("sculpt.mask_expand", {"type": 'W', "value": 'PRESS', "shift": True},
{"properties": [("use_normals", False), ("keep_previous_mask", False), ("invert", False), ("smooth_iterations", 0), ("create_face_set", True)]}),
("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True},
{"properties": [("mode", "GROW")]}),
("sculpt.face_set_edit", {"type": 'W', "value": 'PRESS', "ctrl": True, "alt": True},
{"properties": [("mode", "SHRINK")]}),
# Subdivision levels
*_template_items_object_subdivision_set(),
("object.subdivision_set", {"type": 'PAGE_UP', "value": 'PRESS'},

View File

@ -3126,6 +3126,14 @@ class VIEW3D_MT_face_sets(Menu):
layout.separator()
op = layout.operator("sculpt.face_set_edit", text='Grow Face Set')
op.mode = 'GROW'
op = layout.operator("sculpt.face_set_edit", text='Shrink Face Set')
op.mode = 'SHRINK'
layout.separator()
op = layout.operator("sculpt.face_set_change_visibility", text='Invert Visible Face Sets')
op.mode = 'INVERT'

View File

@ -315,6 +315,8 @@ typedef struct SculptSession {
/* Mesh Face Sets */
/* Total number of polys of the base mesh. */
int totfaces;
/* Face sets store its visibility in the sign of the integer, using the absolute value as the
* Face Set ID. Positive IDs are visible, negative IDs are hidden. */
int *face_sets;
/* BMesh for dynamic topology sculpting */

View File

@ -7907,4 +7907,5 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors);
WM_operatortype_append(SCULPT_OT_face_sets_init);
WM_operatortype_append(SCULPT_OT_cloth_filter);
WM_operatortype_append(SCULPT_OT_face_sets_edit);
}

View File

@ -978,3 +978,170 @@ void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
typedef enum eSculptFaceSetEditMode {
SCULPT_FACE_SET_EDIT_GROW = 0,
SCULPT_FACE_SET_EDIT_SHRINK = 1,
} eSculptFaceSetEditMode;
static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = {
{
SCULPT_FACE_SET_EDIT_GROW,
"GROW",
0,
"Grow Face Set",
"Grows the Face Sets boundary by one face based on mesh topology",
},
{
SCULPT_FACE_SET_EDIT_SHRINK,
"SHRINK",
0,
"Shrink Face Set",
"Shrinks the Face Sets boundary by one face based on mesh topology",
},
{0, NULL, 0, NULL, NULL},
};
static void sculpt_face_set_grow(Object *ob,
SculptSession *ss,
int *prev_face_sets,
const int active_face_set_id)
{
Mesh *mesh = BKE_mesh_from_object(ob);
for (int p = 0; p < mesh->totpoly; p++) {
const MPoly *c_poly = &mesh->mpoly[p];
for (int l = 0; l < c_poly->totloop; l++) {
const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
if (neighbor_face_index != p) {
if (abs(prev_face_sets[neighbor_face_index]) == active_face_set_id) {
ss->face_sets[p] = active_face_set_id;
}
}
}
}
}
}
static void sculpt_face_set_shrink(Object *ob,
SculptSession *ss,
int *prev_face_sets,
const int active_face_set_id)
{
Mesh *mesh = BKE_mesh_from_object(ob);
for (int p = 0; p < mesh->totpoly; p++) {
if (abs(prev_face_sets[p]) == active_face_set_id) {
const MPoly *c_poly = &mesh->mpoly[p];
for (int l = 0; l < c_poly->totloop; l++) {
const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l];
const MeshElemMap *vert_map = &ss->pmap[c_loop->v];
for (int i = 0; i < vert_map->count; i++) {
const int neighbor_face_index = vert_map->indices[i];
if (neighbor_face_index != p) {
if (abs(prev_face_sets[neighbor_face_index]) != active_face_set_id) {
ss->face_sets[p] = prev_face_sets[neighbor_face_index];
}
}
}
}
}
}
}
static void sculpt_face_set_apply_edit(Object *ob, const int active_face_set_id, const int mode)
{
SculptSession *ss = ob->sculpt;
int *prev_face_sets = MEM_dupallocN(ss->face_sets);
switch (mode) {
case SCULPT_FACE_SET_EDIT_GROW:
sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id);
break;
case SCULPT_FACE_SET_EDIT_SHRINK:
sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id);
break;
}
MEM_SAFE_FREE(prev_face_sets);
}
static int sculpt_face_set_edit_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 edit");
SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS);
const int active_face_set = SCULPT_active_face_set_get(ss);
sculpt_face_set_apply_edit(ob, abs(active_face_set), mode);
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_edit(struct wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Edit Face Set";
ot->idname = "SCULPT_OT_face_set_edit";
ot->description = "Edits the current active Face Set";
/* Api callbacks. */
ot->invoke = sculpt_face_set_edit_invoke;
ot->poll = SCULPT_mode_poll;
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
RNA_def_enum(
ot->srna, "mode", prop_sculpt_face_sets_edit_types, SCULPT_FACE_SET_EDIT_GROW, "Mode", "");
}

View File

@ -890,6 +890,7 @@ 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);
void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot);
/* Transform. */
void SCULPT_OT_set_pivot_position(struct wmOperatorType *ot);