From 61e7026e272b5627df97d17ba2941336a4b9b241 Mon Sep 17 00:00:00 2001 From: Monique Date: Fri, 14 Oct 2022 11:07:57 +0200 Subject: Added method to link multiple objects to a collection. Refactored the findptr method to use a gset to check whether the object is already in the collection. --- source/blender/blenkernel/BKE_collection.h | 15 ++- source/blender/blenkernel/intern/collection.c | 119 ++++++++++++++------- source/blender/blenkernel/intern/object.cc | 2 +- source/blender/blenloader/intern/versioning_280.c | 6 +- .../blender/python/intern/bpy_rna_id_collection.c | 91 ++++++++++++++++ .../blender/python/intern/bpy_rna_id_collection.h | 1 + source/blender/python/intern/bpy_rna_types_capi.c | 17 +++ 7 files changed, 206 insertions(+), 45 deletions(-) diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index dd7866d83e5..0b2603cc240 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -117,6 +117,15 @@ bool BKE_collection_object_add(struct Main *bmain, struct Collection *collection, struct Object *ob); +/** + * Add multiple objects to given collection, ensuring this collection is 'editable' (i.e. local and + * not a liboverride), and finding a suitable parent one otherwise. + */ +bool BKE_collection_object_add_multiple(struct Main *bmain, + struct Collection *collection, + struct Object **objects, + int ob_len); + /** * Add object to given collection, similar to #BKE_collection_object_add. * @@ -126,7 +135,8 @@ bool BKE_collection_object_add(struct Main *bmain, bool BKE_collection_viewlayer_object_add(struct Main *bmain, const struct ViewLayer *view_layer, struct Collection *collection, - struct Object *ob); + struct Object **objects, + int objects_len); /** * Same as #BKE_collection_object_add, but unconditionally adds the object to the given collection. @@ -135,7 +145,8 @@ bool BKE_collection_viewlayer_object_add(struct Main *bmain, */ bool BKE_collection_object_add_notest(struct Main *bmain, struct Collection *collection, - struct Object *ob); + struct Object **objects, + int objects_len); /** * Add \a ob_dst to all scene collections that reference object \a ob_src is in. * Used for copying objects. diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 751b5185e39..42b709104f3 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -59,8 +59,12 @@ static bool collection_child_add(Collection *parent, 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_add(Main *bmain, + Collection *collection, + Object **objects, + int objects_len, + int flag, + const bool add_us); static bool collection_object_remove(Main *bmain, Collection *collection, Object *ob, @@ -125,7 +129,7 @@ static void collection_copy_data(Main *bmain, ID *id_dst, const ID *id_src, cons collection_child_add(collection_dst, child->collection, flag, false); } LISTBASE_FOREACH (CollectionObject *, cob, &collection_src->gobject) { - collection_object_add(bmain, collection_dst, cob->ob, flag, false); + collection_object_add(bmain, collection_dst, &cob->ob, 1, flag, false); } } @@ -554,7 +558,7 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy) /* Link child object into parent collections. */ LISTBASE_FOREACH (CollectionParent *, cparent, &collection->parents) { Collection *parent = cparent->collection; - collection_object_add(bmain, parent, cob->ob, 0, true); + collection_object_add(bmain, parent, &cob->ob, 1, 0, true); } /* Remove child object. */ @@ -657,7 +661,7 @@ static Collection *collection_duplicate_recursive(Main *bmain, continue; } - collection_object_add(bmain, collection_new, ob_new, 0, true); + collection_object_add(bmain, collection_new, &ob_new, 1, 0, true); collection_object_remove(bmain, collection_new, ob_old, false); } } @@ -1066,40 +1070,65 @@ static Collection *collection_parent_editable_find_recursive(const ViewLayer *vi return NULL; } -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) || - ob->instance_collection == collection) { - return false; +static bool collection_object_add(Main *bmain, + Collection *collection, + Object **objects, + int objects_len, + int flag, + const bool add_us) +{ + bool result = true; + GSet *collection_objects = NULL; + + for (int i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + if (ob == NULL) { + result = false; + continue; + } + if (ob->instance_collection) { + /* Cyclic dependency check. */ + if (collection_find_child_recursive(ob->instance_collection, collection) || + ob->instance_collection == collection) { + result = false; + continue; + } } - } - CollectionObject *cob = BLI_findptr(&collection->gobject, ob, offsetof(CollectionObject, ob)); - if (cob) { - return false; - } + if (collection_objects == NULL) { + collection_objects = BLI_gset_ptr_new(__func__); + LISTBASE_FOREACH (CollectionObject *, collection_object, &collection->gobject) { + BLI_gset_insert(collection_objects, collection_object->ob); + } + } - cob = MEM_callocN(sizeof(CollectionObject), __func__); - cob->ob = ob; - BLI_addtail(&collection->gobject, cob); - BKE_collection_object_cache_free(collection); + if (!BLI_gset_add(collection_objects, ob)) { + result = false; + continue; + } - if (add_us && (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { - id_us_plus(&ob->id); - } + CollectionObject *cob = MEM_callocN(sizeof(CollectionObject), __func__); + cob->ob = ob; + BLI_addtail(&collection->gobject, cob); + BKE_collection_object_cache_free(collection); - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - collection_tag_update_parent_recursive(bmain, collection, ID_RECALC_COPY_ON_WRITE); - } + if (add_us && (flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { + id_us_plus(&ob->id); + } - if ((flag & LIB_ID_CREATE_NO_MAIN) == 0) { - BKE_rigidbody_main_collection_object_add(bmain, collection, ob); + 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; + if (collection_objects) { + BLI_gset_free(collection_objects, NULL); + } + return result; } static bool collection_object_remove(Main *bmain, @@ -1127,9 +1156,12 @@ static bool collection_object_remove(Main *bmain, return true; } -bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Object *ob) +bool BKE_collection_object_add_notest(Main *bmain, + Collection *collection, + Object **objects, + int objects_len) { - if (ob == NULL) { + if (objects == NULL) { return false; } @@ -1140,7 +1172,7 @@ bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Objec return false; } - if (!collection_object_add(bmain, collection, ob, 0, true)) { + if (!collection_object_add(bmain, collection, objects, objects_len, 0, true)) { return false; } @@ -1155,13 +1187,22 @@ bool BKE_collection_object_add_notest(Main *bmain, Collection *collection, Objec bool BKE_collection_object_add(Main *bmain, Collection *collection, Object *ob) { - return BKE_collection_viewlayer_object_add(bmain, NULL, collection, ob); + return BKE_collection_object_add_multiple(bmain, collection, &ob, 1); +} + +bool BKE_collection_object_add_multiple(Main *bmain, + Collection *collection, + Object **objects, + int objects_len) +{ + return BKE_collection_viewlayer_object_add(bmain, NULL, collection, objects, objects_len); } bool BKE_collection_viewlayer_object_add(Main *bmain, const ViewLayer *view_layer, Collection *collection, - Object *ob) + Object **objects, + int objects_len) { if (collection == NULL) { return false; @@ -1173,7 +1214,7 @@ bool BKE_collection_viewlayer_object_add(Main *bmain, return false; } - return BKE_collection_object_add_notest(bmain, collection, ob); + return BKE_collection_object_add_notest(bmain, collection, objects, objects_len); } void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, Object *ob_dst) @@ -1183,7 +1224,7 @@ void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, O FOREACH_SCENE_COLLECTION_BEGIN (scene, collection) { if (!ID_IS_LINKED(collection) && !ID_IS_OVERRIDE_LIBRARY(collection) && BKE_collection_has_object(collection, ob_src)) { - collection_object_add(bmain, collection, ob_dst, 0, true); + collection_object_add(bmain, collection, &ob_dst, 1, 0, true); is_instantiated = true; } } @@ -1192,7 +1233,7 @@ void BKE_collection_object_add_from(Main *bmain, Scene *scene, Object *ob_src, O 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); + collection_object_add(bmain, scene->master_collection, &ob_dst, 1, 0, true); } BKE_main_collection_sync(bmain); diff --git a/source/blender/blenkernel/intern/object.cc b/source/blender/blenkernel/intern/object.cc index 2d949fb5c65..42a1b869ce8 100644 --- a/source/blender/blenkernel/intern/object.cc +++ b/source/blender/blenkernel/intern/object.cc @@ -2276,7 +2276,7 @@ Object *BKE_object_add( Object *ob = object_add_common(bmain, scene, view_layer, type, name); LayerCollection *layer_collection = BKE_layer_collection_get_active(view_layer); - BKE_collection_viewlayer_object_add(bmain, view_layer, layer_collection->collection, ob); + BKE_collection_viewlayer_object_add(bmain, view_layer, layer_collection->collection, &ob, 1); /* NOTE: There is no way to be sure that #BKE_collection_viewlayer_object_add will actually * manage to find a valid collection in given `view_layer` to add the new object to. */ diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 1a8fec49516..98f5d508fd4 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -337,7 +337,7 @@ static void do_version_scene_collection_convert( LISTBASE_FOREACH (LinkData *, link, &sc->objects) { Object *ob = link->data; if (ob) { - BKE_collection_object_add_notest(bmain, collection, ob); + BKE_collection_object_add_notest(bmain, collection, &ob, 1); id_us_min(&ob->id); } } @@ -447,7 +447,7 @@ static void do_version_layers_to_collections(Main *bmain, Scene *scene) /* Note usually this would do slow collection syncing for view layers, * but since no view layers exists yet at this point it's fast. */ - BKE_collection_object_add_notest(bmain, collections[layer], base->object); + BKE_collection_object_add_notest(bmain, collections[layer], &base->object, 1); } if (base->flag & SELECT) { @@ -1218,7 +1218,7 @@ void do_versions_after_linking_280(Main *bmain, ReportList *UNUSED(reports)) (*collection_hidden)->flag |= COLLECTION_HIDE_VIEWPORT | COLLECTION_HIDE_RENDER; } - BKE_collection_object_add_notest(bmain, *collection_hidden, ob); + BKE_collection_object_add_notest(bmain, *collection_hidden, &ob, 1); BKE_collection_object_remove(bmain, collection, ob, true); } } diff --git a/source/blender/python/intern/bpy_rna_id_collection.c b/source/blender/python/intern/bpy_rna_id_collection.c index 766c74c0bbc..10b7ba50988 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.c +++ b/source/blender/python/intern/bpy_rna_id_collection.c @@ -14,14 +14,19 @@ #include "BLI_bitmap.h" #include "BLI_utildefines.h" +#include "BKE_collection.h" #include "BKE_global.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_build.h" + #include "DNA_ID.h" /* Those following are only to support hack of not listing some internal * 'backward' pointers in generated user_map. */ +#include "DNA_collection_types.h" #include "DNA_key_types.h" #include "DNA_object_types.h" @@ -413,6 +418,86 @@ static PyObject *bpy_orphans_purge(PyObject *UNUSED(self), PyObject *args, PyObj return PyLong_FromSize_t(num_datablocks_deleted); } +PyDoc_STRVAR(bpy_link_multiple_doc, + ".. method:: link_multiple(objects)\n" + "\n" + " Link multiple objects at once.\n" + "\n" + " Note that this function is quicker than individual calls to :func:`link()` " + "(from :class:`bpy.types.CollectionObjects`)\n" + "\n" + " :arg objects: Iterables of Objects.\n" + " :type subset: sequence\n"); +static PyObject *bpy_link_multiple(PyObject *self, PyObject *args, PyObject *kwds) +{ + BPy_StructRNA *pyrna = (BPy_StructRNA *)self; + Collection *collection = pyrna->ptr.data; + Main *bmain = G_MAIN; /* XXX Ugly, but should work! */ + + PyObject *objects = NULL; + PyObject *ret = NULL; + + static const char *_keywords[] = {"objects", NULL}; + static _PyArg_Parser _parser = { + "O" /* `objects` */ + ":link_multiple", + _keywords, + 0, + }; + + if (!_PyArg_ParseTupleAndKeywordsFast(args, kwds, &_parser, &objects)) { + return ret; + } + + if (objects) { + PyObject *objects_fast = PySequence_Fast(objects, "link_multiple"); + if (objects_fast == NULL) { + goto error; + } + + PyObject **objects_array = PySequence_Fast_ITEMS(objects_fast); + Py_ssize_t objects_len = PySequence_Fast_GET_SIZE(objects_fast); + Object **objs = MEM_malloc_arrayN(objects_len, sizeof(Object *), __func__); + + for (int i = 0; i < objects_len; i++) { + ID *id; + if (!pyrna_id_FromPyObject(objects_array[i], &id)) { + PyErr_Format(PyExc_TypeError, + "Expected an ID type, not %.200s", + Py_TYPE(objects_array[i])->tp_name); + Py_DECREF(objects_fast); + MEM_freeN(objs); + goto error; + } + if (id == NULL || GS(id->name) != ID_OB) { + PyErr_Format(PyExc_TypeError, "Expected an Object type"); + Py_DECREF(objects_fast); + MEM_freeN(objs); + goto error; + } + Object *obj = (Object *)id; + objs[i] = obj; + } + Py_DECREF(objects_fast); + for (int j = 0; j < objects_len; j++) { + BKE_collection_object_add(bmain, collection, objs[j]); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, &objs[j]->id); + } + MEM_freeN(objs); + } + else { + goto error; + } + Py_INCREF(Py_None); + ret = Py_None; + + DEG_id_tag_update(&collection->id, ID_RECALC_COPY_ON_WRITE); + DEG_relations_tag_update(bmain); + +error: + return ret; +} + PyMethodDef BPY_rna_id_collection_user_map_method_def = { "user_map", (PyCFunction)bpy_user_map, @@ -431,3 +516,9 @@ PyMethodDef BPY_rna_id_collection_orphans_purge_method_def = { METH_STATIC | METH_VARARGS | METH_KEYWORDS, bpy_orphans_purge_doc, }; +PyMethodDef BPY_rna_id_collection_objects_link_multiple_method_def = { + "link_multiple", + (PyCFunction)bpy_link_multiple, + METH_VARARGS | METH_KEYWORDS, + bpy_link_multiple_doc, +}; diff --git a/source/blender/python/intern/bpy_rna_id_collection.h b/source/blender/python/intern/bpy_rna_id_collection.h index d06addc24e3..571d34c4dbb 100644 --- a/source/blender/python/intern/bpy_rna_id_collection.h +++ b/source/blender/python/intern/bpy_rna_id_collection.h @@ -13,6 +13,7 @@ extern "C" { extern PyMethodDef BPY_rna_id_collection_user_map_method_def; extern PyMethodDef BPY_rna_id_collection_batch_remove_method_def; extern PyMethodDef BPY_rna_id_collection_orphans_purge_method_def; +extern PyMethodDef BPY_rna_id_collection_objects_link_multiple_method_def; #ifdef __cplusplus } diff --git a/source/blender/python/intern/bpy_rna_types_capi.c b/source/blender/python/intern/bpy_rna_types_capi.c index 2b830eb9ffe..3481995a472 100644 --- a/source/blender/python/intern/bpy_rna_types_capi.c +++ b/source/blender/python/intern/bpy_rna_types_capi.c @@ -100,6 +100,17 @@ static struct PyMethodDef pyrna_text_methods[] = { /** \} */ +/* -------------------------------------------------------------------- */ +/** \name Collection Objects + * \{ */ + +static struct PyMethodDef pyrna_collection_objects_methods[] = { + {NULL, NULL, 0, NULL}, /* #BPY_rna_id_collection_objects_link_multiple_method_def */ + {NULL, NULL, 0, NULL}, +}; + +/** \} */ + /* -------------------------------------------------------------------- */ /** \name Window Manager Clipboard Property * @@ -275,6 +286,12 @@ void BPY_rna_types_extend_capi(void) ARRAY_SET_ITEMS(pyrna_context_methods, BPY_rna_context_temp_override_method_def); pyrna_struct_type_extend_capi(&RNA_Context, pyrna_context_methods, NULL); + + /* Collection Objects */ + ARRAY_SET_ITEMS(pyrna_collection_objects_methods, + BPY_rna_id_collection_objects_link_multiple_method_def); + BLI_assert(ARRAY_SIZE(pyrna_collection_objects_methods) == 2); + pyrna_struct_type_extend_capi(&RNA_CollectionObjects, pyrna_collection_objects_methods, NULL); } /** \} */ -- cgit v1.2.3