Sculpt: Standardize face set undo steps, optimize memory usage

Currently the face set of every single face is saved for every sculpt undo step.
When only changing the face sets of a small section of the mesh, this can be quite
wasteful. It also makes face sets a special case compare to all other sculpt undo step
types, which makes the whole system more complex and harder to improve.

Fixes T101203.

Reviewed By: Hans Goudey
Differential Revision: https://developer.blender.org/D16224
Ref D16224
This commit is contained in:
Joseph Eagar 2022-11-22 11:37:20 -08:00
parent 3f294a37f5
commit df788ecfd9
Notes: blender-bot 2023-05-25 18:08:30 +02:00
Referenced by issue #101203, Regression: Issues with undoing the visibility of face sets in sculpt mode
Referenced by issue #108267, Regression: Face set undo give incorrect result
12 changed files with 555 additions and 122 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;

View File

@ -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;
}

View File

@ -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;
}
}
/** \} */

View File

@ -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;
}
}

View File

@ -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++) {

View File

@ -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);

View File

@ -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);
/** \} */
/* -------------------------------------------------------------------- */

View File

@ -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 {

View File

@ -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;
}