Fix T93799: Outliner: Remaping objects could result in duplicates in a collection.
Fix is similar to how CollectionObject with NULL object pointers are handled. Using one of the 'free' pad bytes in Object_Runtime struct instead of a gset (or other external way to detect object duplicates), as this is several times faster. NOTE: This makes remapping slightly slower again (adds 10 extra seconds to file case in T94059). General improvements of remapping time complexity, especially when remapping a lot of IDs at once, is a separate topic currently investigated in D13615.
This commit is contained in:
parent
902318f0fd
commit
8d3e57f338
Notes:
blender-bot
2023-09-08 04:55:43 +02:00
Referenced by issue #93799, Outliner: Remap Users crash (for `ID Type` `Object`)
|
@ -163,7 +163,21 @@ bool BKE_scene_collections_object_remove(struct Main *bmain,
|
|||
struct Scene *scene,
|
||||
struct Object *object,
|
||||
const bool free_us);
|
||||
|
||||
/**
|
||||
* Check all collections in \a bmain (including embedded ones in scenes) for CollectionObject with
|
||||
* NULL object pointer, and remove them.
|
||||
*/
|
||||
void BKE_collections_object_remove_nulls(struct Main *bmain);
|
||||
|
||||
/**
|
||||
* Check all collections in \a bmain (including embedded ones in scenes) for duplicate
|
||||
* CollectionObject with a same object pointer within a same object, and remove them.
|
||||
*
|
||||
* NOTE: Always keeps the first of the detected duplicates.
|
||||
*/
|
||||
void BKE_collections_object_remove_duplicates(struct Main *bmain);
|
||||
|
||||
/**
|
||||
* Remove all NULL children from parent collections of changed \a collection.
|
||||
* This is used for library remapping, where these pointers have been set to NULL.
|
||||
|
|
|
@ -1231,6 +1231,50 @@ void BKE_collections_object_remove_nulls(Main *bmain)
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Remove all duplicate objects from collections.
|
||||
* This is used for library remapping, happens when remapping an object to another one already
|
||||
* present in the collection. Otherwise this should never happen.
|
||||
*/
|
||||
static void collection_object_remove_duplicates(Collection *collection)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (CollectionObject *, cob, &collection->gobject) {
|
||||
if (cob->ob->runtime.collection_management) {
|
||||
BLI_freelinkN(&collection->gobject, cob);
|
||||
changed = true;
|
||||
continue;
|
||||
}
|
||||
cob->ob->runtime.collection_management = true;
|
||||
}
|
||||
|
||||
/* Cleanup. */
|
||||
LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) {
|
||||
cob->ob->runtime.collection_management = false;
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
BKE_collection_object_cache_free(collection);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_collections_object_remove_duplicates(struct Main *bmain)
|
||||
{
|
||||
LISTBASE_FOREACH (Object *, ob, &bmain->objects) {
|
||||
ob->runtime.collection_management = false;
|
||||
}
|
||||
|
||||
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
|
||||
collection_object_remove_duplicates(scene->master_collection);
|
||||
}
|
||||
|
||||
for (Collection *collection = bmain->collections.first; collection;
|
||||
collection = collection->id.next) {
|
||||
collection_object_remove_duplicates(collection);
|
||||
}
|
||||
}
|
||||
|
||||
static void collection_null_children_remove(Collection *collection)
|
||||
{
|
||||
for (CollectionChild *child = collection->children.first, *child_next = NULL; child;
|
||||
|
|
|
@ -282,6 +282,11 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain,
|
|||
* to remove the NULL children from collections not used in any scene. */
|
||||
BKE_collections_object_remove_nulls(bmain);
|
||||
}
|
||||
else {
|
||||
/* Remapping may have created duplicates of CollectionObject pointing to the same object within
|
||||
* the same collection. */
|
||||
BKE_collections_object_remove_duplicates(bmain);
|
||||
}
|
||||
|
||||
BKE_main_collection_sync_remap(bmain);
|
||||
|
||||
|
@ -319,6 +324,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain,
|
|||
else {
|
||||
/* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from
|
||||
* old_collection instead? */
|
||||
/* NOTE: Also takes care of duplicated child collections that remapping may have created. */
|
||||
BKE_main_collections_parent_relations_rebuild(bmain);
|
||||
}
|
||||
|
||||
|
|
|
@ -126,7 +126,12 @@ typedef struct Object_Runtime {
|
|||
/** Did last modifier stack generation need mapping support? */
|
||||
char last_need_mapping;
|
||||
|
||||
char _pad0[3];
|
||||
/** Opaque data reserved for management of objects in collection context.
|
||||
* E.g. used currently to check for potential duplicates of objects in a collection, after
|
||||
* remapping process. */
|
||||
char collection_management;
|
||||
|
||||
char _pad0[2];
|
||||
|
||||
/** Only used for drawing the parent/child help-line. */
|
||||
float parent_display_origin[3];
|
||||
|
|
Loading…
Reference in New Issue