This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/collection.c
Sergey Sharybin e39dc48bd5 Fix crash when duplicating object
This fixes null pointer check fiasco.
2017-07-28 13:02:20 +02:00

652 lines
17 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_idprop.h"
#include "BKE_layer.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_scene.h"
#include "DNA_ID.h"
#include "DNA_layer_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "MEM_guardedalloc.h"
/**
* 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(Scene *scene, SceneCollection *sc_parent, const char *name)
{
SceneCollection *sc_master = BKE_collection_master(scene);
SceneCollection *sc = MEM_callocN(sizeof(SceneCollection), "New Collection");
if (!name) {
name = DATA_("New Collection");
}
if (!sc_parent) {
sc_parent = sc_master;
}
BKE_collection_rename(scene, sc, name);
BLI_addtail(&sc_parent->scene_collections, sc);
BKE_layer_sync_new_scene_collection(scene, sc_parent, sc);
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(SceneLayer *sl, ListBase *lb, const SceneCollection *sc)
{
LayerCollection *lc = lb->first;
while (lc) {
if (lc->scene_collection == sc) {
BKE_layer_collection_free(sl, 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 != &sl->layer_collections) {
return;
}
}
else {
layer_collection_remove(sl, &lc->layer_collections, sc);
lc = lc->next;
}
}
}
/**
* Remove a collection from the scene, and syncronize all render layers
*/
bool BKE_collection_remove(Scene *scene, SceneCollection *sc)
{
SceneCollection *sc_master = BKE_collection_master(scene);
/* the master collection cannot be removed */
if (sc == sc_master) {
return false;
}
/* unlink from the respective collection tree */
if (!collection_remlink(sc_master, sc)) {
BLI_assert(false);
}
/* clear the collection items */
collection_free(sc, true);
/* check all layers that use this collection and clear them */
for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) {
layer_collection_remove(sl, &sl->layer_collections, sc);
sl->active_collection = 0;
}
MEM_freeN(sc);
return true;
}
/**
* Returns the master collection
*/
SceneCollection *BKE_collection_master(const Scene *scene)
{
return scene->collection;
}
struct UniqueNameCheckData {
ListBase *lb;
SceneCollection *lookup_sc;
};
static bool collection_unique_name_check(void *arg, const char *name)
{
struct UniqueNameCheckData *data = arg;
for (SceneCollection *sc = data->lb->first; sc; sc = sc->next) {
struct UniqueNameCheckData child_data = {.lb = &sc->scene_collections, .lookup_sc = data->lookup_sc};
if (sc != data->lookup_sc) {
if (STREQ(sc->name, name)) {
return true;
}
}
if (collection_unique_name_check(&child_data, name)) {
return true;
}
}
return false;
}
void BKE_collection_rename(const Scene *scene, SceneCollection *sc, const char *name)
{
SceneCollection *sc_master = BKE_collection_master(scene);
struct UniqueNameCheckData data = {.lb = &sc_master->scene_collections, .lookup_sc = sc};
BLI_strncpy(sc->name, name, sizeof(sc->name));
BLI_uniquename_cb(collection_unique_name_check, &data, DATA_("Collection"), '.', sc->name, sizeof(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 data since it's not doing re-syncing of the LayerCollection tree
*/
void BKE_collection_master_free(Scene *scene, const bool do_id_user)
{
collection_free(BKE_collection_master(scene), do_id_user);
}
static void collection_object_add(const Scene *scene, SceneCollection *sc, Object *ob)
{
BLI_addtail(&sc->objects, BLI_genericNodeN(ob));
id_us_plus((ID *)ob);
BKE_layer_sync_object_link(scene, sc, ob);
}
/**
* Add object to collection
*/
void BKE_collection_object_add(const Scene *scene, SceneCollection *sc, Object *ob)
{
if (BLI_findptr(&sc->objects, ob, offsetof(LinkData, data))) {
/* don't add the same object twice */
return;
}
collection_object_add(scene, sc, ob);
}
/**
* 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, sc, ob_dst);
}
}
FOREACH_SCENE_COLLECTION_END
for (SceneLayer *sl = scene->render_layers.first; sl; sl = sl->next) {
Base *base_src = BKE_scene_layer_base_find(sl, ob_src);
if (base_src != NULL) {
if (base_src->collection_properties == NULL) {
continue;
}
Base *base_dst = BKE_scene_layer_base_find(sl, 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.
*/
void BKE_collection_object_remove(Main *bmain, const Scene *scene, SceneCollection *sc, Object *ob, const bool free_us)
{
LinkData *link = BLI_findptr(&sc->objects, ob, offsetof(LinkData, data));
if (link == NULL) {
return;
}
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(scene, sc, ob);
if (free_us) {
BKE_libblock_free_us(bmain, ob);
}
else {
id_us_min(&ob->id);
}
}
/**
* Move object from a collection into another
*/
void BKE_collection_object_move(const Scene *scene, SceneCollection *sc_dst, SceneCollection *sc_src, Object *ob)
{
BKE_collection_object_add(scene, sc_dst, ob);
BKE_collection_object_remove(NULL, scene, sc_src, ob, false);
}
/**
* Remove object from all collections of scene
*/
void BKE_collections_object_remove(Main *bmain, Scene *scene, Object *ob, const bool free_us)
{
BKE_scene_remove_rigidbody_object(scene, ob);
FOREACH_SCENE_COLLECTION(scene, sc)
{
BKE_collection_object_remove(bmain, scene, sc, ob, free_us);
}
FOREACH_SCENE_COLLECTION_END
}
/* ---------------------------------------------------------------------- */
/* 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 Scene *scene, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* Find the SceneCollection the sc_src belongs to */
SceneCollection *sc_master = BKE_collection_master(scene);
/* 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(scene, sc_src_parent);
BKE_layer_collection_resync(scene, sc_dst_parent);
return true;
}
bool BKE_collection_move_below(const Scene *scene, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* Find the SceneCollection the sc_src belongs to */
SceneCollection *sc_master = BKE_collection_master(scene);
/* 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(scene, sc_src_parent);
BKE_layer_collection_resync(scene, sc_dst_parent);
return true;
}
bool BKE_collection_move_into(const Scene *scene, SceneCollection *sc_dst, SceneCollection *sc_src)
{
/* Find the SceneCollection the sc_src belongs to */
SceneCollection *sc_master = BKE_collection_master(scene);
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(scene, sc_src_parent);
BKE_layer_collection_resync(scene, sc_dst);
return true;
}
/* ---------------------------------------------------------------------- */
/* Iteractors */
/* scene collection iteractor */
typedef struct SceneCollectionsIteratorData {
Scene *scene;
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(Scene *scene, SceneCollection ***collections_array, int *tot)
{
SceneCollection *sc = BKE_collection_master(scene);
SceneCollection **array;
*collections_array = NULL;
*tot = 0;
if (scene == NULL)
return;
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)
{
Scene *scene = data_in;
SceneCollectionsIteratorData *data = MEM_callocN(sizeof(SceneCollectionsIteratorData), __func__);
data->scene = scene;
iter->data = data;
scene_collections_array(scene, (SceneCollection ***)&data->array, &data->tot);
BLI_assert(data->tot != 0);
data->cur = 0;
iter->current = data->array[data->cur];
iter->valid = true;
}
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;
iter->current = sc->objects.first ? ((LinkData *)sc->objects.first)->data : NULL;
iter->valid = true;
if (iter->current == NULL) {
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);
}
}