LibOverride: Refactor 'make override' 3DView operator.
authorBastien Montagne <bastien@blender.org>
Tue, 30 Jun 2020 10:04:44 +0000 (12:04 +0200)
committerBastien Montagne <bastien@blender.org>
Tue, 30 Jun 2020 10:19:11 +0000 (12:19 +0200)
This one now uses a generic 'dependency detection' process to decide
which IDs should be overridden.

This will e.g. allow to override mesh and shapekeys when those have some
values controlled by drivers using an armature bone...

source/blender/editors/object/object_relations.c

index 7ec236de6f0913464104fca981471b9a458c07b8..509b70f849e4ce1055b0fcae3406d5aabc87e894 100644 (file)
@@ -33,6 +33,7 @@
 #include "DNA_collection_types.h"
 #include "DNA_constraint_types.h"
 #include "DNA_gpencil_types.h"
+#include "DNA_key_types.h"
 #include "DNA_lattice_types.h"
 #include "DNA_light_types.h"
 #include "DNA_material_types.h"
@@ -68,6 +69,7 @@
 #include "BKE_gpencil.h"
 #include "BKE_hair.h"
 #include "BKE_idprop.h"
+#include "BKE_idtype.h"
 #include "BKE_lattice.h"
 #include "BKE_layer.h"
 #include "BKE_lib_id.h"
@@ -2249,47 +2251,66 @@ void OBJECT_OT_make_local(wmOperatorType *ot)
 /** \name Make Library Override Operator
  * \{ */
 
