Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/sculpt_face_set.c
| Show First 20 Lines • Show All 1,012 Lines • ▼ Show 20 Lines | void SCULPT_OT_face_sets_randomize_colors(wmOperatorType *ot) | ||||||||
| ot->poll = SCULPT_mode_poll; | ot->poll = SCULPT_mode_poll; | ||||||||
| ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; | ||||||||
| } | } | ||||||||
| typedef enum eSculptFaceSetEditMode { | typedef enum eSculptFaceSetEditMode { | ||||||||
| SCULPT_FACE_SET_EDIT_GROW = 0, | SCULPT_FACE_SET_EDIT_GROW = 0, | ||||||||
| SCULPT_FACE_SET_EDIT_SHRINK = 1, | SCULPT_FACE_SET_EDIT_SHRINK = 1, | ||||||||
| SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY = 2, | |||||||||
| } eSculptFaceSetEditMode; | } eSculptFaceSetEditMode; | ||||||||
| static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { | static EnumPropertyItem prop_sculpt_face_sets_edit_types[] = { | ||||||||
| { | { | ||||||||
| SCULPT_FACE_SET_EDIT_GROW, | SCULPT_FACE_SET_EDIT_GROW, | ||||||||
| "GROW", | "GROW", | ||||||||
| 0, | 0, | ||||||||
| "Grow Face Set", | "Grow Face Set", | ||||||||
| "Grows the Face Sets boundary by one face based on mesh topology", | "Grows the Face Sets boundary by one face based on mesh topology", | ||||||||
| }, | }, | ||||||||
| { | { | ||||||||
| SCULPT_FACE_SET_EDIT_SHRINK, | SCULPT_FACE_SET_EDIT_SHRINK, | ||||||||
| "SHRINK", | "SHRINK", | ||||||||
| 0, | 0, | ||||||||
| "Shrink Face Set", | "Shrink Face Set", | ||||||||
| "Shrinks the Face Sets boundary by one face based on mesh topology", | "Shrinks the Face Sets boundary by one face based on mesh topology", | ||||||||
| }, | }, | ||||||||
| { | |||||||||
| SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY, | |||||||||
| "DELETE_GEOMETRY", | |||||||||
| 0, | |||||||||
| "Delete Geometry", | |||||||||
| "Deletes the faces that are assigned to the Face Set", | |||||||||
| }, | |||||||||
| {0, NULL, 0, NULL, NULL}, | {0, NULL, 0, NULL, NULL}, | ||||||||
| }; | }; | ||||||||
| static void sculpt_face_set_grow(Object *ob, | static void sculpt_face_set_grow(Object *ob, | ||||||||
| SculptSession *ss, | SculptSession *ss, | ||||||||
| const int *prev_face_sets, | const int *prev_face_sets, | ||||||||
| const int active_face_set_id, | const int active_face_set_id, | ||||||||
| const bool modify_hidden) | const bool modify_hidden) | ||||||||
| ▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | if (abs(prev_face_sets[p]) == active_face_set_id) { | ||||||||
| ss->face_sets[p] = prev_face_sets[neighbor_face_index]; | ss->face_sets[p] = prev_face_sets[neighbor_face_index]; | ||||||||
| } | } | ||||||||
| } | } | ||||||||
| } | } | ||||||||
| } | } | ||||||||
| } | } | ||||||||
| } | } | ||||||||
| static bool check_single_face_set(SculptSession *ss, int *face_sets, const bool check_visible_only) | |||||||||
sergey: `check_single_face_set` is something what checks the state of the mesh regardless of the… | |||||||||
| { | |||||||||
| int first_face_set = SCULPT_FACE_SET_NONE; | |||||||||
| if (check_visible_only) { | |||||||||
| for (int f = 0; f < ss->totfaces; f++) { | |||||||||
| if (face_sets[f] > 0) { | |||||||||
| first_face_set = face_sets[f]; | |||||||||
| break; | |||||||||
| } | |||||||||
| } | |||||||||
| } | |||||||||
| else { | |||||||||
| first_face_set = abs(face_sets[0]); | |||||||||
| } | |||||||||
| if (first_face_set == SCULPT_FACE_SET_NONE) { | |||||||||
| return true; | |||||||||
| } | |||||||||
| for (int f = 0; f < ss->totfaces; f++) { | |||||||||
| const int face_set_id = check_visible_only ? face_sets[f] : abs(face_sets[f]); | |||||||||
| if (face_set_id != first_face_set) { | |||||||||
| return false; | |||||||||
| } | |||||||||
| } | |||||||||
| return true; | |||||||||
| } | |||||||||
| static void sculpt_face_set_delete_geometry(Object *ob, | |||||||||
| SculptSession *ss, | |||||||||
| const int active_face_set_id, | |||||||||
| const bool modify_hidden) | |||||||||
| { | |||||||||
| Mesh *mesh = ob->data; | |||||||||
| const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); | |||||||||
| BMesh *bm = BM_mesh_create(&allocsize, | |||||||||
| &((struct BMeshCreateParams){ | |||||||||
Done Inline ActionsReduce vertical size, BMesh *bm = ... sergey: Reduce vertical size, ` BMesh *bm = ...`
| |||||||||
| .use_toolflags = true, | |||||||||
| })); | |||||||||
| BM_mesh_bm_from_me(bm, | |||||||||
| mesh, | |||||||||
| (&(struct BMeshFromMeshParams){ | |||||||||
| .calc_face_normal = true, | |||||||||
| })); | |||||||||
| BM_mesh_elem_table_init(bm, BM_FACE); | |||||||||
| BM_mesh_elem_table_ensure(bm, BM_FACE); | |||||||||
| BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); | |||||||||
| BMIter iter; | |||||||||
| BMFace *f; | |||||||||
| BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) { | |||||||||
| const int face_index = BM_elem_index_get(f); | |||||||||
| const int face_set_id = modify_hidden ? abs(ss->face_sets[face_index]) : | |||||||||
| ss->face_sets[face_index]; | |||||||||
| BM_elem_flag_set(f, BM_ELEM_TAG, face_set_id == active_face_set_id); | |||||||||
| } | |||||||||
| BM_mesh_delete_hflag_context(bm, BM_ELEM_TAG, DEL_FACES); | |||||||||
| BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); | |||||||||
| BM_mesh_bm_to_me(NULL, | |||||||||
| bm, | |||||||||
| ob->data, | |||||||||
| (&(struct BMeshToMeshParams){ | |||||||||
| .calc_object_remap = false, | |||||||||
| })); | |||||||||
| BM_mesh_free(bm); | |||||||||
| } | |||||||||
| static void sculpt_face_set_apply_edit(Object *ob, | static void sculpt_face_set_apply_edit(Object *ob, | ||||||||
| const int active_face_set_id, | const int active_face_set_id, | ||||||||
| const int mode, | const int mode, | ||||||||
| const bool modify_hidden) | const bool modify_hidden) | ||||||||
| { | { | ||||||||
| SculptSession *ss = ob->sculpt; | SculptSession *ss = ob->sculpt; | ||||||||
| int *prev_face_sets = MEM_dupallocN(ss->face_sets); | |||||||||
| switch (mode) { | switch (mode) { | ||||||||
| case SCULPT_FACE_SET_EDIT_GROW: | case SCULPT_FACE_SET_EDIT_GROW: { | ||||||||
| int *prev_face_sets = MEM_dupallocN(ss->face_sets); | |||||||||
| sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); | sculpt_face_set_grow(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); | ||||||||
| MEM_SAFE_FREE(prev_face_sets); | |||||||||
| break; | break; | ||||||||
| case SCULPT_FACE_SET_EDIT_SHRINK: | } | ||||||||
| case SCULPT_FACE_SET_EDIT_SHRINK: { | |||||||||
| int *prev_face_sets = MEM_dupallocN(ss->face_sets); | |||||||||
| sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); | sculpt_face_set_shrink(ob, ss, prev_face_sets, active_face_set_id, modify_hidden); | ||||||||
| MEM_SAFE_FREE(prev_face_sets); | |||||||||
| break; | |||||||||
| } | |||||||||
| case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: | |||||||||
| sculpt_face_set_delete_geometry(ob, ss, active_face_set_id, modify_hidden); | |||||||||
| break; | break; | ||||||||
| } | } | ||||||||
| MEM_SAFE_FREE(prev_face_sets); | |||||||||
| } | } | ||||||||
| static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) | static bool sculpt_face_set_edit_is_operation_valid(SculptSession *ss, | ||||||||
| const eSculptFaceSetEditMode mode, | |||||||||
| const bool modify_hidden) | |||||||||
| { | { | ||||||||
| Object *ob = CTX_data_active_object(C); | |||||||||
| SculptSession *ss = ob->sculpt; | |||||||||
| 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) { | if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { | ||||||||
| /* Dyntopo is not supported. */ | |||||||||
Done Inline Actions
sergey: | |||||||||
| return OPERATOR_CANCELLED; | return OPERATOR_CANCELLED; | ||||||||
| } | } | ||||||||
| /* Ignore other events to avoid repeated operations. */ | if (mode == SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY) { | ||||||||
| if (event->val != KM_PRESS) { | if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { | ||||||||
| return OPERATOR_CANCELLED; | /* Modification of base mesh geometry requires special remapping of multires displacement, | ||||||||
| * which does not happen here. | |||||||||
Done Inline ActionsI do not quite follow the explanation. My interpretation: /* Modification of base mesh geometry requires special remapping of multires displacement, which does not happen here. * Disable delete operation. It can be supported in the future by * by doing similar displacement data remapping as what happens in the mesh edit mode. */ sergey: I do not quite follow the explanation. My interpretation:
/* Modification of base mesh… | |||||||||
| * Disable delete operation. It can be supported in the future by doing similar displacement | |||||||||
| * data remapping as what happens in the mesh edit mode. */ | |||||||||
| return false; | |||||||||
| } | |||||||||
| if (check_single_face_set(ss, ss->face_sets, !modify_hidden)) { | |||||||||
| /* Cancel the operator if the mesh only contains one Face Set to avoid deleting the | |||||||||
| * entire object. */ | |||||||||
| return false; | |||||||||
| } | |||||||||
| } | |||||||||
| return true; | |||||||||
| } | } | ||||||||
| BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); | static void sculpt_face_set_edit_modify_geometry(bContext *C, | ||||||||
| Object *ob, | |||||||||
| const int active_face_set, | |||||||||
| const eSculptFaceSetEditMode mode, | |||||||||
| const bool modify_hidden) | |||||||||
| { | |||||||||
| ED_sculpt_undo_geometry_begin(ob, "edit face set delete geometry"); | |||||||||
| sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); | |||||||||
| ED_sculpt_undo_geometry_end(ob); | |||||||||
| BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL); | |||||||||
| DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); | |||||||||
| WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data); | |||||||||
| } | |||||||||
| /* Update the current active Face Set and Vertex as the operator can be used directly from the | static void face_set_edit_do_post_visibility_updates(Object *ob, PBVHNode **nodes, int totnode) | ||||||||
| * tool without brush cursor. */ | { | ||||||||
| SculptCursorGeometryInfo sgi; | SculptSession *ss = ob->sculpt; | ||||||||
| float mouse[2]; | PBVH *pbvh = ss->pbvh; | ||||||||
| mouse[0] = event->mval[0]; | |||||||||
| mouse[1] = event->mval[1]; | |||||||||
| SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); | |||||||||
| /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ | |||||||||
| SCULPT_visibility_sync_all_face_sets_to_vertices(ob); | |||||||||
| for (int i = 0; i < totnode; i++) { | |||||||||
| BKE_pbvh_node_mark_update_visibility(nodes[i]); | |||||||||
| } | |||||||||
| BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); | |||||||||
| if (BKE_pbvh_type(pbvh) == PBVH_FACES) { | |||||||||
| BKE_mesh_flush_hidden_from_verts(ob->data); | |||||||||
| } | |||||||||
| } | |||||||||
| static void sculpt_face_set_edit_modify_face_sets(Object *ob, | |||||||||
| const int active_face_set, | |||||||||
| const eSculptFaceSetEditMode mode, | |||||||||
| const bool modify_hidden) | |||||||||
| { | |||||||||
| PBVH *pbvh = ob->sculpt->pbvh; | PBVH *pbvh = ob->sculpt->pbvh; | ||||||||
| PBVHNode **nodes; | PBVHNode **nodes; | ||||||||
| int totnode; | int totnode; | ||||||||
| BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); | BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); | ||||||||
| if (!nodes) { | if (!nodes) { | ||||||||
| return OPERATOR_CANCELLED; | return; | ||||||||
| } | } | ||||||||
| SCULPT_undo_push_begin("face set edit"); | SCULPT_undo_push_begin("face set edit"); | ||||||||
| SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); | SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); | ||||||||
| const int active_face_set = SCULPT_active_face_set_get(ss); | |||||||||
| const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); | |||||||||
| sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); | sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); | ||||||||
| SCULPT_undo_push_end(); | SCULPT_undo_push_end(); | ||||||||
| face_set_edit_do_post_visibility_updates(ob, nodes, totnode); | |||||||||
| MEM_freeN(nodes); | |||||||||
| } | |||||||||
| /* Sync face sets visibility and vertex visibility as now all Face Sets are visible. */ | static int sculpt_face_set_edit_invoke(bContext *C, wmOperator *op, const wmEvent *event) | ||||||||
| SCULPT_visibility_sync_all_face_sets_to_vertices(ob); | { | ||||||||
| Object *ob = CTX_data_active_object(C); | |||||||||
| SculptSession *ss = ob->sculpt; | |||||||||
| Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); | |||||||||
| for (int i = 0; i < totnode; i++) { | const int mode = RNA_enum_get(op->ptr, "mode"); | ||||||||
| BKE_pbvh_node_mark_update_visibility(nodes[i]); | const bool modify_hidden = RNA_boolean_get(op->ptr, "modify_hidden"); | ||||||||
| if (!sculpt_face_set_edit_is_operation_valid(ss, mode, modify_hidden)) { | |||||||||
| return OPERATOR_CANCELLED; | |||||||||
| } | } | ||||||||
Done Inline ActionsI don't understand this. You shouldn't be checking for events in the invoke. sergey: I don't understand this. You shouldn't be checking for events in the `invoke`. | |||||||||
| BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); | BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); | ||||||||
| MEM_SAFE_FREE(nodes); | /* Update the current active Face Set and Vertex as the operator can be used directly from the | ||||||||
| * tool without brush cursor. */ | |||||||||
| SculptCursorGeometryInfo sgi; | |||||||||
| const float mouse[2] = {event->mval[0], event->mval[1]}; | |||||||||
| SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false); | |||||||||
| const int active_face_set = SCULPT_active_face_set_get(ss); | |||||||||
| if (BKE_pbvh_type(pbvh) == PBVH_FACES) { | switch (mode) { | ||||||||
| BKE_mesh_flush_hidden_from_verts(ob->data); | case SCULPT_FACE_SET_EDIT_DELETE_GEOMETRY: | ||||||||
| sculpt_face_set_edit_modify_geometry(C, ob, active_face_set, mode, modify_hidden); | |||||||||
| break; | |||||||||
| case SCULPT_FACE_SET_EDIT_GROW: | |||||||||
| case SCULPT_FACE_SET_EDIT_SHRINK: | |||||||||
Done Inline ActionsYou should be able to const float mouse[2] = {event->mval[0], event->mval[1]};@Julian Eisel (Severin), is it how we handle other operators where we need them to be called from mouse events? sergey: You should be able to
const float mouse[2] = {event->mval[0], event->mval[1]};
@Severin, is… | |||||||||
Not Done Inline ActionsDoing it like this is fine. Although I'd prefer it to be const (it's not in the current patch). Why make int active_face_set const but not an immutable array that's also a reference? :) Severin: Doing it like this is fine. Although I'd prefer it to be `const` (it's not in the current… | |||||||||
| sculpt_face_set_edit_modify_face_sets(ob, active_face_set, mode, modify_hidden); | |||||||||
| break; | |||||||||
| } | } | ||||||||
Done Inline ActionsAll this feels to belong to two separate function, one of which handles geometry case, another one which handles visibility case. Interleaving them together is confusing. sergey: All this feels to belong to two separate function, one of which handles geometry case, another… | |||||||||
| SCULPT_tag_update_overlays(C); | SCULPT_tag_update_overlays(C); | ||||||||
| return OPERATOR_FINISHED; | return OPERATOR_FINISHED; | ||||||||
| } | } | ||||||||
| void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot) | void SCULPT_OT_face_sets_edit(struct wmOperatorType *ot) | ||||||||
| { | { | ||||||||
| Show All 19 Lines | |||||||||
check_single_face_set is something what checks the state of the mesh regardless of the operation. Hence, having an argument modify_hidden is confusing and misleading:
For good API it will be clear what function does from its name and argument list.