diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt_undo.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_undo.c | 278 |
1 files changed, 247 insertions, 31 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 1b3fd24ae22..c828e8c8651 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -65,6 +65,7 @@ #include "GPU_buffers.h" #include "ED_sculpt.h" +#include "bmesh.h" #include "paint_intern.h" #include "sculpt_intern.h" @@ -72,10 +73,10 @@ static void update_cb(PBVHNode *node, void *rebuild) { - BLI_pbvh_node_mark_update(node); + BKE_pbvh_node_mark_update(node); if (*((int *)rebuild)) - BLI_pbvh_node_mark_rebuild_draw(node); - BLI_pbvh_node_fully_hidden_set(node, 0); + BKE_pbvh_node_mark_rebuild_draw(node); + BKE_pbvh_node_fully_hidden_set(node, 0); } static void sculpt_undo_restore_deformed(const SculptSession *ss, @@ -142,7 +143,7 @@ static int sculpt_undo_restore_coords(bContext *C, DerivedMesh *dm, SculptUndoNo /* pbvh uses it's own mvert array, so coords should be */ /* propagated to pbvh here */ - BLI_pbvh_apply_vertCos(ss->pbvh, vertCos); + BKE_pbvh_apply_vertCos(ss->pbvh, vertCos); MEM_freeN(vertCos); } @@ -261,6 +262,111 @@ static int sculpt_undo_restore_mask(bContext *C, DerivedMesh *dm, SculptUndoNode return 1; } +static void sculpt_undo_bmesh_restore_generic(SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + BM_log_undo(ss->bm, ss->bm_log); + unode->applied = FALSE; + } + else { + BM_log_redo(ss->bm, ss->bm_log); + unode->applied = TRUE; + } + + /* A bit lame, but for now just recreate the PBVH. The alternative + * is to store changes to the PBVH in the undo stack. */ + sculpt_pbvh_clear(ob); +} + +/* Create empty sculpt BMesh and enable logging */ +static void sculpt_undo_bmesh_enable(Object *ob, + SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + /* Create empty BMesh and enable logging */ + ss->bm = BM_mesh_create(&bm_mesh_allocsize_default); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* Restore the BMLog using saved entries */ + ss->bm_log = BM_log_from_existing_entries_create(ss->bm, + unode->bm_entry); +} + +static void sculpt_undo_bmesh_restore_begin(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + sculpt_dynamic_topology_disable(C, unode); + unode->applied = FALSE; + } + else { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the first log entry */ + BM_log_redo(ss->bm, ss->bm_log); + + unode->applied = TRUE; + } +} + +static void sculpt_undo_bmesh_restore_end(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + if (unode->applied) { + sculpt_undo_bmesh_enable(ob, unode); + + /* Restore the mesh from the last log entry */ + BM_log_undo(ss->bm, ss->bm_log); + + unode->applied = FALSE; + } + else { + /* Disable dynamic topology sculpting */ + sculpt_dynamic_topology_disable(C, NULL); + unode->applied = TRUE; + } +} + +/* Handle all dynamic-topology updates + * + * Returns TRUE if this was a dynamic-topology undo step, otherwise + * returns FALSE to indicate the non-dyntopo code should run. */ +static int sculpt_undo_bmesh_restore(bContext *C, + SculptUndoNode *unode, + Object *ob, + SculptSession *ss) +{ + switch (unode->type) { + case SCULPT_UNDO_DYNTOPO_BEGIN: + sculpt_undo_bmesh_restore_begin(C, unode, ob, ss); + return TRUE; + + case SCULPT_UNDO_DYNTOPO_END: + sculpt_undo_bmesh_restore_end(C, unode, ob, ss); + return TRUE; + + default: + if (ss->bm_log) { + sculpt_undo_bmesh_restore_generic(unode, ob, ss); + return TRUE; + } + break; + } + + return FALSE; +} + static void sculpt_undo_restore(bContext *C, ListBase *lb) { Scene *scene = CTX_data_scene(C); @@ -289,6 +395,9 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) /* call _after_ sculpt_update_mesh_elements() which may update 'ob->derivedFinal' */ dm = mesh_get_derived_final(scene, ob, 0); + if (lb->first && sculpt_undo_bmesh_restore(C, lb->first, ob, ss)) + return; + for (unode = lb->first; unode; unode = unode->next) { if (!(strcmp(unode->idname, ob->id.name) == 0)) continue; @@ -306,9 +415,6 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) continue; } } - else { - continue; - } switch (unode->type) { case SCULPT_UNDO_COORDS: @@ -323,6 +429,12 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) if (sculpt_undo_restore_mask(C, dm, unode)) update = TRUE; break; + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } } @@ -331,8 +443,8 @@ static void sculpt_undo_restore(bContext *C, ListBase *lb) /* we update all nodes still, should be more clever, but also * needs to work correct when exiting/entering sculpt mode and * the nodes get recreated, though in that case it could do all */ - BLI_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); - BLI_pbvh_update(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw, NULL); + BKE_pbvh_search_callback(ss->pbvh, NULL, NULL, update_cb, &rebuild); + BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB | PBVH_UpdateOriginalBB | PBVH_UpdateRedraw, NULL); if ((mmd = sculpt_multires_active(scene, ob))) { if (rebuild) @@ -374,8 +486,6 @@ static void sculpt_undo_free(ListBase *lb) MEM_freeN(unode->index); if (unode->grids) MEM_freeN(unode->grids); - if (unode->layer_disp) - MEM_freeN(unode->layer_disp); if (unode->orig_co) MEM_freeN(unode->orig_co); if (unode->vert_hidden) @@ -389,6 +499,17 @@ static void sculpt_undo_free(ListBase *lb) } if (unode->mask) MEM_freeN(unode->mask); + if (unode->bm_entry) { + BM_log_entry_drop(unode->bm_entry); + } + if (unode->bm_enter_totvert) + CustomData_free(&unode->bm_enter_vdata, unode->bm_enter_totvert); + if (unode->bm_enter_totedge) + CustomData_free(&unode->bm_enter_edata, unode->bm_enter_totedge); + if (unode->bm_enter_totloop) + CustomData_free(&unode->bm_enter_ldata, unode->bm_enter_totloop); + if (unode->bm_enter_totpoly) + CustomData_free(&unode->bm_enter_pdata, unode->bm_enter_totpoly); } } @@ -410,9 +531,9 @@ static void sculpt_undo_alloc_and_store_hidden(PBVH *pbvh, BLI_bitmap *grid_hidden; int i, *grid_indices, totgrid; - grid_hidden = BLI_pbvh_grid_hidden(pbvh); + grid_hidden = BKE_pbvh_grid_hidden(pbvh); - BLI_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, + BKE_pbvh_node_get_grids(pbvh, node, &grid_indices, &totgrid, NULL, NULL, NULL, NULL); unode->grid_hidden = MEM_mapallocN(sizeof(BLI_bitmap) * totgrid, @@ -439,11 +560,13 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, unode->type = type; unode->node = node; - BLI_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, - &maxgrid, &gridsize, NULL, NULL); + if (node) { + BKE_pbvh_node_num_verts(ss->pbvh, node, &totvert, &allvert); + BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, + &maxgrid, &gridsize, NULL, NULL); - unode->totvert = totvert; + unode->totvert = totvert; + } /* we will use this while sculpting, is mapalloc slow to access then? */ @@ -468,6 +591,11 @@ static SculptUndoNode *sculpt_undo_alloc_node(Object *ob, PBVHNode *node, unode->mask = MEM_mapallocN(sizeof(float) * allvert, "SculptUndoNode.mask"); undo_paint_push_count_alloc(UNDO_PAINT_MESH, (sizeof(float) * sizeof(int)) * allvert); break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } BLI_addtail(lb, unode); @@ -496,7 +624,7 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BLI_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) + BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { copy_v3_v3(unode->co[vd.i], vd.co); if (vd.no) copy_v3_v3_short(unode->no[vd.i], vd.no); @@ -505,7 +633,7 @@ static void sculpt_undo_store_coords(Object *ob, SculptUndoNode *unode) if (ss->modifiers_active) copy_v3_v3(unode->orig_co[vd.i], ss->orig_cos[unode->index[vd.i]]); } - BLI_pbvh_vertex_iter_end; + BKE_pbvh_vertex_iter_end; } static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) @@ -521,8 +649,8 @@ static void sculpt_undo_store_hidden(Object *ob, SculptUndoNode *unode) int *vert_indices, allvert; int i; - BLI_pbvh_node_num_verts(pbvh, node, NULL, &allvert); - BLI_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); + BKE_pbvh_node_num_verts(pbvh, node, NULL, &allvert); + BKE_pbvh_node_get_verts(pbvh, node, &vert_indices, &mvert); for (i = 0; i < allvert; i++) { BLI_BITMAP_MODIFY(unode->vert_hidden, i, mvert[vert_indices[i]].flag & ME_HIDE); @@ -535,11 +663,85 @@ static void sculpt_undo_store_mask(Object *ob, SculptUndoNode *unode) SculptSession *ss = ob->sculpt; PBVHVertexIter vd; - BLI_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) + BKE_pbvh_vertex_iter_begin(ss->pbvh, unode->node, vd, PBVH_ITER_ALL) { unode->mask[vd.i] = *vd.mask; } - BLI_pbvh_vertex_iter_end; + BKE_pbvh_vertex_iter_end; +} + +static SculptUndoNode *sculpt_undo_bmesh_push(Object *ob, + PBVHNode *node, + SculptUndoType type) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_MESH); + SculptUndoNode *unode = lb->first; + SculptSession *ss = ob->sculpt; + PBVHVertexIter vd; + + if (!lb->first) { + unode = MEM_callocN(sizeof(*unode), AT); + + BLI_strncpy(unode->idname, ob->id.name, sizeof(unode->idname)); + unode->type = type; + unode->applied = TRUE; + + if (type == SCULPT_UNDO_DYNTOPO_END) { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_before_all_removed(ss->bm, ss->bm_log); + } + else if (type == SCULPT_UNDO_DYNTOPO_BEGIN) { + Mesh *me = ob->data; + + /* Store a copy of the mesh's current vertices, loops, and + * polys. A full copy like this is needed because entering + * dynamic-topology immediately does topological edits + * (converting polys to triangles) that the BMLog can't + * fully restore from */ + CustomData_copy(&me->vdata, &unode->bm_enter_vdata, CD_MASK_MESH, + CD_DUPLICATE, me->totvert); + CustomData_copy(&me->edata, &unode->bm_enter_edata, CD_MASK_MESH, + CD_DUPLICATE, me->totedge); + CustomData_copy(&me->ldata, &unode->bm_enter_ldata, CD_MASK_MESH, + CD_DUPLICATE, me->totloop); + CustomData_copy(&me->pdata, &unode->bm_enter_pdata, CD_MASK_MESH, + CD_DUPLICATE, me->totpoly); + unode->bm_enter_totvert = me->totvert; + unode->bm_enter_totedge = me->totedge; + unode->bm_enter_totloop = me->totloop; + unode->bm_enter_totpoly = me->totpoly; + + unode->bm_entry = BM_log_entry_add(ss->bm_log); + BM_log_all_added(ss->bm, ss->bm_log); + } + else { + unode->bm_entry = BM_log_entry_add(ss->bm_log); + } + + BLI_addtail(lb, unode); + } + + if (node) { + switch (type) { + case SCULPT_UNDO_COORDS: + case SCULPT_UNDO_HIDDEN: + case SCULPT_UNDO_MASK: + /* Before any vertex values get modified, ensure their + * original positions are logged */ + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) { + BM_log_vert_before_modified(ss->bm, ss->bm_log, vd.bm_vert); + } + BKE_pbvh_vertex_iter_end; + break; + + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + break; + } + } + + return unode; } SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, @@ -551,7 +753,18 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, /* list is manipulated by multiple threads, so we lock */ BLI_lock_thread(LOCK_CUSTOM1); - if ((unode = sculpt_undo_get_node(node))) { + if (ss->bm || + ELEM(type, + SCULPT_UNDO_DYNTOPO_BEGIN, + SCULPT_UNDO_DYNTOPO_END)) + { + /* Dynamic topology stores only one undo node per stroke, + * regardless of the number of PBVH nodes modified */ + unode = sculpt_undo_bmesh_push(ob, node, type); + BLI_unlock_thread(LOCK_CUSTOM1); + return unode; + } + else if ((unode = sculpt_undo_get_node(node))) { BLI_unlock_thread(LOCK_CUSTOM1); return unode; } @@ -564,14 +777,14 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, if (unode->grids) { int totgrid, *grids; - BLI_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, + BKE_pbvh_node_get_grids(ss->pbvh, node, &grids, &totgrid, NULL, NULL, NULL, NULL); memcpy(unode->grids, grids, sizeof(int) * totgrid); } else { int *vert_indices, allvert; - BLI_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); - BLI_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); + BKE_pbvh_node_num_verts(ss->pbvh, node, NULL, &allvert); + BKE_pbvh_node_get_verts(ss->pbvh, node, &vert_indices, NULL); memcpy(unode->index, vert_indices, sizeof(int) * unode->totvert); } @@ -585,6 +798,11 @@ SculptUndoNode *sculpt_undo_push_node(Object *ob, PBVHNode *node, case SCULPT_UNDO_MASK: sculpt_undo_store_mask(ob, unode); break; + case SCULPT_UNDO_DYNTOPO_BEGIN: + case SCULPT_UNDO_DYNTOPO_END: + case SCULPT_UNDO_DYNTOPO_SYMMETRIZE: + BLI_assert(!"Dynamic topology should've already been handled"); + break; } /* store active shape key */ @@ -612,10 +830,8 @@ void sculpt_undo_push_end(void) unode->no = NULL; } - if (unode->layer_disp) { - MEM_freeN(unode->layer_disp); - unode->layer_disp = NULL; - } + if (unode->node) + BKE_pbvh_node_layer_disp_free(unode->node); } undo_paint_push_end(UNDO_PAINT_MESH); |