diff --git a/.clang-format b/.clang-format index 72add4594a4..4c140988720 100644 --- a/.clang-format +++ b/.clang-format @@ -162,6 +162,7 @@ PenaltyBreakString: 1000000 ForEachMacros: - BEGIN_ANIMFILTER_SUBCHANNELS - BKE_pbvh_vertex_iter_begin + - BKE_pbvh_face_iter_begin - BLI_FOREACH_SPARSE_RANGE - BLI_SMALLSTACK_ITER_BEGIN - BMO_ITER diff --git a/source/blender/blenkernel/BKE_pbvh.h b/source/blender/blenkernel/BKE_pbvh.h index 13ff0c6d5b3..d256ca81ec6 100644 --- a/source/blender/blenkernel/BKE_pbvh.h +++ b/source/blender/blenkernel/BKE_pbvh.h @@ -69,14 +69,22 @@ struct PBVHPublic { * intptr_t's in structs. */ +/* A generic PBVH vertex. + * + * Note: in PBVH_GRIDS we consider the final grid points + * to be vertices. This is not true of edges or faces which are pulled from + * the base mesh. + */ typedef struct PBVHVertRef { intptr_t i; } PBVHVertRef; +/* Note: edges in PBVH_GRIDS are always pulled from the base mesh.*/ typedef struct PBVHEdgeRef { intptr_t i; } PBVHEdgeRef; +/* Note: faces in PBVH_GRIDS are always puled from the base mesh.*/ typedef struct PBVHFaceRef { intptr_t i; } PBVHFaceRef; @@ -434,6 +442,7 @@ bool BKE_pbvh_bmesh_update_topology(PBVH *pbvh, void BKE_pbvh_node_mark_update(PBVHNode *node); void BKE_pbvh_node_mark_update_mask(PBVHNode *node); void BKE_pbvh_node_mark_update_color(PBVHNode *node); +void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node); void BKE_pbvh_node_mark_update_visibility(PBVHNode *node); void BKE_pbvh_node_mark_rebuild_draw(PBVHNode *node); void BKE_pbvh_node_mark_redraw(PBVHNode *node); @@ -466,6 +475,11 @@ void BKE_pbvh_node_get_loops(PBVH *pbvh, const int **r_loop_indices, const struct MLoop **r_loops); +/* Get number of faces in the mesh; for PBVH_GRIDS the + * number of base mesh faces is returned. + */ +int BKE_pbvh_num_faces(const PBVH *pbvh); + void BKE_pbvh_node_get_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); void BKE_pbvh_node_get_original_BB(PBVHNode *node, float bb_min[3], float bb_max[3]); @@ -665,6 +679,57 @@ void pbvh_vertex_iter_init(PBVH *pbvh, PBVHNode *node, PBVHVertexIter *vi, int m } \ ((void)0) +#define PBVH_FACE_ITER_VERTS_RESERVED 8 + +typedef struct PBVHFaceIter { + PBVHFaceRef face; + int index; + bool *hide; + int *face_set; + int i; + + PBVHVertRef *verts; + int verts_num; + + /* Private. */ +#ifdef __cplusplus + private: +#endif + + PBVHVertRef verts_reserved_[PBVH_FACE_ITER_VERTS_RESERVED]; + const PBVHNode *node_; + PBVHType pbvh_type_; + int verts_size_; + GSetIterator bm_faces_iter_; + int cd_hide_poly_, cd_face_set_; + bool *hide_poly_; + int *face_sets_; + const struct MPoly *mpoly_; + const struct MLoopTri *looptri_; + const struct MLoop *mloop_; + int prim_index_; + const struct SubdivCCG *subdiv_ccg_; + const struct BMesh *bm; + + int last_face_index_; +} PBVHFaceIter; + +void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd); +void BKE_pbvh_face_iter_step(PBVHFaceIter *fd); +bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd); +void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd); + +/** Iterate over faces inside a PBVHNode. These are either base mesh faces + * (for PBVH_FACES and PBVH_GRIDS) or BMesh faces (for PBVH_BMESH). + */ +#define BKE_pbvh_face_iter_begin(pbvh, node, fd) \ + BKE_pbvh_face_iter_init(pbvh, node, &fd); \ + for (; !BKE_pbvh_face_iter_done(&fd); BKE_pbvh_face_iter_step(&fd)) { + +#define BKE_pbvh_face_iter_end(fd) \ + } \ + BKE_pbvh_face_iter_finish(&fd) + void BKE_pbvh_node_get_proxies(PBVHNode *node, PBVHProxyNode **proxies, int *proxy_count); void BKE_pbvh_node_free_proxies(PBVHNode *node); PBVHProxyNode *BKE_pbvh_node_add_proxy(PBVH *pbvh, PBVHNode *node); diff --git a/source/blender/blenkernel/intern/pbvh.c b/source/blender/blenkernel/intern/pbvh.c index 934871d22eb..9195683f70c 100644 --- a/source/blender/blenkernel/intern/pbvh.c +++ b/source/blender/blenkernel/intern/pbvh.c @@ -746,6 +746,7 @@ void BKE_pbvh_build_mesh(PBVH *pbvh, pbvh->vdata = vdata; pbvh->ldata = ldata; pbvh->pdata = pdata; + pbvh->faces_num = mesh->totpoly; pbvh->face_sets_color_seed = mesh->face_sets_color_seed; pbvh->face_sets_color_default = mesh->face_sets_color_default; @@ -833,6 +834,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->gridkey = *key; pbvh->grid_hidden = grid_hidden; pbvh->subdiv_ccg = subdiv_ccg; + pbvh->faces_num = me->totpoly; /* Find maximum number of grids per face. */ int max_grids = 1; @@ -853,6 +855,9 @@ void BKE_pbvh_build_grids(PBVH *pbvh, pbvh->ldata = &me->ldata; pbvh->pdata = &me->pdata; + pbvh->mpoly = BKE_mesh_polys(me); + pbvh->mloop = BKE_mesh_loops(me); + /* We also need the base mesh for PBVH draw. */ pbvh->mesh = me; @@ -2015,6 +2020,11 @@ void BKE_pbvh_node_mark_update_color(PBVHNode *node) node->flag |= PBVH_UpdateColor | PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; } +void BKE_pbvh_node_mark_update_face_sets(PBVHNode *node) +{ + node->flag |= PBVH_UpdateDrawBuffers | PBVH_UpdateRedraw; +} + void BKE_pbvh_mark_rebuild_pixels(PBVH *pbvh) { for (int n = 0; n < pbvh->totnode; n++) { @@ -2119,6 +2129,20 @@ void BKE_pbvh_node_get_loops(PBVH *pbvh, } } +int BKE_pbvh_num_faces(const PBVH *pbvh) +{ + switch (pbvh->header.type) { + case PBVH_GRIDS: + case PBVH_FACES: + return pbvh->faces_num; + case PBVH_BMESH: + return pbvh->header.bm->totface; + } + + BLI_assert_unreachable(); + return 0; +} + void BKE_pbvh_node_get_verts(PBVH *pbvh, PBVHNode *node, const int **r_vert_indices, @@ -3611,3 +3635,184 @@ void BKE_pbvh_sync_visibility_from_verts(PBVH *pbvh, Mesh *mesh) } } } + +static void pbvh_face_iter_verts_reserve(PBVHFaceIter *fd, int verts_num) +{ + if (verts_num >= fd->verts_size_) { + fd->verts_size_ = (verts_num + 1) << 2; + + if (fd->verts != fd->verts_reserved_) { + MEM_SAFE_FREE(fd->verts); + } + + fd->verts = MEM_malloc_arrayN(fd->verts_size_, sizeof(void *), __func__); + } + + fd->verts_num = verts_num; +} + +BLI_INLINE int face_iter_prim_to_face(PBVHFaceIter *fd, int prim_index) +{ + if (fd->subdiv_ccg_) { + return BKE_subdiv_ccg_grid_to_face_index(fd->subdiv_ccg_, prim_index); + } + else { + return fd->looptri_[prim_index].poly; + } +} + +void pbvh_face_iter_step(PBVHFaceIter *fd, bool do_step) +{ + if (do_step) { + fd->i++; + } + + switch (fd->pbvh_type_) { + case PBVH_BMESH: { + if (do_step) { + BLI_gsetIterator_step(&fd->bm_faces_iter_); + if (BLI_gsetIterator_done(&fd->bm_faces_iter_)) { + return; + } + } + + BMFace *f = (BMFace *)BLI_gsetIterator_getKey(&fd->bm_faces_iter_); + fd->face.i = (intptr_t)f; + fd->index = f->head.index; + + if (fd->cd_face_set_ != -1) { + fd->face_set = (int *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_face_set_); + } + + if (fd->cd_hide_poly_ != -1) { + fd->hide = (bool *)BM_ELEM_CD_GET_VOID_P(f, fd->cd_hide_poly_); + } + + pbvh_face_iter_verts_reserve(fd, f->len); + int vertex_i = 0; + + BMLoop *l = f->l_first; + do { + fd->verts[vertex_i++].i = (intptr_t)l->v; + } while ((l = l->next) != f->l_first); + + break; + } + case PBVH_GRIDS: + case PBVH_FACES: { + int face_index = 0; + + if (do_step) { + fd->prim_index_++; + + while (fd->prim_index_ < fd->node_->totprim) { + face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); + + if (face_index != fd->last_face_index_) { + break; + } + + fd->prim_index_++; + } + } + else if (fd->prim_index_ < fd->node_->totprim) { + face_index = face_iter_prim_to_face(fd, fd->node_->prim_indices[fd->prim_index_]); + } + + if (fd->prim_index_ >= fd->node_->totprim) { + return; + } + + fd->last_face_index_ = face_index; + const MPoly *mp = fd->mpoly_ + face_index; + + fd->face.i = fd->index = face_index; + + if (fd->face_sets_) { + fd->face_set = fd->face_sets_ + face_index; + } + if (fd->hide_poly_) { + fd->hide = fd->hide_poly_ + face_index; + } + + pbvh_face_iter_verts_reserve(fd, mp->totloop); + + const MLoop *ml = fd->mloop_ + mp->loopstart; + for (int i = 0; i < mp->totloop; i++, ml++) { + if (fd->pbvh_type_ == PBVH_GRIDS) { + /* Grid corners. */ + fd->verts[i].i = mp->loopstart + i; + } + else { + fd->verts[i].i = ml->v; + } + } + + break; + } + } +} + +void BKE_pbvh_face_iter_step(PBVHFaceIter *fd) +{ + pbvh_face_iter_step(fd, true); +} + +void BKE_pbvh_face_iter_init(PBVH *pbvh, PBVHNode *node, PBVHFaceIter *fd) +{ + memset(fd, 0, sizeof(*fd)); + + fd->node_ = node; + fd->pbvh_type_ = BKE_pbvh_type(pbvh); + + fd->verts = fd->verts_reserved_; + fd->verts_size_ = PBVH_FACE_ITER_VERTS_RESERVED; + + switch (BKE_pbvh_type(pbvh)) { + case PBVH_GRIDS: + fd->subdiv_ccg_ = pbvh->subdiv_ccg; + case PBVH_FACES: + fd->mpoly_ = pbvh->mpoly; + fd->mloop_ = pbvh->mloop; + fd->looptri_ = pbvh->looptri; + fd->hide_poly_ = pbvh->hide_poly; + fd->face_sets_ = pbvh->face_sets; + fd->last_face_index_ = -1; + + break; + case PBVH_BMESH: + fd->bm = pbvh->header.bm; + fd->cd_face_set_ = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".sculpt_face_set"); + fd->cd_hide_poly_ = CustomData_get_offset_named( + &pbvh->header.bm->pdata, CD_PROP_INT32, ".hide_poly"); + + BLI_gsetIterator_init(&fd->bm_faces_iter_, node->bm_faces); + break; + } + + if (!BKE_pbvh_face_iter_done(fd)) { + pbvh_face_iter_step(fd, false); + } +} + +void BKE_pbvh_face_iter_finish(PBVHFaceIter *fd) +{ + if (fd->verts != fd->verts_reserved_) { + MEM_SAFE_FREE(fd->verts); + } +} + +bool BKE_pbvh_face_iter_done(PBVHFaceIter *fd) +{ + switch (fd->pbvh_type_) { + case PBVH_FACES: + case PBVH_GRIDS: + return fd->prim_index_ >= fd->node_->totprim; + case PBVH_BMESH: + return BLI_gsetIterator_done(&fd->bm_faces_iter_); + default: + BLI_assert_unreachable(); + return true; + } +} diff --git a/source/blender/blenkernel/intern/pbvh_intern.h b/source/blender/blenkernel/intern/pbvh_intern.h index 368a9ffa1ea..4281796d9a4 100644 --- a/source/blender/blenkernel/intern/pbvh_intern.h +++ b/source/blender/blenkernel/intern/pbvh_intern.h @@ -148,6 +148,7 @@ struct PBVH { int *prim_indices; int totprim; int totvert; + int faces_num; /* Do not use directly, use BKE_pbvh_num_faces. */ int leaf_limit; diff --git a/source/blender/editors/sculpt_paint/paint_mask.c b/source/blender/editors/sculpt_paint/paint_mask.c index ce7db91571c..eb24d15dad5 100644 --- a/source/blender/editors/sculpt_paint/paint_mask.c +++ b/source/blender/editors/sculpt_paint/paint_mask.c @@ -663,10 +663,11 @@ static bool sculpt_gesture_is_effected_lasso(SculptGestureContext *sgcontext, co return BLI_BITMAP_TEST_BOOL(lasso->mask_px, scr_co_s[1] * lasso->width + scr_co_s[0]); } -static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertexIter *vd) +static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, PBVHVertRef vertex) { float vertex_normal[3]; - SCULPT_vertex_normal_get(sgcontext->ss, vd->vertex, vertex_normal); + const float *co = SCULPT_vertex_co_get(sgcontext->ss, vertex); + SCULPT_vertex_normal_get(sgcontext->ss, vertex, vertex_normal); float dot = dot_v3v3(sgcontext->view_normal, vertex_normal); const bool is_effected_front_face = !(sgcontext->front_faces_only && dot < 0.0f); @@ -676,20 +677,31 @@ static bool sculpt_gesture_is_vertex_effected(SculptGestureContext *sgcontext, P switch (sgcontext->shape_type) { case SCULPT_GESTURE_SHAPE_BOX: - return isect_point_planes_v3(sgcontext->clip_planes, 4, vd->co); + return isect_point_planes_v3(sgcontext->clip_planes, 4, co); case SCULPT_GESTURE_SHAPE_LASSO: - return sculpt_gesture_is_effected_lasso(sgcontext, vd->co); + return sculpt_gesture_is_effected_lasso(sgcontext, co); case SCULPT_GESTURE_SHAPE_LINE: if (sgcontext->line.use_side_planes) { - return plane_point_side_v3(sgcontext->line.plane, vd->co) > 0.0f && - plane_point_side_v3(sgcontext->line.side_plane[0], vd->co) > 0.0f && - plane_point_side_v3(sgcontext->line.side_plane[1], vd->co) > 0.0f; + return plane_point_side_v3(sgcontext->line.plane, co) > 0.0f && + plane_point_side_v3(sgcontext->line.side_plane[0], co) > 0.0f && + plane_point_side_v3(sgcontext->line.side_plane[1], co) > 0.0f; } - return plane_point_side_v3(sgcontext->line.plane, vd->co) > 0.0f; + return plane_point_side_v3(sgcontext->line.plane, co) > 0.0f; } return false; } +static bool sculpt_gesture_is_face_effected(SculptGestureContext *sgcontext, PBVHFaceIter *fd) +{ + for (int i = 0; i < fd->verts_num; i++) { + if (sculpt_gesture_is_vertex_effected(sgcontext, fd->verts[i])) { + return true; + } + } + + return false; +} + static void sculpt_gesture_apply(bContext *C, SculptGestureContext *sgcontext, wmOperator *op) { SculptGestureOperation *operation = sgcontext->operation; @@ -728,9 +740,6 @@ static void sculpt_gesture_face_set_begin(bContext *C, SculptGestureContext *sgc { Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); BKE_sculpt_update_object_for_edit(depsgraph, sgcontext->vc.obact, true, false, false); - - /* Face Sets modifications do a single undo push. */ - SCULPT_undo_push_node(sgcontext->vc.obact, NULL, SCULPT_UNDO_FACE_SETS); } static void face_set_gesture_apply_task_cb(void *__restrict userdata, @@ -741,16 +750,18 @@ static void face_set_gesture_apply_task_cb(void *__restrict userdata, SculptGestureFaceSetOperation *face_set_operation = (SculptGestureFaceSetOperation *) sgcontext->operation; PBVHNode *node = sgcontext->nodes[i]; - PBVHVertexIter vd; bool any_updated = false; - BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { - SCULPT_vertex_face_set_set(sgcontext->ss, vd.vertex, face_set_operation->new_face_set_id); + SCULPT_undo_push_node(sgcontext->vc.obact, node, SCULPT_UNDO_FACE_SETS); + + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (sgcontext->ss->pbvh, node, fd) { + if (sculpt_gesture_is_face_effected(sgcontext, &fd)) { + SCULPT_face_set_set(sgcontext->ss, fd.face, face_set_operation->new_face_set_id); any_updated = true; } } - BKE_pbvh_vertex_iter_end; + BKE_pbvh_face_iter_end(fd); if (any_updated) { BKE_pbvh_node_mark_update_visibility(node); @@ -821,7 +832,7 @@ static void mask_gesture_apply_task_cb(void *__restrict userdata, bool redraw = false; BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { + if (sculpt_gesture_is_vertex_effected(sgcontext, vd.vertex)) { float prevmask = vd.mask ? *vd.mask : 0.0f; if (!any_masked) { any_masked = true; @@ -1442,7 +1453,7 @@ static void project_line_gesture_apply_task_cb(void *__restrict userdata, SCULPT_undo_push_node(sgcontext->vc.obact, node, SCULPT_UNDO_COORDS); BKE_pbvh_vertex_iter_begin (sgcontext->ss->pbvh, node, vd, PBVH_ITER_UNIQUE) { - if (!sculpt_gesture_is_vertex_effected(sgcontext, &vd)) { + if (!sculpt_gesture_is_vertex_effected(sgcontext, vd.vertex)) { continue; } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 3477285814e..6a38ef8bc8c 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1399,6 +1399,39 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter } } +void SCULPT_orig_face_data_unode_init(SculptOrigFaceData *data, Object *ob, SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; + + memset(data, 0, sizeof(*data)); + data->unode = unode; + + if (bm) { + data->bm_log = ss->bm_log; + } + else { + data->face_sets = unode->face_sets; + } +} + +void SCULPT_orig_face_data_init(SculptOrigFaceData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type) +{ + SculptUndoNode *unode; + unode = SCULPT_undo_push_node(ob, node, type); + SCULPT_orig_face_data_unode_init(data, ob, unode); +} + +void SCULPT_orig_face_data_update(SculptOrigFaceData *orig_data, PBVHFaceIter *iter) +{ + if (orig_data->unode->type == SCULPT_UNDO_FACE_SETS) { + orig_data->face_set = orig_data->face_sets ? orig_data->face_sets[iter->i] : false; + } +} + static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3]) { float rake_dist = len_v3v3(srd->follow_co, co); @@ -1450,6 +1483,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, case SCULPT_TOOL_SMEAR: type = SCULPT_UNDO_COLOR; break; + case SCULPT_TOOL_DRAW_FACE_SETS: + type = ss->cache->alt_smooth ? SCULPT_UNDO_COORDS : SCULPT_UNDO_FACE_SETS; + break; default: type = SCULPT_UNDO_COORDS; break; @@ -1473,6 +1509,9 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, case SCULPT_UNDO_COLOR: BKE_pbvh_node_mark_update_color(data->nodes[n]); break; + case SCULPT_UNDO_FACE_SETS: + BKE_pbvh_node_mark_update_face_sets(data->nodes[n]); + break; case SCULPT_UNDO_COORDS: BKE_pbvh_node_mark_update(data->nodes[n]); break; @@ -1481,30 +1520,50 @@ static void paint_mesh_restore_co_task_cb(void *__restrict userdata, } PBVHVertexIter vd; - SculptOrigVertData orig_data; + SculptOrigVertData orig_vert_data; + SculptOrigFaceData orig_face_data; - SCULPT_orig_vert_data_unode_init(&orig_data, data->ob, unode); + if (type != SCULPT_UNDO_FACE_SETS) { + SCULPT_orig_vert_data_unode_init(&orig_vert_data, data->ob, unode); + } + else { + SCULPT_orig_face_data_unode_init(&orig_face_data, data->ob, unode); + } + + if (unode->type == SCULPT_UNDO_FACE_SETS) { + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, data->nodes[n], fd) { + SCULPT_orig_face_data_update(&orig_face_data, &fd); + + if (fd.face_set) { + *fd.face_set = orig_face_data.face_set; + } + } + + BKE_pbvh_face_iter_end(fd); + return; + } BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - SCULPT_orig_vert_data_update(&orig_data, &vd); + SCULPT_orig_vert_data_update(&orig_vert_data, &vd); - if (orig_data.unode->type == SCULPT_UNDO_COORDS) { - copy_v3_v3(vd.co, orig_data.co); + if (orig_vert_data.unode->type == SCULPT_UNDO_COORDS) { + copy_v3_v3(vd.co, orig_vert_data.co); if (vd.no) { - copy_v3_v3(vd.no, orig_data.no); + copy_v3_v3(vd.no, orig_vert_data.no); } else { - copy_v3_v3(vd.fno, orig_data.no); + copy_v3_v3(vd.fno, orig_vert_data.no); } if (vd.mvert) { BKE_pbvh_vert_tag_update_normal(ss->pbvh, vd.vertex); } } - else if (orig_data.unode->type == SCULPT_UNDO_MASK) { - *vd.mask = orig_data.mask; + else if (orig_vert_data.unode->type == SCULPT_UNDO_MASK) { + *vd.mask = orig_vert_data.mask; } - else if (orig_data.unode->type == SCULPT_UNDO_COLOR) { - SCULPT_vertex_color_set(ss, vd.vertex, orig_data.col); + else if (orig_vert_data.unode->type == SCULPT_UNDO_COLOR) { + SCULPT_vertex_color_set(ss, vd.vertex, orig_vert_data.col); } } BKE_pbvh_vertex_iter_end; @@ -3296,13 +3355,16 @@ static void do_brush_action_task_cb(void *__restrict userdata, bool need_coords = ss->cache->supports_gravity; - /* Face Sets modifications do a single undo push */ if (data->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { - BKE_pbvh_node_mark_redraw(data->nodes[n]); + BKE_pbvh_node_mark_update_face_sets(data->nodes[n]); + /* Draw face sets in smooth mode moves the vertices. */ if (ss->cache->alt_smooth) { need_coords = true; } + else { + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); + } } else if (data->brush->sculpt_tool == SCULPT_TOOL_MASK) { SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK); @@ -3380,14 +3442,6 @@ static void do_brush_action(Sculpt *sd, * and the number of nodes under the brush influence. */ if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) { - - /* Dynamic-topology does not support Face Sets data, so it can't store/restore it from undo. */ - /* TODO(pablodp606): This check should be done in the undo code and not here, but the rest of - * the sculpt code is not checking for unsupported undo types that may return a null node. */ - if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { - SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS); - } - 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); @@ -5217,13 +5271,6 @@ static void sculpt_restore_mesh(Sculpt *sd, Object *ob) BKE_brush_use_size_pressure(brush)) || (brush->flag & BRUSH_DRAG_DOT)) { - SculptUndoNode *unode = SCULPT_undo_get_first_node(); - if (unode && unode->type == SCULPT_UNDO_FACE_SETS) { - for (int i = 0; i < ss->totfaces; i++) { - ss->face_sets[i] = unode->face_sets[i]; - } - } - paint_mesh_restore_co(sd, ob); if (ss->cache) { @@ -6195,4 +6242,30 @@ void SCULPT_stroke_id_ensure(Object *ob) } } +int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + return 0; + case PBVH_FACES: + case PBVH_GRIDS: + return ss->face_sets[face.i]; + } + + BLI_assert_unreachable(); + + return 0; +} + +void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_BMESH: + break; + case PBVH_FACES: + case PBVH_GRIDS: + ss->face_sets[face.i] = fset; + } +} + /** \} */ diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.cc b/source/blender/editors/sculpt_paint/sculpt_automasking.cc index 4117a2b5622..f0b803ecfa1 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.cc +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.cc @@ -545,7 +545,11 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, } if (automasking->settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) { - if (!SCULPT_vertex_has_unique_face_set(ss, vert)) { + bool ignore = ss->cache && ss->cache->brush && + ss->cache->brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && + SCULPT_vertex_face_set_get(ss, vert) == ss->cache->paint_face_set; + + if (!ignore && !SCULPT_vertex_has_unique_face_set(ss, vert)) { return 0.0f; } } diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 3133bb2007e..c4955ab1f03 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -2074,7 +2074,9 @@ static void sculpt_expand_undo_push(Object *ob, ExpandCache *expand_cache) } break; case SCULPT_EXPAND_TARGET_FACE_SETS: - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_FACE_SETS); + } break; case SCULPT_EXPAND_TARGET_COLORS: for (int i = 0; i < totnode; i++) { diff --git a/source/blender/editors/sculpt_paint/sculpt_face_set.cc b/source/blender/editors/sculpt_paint/sculpt_face_set.cc index 40835172be9..d0c8a19434b 100644 --- a/source/blender/editors/sculpt_paint/sculpt_face_set.cc +++ b/source/blender/editors/sculpt_paint/sculpt_face_set.cc @@ -125,6 +125,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, SCULPT_automasking_node_begin( data->ob, ss, ss->cache->automasking, &automask_data, data->nodes[n]); + bool changed = false; BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SCULPT_automasking_node_update(ss, &automask_data, &vd); @@ -156,6 +157,7 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, if (fade > 0.05f) { ss->face_sets[vert_map->indices[j]] = ss->cache->paint_face_set; + changed = true; } } } @@ -176,10 +178,15 @@ static void do_draw_face_sets_brush_task_cb_ex(void *__restrict userdata, if (fade > 0.05f) { SCULPT_vertex_face_set_set(ss, vd.vertex, ss->cache->paint_face_set); + changed = true; } } } BKE_pbvh_vertex_iter_end; + + if (changed) { + SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_FACE_SETS); + } } static void do_relax_face_sets_brush_task_cb_ex(void *__restrict userdata, @@ -344,7 +351,9 @@ static int sculpt_face_set_create_exec(bContext *C, wmOperator *op) } SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + for (const int i : blender::IndexRange(totnode)) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_FACE_SETS); + } const int next_face_set = SCULPT_face_set_next_available_get(ss); @@ -637,7 +646,9 @@ static int sculpt_face_set_init_exec(bContext *C, wmOperator *op) } SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + for (const int i : blender::IndexRange(totnode)) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_FACE_SETS); + } const float threshold = RNA_float_get(op->ptr, "threshold"); @@ -1366,7 +1377,9 @@ static void sculpt_face_set_edit_modify_face_sets(Object *ob, return; } SCULPT_undo_push_begin(ob, op); - SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + for (const int i : blender::IndexRange(totnode)) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_FACE_SETS); + } sculpt_face_set_apply_edit(ob, abs(active_face_set), mode, modify_hidden); SCULPT_undo_push_end(ob); face_set_edit_do_post_visibility_updates(ob, nodes, totnode); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index bf47b64d176..7bc947fe215 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -98,6 +98,13 @@ typedef struct { const float *col; } SculptOrigVertData; +typedef struct SculptOrigFaceData { + struct SculptUndoNode *unode; + struct BMLog *bm_log; + const int *face_sets; + int face_set; +} SculptOrigFaceData; + /* Flood Fill. */ typedef struct { GSQueue *queue; @@ -201,6 +208,9 @@ typedef struct SculptUndoNode { /* Sculpt Face Sets */ int *face_sets; + PBVHFaceRef *faces; + int faces_num; + size_t undo_size; } SculptUndoNode; @@ -1034,6 +1044,9 @@ int SCULPT_active_face_set_get(SculptSession *ss); int SCULPT_vertex_face_set_get(SculptSession *ss, PBVHVertRef vertex); void SCULPT_vertex_face_set_set(SculptSession *ss, PBVHVertRef vertex, int face_set); +int SCULPT_face_set_get(const SculptSession *ss, PBVHFaceRef face); +void SCULPT_face_set_set(SculptSession *ss, PBVHFaceRef face, int fset); + bool SCULPT_vertex_has_face_set(SculptSession *ss, PBVHVertRef vertex, int face_set); bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, PBVHVertRef vertex); @@ -1066,6 +1079,25 @@ void SCULPT_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter void SCULPT_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, struct SculptUndoNode *unode); +/** + * Initialize a #SculptOrigFaceData for accessing original face data; + * handles #BMesh, #Mesh, and multi-resolution. + */ +void SCULPT_orig_face_data_init(SculptOrigFaceData *data, + Object *ob, + PBVHNode *node, + SculptUndoType type); +/** + * Update a #SculptOrigFaceData for a particular vertex from the PBVH iterator. + */ +void SCULPT_orig_face_data_update(SculptOrigFaceData *orig_data, PBVHFaceIter *iter); +/** + * Initialize a #SculptOrigVertData for accessing original vertex data; + * handles #BMesh, #Mesh, and multi-resolution. + */ +void SCULPT_orig_face_data_unode_init(SculptOrigFaceData *data, + Object *ob, + struct SculptUndoNode *unode); /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c index 1b8cc5347ac..74b3ca95389 100644 --- a/source/blender/editors/sculpt_paint/sculpt_mask_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_mask_expand.c @@ -86,6 +86,8 @@ static void sculpt_expand_task_cb(void *__restrict userdata, PBVHVertRef active_vertex = SCULPT_active_vertex_get(ss); int active_vertex_i = BKE_pbvh_vertex_to_index(ss->pbvh, active_vertex); + bool face_sets_changed = false; + BKE_pbvh_vertex_iter_begin (ss->pbvh, node, vd, PBVH_ITER_ALL) { int vi = vd.index; float final_mask = *vd.mask; @@ -111,6 +113,7 @@ static void sculpt_expand_task_cb(void *__restrict userdata, if (data->mask_expand_create_face_set) { if (final_mask == 1.0f) { SCULPT_vertex_face_set_set(ss, vd.vertex, ss->filter_cache->new_face_set); + face_sets_changed = true; } BKE_pbvh_node_mark_redraw(node); } @@ -131,6 +134,10 @@ static void sculpt_expand_task_cb(void *__restrict userdata, } } BKE_pbvh_vertex_iter_end; + + if (face_sets_changed) { + SCULPT_undo_push_node(data->ob, node, SCULPT_UNDO_FACE_SETS); + } } static int sculpt_mask_expand_modal(bContext *C, wmOperator *op, const wmEvent *event) @@ -353,9 +360,9 @@ static int sculpt_mask_expand_invoke(bContext *C, wmOperator *op, const wmEvent SCULPT_undo_push_begin(ob, op); if (create_face_set) { - SCULPT_undo_push_node(ob, ss->filter_cache->nodes[0], SCULPT_UNDO_FACE_SETS); for (int i = 0; i < ss->filter_cache->totnode; i++) { BKE_pbvh_node_mark_redraw(ss->filter_cache->nodes[i]); + SCULPT_undo_push_node(ob, ss->filter_cache->nodes[i], SCULPT_UNDO_FACE_SETS); } } else { diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 590efba4642..81bb90d88ee 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -292,6 +292,7 @@ struct PartialUpdateData { bool *modified_hidden_verts; bool *modified_mask_verts; bool *modified_color_verts; + bool *modified_face_set_faces; }; /** @@ -350,6 +351,16 @@ static void update_cb_partial(PBVHNode *node, void *userdata) } } } + if (data->modified_face_set_faces) { + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (data->pbvh, node, fd) { + if (data->modified_face_set_faces[fd.index]) { + BKE_pbvh_node_mark_update_face_sets(node); + break; + } + } + BKE_pbvh_face_iter_end(fd); + } } static bool test_swap_v3_v3(float a[3], float b[3]) @@ -600,24 +611,32 @@ static bool sculpt_undo_restore_mask(bContext *C, SculptUndoNode *unode, bool *m return true; } -static bool sculpt_undo_restore_face_sets(bContext *C, SculptUndoNode *unode) +static bool sculpt_undo_restore_face_sets(bContext *C, + SculptUndoNode *unode, + bool *modified_face_set_faces) { const Scene *scene = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); BKE_view_layer_synced_ensure(scene, view_layer); Object *ob = BKE_view_layer_active_object_get(view_layer); Mesh *me = BKE_object_get_original_mesh(ob); + SculptSession *ss = ob->sculpt; - int *face_sets = CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, ".sculpt_face_set"); - if (!face_sets) { - face_sets = CustomData_add_layer_named( - &me->pdata, CD_PROP_INT32, CD_CONSTRUCT, NULL, me->totpoly, ".sculpt_face_set"); + ss->face_sets = BKE_sculpt_face_sets_ensure(me); + BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets); + + bool modified = false; + + for (int i = 0; i < unode->faces_num; i++) { + int face_index = unode->faces[i].i; + + SWAP(int, unode->face_sets[i], ss->face_sets[face_index]); + + modified_face_set_faces[face_index] = unode->face_sets[i] != ss->face_sets[face_index]; + modified |= modified_face_set_faces[face_index]; } - for (int i = 0; i < me->totpoly; i++) { - SWAP(int, face_sets[i], unode->face_sets[i]); - } - return false; + return modified; } static void sculpt_undo_bmesh_restore_generic_task_cb( @@ -864,6 +883,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase SubdivCCG *subdiv_ccg = ss->subdiv_ccg; SculptUndoNode *unode; bool update = false, rebuild = false, update_mask = false, update_visibility = false; + bool update_face_sets = false; bool need_mask = false; bool need_refine_subdiv = false; bool clear_automask_cache = false; @@ -892,34 +912,6 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - if (lb->first) { - unode = lb->first; - if (unode->type == SCULPT_UNDO_FACE_SETS) { - sculpt_undo_restore_face_sets(C, unode); - - rebuild = true; - BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); - - BKE_sculpt_update_object_for_edit(depsgraph, ob, true, need_mask, false); - - SCULPT_visibility_sync_all_from_faces(ob); - - BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateVisibility); - - if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { - BKE_mesh_flush_hidden_from_verts(ob->data); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - if (!BKE_sculptsession_use_pbvh_draw(ob, rv3d)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } - - unode->applied = true; - return; - } - } - if (lb->first != NULL) { /* Only do early object update for edits if first node needs this. * Undo steps like geometry does not need object to be updated before they run and will @@ -934,12 +926,13 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } - /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep track - * of other updated. In order to tell the corresponding PBVH nodes to update, keep track of which - * elements were updated for specific layers. */ + /* The PBVH already keeps track of which vertices need updated normals, but it doesn't keep + * track of other updated. In order to tell the corresponding PBVH nodes to update, keep track + * of which elements were updated for specific layers. */ bool *modified_hidden_verts = NULL; bool *modified_mask_verts = NULL; bool *modified_color_verts = NULL; + bool *modified_face_set_faces = NULL; char *undo_modified_grids = NULL; bool use_multires_undo = false; @@ -990,6 +983,14 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } break; case SCULPT_UNDO_FACE_SETS: + if (modified_face_set_faces == NULL) { + modified_face_set_faces = MEM_calloc_arrayN( + BKE_pbvh_num_faces(ss->pbvh), sizeof(bool), __func__); + } + if (sculpt_undo_restore_face_sets(C, unode, modified_face_set_faces)) { + update = true; + update_face_sets = true; + } break; case SCULPT_UNDO_COLOR: if (modified_color_verts == NULL) { @@ -1049,6 +1050,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase .modified_hidden_verts = modified_hidden_verts, .modified_mask_verts = modified_mask_verts, .modified_color_verts = modified_color_verts, + .modified_face_set_faces = modified_face_set_faces, }; BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb_partial, &data); BKE_pbvh_update_bounds(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw); @@ -1056,6 +1058,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase if (update_mask) { BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask); } + if (update_face_sets) { + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_RebuildDrawBuffers); + } if (update_visibility) { if (ELEM(BKE_pbvh_type(ss->pbvh), PBVH_FACES, PBVH_GRIDS)) { @@ -1098,6 +1104,7 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase MEM_SAFE_FREE(modified_hidden_verts); MEM_SAFE_FREE(modified_mask_verts); MEM_SAFE_FREE(modified_color_verts); + MEM_SAFE_FREE(modified_face_set_faces); MEM_SAFE_FREE(undo_modified_grids); } @@ -1121,6 +1128,9 @@ static void sculpt_undo_free_list(ListBase *lb) if (unode->index) { MEM_freeN(unode->index); } + if (unode->faces) { + MEM_freeN(unode->faces); + } if (unode->loop_index) { MEM_freeN(unode->loop_index); } @@ -1270,6 +1280,23 @@ static SculptUndoNode *sculpt_undo_find_or_alloc_node_type(Object *object, Sculp return sculpt_undo_alloc_node_type(object, type); } +static void sculpt_undo_store_faces(SculptSession *ss, SculptUndoNode *unode) +{ + unode->faces_num = 0; + + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, unode->node, fd) { + unode->faces_num++; + } + BKE_pbvh_face_iter_end(fd); + + unode->faces = MEM_malloc_arrayN(sizeof(*unode->faces), unode->faces_num, __func__); + BKE_pbvh_face_iter_begin (ss->pbvh, unode->node, fd) { + unode->faces[fd.i] = fd.face; + } + BKE_pbvh_face_iter_end(fd); +} + static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, SculptUndoType type) { UndoSculpt *usculpt = sculpt_undo_get_nodes(); @@ -1292,6 +1319,7 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt } bool need_loops = type == SCULPT_UNDO_COLOR; + const bool need_faces = type == SCULPT_UNDO_FACE_SETS; if (need_loops) { int totloop; @@ -1306,6 +1334,12 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt usculpt->undo_size += alloc_size; } + if (need_faces) { + sculpt_undo_store_faces(ss, unode); + const size_t alloc_size = sizeof(*unode->faces) * (size_t)unode->faces_num; + usculpt->undo_size += alloc_size; + } + switch (type) { case SCULPT_UNDO_COORDS: { size_t alloc_size = sizeof(*unode->co) * (size_t)allvert; @@ -1356,9 +1390,14 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, Sculpt case SCULPT_UNDO_DYNTOPO_END: case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert_msg(0, "Dynamic topology should've already been handled"); - case SCULPT_UNDO_GEOMETRY: - case SCULPT_UNDO_FACE_SETS: break; + case SCULPT_UNDO_GEOMETRY: + break; + case SCULPT_UNDO_FACE_SETS: { + const size_t alloc_size = sizeof(*unode->face_sets) * (size_t)unode->faces_num; + usculpt->undo_size += alloc_size; + break; + } } if (maxgrid) { @@ -1488,32 +1527,15 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType return unode; } -static SculptUndoNode *sculpt_undo_face_sets_push(Object *ob, SculptUndoType type) +static void sculpt_undo_store_face_sets(SculptSession *ss, SculptUndoNode *unode) { - UndoSculpt *usculpt = sculpt_undo_get_nodes(); - SculptUndoNode *unode = MEM_callocN(sizeof(*unode), __func__); + unode->face_sets = MEM_malloc_arrayN(sizeof(*unode->face_sets), unode->faces_num, __func__); - BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); - unode->type = type; - unode->applied = true; - - Mesh *me = BKE_object_get_original_mesh(ob); - - unode->face_sets = MEM_callocN(me->totpoly * sizeof(int), "sculpt face sets"); - - const int *face_sets = CustomData_get_layer_named(&me->pdata, CD_PROP_INT32, ".sculpt_face_set"); - if (face_sets) { - for (int i = 0; i < me->totpoly; i++) { - unode->face_sets[i] = face_sets[i]; - } + PBVHFaceIter fd; + BKE_pbvh_face_iter_begin (ss->pbvh, unode->node, fd) { + unode->face_sets[fd.i] = fd.face_set ? *fd.face_set : SCULPT_FACE_SET_NONE; } - else { - memset(unode->face_sets, SCULPT_FACE_SET_NONE, sizeof(int) * me->totpoly); - } - - BLI_addtail(&usculpt->nodes, unode); - - return unode; + BKE_pbvh_face_iter_end(fd); } static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, PBVHNode *node, SculptUndoType type) @@ -1616,11 +1638,6 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType BLI_thread_unlock(LOCK_CUSTOM1); return unode; } - if (type == SCULPT_UNDO_FACE_SETS) { - unode = sculpt_undo_face_sets_push(ob, type); - BLI_thread_unlock(LOCK_CUSTOM1); - return unode; - } if ((unode = SCULPT_undo_get_node(node, type))) { BLI_thread_unlock(LOCK_CUSTOM1); return unode; @@ -1678,7 +1695,9 @@ SculptUndoNode *SCULPT_undo_push_node(Object *ob, PBVHNode *node, SculptUndoType case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: BLI_assert_msg(0, "Dynamic topology should've already been handled"); case SCULPT_UNDO_GEOMETRY: + break; case SCULPT_UNDO_FACE_SETS: + sculpt_undo_store_face_sets(ss, unode); break; }