Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBrecht Van Lommel <brechtvanlommel@gmail.com>2018-04-30 16:57:22 +0300
committerBrecht Van Lommel <brechtvanlommel@gmail.com>2018-05-18 14:34:24 +0300
commit17bd5c9d4b1e9377b896c13043e24eaa7e979714 (patch)
tree60ca4e4118c01d88ccc04919d4f7ed46014875c1 /source/blender/blenkernel/intern/collection.c
parent70aec3732d0f03138d6477fd04c81dfd9fd21256 (diff)
Collections and groups unification
OVERVIEW * In 2.7 terminology, all layers and groups are now collection datablocks. * These collections are nestable, linkable, instanceable, overrideable, .. which opens up new ways to set up scenes and link + override data. * Viewport/render visibility and selectability are now a part of the collection and shared across all view layers and linkable. * View layers define which subset of the scene collection hierarchy is excluded for each. For many workflows one view layer can be used, these are more of an advanced feature now. OUTLINER * The outliner now has a "View Layer" display mode instead of "Collections", which can display the collections and/or objects in the view layer. * In this display mode, collections can be excluded with the right click menu. These will then be greyed out and their objects will be excluded. * To view collections not linked to any scene, the "Blender File" display mode can be used, with the new filtering option to just see Colleciton datablocks. * The outliner right click menus for collections and objects were reorganized. * Drag and drop still needs to be improved. Like before, dragging the icon or text gives different results, we'll unify this later. LINKING AND OVERRIDES * Collections can now be linked into the scene without creating an instance, with the link/append operator or from the collections view in the outliner. * Collections can get static overrides with the right click menu in the outliner, but this is rather unreliable and not clearly communicated at the moment. * We still need to improve the make override operator to turn collection instances into collections with overrides directly in the scene. PERFORMANCE * We tried to make performance not worse than before and improve it in some cases. The main thing that's still a bit slower is multiple scenes, we have to change the layer syncing to only updated affected scenes. * Collections keep a list of their parent collections for faster incremental updates in syncing and caching. * View layer bases are now in a object -> base hash to avoid quadratic time lookups internally and in API functions like visible_get(). VERSIONING * Compatibility with 2.7 files should be improved due to the new visibility controls. Of course users may not want to set up their scenes differently now to avoid having separate layers and groups. * Compatibility with 2.8 is mostly there, and was tested on Eevee demo and Hero files. There's a few things which are know to be not quite compatible, like nested layer collections inside groups. * The versioning code for 2.8 files is quite complicated, and isolated behind #ifdef so it can be removed at the end of the release cycle. KNOWN ISSUES * The G-key group operators in the 3D viewport were left mostly as is, they need to be modified still to fit better. * Same for the groups panel in the object properties. This needs to be updated still, or perhaps replaced by something better. * Collections must all have a unique name. Less restrictive namespacing is to be done later, we'll have to see how important this is as all objects within the collections must also have a unique name anyway. * Full scene copy and delete scene are exactly doing the right thing yet. Differential Revision: https://developer.blender.org/D3383 https://code.blender.org/2018/05/collections-and-groups/
Diffstat (limited to 'source/blender/blenkernel/intern/collection.c')
-rw-r--r--source/blender/blenkernel/intern/collection.c1275
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);