/* SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ /* Allow using deprecated functionality for .blend file I/O. */ #define DNA_DEPRECATED_ALLOW #include #include "CLG_log.h" #include "BLI_listbase.h" #include "BLI_mempool.h" #include "BLI_string.h" #include "BLI_string_utf8.h" #include "BLI_string_utils.h" #include "BLI_threads.h" #include "BLT_translation.h" #include "BKE_animsys.h" #include "BKE_collection.h" #include "BKE_freestyle.h" #include "BKE_idprop.h" #include "BKE_layer.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" #include "BKE_object.h" #include "DNA_ID.h" #include "DNA_collection_types.h" #include "DNA_layer_types.h" #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" #include "DNA_view3d_types.h" #include "DNA_windowmanager_types.h" #include "DNA_workspace_types.h" #include "DNA_world_types.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_debug.h" #include "DEG_depsgraph_query.h" #include "DRW_engine.h" #include "RE_engine.h" #include "MEM_guardedalloc.h" #include "BLO_read_write.h" static CLG_LogRef LOG = {"bke.layercollection"}; /* Set of flags which are dependent on a collection settings. */ static const short g_base_collection_flags = (BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT | BASE_SELECTABLE | BASE_ENABLED_VIEWPORT | BASE_ENABLED_RENDER | BASE_HOLDOUT | BASE_INDIRECT_ONLY); /* prototype */ static void object_bases_iterator_next(BLI_Iterator *iter, const int flag); /* -------------------------------------------------------------------- */ /** \name Layer Collections and Bases * \{ */ static LayerCollection *layer_collection_add(ListBase *lb_parent, Collection *collection) { LayerCollection *lc = MEM_callocN(sizeof(LayerCollection), "Collection Base"); lc->collection = collection; lc->local_collections_bits = ~(0); BLI_addtail(lb_parent, lc); return lc; } static void layer_collection_free(ViewLayer *view_layer, LayerCollection *lc) { if (lc == view_layer->active_collection) { view_layer->active_collection = NULL; } LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) { layer_collection_free(view_layer, nlc); } BLI_freelistN(&lc->layer_collections); } static Base *object_base_new(Object *ob) { Base *base = MEM_callocN(sizeof(Base), "Object Base"); base->object = ob; base->local_view_bits = ~(0); if (ob->base_flag & BASE_SELECTED) { base->flag |= BASE_SELECTED; } return base; } /** \} */ /* -------------------------------------------------------------------- */ /** \name View Layer * \{ */ /* RenderLayer */ ViewLayer *BKE_view_layer_default_view(const Scene *scene) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (!(view_layer->flag & VIEW_LAYER_RENDER)) { return view_layer; } } BLI_assert(scene->view_layers.first); return scene->view_layers.first; } ViewLayer *BKE_view_layer_default_render(const Scene *scene) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (view_layer->flag & VIEW_LAYER_RENDER) { return view_layer; } } BLI_assert(scene->view_layers.first); return scene->view_layers.first; } ViewLayer *BKE_view_layer_find(const Scene *scene, const char *layer_name) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (STREQ(view_layer->name, layer_name)) { return view_layer; } } return NULL; } ViewLayer *BKE_view_layer_context_active_PLACEHOLDER(const Scene *scene) { BLI_assert(scene->view_layers.first); return scene->view_layers.first; } static ViewLayer *view_layer_add(const char *name) { if (!name) { name = DATA_("ViewLayer"); } ViewLayer *view_layer = MEM_callocN(sizeof(ViewLayer), "View Layer"); view_layer->flag = VIEW_LAYER_RENDER | VIEW_LAYER_FREESTYLE; BLI_strncpy_utf8(view_layer->name, name, sizeof(view_layer->name)); /* Pure rendering pipeline settings. */ view_layer->layflag = SCE_LAY_FLAG_DEFAULT; view_layer->passflag = SCE_PASS_COMBINED; view_layer->pass_alpha_threshold = 0.5f; view_layer->cryptomatte_levels = 6; view_layer->cryptomatte_flag = VIEW_LAYER_CRYPTOMATTE_ACCURATE; BKE_freestyle_config_init(&view_layer->freestyle_config); return view_layer; } static void layer_collection_exclude_all(LayerCollection *layer_collection) { LayerCollection *sub_collection = layer_collection->layer_collections.first; for (; sub_collection != NULL; sub_collection = sub_collection->next) { sub_collection->flag |= LAYER_COLLECTION_EXCLUDE; layer_collection_exclude_all(sub_collection); } } ViewLayer *BKE_view_layer_add(Scene *scene, const char *name, ViewLayer *view_layer_source, const int type) { ViewLayer *view_layer_new; if (view_layer_source) { name = view_layer_source->name; } switch (type) { default: case VIEWLAYER_ADD_NEW: { view_layer_new = view_layer_add(name); BLI_addtail(&scene->view_layers, view_layer_new); BKE_layer_collection_sync(scene, view_layer_new); break; } case VIEWLAYER_ADD_COPY: { /* Allocate and copy view layer data */ view_layer_new = MEM_callocN(sizeof(ViewLayer), "View Layer"); *view_layer_new = *view_layer_source; BKE_view_layer_copy_data(scene, scene, view_layer_new, view_layer_source, 0); BLI_addtail(&scene->view_layers, view_layer_new); BLI_strncpy_utf8(view_layer_new->name, name, sizeof(view_layer_new->name)); break; } case VIEWLAYER_ADD_EMPTY: { view_layer_new = view_layer_add(name); BLI_addtail(&scene->view_layers, view_layer_new); /* Initialize layer-collections. */ BKE_layer_collection_sync(scene, view_layer_new); layer_collection_exclude_all(view_layer_new->layer_collections.first); /* Update collections after changing visibility */ BKE_layer_collection_sync(scene, view_layer_new); break; } } /* unique name */ BLI_uniquename(&scene->view_layers, view_layer_new, DATA_("ViewLayer"), '_', offsetof(ViewLayer, name), sizeof(view_layer_new->name)); return view_layer_new; } void BKE_view_layer_free(ViewLayer *view_layer) { BKE_view_layer_free_ex(view_layer, true); } void BKE_view_layer_free_ex(ViewLayer *view_layer, const bool do_id_user) { view_layer->basact = NULL; BLI_freelistN(&view_layer->object_bases); if (view_layer->object_bases_hash) { BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL); } LISTBASE_FOREACH (LayerCollection *, lc, &view_layer->layer_collections) { layer_collection_free(view_layer, lc); } BLI_freelistN(&view_layer->layer_collections); LISTBASE_FOREACH (ViewLayerEngineData *, sled, &view_layer->drawdata) { if (sled->storage) { if (sled->free) { sled->free(sled->storage); } MEM_freeN(sled->storage); } } BLI_freelistN(&view_layer->drawdata); BLI_freelistN(&view_layer->aovs); view_layer->active_aov = NULL; BLI_freelistN(&view_layer->lightgroups); view_layer->active_lightgroup = NULL; MEM_SAFE_FREE(view_layer->stats); BKE_freestyle_config_free(&view_layer->freestyle_config, do_id_user); if (view_layer->id_properties) { IDP_FreeProperty_ex(view_layer->id_properties, do_id_user); } MEM_SAFE_FREE(view_layer->object_bases_array); MEM_freeN(view_layer); } void BKE_view_layer_selected_objects_tag(const Scene *scene, ViewLayer *view_layer, const int tag) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { if ((base->flag & BASE_SELECTED) != 0) { base->object->flag |= tag; } else { base->object->flag &= ~tag; } } } static bool find_scene_collection_in_scene_collections(ListBase *lb, const LayerCollection *lc) { LISTBASE_FOREACH (LayerCollection *, lcn, lb) { if (lcn == lc) { return true; } if (find_scene_collection_in_scene_collections(&lcn->layer_collections, lc)) { return true; } } return false; } Object *BKE_view_layer_camera_find(const Scene *scene, ViewLayer *view_layer) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { if (base->object->type == OB_CAMERA) { return base->object; } } return NULL; } ViewLayer *BKE_view_layer_find_from_collection(const Scene *scene, LayerCollection *lc) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (find_scene_collection_in_scene_collections(&view_layer->layer_collections, lc)) { return view_layer; } } return NULL; } /* Base */ static void view_layer_bases_hash_create(ViewLayer *view_layer, const bool do_base_duplicates_fix) { static ThreadMutex hash_lock = BLI_MUTEX_INITIALIZER; if (view_layer->object_bases_hash == NULL) { BLI_mutex_lock(&hash_lock); if (view_layer->object_bases_hash == NULL) { GHash *hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); LISTBASE_FOREACH_MUTABLE (Base *, base, &view_layer->object_bases) { if (base->object) { void **val_pp; if (!BLI_ghash_ensure_p(hash, base->object, &val_pp)) { *val_pp = base; } /* The same object has several bases. * * In normal cases this is a serious bug, but this is a common situation when remapping * an object into another one already present in the same View Layer. While ideally we * would process this case separately, for performances reasons it makes more sense to * tackle it here. */ else if (do_base_duplicates_fix) { if (view_layer->basact == base) { view_layer->basact = NULL; } BLI_freelinkN(&view_layer->object_bases, base); } else { CLOG_FATAL(&LOG, "Object '%s' has more than one entry in view layer's object bases listbase", base->object->id.name + 2); } } } /* Assign pointer only after hash is complete. */ view_layer->object_bases_hash = hash; } BLI_mutex_unlock(&hash_lock); } } Base *BKE_view_layer_base_find(ViewLayer *view_layer, Object *ob) { BLI_assert_msg((view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) == 0, "View layer out of sync, invoke BKE_view_layer_synced_ensure."); if (!view_layer->object_bases_hash) { view_layer_bases_hash_create(view_layer, false); } return BLI_ghash_lookup(view_layer->object_bases_hash, ob); } void BKE_view_layer_base_deselect_all(const Scene *scene, ViewLayer *view_layer) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { base->flag &= ~BASE_SELECTED; } } void BKE_view_layer_base_select_and_set_active(struct ViewLayer *view_layer, Base *selbase) { view_layer->basact = selbase; if ((selbase->flag & BASE_SELECTABLE) != 0) { selbase->flag |= BASE_SELECTED; } } /** \} */ /* -------------------------------------------------------------------- */ /** \name Copy View Layer and Layer Collections * \{ */ static void layer_aov_copy_data(ViewLayer *view_layer_dst, const ViewLayer *view_layer_src, ListBase *aovs_dst, const ListBase *aovs_src) { if (aovs_src != NULL) { BLI_duplicatelist(aovs_dst, aovs_src); } ViewLayerAOV *aov_dst = aovs_dst->first; const ViewLayerAOV *aov_src = aovs_src->first; while (aov_dst != NULL) { BLI_assert(aov_src); if (aov_src == view_layer_src->active_aov) { view_layer_dst->active_aov = aov_dst; } aov_dst = aov_dst->next; aov_src = aov_src->next; } } static void layer_lightgroup_copy_data(ViewLayer *view_layer_dst, const ViewLayer *view_layer_src, ListBase *lightgroups_dst, const ListBase *lightgroups_src) { if (lightgroups_src != NULL) { BLI_duplicatelist(lightgroups_dst, lightgroups_src); } ViewLayerLightgroup *lightgroup_dst = lightgroups_dst->first; const ViewLayerLightgroup *lightgroup_src = lightgroups_src->first; while (lightgroup_dst != NULL) { BLI_assert(lightgroup_src); if (lightgroup_src == view_layer_src->active_lightgroup) { view_layer_dst->active_lightgroup = lightgroup_dst; } lightgroup_dst = lightgroup_dst->next; lightgroup_src = lightgroup_src->next; } } static void layer_collections_copy_data(ViewLayer *view_layer_dst, const ViewLayer *view_layer_src, ListBase *layer_collections_dst, const ListBase *layer_collections_src) { BLI_duplicatelist(layer_collections_dst, layer_collections_src); LayerCollection *layer_collection_dst = layer_collections_dst->first; const LayerCollection *layer_collection_src = layer_collections_src->first; while (layer_collection_dst != NULL) { layer_collections_copy_data(view_layer_dst, view_layer_src, &layer_collection_dst->layer_collections, &layer_collection_src->layer_collections); if (layer_collection_src == view_layer_src->active_collection) { view_layer_dst->active_collection = layer_collection_dst; } layer_collection_dst = layer_collection_dst->next; layer_collection_src = layer_collection_src->next; } } void BKE_view_layer_copy_data(Scene *scene_dst, const Scene *UNUSED(scene_src), ViewLayer *view_layer_dst, const ViewLayer *view_layer_src, const int flag) { if (view_layer_dst->id_properties != NULL) { view_layer_dst->id_properties = IDP_CopyProperty_ex(view_layer_dst->id_properties, flag); } BKE_freestyle_config_copy( &view_layer_dst->freestyle_config, &view_layer_src->freestyle_config, flag); view_layer_dst->stats = NULL; /* Clear temporary data. */ BLI_listbase_clear(&view_layer_dst->drawdata); view_layer_dst->object_bases_array = NULL; view_layer_dst->object_bases_hash = NULL; /* Copy layer collections and object bases. */ /* Inline 'BLI_duplicatelist' and update the active base. */ BLI_listbase_clear(&view_layer_dst->object_bases); BLI_assert_msg((view_layer_src->flag & VIEW_LAYER_OUT_OF_SYNC) == 0, "View Layer Object Base out of sync, invoke BKE_view_layer_synced_ensure."); LISTBASE_FOREACH (const Base *, base_src, &view_layer_src->object_bases) { Base *base_dst = MEM_dupallocN(base_src); BLI_addtail(&view_layer_dst->object_bases, base_dst); if (view_layer_src->basact == base_src) { view_layer_dst->basact = base_dst; } } view_layer_dst->active_collection = NULL; layer_collections_copy_data(view_layer_dst, view_layer_src, &view_layer_dst->layer_collections, &view_layer_src->layer_collections); LayerCollection *lc_scene_dst = view_layer_dst->layer_collections.first; lc_scene_dst->collection = scene_dst->master_collection; BLI_listbase_clear(&view_layer_dst->aovs); layer_aov_copy_data( view_layer_dst, view_layer_src, &view_layer_dst->aovs, &view_layer_src->aovs); BLI_listbase_clear(&view_layer_dst->lightgroups); layer_lightgroup_copy_data( view_layer_dst, view_layer_src, &view_layer_dst->lightgroups, &view_layer_src->lightgroups); if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { id_us_plus((ID *)view_layer_dst->mat_override); } } void BKE_view_layer_rename(Main *bmain, Scene *scene, ViewLayer *view_layer, const char *newname) { char oldname[sizeof(view_layer->name)]; BLI_strncpy(oldname, view_layer->name, sizeof(view_layer->name)); BLI_strncpy_utf8(view_layer->name, newname, sizeof(view_layer->name)); BLI_uniquename(&scene->view_layers, view_layer, DATA_("ViewLayer"), '.', offsetof(ViewLayer, name), sizeof(view_layer->name)); if (scene->nodetree) { bNode *node; int index = BLI_findindex(&scene->view_layers, view_layer); for (node = scene->nodetree->nodes.first; node; node = node->next) { if (node->type == CMP_NODE_R_LAYERS && node->id == NULL) { if (node->custom1 == index) { BLI_strncpy(node->name, view_layer->name, NODE_MAXSTR); } } } } /* Fix all the animation data and windows which may link to this. */ BKE_animdata_fix_paths_rename_all(NULL, "view_layers", oldname, view_layer->name); /* WM can be missing on startup. */ wmWindowManager *wm = bmain->wm.first; if (wm) { LISTBASE_FOREACH (wmWindow *, win, &wm->windows) { if (win->scene == scene && STREQ(win->view_layer_name, oldname)) { STRNCPY(win->view_layer_name, view_layer->name); } } } /* Dependency graph uses view layer name based lookups. */ DEG_id_tag_update(&scene->id, ID_RECALC_BASE_FLAGS); } /* LayerCollection */ /** * Recursively get the collection for a given index */ static LayerCollection *collection_from_index(ListBase *lb, const int number, int *i) { LISTBASE_FOREACH (LayerCollection *, lc, lb) { if (*i == number) { return lc; } (*i)++; } LISTBASE_FOREACH (LayerCollection *, lc, lb) { LayerCollection *lc_nested = collection_from_index(&lc->layer_collections, number, i); if (lc_nested) { return lc_nested; } } return NULL; } /** * Determine if a collection is hidden, viewport visibility restricted, or excluded */ static bool layer_collection_hidden(ViewLayer *view_layer, LayerCollection *lc) { if (lc->flag & LAYER_COLLECTION_EXCLUDE) { return true; } /* Check visibility restriction flags */ if (lc->flag & LAYER_COLLECTION_HIDE || lc->collection->flag & COLLECTION_HIDE_VIEWPORT) { return true; } /* Restriction flags stay set, so we need to check parents */ CollectionParent *parent = lc->collection->parents.first; if (parent) { lc = BKE_layer_collection_first_from_scene_collection(view_layer, parent->collection); return lc && layer_collection_hidden(view_layer, lc); } return false; return false; } LayerCollection *BKE_layer_collection_from_index(ViewLayer *view_layer, const int index) { int i = 0; return collection_from_index(&view_layer->layer_collections, index, &i); } LayerCollection *BKE_layer_collection_get_active(ViewLayer *view_layer) { return view_layer->active_collection; } bool BKE_layer_collection_activate(ViewLayer *view_layer, LayerCollection *lc) { if (lc->flag & LAYER_COLLECTION_EXCLUDE) { return false; } view_layer->active_collection = lc; return true; } LayerCollection *BKE_layer_collection_activate_parent(ViewLayer *view_layer, LayerCollection *lc) { CollectionParent *parent = lc->collection->parents.first; if (parent) { lc = BKE_layer_collection_first_from_scene_collection(view_layer, parent->collection); } else { lc = NULL; } /* Don't activate excluded or hidden collections to prevent creating objects in a hidden * collection from the UI */ if (lc && layer_collection_hidden(view_layer, lc)) { return BKE_layer_collection_activate_parent(view_layer, lc); } if (!lc) { lc = view_layer->layer_collections.first; } view_layer->active_collection = lc; return lc; } /** * Recursively get the count of collections */ static int collection_count(const ListBase *lb) { int i = 0; LISTBASE_FOREACH (const LayerCollection *, lc, lb) { i += collection_count(&lc->layer_collections) + 1; } return i; } int BKE_layer_collection_count(const ViewLayer *view_layer) { return collection_count(&view_layer->layer_collections); } /** * Recursively get the index for a given collection */ static int index_from_collection(ListBase *lb, const LayerCollection *lc, int *i) { LISTBASE_FOREACH (LayerCollection *, lcol, lb) { if (lcol == lc) { return *i; } (*i)++; } LISTBASE_FOREACH (LayerCollection *, lcol, lb) { int i_nested = index_from_collection(&lcol->layer_collections, lc, i); if (i_nested != -1) { return i_nested; } } return -1; } int BKE_layer_collection_findindex(ViewLayer *view_layer, const LayerCollection *lc) { int i = 0; return index_from_collection(&view_layer->layer_collections, lc, &i); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Syncing * * The layer collection tree mirrors the scene collection tree. Whenever that * changes we need to synchronize them so that there is a corresponding layer * collection for each collection. Note that the scene collection tree can * contain link or override collections, and so this is also called on .blend * file load to ensure any new or removed collections are synced. * * The view layer also contains a list of bases for each object that exists * in at least one layer collection. That list is also synchronized here, and * stores state like selection. * * This API allows to temporarily forbid resync of LayerCollections. * * This can greatly improve performances in cases where those functions get * called a lot (e.g. during massive remappings of IDs). * * Usage of these should be done very carefully though. In particular, calling * code must ensures it resync LayerCollections before any UI/Event loop * handling can happen. * * \warning This is not threadsafe at all, only use from main thread. * * \note It is probably needed to use #BKE_main_collection_sync_remap instead * of just #BKE_main_collection_sync after disabling LayerCollection resync, * unless it is absolutely certain that no ID remapping (or any other process * that may invalidate the caches) will happen while it is disabled. * * \note This is a quick and safe band-aid around the long-known issue * regarding this resync process. * Proper fix would be to make resync itself lazy, i.e. only happen * when actually needed. * See also T73411. * \{ */ static bool no_resync = false; void BKE_layer_collection_resync_forbid(void) { no_resync = true; } void BKE_layer_collection_resync_allow(void) { 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 { 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 : "", 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 : ""); 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; } } void BKE_view_layer_need_resync_tag(struct ViewLayer *view_layer) { view_layer->flag |= VIEW_LAYER_OUT_OF_SYNC; } void BKE_view_layer_synced_ensure(const Scene *scene, struct ViewLayer *view_layer) { if (view_layer->flag & VIEW_LAYER_OUT_OF_SYNC) { BKE_layer_collection_sync(scene, view_layer); view_layer->flag &= ~VIEW_LAYER_OUT_OF_SYNC; } } void BKE_scene_view_layers_synced_ensure(const Scene *scene) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { BKE_view_layer_synced_ensure(scene, view_layer); } } void BKE_main_view_layers_synced_ensure(const Main *bmain) { for (const Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { BKE_scene_view_layers_synced_ensure(scene); } /* NOTE: This is not (yet?) covered by the dirty tag and differed re-sync system */ BKE_layer_collection_local_sync_all(bmain); } static void layer_collection_objects_sync(ViewLayer *view_layer, LayerCollection *layer, ListBase *r_lb_new_object_bases, const short collection_restrict, const short layer_restrict, const ushort local_collections_bits) { /* No need to sync objects if the collection is excluded. */ if ((layer->flag & LAYER_COLLECTION_EXCLUDE) != 0) { return; } LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) { if (cob->ob == NULL) { continue; } /* Tag linked object as a weak reference so we keep the object * base pointer on file load and remember hidden state. */ id_lib_indirect_weak_link(&cob->ob->id); void **base_p; Base *base; if (BLI_ghash_ensure_p(view_layer->object_bases_hash, cob->ob, &base_p)) { /* Move from old base list to new base list. Base might have already * been moved to the new base list and the first/last test ensure that * case also works. */ base = *base_p; if (!ELEM(base, r_lb_new_object_bases->first, r_lb_new_object_bases->last)) { BLI_remlink(&view_layer->object_bases, base); BLI_addtail(r_lb_new_object_bases, base); } } else { /* Create new base. */ base = object_base_new(cob->ob); base->local_collections_bits = local_collections_bits; *base_p = base; BLI_addtail(r_lb_new_object_bases, base); } if ((collection_restrict & COLLECTION_HIDE_VIEWPORT) == 0) { base->flag_from_collection |= (BASE_ENABLED_VIEWPORT | BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT); if ((layer_restrict & LAYER_COLLECTION_HIDE) == 0) { base->flag_from_collection |= BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } if ((collection_restrict & COLLECTION_HIDE_SELECT) == 0) { base->flag_from_collection |= BASE_SELECTABLE; } } if ((collection_restrict & COLLECTION_HIDE_RENDER) == 0) { base->flag_from_collection |= BASE_ENABLED_RENDER; } /* Holdout and indirect only */ if ((layer->flag & LAYER_COLLECTION_HOLDOUT)) { base->flag_from_collection |= BASE_HOLDOUT; } if (layer->flag & LAYER_COLLECTION_INDIRECT_ONLY) { base->flag_from_collection |= BASE_INDIRECT_ONLY; } layer->runtime_flag |= LAYER_COLLECTION_HAS_OBJECTS; } } static void layer_collection_sync(ViewLayer *view_layer, LayerCollectionResync *layer_resync, BLI_mempool *layer_resync_mempool, ListBase *r_lb_new_object_bases, const short parent_layer_flag, const short parent_collection_restrict, const short parent_layer_restrict, const ushort parent_local_collections_bits) { /* This function assumes current 'parent' layer collection is already fully (re)synced and valid * regarding current Collection hierarchy. * * It will process all the children collections of the collection from the given 'parent' layer, * re-use or create layer collections for each of them, and ensure orders also match. * * Then it will ensure that the objects owned by the given parent collection have a proper base. * * NOTE: This process is recursive. */ /* Temporary storage for all valid (new or reused) children layers. */ ListBase new_lb_layer = {NULL, NULL}; BLI_assert(layer_resync->is_used); uint64_t skipped_children = 0; LISTBASE_FOREACH (CollectionChild *, child, &layer_resync->collection->children) { Collection *child_collection = child->collection; /* Collection relations may not have rebuild yet. */ if (child_collection == NULL) { skipped_children++; continue; } LayerCollectionResync *child_layer_resync = layer_collection_resync_find(layer_resync, child_collection); if (child_layer_resync != NULL) { BLI_assert(child_layer_resync->collection != NULL); 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 { 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_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 & child_layer->local_collections_bits; /* Tag linked collection as a weak reference so we keep the layer * collection pointer on file load and remember exclude state. */ id_lib_indirect_weak_link(&child_collection->id); /* Collection restrict is inherited. */ short child_collection_restrict = parent_collection_restrict; short child_layer_restrict = parent_layer_restrict; if (!(child_collection->flag & COLLECTION_IS_MASTER)) { child_collection_restrict |= child_collection->flag; child_layer_restrict |= child_layer->flag; } /* Sync child collections. */ layer_collection_sync(view_layer, child_layer_resync, layer_resync_mempool, r_lb_new_object_bases, child_layer->flag, child_collection_restrict, child_layer_restrict, child_local_collections_bits); /* Layer collection exclude is not inherited. */ child_layer->runtime_flag = 0; if (child_layer->flag & LAYER_COLLECTION_EXCLUDE) { continue; } /* 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). */ if (child_collection_restrict & COLLECTION_HIDE_VIEWPORT) { child_layer->runtime_flag |= LAYER_COLLECTION_HIDE_VIEWPORT; } if (((child_layer->runtime_flag & LAYER_COLLECTION_HIDE_VIEWPORT) == 0) && ((child_layer_restrict & LAYER_COLLECTION_HIDE) == 0)) { child_layer->runtime_flag |= LAYER_COLLECTION_VISIBLE_VIEW_LAYER; } } /* Replace layer collection list with new one. */ layer_resync->layer->layer_collections = new_lb_layer; BLI_assert(BLI_listbase_count(&layer_resync->collection->children) - skipped_children == 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); } #ifndef NDEBUG static bool view_layer_objects_base_cache_validate(ViewLayer *view_layer, LayerCollection *layer) { bool is_valid = true; if (layer == NULL) { layer = view_layer->layer_collections.first; } /* Only check for a collection's objects if its layer is not excluded. */ if ((layer->flag & LAYER_COLLECTION_EXCLUDE) == 0) { LISTBASE_FOREACH (CollectionObject *, cob, &layer->collection->gobject) { if (cob->ob == NULL) { continue; } if (BLI_ghash_lookup(view_layer->object_bases_hash, cob->ob) == NULL) { CLOG_FATAL( &LOG, "Object '%s' from collection '%s' has no entry in view layer's object bases cache", cob->ob->id.name + 2, layer->collection->id.name + 2); is_valid = false; break; } } } if (is_valid) { LISTBASE_FOREACH (LayerCollection *, layer_child, &layer->layer_collections) { if (!view_layer_objects_base_cache_validate(view_layer, layer_child)) { is_valid = false; break; } } } return is_valid; } #else static bool view_layer_objects_base_cache_validate(ViewLayer *UNUSED(view_layer), LayerCollection *UNUSED(layer)) { return true; } #endif void BKE_layer_collection_doversion_2_80(const Scene *scene, ViewLayer *view_layer) { LayerCollection *first_layer_collection = view_layer->layer_collections.first; if (BLI_listbase_count_at_most(&view_layer->layer_collections, 2) > 1 || first_layer_collection->collection != scene->master_collection) { /* In some cases (from older files) we do have a master collection, but no matching layer, * instead all the children of the master collection have their layer collections in the * viewlayer's list. This is not a valid situation, add a layer for the master collection and * add all existing first-level layers as children of that new master layer. */ ListBase layer_collections = view_layer->layer_collections; BLI_listbase_clear(&view_layer->layer_collections); LayerCollection *master_layer_collection = layer_collection_add(&view_layer->layer_collections, scene->master_collection); master_layer_collection->layer_collections = layer_collections; } } void BKE_layer_collection_sync(const Scene *scene, ViewLayer *view_layer) { if (no_resync) { return; } if (!scene->master_collection) { /* Happens for old files that don't have versioning applied yet. */ return; } if (BLI_listbase_is_empty(&view_layer->layer_collections)) { /* In some cases (from older files, or when creating a new ViewLayer from * #BKE_view_layer_add), 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. */ layer_collection_add(&view_layer->layer_collections, scene->master_collection); } #ifndef NDEBUG { BLI_assert_msg(BLI_listbase_count_at_most(&view_layer->layer_collections, 2) == 1, "ViewLayer's first level of children layer collections should always have " "exactly one item"); LayerCollection *first_layer_collection = view_layer->layer_collections.first; BLI_assert_msg(first_layer_collection->collection == scene->master_collection, "ViewLayer's first layer collection should always be the one for the scene's " "master collection"); } #endif /* Free cache. */ MEM_SAFE_FREE(view_layer->object_bases_array); /* Create object to base hash if it does not exist yet. */ if (!view_layer->object_bases_hash) { view_layer_bases_hash_create(view_layer, false); } /* Clear visible and selectable flags to be reset. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { base->flag &= ~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. */ ListBase new_object_bases = {.first = NULL, .last = NULL}; const short parent_exclude = 0, parent_restrict = 0, parent_layer_restrict = 0; layer_collection_sync(view_layer, master_layer_resync, layer_resync_mempool, &new_object_bases, parent_exclude, parent_restrict, parent_layer_restrict, ~(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. */ LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { if (view_layer->basact == base) { view_layer->basact = NULL; } if (base->object) { /* Those asserts are commented, since they are too expensive to perform even in debug, as * this layer resync function currently gets called way too often. */ #if 0 BLI_assert(BLI_findindex(&new_object_bases, base) == -1); BLI_assert(BLI_findptr(&new_object_bases, base->object, offsetof(Base, object)) == NULL); #endif BLI_ghash_remove(view_layer->object_bases_hash, base->object, NULL, NULL); } } BLI_freelistN(&view_layer->object_bases); view_layer->object_bases = new_object_bases; view_layer_objects_base_cache_validate(view_layer, NULL); LISTBASE_FOREACH (Base *, base, &view_layer->object_bases) { BKE_base_eval_flags(base); } /* Always set a valid active collection. */ LayerCollection *active = view_layer->active_collection; if (active && layer_collection_hidden(view_layer, active)) { BKE_layer_collection_activate_parent(view_layer, active); } else if (active == NULL) { view_layer->active_collection = view_layer->layer_collections.first; } } void BKE_scene_collection_sync(const Scene *scene) { if (no_resync) { return; } LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { BKE_view_layer_need_resync_tag(view_layer); } } void BKE_main_collection_sync(const Main *bmain) { if (no_resync) { return; } /* TODO: if a single collection changed, figure out which * scenes it belongs to and only update those. */ /* TODO: optimize for file load so only linked collections get checked? */ for (const Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { BKE_scene_collection_sync(scene); } BKE_layer_collection_local_sync_all(bmain); } void BKE_main_collection_sync_remap(const Main *bmain) { if (no_resync) { return; } /* On remapping of object or collection pointers free caches. */ /* TODO: try to make this faster */ for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { MEM_SAFE_FREE(view_layer->object_bases_array); if (view_layer->object_bases_hash) { BLI_ghash_free(view_layer->object_bases_hash, NULL, NULL); view_layer->object_bases_hash = NULL; } /* Directly re-create the mapping here, so that we can also deal with duplicates in * `view_layer->object_bases` list of bases properly. This is the only place where such * duplicates should be fixed, and not considered as a critical error. */ view_layer_bases_hash_create(view_layer, true); } BKE_collection_object_cache_free(scene->master_collection); DEG_id_tag_update_ex((Main *)bmain, &scene->master_collection->id, ID_RECALC_COPY_ON_WRITE); DEG_id_tag_update_ex((Main *)bmain, &scene->id, ID_RECALC_COPY_ON_WRITE); } for (Collection *collection = bmain->collections.first; collection; collection = collection->id.next) { BKE_collection_object_cache_free(collection); DEG_id_tag_update_ex((Main *)bmain, &collection->id, ID_RECALC_COPY_ON_WRITE); } BKE_main_collection_sync(bmain); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Object Selection * \{ */ bool BKE_layer_collection_objects_select(const Scene *scene, ViewLayer *view_layer, LayerCollection *lc, bool deselect) { if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } bool changed = false; if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) { Base *base = BKE_view_layer_base_find(view_layer, cob->ob); if (base) { if (deselect) { if (base->flag & BASE_SELECTED) { base->flag &= ~BASE_SELECTED; changed = true; } } else { if ((base->flag & BASE_SELECTABLE) && !(base->flag & BASE_SELECTED)) { base->flag |= BASE_SELECTED; changed = true; } } } } } LISTBASE_FOREACH (LayerCollection *, iter, &lc->layer_collections) { changed |= BKE_layer_collection_objects_select(scene, view_layer, iter, deselect); } return changed; } bool BKE_layer_collection_has_selected_objects(const Scene *scene, ViewLayer *view_layer, LayerCollection *lc) { if (lc->collection->flag & COLLECTION_HIDE_SELECT) { return false; } if (!(lc->flag & LAYER_COLLECTION_EXCLUDE)) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) { Base *base = BKE_view_layer_base_find(view_layer, cob->ob); if (base && (base->flag & BASE_SELECTED) && (base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT)) { return true; } } } LISTBASE_FOREACH (LayerCollection *, iter, &lc->layer_collections) { if (BKE_layer_collection_has_selected_objects(scene, view_layer, iter)) { return true; } } return false; } bool BKE_layer_collection_has_layer_collection(LayerCollection *lc_parent, LayerCollection *lc_child) { if (lc_parent == lc_child) { return true; } LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_parent->layer_collections) { if (BKE_layer_collection_has_layer_collection(lc_iter, lc_child)) { return true; } } return false; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Object Visibility * \{ */ void BKE_base_set_visible(Scene *scene, ViewLayer *view_layer, Base *base, bool extend) { if (!extend) { /* Make only one base visible. */ BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, other, BKE_view_layer_object_bases_get(view_layer)) { other->flag |= BASE_HIDDEN; } base->flag &= ~BASE_HIDDEN; } else { /* Toggle visibility of one base. */ base->flag ^= BASE_HIDDEN; } BKE_view_layer_need_resync_tag(view_layer); } bool BKE_base_is_visible(const View3D *v3d, const Base *base) { if ((base->flag & BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT) == 0) { return false; } if (v3d == NULL) { return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } if ((v3d->localvd) && ((v3d->local_view_uuid & base->local_view_bits) == 0)) { return false; } if (((1 << (base->object->type)) & v3d->object_type_exclude_viewport) != 0) { return false; } if (v3d->flag & V3D_LOCAL_COLLECTIONS) { return (v3d->local_collections_uuid & base->local_collections_bits) != 0; } return base->flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT; } bool BKE_object_is_visible_in_viewport(const View3D *v3d, const struct Object *ob) { BLI_assert(v3d != NULL); if (ob->visibility_flag & OB_HIDE_VIEWPORT) { return false; } if ((v3d->object_type_exclude_viewport & (1 << ob->type)) != 0) { return false; } if (v3d->localvd && ((v3d->local_view_uuid & ob->base_local_view_bits) == 0)) { return false; } if ((v3d->flag & V3D_LOCAL_COLLECTIONS) && ((v3d->local_collections_uuid & ob->runtime.local_collections_bits) == 0)) { return false; } /* If not using local collection the object may still be in a hidden collection. */ if ((v3d->flag & V3D_LOCAL_COLLECTIONS) == 0) { return (ob->base_flag & BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT) != 0; } return true; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Collection Isolation & Local View * \{ */ static void layer_collection_flag_set_recursive(LayerCollection *lc, const int flag) { lc->flag |= flag; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) { layer_collection_flag_set_recursive(lc_iter, flag); } } static void layer_collection_flag_unset_recursive(LayerCollection *lc, const int flag) { lc->flag &= ~flag; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) { layer_collection_flag_unset_recursive(lc_iter, flag); } } void BKE_layer_collection_isolate_global(Scene *UNUSED(scene), ViewLayer *view_layer, LayerCollection *lc, bool extend) { LayerCollection *lc_master = view_layer->layer_collections.first; bool hide_it = extend && (lc->runtime_flag & LAYER_COLLECTION_VISIBLE_VIEW_LAYER); if (!extend) { /* Hide all collections. */ LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { layer_collection_flag_set_recursive(lc_iter, LAYER_COLLECTION_HIDE); } } /* Make all the direct parents visible. */ if (hide_it) { lc->flag |= LAYER_COLLECTION_HIDE; } else { LayerCollection *lc_parent = lc; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) { lc_parent = lc_iter; break; } } while (lc_parent != lc) { lc_parent->flag &= ~LAYER_COLLECTION_HIDE; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_parent->layer_collections) { if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) { lc_parent = lc_iter; break; } } } /* Make all the children visible, but respect their disable state. */ layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_HIDE); BKE_layer_collection_activate(view_layer, lc); } BKE_view_layer_need_resync_tag(view_layer); } static void layer_collection_local_visibility_set_recursive(LayerCollection *layer_collection, const int local_collections_uuid) { layer_collection->local_collections_bits |= local_collections_uuid; LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) { layer_collection_local_visibility_set_recursive(child, local_collections_uuid); } } static void layer_collection_local_visibility_unset_recursive(LayerCollection *layer_collection, const int local_collections_uuid) { layer_collection->local_collections_bits &= ~local_collections_uuid; LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) { layer_collection_local_visibility_unset_recursive(child, local_collections_uuid); } } static void layer_collection_local_sync(const Scene *scene, ViewLayer *view_layer, LayerCollection *layer_collection, const ushort local_collections_uuid, bool visible) { if ((layer_collection->local_collections_bits & local_collections_uuid) == 0) { visible = false; } if (visible) { LISTBASE_FOREACH (CollectionObject *, cob, &layer_collection->collection->gobject) { if (cob->ob == NULL) { continue; } BKE_view_layer_synced_ensure(scene, view_layer); Base *base = BKE_view_layer_base_find(view_layer, cob->ob); base->local_collections_bits |= local_collections_uuid; } } LISTBASE_FOREACH (LayerCollection *, child, &layer_collection->layer_collections) { if ((child->flag & LAYER_COLLECTION_EXCLUDE) == 0) { layer_collection_local_sync(scene, view_layer, child, local_collections_uuid, visible); } } } void BKE_layer_collection_local_sync(const Scene *scene, ViewLayer *view_layer, const View3D *v3d) { if (no_resync) { return; } const ushort local_collections_uuid = v3d->local_collections_uuid; /* Reset flags and set the bases visible by default. */ BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { base->local_collections_bits &= ~local_collections_uuid; } LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) { layer_collection_local_sync(scene, view_layer, layer_collection, local_collections_uuid, true); } } void BKE_layer_collection_local_sync_all(const Main *bmain) { if (no_resync) { return; } LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { LISTBASE_FOREACH (bScreen *, screen, &bmain->screens) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { if (area->spacetype != SPACE_VIEW3D) { continue; } View3D *v3d = area->spacedata.first; if (v3d->flag & V3D_LOCAL_COLLECTIONS) { BKE_layer_collection_local_sync(scene, view_layer, v3d); } } } } } } void BKE_layer_collection_isolate_local( const Scene *scene, ViewLayer *view_layer, const View3D *v3d, LayerCollection *lc, bool extend) { LayerCollection *lc_master = view_layer->layer_collections.first; bool hide_it = extend && ((v3d->local_collections_uuid & lc->local_collections_bits) != 0); if (!extend) { /* Hide all collections. */ LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { layer_collection_local_visibility_unset_recursive(lc_iter, v3d->local_collections_uuid); } } /* Make all the direct parents visible. */ if (hide_it) { lc->local_collections_bits &= ~(v3d->local_collections_uuid); } else { LayerCollection *lc_parent = lc; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_master->layer_collections) { if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) { lc_parent = lc_iter; break; } } while (lc_parent != lc) { lc_parent->local_collections_bits |= v3d->local_collections_uuid; LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc_parent->layer_collections) { if (BKE_layer_collection_has_layer_collection(lc_iter, lc)) { lc_parent = lc_iter; break; } } } /* Make all the children visible. */ layer_collection_local_visibility_set_recursive(lc, v3d->local_collections_uuid); } BKE_layer_collection_local_sync(scene, view_layer, v3d); } static void layer_collection_bases_show_recursive(const Scene *scene, ViewLayer *view_layer, LayerCollection *lc) { if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) { Base *base = BKE_view_layer_base_find(view_layer, cob->ob); base->flag &= ~BASE_HIDDEN; } } LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) { layer_collection_bases_show_recursive(scene, view_layer, lc_iter); } } static void layer_collection_bases_hide_recursive(const Scene *scene, ViewLayer *view_layer, LayerCollection *lc) { if ((lc->flag & LAYER_COLLECTION_EXCLUDE) == 0) { BKE_view_layer_synced_ensure(scene, view_layer); LISTBASE_FOREACH (CollectionObject *, cob, &lc->collection->gobject) { Base *base = BKE_view_layer_base_find(view_layer, cob->ob); base->flag |= BASE_HIDDEN; } } LISTBASE_FOREACH (LayerCollection *, lc_iter, &lc->layer_collections) { layer_collection_bases_hide_recursive(scene, view_layer, lc_iter); } } void BKE_layer_collection_set_visible(const Scene *scene, ViewLayer *view_layer, LayerCollection *lc, const bool visible, const bool hierarchy) { if (hierarchy) { if (visible) { layer_collection_flag_unset_recursive(lc, LAYER_COLLECTION_HIDE); layer_collection_bases_show_recursive(scene, view_layer, lc); } else { layer_collection_flag_set_recursive(lc, LAYER_COLLECTION_HIDE); layer_collection_bases_hide_recursive(scene, view_layer, lc); } } else { if (visible) { lc->flag &= ~LAYER_COLLECTION_HIDE; } else { lc->flag |= LAYER_COLLECTION_HIDE; } } } /** * Set layer collection hide/exclude/indirect flag on a layer collection. * recursively. */ static void layer_collection_flag_recursive_set(LayerCollection *lc, const int flag, const bool value, const bool restore_flag) { if (flag == LAYER_COLLECTION_EXCLUDE) { /* For exclude flag, we remember the state the children had before * excluding and restoring it when enabling the parent collection again. */ if (value) { if (restore_flag) { SET_FLAG_FROM_TEST( lc->flag, (lc->flag & LAYER_COLLECTION_EXCLUDE), LAYER_COLLECTION_PREVIOUSLY_EXCLUDED); } else { lc->flag &= ~LAYER_COLLECTION_PREVIOUSLY_EXCLUDED; } lc->flag |= flag; } else { if (!(lc->flag & LAYER_COLLECTION_PREVIOUSLY_EXCLUDED)) { lc->flag &= ~flag; } } } else { SET_FLAG_FROM_TEST(lc->flag, value, flag); } LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) { layer_collection_flag_recursive_set(nlc, flag, value, true); } } void BKE_layer_collection_set_flag(LayerCollection *lc, const int flag, const bool value) { layer_collection_flag_recursive_set(lc, flag, value, false); } /* ---------------------------------------------------------------------- */ static LayerCollection *find_layer_collection_by_scene_collection(LayerCollection *lc, const Collection *collection) { if (lc->collection == collection) { return lc; } LISTBASE_FOREACH (LayerCollection *, nlc, &lc->layer_collections) { LayerCollection *found = find_layer_collection_by_scene_collection(nlc, collection); if (found) { return found; } } return NULL; } LayerCollection *BKE_layer_collection_first_from_scene_collection(const ViewLayer *view_layer, const Collection *collection) { LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) { LayerCollection *found = find_layer_collection_by_scene_collection(layer_collection, collection); if (found != NULL) { return found; } } return NULL; } bool BKE_view_layer_has_collection(const ViewLayer *view_layer, const Collection *collection) { return BKE_layer_collection_first_from_scene_collection(view_layer, collection) != NULL; } bool BKE_scene_has_object(Scene *scene, Object *ob) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { BKE_view_layer_synced_ensure(scene, view_layer); Base *base = BKE_view_layer_base_find(view_layer, ob); if (base) { return true; } } return false; } /** \} */ /* Iterators */ /* -------------------------------------------------------------------- */ /** \name Private Iterator Helpers * \{ */ typedef struct LayerObjectBaseIteratorData { const View3D *v3d; Base *base; } LayerObjectBaseIteratorData; static bool object_bases_iterator_is_valid(const View3D *v3d, Base *base, const int flag) { BLI_assert((v3d == NULL) || (v3d->spacetype == SPACE_VIEW3D)); /* Any flag satisfies the condition. */ if (flag == ~0) { return (base->flag != 0); } /* Flags may be more than one flag, so we can't check != 0. */ return BASE_VISIBLE(v3d, base) && ((base->flag & flag) == flag); } static void object_bases_iterator_begin(BLI_Iterator *iter, void *data_in_v, const int flag) { ObjectsVisibleIteratorData *data_in = data_in_v; ViewLayer *view_layer = data_in->view_layer; const View3D *v3d = data_in->v3d; Base *base = BKE_view_layer_object_bases_get(view_layer)->first; /* when there are no objects */ if (base == NULL) { iter->data = NULL; iter->valid = false; return; } LayerObjectBaseIteratorData *data = MEM_callocN(sizeof(LayerObjectBaseIteratorData), __func__); iter->data = data; data->v3d = v3d; data->base = base; if (object_bases_iterator_is_valid(v3d, base, flag) == false) { object_bases_iterator_next(iter, flag); } else { iter->current = base; } } static void object_bases_iterator_next(BLI_Iterator *iter, const int flag) { LayerObjectBaseIteratorData *data = iter->data; Base *base = data->base->next; while (base) { if (object_bases_iterator_is_valid(data->v3d, base, flag)) { iter->current = base; data->base = base; return; } base = base->next; } iter->valid = false; } static void object_bases_iterator_end(BLI_Iterator *iter) { MEM_SAFE_FREE(iter->data); } static void objects_iterator_begin(BLI_Iterator *iter, void *data_in, const int flag) { object_bases_iterator_begin(iter, data_in, flag); if (iter->valid) { iter->current = ((Base *)iter->current)->object; } } static void objects_iterator_next(BLI_Iterator *iter, const int flag) { object_bases_iterator_next(iter, flag); if (iter->valid) { iter->current = ((Base *)iter->current)->object; } } static void objects_iterator_end(BLI_Iterator *iter) { object_bases_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_selected_objects_iterator * See: #FOREACH_SELECTED_OBJECT_BEGIN * \{ */ void BKE_view_layer_selected_objects_iterator_begin(BLI_Iterator *iter, void *data_in) { objects_iterator_begin( iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_objects_iterator_next(BLI_Iterator *iter) { objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_objects_iterator_end(BLI_Iterator *iter) { objects_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_visible_objects_iterator * \{ */ void BKE_view_layer_visible_objects_iterator_begin(BLI_Iterator *iter, void *data_in) { objects_iterator_begin(iter, data_in, 0); } void BKE_view_layer_visible_objects_iterator_next(BLI_Iterator *iter) { objects_iterator_next(iter, 0); } void BKE_view_layer_visible_objects_iterator_end(BLI_Iterator *iter) { objects_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_selected_editable_objects_iterator * \{ */ void BKE_view_layer_selected_editable_objects_iterator_begin(BLI_Iterator *iter, void *data_in) { objects_iterator_begin( iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); if (iter->valid) { if (BKE_object_is_libdata((Object *)iter->current) == false) { /* First object is valid (selectable and not libdata) -> all good. */ return; } /* Object is selectable but not editable -> search for another one. */ BKE_view_layer_selected_editable_objects_iterator_next(iter); } } void BKE_view_layer_selected_editable_objects_iterator_next(BLI_Iterator *iter) { /* Search while there are objects and the one we have is not editable (editable = not libdata). */ do { objects_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } while (iter->valid && BKE_object_is_libdata((Object *)iter->current) != false); } void BKE_view_layer_selected_editable_objects_iterator_end(BLI_Iterator *iter) { objects_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_selected_bases_iterator * \{ */ void BKE_view_layer_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in) { objects_iterator_begin( iter, data_in, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_bases_iterator_next(BLI_Iterator *iter) { object_bases_iterator_next(iter, BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_SELECTED); } void BKE_view_layer_selected_bases_iterator_end(BLI_Iterator *iter) { object_bases_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_visible_bases_iterator * \{ */ void BKE_view_layer_visible_bases_iterator_begin(BLI_Iterator *iter, void *data_in) { object_bases_iterator_begin(iter, data_in, 0); } void BKE_view_layer_visible_bases_iterator_next(BLI_Iterator *iter) { object_bases_iterator_next(iter, 0); } void BKE_view_layer_visible_bases_iterator_end(BLI_Iterator *iter) { object_bases_iterator_end(iter); } /** \} */ /* -------------------------------------------------------------------- */ /** \name BKE_view_layer_bases_in_mode_iterator * \{ */ static bool base_is_in_mode(struct ObjectsInModeIteratorData *data, Base *base) { return (base->object->type == data->object_type) && (base->object->mode & data->object_mode) != 0; } void BKE_view_layer_bases_in_mode_iterator_begin(BLI_Iterator *iter, void *data_in) { struct ObjectsInModeIteratorData *data = data_in; Base *base = data->base_active; /* In this case the result will always be empty, the caller must check for no mode. */ BLI_assert(data->object_mode != 0); /* when there are no objects */ if (base == NULL) { iter->valid = false; return; } iter->data = data_in; iter->current = base; /* default type is active object type */ if (data->object_type < 0) { data->object_type = base->object->type; } if (!(base_is_in_mode(data, base) && BKE_base_is_visible(data->v3d, base))) { BKE_view_layer_bases_in_mode_iterator_next(iter); } } void BKE_view_layer_bases_in_mode_iterator_next(BLI_Iterator *iter) { struct ObjectsInModeIteratorData *data = iter->data; Base *base = iter->current; if (base == data->base_active) { /* first step */ base = data->view_layer->object_bases.first; if ((base == data->base_active) && BKE_base_is_visible(data->v3d, base)) { base = base->next; } } else { base = base->next; } while (base) { if ((base != data->base_active) && base_is_in_mode(data, base) && BKE_base_is_visible(data->v3d, base)) { iter->current = base; return; } base = base->next; } iter->valid = false; } void BKE_view_layer_bases_in_mode_iterator_end(BLI_Iterator *UNUSED(iter)) { /* do nothing */ } /** \} */ /* -------------------------------------------------------------------- */ /** \name Evaluation * \{ */ void BKE_base_eval_flags(Base *base) { /* Apply collection flags. */ base->flag &= ~g_base_collection_flags; base->flag |= (base->flag_from_collection & g_base_collection_flags); /* Apply object restrictions. */ const int object_restrict = base->object->visibility_flag; if (object_restrict & OB_HIDE_VIEWPORT) { base->flag &= ~BASE_ENABLED_VIEWPORT; } if (object_restrict & OB_HIDE_RENDER) { base->flag &= ~BASE_ENABLED_RENDER; } if (object_restrict & OB_HIDE_SELECT) { base->flag &= ~BASE_SELECTABLE; } /* Apply viewport visibility by default. The dependency graph for render * can change these again, but for tools we always want the viewport * visibility to be in sync regardless if depsgraph was evaluated. */ if (!(base->flag & BASE_ENABLED_VIEWPORT) || (base->flag & BASE_HIDDEN)) { base->flag &= ~(BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT | BASE_ENABLED_AND_VISIBLE_IN_DEFAULT_VIEWPORT | BASE_SELECTABLE); } /* Deselect unselectable objects. */ if (!(base->flag & BASE_SELECTABLE)) { base->flag &= ~BASE_SELECTED; } } static void layer_eval_view_layer(struct Depsgraph *depsgraph, struct Scene *scene, ViewLayer *view_layer) { DEG_debug_print_eval(depsgraph, __func__, view_layer->name, view_layer); /* Create array of bases, for fast index-based lookup. */ BKE_view_layer_synced_ensure(scene, view_layer); const int num_object_bases = BLI_listbase_count(BKE_view_layer_object_bases_get(view_layer)); MEM_SAFE_FREE(view_layer->object_bases_array); view_layer->object_bases_array = MEM_malloc_arrayN( num_object_bases, sizeof(Base *), "view_layer->object_bases_array"); int base_index = 0; LISTBASE_FOREACH (Base *, base, BKE_view_layer_object_bases_get(view_layer)) { view_layer->object_bases_array[base_index++] = base; } } void BKE_layer_eval_view_layer_indexed(struct Depsgraph *depsgraph, struct Scene *scene, int view_layer_index) { BLI_assert(view_layer_index >= 0); ViewLayer *view_layer = BLI_findlink(&scene->view_layers, view_layer_index); BLI_assert(view_layer != NULL); layer_eval_view_layer(depsgraph, scene, view_layer); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Blend File I/O * \{ */ static void write_layer_collections(BlendWriter *writer, ListBase *lb) { LISTBASE_FOREACH (LayerCollection *, lc, lb) { BLO_write_struct(writer, LayerCollection, lc); write_layer_collections(writer, &lc->layer_collections); } } void BKE_view_layer_blend_write(BlendWriter *writer, const Scene *scene, ViewLayer *view_layer) { BKE_view_layer_synced_ensure(scene, view_layer); BLO_write_struct(writer, ViewLayer, view_layer); BLO_write_struct_list(writer, Base, BKE_view_layer_object_bases_get(view_layer)); if (view_layer->id_properties) { IDP_BlendWrite(writer, view_layer->id_properties); } LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { BLO_write_struct(writer, FreestyleModuleConfig, fmc); } LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { BLO_write_struct(writer, FreestyleLineSet, fls); } LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { BLO_write_struct(writer, ViewLayerAOV, aov); } LISTBASE_FOREACH (ViewLayerLightgroup *, lightgroup, &view_layer->lightgroups) { BLO_write_struct(writer, ViewLayerLightgroup, lightgroup); } write_layer_collections(writer, &view_layer->layer_collections); } static void direct_link_layer_collections(BlendDataReader *reader, ListBase *lb, bool master) { BLO_read_list(reader, lb); LISTBASE_FOREACH (LayerCollection *, lc, lb) { #ifdef USE_COLLECTION_COMPAT_28 BLO_read_data_address(reader, &lc->scene_collection); #endif /* Master collection is not a real data-block. */ if (master) { BLO_read_data_address(reader, &lc->collection); } direct_link_layer_collections(reader, &lc->layer_collections, false); } } void BKE_view_layer_blend_read_data(BlendDataReader *reader, ViewLayer *view_layer) { view_layer->stats = NULL; BLO_read_list(reader, &view_layer->object_bases); BLO_read_data_address(reader, &view_layer->basact); direct_link_layer_collections(reader, &view_layer->layer_collections, true); BLO_read_data_address(reader, &view_layer->active_collection); BLO_read_data_address(reader, &view_layer->id_properties); IDP_BlendDataRead(reader, &view_layer->id_properties); BLO_read_list(reader, &(view_layer->freestyle_config.modules)); BLO_read_list(reader, &(view_layer->freestyle_config.linesets)); BLO_read_list(reader, &view_layer->aovs); BLO_read_data_address(reader, &view_layer->active_aov); BLO_read_list(reader, &view_layer->lightgroups); BLO_read_data_address(reader, &view_layer->active_lightgroup); BLI_listbase_clear(&view_layer->drawdata); view_layer->object_bases_array = NULL; view_layer->object_bases_hash = NULL; } static void lib_link_layer_collection(BlendLibReader *reader, Library *lib, LayerCollection *layer_collection, bool master) { /* Master collection is not a real data-block. */ if (!master) { BLO_read_id_address(reader, lib, &layer_collection->collection); } LISTBASE_FOREACH ( LayerCollection *, layer_collection_nested, &layer_collection->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection_nested, false); } } void BKE_view_layer_blend_read_lib(BlendLibReader *reader, Library *lib, ViewLayer *view_layer) { LISTBASE_FOREACH (FreestyleModuleConfig *, fmc, &view_layer->freestyle_config.modules) { BLO_read_id_address(reader, lib, &fmc->script); } LISTBASE_FOREACH (FreestyleLineSet *, fls, &view_layer->freestyle_config.linesets) { BLO_read_id_address(reader, lib, &fls->linestyle); BLO_read_id_address(reader, lib, &fls->group); } LISTBASE_FOREACH_MUTABLE (Base *, base, &view_layer->object_bases) { /* we only bump the use count for the collection objects */ BLO_read_id_address(reader, lib, &base->object); if (base->object == NULL) { /* Free in case linked object got lost. */ BLI_freelinkN(&view_layer->object_bases, base); if (view_layer->basact == base) { view_layer->basact = NULL; } } } LISTBASE_FOREACH (LayerCollection *, layer_collection, &view_layer->layer_collections) { lib_link_layer_collection(reader, lib, layer_collection, true); } BLO_read_id_address(reader, lib, &view_layer->mat_override); IDP_BlendReadLib(reader, lib, view_layer->id_properties); } /** \} */ /* -------------------------------------------------------------------- */ /** \name Shader AOV * \{ */ static void viewlayer_aov_make_name_unique(ViewLayer *view_layer) { ViewLayerAOV *aov = view_layer->active_aov; if (aov == NULL) { return; } /* Don't allow dots, it's incompatible with OpenEXR convention to store channels * as "layer.pass.channel". */ BLI_str_replace_char(aov->name, '.', '_'); BLI_uniquename( &view_layer->aovs, aov, DATA_("AOV"), '_', offsetof(ViewLayerAOV, name), sizeof(aov->name)); } static void viewlayer_aov_active_set(ViewLayer *view_layer, ViewLayerAOV *aov) { if (aov != NULL) { BLI_assert(BLI_findindex(&view_layer->aovs, aov) != -1); view_layer->active_aov = aov; } else { view_layer->active_aov = NULL; } } struct ViewLayerAOV *BKE_view_layer_add_aov(struct ViewLayer *view_layer) { ViewLayerAOV *aov; aov = MEM_callocN(sizeof(ViewLayerAOV), __func__); aov->type = AOV_TYPE_COLOR; BLI_strncpy(aov->name, DATA_("AOV"), sizeof(aov->name)); BLI_addtail(&view_layer->aovs, aov); viewlayer_aov_active_set(view_layer, aov); viewlayer_aov_make_name_unique(view_layer); return aov; } void BKE_view_layer_remove_aov(ViewLayer *view_layer, ViewLayerAOV *aov) { BLI_assert(BLI_findindex(&view_layer->aovs, aov) != -1); BLI_assert(aov != NULL); if (view_layer->active_aov == aov) { if (aov->next) { viewlayer_aov_active_set(view_layer, aov->next); } else { viewlayer_aov_active_set(view_layer, aov->prev); } } BLI_freelinkN(&view_layer->aovs, aov); } void BKE_view_layer_set_active_aov(ViewLayer *view_layer, ViewLayerAOV *aov) { viewlayer_aov_active_set(view_layer, aov); } static void bke_view_layer_verify_aov_cb(void *userdata, Scene *UNUSED(scene), ViewLayer *UNUSED(view_layer), const char *name, int UNUSED(channels), const char *UNUSED(chanid), eNodeSocketDatatype UNUSED(type)) { GHash *name_count = userdata; void **value_p; void *key = BLI_strdup(name); if (!BLI_ghash_ensure_p(name_count, key, &value_p)) { *value_p = POINTER_FROM_INT(1); } else { int value = POINTER_AS_INT(*value_p); value++; *value_p = POINTER_FROM_INT(value); MEM_freeN(key); } } void BKE_view_layer_verify_aov(struct RenderEngine *engine, struct Scene *scene, struct ViewLayer *view_layer) { viewlayer_aov_make_name_unique(view_layer); GHash *name_count = BLI_ghash_str_new(__func__); RE_engine_update_render_passes( engine, scene, view_layer, bke_view_layer_verify_aov_cb, name_count); LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { void **value_p = BLI_ghash_lookup(name_count, aov->name); int count = POINTER_AS_INT(value_p); SET_FLAG_FROM_TEST(aov->flag, count > 1, AOV_CONFLICT); } BLI_ghash_free(name_count, MEM_freeN, NULL); } bool BKE_view_layer_has_valid_aov(ViewLayer *view_layer) { LISTBASE_FOREACH (ViewLayerAOV *, aov, &view_layer->aovs) { if ((aov->flag & AOV_CONFLICT) == 0) { return true; } } return false; } ViewLayer *BKE_view_layer_find_with_aov(struct Scene *scene, struct ViewLayerAOV *aov) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (BLI_findindex(&view_layer->aovs, aov) != -1) { return view_layer; } } return NULL; } /** \} */ /* -------------------------------------------------------------------- */ /** \name Light Groups * \{ */ static void viewlayer_lightgroup_make_name_unique(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup) { /* Don't allow dots, it's incompatible with OpenEXR convention to store channels * as "layer.pass.channel". */ BLI_str_replace_char(lightgroup->name, '.', '_'); BLI_uniquename(&view_layer->lightgroups, lightgroup, DATA_("Lightgroup"), '_', offsetof(ViewLayerLightgroup, name), sizeof(lightgroup->name)); } static void viewlayer_lightgroup_active_set(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup) { if (lightgroup != NULL) { BLI_assert(BLI_findindex(&view_layer->lightgroups, lightgroup) != -1); view_layer->active_lightgroup = lightgroup; } else { view_layer->active_lightgroup = NULL; } } struct ViewLayerLightgroup *BKE_view_layer_add_lightgroup(struct ViewLayer *view_layer, const char *name) { ViewLayerLightgroup *lightgroup; lightgroup = MEM_callocN(sizeof(ViewLayerLightgroup), __func__); if (name && name[0]) { BLI_strncpy(lightgroup->name, name, sizeof(lightgroup->name)); } else { BLI_strncpy(lightgroup->name, DATA_("Lightgroup"), sizeof(lightgroup->name)); } BLI_addtail(&view_layer->lightgroups, lightgroup); viewlayer_lightgroup_active_set(view_layer, lightgroup); viewlayer_lightgroup_make_name_unique(view_layer, lightgroup); return lightgroup; } void BKE_view_layer_remove_lightgroup(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup) { BLI_assert(BLI_findindex(&view_layer->lightgroups, lightgroup) != -1); BLI_assert(lightgroup != NULL); if (view_layer->active_lightgroup == lightgroup) { if (lightgroup->next) { viewlayer_lightgroup_active_set(view_layer, lightgroup->next); } else { viewlayer_lightgroup_active_set(view_layer, lightgroup->prev); } } BLI_freelinkN(&view_layer->lightgroups, lightgroup); } void BKE_view_layer_set_active_lightgroup(ViewLayer *view_layer, ViewLayerLightgroup *lightgroup) { viewlayer_lightgroup_active_set(view_layer, lightgroup); } ViewLayer *BKE_view_layer_find_with_lightgroup(struct Scene *scene, struct ViewLayerLightgroup *lightgroup) { LISTBASE_FOREACH (ViewLayer *, view_layer, &scene->view_layers) { if (BLI_findindex(&view_layer->lightgroups, lightgroup) != -1) { return view_layer; } } return NULL; } void BKE_view_layer_rename_lightgroup(Scene *scene, ViewLayer *view_layer, ViewLayerLightgroup *lightgroup, const char *name) { char old_name[64]; BLI_strncpy_utf8(old_name, lightgroup->name, sizeof(old_name)); BLI_strncpy_utf8(lightgroup->name, name, sizeof(lightgroup->name)); viewlayer_lightgroup_make_name_unique(view_layer, lightgroup); if (scene != NULL) { /* Update objects in the scene to refer to the new name instead. */ FOREACH_SCENE_OBJECT_BEGIN (scene, ob) { if (!ID_IS_LINKED(ob) && ob->lightgroup != NULL) { LightgroupMembership *lgm = ob->lightgroup; if (STREQ(lgm->name, old_name)) { BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name)); } } } FOREACH_SCENE_OBJECT_END; /* Update the scene's world to refer to the new name instead. */ if (scene->world != NULL && !ID_IS_LINKED(scene->world) && scene->world->lightgroup != NULL) { LightgroupMembership *lgm = scene->world->lightgroup; if (STREQ(lgm->name, old_name)) { BLI_strncpy_utf8(lgm->name, lightgroup->name, sizeof(lgm->name)); } } } } void BKE_lightgroup_membership_get(struct LightgroupMembership *lgm, char *name) { if (lgm != NULL) { BLI_strncpy(name, lgm->name, sizeof(lgm->name)); } else { name[0] = '\0'; } } int BKE_lightgroup_membership_length(struct LightgroupMembership *lgm) { if (lgm != NULL) { return strlen(lgm->name); } return 0; } void BKE_lightgroup_membership_set(struct LightgroupMembership **lgm, const char *name) { if (name[0] != '\0') { if (*lgm == NULL) { *lgm = MEM_callocN(sizeof(LightgroupMembership), __func__); } BLI_strncpy((*lgm)->name, name, sizeof((*lgm)->name)); } else { if (*lgm != NULL) { MEM_freeN(*lgm); *lgm = NULL; } } } /** \} */