diff --git a/source/blender/editors/include/ED_sculpt.h b/source/blender/editors/include/ED_sculpt.h index 26871cf8dd0..e61c7be5216 100644 --- a/source/blender/editors/include/ED_sculpt.h +++ b/source/blender/editors/include/ED_sculpt.h @@ -54,6 +54,11 @@ void ED_sculpt_undosys_type(struct UndoType *ut); void ED_sculpt_undo_geometry_begin(struct Object *ob, const char *name); void ED_sculpt_undo_geometry_end(struct Object *ob); +/* Undo for changes happening on a base mesh for multires sculpting. + * if there is no multires sculpt active regular undo is used. */ +void ED_sculpt_undo_push_multires_mesh_begin(struct bContext *C, const char *str); +void ED_sculpt_undo_push_multires_mesh_end(struct bContext *C, const char *str); + #ifdef __cplusplus } #endif diff --git a/source/blender/editors/object/object_modifier.c b/source/blender/editors/object/object_modifier.c index 2babf27eb61..a24f3ba2269 100644 --- a/source/blender/editors/object/object_modifier.c +++ b/source/blender/editors/object/object_modifier.c @@ -86,6 +86,7 @@ #include "ED_mesh.h" #include "ED_object.h" #include "ED_screen.h" +#include "ED_sculpt.h" #include "WM_api.h" #include "WM_types.h" @@ -1697,8 +1698,12 @@ static int multires_base_apply_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + ED_sculpt_undo_push_multires_mesh_begin(C, op->type->name); + multiresModifier_base_apply(depsgraph, object, mmd); + ED_sculpt_undo_push_multires_mesh_end(C, op->type->name); + DEG_id_tag_update(&object->id, ID_RECALC_GEOMETRY); WM_event_add_notifier(C, NC_OBJECT | ND_MODIFIER, object); @@ -1726,7 +1731,7 @@ void OBJECT_OT_multires_base_apply(wmOperatorType *ot) ot->exec = multires_base_apply_exec; /* flags */ - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_INTERNAL; + ot->flag = OPTYPE_REGISTER | OPTYPE_INTERNAL; edit_modifier_properties(ot); } diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index e2fd54596e7..b379c1ab8af 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -478,6 +478,7 @@ typedef struct SculptUndoNode { * the object when undoing the operation * * Modified geometry is stored after the modification and is used to redo the modification. */ + bool geometry_clear_pbvh; SculptUndoNodeGeometry geometry_original; SculptUndoNodeGeometry geometry_modified; diff --git a/source/blender/editors/sculpt_paint/sculpt_undo.c b/source/blender/editors/sculpt_paint/sculpt_undo.c index 340f7191b95..c07ebf790d4 100644 --- a/source/blender/editors/sculpt_paint/sculpt_undo.c +++ b/source/blender/editors/sculpt_paint/sculpt_undo.c @@ -55,6 +55,9 @@ #include "BKE_subsurf.h" #include "BKE_undo_system.h" +// XXX: Ideally should be no direct call to such low level things. +#include "BKE_subdiv_eval.h" + #include "DEG_depsgraph.h" #include "WM_api.h" @@ -552,7 +555,9 @@ static void sculpt_undo_geometry_free_data(SculptUndoNodeGeometry *geometry) static void sculpt_undo_geometry_restore(SculptUndoNode *unode, Object *object) { - SCULPT_pbvh_clear(object); + if (unode->geometry_clear_pbvh) { + SCULPT_pbvh_clear(object); + } if (unode->applied) { sculpt_undo_geometry_restore_data(&unode->geometry_modified, object); @@ -740,6 +745,10 @@ static void sculpt_undo_restore_list(bContext *C, Depsgraph *depsgraph, ListBase } } + if (subdiv_ccg != NULL) { + BKE_subdiv_eval_refine_from_mesh(subdiv_ccg->subdiv, ob->data, NULL); + } + if (update || rebuild) { bool tag_update = false; /* We update all nodes still, should be more clever, but also @@ -1079,6 +1088,7 @@ static SculptUndoNode *sculpt_undo_geometry_push(Object *object, SculptUndoType { SculptUndoNode *unode = sculpt_undo_find_or_alloc_node_type(object, type); unode->applied = false; + unode->geometry_clear_pbvh = true; SculptUndoNodeGeometry *geometry = sculpt_undo_geometry_get(unode); sculpt_undo_geometry_store_data(geometry, object); @@ -1510,3 +1520,85 @@ static UndoSculpt *sculpt_undo_get_nodes(void) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Undo for changes happening on a base mesh for multires sculpting. + * + * Use this for multires operators which changes base mesh and which are to be + * possible. Example of such operators is Apply Base. + * + * Usage: + * + * static int operator_exec((bContext *C, wmOperator *op) { + * + * ED_sculpt_undo_push_mixed_begin(C, op->type->name); + * // Modify base mesh. + * ED_sculpt_undo_push_mixed_end(C, op->type->name); + * + * return OPERATOR_FINISHED; + * } + * + * If object is not in sculpt mode or sculpt does not happen on multires then + * regular ED_undo_push() is used. + * * + * \{ */ + +static bool sculpt_undo_use_multires_mesh(bContext *C) +{ + if (BKE_paintmode_get_active_from_context(C) != PAINT_MODE_SCULPT) { + return false; + } + + Object *object = CTX_data_active_object(C); + SculptSession *sculpt_session = object->sculpt; + + return sculpt_session->multires.active; +} + +static void sculpt_undo_push_all_grids(Object *object) +{ + SculptSession *ss = object->sculpt; + PBVHNode **nodes; + int totnodes; + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + for (int i = 0; i < totnodes; i++) { + SculptUndoNode *unode = SCULPT_undo_push_node(object, nodes[i], SCULPT_UNDO_COORDS); + unode->node = NULL; + } + + MEM_SAFE_FREE(nodes); +} + +void ED_sculpt_undo_push_multires_mesh_begin(bContext *C, const char *str) +{ + if (!sculpt_undo_use_multires_mesh(C)) { + return; + } + + Object *object = CTX_data_active_object(C); + + SCULPT_undo_push_begin(str); + + SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); + geometry_unode->geometry_clear_pbvh = false; + + sculpt_undo_push_all_grids(object); +} + +void ED_sculpt_undo_push_multires_mesh_end(bContext *C, const char *str) +{ + if (!sculpt_undo_use_multires_mesh(C)) { + ED_undo_push(C, str); + return; + } + + Object *object = CTX_data_active_object(C); + + SculptUndoNode *geometry_unode = SCULPT_undo_push_node(object, NULL, SCULPT_UNDO_GEOMETRY); + geometry_unode->geometry_clear_pbvh = false; + + SCULPT_undo_push_end(); +} + +/** \} */