[Bf-blender-cvs] [52f318b9142] master: Refactor duplicate code for collections.

Bastien Montagne noreply at git.blender.org
Sat Mar 2 22:10:17 CET 2019


Commit: 52f318b9142ba5600b56e4797f77ca04a87c4fab
Author: Bastien Montagne
Date:   Sat Mar 2 22:00:34 2019 +0100
Branches: master
https://developer.blender.org/rB52f318b9142ba5600b56e4797f77ca04a87c4fab

Refactor duplicate code for collections.

* Fix incorrect handling of children collections being linked more than
once in the hierarchy (previous code would make a new copy for each
link, instead of just re-linking the first copy for each extra link).

* Simplify some aspects of it (we do not need a GHash for new objects,
we can use ID->newid pointer instead, and some iterations can be done
directly on existing linked lists of old collection, instead of making
temp local copies of them).

* Move all copy logic into a single private recursive function (it was a
bit odd/disturbing to see calling function being indirectly called again
by the recursive helper one - not wrong, but that kind of code path can
quickly become problematic in recursive patterns).

* Added some comments about expected behavior of
`BKE_collection_duplicate()` depending on its booleans options.

===================================================================

M	source/blender/blenkernel/intern/collection.c

===================================================================

diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c
index ce7a5b141a4..ceac0215a5f 100644
--- a/source/blender/blenkernel/intern/collection.c
+++ b/source/blender/blenkernel/intern/collection.c
@@ -209,68 +209,97 @@ void BKE_collection_copy_data(
 	}
 }
 
