LibOverride: Refactor 'make override' 3DView operator.

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...
This commit is contained in:
Bastien Montagne 2020-06-30 12:04:44 +02:00
parent f6394e66ed
commit 4c3d51326e
Notes: blender-bot 2023-02-14 08:35:51 +01:00
Referenced by issue #85981, Undo on linked rig with overrides loses custom shapes
1 changed files with 185 additions and 118 deletions

View 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);
if (!ID_IS_LINKED(ob)) {
return;
}
/* 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);
/* 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... */
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;
}
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;
}
}
}
else if (ob->parent == obact) {
ob->id.tag |= LIB_TAG_DOIT;
}
if (ob->id.tag & LIB_TAG_DOIT) {
printf("Indirectly overriding %s for %s\n", ob->id.name, obact->id.name);
}
return (id->tag & LIB_TAG_DOIT) != 0;
}
static void make_override_library_tag_collections(Collection *collection)
static int make_override_tag_ids_cb(LibraryIDLinkCallbackData *cb_data)
{
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);
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_LOOPBACK)) {
return IDWALK_RET_STOP_RECURSION;
}
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;
BLI_assert(id_owner == cb_data->id_self);
if (ELEM(id, NULL, id_owner)) {
return IDWALK_RET_NOP;
}
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 (id->lib != library_root) {
/* We do not override data-blocks from other libraries, nor do we process them. */
return IDWALK_RET_STOP_RECURSION;
}
/* 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,76 +2373,7 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
Object *obcollection = obact;
Collection *collection = obcollection->instance_collection;
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;
/* First, we make a library override of the linked collection itself, and all its children. */
make_override_library_tag_collections(collection);
/* 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;
}
}
}
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
success = BKE_lib_override_library_create_from_tag(bmain);
/* Instantiate our newly overridden objects in scene, if not yet done. */
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);
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 (new_ob == (Object *)obact->id.newid) {
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
}
/* 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);
id_root = &obact->instance_collection->id;
}
else if (!ID_IS_OVERRIDABLE_LIBRARY(obact)) {
BKE_reportf(op->reports,
@ -2430,32 +2383,146 @@ static int make_override_library_exec(bContext *C, wmOperator *op)
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);
else {
id_root = &obact->id;
}
obact->id.tag |= LIB_TAG_DOIT;
BKE_main_id_tag_all(bmain, LIB_TAG_DOIT, false);
for (Object *ob = bmain->objects.first; ob != NULL; ob = ob->id.next) {
make_override_library_tag_object(obact, ob);
/* Tag all collections and objects, as well as other IDs using them. */
id_root->tag |= LIB_TAG_DOIT;
BKE_main_relations_create(bmain, 0);
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;
}
}
}
}
/* 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;
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);
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);
}
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);
}
success = BKE_lib_override_library_create_from_tag(bmain);
/* 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);
/* Also, we'd likely want to lock by default things like
* transformations of implicitly overridden objects? */
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;
}
/* 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);
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);
}
}
}
/* Remove the instance empty from this scene, the items now have an overridden collection
* instead. */
if (id_root != &obact->id) {
ED_object_base_free_and_unlink(bmain, scene, obact);
}
}
/* 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;