Fix T63101: Blender crashes on adding any object to collection duplicated with added...
authorBastien Montagne <montagne29@wanadoo.fr>
Mon, 1 Apr 2019 19:10:25 +0000 (21:10 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Mon, 1 Apr 2019 19:15:43 +0000 (21:15 +0200)
Issue was that (deep) duplication code of scene ended up leaving
children collections of new master one without any parent.

Note that even though I think that fix is OK for now, we should really
make 'deep' duplication of IDs part of the generic ID management code.
Am less and less happy with current handling of this, done half from
/editors code, half from some semi-specialized helpers from /blenkernel,
with sometimes nearly the same logic replicated several times for
slightly different needs, etc. Unfortunately this would not be a small
refactor, so it will have to wait...

source/blender/editors/object/object_relations.c

index dbee2e7..ec6c105 100644 (file)
@@ -1602,7 +1602,7 @@ static void libblock_relink_collection(Collection *collection)
        }
 }
 
-static void single_object_users_collection(
+static Collection *single_object_users_collection(
         Main *bmain, Scene *scene, Collection *collection,
         const int flag, const bool copy_collections, const bool is_master_collection)
 {
@@ -1623,9 +1623,24 @@ static void single_object_users_collection(
                }
        }
 
-       for (CollectionChild *child = collection->children.first; child; child = child->next) {
-               single_object_users_collection(bmain, scene, child->collection, flag, copy_collections, false);
+       /* Since master collection has already be duplicated as part of scene copy, we do not duplictae it here.
+        * However, this means its children need to be re-added manually here, otherwise their parent lists are empty
+        * (which will lead to crashes, see T63101). */
+       CollectionChild *child_next, *child = collection->children.first;
+       if (is_master_collection) {
+               BLI_listbase_clear(&collection->children);
+       }
+       for (; child; child = child_next) {
+               child_next = child->next;
+               Collection *collection_child_new = single_object_users_collection(
+                                                      bmain, scene, child->collection, flag, copy_collections, false);
+               if (is_master_collection) {
+                       BKE_collection_child_add(bmain, collection, collection_child_new);
+                       MEM_freeN(child);
+               }
        }
+
+       return collection;
 }
 
 /* Warning, sets ID->newid pointers of objects and collections, but does not clear them. */
@@ -1679,9 +1694,7 @@ static void single_object_users(Main *bmain, Scene *scene, View3D *v3d, const in
        }
 
        /* Making single user may affect other scenes if they share with current one some collections in their ViewLayer. */
-       for (Scene *sce = bmain->scenes.first; sce != NULL; sce = sce->id.next) {
-               BKE_scene_collection_sync(sce);
-       }
+       BKE_main_collection_sync(bmain);
 }
 
 /* not an especially efficient function, only added so the single user