diff options
Diffstat (limited to 'source/blender/blenkernel/intern/collection.c')
-rw-r--r-- | source/blender/blenkernel/intern/collection.c | 1275 |
1 files changed, 733 insertions, 542 deletions
diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 4303f7d9150..5ba4a1196b8 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -31,15 +31,17 @@ #include "BLI_iterator.h" #include "BLI_listbase.h" #include "BLI_math_base.h" +#include "BLI_threads.h" #include "BLT_translation.h" #include "BLI_string_utils.h" #include "BKE_collection.h" -#include "BKE_group.h" +#include "BKE_icons.h" #include "BKE_idprop.h" #include "BKE_layer.h" #include "BKE_library.h" #include "BKE_main.h" +#include "BKE_object.h" #include "BKE_scene.h" #include "DNA_group_types.h" @@ -48,412 +50,573 @@ #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + #include "MEM_guardedalloc.h" -/* Prototypes. */ -static SceneCollection *find_collection_parent(const struct SceneCollection *sc_child, struct SceneCollection *sc_parent); -static bool is_collection_in_tree(const struct SceneCollection *sc_reference, struct SceneCollection *sc_parent); +/******************************** Prototypes ********************************/ -static SceneCollection *collection_master_from_id(const ID *owner_id) -{ - switch (GS(owner_id->name)) { - case ID_SCE: - return ((Scene *)owner_id)->collection; - case ID_GR: - return ((Group *)owner_id)->collection; - default: - BLI_assert(!"ID doesn't support collections"); - return NULL; - } -} +static bool collection_child_add(Collection *parent, Collection *collection, int flag, const bool add_us); +static bool collection_child_remove(Collection *parent, Collection *collection); +static bool collection_object_add(Collection *collection, Object *ob, int flag, const bool add_us); +static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us); -/** - * The automatic/fallback name of a new collection. - */ -void BKE_collection_new_name_get(ID *owner_id, SceneCollection *sc_parent, char *rname) -{ - SceneCollection *sc_master = collection_master_from_id(owner_id); - char *name; +static CollectionChild *collection_find_child(Collection *parent, Collection *collection); +static CollectionParent *collection_find_parent(Collection *child, Collection *collection); - if (sc_parent == sc_master) { - name = BLI_sprintfN("Collection %d", BLI_listbase_count(&sc_master->scene_collections) + 1); - } - else { - const int number = BLI_listbase_count(&sc_parent->scene_collections) + 1; - const int digits = integer_digits_i(number); - const int max_len = sizeof(sc_parent->name) - 1 /* NULL terminator */ - (1 + digits) /* " %d" */; - name = BLI_sprintfN("%.*s %d", max_len, sc_parent->name, number); - } +static bool collection_find_child_recursive(Collection *parent, Collection *collection); - BLI_strncpy(rname, name, MAX_NAME); - MEM_freeN(name); -} +/***************************** Add Collection *******************************/ -/** - * Add a new collection, but don't handle syncing with layer collections - */ -static SceneCollection *collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom) +/* Add new collection, without view layer syncing. */ +static Collection *collection_add(Main *bmain, Collection *collection_parent, const char *name_custom) { - SceneCollection *sc_master = collection_master_from_id(owner_id); - SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection"); - sc->type = type; + /* Determine new collection name. */ char name[MAX_NAME]; - if (!sc_parent) { - sc_parent = sc_master; - } - - if (name_custom != NULL) { - BLI_strncpy(name, name_custom, MAX_NAME); + if (name_custom) { + STRNCPY(name, name_custom); } else { - BKE_collection_new_name_get(owner_id, sc_parent, name); + BKE_collection_new_name_get(collection_parent, name); } - BLI_addtail(&sc_parent->scene_collections, sc); - BKE_collection_rename(owner_id, sc, name); + /* Create new collection. */ + Collection *collection = BKE_libblock_alloc(bmain, ID_GR, name, 0); + + /* We increase collection user count when linking to Collections. */ + id_us_min(&collection->id); + + /* Optionally add to parent collection. */ + if (collection_parent) { + collection_child_add(collection_parent, collection, 0, true); + } - return sc; + return collection; } /** * Add a collection to a collection ListBase and syncronize all render layers * The ListBase is NULL when the collection is to be added to the master collection */ -SceneCollection *BKE_collection_add(ID *owner_id, SceneCollection *sc_parent, const int type, const char *name_custom) +Collection *BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom) { - if (sc_parent == NULL) { - sc_parent = BKE_collection_master(owner_id); - } + Collection *collection = collection_add(bmain, collection_parent, name_custom); + BKE_main_collection_sync(bmain); + return collection; +} - SceneCollection *scene_collection = collection_add(owner_id, sc_parent, type, name_custom); - BKE_layer_sync_new_scene_collection(owner_id, sc_parent, scene_collection); - return scene_collection; +/*********************** Free and Delete Collection ****************************/ + +/** Free (or release) any data used by this collection (does not free the collection itself). */ +void BKE_collection_free(Collection *collection) +{ + /* No animdata here. */ + BKE_previewimg_free(&collection->preview); + + BLI_freelistN(&collection->gobject); + BLI_freelistN(&collection->children); + BLI_freelistN(&collection->parents); + + BKE_collection_object_cache_free(collection); } /** - * Free the collection items recursively + * Remove a collection, optionally removing its child objects or moving + * them to parent collections. */ -static void collection_free(SceneCollection *sc, const bool do_id_user) +bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy) { - if (do_id_user) { - for (LinkData *link = sc->objects.first; link; link = link->next) { - id_us_min(link->data); + /* Master collection is not real datablock, can't be removed. */ + if (collection->flag & COLLECTION_IS_MASTER) { + BLI_assert("!Scene master collection can't be deleted"); + return false; + } + + if (hierarchy) { + /* Remove child objects. */ + CollectionObject *cob = collection->gobject.first; + while (cob != NULL) { + collection_object_remove(bmain, collection, cob->ob, true); + cob = collection->gobject.first; + } + + /* Delete all child collections recursively. */ + CollectionChild *child = collection->children.first; + while (child != NULL) { + BKE_collection_delete(bmain, child->collection, hierarchy); + child = collection->children.first; } } + else { + /* Link child collections into parent collection. */ + for (CollectionChild *child = collection->children.first; child; child = child->next) { + for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) { + Collection *parent = cparent->collection; + collection_child_add(parent, child->collection, 0, true); + } + } - BLI_freelistN(&sc->objects); + CollectionObject *cob = collection->gobject.first; + while (cob != NULL) { + /* Link child object into parent collections. */ + for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) { + Collection *parent = cparent->collection; + collection_object_add(parent, cob->ob, 0, true); + } - for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) { - collection_free(nsc, do_id_user); + /* Remove child object. */ + collection_object_remove(bmain, collection, cob->ob, true); + cob = collection->gobject.first; + } } - BLI_freelistN(&sc->scene_collections); + + BKE_libblock_delete(bmain, collection); + + BKE_main_collection_sync(bmain); + + return true; } +/***************************** Collection Copy *******************************/ + /** - * Unlink the collection recursively - * \return true if unlinked. + * Only copy internal data of Collection ID from source to already allocated/initialized destination. + * You probably nerver want to use that directly, use id_copy or BKE_id_copy_ex for typical needs. + * + * WARNING! This function will not handle ID user count! + * + * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). */ -static bool collection_remlink(SceneCollection *sc_parent, SceneCollection *sc_gone) +void BKE_collection_copy_data(Main *UNUSED(bmain), Collection *collection_dst, const Collection *collection_src, const int flag) { - for (SceneCollection *sc = sc_parent->scene_collections.first; sc; sc = sc->next) { - if (sc == sc_gone) { - BLI_remlink(&sc_parent->scene_collections, sc_gone); - return true; - } + /* Do not copy collection's preview (same behavior as for objects). */ + if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0 && false) { /* XXX TODO temp hack */ + BKE_previewimg_id_copy(&collection_dst->id, &collection_src->id); + } + else { + collection_dst->preview = NULL; + } - if (collection_remlink(sc, sc_gone)) { - return true; - } + collection_dst->flag &= ~COLLECTION_HAS_OBJECT_CACHE; + BLI_listbase_clear(&collection_dst->object_cache); + + BLI_listbase_clear(&collection_dst->gobject); + BLI_listbase_clear(&collection_dst->children); + BLI_listbase_clear(&collection_dst->parents); + + for (CollectionChild *child = collection_src->children.first; child; child = child->next) { + collection_child_add(collection_dst, child->collection, flag, false); + } + for (CollectionObject *cob = collection_src->gobject.first; cob; cob = cob->next) { + collection_object_add(collection_dst, cob->ob, flag, false); } - return false; } /** - * Recursively remove any instance of this SceneCollection + * Makes a shallow copy of a Collection + * + * Add a new collection in the same level as the old one, copy any nested collections + * but link the objects to the new collection (as oppose to copy them). */ -static void layer_collection_remove(ViewLayer *view_layer, ListBase *lb, const SceneCollection *sc) -{ - LayerCollection *lc = lb->first; - while (lc) { - if (lc->scene_collection == sc) { - BKE_layer_collection_free(view_layer, lc); - BLI_remlink(lb, lc); - - LayerCollection *lc_next = lc->next; - MEM_freeN(lc); - lc = lc_next; - - /* only the "top-level" layer collections may have the - * same SceneCollection in a sibling tree. - */ - if (lb != &view_layer->layer_collections) { - return; - } - } +Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection) +{ + /* It's not allowed to copy the master collection. */ + if (collection->flag & COLLECTION_IS_MASTER) { + BLI_assert("!Master collection can't be copied"); + return NULL; + } - else { - layer_collection_remove(view_layer, &lc->layer_collections, sc); - lc = lc->next; + Collection *collection_new; + BKE_id_copy_ex(bmain, &collection->id, (ID **)&collection_new, 0, false); + + /* Optionally add to parent. */ + if (parent) { + if (collection_child_add(parent, collection_new, 0, true)) { + /* Put collection right after existing one. */ + CollectionChild *child = collection_find_child(parent, collection); + CollectionChild *child_new = collection_find_child(parent, collection_new); + + if (child && child_new) { + BLI_remlink(&parent->children, child_new); + BLI_insertlinkafter(&parent->children, child, child_new); + } } } + + BKE_main_collection_sync(bmain); + + return collection_new; } +Collection *BKE_collection_copy_master(Main *bmain, Collection *collection, const int flag) +{ + BLI_assert(collection->flag & COLLECTION_IS_MASTER); + + Collection *collection_dst = MEM_dupallocN(collection); + BKE_collection_copy_data(bmain, collection_dst, collection, flag); + return collection_dst; +} + +void BKE_collection_copy_full(Main *UNUSED(bmain), Collection *UNUSED(collection)) +{ + // TODO: implement full scene copy +} + +void BKE_collection_make_local(Main *bmain, Collection *collection, const bool lib_local) +{ + BKE_id_make_local_generic(bmain, &collection->id, true, lib_local); +} + +/********************************* Naming *******************************/ + /** - * Remove a collection from the scene, and syncronize all render layers - * - * If an object is in any other collection, link the object to the master collection. + * The automatic/fallback name of a new collection. */ -bool BKE_collection_remove(ID *owner_id, SceneCollection *sc) +void BKE_collection_new_name_get(Collection *collection_parent, char *rname) { - SceneCollection *sc_master = collection_master_from_id(owner_id); + char *name; - /* The master collection cannot be removed. */ - if (sc == sc_master) { - return false; + if (!collection_parent) { + name = BLI_sprintfN("Collection"); } - - /* We need to do bottom up removal, otherwise we get a crash when we remove a collection that - * has one of its nested collections linked to a view layer. */ - SceneCollection *scene_collection_nested = sc->scene_collections.first; - while (scene_collection_nested != NULL) { - SceneCollection *scene_collection_next = scene_collection_nested->next; - BKE_collection_remove(owner_id, scene_collection_nested); - scene_collection_nested = scene_collection_next; + else if (collection_parent->flag & COLLECTION_IS_MASTER) { + name = BLI_sprintfN("Collection %d", BLI_listbase_count(&collection_parent->children) + 1); } - - /* Unlink from the respective collection tree. */ - if (!collection_remlink(sc_master, sc)) { - BLI_assert(false); + else { + const int number = BLI_listbase_count(&collection_parent->children) + 1; + const int digits = integer_digits_i(number); + const int max_len = sizeof(collection_parent->id.name) - 1 /* NULL terminator */ - (1 + digits) /* " %d" */ - 2 /* ID */; + name = BLI_sprintfN("%.*s %d", max_len, collection_parent->id.name + 2, number); } - /* If an object is no longer in any collection, we add it to the master collection. */ - ListBase collection_objects; - BLI_duplicatelist(&collection_objects, &sc->objects); + BLI_strncpy(rname, name, MAX_NAME); + MEM_freeN(name); +} - FOREACH_SCENE_COLLECTION_BEGIN(owner_id, scene_collection_iter) +/************************* Dependencies ****************************/ + +bool BKE_collection_is_animated(Collection *collection, Object *UNUSED(parent)) +{ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(collection, object) { - if (scene_collection_iter == sc) { - continue; + if (object->proxy) { + return true; } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + return false; +} - LinkData *link_next, *link = collection_objects.first; - while (link) { - link_next = link->next; +/* puts all collection members in local timing system, after this call + * you can draw everything, leaves tags in objects to signal it needs further updating */ - if (BLI_findptr(&scene_collection_iter->objects, link->data, offsetof(LinkData, data))) { - BLI_remlink(&collection_objects, link); - MEM_freeN(link); - } - - link = link_next; +/* note: does not work for derivedmesh and render... it recreates all again in convertblender.c */ +void BKE_collection_handle_recalc_and_update(struct Depsgraph *depsgraph, Scene *scene, Object *UNUSED(parent), Collection *collection) +{ + /* only do existing tags, as set by regular depsgraph */ + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(collection, object) + { + if (object->id.recalc & ID_RECALC_ALL) { + BKE_object_handle_update(depsgraph, scene, object); } } - FOREACH_SCENE_COLLECTION_END; - - for (LinkData *link = collection_objects.first; link; link = link->next) { - BKE_collection_object_add(owner_id, sc_master, link->data); + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; } - BLI_freelistN(&collection_objects); +/* **************** Object List Cache *******************/ + +static void collection_object_cache_fill(ListBase *lb, Collection *collection, int parent_restrict) +{ + int child_restrict = collection->flag | parent_restrict; + + for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + Base *base = BLI_findptr(lb, cob->ob, offsetof(Base, object)); + + if (base == NULL) { + base = MEM_callocN(sizeof(Base), "Object Base"); + base->object = cob->ob; - /* Clear the collection items. */ - collection_free(sc, true); + if ((child_restrict & COLLECTION_RESTRICT_VIEW) == 0) { + base->flag |= BASE_VISIBLED | BASE_VISIBLE_VIEWPORT; - /* check all layers that use this collection and clear them */ - for (ViewLayer *view_layer = BKE_view_layer_first_from_id(owner_id); view_layer; view_layer = view_layer->next) { - layer_collection_remove(view_layer, &view_layer->layer_collections, sc); - view_layer->active_collection = 0; + if ((child_restrict & COLLECTION_RESTRICT_SELECT) == 0) { + base->flag |= BASE_SELECTABLED; + } + } + + if ((child_restrict & COLLECTION_RESTRICT_RENDER) == 0) { + base->flag |= BASE_VISIBLE_RENDER; + } + + BLI_addtail(lb, base); + } } - MEM_freeN(sc); - return true; + for (CollectionChild *child = collection->children.first; child; child = child->next) { + collection_object_cache_fill(lb, child->collection, child_restrict); + } } -/** - * Copy SceneCollection tree but keep pointing to the same objects - * - * \param flag Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more). - */ -void BKE_collection_copy_data(SceneCollection *sc_dst, SceneCollection *sc_src, const int flag) +ListBase BKE_collection_object_cache_get(Collection *collection) { - BLI_duplicatelist(&sc_dst->objects, &sc_src->objects); - if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - for (LinkData *link = sc_dst->objects.first; link; link = link->next) { - id_us_plus(link->data); + if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) { + static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER; + + if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) { + BLI_mutex_lock(&cache_lock); + collection_object_cache_fill(&collection->object_cache, collection, 0); + collection->flag |= COLLECTION_HAS_OBJECT_CACHE; + BLI_mutex_unlock(&cache_lock); } } - BLI_duplicatelist(&sc_dst->scene_collections, &sc_src->scene_collections); - for (SceneCollection *nsc_src = sc_src->scene_collections.first, *nsc_dst = sc_dst->scene_collections.first; - nsc_src; - nsc_src = nsc_src->next, nsc_dst = nsc_dst->next) - { - BKE_collection_copy_data(nsc_dst, nsc_src, flag); - } + return collection->object_cache; } -/** - * Makes a shallow copy of a SceneCollection - * - * Add a new collection in the same level as the old one, copy any nested collections - * but link the objects to the new collection (as oppose to copy them). - */ -SceneCollection *BKE_collection_duplicate(ID *owner_id, SceneCollection *scene_collection) +static void collection_object_cache_free(Collection *collection) { - SceneCollection *scene_collection_master = BKE_collection_master(owner_id); - SceneCollection *scene_collection_parent = find_collection_parent(scene_collection, scene_collection_master); + /* Clear own cache an for all parents, since those are affected by changes as well. */ + collection->flag &= ~COLLECTION_HAS_OBJECT_CACHE; + BLI_freelistN(&collection->object_cache); - /* It's not allowed to copy the master collection. */ - if (scene_collection_master == scene_collection) { - return NULL; + for (CollectionParent *parent = collection->parents.first; parent; parent = parent->next) { + collection_object_cache_free(parent->collection); } +} + +void BKE_collection_object_cache_free(Collection *collection) +{ + collection_object_cache_free(collection); +} + +Base *BKE_collection_or_layer_objects(Depsgraph *depsgraph, + const Scene *scene, + const ViewLayer *view_layer, + Collection *collection) +{ + // TODO: this is used by physics to get objects from a collection, but the + // the physics systems are not all using the depsgraph correctly which means + // we try different things. Instead we should explicitly get evaluated or + // non-evaluated data and always have the depsgraph available when needed - SceneCollection *scene_collection_new = collection_add( - owner_id, - scene_collection_parent, - scene_collection->type, - scene_collection->name); + if (collection) { + return BKE_collection_object_cache_get(collection).first; + } + else if (depsgraph) { + view_layer = DEG_get_evaluated_view_layer(depsgraph); - if (scene_collection_new != scene_collection->next) { - BLI_remlink(&scene_collection_parent->scene_collections, scene_collection_new); - BLI_insertlinkafter(&scene_collection_parent->scene_collections, scene_collection, scene_collection_new); + if (view_layer) { + return FIRSTBASE(view_layer); + } + else { + view_layer = DEG_get_input_view_layer(depsgraph); + return FIRSTBASE(view_layer); + } + } + else if (view_layer) { + return FIRSTBASE(view_layer); + } + else { + /* depsgraph is NULL during deg build */ + return FIRSTBASE(BKE_view_layer_context_active_PLACEHOLDER(scene)); } +} - BKE_collection_copy_data(scene_collection_new, scene_collection, 0); - BKE_layer_sync_new_scene_collection(owner_id, scene_collection_parent, scene_collection_new); +/*********************** Scene Master Collection ***************/ - /* Make sure every linked instance of the new collection has the same values (flags, overrides, ...) as the - * corresponding original collection. */ - BKE_layer_collection_sync_flags(owner_id, scene_collection_new, scene_collection); +Collection *BKE_collection_master_add() +{ + /* Not an actual datablock, but owned by scene. */ + Collection *master_collection = MEM_callocN(sizeof(Collection), "Master Collection"); + STRNCPY(master_collection->id.name, "GRMaster Collection"); + master_collection->flag |= COLLECTION_IS_MASTER; + return master_collection; +} - return scene_collection_new; +Collection *BKE_collection_master(const Scene *scene) +{ + return scene->master_collection; } -static SceneCollection *master_collection_from_id(const ID *owner_id) +/*********************** Cyclic Checks ************************/ + +static bool collection_object_cyclic_check_internal(Object *object, Collection *collection) { - switch (GS(owner_id->name)) { - case ID_SCE: - return ((const Scene *)owner_id)->collection; - case ID_GR: - return ((const Group *)owner_id)->collection; - default: - BLI_assert(!"ID doesn't support scene collection"); - return NULL; + if (object->dup_group) { + Collection *dup_collection = object->dup_group; + if ((dup_collection->id.tag & LIB_TAG_DOIT) == 0) { + /* Cycle already exists in collections, let's prevent further crappyness */ + return true; + } + /* flag the object to identify cyclic dependencies in further dupli collections */ + dup_collection->id.tag &= ~LIB_TAG_DOIT; + + if (dup_collection == collection) { + return true; + } + else { + FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN(dup_collection, collection_object) + { + if (collection_object_cyclic_check_internal(collection_object, dup_collection)) { + return true; + } + } + FOREACH_COLLECTION_OBJECT_RECURSIVE_END; + } + + /* un-flag the object, it's allowed to have the same collection multiple times in parallel */ + dup_collection->id.tag |= LIB_TAG_DOIT; } + + return false; } -/** - * Returns the master collection of the scene or group - */ -SceneCollection *BKE_collection_master(const ID *owner_id) +bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection *collection) { - return master_collection_from_id(owner_id); + /* first flag all collections */ + BKE_main_id_tag_listbase(&bmain->collection, LIB_TAG_DOIT, true); + + return collection_object_cyclic_check_internal(object, collection); } -static void collection_rename(const ID *owner_id, SceneCollection *sc, const char *name) +/******************* Collection Object Membership *******************/ + +bool BKE_collection_has_object(Collection *collection, Object *ob) { - SceneCollection *sc_parent = find_collection_parent(sc, collection_master_from_id(owner_id)); - BLI_strncpy(sc->name, name, sizeof(sc->name)); - BLI_uniquename(&sc_parent->scene_collections, sc, DATA_("Collection"), '.', offsetof(SceneCollection, name), sizeof(sc->name)); + if (ELEM(NULL, collection, ob)) { + return false; + } + + return (BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob))); } -void BKE_collection_rename(const ID *owner_id, SceneCollection *sc, const char *name) +bool BKE_collection_has_object_recursive(Collection *collection, Object *ob) { - collection_rename(owner_id, sc, name); + if (ELEM(NULL, collection, ob)) { + return false; + } + + const ListBase objects = BKE_collection_object_cache_get(collection); + return (BLI_findptr(&objects, ob, offsetof(Base, object))); } -/** - * Make sure the collection name is still unique within its siblings. - */ -static void collection_name_check(const ID *owner_id, SceneCollection *sc) +Collection *BKE_collection_object_find(Main *bmain, Collection *collection, Object *ob) { - /* It's a bit of a hack, we simply try to make sure the collection name is valid. */ - collection_rename(owner_id, sc, sc->name); + if (collection) + collection = collection->id.next; + else + collection = bmain->collection.first; + + while (collection) { + if (BKE_collection_has_object(collection, ob)) + return collection; + collection = collection->id.next; + } + return NULL; } -/** - * Free (or release) any data used by the master collection (does not free the master collection itself). - * Used only to clear the entire scene or group data since it's not doing re-syncing of the LayerCollection tree - */ -void BKE_collection_master_free(ID *owner_id, const bool do_id_user) +/********************** Collection Objects *********************/ + +static bool collection_object_add(Collection *collection, Object *ob, int flag, const bool add_us) { - collection_free(BKE_collection_master(owner_id), do_id_user); + if (ob->dup_group) { + /* Cyclic dependency check. */ + if (collection_find_child_recursive(collection, ob->dup_group)) { + return false; + } + } + + CollectionObject *cob = BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob)); + if (cob) { + return false; + } + + cob = MEM_callocN(sizeof(CollectionObject), __func__); + cob->ob = ob; + BLI_addtail(&collection->gobject, cob); + BKE_collection_object_cache_free(collection); + + if (add_us && (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id_us_plus(&ob->id); + } + + return true; } -static void collection_object_add(const ID *owner_id, SceneCollection *sc, Object *ob) +static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us) { - BLI_addtail(&sc->objects, BLI_genericNodeN(ob)); + CollectionObject *cob = BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob)); + if (cob == NULL) { + return false; + } + + BLI_freelinkN(&collection->gobject, cob); + BKE_collection_object_cache_free(collection); - if (GS(owner_id->name) == ID_SCE) { - id_us_plus((ID *)ob); + if (free_us) { + BKE_libblock_free_us(bmain, ob); } else { - BLI_assert(GS(owner_id->name) == ID_GR); - if ((ob->flag & OB_FROMGROUP) == 0) { - ob->flag |= OB_FROMGROUP; - } + id_us_min(&ob->id); } - BKE_layer_sync_object_link(owner_id, sc, ob); + return true; } /** * Add object to collection */ -bool BKE_collection_object_add(const ID *owner_id, SceneCollection *sc, Object *ob) +bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) { - if (BKE_collection_object_exists(sc, ob)) { - /* don't add the same object twice */ + if (ELEM(NULL, collection, ob)) { return false; } - collection_object_add(owner_id, sc, ob); + if (!collection_object_add(collection, ob, 0, true)) { + return false; + } + + if (BKE_collection_is_in_scene(collection)) { + BKE_main_collection_sync(bmain); + } + return true; } /** - * Add object to all collections that reference objects is in + * Add object to all scene collections that reference objects is in * (used to copy objects) */ -void BKE_collection_object_add_from(Scene *scene, Object *ob_src, Object *ob_dst) +void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst) { - FOREACH_SCENE_COLLECTION_BEGIN(scene, sc) + FOREACH_SCENE_COLLECTION_BEGIN(scene, collection) { - if (BLI_findptr(&sc->objects, ob_src, offsetof(LinkData, data))) { - collection_object_add(&scene->id, sc, ob_dst); + if (BKE_collection_has_object(collection, ob_src)) { + collection_object_add(collection, ob_dst, 0, true); } } FOREACH_SCENE_COLLECTION_END; + + BKE_main_collection_sync(bmain); } /** * Remove object from collection. - * \param bmain: Can be NULL if free_us is false. */ -bool BKE_collection_object_remove(Main *bmain, ID *owner_id, SceneCollection *sc, Object *ob, const bool free_us) +bool BKE_collection_object_remove(Main *bmain, Collection *collection, Object *ob, const bool free_us) { - LinkData *link = BLI_findptr(&sc->objects, ob, offsetof(LinkData, data)); - - if (link == NULL) { + if (ELEM(NULL, collection, ob)) { return false; } - BLI_remlink(&sc->objects, link); - MEM_freeN(link); - - BKE_layer_sync_object_unlink(owner_id, sc, ob); - - if (GS(owner_id->name) == ID_SCE) { - if (free_us) { - BKE_libblock_free_us(bmain, ob); - } - else { - id_us_min(&ob->id); - } + if (collection_object_remove(bmain, collection, ob, free_us)) { + return false; } - else { - BLI_assert(GS(owner_id->name) == ID_GR); + + if (BKE_collection_is_in_scene(collection)) { + BKE_main_collection_sync(bmain); } return true; @@ -463,408 +626,436 @@ bool BKE_collection_object_remove(Main *bmain, ID *owner_id, SceneCollection *sc * Remove object from all collections of scene * \param scene_collection_skip: Don't remove base from this collection. */ -static bool collections_object_remove_ex(Main *bmain, ID *owner_id, Object *ob, const bool free_us, - SceneCollection *scene_collection_skip) +static bool scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us, + Collection *collection_skip) { bool removed = false; - if (GS(owner_id->name) == ID_SCE) { - BKE_scene_remove_rigidbody_object((Scene *)owner_id, ob); - } - else { - BLI_assert(GS(owner_id->name) == ID_GR); - } - FOREACH_SCENE_COLLECTION_BEGIN(owner_id, sc) + BKE_scene_remove_rigidbody_object(scene, ob); + + FOREACH_SCENE_COLLECTION_BEGIN(scene, collection) { - if (sc != scene_collection_skip) { - removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us); + if (collection != collection_skip) { + removed |= collection_object_remove(bmain, collection, ob, free_us); } } FOREACH_SCENE_COLLECTION_END; + + BKE_main_collection_sync(bmain); + return removed; } /** * Remove object from all collections of scene */ -bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us) +bool BKE_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us) { - return collections_object_remove_ex(bmain, owner_id, ob, free_us, NULL); + return scene_collections_object_remove(bmain, scene, ob, free_us, NULL); } -/** - * Move object from a collection into another - * - * If source collection is NULL move it from all the existing collections. +/* + * Remove all NULL objects from non-scene collections. + * This is used for library remapping, where these pointers have been set to NULL. + * Otherwise this should never happen. */ -void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob) +void BKE_collections_object_remove_nulls(Main *bmain) { - /* In both cases we first add the object, then remove it from the other collections. - * Otherwise we lose the original base and whether it was active and selected. */ - if (sc_src != NULL) { - if (BKE_collection_object_add(owner_id, sc_dst, ob)) { - BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false); + for (Collection *collection = bmain->collection.first; collection; collection = collection->id.next) { + if (!BKE_collection_is_in_scene(collection)) { + bool changed = false; + + for (CollectionObject *cob = collection->gobject.first, *cob_next = NULL; cob; cob = cob_next) { + cob_next = cob->next; + + if (cob->ob == NULL) { + BLI_freelinkN(&collection->gobject, cob); + changed = true; + } + } + + if (changed) { + BKE_collection_object_cache_free(collection); + } } } - else { - /* Adding will fail if object is already in collection. - * However we still need to remove it from the other collections. */ - BKE_collection_object_add(owner_id, sc_dst, ob); - collections_object_remove_ex(NULL, owner_id, ob, false, sc_dst); - } } -/** - * Whether the object is directly inside the collection. +/* + * Remove all NULL children from parent objects of changed old_collection. + * This is used for library remapping, where these pointers have been set to NULL. + * Otherwise this should never happen. */ -bool BKE_collection_object_exists(struct SceneCollection *scene_collection, struct Object *ob) +void BKE_collections_child_remove_nulls(Main *bmain, Collection *old_collection) { - if (BLI_findptr(&scene_collection->objects, ob, offsetof(LinkData, data))) { - return true; - } - return false; -} + bool changed = false; -static SceneCollection *scene_collection_from_index_recursive(SceneCollection *scene_collection, const int index, int *index_current) -{ - if (index == (*index_current)) { - return scene_collection; + for (CollectionChild *child = old_collection->children.first; child; child = child->next) { + CollectionParent *cparent = collection_find_parent(child->collection, old_collection); + if (cparent) { + BLI_freelinkN(&child->collection->parents, cparent); + } } - (*index_current)++; + for (CollectionParent *cparent = old_collection->parents.first; cparent; cparent = cparent->next) { + Collection *parent = cparent->collection; - for (SceneCollection *scene_collection_iter = scene_collection->scene_collections.first; - scene_collection_iter != NULL; - scene_collection_iter = scene_collection_iter->next) - { - SceneCollection *nested = scene_collection_from_index_recursive(scene_collection_iter, index, index_current); - if (nested != NULL) { - return nested; + for (CollectionChild *child = parent->children.first, *child_next = NULL; child; child = child_next) { + child_next = child->next; + + if (child->collection == NULL) { + BLI_freelinkN(&parent->children, child); + changed = true; + } } } - return NULL; -} -/** - * Return Scene Collection for a given index. - * - * The index is calculated from top to bottom counting the children before the siblings. - */ -SceneCollection *BKE_collection_from_index(Scene *scene, const int index) -{ - int index_current = 0; - SceneCollection *master_collection = BKE_collection_master(&scene->id); - return scene_collection_from_index_recursive(master_collection, index, &index_current); -} + BLI_freelistN(&old_collection->parents); -static void layer_collection_sync(LayerCollection *lc_dst, LayerCollection *lc_src) -{ - lc_dst->flag = lc_src->flag; - - /* Continue recursively. */ - LayerCollection *lc_dst_nested, *lc_src_nested; - lc_src_nested = lc_src->layer_collections.first; - for (lc_dst_nested = lc_dst->layer_collections.first; - lc_dst_nested && lc_src_nested; - lc_dst_nested = lc_dst_nested->next, lc_src_nested = lc_src_nested->next) - { - layer_collection_sync(lc_dst_nested, lc_src_nested); + if (changed) { + BKE_main_collection_sync(bmain); } } /** - * Select all the objects in this SceneCollection (and its nested collections) for this ViewLayer. - * Return true if any object was selected. + * Move object from a collection into another + * + * If source collection is NULL move it from all the existing collections. */ -bool BKE_collection_objects_select(ViewLayer *view_layer, SceneCollection *scene_collection) +void BKE_collection_object_move(Main *bmain, Scene *scene, Collection *collection_dst, Collection *collection_src, Object *ob) { - LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(view_layer, scene_collection); - if (layer_collection != NULL) { - BKE_layer_collection_objects_select(layer_collection); - return true; + /* In both cases we first add the object, then remove it from the other collections. + * Otherwise we lose the original base and whether it was active and selected. */ + if (collection_src != NULL) { + if (BKE_collection_object_add(bmain, collection_dst, ob)) { + BKE_collection_object_remove(bmain, collection_src, ob, false); + } } else { - /* Slower approach, we need to iterate over all the objects and for each one we see if there is a base. */ - bool changed = false; - for (LinkData *link = scene_collection->objects.first; link; link = link->next) { - Base *base = BKE_view_layer_base_find(view_layer, link->data); - if (base != NULL) { - if (((base->flag & BASE_SELECTED) == 0) && ((base->flag & BASE_SELECTABLED) != 0)) { - base->flag |= BASE_SELECTED; - changed = true; - } - } - } - return changed; + /* Adding will fail if object is already in collection. + * However we still need to remove it from the other collections. */ + BKE_collection_object_add(bmain, collection_dst, ob); + scene_collections_object_remove(bmain, scene, ob, false, collection_dst); } } -/** - * Leave only the master collection in, remove everything else. - * @param group - */ -static void collection_group_cleanup(Group *group) +/***************** Collection Scene Membership ****************/ + +bool BKE_collection_is_in_scene(Collection *collection) { - /* Unlink all the LayerCollections. */ - while (group->view_layer->layer_collections.last != NULL) { - BKE_collection_unlink(group->view_layer, group->view_layer->layer_collections.last); + if (collection->flag & COLLECTION_IS_MASTER) { + return true; + } + + for (CollectionParent *cparent = collection->parents.first; cparent; cparent = cparent->next) { + if (BKE_collection_is_in_scene(cparent->collection)) { + return true; + } } - /* Remove all the SceneCollections but the master. */ - collection_free(group->collection, false); + return false; } -/** - * Create a group from a collection - * - * Any ViewLayer that may have this the related SceneCollection linked is converted - * to a Group Collection. - */ -Group *BKE_collection_group_create(Main *bmain, Scene *scene, LayerCollection *lc_src) +void BKE_collections_after_lib_link(Main *bmain) { - SceneCollection *sc_dst, *sc_src = lc_src->scene_collection; - LayerCollection *lc_dst; + /* Update view layer collections to match any changes in linked + * collections after file load. */ + BKE_main_collection_sync(bmain); +} - /* The master collection can't be converted. */ - if (sc_src == BKE_collection_master(&scene->id)) { - return NULL; - } +/********************** Collection Children *******************/ - /* If a sub-collection of sc_dst is directly linked into a ViewLayer we can't convert. */ - for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) { - for (LayerCollection *lc_child = view_layer->layer_collections.first; lc_child; lc_child = lc_child->next) { - if (is_collection_in_tree(lc_child->scene_collection, sc_src)) { - return NULL; - } - } +bool BKE_collection_find_cycle(Collection *new_ancestor, Collection *collection) +{ + if (collection == new_ancestor) { + return true; } - /* Create new group with the same data as the original collection. */ - Group *group = BKE_group_add(bmain, sc_src->name); - collection_group_cleanup(group); - - sc_dst = BKE_collection_add(&group->id, NULL, COLLECTION_TYPE_GROUP_INTERNAL, sc_src->name); - BKE_collection_copy_data(sc_dst, sc_src, 0); - FOREACH_SCENE_COLLECTION_BEGIN(&group->id, sc_group) - { - sc_group->type = COLLECTION_TYPE_GROUP_INTERNAL; + for (CollectionParent *parent = new_ancestor->parents.first; parent; parent = parent->next) { + if (BKE_collection_find_cycle(parent->collection, collection)) { + return true; + } } - FOREACH_SCENE_COLLECTION_END; - lc_dst = BKE_collection_link(group->view_layer, sc_dst); - layer_collection_sync(lc_dst, lc_src); - - return group; + return false; } -/* ---------------------------------------------------------------------- */ -/* Outliner drag and drop */ +static CollectionChild *collection_find_child(Collection *parent, Collection *collection) +{ + return BLI_findptr(&parent->children, collection, offsetof(CollectionChild, collection)); +} -/** - * Find and return the SceneCollection that has \a sc_child as one of its directly - * nested SceneCollection. - * - * \param sc_parent Initial SceneCollection to look into recursively, usually the master collection - */ -static SceneCollection *find_collection_parent(const SceneCollection *sc_child, SceneCollection *sc_parent) +static bool collection_find_child_recursive(Collection *parent, Collection *collection) { - for (SceneCollection *sc_nested = sc_parent->scene_collections.first; sc_nested; sc_nested = sc_nested->next) { - if (sc_nested == sc_child) { - return sc_parent; + for (CollectionChild *child = parent->children.first; child; child = child->next) { + if (child->collection == collection) { + return true; } - SceneCollection *found = find_collection_parent(sc_child, sc_nested); - if (found) { - return found; + if (collection_find_child_recursive(child->collection, collection)) { + return true; } } - return NULL; + return false; } -/** - * Check if \a sc_reference is nested to \a sc_parent SceneCollection - */ -static bool is_collection_in_tree(const SceneCollection *sc_reference, SceneCollection *sc_parent) +static CollectionParent *collection_find_parent(Collection *child, Collection *collection) { - return find_collection_parent(sc_reference, sc_parent) != NULL; + return BLI_findptr(&child->parents, collection, offsetof(CollectionParent, collection)); } -bool BKE_collection_move_above(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src) +static bool collection_child_add(Collection *parent, Collection *collection, const int flag, const bool add_us) { - /* Find the SceneCollection the sc_src belongs to */ - SceneCollection *sc_master = master_collection_from_id(owner_id); - - /* Master Layer can't be moved around*/ - if (ELEM(sc_master, sc_src, sc_dst)) { + CollectionChild *child = collection_find_child(parent, collection); + if (child) { return false; } - - /* collection is already where we wanted it to be */ - if (sc_dst->prev == sc_src) { + if (BKE_collection_find_cycle(parent, collection)) { return false; } - /* We can't move a collection fs the destiny collection - * is nested to the source collection */ - if (is_collection_in_tree(sc_dst, sc_src)) { - return false; + child = MEM_callocN(sizeof(CollectionChild), "CollectionChild"); + child->collection = collection; + BLI_addtail(&parent->children, child); + + /* Don't add parent links for depsgraph datablocks, these are not kept in sync. */ + if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { + CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), "CollectionParent"); + cparent->collection = parent; + BLI_addtail(&collection->parents, cparent); } - SceneCollection *sc_src_parent = find_collection_parent(sc_src, sc_master); - SceneCollection *sc_dst_parent = find_collection_parent(sc_dst, sc_master); - BLI_assert(sc_src_parent); - BLI_assert(sc_dst_parent); + if (add_us) { + id_us_plus(&collection->id); + } - /* Remove sc_src from its parent */ - BLI_remlink(&sc_src_parent->scene_collections, sc_src); + BKE_collection_object_cache_free(parent); - /* Re-insert it where it belongs */ - BLI_insertlinkbefore(&sc_dst_parent->scene_collections, sc_dst, sc_src); + return true; +} + +static bool collection_child_remove(Collection *parent, Collection *collection) +{ + CollectionChild *child = collection_find_child(parent, collection); + if (child == NULL) { + return false; + } - /* Update the tree */ - BKE_layer_collection_resync(owner_id, sc_src_parent); - BKE_layer_collection_resync(owner_id, sc_dst_parent); + CollectionParent *cparent = collection_find_parent(collection, parent); + BLI_freelinkN(&collection->parents, cparent); + BLI_freelinkN(&parent->children, child); - /* Keep names unique. */ - collection_name_check(owner_id, sc_src); + id_us_min(&collection->id); + + BKE_collection_object_cache_free(parent); return true; } -bool BKE_collection_move_below(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src) +bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child) { - /* Find the SceneCollection the sc_src belongs to */ - SceneCollection *sc_master = master_collection_from_id(owner_id); - - /* Master Layer can't be moved around*/ - if (ELEM(sc_master, sc_src, sc_dst)) { + if (!collection_child_add(parent, child, 0, true)) { return false; } - /* Collection is already where we wanted it to be */ - if (sc_dst->next == sc_src) { - return false; - } + BKE_main_collection_sync(bmain); + return true; +} - /* We can't move a collection if the destiny collection - * is nested to the source collection */ - if (is_collection_in_tree(sc_dst, sc_src)) { +bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child) +{ + if (!collection_child_remove(parent, child)) { return false; } - SceneCollection *sc_src_parent = find_collection_parent(sc_src, sc_master); - SceneCollection *sc_dst_parent = find_collection_parent(sc_dst, sc_master); - BLI_assert(sc_src_parent); - BLI_assert(sc_dst_parent); + BKE_main_collection_sync(bmain); + return true; +} - /* Remove sc_src from its parent */ - BLI_remlink(&sc_src_parent->scene_collections, sc_src); +/********************** Collection index *********************/ - /* Re-insert it where it belongs */ - BLI_insertlinkafter(&sc_dst_parent->scene_collections, sc_dst, sc_src); +static Collection *collection_from_index_recursive(Collection *collection, const int index, int *index_current) +{ + if (index == (*index_current)) { + return collection; + } - /* Update the tree */ - BKE_layer_collection_resync(owner_id, sc_src_parent); - BKE_layer_collection_resync(owner_id, sc_dst_parent); + (*index_current)++; - /* Keep names unique. */ - collection_name_check(owner_id, sc_src); + for (CollectionChild *child = collection->children.first; child; child = child->next) { + Collection *nested = collection_from_index_recursive(child->collection, index, index_current); + if (nested != NULL) { + return nested; + } + } + return NULL; +} - return true; +/** + * Return Scene Collection for a given index. + * + * The index is calculated from top to bottom counting the children before the siblings. + */ +Collection *BKE_collection_from_index(Scene *scene, const int index) +{ + int index_current = 0; + Collection *master_collection = BKE_collection_master(scene); + return collection_from_index_recursive(master_collection, index, &index_current); } -bool BKE_collection_move_into(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src) +static bool collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect) { - /* Find the SceneCollection the sc_src belongs to */ - SceneCollection *sc_master = master_collection_from_id(owner_id); - if (sc_src == sc_master) { + bool changed = false; + + if (collection->flag & COLLECTION_RESTRICT_SELECT) { return false; } - /* We can't move a collection if the destiny collection - * is nested to the source collection */ - if (is_collection_in_tree(sc_dst, sc_src)) { - return false; + for (CollectionObject *cob = collection->gobject.first; cob; cob = cob->next) { + 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_SELECTABLED) && !(base->flag & BASE_SELECTED)) { + base->flag |= BASE_SELECTED; + changed = true; + } + } + } + } + + for (CollectionChild *child = collection->children.first; child; child = child->next) { + if (collection_objects_select(view_layer, collection, deselect)) { + changed = true; + } } - SceneCollection *sc_src_parent = find_collection_parent(sc_src, sc_master); - BLI_assert(sc_src_parent); + return changed; +} - /* collection is already where we wanted it to be */ - if (sc_dst->scene_collections.last == sc_src) { +/** + * Select all the objects in this Collection (and its nested collections) for this ViewLayer. + * Return true if any object was selected. + */ +bool BKE_collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect) +{ + LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(view_layer, collection); + + if (layer_collection != NULL) { + return BKE_layer_collection_objects_select(view_layer, layer_collection, deselect); + } + else { + return collection_objects_select(view_layer, collection, deselect); + } +} + +/***************** Collection move (outliner drag & drop) *********************/ + +bool BKE_collection_move(Main *bmain, + Collection *to_parent, + Collection *from_parent, + Collection *relative, + bool relative_after, + Collection *collection) +{ + if (collection->flag & COLLECTION_IS_MASTER) { return false; } + if (BKE_collection_find_cycle(to_parent, collection)) { + return false; + } + + /* Move to new parent collection */ + if (from_parent) { + collection_child_remove(from_parent, collection); + } + + collection_child_add(to_parent, collection, 0, true); + + /* Move to specified location under parent. */ + if (relative) { + CollectionChild *child = collection_find_child(to_parent, collection); + CollectionChild *relative_child = collection_find_child(to_parent, relative); - /* Remove sc_src from it */ - BLI_remlink(&sc_src_parent->scene_collections, sc_src); + if (relative_child) { + BLI_remlink(&to_parent->children, child); - /* Insert sc_src into sc_dst */ - BLI_addtail(&sc_dst->scene_collections, sc_src); + if (relative_after) { + BLI_insertlinkafter(&to_parent->children, relative_child, child); + } + else { + BLI_insertlinkbefore(&to_parent->children, relative_child, child); + } - /* Update the tree */ - BKE_layer_collection_resync(owner_id, sc_src_parent); - BKE_layer_collection_resync(owner_id, sc_dst); + BKE_collection_object_cache_free(to_parent); + } + } - /* Keep names unique. */ - collection_name_check(owner_id, sc_src); + BKE_main_collection_sync(bmain); return true; } -/* ---------------------------------------------------------------------- */ -/* Iteractors */ +/**************************** Iterators ******************************/ + /* scene collection iteractor */ -typedef struct SceneCollectionsIteratorData { - ID *owner_id; +typedef struct CollectionsIteratorData { + Scene *scene; void **array; int tot, cur; -} SceneCollectionsIteratorData; +} CollectionsIteratorData; -static void scene_collection_callback(SceneCollection *sc, BKE_scene_collections_Cb callback, void *data) +static void scene_collection_callback(Collection *collection, BKE_scene_collections_Cb callback, void *data) { - callback(sc, data); + callback(collection, data); - for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) { - scene_collection_callback(nsc, callback, data); + for (CollectionChild *child = collection->children.first; child; child = child->next) { + scene_collection_callback(child->collection, callback, data); } } -static void scene_collections_count(SceneCollection *UNUSED(sc), void *data) +static void scene_collections_count(Collection *UNUSED(collection), void *data) { int *tot = data; (*tot)++; } -static void scene_collections_build_array(SceneCollection *sc, void *data) +static void scene_collections_build_array(Collection *collection, void *data) { - SceneCollection ***array = data; - **array = sc; + Collection ***array = data; + **array = collection; (*array)++; } -static void scene_collections_array(ID *owner_id, SceneCollection ***collections_array, int *tot) +static void scene_collections_array(Scene *scene, Collection ***collections_array, int *tot) { - SceneCollection *sc; - SceneCollection **array; + Collection *collection; + Collection **array; *collections_array = NULL; *tot = 0; - if (owner_id == NULL) { + if (scene == NULL) { return; } - sc = master_collection_from_id(owner_id); - BLI_assert(sc != NULL); - scene_collection_callback(sc, scene_collections_count, tot); + collection = BKE_collection_master(scene); + BLI_assert(collection != NULL); + scene_collection_callback(collection, scene_collections_count, tot); if (*tot == 0) return; - *collections_array = array = MEM_mallocN(sizeof(SceneCollection *) * (*tot), "SceneCollectionArray"); - scene_collection_callback(sc, scene_collections_build_array, &array); + *collections_array = array = MEM_mallocN(sizeof(Collection *) * (*tot), "CollectionArray"); + scene_collection_callback(collection, scene_collections_build_array, &array); } /** @@ -873,14 +1064,14 @@ static void scene_collections_array(ID *owner_id, SceneCollection ***collections */ void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in) { - ID *owner_id = data_in; - SceneCollectionsIteratorData *data = MEM_callocN(sizeof(SceneCollectionsIteratorData), __func__); + Scene *scene = data_in; + CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__); - data->owner_id = owner_id; + data->scene = scene; iter->data = data; iter->valid = true; - scene_collections_array(owner_id, (SceneCollection ***)&data->array, &data->tot); + scene_collections_array(scene, (Collection ***)&data->array, &data->tot); BLI_assert(data->tot != 0); data->cur = 0; @@ -889,7 +1080,7 @@ void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in) void BKE_scene_collections_iterator_next(struct BLI_Iterator *iter) { - SceneCollectionsIteratorData *data = iter->data; + CollectionsIteratorData *data = iter->data; if (++data->cur < data->tot) { iter->current = data->array[data->cur]; @@ -901,7 +1092,7 @@ void BKE_scene_collections_iterator_next(struct BLI_Iterator *iter) void BKE_scene_collections_iterator_end(struct BLI_Iterator *iter) { - SceneCollectionsIteratorData *data = iter->data; + CollectionsIteratorData *data = iter->data; if (data) { if (data->array) { @@ -917,7 +1108,7 @@ void BKE_scene_collections_iterator_end(struct BLI_Iterator *iter) typedef struct SceneObjectsIteratorData { GSet *visited; - LinkData *link_next; + CollectionObject *cob_next; BLI_Iterator scene_collection_iter; } SceneObjectsIteratorData; @@ -933,9 +1124,9 @@ void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in) /* we wrap the scenecollection iterator here to go over the scene collections */ BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene); - SceneCollection *sc = data->scene_collection_iter.current; - if (sc->objects.first != NULL) { - iter->current = ((LinkData *)sc->objects.first)->data; + Collection *collection = data->scene_collection_iter.current; + if (collection->gobject.first != NULL) { + iter->current = ((CollectionObject *)collection->gobject.first)->ob; } else { BKE_scene_objects_iterator_next(iter); @@ -945,14 +1136,14 @@ void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in) /** * Gets the first unique object in the sequence */ -static LinkData *object_base_unique(GSet *gs, LinkData *link) +static CollectionObject *object_base_unique(GSet *gs, CollectionObject *cob) { - for (; link != NULL; link = link->next) { - Object *ob = link->data; + for (; cob != NULL; cob = cob->next) { + Object *ob = cob->ob; void **ob_key_p; if (!BLI_gset_ensure_p_ex(gs, ob, &ob_key_p)) { *ob_key_p = ob; - return link; + return cob; } } return NULL; @@ -961,23 +1152,23 @@ static LinkData *object_base_unique(GSet *gs, LinkData *link) void BKE_scene_objects_iterator_next(BLI_Iterator *iter) { SceneObjectsIteratorData *data = iter->data; - LinkData *link = data->link_next ? object_base_unique(data->visited, data->link_next) : NULL; + CollectionObject *cob = data->cob_next ? object_base_unique(data->visited, data->cob_next) : NULL; - if (link) { - data->link_next = link->next; - iter->current = link->data; + if (cob) { + data->cob_next = cob->next; + iter->current = cob->ob; } else { - /* if this is the last object of this ListBase look at the next SceneCollection */ - SceneCollection *sc; + /* if this is the last object of this ListBase look at the next Collection */ + Collection *collection; BKE_scene_collections_iterator_next(&data->scene_collection_iter); do { - sc = data->scene_collection_iter.current; + collection = data->scene_collection_iter.current; /* get the first unique object of this collection */ - LinkData *new_link = object_base_unique(data->visited, sc->objects.first); - if (new_link) { - data->link_next = new_link->next; - iter->current = new_link->data; + CollectionObject *new_cob = object_base_unique(data->visited, collection->gobject.first); + if (new_cob) { + data->cob_next = new_cob->next; + iter->current = new_cob->ob; return; } BKE_scene_collections_iterator_next(&data->scene_collection_iter); |