BKE Collection: Add new function to rebuild parent relationships.
authorBastien Montagne <montagne29@wanadoo.fr>
Wed, 22 May 2019 20:51:36 +0000 (22:51 +0200)
committerBastien Montagne <montagne29@wanadoo.fr>
Wed, 22 May 2019 21:33:27 +0000 (23:33 +0200)
It's not always possible to keep 'by hand' parent relationships valid in
collections hierarchy. Add functions to remake those
(re-using/factorizing code from `readfile.c` `lib_link_collection_data()`
function).

Can't stress again how painful it is to have those kind of backward
relationships in our data structures, those *always* end up being
serious issues to keep in sync... Should only be generated on the fly
when needed, period. :(

source/blender/blenkernel/BKE_collection.h
source/blender/blenkernel/intern/collection.c
source/blender/blenloader/intern/readfile.c

index 0e093bb..502c949 100644 (file)
@@ -163,6 +163,9 @@ bool BKE_collection_find_cycle(struct Collection *new_ancestor, struct Collectio
 
 bool BKE_collection_has_collection(struct Collection *parent, struct Collection *collection);
 
+void BKE_collection_parent_relations_rebuild(struct Collection *collection);
+void BKE_main_collections_parent_relations_rebuild(struct Main *bmain);
+
 /* Iteration callbacks. */
 
 typedef void (*BKE_scene_objects_Cb)(struct Object *ob, void *data);
index d2ca304..68392bf 100644 (file)
@@ -1056,6 +1056,72 @@ bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *ch
   return true;
 }
 
+/**
+ * Rebuild parent relationships from child ones, for all children of given \a collection.
+ *
+ * \note Given collection is assumed to already have valid parents.
+ */
+void BKE_collection_parent_relations_rebuild(Collection *collection)
+{
+  for (CollectionChild *child = collection->children.first, *child_next = NULL; child;
+       child = child_next) {
+    child_next = child->next;
+
+    if (child->collection == NULL || BKE_collection_find_cycle(collection, child->collection)) {
+      BLI_freelinkN(&collection->children, child);
+    }
+    else {
+      CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__);
+      cparent->collection = collection;
+      BLI_addtail(&child->collection->parents, cparent);
+    }
+  }
+}
+
+static void collection_parents_rebuild_recursive(Collection *collection)
+{
+  BKE_collection_parent_relations_rebuild(collection);
+  collection->id.tag &= ~LIB_TAG_DOIT;
+
+  for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
+    collection_parents_rebuild_recursive(child->collection);
+  }
+}
+
+/**
+ * Rebuild parent relationships from child ones, for all collections in given \a bmain.
+ *
+ * \note Uses LIB_TAG_DOIT internally...
+ */
+void BKE_main_collections_parent_relations_rebuild(Main *bmain)
+{
+  /* Only collections not in bmain (master ones in scenes) have no parent... */
+  for (Collection *collection = bmain->collections.first; collection != NULL;
+       collection = collection->id.next) {
+    BLI_freelistN(&collection->parents);
+
+    collection->id.tag |= LIB_TAG_DOIT;
+  }
+
+  /* Scene's master collections will be 'root' parent of most of our collections, so start with
+   * them. */
+  for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
+    collection_parents_rebuild_recursive(scene->master_collection);
+  }
+
+  /* We may have parent chains outside of scene's master_collection context? At least, readfile's
+   * lib_link_collection_data() seems to assume that, so do the same here. */
+  for (Collection *collection = bmain->collections.first; collection != NULL;
+       collection = collection->id.next) {
+    if (collection->id.tag & LIB_TAG_DOIT) {
+      /* Note: we do not have easy access to 'which collections is root' info in that case, which
+       * means test for cycles in collection relationships may fail here. I don't think that is an
+       * issue in practice here, but worth keeping in mind... */
+      collection_parents_rebuild_recursive(collection);
+    }
+  }
+}
+
 /********************** Collection index *********************/
 
 static Collection *collection_from_index_recursive(Collection *collection,
index 5edba27..de6e5a8 100644 (file)
@@ -6228,20 +6228,11 @@ static void lib_link_collection_data(FileData *fd, Library *lib, Collection *col
     }
   }
 
-  for (CollectionChild *child = collection->children.first, *child_next = NULL; child;
-       child = child_next) {
-    child_next = child->next;
+  for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
     child->collection = newlibadr_us(fd, lib, child->collection);
-
-    if (child->collection == NULL || BKE_collection_find_cycle(collection, child->collection)) {
-      BLI_freelinkN(&collection->children, child);
-    }
-    else {
-      CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), "CollectionParent");
-      cparent->collection = collection;
-      BLI_addtail(&child->collection->parents, cparent);
-    }
   }
+
+  BKE_collection_parent_relations_rebuild(collection);
 }
 
 static void lib_link_collection(FileData *fd, Main *main)