LibOverride: Fix several issues with resync code.

This commit essentially touches to post-processing of collections and
objects after resync itself has been done, to ensure their proper
instantiation in the scene:

 - Remove a lot of the process in resync case (resynced data are assumed
   to be already instantiated in the scene, unlike override creation
   case).

 - For auto-resync, only do post-processing once after all overrides
   have been resynced (doing it after each individual resynced was
   causing a lot of instantiation glitches, with a lot of unwanted
   extra objects and collections being added to the master collection).

It also deals in a much more reliable way with detection of objects
missing from the scene, by using the new `BKE_scene_objects_as_gset`
utils.

As a bonus this makes auto-resync process slightly faster (only by a few
percents, but that's always good to get).
This commit is contained in:
Bastien Montagne 2021-04-08 11:27:05 +02:00
parent c1b1ed4d5b
commit e92a7800b5
3 changed files with 49 additions and 77 deletions

View File

@ -84,7 +84,8 @@ bool BKE_lib_override_library_resync(struct Main *bmain,
struct ViewLayer *view_layer,
struct ID *id_root,
struct Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce);
const bool do_hierarchy_enforce,
const bool do_post_process);
void BKE_lib_override_library_main_resync(struct Main *bmain,
struct Scene *scene,
struct ViewLayer *view_layer);

View File

