Files
test/source/blender/blenkernel/intern/collection.c
Dalai Felinto ae12424298 Outliner/Collections: Fix objects disappearing when moving to collections
Bug introduced on fb4cd136a7 (multi-object drag-and-drop).

How to reproduce the bug:
* Create a new collection
* Move the Cube to the new collection
* Move the Camera to the new collection (Cube disappears)
* Move the Lamp to the new collection (Camera disappears)

Explanation of the bug:
The moved object was still selected, so we were trying to add the object to the
collection were the object was already inserted (which would fail silently) and
then remove it.
2018-01-15 18:27:50 -02:00

849 lines
23 KiB
C

/*
* ***** BEGIN GPL LICENSE BLOCK *****
*
* 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.
*
* Contributor(s): Dalai Felinto
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/collection.c
* \ingroup bke
*/
#include <string.h>
#include "BLI_blenlib.h"
#include "BLI_ghash.h"
#include "BLI_iterator.h"
#include "BLI_listbase.h"
#include "BLT_translation.h"
#include "BLI_string_utils.h"
#include "BKE_collection.h"
#include "BKE_group.h"
#include "BKE_idprop.h"
#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_scene.h"
#include "DNA_group_types.h"
#include "DNA_ID.h"
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.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);
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;
}
}
/**
* 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)
{
SceneCollection *sc_master = collection_master_from_id(owner_id);
SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection");
sc->type = type;
const char *name = name_custom;
if (!sc_parent) {
sc_parent = sc_master;
}
if (!name) {
if (sc_parent == sc_master) {
name = BLI_sprintfN("Collection %d", BLI_listbase_count(&sc_master->scene_collections) + 1);
}
else {
name = BLI_sprintfN("%s %d", sc_parent->name, BLI_listbase_count(&sc_parent->scene_collections) + 1);
}
}
BLI_addtail(&sc_parent->scene_collections, sc);
BKE_collection_rename((Scene *)owner_id, sc, name);
BKE_layer_sync_new_scene_collection(owner_id, sc_parent, sc);
if (name != name_custom) {
MEM_freeN((char *)name);
}
return sc;
}
/**
* Free the collection items recursively
*/
static void collection_free(SceneCollection *sc, const bool do_id_user)
{
if (do_id_user) {
for (LinkData *link = sc->objects.first; link; link = link->next) {
id_us_min(link->data);
}
for (LinkData *link = sc->filter_objects.first; link; link = link->next) {
id_us_min(link->data);
}
}
BLI_freelistN(&sc->objects);
BLI_freelistN(&sc->filter_objects);
for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
collection_free(nsc, do_id_user);
}
BLI_freelistN(&sc->scene_collections);
}
/**
* Unlink the collection recursively
* \return true if unlinked.
*/
static bool collection_remlink(SceneCollection *sc_parent, SceneCollection *sc_gone)
{
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;
}
if (collection_remlink(sc, sc_gone)) {
return true;
}
}
return false;
}
/**
* Recursively remove any instance of this SceneCollection
*/
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;
}
}
else {
layer_collection_remove(view_layer, &lc->layer_collections, sc);
lc = lc->next;
}
}
}
/**
* 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.
*/
bool BKE_collection_remove(ID *owner_id, SceneCollection *sc)
{
SceneCollection *sc_master = collection_master_from_id(owner_id);
/* The master collection cannot be removed. */
if (sc == sc_master) {
return false;
}
/* 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;
}
/* Unlink from the respective collection tree. */
if (!collection_remlink(sc_master, sc)) {
BLI_assert(false);
}
/* 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);
FOREACH_SCENE_COLLECTION(owner_id, scene_collection_iter)
{
if (scene_collection_iter == sc) {
continue;
}
LinkData *link_next, *link = collection_objects.first;
while (link) {
link_next = link->next;
if (BLI_findptr(&scene_collection_iter->objects, link->data, offsetof(LinkData, data))) {
BLI_remlink(&collection_objects, link);
MEM_freeN(link);
}
link = link_next;
}
}
FOREACH_SCENE_COLLECTION_END
for (LinkData *link = collection_objects.first; link; link = link->next) {
BKE_collection_object_add(owner_id, sc_master, link->data);
}
BLI_freelistN(&collection_objects);
/* Clear the collection items. */
collection_free(sc, true);
/* 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;
}
MEM_freeN(sc);
return true;
}
/**
* 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)
{
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);
}
}
BLI_duplicatelist(&sc_dst->filter_objects, &sc_src->filter_objects);
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
for (LinkData *link = sc_dst->filter_objects.first; link; link = link->next) {
id_us_plus(link->data);
}
}
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);
}
}
static SceneCollection *master_collection_from_id(const ID *owner_id)
{
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;
}
}
/**
* Returns the master collection of the scene or group
*/
SceneCollection *BKE_collection_master(const ID *owner_id)
{
return master_collection_from_id(owner_id);
}
static void collection_rename(const ID *owner_id, SceneCollection *sc, const char *name)
{
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));
}
void BKE_collection_rename(const Scene *scene, SceneCollection *sc, const char *name)
{
collection_rename(&scene->id, sc, name);
}
/**
* 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_free(BKE_collection_master(owner_id), do_id_user);
}
static void collection_object_add(const ID *owner_id, SceneCollection *sc, Object *ob)
{
BLI_addtail(&sc->objects, BLI_genericNodeN(ob));
if (GS(owner_id->name) == ID_SCE) {
id_us_plus((ID *)ob);
}
else {
BLI_assert(GS(owner_id->name) == ID_GR);
if ((ob->flag & OB_FROMGROUP) == 0) {
ob->flag |= OB_FROMGROUP;
}
}
BKE_layer_sync_object_link(owner_id, sc, ob);
}
/**
* Add object to collection
*/
bool BKE_collection_object_add(const ID *owner_id, SceneCollection *sc, Object *ob)
{
if (BLI_findptr(&sc->objects, ob, offsetof(LinkData, data))) {
/* don't add the same object twice */
return false;
}
collection_object_add(owner_id, sc, ob);
return true;
}
/**
* Add object to all collections that reference objects is in
* (used to copy objects)
*/
void BKE_collection_object_add_from(Scene *scene, Object *ob_src, Object *ob_dst)
{
FOREACH_SCENE_COLLECTION(scene, sc)
{
if (BLI_findptr(&sc->objects, ob_src, offsetof(LinkData, data))) {
collection_object_add(&scene->id, sc, ob_dst);
}
}
FOREACH_SCENE_COLLECTION_END
for (ViewLayer *view_layer = scene->view_layers.first; view_layer; view_layer = view_layer->next) {
Base *base_src = BKE_view_layer_base_find(view_layer, ob_src);
if (base_src != NULL) {
if (base_src->collection_properties == NULL) {
continue;
}
Base *base_dst = BKE_view_layer_base_find(view_layer, ob_dst);
IDP_MergeGroup(base_dst->collection_properties, base_src->collection_properties, true);
}
}
}
/**
* 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)
{
LinkData *link = BLI_findptr(&sc->objects, ob, offsetof(LinkData, data));
if (link == NULL) {
return false;
}
BLI_remlink(&sc->objects, link);
MEM_freeN(link);
TODO_LAYER_SYNC_FILTER; /* need to remove all instances of ob in scene collections -> filter_objects */
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);
}
}
else {
BLI_assert(GS(owner_id->name) == ID_GR);
}
return true;
}
/**
* Move object from a collection into another
*/
void BKE_collection_object_move(ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
{
if (BKE_collection_object_add(owner_id, sc_dst, ob)) {
BKE_collection_object_remove(NULL, owner_id, sc_src, ob, false);
}
}
/**
* Remove object from all collections of scene
*/
bool BKE_collections_object_remove(Main *bmain, ID *owner_id, Object *ob, const bool free_us)
{
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(owner_id, sc)
{
removed |= BKE_collection_object_remove(bmain, owner_id, sc, ob, free_us);
}
FOREACH_SCENE_COLLECTION_END
return removed;
}
static void layer_collection_sync(LayerCollection *lc_dst, LayerCollection *lc_src)
{
lc_dst->flag = lc_src->flag;
/* Pending: sync overrides. */
IDP_MergeGroup(lc_dst->properties, lc_src->properties, true);
/* 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);
}
}
/**
* Leave only the master collection in, remove everything else.
* @param group
*/
static void collection_group_cleanup(Group *group)
{
/* Unlink all the LayerCollections. */
while (group->view_layer->layer_collections.last != NULL) {
BKE_collection_unlink(group->view_layer, group->view_layer->layer_collections.last);
}
/* Remove all the SceneCollections but the master. */
collection_free(group->collection, 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)
{
SceneCollection *sc_dst, *sc_src = lc_src->scene_collection;
LayerCollection *lc_dst;
/* The master collection can't be converted. */
if (sc_src == BKE_collection_master(&scene->id)) {
return NULL;
}
/* 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;
}
}
}
/* 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(&group->id, sc_group)
{
sc_group->type = COLLECTION_TYPE_GROUP_INTERNAL;
}
FOREACH_SCENE_COLLECTION_END
lc_dst = BKE_collection_link(group->view_layer, sc_dst);
layer_collection_sync(lc_dst, lc_src);
return group;
}
/* ---------------------------------------------------------------------- */
/* Outliner drag and drop */
/**
* 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)
{
for (SceneCollection *sc_nested = sc_parent->scene_collections.first; sc_nested; sc_nested = sc_nested->next) {
if (sc_nested == sc_child) {
return sc_parent;
}
SceneCollection *found = find_collection_parent(sc_child, sc_nested);
if (found) {
return found;
}
}
return NULL;
}
/**
* 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)
{
return find_collection_parent(sc_reference, sc_parent) != NULL;
}
bool BKE_collection_move_above(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* 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)) {
return false;
}
/* collection is already where we wanted it to be */
if (sc_dst->prev == sc_src) {
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;
}
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);
/* Remove sc_src from its parent */
BLI_remlink(&sc_src_parent->scene_collections, sc_src);
/* Re-insert it where it belongs */
BLI_insertlinkbefore(&sc_dst_parent->scene_collections, sc_dst, sc_src);
/* Update the tree */
BKE_layer_collection_resync(owner_id, sc_src_parent);
BKE_layer_collection_resync(owner_id, sc_dst_parent);
return true;
}
bool BKE_collection_move_below(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* 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)) {
return false;
}
/* Collection is already where we wanted it to be */
if (sc_dst->next == sc_src) {
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;
}
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);
/* Remove sc_src from its parent */
BLI_remlink(&sc_src_parent->scene_collections, sc_src);
/* Re-insert it where it belongs */
BLI_insertlinkafter(&sc_dst_parent->scene_collections, sc_dst, sc_src);
/* Update the tree */
BKE_layer_collection_resync(owner_id, sc_src_parent);
BKE_layer_collection_resync(owner_id, sc_dst_parent);
return true;
}
bool BKE_collection_move_into(const ID *owner_id, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* Find the SceneCollection the sc_src belongs to */
SceneCollection *sc_master = master_collection_from_id(owner_id);
if (sc_src == sc_master) {
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;
}
SceneCollection *sc_src_parent = find_collection_parent(sc_src, sc_master);
BLI_assert(sc_src_parent);
/* collection is already where we wanted it to be */
if (sc_dst->scene_collections.last == sc_src) {
return false;
}
/* Remove sc_src from it */
BLI_remlink(&sc_src_parent->scene_collections, sc_src);
/* Insert sc_src into sc_dst */
BLI_addtail(&sc_dst->scene_collections, sc_src);
/* Update the tree */
BKE_layer_collection_resync(owner_id, sc_src_parent);
BKE_layer_collection_resync(owner_id, sc_dst);
return true;
}
/* ---------------------------------------------------------------------- */
/* Iteractors */
/* scene collection iteractor */
typedef struct SceneCollectionsIteratorData {
ID *owner_id;
void **array;
int tot, cur;
} SceneCollectionsIteratorData;
static void scene_collection_callback(SceneCollection *sc, BKE_scene_collections_Cb callback, void *data)
{
callback(sc, data);
for (SceneCollection *nsc = sc->scene_collections.first; nsc; nsc = nsc->next) {
scene_collection_callback(nsc, callback, data);
}
}
static void scene_collections_count(SceneCollection *UNUSED(sc), void *data)
{
int *tot = data;
(*tot)++;
}
static void scene_collections_build_array(SceneCollection *sc, void *data)
{
SceneCollection ***array = data;
**array = sc;
(*array)++;
}
static void scene_collections_array(ID *owner_id, SceneCollection ***collections_array, int *tot)
{
SceneCollection *sc;
SceneCollection **array;
*collections_array = NULL;
*tot = 0;
if (owner_id == NULL) {
return;
}
sc = master_collection_from_id(owner_id);
BLI_assert(sc != NULL);
scene_collection_callback(sc, 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);
}
/**
* 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)
{
ID *owner_id = data_in;
SceneCollectionsIteratorData *data = MEM_callocN(sizeof(SceneCollectionsIteratorData), __func__);
data->owner_id = owner_id;
iter->data = data;
iter->valid = true;
scene_collections_array(owner_id, (SceneCollection ***)&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)
{
SceneCollectionsIteratorData *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)
{
SceneCollectionsIteratorData *data = iter->data;
if (data) {
if (data->array) {
MEM_freeN(data->array);
}
MEM_freeN(data);
}
iter->valid = false;
}
/* scene objects iteractor */
typedef struct SceneObjectsIteratorData {
GSet *visited;
LinkData *link_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);
SceneCollection *sc = data->scene_collection_iter.current;
if (sc->objects.first != NULL) {
iter->current = ((LinkData *)sc->objects.first)->data;
}
else {
BKE_scene_objects_iterator_next(iter);
}
}
/**
* Gets the first unique object in the sequence
*/
static LinkData *object_base_unique(GSet *gs, LinkData *link)
{
for (; link != NULL; link = link->next) {
Object *ob = link->data;
void **ob_key_p;
if (!BLI_gset_ensure_p_ex(gs, ob, &ob_key_p)) {
*ob_key_p = ob;
return link;
}
}
return NULL;
}
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;
if (link) {
data->link_next = link->next;
iter->current = link->data;
}
else {
/* if this is the last object of this ListBase look at the next SceneCollection */
SceneCollection *sc;
BKE_scene_collections_iterator_next(&data->scene_collection_iter);
do {
sc = 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;
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);
}
}