From e72b86d3cba8c7366bee2e92162f3b07bf367f3d Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Wed, 1 Jun 2022 12:44:11 +0200 Subject: Fix T98469: Crash trying to add an object to a linked collection that is linked to multiple scenes. Crash happened because code could not find a valid base in current scene after adding the object, added some checks for that. Root of the issue was wrong assumptions in `BKE_object_add` logic, which would pick the first valid ancestor collection in case initially selected collection was not editable. In some case, this could pick a collection not instanced in the current scene's view layer, leading to not getting a valid base for the newly added object. Addressed this by adding a new variant of `BKE_collection_object_add`, `BKE_collection_viewlayer_object_add`, which ensures final collection is in given viewlayer. --- source/blender/blenkernel/BKE_collection.h | 12 ++++++++++++ source/blender/blenkernel/intern/collection.c | 28 +++++++++++++++++++++++---- source/blender/blenkernel/intern/object.cc | 8 ++++++-- source/blender/editors/object/object_add.cc | 13 ++++++++++--- 4 files changed, 52 insertions(+), 9 deletions(-) diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index a3bbcc8687a..41d369ae9b2 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -115,6 +115,18 @@ bool BKE_collection_is_empty(const struct Collection *collection); bool BKE_collection_object_add(struct Main *bmain, struct Collection *collection, struct Object *ob); + +/** + * Add object to given collection, similar to #BKE_collection_object_add. + * + * However, it additionnally ensures that the selected collection is also part of the given + * `view_layer`, if non-NULL. Otherwise, the object is not added to any collection. + */ +bool BKE_collection_viewlayer_object_add(struct Main *bmain, + const struct ViewLayer *view_layer, + struct Collection *collection, + struct Object *ob); + /** * Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection. * diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 76c6dc1d5e7..b71bcef229a 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -996,9 +996,11 @@ static void collection_tag_update_parent_recursive(Main *bmain, } } -static Collection *collection_parent_editable_find_recursive(Collection *collection) +static Collection *collection_parent_editable_find_recursive(const ViewLayer *view_layer, + Collection *collection) { - if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection)) { + if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection) && + (view_layer == NULL || BKE_view_layer_has_collection(view_layer, collection))) { return collection; } @@ -1009,10 +1011,16 @@ static Collection *collection_parent_editable_find_recursive(Collection *collect LISTBASE_FOREACH (CollectionParent *, collection_parent, &collection->parents) { if (!ID_IS_LINKED(collection_parent->collection) && !ID_IS_OVERRIDE_LIBRARY(collection_parent->collection)) { + if (view_layer != NULL && + !BKE_view_layer_has_collection(view_layer, collection_parent->collection)) { + /* In case this parent collection is not in given view_layer, there is no point in + * searching in its ancestors either, we can skip that whole parenting branch. */ + continue; + } return collection_parent->collection; } Collection *editable_collection = collection_parent_editable_find_recursive( - collection_parent->collection); + view_layer, collection_parent->collection); if (editable_collection != NULL) { return editable_collection; } @@ -1109,12 +1117,24 @@ bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Objec } bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) +{ + return BKE_collection_viewlayer_object_add(bmain, NULL, collection, ob); +} + +bool BKE_collection_viewlayer_object_add(Main *bmain, + const ViewLayer *view_layer, + Collection *collection, + Object *ob) { if (collection == NULL) { return false; } - collection = collection_parent_editable_find_recursive(collection); + collection = collection_parent_editable_find_recursive(view_layer, collection); + + if (collection == NULL) { + return false; + } return BKE_collection_object_add_notest(bmain, collection, ob); } diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 2a25d73ed87..0bc092bec4f 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -2271,10 +2271,14 @@ Object *BKE_object_add(Main *bmain, ViewLayer *view_layer, int type, const char Object *ob = object_add_common(bmain, view_layer, type, name); LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_object_add(bmain, layer_collection->collection, ob); + BKE_collection_viewlayer_object_add(bmain, view_layer, layer_collection->collection, ob); + /* Note: There is no way to be sure that #BKE_collection_viewlayer_object_add will actually + * manage to find a valid collection in given `view_layer` to add the new object to. */ Base *base = BKE_view_layer_base_find(view_layer, ob); - BKE_view_layer_base_select_and_set_active(view_layer, base); + if (base != nullptr) { + BKE_view_layer_base_select_and_set_active(view_layer, base); + } return ob; } diff --git a/source/blender/editors/object/object_add.cc b/source/blender/editors/object/object_add.cc index 20b9844ac89..e486d606e91 100644 --- a/source/blender/editors/object/object_add.cc +++ b/source/blender/editors/object/object_add.cc @@ -621,9 +621,16 @@ Object *ED_object_add_type_with_obdata(bContext *C, else { ob = BKE_object_add(bmain, view_layer, type, name); } - BASACT(view_layer)->local_view_bits = local_view_bits; - /* editor level activate, notifiers */ - ED_object_base_activate(C, view_layer->basact); + + Base *ob_base_act = BASACT(view_layer); + /* While not getting a valid base is not a good thing, it can happen in convoluted corner cases, + * better not crash on it in releases. */ + BLI_assert(ob_base_act != nullptr); + if (ob_base_act != nullptr) { + ob_base_act->local_view_bits = local_view_bits; + /* editor level activate, notifiers */ + ED_object_base_activate(C, ob_base_act); + } /* more editor stuff */ ED_object_base_init_transform_on_add(ob, loc, rot); -- cgit v1.2.3