From 0964ae5266541f09a1d54d8941393bada97d08a2 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Tue, 4 Feb 2020 18:26:57 +1100 Subject: Fix T73234: Undo/redo with local collection crashes Resolves crashes when edit-mode undo data wasn't included because it wasn't visible, also resolves T73416. --- .../blender/editors/armature/editarmature_undo.c | 6 +- source/blender/editors/curve/editcurve_undo.c | 3 +- source/blender/editors/include/ED_undo.h | 5 + source/blender/editors/lattice/editlattice_undo.c | 6 +- source/blender/editors/mesh/editmesh_undo.c | 6 +- source/blender/editors/metaball/editmball_undo.c | 6 +- source/blender/editors/undo/ed_undo.c | 103 ++++++++++++++++++++- 7 files changed, 120 insertions(+), 15 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index 4e3ab11a9f7..4753698ae40 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -108,7 +108,8 @@ static void undoarm_free_data(UndoArmature *uarm) static Object *editarm_object_from_context(bContext *C) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit && obedit->type == OB_ARMATURE) { bArmature *arm = obedit->data; if (arm->edbo != NULL) { @@ -151,8 +152,7 @@ static bool armature_undosys_step_encode(struct bContext *C, struct Main *bmain, * outside of this list will be moved out of edit-mode when reading back undo steps. */ ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, NULL, &objects_len); + Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len); us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index ff3a1386fd9..8bcabb5ed85 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -216,8 +216,7 @@ static bool curve_undosys_step_encode(struct bContext *C, struct Main *bmain, Un * outside of this list will be moved out of edit-mode when reading back undo steps. */ ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, NULL, &objects_len); + Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len); us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; diff --git a/source/blender/editors/include/ED_undo.h b/source/blender/editors/include/ED_undo.h index fa2630ba726..1fbf2f96cfa 100644 --- a/source/blender/editors/include/ED_undo.h +++ b/source/blender/editors/include/ED_undo.h @@ -23,6 +23,7 @@ #include "BLI_compiler_attrs.h" +struct Base; struct CLG_LogRef; struct Object; struct UndoStack; @@ -62,6 +63,10 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C, uint object_array_len, uint object_array_stride); +struct Object **ED_undo_editmode_objects_from_view_layer(struct ViewLayer *view_layer, + uint *r_len); +struct Base **ED_undo_editmode_bases_from_view_layer(struct ViewLayer *view_layer, uint *r_len); + struct UndoStack *ED_undo_stack_get(void); /* helpers */ diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index ed2560698b6..f90b70c492f 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -146,7 +146,8 @@ static int validate_undoLatt(void *data, void *edata) static Object *editlatt_object_from_context(bContext *C) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit && obedit->type == OB_LATTICE) { Lattice *lt = obedit->data; if (lt->editlatt != NULL) { @@ -189,8 +190,7 @@ static bool lattice_undosys_step_encode(struct bContext *C, Main *bmain, UndoSte * outside of this list will be moved out of edit-mode when reading back undo steps. */ ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, NULL, &objects_len); + Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len); us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index faa80341b0f..951efdc6127 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -672,7 +672,8 @@ static void undomesh_free_data(UndoMesh *um) static Object *editmesh_object_from_context(bContext *C) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit && obedit->type == OB_MESH) { Mesh *me = obedit->data; if (me->edit_mesh != NULL) { @@ -715,8 +716,7 @@ static bool mesh_undosys_step_encode(struct bContext *C, struct Main *bmain, Und * outside of this list will be moved out of edit-mode when reading back undo steps. */ ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, NULL, &objects_len); + Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len); us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index e8700e94e91..165db6c5a06 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -120,7 +120,8 @@ static void undomball_free_data(UndoMBall *umb) static Object *editmball_object_from_context(bContext *C) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); if (obedit && obedit->type == OB_MBALL) { MetaBall *mb = obedit->data; if (mb->editelems != NULL) { @@ -162,8 +163,7 @@ static bool mball_undosys_step_encode(struct bContext *C, struct Main *bmain, Un * outside of this list will be moved out of edit-mode when reading back undo steps. */ ViewLayer *view_layer = CTX_data_view_layer(C); uint objects_len = 0; - Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data( - view_layer, NULL, &objects_len); + Object **objects = ED_undo_editmode_objects_from_view_layer(view_layer, &objects_len); us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); us->elems_len = objects_len; diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index 9770b52158a..d90590bce82 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -746,7 +746,7 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C, uint bases_len = 0; /* Don't request unique data because we want to de-select objects when exiting edit-mode * for that to be done on all objects we can't skip ones that share data. */ - Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(view_layer, NULL, &bases_len); + Base **bases = ED_undo_editmode_bases_from_view_layer(view_layer, &bases_len); for (uint i = 0; i < bases_len; i++) { ((ID *)bases[i]->object->data)->tag |= LIB_TAG_DOIT; } @@ -770,3 +770,104 @@ void ED_undo_object_editmode_restore_helper(struct bContext *C, } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Undo View Layer Helper Functions + * + * Needed because view layer functions such as + * #BKE_view_layer_array_from_objects_in_edit_mode_unique_data also check visibility, + * which is not reliable when it comes to object undo operations, + * since hidden objects can be operated on in the properties editor, + * and local collections may be used. + * \{ */ + +static int undo_editmode_objects_from_view_layer_prepare(ViewLayer *view_layer, + Object *obact, + int *r_active_index) +{ + const short object_type = obact->type; + + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) { + ID *id = ob->data; + id->tag &= ~LIB_TAG_DOIT; + } + } + + int len = 0; + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) { + if (ob == obact) { + *r_active_index = len; + } + ID *id = ob->data; + if ((id->tag & LIB_TAG_DOIT) == 0) { + len += 1; + id->tag |= LIB_TAG_DOIT; + } + } + } + return len; +} + +Object **ED_undo_editmode_objects_from_view_layer(ViewLayer *view_layer, uint *r_len) +{ + Object *obact = OBACT(view_layer); + if ((obact == NULL) || (obact->mode & OB_MODE_EDIT) == 0) { + return MEM_mallocN(0, __func__); + } + int active_index = 0; + const int len = undo_editmode_objects_from_view_layer_prepare(view_layer, obact, &active_index); + const short object_type = obact->type; + int i = 0; + Object **objects = MEM_malloc_arrayN(len, sizeof(*objects), __func__); + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) { + ID *id = ob->data; + if (id->tag & LIB_TAG_DOIT) { + objects[i++] = ob; + id->tag &= ~LIB_TAG_DOIT; + } + } + } + BLI_assert(i == len); + if (active_index > 0) { + SWAP(Object *, objects[0], objects[active_index]); + } + *r_len = len; + return objects; +} + +Base **ED_undo_editmode_bases_from_view_layer(ViewLayer *view_layer, uint *r_len) +{ + Object *obact = OBACT(view_layer); + if ((obact == NULL) || (obact->mode & OB_MODE_EDIT) == 0) { + return MEM_mallocN(0, __func__); + } + int active_index = 0; + const int len = undo_editmode_objects_from_view_layer_prepare(view_layer, obact, &active_index); + const short object_type = obact->type; + int i = 0; + Base **base_array = MEM_malloc_arrayN(len, sizeof(*base_array), __func__); + for (Base *base = view_layer->object_bases.first; base; base = base->next) { + Object *ob = base->object; + if ((ob->type == object_type) && (ob->mode & OB_MODE_EDIT)) { + ID *id = ob->data; + if (id->tag & LIB_TAG_DOIT) { + base_array[i++] = base; + id->tag &= ~LIB_TAG_DOIT; + } + } + } + BLI_assert(i == len); + if (active_index > 0) { + SWAP(Base *, base_array[0], base_array[active_index]); + } + *r_len = len; + return base_array; +} + +/** \} */ -- cgit v1.2.3