-static void make_override_library_tag_object(Object *obact, Object *ob)
+static bool make_override_hierarchy_recursive_tag(Main *bmain, ID *id)
 {
-  if (ob == obact) {
-    return;
+  MainIDRelationsEntry *entry = BLI_ghash_lookup(bmain->relations->id_user_to_used, id);
+
+  /* This way we won't process again that ID should we encounter it again through another
+   * relationship hierarchy.
+   * Note that this does not free any memory from relations, so we can still use the entries.
+   */
+  BKE_main_relations_ID_remove(bmain, id);
+
+  for (; entry != NULL; entry = entry->next) {
+    /* We only consider IDs from the same library. */
+    if (entry->id_pointer != NULL && (*entry->id_pointer)->lib == id->lib) {
+      if (make_override_hierarchy_recursive_tag(bmain, *entry->id_pointer)) {
+        id->tag |= LIB_TAG_DOIT;
+      }
+    }
   }
 
-  if (!ID_IS_LINKED(ob)) {
-    return;
+  return (id->tag & LIB_TAG_DOIT) != 0;
+}
+
+static int make_override_tag_ids_cb(LibraryIDLinkCallbackData *cb_data)
+{
+  if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) {
+    return IDWALK_RET_STOP_RECURSION;
   }
 
-  /* Note: all this is very case-by-case bad handling, ultimately we'll want a real full
-   * 'automatic', generic handling of all this,
-   * will probably require adding some override-aware stuff to library_query code... */
+  ID *id_root = cb_data->user_data;
+  Library *library_root = id_root->lib;
+  ID *id = *cb_data->id_pointer;
+  ID *id_owner = cb_data->id_owner;
 
-  if (obact->type == OB_ARMATURE && ob->modifiers.first != NULL) {
-    for (ModifierData *md = ob->modifiers.first; md != NULL; md = md->next) {
-      if (md->type == eModifierType_Armature) {
-        ArmatureModifierData *amd = (ArmatureModifierData *)md;
-        if (amd->object == obact) {
-          ob->id.tag |= LIB_TAG_DOIT;
-          break;
-        }
-      }
-    }
+  BLI_assert(id_owner == cb_data->id_self);
+
+  if (ELEM(id, NULL, id_owner)) {
+    return IDWALK_RET_NOP;
   }
-  else if (ob->parent == obact) {
-    ob->id.tag |= LIB_TAG_DOIT;
+
+  BLI_assert(id->lib != NULL);
+  BLI_assert(id_owner->lib == library_root);
+
+  if (id->tag & LIB_TAG_DOIT) {
+    /* Already processed, but maybe not with the same chain of dependency, so we need to check that
+     * one nonetheless. */
+    return IDWALK_RET_STOP_RECURSION;
   }
 
-  if (ob->id.tag & LIB_TAG_DOIT) {
-    printf("Indirectly overriding %s for %s\n", ob->id.name, obact->id.name);
+  if (id->lib != library_root) {
+    /* We do not override data-blocks from other libraries, nor do we process them. */
+    return IDWALK_RET_STOP_RECURSION;
   }
-}
 
-static void make_override_library_tag_collections(Collection *collection)
-{
-  collection->id.tag |= LIB_TAG_DOIT;
-  for (CollectionChild *coll_child = collection->children.first; coll_child != NULL;
-       coll_child = coll_child->next) {
-    make_override_library_tag_collections(coll_child->collection);
+  /* We tag all collections and objects for override. And we also tag all other data-blocks which
+   * would user one of those. */
+  if (ELEM(GS(id->name), ID_OB, ID_GR)) {
+    id->tag |= LIB_TAG_DOIT;
   }
+
+  return IDWALK_RET_NOP;
 }
 
 /* Set the object to override. */
@@ -2338,6 +2359,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
 {
   Main *bmain = CTX_data_main(C);
   Object *obact = CTX_data_active_object(C);
+  ID *id_root = NULL;
 
   bool success = false;
 
@@ -2351,111 +2373,156 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
       return OPERATOR_CANCELLED;
     }
 
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+    id_root = &obact->instance_collection->id;
+  }
+  else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
+    BKE_reportf(op->reports,
+                RPT_ERROR_INVALID_INPUT,
+                "Active object '%s' is not overridable",
+                obact->id.name + 2);
+    return OPERATOR_CANCELLED;
+  }
+  /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
+  else {
+    id_root = &obact->id;
+  }
 
-    Object *obcollection = obact;
-    Collection *collection = obcollection->instance_collection;
+  BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
 
-    const ListBase dup_collection_objects = BKE_collection_object_cache_get(collection);
-    Base *base = BLI_findlink(&dup_collection_objects, RNA_enum_get(op->ptr, "object"));
-    obact = base->object;
+  /* Tag all collections and objects, as well as other IDs using them. */
+  id_root->tag |= LIB_TAG_DOIT;
 
-    /* First, we make a library override of the linked collection itself, and all its children. */
-    make_override_library_tag_collections(collection);
+  BKE_main_relations_create(bmain, 0);
 
-    /* Then, we make library override of the whole set of objects in the Collection. */
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
-      ob->id.tag |= LIB_TAG_DOIT;
-    }
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
-
-    /* Then, we remove (untag) bone shape objects, you shall never want to override those
-     * (hopefully)... */
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
-      if (ob->type == OB_ARMATURE && ob->pose != NULL) {
-        for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
-          if (pchan->custom != NULL) {
-            pchan->custom->id.tag &= ~LIB_TAG_DOIT;
-          }
+  BKE_library_foreach_ID_link(
+      bmain, id_root, make_override_tag_ids_cb, id_root, IDWALK_READONLY | IDWALK_RECURSE);
+
+  /* Then, we remove (untag) bone shape objects, you shall never want to override those
+   * (hopefully)... */
+  LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+    if (ob->type == OB_ARMATURE && ob->pose != NULL && (ob->id.tag & LIB_TAG_DOIT)) {
+      for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan != NULL; pchan = pchan->next) {
+        if (pchan->custom != NULL) {
+          pchan->custom->id.tag &= ~LIB_TAG_DOIT;
         }
       }
     }
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+  }
 
-    success = BKE_lib_override_library_create_from_tag(bmain);
+  /* The we tag all intermediary data-blocks in-between to overridden ones (e.g. if a shapekey has
+   * a driver using an armature object's bone, we need to override the shapekey/obdata, the objects
+   * using them, etc.) */
+  make_override_hierarchy_recursive_tag(bmain, id_root);
+
+  BKE_main_relations_free(bmain);
+
+  ID *id;
+  FOREACH_MAIN_ID_BEGIN (bmain, id) {
+    if (id->tag & LIB_TAG_DOIT && id->lib != NULL) {
+      printf("ID %s tagged for override\n", id->name);
+    }
+  }
+  FOREACH_MAIN_ID_END;
 
-    /* Instantiate our newly overridden objects in scene, if not yet done. */
+  success = BKE_lib_override_library_create_from_tag(bmain);
+
+  if (success) {
     Scene *scene = CTX_data_scene(C);
     ViewLayer *view_layer = CTX_data_view_layer(C);
-    Collection *new_collection = (Collection *)collection->id.newid;
 
-    BKE_collection_add_from_object(bmain, scene, obcollection, new_collection);
+    BKE_main_collection_sync(bmain);
+
+    switch (GS(id_root->name)) {
+      case ID_GR: {
+        Collection *collection_new = ((Collection *)id_root->newid);
+        BKE_collection_add_from_object(bmain, scene, obact, collection_new);
+
+        FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
+          if (ob_new != NULL && ob_new->id.override_library != NULL) {
+            Base *base;
+            if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+              BKE_collection_object_add_from(bmain, scene, obact, ob_new);
+              base = BKE_view_layer_base_find(view_layer, ob_new);
+              DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+            }
 
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (new_collection, new_ob) {
-      if (new_ob != NULL && new_ob->id.override_library != NULL) {
-        if ((base = BKE_view_layer_base_find(view_layer, new_ob)) == NULL) {
-          BKE_collection_object_add_from(bmain, scene, obcollection, new_ob);
-          base = BKE_view_layer_base_find(view_layer, new_ob);
-          DEG_id_tag_update_ex(bmain, &new_ob->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
+            if (ob_new == (Object *)obact->id.newid) {
+              /* TODO: is setting active needed? */
+              BKE_view_layer_base_select_and_set_active(view_layer, base);
+            }
+          }
         }
+        FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
+        break;
+      }
+      case ID_OB: {
+        BKE_collection_object_add_from(bmain, scene, obact, ((Object *)id_root->newid));
+        break;
+      }
+      default:
+        BLI_assert(0);
+    }
 
-        if (new_ob == (Object *)obact->id.newid) {
-          /* TODO: is setting active needed? */
-          BKE_view_layer_base_select_and_set_active(view_layer, base);
+    /* We need to ensure all new overrides of objects are properly instantiated. */
+    LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
+      Object *ob_new = (Object *)ob->id.newid;
+      if (ob_new != NULL) {
+        BLI_assert(ob_new->id.override_library != NULL &&
+                   ob_new->id.override_library->reference == &ob->id);
+
+        Collection *default_instantiating_collection = NULL;
+        Base *base;
+        if ((base = BKE_view_layer_base_find(view_layer, ob_new)) == NULL) {
+          if (default_instantiating_collection == NULL) {
+            switch (GS(id_root->name)) {
+              case ID_GR: {
+                default_instantiating_collection = BKE_collection_add(
+                    bmain, (Collection *)id_root, "OVERRIDE_HIDDEN");
+                break;
+              }
+              case ID_OB: {
+                /* Add the new container collection to one of the collections instantiating the
+                 * root object, or scene's master collection if none found. */
+                Object *ob_root = (Object *)id_root;
+                LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+                  if (BKE_collection_has_object(collection, ob_root) &&
+                      BKE_view_layer_has_collection(view_layer, collection)) {
+                    default_instantiating_collection = BKE_collection_add(
+                        bmain, collection, "OVERRIDE_HIDDEN");
+                  }
+                }
+                if (default_instantiating_collection == NULL) {
+                  default_instantiating_collection = BKE_collection_add(
+                      bmain, scene->master_collection, "OVERRIDE_HIDDEN");
+                }
+                break;
+              }
+              default:
+                BLI_assert(0);
+            }
+            /* Hide the collection from viewport and render. */
+            default_instantiating_collection->flag |= COLLECTION_RESTRICT_VIEWPORT |
+                                                      COLLECTION_RESTRICT_RENDER;
+          }
+
+          BKE_collection_object_add(bmain, default_instantiating_collection, ob_new);
+          DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
         }
-        /* We still want to store all objects' current override status (i.e. change of parent). */
-        BKE_lib_override_library_operations_create(bmain, &new_ob->id);
       }
     }
-    FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
 
     /* Remove the instance empty from this scene, the items now have an overridden collection
      * instead. */
-    ED_object_base_free_and_unlink(bmain, scene, obcollection);
-
-    /* Also, we'd likely want to lock by default things like
-     * transformations of implicitly overridden objects? */
-
-    DEG_id_tag_update(&scene->id, 0);
-
-    /* Cleanup. */
-    BKE_main_id_clear_newpoins(bmain);
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-  }
-  else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
-    BKE_reportf(op->reports,
-                RPT_ERROR_INVALID_INPUT,
-                "Active object '%s' is not overridable",
-                obact->id.name + 2);
-    return OPERATOR_CANCELLED;
-  }
-  /* Else, poll func ensures us that ID_IS_LINKED(obact) is true. */
-  else if (obact->type == OB_ARMATURE) {
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-
-    obact->id.tag |= LIB_TAG_DOIT;
-
-    for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
-      make_override_library_tag_object(obact, ob);
+    if (id_root != &obact->id) {
+      ED_object_base_free_and_unlink(bmain, scene, obact);
     }
-
-    success = BKE_lib_override_library_create_from_tag(bmain);
-
-    /* Also, we'd likely want to lock by default things like
-     * transformations of implicitly overridden objects? */
-
-    /* Cleanup. */
-    BKE_main_id_clear_newpoins(bmain);
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
-  }
-  /* TODO: probably more cases where we want to do automated smart things in the future! */
-  else {
-    /* For now, remapp all local usages of linked ID to local override one here. */
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, true);
-    success = (BKE_lib_override_library_create_from_id(bmain, &obact->id, true) != NULL);
-    BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
   }
 
+  /* Cleanup. */
+  BKE_main_id_clear_newpoins(bmain);
+  BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
+
+  DEG_id_tag_update(&CTX_data_scene(C)->id, ID_RECALC_BASE_FLAGS | ID_RECALC_COPY_ON_WRITE);
   WM_event_add_notifier(C, NC_WINDOW, NULL);
 
   return success ? OPERATOR_FINISHED : OPERATOR_CANCELLED;