LibOverride: Modify automatic handling of which IDs to override in a hierarchy. #115853

Merged
Bastien Montagne merged 1 commits from mont29/blender:tmp-liboverride-default-hierarchy-handling into main 2023-12-13 11:59:05 +01:00
4 changed files with 138 additions and 56 deletions

View File

@ -70,12 +70,6 @@ enum {
* How to handle that kind of cases totally depends on what caller code is doing... */
IDWALK_CB_LOOPBACK = (1 << 6),
/** That ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 7),
/** That ID pointer is not overridable. */
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 8),
/**
* Indicates that this is an internal runtime ID pointer, like e.g. `ID.newid` or `ID.original`.
* \note Those should be ignored in most cases, and won't be processed/generated anyway unless
@ -105,6 +99,16 @@ enum {
* Callback is responsible to deal accordingly with #ID.us if needed.
*/
IDWALK_CB_USER_ONE = (1 << 12),
/** This ID is used as library override's reference by its owner. */
IDWALK_CB_OVERRIDE_LIBRARY_REFERENCE = (1 << 16),
/** This ID pointer is not overridable. */
IDWALK_CB_OVERRIDE_LIBRARY_NOT_OVERRIDABLE = (1 << 17),
/** This ID pointer is expected to be overridden by default, in liboverride hierarchy context. */
IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT = (1 << 18),
};
enum {

View File

@ -185,7 +185,8 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
Object *cob_ob_old = cob->ob;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, cob->ob, IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, cob->ob, IDWALK_CB_USER | IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT);
if (collection->runtime.gobject_hash) {
/* If the remapping does not create inconsistent data (nullptr object pointer or duplicate
@ -200,8 +201,10 @@ static void collection_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
LISTBASE_FOREACH (CollectionChild *, child, &collection->children) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, child->collection, IDWALK_CB_NEVER_SELF | IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data,
child->collection,
IDWALK_CB_NEVER_SELF | IDWALK_CB_USER |
IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT);
}
LISTBASE_FOREACH (CollectionParent *, parent, &collection->runtime.parents) {
/* XXX This is very weak. The whole idea of keeping pointers to private IDs is very bad

View File

@ -790,6 +790,46 @@ struct LibOverrideGroupTagData {
uint tag;
uint missing_tag;
/**
* A set of all IDs belonging to the reference linked hierarchy that is being overridden.
*
* NOTE: This is needed only for partial resync, when only part of the liboverridden hierarchy is
* re-generated, since some IDs in that sub-hierarchy may not be detected as needing to be
* overridden, while they would when comsidering the whole hierarchy. */
blender::Set<ID *> linked_ids_hierarchy_default_override;
bool do_create_linked_overrides_set;
/** Helpers to mark or unmark an ID as part of the processed (reference of) liboverride
* hierarchy.
*
* \return `true` if the given ID is tagged as missing linked data, `false` otherwise. */
bool id_tag_set(ID *id, const bool is_missing)
{
if (do_create_linked_overrides_set) {
linked_ids_hierarchy_default_override.add(id);
}
else if (is_missing) {
id->tag |= missing_tag;
}
else {
id->tag |= tag;
}
return is_missing;
}
bool id_tag_clear(ID *id, const bool is_missing)
{
if (do_create_linked_overrides_set) {
linked_ids_hierarchy_default_override.remove(id);
}
else if (is_missing) {
id->tag &= ~missing_tag;
}
else {
id->tag &= ~tag;
}
return is_missing;
}
/* Mapping linked objects to all their instantiating collections (as a linked list).
* Avoids calling #BKE_collection_object_find over and over, this function is very expansive. */
GHash *linked_object_to_instantiating_collections;
@ -797,6 +837,7 @@ struct LibOverrideGroupTagData {
void clear(void)
{
linked_ids_hierarchy_default_override.clear();
BLI_ghash_free(linked_object_to_instantiating_collections, nullptr, nullptr);
BLI_memarena_free(mem_arena);
@ -967,8 +1008,6 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
ID *id_owner = data->root_get();
BLI_assert(ID_IS_LINKED(id_owner));
BLI_assert(!data->is_override);
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner));
@ -1000,21 +1039,21 @@ static void lib_override_linked_group_tag_recursive(LibOverrideGroupTagData *dat
}
BLI_assert(ID_IS_LINKED(to_id));
/* We tag all collections and objects for override. And we also tag all other data-blocks which
* would use one of those.
/* Only tag ID if their usages is tagged as requiring liboverride by default, and the owner is
* already tagged for liboverride.
*
* NOTE: 'in-between' IDs are handled as a separate step, typically by calling
* #lib_override_hierarchy_dependencies_recursive_tag.
* NOTE: missing IDs (aka placeholders) are never overridden. */
if (ELEM(GS(to_id->name), ID_OB, ID_GR)) {
if (to_id->tag & LIB_TAG_MISSING) {
to_id->tag |= missing_tag;
}
else {
to_id->tag |= tag;
if ((to_id_entry->usage_flag & IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT) != 0 ||
data->linked_ids_hierarchy_default_override.contains(to_id))
{
if (!data->id_tag_set(to_id, bool(to_id->tag & LIB_TAG_MISSING))) {
/* Only recursively process the dependencies if the owner is tagged for liboverride. */
data->root_set(to_id);
lib_override_linked_group_tag_recursive(data);
}
}
/* Recursively process the dependencies. */
data->root_set(to_id);
lib_override_linked_group_tag_recursive(data);
}
data->root_set(id_owner);
}
@ -1035,7 +1074,9 @@ static bool lib_override_linked_group_tag_collections_keep_tagged_check_recursiv
if (object == nullptr) {
continue;
}
if ((object->id.tag & data->tag) != 0) {
if ((object->id.tag & data->tag) != 0 ||
data->linked_ids_hierarchy_default_override.contains(&object->id))
{
return true;
}
}
@ -1066,13 +1107,16 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr
if (ob->id.lib != id_root->lib) {
continue;
}
if (ob->type == OB_ARMATURE && ob->pose != nullptr && (ob->id.tag & data->tag)) {
if (ob->type == OB_ARMATURE && ob->pose != nullptr &&
((ob->id.tag & data->tag) ||
data->linked_ids_hierarchy_default_override.contains(&ob->id)))
{
for (bPoseChannel *pchan = static_cast<bPoseChannel *>(ob->pose->chanbase.first);
pchan != nullptr;
pchan = pchan->next)
{
if (pchan->custom != nullptr && &pchan->custom->id != id_root) {
pchan->custom->id.tag &= ~data->tag;
data->id_tag_clear(&pchan->custom->id, bool(pchan->custom->id.tag & LIB_TAG_MISSING));
}
}
}
@ -1081,14 +1125,15 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr
/* Remove (untag) collections if they do not own any tagged object (either themselves, or in
* their children collections). */
LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
if ((collection->id.tag & data->tag) == 0 || &collection->id == id_root ||
collection->id.lib != id_root->lib)
if (!((collection->id.tag & data->tag) != 0 ||
data->linked_ids_hierarchy_default_override.contains(&collection->id)) ||
&collection->id == id_root || collection->id.lib != id_root->lib)
{
continue;
}
if (!lib_override_linked_group_tag_collections_keep_tagged_check_recursive(data, collection)) {
collection->id.tag &= ~data->tag;
data->id_tag_clear(&collection->id, bool(collection->id.tag & LIB_TAG_MISSING));
}
}
}
@ -1100,13 +1145,15 @@ static void lib_override_linked_group_tag_clear_boneshapes_objects(LibOverrideGr
* Note that you will then need to call #lib_override_hierarchy_dependencies_recursive_tag to
* complete tagging of all dependencies within the override group.
*
* We currently only consider Collections and Objects (that are not used as bone shapes) as valid
* boundary IDs to define an override group.
* We currently only consider IDs which usages are marked as to be overridden by default (i.e.
* tagged with #IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT) as valid boundary IDs to define an
* override group.
*/
static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
{
Main *bmain = data->bmain;
ID *id_root = data->root_get();
ID *hierarchy_root_id = data->hierarchy_root_get();
const bool is_resync = data->is_resync;
BLI_assert(!data->is_override);
@ -1115,13 +1162,49 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
return;
}
/* Tag all collections and objects recursively. */
/* In case this code only process part of the whole hierarchy, it first needs to process the
* whole linked hierarchy to know which IDs should be overridden anyway, even though in the more
* limited sub-hierarchy scope they would not be. This is critical for partial resync to work
* propoerly.
*
* NOTE: Regenerating that Set for every processed sub-hierarchy is not optimal. This is done
* that way for now to limit the scope of these changes. Better handling is considered a TODO for
* later (as part of a general refactoring/modernization of this whole code area). */
const bool use_linked_overrides_set = hierarchy_root_id &&
hierarchy_root_id->lib == id_root->lib &&
hierarchy_root_id != id_root;
BLI_assert(data->do_create_linked_overrides_set == false);
if (use_linked_overrides_set) {
BLI_assert(data->linked_ids_hierarchy_default_override.is_empty());
data->linked_ids_hierarchy_default_override.add(id_root);
data->linked_ids_hierarchy_default_override.add(hierarchy_root_id);
data->do_create_linked_overrides_set = true;
/* Store recursively all IDs in the hierarchy which should be liboverridden by default. */
data->root_set(hierarchy_root_id);
lib_override_linked_group_tag_recursive(data);
/* Do not override objects used as bone shapes, nor their collections if possible. */
lib_override_linked_group_tag_clear_boneshapes_objects(data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data->root_set(id_root);
data->do_create_linked_overrides_set = false;
}
/* Tag recursively all IDs in the hierarchy which should be liboverridden by default. */
id_root->tag |= data->tag;
lib_override_linked_group_tag_recursive(data);
/* Do not override objects used as bone shapes, nor their collections if possible. */
lib_override_linked_group_tag_clear_boneshapes_objects(data);
if (use_linked_overrides_set) {
data->linked_ids_hierarchy_default_override.clear();
}
/* For each object tagged for override, ensure we get at least one local or liboverride
* collection to host it. Avoids getting a bunch of random object in the scene's master
* collection when all objects' dependencies are not properly 'packed' into a single root
@ -1162,7 +1245,9 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
* else to be done here. */
break;
}
if (instantiating_collection->id.tag & data->tag) {
if (instantiating_collection->id.tag & data->tag ||
data->linked_ids_hierarchy_default_override.contains(&instantiating_collection->id))
{
/* There is a linked collection instantiating the linked object to override,
* already tagged to be overridden, nothing else to be done here. */
break;
@ -1174,12 +1259,9 @@ static void lib_override_linked_group_tag(LibOverrideGroupTagData *data)
if (instantiating_collection == nullptr &&
instantiating_collection_override_candidate != nullptr) {
if (instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING) {
instantiating_collection_override_candidate->id.tag |= data->missing_tag;
}
else {
instantiating_collection_override_candidate->id.tag |= data->tag;
}
data->id_tag_set(
&instantiating_collection_override_candidate->id,
bool(instantiating_collection_override_candidate->id.tag & LIB_TAG_MISSING));
}
}
}
@ -1191,6 +1273,7 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
ID *id_owner = data->root_get();
BLI_assert(ID_IS_OVERRIDE_LIBRARY(id_owner));
BLI_assert(data->is_override);
BLI_assert(data->do_create_linked_overrides_set == false);
ID *id_hierarchy_root = data->hierarchy_root_get();
@ -1200,9 +1283,6 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
return;
}
const uint tag = data->tag;
const uint missing_tag = data->missing_tag;
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_owner));
BLI_assert(entry != nullptr);
@ -1247,12 +1327,7 @@ static void lib_override_overrides_group_tag_recursive(LibOverrideGroupTagData *
continue;
}
if (to_id_reference->tag & LIB_TAG_MISSING) {
to_id->tag |= missing_tag;
}
else {
to_id->tag |= tag;
}
data->id_tag_set(to_id, bool(to_id_reference->tag & LIB_TAG_MISSING));
/* Recursively process the dependencies. */
data->root_set(to_id);
@ -1267,18 +1342,14 @@ static void lib_override_overrides_group_tag(LibOverrideGroupTagData *data)
ID *id_root = data->root_get();
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(data->is_override);
BLI_assert(data->do_create_linked_overrides_set == false);
ID *id_hierarchy_root = data->hierarchy_root_get();
BLI_assert(id_hierarchy_root != nullptr);
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_hierarchy_root));
UNUSED_VARS_NDEBUG(id_hierarchy_root);
if (id_root->override_library->reference->tag & LIB_TAG_MISSING) {
id_root->tag |= data->missing_tag;
}
else {
id_root->tag |= data->tag;
}
data->id_tag_set(id_root, bool(id_root->override_library->reference->tag & LIB_TAG_MISSING));
/* Tag all local overrides in id_root's group. */
lib_override_overrides_group_tag_recursive(data);
@ -1301,6 +1372,7 @@ static bool lib_override_library_create_do(Main *bmain,
data.is_resync = false;
data.root_set(id_root_reference);
data.hierarchy_root_set(id_hierarchy_root_reference);
lib_override_group_tag_data_object_to_collection_init(&data);
lib_override_linked_group_tag(&data);
@ -1908,7 +1980,6 @@ static void lib_override_library_remap(Main *bmain,
if (id_override_old == nullptr) {
continue;
}
BKE_id_remapper_add(remapper, id_override_old, id_override_new);
}
}
@ -2115,6 +2186,8 @@ static bool lib_override_library_resync(Main *bmain,
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data.is_override = false;
data.root_set(id_resync_root->override_library->reference);
data.hierarchy_root_set(
id_resync_root->override_library->hierarchy_root->override_library->reference);
lib_override_linked_group_tag(&data);
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
@ -2215,6 +2288,7 @@ static bool lib_override_library_resync(Main *bmain,
BKE_main_relations_tag_set(bmain, MAINIDRELATIONS_ENTRY_TAGS_PROCESSED, false);
data.is_override = true;
data.root_set(id_root);
data.hierarchy_root_set(id_root->override_library->hierarchy_root);
lib_override_overrides_group_tag(&data);
BKE_main_relations_free(bmain);

View File

@ -411,7 +411,8 @@ static void object_foreach_id(ID *id, LibraryForeachIDData *data)
}
}
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->parent, IDWALK_CB_NEVER_SELF);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(
data, object->parent, IDWALK_CB_NEVER_SELF | IDWALK_CB_OVERRIDE_LIBRARY_HIERARCHY_DEFAULT);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, object->track, IDWALK_CB_NEVER_SELF);
for (int i = 0; i < object->totcol; i++) {