@ -650,18 +650,6 @@ static bool lib_override_library_create_do(Main *bmain, ID *id_root)
return BKE_lib_override_library_create_from_tag(bmain);
}
BLI_INLINE bool lib_override_library_create_post_process_object_is_instantiated(
ViewLayer *view_layer, Object *object, const bool is_resync)
{
/* We cannot rely on check for object being actually instantiated in resync case, because often
* the overridden collection is 'excluded' from the current view-layer.
*
* Fallback to a basic user-count check then, this is weak (since it could lead to some object
* not being instantiated at all), but it should work fine in most common cases. */
return ((is_resync && ID_REAL_USERS(object) >= 1) ||
(!is_resync && BKE_view_layer_base_find(view_layer, object) != NULL));
}
static void lib_override_library_create_post_process(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@ -672,13 +660,23 @@ static void lib_override_library_create_post_process(Main *bmain,
{
BKE_main_collection_sync(bmain);
if (id_root->newid != NULL) {
/* We create a set of all objects referenced into the scene by its hierarchy of collections.
* NOTE: This is different that the list of bases, since objects in excluded collections etc.
* won't have a base, but are still considered as instanced from our point of view. */
GSet *all_objects_in_scene = BKE_scene_objects_as_gset(scene, NULL);
/* Instantiating the root collection or object should never be needed in resync case, since the
* old override would be remapped to the new one. */
if (!is_resync && id_root != NULL && id_root->newid != NULL) {
switch (GS(id_root->name)) {
case ID_GR: {
Object *ob_reference = id_reference != NULL && GS(id_reference->name) == ID_OB ?
(Object *)id_reference :
NULL;
Collection *collection_new = ((Collection *)id_root->newid);
if (is_resync && BKE_collection_is_in_scene(collection_new)) {
break;
}
if (ob_reference != NULL) {
BKE_collection_add_from_object(bmain, scene, ob_reference, collection_new);
}
@ -692,52 +690,16 @@ static void lib_override_library_create_post_process(Main *bmain,
bmain, scene, ((Collection *)id_root), collection_new);
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection_new, ob_new) {
if (ob_new != NULL && ob_new->id.override_library != NULL) {
if (ob_reference != NULL) {
Base *base = BKE_view_layer_base_find(view_layer, ob_new);
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
BKE_collection_object_add_from(bmain, scene, ob_reference, 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 (is_resync) {
ob_new->id.flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
}
}
BLI_assert(BKE_collection_is_in_scene(collection_new));
if (ob_new == (Object *)ob_reference->id.newid && base != NULL) {
/* TODO: is setting active needed? */
BKE_view_layer_base_select_and_set_active(view_layer, base);
}
}
else if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
BKE_collection_object_add(bmain, collection_new, ob_new);
DEG_id_tag_update_ex(bmain, &ob_new->id, ID_RECALC_TRANSFORM | ID_RECALC_BASE_FLAGS);
if (is_resync) {
ob_new->id.flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
}
}
}
}
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
break;
}
case ID_OB: {
Object *ob_new = (Object *)id_root->newid;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
if (is_resync && residual_storage != NULL) {
BKE_collection_object_add(bmain, residual_storage, ob_new);
}
else {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
}
if (is_resync) {
ob_new->id.flag |= LIB_LIB_OVERRIDE_RESYNC_LEFTOVER;
}
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
BKE_collection_object_add_from(bmain, scene, (Object *)id_root, ob_new);
all_objects_in_scene = BKE_scene_objects_as_gset(scene, all_objects_in_scene);
}
break;
}
@ -747,16 +709,15 @@ static void lib_override_library_create_post_process(Main *bmain,
}
/* We need to ensure all new overrides of objects are properly instantiated. */
Collection *default_instantiating_collection = residual_storage;
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 = residual_storage;
if (!lib_override_library_create_post_process_object_is_instantiated(
view_layer, ob_new, is_resync)) {
if (default_instantiating_collection == NULL) {
if (BLI_gset_lookup(all_objects_in_scene, ob_new) == NULL) {
if (id_root != NULL && default_instantiating_collection == NULL) {
switch (GS(id_root->name)) {
case ID_GR: {
default_instantiating_collection = BKE_collection_add(
@ -777,21 +738,23 @@ static void lib_override_library_create_post_process(Main *bmain,
default_instantiating_collection = collection;
}
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
break;
}
default:
BLI_assert(0);
}
}
if (default_instantiating_collection == NULL) {
default_instantiating_collection = scene->master_collection;
}
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);
}
}
}
BLI_gset_free(all_objects_in_scene, NULL);
}
/**
@ -902,7 +865,8 @@ bool BKE_lib_override_library_resync(Main *bmain,
ViewLayer *view_layer,
ID *id_root,
Collection *override_resync_residual_storage,
const bool do_hierarchy_enforce)
const bool do_hierarchy_enforce,
const bool do_post_process)
{
BLI_assert(ID_IS_OVERRIDE_LIBRARY_REAL(id_root));
BLI_assert(!ID_IS_LINKED(id_root));
@ -1109,18 +1073,20 @@ bool BKE_lib_override_library_resync(Main *bmain,
*/
id_root = id_root_reference->newid;
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
lib_override_library_create_post_process(bmain,
scene,
view_layer,
id_root_reference,
id_root,
override_resync_residual_storage,
true);
if (do_post_process) {
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
/* Note: Here 'reference' collection and 'newly added' collection are the same, which is fine
* since we already relinked old root override collection to new resync'ed one above. So this
* call is not expected to instantiate this new resync'ed collection anywhere, just to ensure
* that we do not have any stray objects. */
lib_override_library_create_post_process(bmain,
scene,
view_layer,
id_root_reference,
id_root,
override_resync_residual_storage,
true);
}
/* Cleanup. */
BLI_ghash_free(linkedref_to_old_override, NULL, NULL);
@ -1252,9 +1218,10 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
continue;
}
do_continue = true;
CLOG_INFO(&LOG, 2, "Resyncing %s...", id->name);
const bool success = BKE_lib_override_library_resync(
bmain, scene, view_layer, id, override_resync_residual_storage, false);
bmain, scene, view_layer, id, override_resync_residual_storage, false, false);
CLOG_INFO(&LOG, 2, "\tSuccess: %d", success);
break;
}
@ -1266,6 +1233,10 @@ void BKE_lib_override_library_main_resync(Main *bmain, Scene *scene, ViewLayer *
FOREACH_MAIN_LISTBASE_END;
}
/* Essentially ensures that potentially new overrides of new objects will be instantiated. */
lib_override_library_create_post_process(
bmain, scene, view_layer, NULL, NULL, override_resync_residual_storage, true);
if (BKE_collection_is_empty(override_resync_residual_storage)) {
BKE_collection_delete(bmain, override_resync_residual_storage, true);
}

View File

@ -925,7 +925,7 @@ static void id_override_library_resync_fn(bContext *C,
}
BKE_lib_override_library_resync(
bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce);
bmain, scene, CTX_data_view_layer(C), id_root, NULL, do_hierarchy_enforce, true);
WM_event_add_notifier(C, NC_WINDOW, NULL);
}