Changeset View
Changeset View
Standalone View
Standalone View
source/blender/blenkernel/intern/layer.c
| Show All 17 Lines | |||||
| * \ingroup bke | * \ingroup bke | ||||
| */ | */ | ||||
| /* Allow using deprecated functionality for .blend file I/O. */ | /* Allow using deprecated functionality for .blend file I/O. */ | ||||
| #define DNA_DEPRECATED_ALLOW | #define DNA_DEPRECATED_ALLOW | ||||
| #include <string.h> | #include <string.h> | ||||
| #include "CLG_log.h" | |||||
| #include "BLI_listbase.h" | #include "BLI_listbase.h" | ||||
| #include "BLI_mempool.h" | |||||
| #include "BLI_string.h" | #include "BLI_string.h" | ||||
| #include "BLI_string_utf8.h" | #include "BLI_string_utf8.h" | ||||
| #include "BLI_string_utils.h" | #include "BLI_string_utils.h" | ||||
| #include "BLI_threads.h" | #include "BLI_threads.h" | ||||
| #include "BLT_translation.h" | #include "BLT_translation.h" | ||||
| #include "BKE_animsys.h" | #include "BKE_animsys.h" | ||||
| #include "BKE_collection.h" | #include "BKE_collection.h" | ||||
| Show All 23 Lines | |||||
| #include "DRW_engine.h" | #include "DRW_engine.h" | ||||
| #include "RE_engine.h" | #include "RE_engine.h" | ||||
| #include "MEM_guardedalloc.h" | #include "MEM_guardedalloc.h" | ||||
| #include "BLO_read_write.h" | #include "BLO_read_write.h" | ||||
| static CLG_LogRef LOG = {"bke.layercollection"}; | |||||
| /* Set of flags which are dependent on a collection settings. */ | /* Set of flags which are dependent on a collection settings. */ | ||||
| static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | | static const short g_base_collection_flags = (BASE_VISIBLE_DEPSGRAPH | BASE_VISIBLE_VIEWLAYER | | ||||
| BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | | BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | | ||||
| BASE_ENABLED_RENDER | BASE_HOLDOUT | | BASE_ENABLED_RENDER | BASE_HOLDOUT | | ||||
| BASE_INDIRECT_ONLY); | BASE_INDIRECT_ONLY); | ||||
| /* prototype */ | /* prototype */ | ||||
| static void object_bases_iterator_next(BLI_Iterator *iter, const int flag); | static void object_bases_iterator_next(BLI_Iterator *iter, const int flag); | ||||
| ▲ Show 20 Lines • Show All 676 Lines • ▼ Show 20 Lines | |||||
| * | * | ||||
| * NOTE: This is a quick and safe band-aid around the long-known issue | * NOTE: This is a quick and safe band-aid around the long-known issue | ||||
| * regarding this resync process. | * regarding this resync process. | ||||
| * Proper fix would be to make resync itself lazy, i.e. only happen | * Proper fix would be to make resync itself lazy, i.e. only happen | ||||
| * when actually needed. | * when actually needed. | ||||
| * See also T73411. | * See also T73411. | ||||
| */ | */ | ||||
| static bool no_resync = false; | static bool no_resync = false; | ||||
| void BKE_layer_collection_resync_forbid(void) | void BKE_layer_collection_resync_forbid(void) | ||||
| { | { | ||||
| no_resync = true; | no_resync = true; | ||||
| } | } | ||||
brecht: Replace `i.e.` with `:`
"i.e." means that the statement is not precisely accurate and there is… | |||||
Done Inline ActionsNot sure I agree with that understanding of i.e.? It's just a shortcut for that is (to say), it means we are going to add some precision to the first part of the sentence. mont29: Not sure I agree with that understanding of `i.e.`? It's just a shortcut for `that is (to say)`… | |||||
Done Inline ActionsFair enough. brecht: Fair enough. | |||||
| void BKE_layer_collection_resync_allow(void) | void BKE_layer_collection_resync_allow(void) | ||||
| { | { | ||||
Done Inline Actionsprents -> parents brecht: prents -> parents | |||||
| no_resync = false; | no_resync = false; | ||||
| } | } | ||||
| typedef struct LayerCollectionResync { | |||||
| struct LayerCollectionResync *prev, *next; | |||||
| /* Temp data used to generate a queue during valid layer search. See | |||||
| * #layer_collection_resync_find. */ | |||||
| struct LayerCollectionResync *queue_next; | |||||
| /* LayerCollection and Collection wrapped by this data. */ | |||||
| LayerCollection *layer; | |||||
| Collection *collection; | |||||
| /* Hierarchical relationships in the old, existing ViewLayer state (except for newly created | |||||
| * layers). */ | |||||
| struct LayerCollectionResync *parent_layer_resync; | |||||
| ListBase children_layer_resync; | |||||
| /* This layer still points to a valid collection. */ | |||||
| bool is_usable; | |||||
| /* This layer is still valid as a parent, i.e. at least one of its original layer children is | |||||
| * usable and matches one of its current children collections. */ | |||||
| bool is_valid_as_parent; | |||||
| /* This layer is still valid as a child, i.e. its original layer parent is usable and matches one | |||||
| * of its current parents collections. */ | |||||
| bool is_valid_as_child; | |||||
| /* This layer is still fully valid in the new collection hierarchy, i.e. itself and all of its | |||||
| * parents fully match the current collection hierarchy. | |||||
| * OR | |||||
| * This layer has already been re-used to match the new collections hierarchy. */ | |||||
| bool is_used; | |||||
| } LayerCollectionResync; | |||||
| static LayerCollectionResync *layer_collection_resync_create_recurse( | |||||
| LayerCollectionResync *parent_layer_resync, LayerCollection *layer, BLI_mempool *mempool) | |||||
| { | |||||
| LayerCollectionResync *layer_resync = BLI_mempool_calloc(mempool); | |||||
| layer_resync->layer = layer; | |||||
| layer_resync->collection = layer->collection; | |||||
| layer_resync->parent_layer_resync = parent_layer_resync; | |||||
| if (parent_layer_resync != NULL) { | |||||
| BLI_addtail(&parent_layer_resync->children_layer_resync, layer_resync); | |||||
| } | |||||
| layer_resync->is_usable = (layer->collection != NULL); | |||||
| layer_resync->is_valid_as_child = | |||||
| layer_resync->is_usable && (parent_layer_resync == NULL || | |||||
| (parent_layer_resync->is_usable && | |||||
| BLI_findptr(&parent_layer_resync->layer->collection->children, | |||||
| layer->collection, | |||||
| offsetof(CollectionChild, collection)) != NULL)); | |||||
| if (layer_resync->is_valid_as_child) { | |||||
| layer_resync->is_used = parent_layer_resync != NULL ? parent_layer_resync->is_used : true; | |||||
| } | |||||
| else { | |||||
| layer_resync->is_used = false; | |||||
| } | |||||
| if (BLI_listbase_is_empty(&layer->layer_collections)) { | |||||
| layer_resync->is_valid_as_parent = layer_resync->is_usable; | |||||
| } | |||||
| else { | |||||
Done Inline Actionseuristics -> heuristics brecht: euristics -> heuristics | |||||
| LISTBASE_FOREACH (LayerCollection *, child_layer, &layer->layer_collections) { | |||||
| LayerCollectionResync *child_layer_resync = layer_collection_resync_create_recurse( | |||||
| layer_resync, child_layer, mempool); | |||||
| if (layer_resync->is_usable && child_layer_resync->is_valid_as_child) { | |||||
| layer_resync->is_valid_as_parent = true; | |||||
| } | |||||
| } | |||||
| } | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "Old LayerCollection for %s is...\n\tusable: %d\n\tvalid parent: %d\n\tvalid child: " | |||||
| "%d\n\tused: %d\n", | |||||
| layer_resync->collection ? layer_resync->collection->id.name : "<NONE>", | |||||
| layer_resync->is_usable, | |||||
| layer_resync->is_valid_as_parent, | |||||
| layer_resync->is_valid_as_child, | |||||
| layer_resync->is_used); | |||||
| return layer_resync; | |||||
| } | |||||
| static LayerCollectionResync *layer_collection_resync_find(LayerCollectionResync *layer_resync, | |||||
| Collection *child_collection) | |||||
| { | |||||
| /* Given the given parent, valid layer collection, find in the old hierarchy the best possible | |||||
| * unused layer matching the given child collection. | |||||
| * | |||||
| * This uses the following heuristics: | |||||
| * - Prefer a layer descendant of the given parent one if possible. | |||||
| * - Prefer a layer as closely related as possible from the given parent. | |||||
| * - Do not used layers that are not head (highest possible ancestor) of a local valid hierarchy | |||||
| * branch, since we can assume we could then re-use its ancestor instead. | |||||
| * | |||||
| * A queue is used to ensure this order of preferences. | |||||
| */ | |||||
| BLI_assert(layer_resync->collection != child_collection); | |||||
| BLI_assert(child_collection != NULL); | |||||
| LayerCollectionResync *current_layer_resync = NULL; | |||||
| LayerCollectionResync *root_layer_resync = layer_resync; | |||||
| LayerCollectionResync *queue_head = layer_resync, *queue_tail = layer_resync; | |||||
| layer_resync->queue_next = NULL; | |||||
| while (queue_head != NULL) { | |||||
| current_layer_resync = queue_head; | |||||
| queue_head = current_layer_resync->queue_next; | |||||
| if (current_layer_resync->collection == child_collection && | |||||
| (current_layer_resync->parent_layer_resync == layer_resync || | |||||
| (!current_layer_resync->is_used && !current_layer_resync->is_valid_as_child))) { | |||||
| /* This layer is a valid candidate, because its collection matches the seeked one, AND: | |||||
| * - It is a direct child of the initial given parent ('unchanged hierarchy' case), OR | |||||
| * - It is not currently used, and not part of a valid hierarchy (sub-)chain. | |||||
| */ | |||||
| break; | |||||
| } | |||||
| /* Else, add all its direct children for further searching. */ | |||||
| LISTBASE_FOREACH (LayerCollectionResync *, | |||||
| child_layer_resync, | |||||
| ¤t_layer_resync->children_layer_resync) { | |||||
| /* Add to tail of the queue. */ | |||||
| queue_tail->queue_next = child_layer_resync; | |||||
| child_layer_resync->queue_next = NULL; | |||||
| queue_tail = child_layer_resync; | |||||
| if (queue_head == NULL) { | |||||
| queue_head = queue_tail; | |||||
| } | |||||
| } | |||||
| /* If all descendants from current layer have been processed, go one step higher and | |||||
| * process all of its other siblings. */ | |||||
| if (queue_head == NULL && root_layer_resync->parent_layer_resync != NULL) { | |||||
| LISTBASE_FOREACH (LayerCollectionResync *, | |||||
| sibling_layer_resync, | |||||
| &root_layer_resync->parent_layer_resync->children_layer_resync) { | |||||
| if (sibling_layer_resync == root_layer_resync) { | |||||
| continue; | |||||
| } | |||||
| /* Add to tail of the queue. */ | |||||
| queue_tail->queue_next = sibling_layer_resync; | |||||
| sibling_layer_resync->queue_next = NULL; | |||||
| queue_tail = sibling_layer_resync; | |||||
| if (queue_head == NULL) { | |||||
| queue_head = queue_tail; | |||||
| } | |||||
| } | |||||
| root_layer_resync = root_layer_resync->parent_layer_resync; | |||||
| } | |||||
| current_layer_resync = NULL; | |||||
| } | |||||
| return current_layer_resync; | |||||
| } | |||||
| static void layer_collection_resync_unused_layers_free(ViewLayer *view_layer, | |||||
| LayerCollectionResync *layer_resync) | |||||
| { | |||||
| LISTBASE_FOREACH ( | |||||
| LayerCollectionResync *, child_layer_resync, &layer_resync->children_layer_resync) { | |||||
| layer_collection_resync_unused_layers_free(view_layer, child_layer_resync); | |||||
| } | |||||
| if (!layer_resync->is_used) { | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "Freeing unused LayerCollection for %s", | |||||
| layer_resync->collection != NULL ? layer_resync->collection->id.name : | |||||
| "<Deleted Collection>"); | |||||
| if (layer_resync->layer == view_layer->active_collection) { | |||||
| view_layer->active_collection = NULL; | |||||
| } | |||||
| /* We do not want to go recursive here, this is handled through the LayerCollectionResync data | |||||
| * wrapper. */ | |||||
| MEM_freeN(layer_resync->layer); | |||||
| layer_resync->layer = NULL; | |||||
| layer_resync->collection = NULL; | |||||
| layer_resync->is_usable = false; | |||||
| } | |||||
| } | |||||
| static void layer_collection_objects_sync(ViewLayer *view_layer, | static void layer_collection_objects_sync(ViewLayer *view_layer, | ||||
| LayerCollection *layer, | LayerCollection *layer, | ||||
| ListBase *r_lb_new_object_bases, | ListBase *r_lb_new_object_bases, | ||||
| const short collection_restrict, | const short collection_restrict, | ||||
| const short layer_restrict, | const short layer_restrict, | ||||
| const ushort local_collections_bits) | const ushort local_collections_bits) | ||||
| { | { | ||||
| /* No need to sync objects if the collection is excluded. */ | /* No need to sync objects if the collection is excluded. */ | ||||
| ▲ Show 20 Lines • Show All 52 Lines • ▼ Show 20 Lines | if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { | ||||
| base->flag_from_collection |= BASE_INDIRECT_ONLY; | base->flag_from_collection |= BASE_INDIRECT_ONLY; | ||||
| } | } | ||||
| layer->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; | layer->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; | ||||
| } | } | ||||
| } | } | ||||
| static void layer_collection_sync(ViewLayer *view_layer, | static void layer_collection_sync(ViewLayer *view_layer, | ||||
| const ListBase *lb_children_collections, | LayerCollectionResync *layer_resync, | ||||
| ListBase *r_lb_children_layers, | BLI_mempool *layer_resync_mempool, | ||||
| ListBase *r_lb_new_object_bases, | ListBase *r_lb_new_object_bases, | ||||
| const short parent_layer_flag, | const short parent_layer_flag, | ||||
| const short parent_collection_restrict, | const short parent_collection_restrict, | ||||
| const short parent_layer_restrict, | const short parent_layer_restrict, | ||||
| const ushort parent_local_collections_bits) | const ushort parent_local_collections_bits) | ||||
| { | { | ||||
| /* TODO: support recovery after removal of intermediate collections, reordering, .. | /* This function assumes current 'parent' layer collection is already fully (re)synced and valid | ||||
| * For local edits we can make editing operating do the appropriate thing, but for | * regarding current Collection hierarchy. | ||||
| * linking we can only sync after the fact. */ | * | ||||
| * It will process all the children collections of the collection from the given 'parent' layer, | |||||
| /* Remove layer collections that no longer have a corresponding scene collection. */ | * re-use or create layer collections for each of them, and ensure orders also match. | ||||
| LISTBASE_FOREACH_MUTABLE (LayerCollection *, child_layer, r_lb_children_layers) { | * | ||||
| /* Note that ID remap can set child_layer->collection to NULL when deleting collections. */ | * Then it will ensure that the objects owned by the given parent collection have a proper base. | ||||
| Collection *child_collection = (child_layer->collection != NULL) ? | * | ||||
| BLI_findptr(lb_children_collections, | * NOTE: This process is recursive. | ||||
| child_layer->collection, | */ | ||||
| offsetof(CollectionChild, collection)) : | |||||
| NULL; | |||||
| if (child_collection == NULL) { | |||||
| if (child_layer == view_layer->active_collection) { | |||||
| view_layer->active_collection = NULL; | |||||
| } | |||||
| /* Free recursively. */ | /* Temporary storage for all valid (new or reused) children layers. */ | ||||
| layer_collection_free(view_layer, child_layer); | ListBase new_lb_layer = {NULL, NULL}; | ||||
| BLI_freelinkN(r_lb_children_layers, child_layer); | |||||
| } | |||||
| } | |||||
| /* Add layer collections for any new scene collections, and ensure order is the same. */ | BLI_assert(layer_resync->is_used); | ||||
| ListBase lb_new_children_layers = {NULL, NULL}; | |||||
| LISTBASE_FOREACH (const CollectionChild *, child, lb_children_collections) { | LISTBASE_FOREACH (CollectionChild *, child, &layer_resync->collection->children) { | ||||
| Collection *child_collection = child->collection; | Collection *child_collection = child->collection; | ||||
| LayerCollection *child_layer = BLI_findptr( | LayerCollectionResync *child_layer_resync = layer_collection_resync_find(layer_resync, | ||||
| r_lb_children_layers, child_collection, offsetof(LayerCollection, collection)); | child_collection); | ||||
| if (child_layer) { | if (child_layer_resync != NULL) { | ||||
| BLI_remlink(r_lb_children_layers, child_layer); | BLI_assert(child_layer_resync->collection != NULL); | ||||
| BLI_addtail(&lb_new_children_layers, child_layer); | BLI_assert(child_layer_resync->layer != NULL); | ||||
| BLI_assert(child_layer_resync->is_usable); | |||||
| if (child_layer_resync->is_used) { | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "Found same existing LayerCollection for %s as child of %s", | |||||
| child_collection->id.name, | |||||
| layer_resync->collection->id.name); | |||||
| } | } | ||||
| else { | else { | ||||
| child_layer = layer_collection_add(&lb_new_children_layers, child_collection); | CLOG_INFO(&LOG, | ||||
| 4, | |||||
| "Found a valid unused LayerCollection for %s as child of %s, re-using it", | |||||
| child_collection->id.name, | |||||
| layer_resync->collection->id.name); | |||||
| } | |||||
| child_layer_resync->is_used = true; | |||||
| /* NOTE: Do not move the resync wrapper to match the new layer hierarchy, so that the old | |||||
| * parenting info remains available. In case a search for a valid layer in the children of | |||||
| * the current is required again, the old parenting hierarchy is needed as reference, not the | |||||
| * new one. | |||||
| */ | |||||
| BLI_remlink(&child_layer_resync->parent_layer_resync->layer->layer_collections, | |||||
| child_layer_resync->layer); | |||||
| BLI_addtail(&new_lb_layer, child_layer_resync->layer); | |||||
| } | |||||
| else { | |||||
| CLOG_INFO(&LOG, | |||||
| 4, | |||||
| "No available LayerCollection for %s as child of %s, creating a new one", | |||||
| child_collection->id.name, | |||||
| layer_resync->collection->id.name); | |||||
| LayerCollection *child_layer = layer_collection_add(&new_lb_layer, child_collection); | |||||
| child_layer->flag = parent_layer_flag; | child_layer->flag = parent_layer_flag; | ||||
| child_layer_resync = BLI_mempool_calloc(layer_resync_mempool); | |||||
| child_layer_resync->collection = child_collection; | |||||
| child_layer_resync->layer = child_layer; | |||||
| child_layer_resync->is_usable = true; | |||||
| child_layer_resync->is_used = true; | |||||
| child_layer_resync->is_valid_as_child = true; | |||||
| child_layer_resync->is_valid_as_parent = true; | |||||
| /* NOTE: Needs to be added to the layer_resync hierarchy so that the resync wrapper gets | |||||
| * freed at the end. */ | |||||
| child_layer_resync->parent_layer_resync = layer_resync; | |||||
| BLI_addtail(&layer_resync->children_layer_resync, child_layer_resync); | |||||
| } | } | ||||
| LayerCollection *child_layer = child_layer_resync->layer; | |||||
| const ushort child_local_collections_bits = parent_local_collections_bits & | const ushort child_local_collections_bits = parent_local_collections_bits & | ||||
| child_layer->local_collections_bits; | child_layer->local_collections_bits; | ||||
| /* Tag linked collection as a weak reference so we keep the layer | /* Tag linked collection as a weak reference so we keep the layer | ||||
| * collection pointer on file load and remember exclude state. */ | * collection pointer on file load and remember exclude state. */ | ||||
| id_lib_indirect_weak_link(&child_collection->id); | id_lib_indirect_weak_link(&child_collection->id); | ||||
| /* Collection restrict is inherited. */ | /* Collection restrict is inherited. */ | ||||
| short child_collection_restrict = parent_collection_restrict; | short child_collection_restrict = parent_collection_restrict; | ||||
| short child_layer_restrict = parent_layer_restrict; | short child_layer_restrict = parent_layer_restrict; | ||||
| if (!(child_collection->flag & COLLECTION_IS_MASTER)) { | if (!(child_collection->flag & COLLECTION_IS_MASTER)) { | ||||
| child_collection_restrict |= child_collection->flag; | child_collection_restrict |= child_collection->flag; | ||||
| child_layer_restrict |= child_layer->flag; | child_layer_restrict |= child_layer->flag; | ||||
| } | } | ||||
| /* Sync child collections. */ | /* Sync child collections. */ | ||||
| layer_collection_sync(view_layer, | layer_collection_sync(view_layer, | ||||
| &child_collection->children, | child_layer_resync, | ||||
| &child_layer->layer_collections, | layer_resync_mempool, | ||||
| r_lb_new_object_bases, | r_lb_new_object_bases, | ||||
| child_layer->flag, | child_layer->flag, | ||||
| child_collection_restrict, | child_collection_restrict, | ||||
| child_layer_restrict, | child_layer_restrict, | ||||
| child_local_collections_bits); | child_local_collections_bits); | ||||
| /* Layer collection exclude is not inherited, we can skip the remaining process, including | /* Layer collection exclude is not inherited. */ | ||||
| * object bases synchronization. */ | |||||
| child_layer->runtime_flag = 0; | child_layer->runtime_flag = 0; | ||||
| if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) { | if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) { | ||||
| continue; | continue; | ||||
| } | } | ||||
| /* We separate restrict viewport and visible view layer because a layer collection can be | /* We separate restrict viewport and visible view layer because a layer collection can be | ||||
| * hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */ | * hidden in the view layer yet (locally) visible in a viewport (if it is not restricted). */ | ||||
| if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) { | if (child_collection_restrict & COLLECTION_RESTRICT_VIEWPORT) { | ||||
| child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; | child_layer->runtime_flag |= LAYER_COLLECTION_RESTRICT_VIEWPORT; | ||||
| } | } | ||||
| if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && | if (((child_layer->runtime_flag & LAYER_COLLECTION_RESTRICT_VIEWPORT) == 0) && | ||||
| ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { | ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { | ||||
| child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; | child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; | ||||
| } | } | ||||
| layer_collection_objects_sync(view_layer, | |||||
| child_layer, | |||||
| r_lb_new_object_bases, | |||||
| child_collection_restrict, | |||||
| child_layer_restrict, | |||||
| child_local_collections_bits); | |||||
| } | |||||
| /* Free potentially remaining unused layer collections in old list. | |||||
| * NOTE: While this does not happen in typical situations, some corner cases (like remapping | |||||
| * several different collections to a single one) can lead to this list having extra unused | |||||
| * items. */ | |||||
| LISTBASE_FOREACH_MUTABLE (LayerCollection *, lc, r_lb_children_layers) { | |||||
| if (lc == view_layer->active_collection) { | |||||
| view_layer->active_collection = NULL; | |||||
| } | |||||
| /* Free recursively. */ | |||||
| layer_collection_free(view_layer, lc); | |||||
| BLI_freelinkN(r_lb_children_layers, lc); | |||||
| } | } | ||||
| BLI_assert(BLI_listbase_is_empty(r_lb_children_layers)); | |||||
| /* Replace layer collection list with new one. */ | /* Replace layer collection list with new one. */ | ||||
| *r_lb_children_layers = lb_new_children_layers; | layer_resync->layer->layer_collections = new_lb_layer; | ||||
| BLI_assert(BLI_listbase_count(lb_children_collections) == | BLI_assert(BLI_listbase_count(&layer_resync->collection->children) == | ||||
| BLI_listbase_count(r_lb_children_layers)); | BLI_listbase_count(&new_lb_layer)); | ||||
| /* Update bases etc. for objects. */ | |||||
| layer_collection_objects_sync(view_layer, | |||||
| layer_resync->layer, | |||||
| r_lb_new_object_bases, | |||||
| parent_collection_restrict, | |||||
| parent_layer_restrict, | |||||
| parent_local_collections_bits); | |||||
| } | } | ||||
| /** | /** | ||||
| * Update view layer collection tree from collections used in the scene. | * Update view layer collection tree from collections used in the scene. | ||||
| * This is used when collections are removed or added, both while editing | * This is used when collections are removed or added, both while editing | ||||
| * and on file loaded in case linked data changed or went missing. | * and on file loaded in case linked data changed or went missing. | ||||
| */ | */ | ||||
| void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) | void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) | ||||
| { | { | ||||
| if (no_resync) { | if (no_resync) { | ||||
| return; | return; | ||||
| } | } | ||||
| if (!scene->master_collection) { | if (!scene->master_collection) { | ||||
| /* Happens for old files that don't have versioning applied yet. */ | /* Happens for old files that don't have versioning applied yet. */ | ||||
| return; | return; | ||||
| } | } | ||||
| /* In some cases (from older files) we do have a master collection, yet no matching layer. Create | |||||
| * the master one here, so that the rest of the code can work as expected. */ | |||||
| if (BLI_listbase_is_empty(&view_layer->layer_collections)) { | |||||
| layer_collection_add(&view_layer->layer_collections, scene->master_collection); | |||||
| } | |||||
| /* Free cache. */ | /* Free cache. */ | ||||
| MEM_SAFE_FREE(view_layer->object_bases_array); | MEM_SAFE_FREE(view_layer->object_bases_array); | ||||
| /* Create object to base hash if it does not exist yet. */ | /* Create object to base hash if it does not exist yet. */ | ||||
| if (!view_layer->object_bases_hash) { | if (!view_layer->object_bases_hash) { | ||||
| view_layer_bases_hash_create(view_layer); | view_layer_bases_hash_create(view_layer); | ||||
| } | } | ||||
| /* Clear visible and selectable flags to be reset. */ | /* Clear visible and selectable flags to be reset. */ | ||||
| LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | ||||
| base->flag &= ~g_base_collection_flags; | base->flag &= ~g_base_collection_flags; | ||||
| base->flag_from_collection &= ~g_base_collection_flags; | base->flag_from_collection &= ~g_base_collection_flags; | ||||
| } | } | ||||
| /* Generate temporary data representing the old layers hierarchy, and how well it matches the | |||||
| * new collections hierarchy. */ | |||||
| BLI_mempool *layer_resync_mempool = BLI_mempool_create( | |||||
| sizeof(LayerCollectionResync), 1024, 1024, BLI_MEMPOOL_NOP); | |||||
| LayerCollectionResync *master_layer_resync = layer_collection_resync_create_recurse( | |||||
| NULL, view_layer->layer_collections.first, layer_resync_mempool); | |||||
| /* Generate new layer connections and object bases when collections changed. */ | /* Generate new layer connections and object bases when collections changed. */ | ||||
| CollectionChild child = {.next = NULL, .prev = NULL, .collection = scene->master_collection}; | |||||
| const ListBase collections = {.first = &child, .last = &child}; | |||||
| ListBase new_object_bases = {.first = NULL, .last = NULL}; | ListBase new_object_bases = {.first = NULL, .last = NULL}; | ||||
| const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0; | const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0; | ||||
| layer_collection_sync(view_layer, | layer_collection_sync(view_layer, | ||||
| &collections, | master_layer_resync, | ||||
| &view_layer->layer_collections, | layer_resync_mempool, | ||||
| &new_object_bases, | &new_object_bases, | ||||
| parent_exclude, | parent_exclude, | ||||
| parent_restrict, | parent_restrict, | ||||
| parent_layer_restrict, | parent_layer_restrict, | ||||
| ~(0)); | ~(0)); | ||||
| layer_collection_resync_unused_layers_free(view_layer, master_layer_resync); | |||||
| BLI_mempool_destroy(layer_resync_mempool); | |||||
| master_layer_resync = NULL; | |||||
| /* Any remaining object bases are to be removed. */ | /* Any remaining object bases are to be removed. */ | ||||
| LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { | ||||
| if (view_layer->basact == base) { | if (view_layer->basact == base) { | ||||
| view_layer->basact = NULL; | view_layer->basact = NULL; | ||||
| } | } | ||||
| if (base->object) { | if (base->object) { | ||||
| BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL); | BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL); | ||||
| ▲ Show 20 Lines • Show All 1,201 Lines • Show Last 20 Lines | |||||
Replace i.e. with :
"i.e." means that the statement is not precisely accurate and there is some detail being omitted, which is not the case.