tornavis/source/blender/blenkernel/intern/collection.c

1497 lines
46 KiB
C

/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/** \file
* \ingroup bke
*/
#include <string.h>
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_iterator.h"
#include "BLI_listbase.h"
#include "BLI_math_base.h"
#include "BLI_threads.h"
#include "BLT_translation.h"
#include "BKE_collection.h"
#include "BKE_icons.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_library_remap.h"
#include "BKE_main.h"
#include "BKE_object.h"
#include "BKE_rigidbody.h"
#include "BKE_scene.h"
#include "DNA_ID.h"
#include "DNA_collection_types.h"
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "DNA_rigidbody_types.h"
#include "DNA_scene_types.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_query.h"
#include "MEM_guardedalloc.h"
/******************************** Prototypes ********************************/
static bool collection_child_add(Collection *parent,
Collection *collection,
const int flag,
const bool add_us);
static bool collection_child_remove(Collection *parent, Collection *collection);
static bool collection_object_add(
Main *bmain, 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);
static CollectionChild *collection_find_child(Collection *parent, Collection *collection);
static CollectionParent *collection_find_parent(Collection *child, Collection *collection);
static bool collection_find_child_recursive(Collection *parent, Collection *collection);
/***************************** Add Collection *******************************/
/* Add new collection, without view layer syncing. */
static Collection *collection_add(Main *bmain,
Collection *collection_parent,
const char *name_custom)
{
/* Determine new collection name. */
char name[MAX_NAME];
if (name_custom) {
STRNCPY(name, name_custom);
}
else {
BKE_collection_new_name_get(collection_parent, 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 collection;
}
/**
* Add a collection to a collection ListBase and synchronize all render layers
* The ListBase is NULL when the collection is to be added to the master collection
*/
Collection *BKE_collection_add(Main *bmain, Collection *collection_parent, const char *name_custom)
{
Collection *collection = collection_add(bmain, collection_parent, name_custom);
BKE_main_collection_sync(bmain);
return 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);
}
/**
* Remove a collection, optionally removing its child objects or moving
* them to parent collections.
*/
bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
{
/* 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);
}
}
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(bmain, parent, cob->ob, 0, true);
}
/* Remove child object. */
collection_object_remove(bmain, collection, cob->ob, true);
cob = collection->gobject.first;
}
}
BKE_id_delete(bmain, collection);
BKE_main_collection_sync(bmain);
return true;
}
/***************************** Collection Copy *******************************/
/**
* Only copy internal data of Collection ID from source
* to already allocated/initialized destination.
* You probably never want to use that directly,
* use #BKE_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).
*/
void BKE_collection_copy_data(Main *bmain,
Collection *collection_dst,
const Collection *collection_src,
const int flag)
{
BLI_assert(((collection_src->flag & COLLECTION_IS_MASTER) != 0) ==
((collection_src->id.flag & LIB_PRIVATE_DATA) != 0));
/* 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;
}
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(bmain, collection_dst, cob->ob, flag, false);
}
}
static Collection *collection_duplicate_recursive(Main *bmain,
Collection *parent,
Collection *collection_old,
const bool do_hierarchy,
const bool do_objects,
const bool do_obdata)
{
Collection *collection_new;
bool do_full_process = false;
const int object_dupflag = (do_obdata) ? U.dupflag : 0;
if (!do_hierarchy || collection_old->id.newid == NULL) {
BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new);
/* Copying add one user by default, need to get rid of that one. */
id_us_min(&collection_new->id);
if (do_hierarchy) {
ID_NEW_SET(collection_old, collection_new);
}
do_full_process = true;
}
else {
collection_new = (Collection *)collection_old->id.newid;
}
/* Optionally add to parent (we always want to do that,
* even if collection_old had already been duplicated). */
if (parent != NULL) {
if (collection_child_add(parent, collection_new, 0, true)) {
/* Put collection right after existing one. */
CollectionChild *child = collection_find_child(parent, collection_old);
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);
}
}
}
/* If we are not doing any kind of deep-copy, we can return immediately.
* False do_full_process means collection_old had already been duplicated,
* no need to redo some deep-copy on it. */
if (!do_hierarchy || !do_full_process) {
return collection_new;
}
if (do_objects) {
/* We can loop on collection_old's objects, that list is currently identical the collection_new
* objects, and won't be changed here. */
for (CollectionObject *cob = collection_old->gobject.first; cob; cob = cob->next) {
Object *ob_old = cob->ob;
Object *ob_new = (Object *)ob_old->id.newid;
if (ob_new == NULL) {
ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag);
ID_NEW_SET(ob_old, ob_new);
}
collection_object_add(bmain, collection_new, ob_new, 0, true);
collection_object_remove(bmain, collection_new, ob_old, false);
}
}
/* We can loop on collection_old's children,
* that list is currently identical the collection_new' children, and won't be changed here. */
for (CollectionChild *child = collection_old->children.first; child; child = child->next) {
Collection *child_collection_old = child->collection;
collection_duplicate_recursive(
bmain, collection_new, child_collection_old, do_hierarchy, do_objects, do_obdata);
collection_child_remove(collection_new, child_collection_old);
}
return collection_new;
}
/**
* Makes a standard (aka shallow) ID copy of a Collection.
*
* Add a new collection in the same level as the old one, link any nested collections
* and finally link the objects to the new collection (as opposed to copying them).
*/
Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection)
{
return BKE_collection_duplicate(bmain, parent, collection, false, false, false);
}
/**
* Make either a shallow copy, or deeper duplicate of given collection.
*
* If \a do_hierarchy and \a do_deep_copy are false, this is a regular (shallow) ID copy.
*
* \warning If any 'deep copy' behavior is enabled,
* this functions will clear all \a bmain id.idnew pointers.
*
* \param do_hierarchy: If true, it will recursively make shallow copies of children collections.
* \param do_objects: If true, it will also make duplicates of objects.
* This one does nothing if \a do_hierarchy is not set.
* \param do_obdata: If true, it will also make deep duplicates of objects,
* using behavior defined in user settings (#U.dupflag).
* This one does nothing if \a do_hierarchy and \a do_objects are not set.
*/
Collection *BKE_collection_duplicate(Main *bmain,
Collection *parent,
Collection *collection,
const bool do_hierarchy,
const bool do_objects,
const bool do_obdata)
{
/* It's not allowed to copy the master collection. */
BLI_assert((collection->id.flag & LIB_PRIVATE_DATA) == 0);
BLI_assert((collection->flag & COLLECTION_IS_MASTER) == 0);
if (collection->flag & COLLECTION_IS_MASTER) {
return NULL;
}
if (do_hierarchy) {
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
Collection *collection_new = collection_duplicate_recursive(
bmain, parent, collection, do_hierarchy, do_objects, do_obdata);
/* This code will follows into all ID links using an ID tagged with LIB_TAG_NEW.*/
BKE_libblock_relink_to_newid(&collection_new->id);
if (do_hierarchy) {
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
BKE_main_collection_sync(bmain);
return collection_new;
}
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 *******************************/
/**
* The automatic/fallback name of a new collection.
*/
void BKE_collection_new_name_get(Collection *collection_parent, char *rname)
{
char *name;
if (!collection_parent) {
name = BLI_strdup("Collection");
}
else if (collection_parent->flag & COLLECTION_IS_MASTER) {
name = BLI_sprintfN("Collection %d", BLI_listbase_count(&collection_parent->children) + 1);
}
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);
}
BLI_strncpy(rname, name, MAX_NAME);
MEM_freeN(name);
}
/**
* The name to show in the interface.
*/
const char *BKE_collection_ui_name_get(struct Collection *collection)
{
if (collection->flag & COLLECTION_IS_MASTER) {
return IFACE_("Scene Collection");
}
else {
return collection->id.name + 2;
}
}
/* **************** 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;
BLI_addtail(lb, base);
}
/* Only collection flags are checked here currently, object restrict flag is checked
* in FOREACH_COLLECTION_VISIBLE_OBJECT_RECURSIVE_BEGIN since it can be animated
* without updating the cache. */
if (((child_restrict & COLLECTION_RESTRICT_VIEWPORT) == 0)) {
base->flag |= BASE_ENABLED_VIEWPORT;
}
if (((child_restrict & COLLECTION_RESTRICT_RENDER) == 0)) {
base->flag |= BASE_ENABLED_RENDER;
}
}
for (CollectionChild *child = collection->children.first; child; child = child->next) {
collection_object_cache_fill(lb, child->collection, child_restrict);
}
}
ListBase BKE_collection_object_cache_get(Collection *collection)
{
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
static ThreadMutex cache_lock = BLI_MUTEX_INITIALIZER;
BLI_mutex_lock(&cache_lock);
if (!(collection->flag & COLLECTION_HAS_OBJECT_CACHE)) {
collection_object_cache_fill(&collection->object_cache, collection, 0);
collection->flag |= COLLECTION_HAS_OBJECT_CACHE;
}
BLI_mutex_unlock(&cache_lock);
}
return collection->object_cache;
}
static void collection_object_cache_free(Collection *collection)
{
/* 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);
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(const ViewLayer *view_layer, Collection *collection)
{
if (collection) {
return BKE_collection_object_cache_get(collection).first;
}
else {
return FIRSTBASE(view_layer);
}
}
/*********************** Scene Master 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->id.flag |= LIB_PRIVATE_DATA;
master_collection->flag |= COLLECTION_IS_MASTER;
return master_collection;
}
Scene *BKE_collection_master_scene_search(const Main *bmain, const Collection *master_collection)
{
BLI_assert((master_collection->flag & COLLECTION_IS_MASTER) != 0);
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
if (scene->master_collection == master_collection) {
return scene;
}
}
return NULL;
}
/*********************** Cyclic Checks ************************/
static bool collection_object_cyclic_check_internal(Object *object, Collection *collection)
{
if (object->instance_collection) {
Collection *dup_collection = object->instance_collection;
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;
}
bool BKE_collection_object_cyclic_check(Main *bmain, Object *object, Collection *collection)
{
/* first flag all collections */
BKE_main_id_tag_listbase(&bmain->collections, LIB_TAG_DOIT, true);
return collection_object_cyclic_check_internal(object, collection);
}
/******************* Collection Object Membership *******************/
bool BKE_collection_has_object(Collection *collection, const Object *ob)
{
if (ELEM(NULL, collection, ob)) {
return false;
}
return (BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob)));
}
bool BKE_collection_has_object_recursive(Collection *collection, Object *ob)
{
if (ELEM(NULL, collection, ob)) {
return false;
}
const ListBase objects = BKE_collection_object_cache_get(collection);
return (BLI_findptr(&objects, ob, offsetof(Base, object)));
}
static Collection *collection_next_find(Main *bmain, Scene *scene, Collection *collection)
{
if (scene && collection == scene->master_collection) {
return bmain->collections.first;
}
else {
return collection->id.next;
}
}
Collection *BKE_collection_object_find(Main *bmain,
Scene *scene,
Collection *collection,
Object *ob)
{
if (collection) {
collection = collection_next_find(bmain, scene, collection);
}
else if (scene) {
collection = scene->master_collection;
}
else {
collection = bmain->collections.first;
}
while (collection) {
if (BKE_collection_has_object(collection, ob)) {
return collection;
}
collection = collection_next_find(bmain, scene, collection);
}
return NULL;
}
bool BKE_collection_is_empty(Collection *collection)
{
return BLI_listbase_is_empty(&collection->gobject) &&
BLI_listbase_is_empty(&collection->children);
}
/********************** Collection Objects *********************/
static void collection_tag_update_parent_recursive(Main *bmain,
Collection *collection,
const int flag)
{
if (collection->flag & COLLECTION_IS_MASTER) {
return;
}
DEG_id_tag_update_ex(bmain, &collection->id, flag);
for (CollectionParent *collection_parent = collection->parents.first; collection_parent;
collection_parent = collection_parent->next) {
if (collection_parent->collection->flag & COLLECTION_IS_MASTER) {
/* We don't care about scene/master collection here. */
continue;
}
collection_tag_update_parent_recursive(bmain, collection_parent->collection, flag);
}
}
static bool collection_object_add(
Main *bmain, Collection *collection, Object *ob, int flag, const bool add_us)
{
if (ob->instance_collection) {
/* Cyclic dependency check. */
if (collection_find_child_recursive(ob->instance_collection, collection)) {
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);
}
if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
collection_tag_update_parent_recursive(bmain, collection, ID_RECALC_COPY_ON_WRITE);
}
if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) {
BKE_rigidbody_main_collection_object_add(bmain, collection, ob);
}
return true;
}
static bool collection_object_remove(Main *bmain,
Collection *collection,
Object *ob,
const bool free_us)
{
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 (free_us) {
BKE_id_free_us(bmain, ob);
}
else {
id_us_min(&ob->id);
}
collection_tag_update_parent_recursive(bmain, collection, ID_RECALC_COPY_ON_WRITE);
return true;
}
/**
* Add object to collection
*/
bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob)
{
if (ELEM(NULL, collection, ob)) {
return false;
}
if (!collection_object_add(bmain, collection, ob, 0, true)) {
return false;
}
if (BKE_collection_is_in_scene(collection)) {
BKE_main_collection_sync(bmain);
}
return true;
}
/**
* Add object to all scene collections that reference object is in
* (used to copy objects).
*/
void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst)
{
bool is_instantiated = false;
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
if (!ID_IS_LINKED(collection) && BKE_collection_has_object(collection, ob_src)) {
collection_object_add(bmain, collection, ob_dst, 0, true);
is_instantiated = true;
}
}
FOREACH_SCENE_COLLECTION_END;
if (!is_instantiated) {
/* In case we could not find any non-linked collections in which instantiate our ob_dst,
* fallback to scene's master collection... */
collection_object_add(bmain, scene->master_collection, ob_dst, 0, true);
}
BKE_main_collection_sync(bmain);
}
/**
* Remove object from collection.
*/
bool BKE_collection_object_remove(Main *bmain,
Collection *collection,
Object *ob,
const bool free_us)
{
if (ELEM(NULL, collection, ob)) {
return false;
}
if (!collection_object_remove(bmain, collection, ob, free_us)) {
return false;
}
if (BKE_collection_is_in_scene(collection)) {
BKE_main_collection_sync(bmain);
}
return true;
}
/**
* Remove object from all collections of scene
* \param scene_collection_skip: Don't remove base from this collection.
*/
static bool scene_collections_object_remove(
Main *bmain, Scene *scene, Object *ob, const bool free_us, Collection *collection_skip)
{
bool removed = false;
if (collection_skip == NULL) {
BKE_scene_remove_rigidbody_object(bmain, scene, ob);
}
FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) {
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_scene_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us)
{
return scene_collections_object_remove(bmain, scene, ob, free_us, NULL);
}
/*
* Remove all NULL objects from collections.
* This is used for library remapping, where these pointers have been set to NULL.
* Otherwise this should never happen.
*/
static void collection_object_remove_nulls(Collection *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);
}
}
void BKE_collections_object_remove_nulls(Main *bmain)
{
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
collection_object_remove_nulls(scene->master_collection);
}
for (Collection *collection = bmain->collections.first; collection;
collection = collection->id.next) {
collection_object_remove_nulls(collection);
}
}
static void collection_null_children_remove(Collection *collection)
{
for (CollectionChild *child = collection->children.first, *child_next = NULL; child;
child = child_next) {
child_next = child->next;
if (child->collection == NULL) {
BLI_freelinkN(&collection->children, child);
}
}
}
static void collection_missing_parents_remove(Collection *collection)
{
for (CollectionParent *parent = collection->parents.first, *parent_next; parent != NULL;
parent = parent_next) {
parent_next = parent->next;
if ((parent->collection == NULL) || !collection_find_child(parent->collection, collection)) {
BLI_freelinkN(&collection->parents, parent);
}
}
}
/**
* Remove all NULL children from parent collections of changed \a collection.
* This is used for library remapping, where these pointers have been set to NULL.
* Otherwise this should never happen.
*
* \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards!
*
* \param collection: may be \a NULL,
* in which case whole \a bmain database of collections is checked.
*/
void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection)
{
if (collection == NULL) {
/* We need to do the checks in two steps when more than one collection may be involved,
* otherwise we can miss some cases...
* Also, master collections are not in bmain, so we also need to loop over scenes.
*/
for (collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
collection_null_children_remove(collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_null_children_remove(scene->master_collection);
}
for (collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
collection_missing_parents_remove(collection);
}
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_missing_parents_remove(scene->master_collection);
}
}
else {
for (CollectionParent *parent = collection->parents.first, *parent_next; parent;
parent = parent_next) {
parent_next = parent->next;
collection_null_children_remove(parent->collection);
if (!collection_find_child(parent->collection, collection)) {
BLI_freelinkN(&collection->parents, parent);
}
}
}
}
/**
* Move object from a collection into another
*
* If source collection is NULL move it from all the existing collections.
*/
void BKE_collection_object_move(
Main *bmain, Scene *scene, Collection *collection_dst, Collection *collection_src, Object *ob)
{
/* 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 {
/* 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);
}
}
/***************** Collection Scene Membership ****************/
bool BKE_collection_is_in_scene(Collection *collection)
{
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;
}
}
return false;
}
void BKE_collections_after_lib_link(Main *bmain)
{
/* Need to update layer collections because objects might have changed
* in linked files, and because undo push does not include updated base
* flags since those are refreshed after the operator completes. */
BKE_main_collection_sync(bmain);
}
/********************** Collection Children *******************/
bool BKE_collection_find_cycle(Collection *new_ancestor, Collection *collection)
{
if (collection == new_ancestor) {
return true;
}
for (CollectionParent *parent = new_ancestor->parents.first; parent; parent = parent->next) {
if (BKE_collection_find_cycle(parent->collection, collection)) {
return true;
}
}
return false;
}
static CollectionChild *collection_find_child(Collection *parent, Collection *collection)
{
return BLI_findptr(&parent->children, collection, offsetof(CollectionChild, collection));
}
static bool collection_find_child_recursive(Collection *parent, Collection *collection)
{
for (CollectionChild *child = parent->children.first; child; child = child->next) {
if (child->collection == collection) {
return true;
}
if (collection_find_child_recursive(child->collection, collection)) {
return true;
}
}
return false;
}
bool BKE_collection_has_collection(Collection *parent, Collection *collection)
{
return collection_find_child_recursive(parent, collection);
}
static CollectionParent *collection_find_parent(Collection *child, Collection *collection)
{
return BLI_findptr(&child->parents, collection, offsetof(CollectionParent, collection));
}
static bool collection_child_add(Collection *parent,
Collection *collection,
const int flag,
const bool add_us)
{
CollectionChild *child = collection_find_child(parent, collection);
if (child) {
return false;
}
if (BKE_collection_find_cycle(parent, collection)) {
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);
}
if (add_us) {
id_us_plus(&collection->id);
}
BKE_collection_object_cache_free(parent);
return true;
}
static bool collection_child_remove(Collection *parent, Collection *collection)
{
CollectionChild *child = collection_find_child(parent, collection);
if (child == NULL) {
return false;
}
CollectionParent *cparent = collection_find_parent(collection, parent);
BLI_freelinkN(&collection->parents, cparent);
BLI_freelinkN(&parent->children, child);
id_us_min(&collection->id);
BKE_collection_object_cache_free(parent);
return true;
}
bool BKE_collection_child_add(Main *bmain, Collection *parent, Collection *child)
{
if (!collection_child_add(parent, child, 0, true)) {
return false;
}
BKE_main_collection_sync(bmain);
return true;
}
bool BKE_collection_child_remove(Main *bmain, Collection *parent, Collection *child)
{
if (!collection_child_remove(parent, child)) {
return false;
}
BKE_main_collection_sync(bmain);
return true;
}
/**
* Rebuild parent relationships from child ones, for all children of given \a collection.
*
* \note Given collection is assumed to already have valid parents.
*/
void BKE_collection_parent_relations_rebuild(Collection *collection)
{
for (CollectionChild *child = collection->children.first, *child_next = NULL; child;
child = child_next) {
child_next = child->next;
if (child->collection == NULL || BKE_collection_find_cycle(collection, child->collection)) {
BLI_freelinkN(&collection->children, child);
}
else {
CollectionParent *cparent = MEM_callocN(sizeof(CollectionParent), __func__);
cparent->collection = collection;
BLI_addtail(&child->collection->parents, cparent);
}
}
}
static void collection_parents_rebuild_recursive(Collection *collection)
{
BKE_collection_parent_relations_rebuild(collection);
collection->tag &= ~COLLECTION_TAG_RELATION_REBUILD;
for (CollectionChild *child = collection->children.first; child != NULL; child = child->next) {
collection_parents_rebuild_recursive(child->collection);
}
}
/**
* Rebuild parent relationships from child ones, for all collections in given \a bmain.
*/
void BKE_main_collections_parent_relations_rebuild(Main *bmain)
{
/* Only collections not in bmain (master ones in scenes) have no parent... */
for (Collection *collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
BLI_freelistN(&collection->parents);
collection->tag |= COLLECTION_TAG_RELATION_REBUILD;
}
/* Scene's master collections will be 'root' parent of most of our collections, so start with
* them. */
for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) {
collection_parents_rebuild_recursive(scene->master_collection);
}
/* We may have parent chains outside of scene's master_collection context? At least, readfile's
* lib_link_collection_data() seems to assume that, so do the same here. */
for (Collection *collection = bmain->collections.first; collection != NULL;
collection = collection->id.next) {
if (collection->tag & COLLECTION_TAG_RELATION_REBUILD) {
/* Note: we do not have easy access to 'which collections is root' info in that case, which
* means test for cycles in collection relationships may fail here. I don't think that is an
* issue in practice here, but worth keeping in mind... */
collection_parents_rebuild_recursive(collection);
}
}
}
/********************** Collection index *********************/
static Collection *collection_from_index_recursive(Collection *collection,
const int index,
int *index_current)
{
if (index == (*index_current)) {
return collection;
}
(*index_current)++;
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 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 = scene->master_collection;
return collection_from_index_recursive(master_collection, index, &index_current);
}
static bool collection_objects_select(ViewLayer *view_layer, Collection *collection, bool deselect)
{
bool changed = false;
if (collection->flag & COLLECTION_RESTRICT_SELECT) {
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_SELECTABLE) && !(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;
}
}
return changed;
}
/**
* 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);
if (relative_child) {
BLI_remlink(&to_parent->children, child);
if (relative_after) {
BLI_insertlinkafter(&to_parent->children, relative_child, child);
}
else {
BLI_insertlinkbefore(&to_parent->children, relative_child, child);
}
BKE_collection_object_cache_free(to_parent);
}
}
/* Make sure we store the flag of the layer collections before we remove and re-create them.
* Otherwise they will get lost and everything will be copied from the new parent collection. */
GHash *view_layer_hash = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
for (Scene *scene = bmain->scenes.first; scene; scene = scene->id.next) {
for (ViewLayer *view_layer = scene->view_layers.first; view_layer;
view_layer = view_layer->next) {
LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(
view_layer, collection);
if (layer_collection == NULL) {
continue;
}
BLI_ghash_insert(view_layer_hash, view_layer, POINTER_FROM_INT(layer_collection->flag));
}
}
/* Create and remove layer collections. */
BKE_main_collection_sync(bmain);
/* Restore back the original layer collection flags. */
GHashIterator gh_iter;
GHASH_ITER (gh_iter, view_layer_hash) {
ViewLayer *view_layer = BLI_ghashIterator_getKey(&gh_iter);
LayerCollection *layer_collection = BKE_layer_collection_first_from_scene_collection(
view_layer, collection);
if (layer_collection) {
/* We treat exclude as a special case.
*
* If in a different view layer the parent collection was disabled (e.g., background)
* and now we moved a new collection to be part of the background this collection should
* probably be disabled.
*
* Note: If we were to also keep the exclude flag we would need to re-sync the collections.
*/
layer_collection->flag = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter)) |
(layer_collection->flag & LAYER_COLLECTION_EXCLUDE);
}
}
BLI_ghash_free(view_layer_hash, NULL, NULL);
return true;
}
/**************************** Iterators ******************************/
/* scene collection iteractor */
typedef struct CollectionsIteratorData {
Scene *scene;
void **array;
int tot, cur;
} CollectionsIteratorData;
static void scene_collection_callback(Collection *collection,
BKE_scene_collections_Cb callback,
void *data)
{
callback(collection, data);
for (CollectionChild *child = collection->children.first; child; child = child->next) {
scene_collection_callback(child->collection, callback, data);
}
}
static void scene_collections_count(Collection *UNUSED(collection), void *data)
{
int *tot = data;
(*tot)++;
}
static void scene_collections_build_array(Collection *collection, void *data)
{
Collection ***array = data;
**array = collection;
(*array)++;
}
static void scene_collections_array(Scene *scene, Collection ***collections_array, int *tot)
{
Collection *collection;
Collection **array;
*collections_array = NULL;
*tot = 0;
if (scene == NULL) {
return;
}
collection = scene->master_collection;
BLI_assert(collection != NULL);
scene_collection_callback(collection, scene_collections_count, tot);
if (*tot == 0) {
return;
}
*collections_array = array = MEM_mallocN(sizeof(Collection *) * (*tot), "CollectionArray");
scene_collection_callback(collection, scene_collections_build_array, &array);
}
/**
* Only use this in non-performance critical situations
* (it iterates over all scene collections twice)
*/
void BKE_scene_collections_iterator_begin(BLI_Iterator *iter, void *data_in)
{
Scene *scene = data_in;
CollectionsIteratorData *data = MEM_callocN(sizeof(CollectionsIteratorData), __func__);
data->scene = scene;
iter->data = data;
iter->valid = true;
scene_collections_array(scene, (Collection ***)&data->array, &data->tot);
BLI_assert(data->tot != 0);
data->cur = 0;
iter->current = data->array[data->cur];
}
void BKE_scene_collections_iterator_next(struct BLI_Iterator *iter)
{
CollectionsIteratorData *data = iter->data;
if (++data->cur < data->tot) {
iter->current = data->array[data->cur];
}
else {
iter->valid = false;
}
}
void BKE_scene_collections_iterator_end(struct BLI_Iterator *iter)
{
CollectionsIteratorData *data = iter->data;
if (data) {
if (data->array) {
MEM_freeN(data->array);
}
MEM_freeN(data);
}
iter->valid = false;
}
/* scene objects iterator */
typedef struct SceneObjectsIteratorData {
GSet *visited;
CollectionObject *cob_next;
BLI_Iterator scene_collection_iter;
} SceneObjectsIteratorData;
void BKE_scene_objects_iterator_begin(BLI_Iterator *iter, void *data_in)
{
Scene *scene = data_in;
SceneObjectsIteratorData *data = MEM_callocN(sizeof(SceneObjectsIteratorData), __func__);
iter->data = data;
/* lookup list ot make sure each object is object called once */
data->visited = BLI_gset_ptr_new(__func__);
/* we wrap the scenecollection iterator here to go over the scene collections */
BKE_scene_collections_iterator_begin(&data->scene_collection_iter, scene);
Collection *collection = data->scene_collection_iter.current;
data->cob_next = collection->gobject.first;
BKE_scene_objects_iterator_next(iter);
}
/**
* Ensures we only get each object once, even when included in several collections.
*/
static CollectionObject *object_base_unique(GSet *gs, CollectionObject *cob)
{
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 cob;
}
}
return NULL;
}
void BKE_scene_objects_iterator_next(BLI_Iterator *iter)
{
SceneObjectsIteratorData *data = iter->data;
CollectionObject *cob = data->cob_next ? object_base_unique(data->visited, data->cob_next) :
NULL;
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 Collection */
Collection *collection;
BKE_scene_collections_iterator_next(&data->scene_collection_iter);
do {
collection = data->scene_collection_iter.current;
/* get the first unique object of this collection */
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);
} while (data->scene_collection_iter.valid);
if (!data->scene_collection_iter.valid) {
iter->valid = false;
}
}
}
void BKE_scene_objects_iterator_end(BLI_Iterator *iter)
{
SceneObjectsIteratorData *data = iter->data;
if (data) {
BKE_scene_collections_iterator_end(&data->scene_collection_iter);
BLI_gset_free(data->visited, NULL);
MEM_freeN(data);
}
}