-static void collection_duplicate_recursive(Main *bmain, GHash *visited, Collection *collection, const int dupflag)
+static Collection *collection_duplicate_recursive(
+        Main *bmain, Collection *parent, Collection *collection_old, const bool do_hierarchy, const bool do_deep_copy)
 {
-	const bool is_first_run = (visited == NULL);
-	if (is_first_run) {
-		visited = BLI_ghash_ptr_new(__func__);
-		BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
+	Collection *collection_new;
+	bool do_full_process = false;
+	const int object_dupflag = (do_deep_copy) ? U.dupflag : 0;
+
+	if (!do_hierarchy || collection_old->id.newid == NULL) {
+		BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new);
+		id_us_min(&collection_new->id);  /* Copying add one user by default, need to get rid of that one. */
+
+		if (do_hierarchy) {
+			ID_NEW_SET(collection_old, collection_new);
+		}
+		do_full_process = true;
+	}
+	else {
+		collection_new = (Collection *)collection_old->id.newid;
 	}
 
-	if (collection->id.tag & LIB_TAG_DOIT) {
-		return;
+	/* Optionally add to parent (we always want to do that, even if collection_old had already been duplicated). */
+	if (parent != NULL) {
+		if (collection_child_add(parent, collection_new, 0, true)) {
+			/* Put collection right after existing one. */
+			CollectionChild *child = collection_find_child(parent, collection_old);
+			CollectionChild *child_new = collection_find_child(parent, collection_new);
+
+			if (child && child_new) {
+				BLI_remlink(&parent->children, child_new);
+				BLI_insertlinkafter(&parent->children, child, child_new);
+			}
+		}
+	}
+
+	/* If we are not doing any kind of deep-copy, we can return immediately.
+	 * False do_full_process means collection_old had already been duplicated, no need to redo some deep-copy on it. */
+	if (!do_hierarchy || !do_full_process) {
+		return collection_new;
 	}
-	collection->id.tag |= LIB_TAG_DOIT;
 
-	ListBase collection_object_list = {NULL, NULL};
-	BLI_duplicatelist(&collection_object_list, &collection->gobject);
-	for (CollectionObject *cob = collection_object_list.first; cob; cob = cob->next) {
+	/* We can loop on collection_old's objects, that list is currently identical the collection_new' objects,
+	 * and won't be changed here. */
+	for (CollectionObject *cob = collection_old->gobject.first; cob; cob = cob->next) {
 		Object *ob_old = cob->ob;
-		Object *ob_new = NULL;
-		void **ob_key_p, **ob_value_p;
+		Object *ob_new = (Object *)ob_old->id.newid;
 
-		if (!BLI_ghash_ensure_p_ex(visited, ob_old, &ob_key_p, &ob_value_p)) {
-			ob_new = BKE_object_duplicate(bmain, ob_old, dupflag);
-			*ob_key_p = ob_old;
-			*ob_value_p = ob_new;
-		}
-		else {
-			ob_new = *ob_value_p;
+		if (ob_new == NULL) {
+			ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag);
+			ID_NEW_SET(ob_old, ob_new);
 		}
 
-		collection_object_add(bmain, collection, ob_new, 0, true);
-		collection_object_remove(bmain, collection, ob_old, false);
+		collection_object_add(bmain, collection_new, ob_new, 0, true);
+		collection_object_remove(bmain, collection_new, ob_old, false);
 	}
-	BLI_freelistN(&collection_object_list);
 
-	ListBase collection_child_list = {NULL, NULL};
-	BLI_duplicatelist(&collection_child_list, &collection->children);
-	for (CollectionChild *child = collection_child_list.first; child; child = child->next) {
+	/* We can loop on collection_old's children, that list is currently identical the collection_new' children,
+	 * and won't be changed here. */
+	for (CollectionChild *child = collection_old->children.first; child; child = child->next) {
 		Collection *child_collection_old = child->collection;
-		Collection *child_collection_new = BKE_collection_copy(bmain, collection, child_collection_old);
 
-		collection_duplicate_recursive(bmain, visited, child_collection_new, dupflag);
-		collection_child_remove(collection, child_collection_old);
+		collection_duplicate_recursive(bmain, collection_new, child_collection_old, do_hierarchy, do_deep_copy);
+		collection_child_remove(collection_new, child_collection_old);
 	}
-	BLI_freelistN(&collection_child_list);
 
-	if (is_first_run) {
-		BLI_ghash_free(visited, NULL, NULL);
-	}
+	return collection_new;
 }
 
 /**
- * Makes a shallow copy of a Collection
+ * Makes a standard (aka shallow) ID copy of a Collection.
  *
  * Add a new collection in the same level as the old one, link any nested collections
- * and finally link the objects to the new collection (as oppose to copy them).
+ * and finally link the objects to the new collection (as opposed to copying them).
  */
 Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection)
 {
 	return BKE_collection_duplicate(bmain, parent, collection, false, false);
 }
 
-Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection *collection, const bool do_hierarchy, const bool do_deep_copy)
+/**
+ * Make either a shallow copy, or deeper duplicate of given collection.
+ *
+ * If \a do_hierarchy and \a do_deep_copy are false, this is a regular (shallow) ID copy.
+ *
+ * \warning If any 'deep copy' behavior is enabled, this functions will clear all \a bmain id.idnew pointers.
+ *
+ * \param do_hierarchy If true, it will recursively make shallow copies of children collections and objects.
+ * \param do_deep_copy If true, it will also make deep duplicates of objects, using behavior defined in user settings
+ *                     (U.dupflag). This one does nothing if \a do_hierarchy is not set.
+ */
+Collection *BKE_collection_duplicate(
+        Main *bmain, Collection *parent, Collection *collection, const bool do_hierarchy, const bool do_deep_copy)
 {
 	/* It's not allowed to copy the master collection. */
 	if (collection->flag & COLLECTION_IS_MASTER) {
@@ -278,26 +307,16 @@ Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection
 		return NULL;
 	}
 
-	Collection *collection_new;
-	BKE_id_copy(bmain, &collection->id, (ID **)&collection_new);
-	id_us_min(&collection_new->id);  /* Copying add one user by default, need to get rid of that one. */
-
-	/* Optionally add to parent. */
-	if (parent) {
-		if (collection_child_add(parent, collection_new, 0, true)) {
-			/* Put collection right after existing one. */
-			CollectionChild *child = collection_find_child(parent, collection);
-			CollectionChild *child_new = collection_find_child(parent, collection_new);
-
-			if (child && child_new) {
-				BLI_remlink(&parent->children, child_new);
-				BLI_insertlinkafter(&parent->children, child, child_new);
-			}
-		}
+	if (do_hierarchy) {
+		BKE_main_id_clear_newpoins(bmain);
 	}
 
+	Collection *collection_new = collection_duplicate_recursive(
+	                                 bmain, parent, collection, do_hierarchy, do_deep_copy);
+
 	if (do_hierarchy) {
-		collection_duplicate_recursive(bmain, NULL, collection_new, (do_deep_copy) ? U.dupflag : 0);
+		/* Cleanup. */
+		BKE_main_id_clear_newpoins(bmain);
 	}
 
 	BKE_main_collection_sync(bmain);



More information about the Bf-blender-cvs mailing list