diff options
author | Dalai Felinto <dfelinto@gmail.com> | 2019-02-26 22:15:30 +0300 |
---|---|---|
committer | Dalai Felinto <dfelinto@gmail.com> | 2019-03-01 17:44:19 +0300 |
commit | e7ea99af1b0d933b9323be39d8b1c6f683bfba52 (patch) | |
tree | 461d23594737af40f62f533dbe357d2264134d1d /source/blender/blenkernel | |
parent | 619c65a4b3372b45a8d6d7317672c48d248104ca (diff) |
Outliner: Collection - Duplicate Hierarchy, and Duplicate Linked Hierarchy
As per the suggestion on T57064, this introduces two new options to duplicate collections.
We then have:
* Duplicate > Collection (New collection with linked content).
* Duplicate > Hierachy (Duplicate entire hierarchy and make all contents single user).
* Duplicate > Linked Hierarchy (Duplicate entire hierarchy keeping content linked with original).
Development TODO: `single_object_users` can/should use the new functions.
Reviewers: brecht, mont29
Subscribers: pablovazquez, billreynish, JulienKaspar
Differential Revision: https://developer.blender.org/D4394
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_collection.h | 6 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_object.h | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/collection.c | 65 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/object.c | 234 |
4 files changed, 302 insertions, 5 deletions
diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 2e4d2a7bf2c..5e6f87e7f97 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -54,11 +54,13 @@ void BKE_collection_free(struct Collection *collection); bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bool hierarchy); struct Collection *BKE_collection_copy(struct Main *bmain, struct Collection *parent, struct Collection *collection); -struct Collection *BKE_collection_copy_master(struct Main *bmain, struct Collection *collection, const int flag); void BKE_collection_copy_data(struct Main *bmain, struct Collection *collection_dst, const struct Collection *collection_src, const int flag); -void BKE_collection_copy_full(struct Main *bmain, struct Collection *collection); void BKE_collection_make_local(struct Main *bmain, struct Collection *collection, const bool lib_local); +struct Collection *BKE_collection_duplicate(struct Main *bmain, struct Collection *parent, struct Collection *collection, const bool do_hierarchy, const bool do_deep_copy); +struct Collection *BKE_collection_copy_master(struct Main *bmain, struct Collection *collection, const int flag); +void BKE_collection_copy_full(struct Main *bmain, struct Collection *collection); + /* Master Collection for Scene */ struct Collection *BKE_collection_master(const struct Scene *scene); diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index 58d648b4aa4..32bc2f03b9e 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -126,6 +126,8 @@ void BKE_object_make_local_ex(struct Main *bmain, struct Object *ob, const bool bool BKE_object_is_libdata(const struct Object *ob); bool BKE_object_obdata_is_libdata(const struct Object *ob); +struct Object *BKE_object_duplicate(struct Main *bmain, const struct Object *ob, const int dupflag); + void BKE_object_obdata_size_init(struct Object *ob, const float scale); void BKE_object_scale_to_mat3(struct Object *ob, float mat[3][3]); diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 37ee6fbeaae..ce7a5b141a4 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -209,17 +209,72 @@ void BKE_collection_copy_data( } } +static void collection_duplicate_recursive(Main *bmain, GHash *visited, Collection *collection, const int dupflag) +{ + const bool is_first_run = (visited == NULL); + if (is_first_run) { + visited = BLI_ghash_ptr_new(__func__); + BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false); + } + + if (collection->id.tag & LIB_TAG_DOIT) { + return; + } + collection->id.tag |= LIB_TAG_DOIT; + + ListBase collection_object_list = {NULL, NULL}; + BLI_duplicatelist(&collection_object_list, &collection->gobject); + for (CollectionObject *cob = collection_object_list.first; cob; cob = cob->next) { + Object *ob_old = cob->ob; + Object *ob_new = NULL; + void **ob_key_p, **ob_value_p; + + if (!BLI_ghash_ensure_p_ex(visited, ob_old, &ob_key_p, &ob_value_p)) { + ob_new = BKE_object_duplicate(bmain, ob_old, dupflag); + *ob_key_p = ob_old; + *ob_value_p = ob_new; + } + else { + ob_new = *ob_value_p; + } + + collection_object_add(bmain, collection, ob_new, 0, true); + collection_object_remove(bmain, collection, ob_old, false); + } + BLI_freelistN(&collection_object_list); + + ListBase collection_child_list = {NULL, NULL}; + BLI_duplicatelist(&collection_child_list, &collection->children); + for (CollectionChild *child = collection_child_list.first; child; child = child->next) { + Collection *child_collection_old = child->collection; + Collection *child_collection_new = BKE_collection_copy(bmain, collection, child_collection_old); + + collection_duplicate_recursive(bmain, visited, child_collection_new, dupflag); + collection_child_remove(collection, child_collection_old); + } + BLI_freelistN(&collection_child_list); + + if (is_first_run) { + BLI_ghash_free(visited, NULL, NULL); + } +} + /** * Makes a shallow copy of a Collection * - * Add a new collection in the same level as the old one, copy any nested collections - * but link the objects to the new collection (as oppose to copy them). + * 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 oppose to copy them). */ Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *collection) { + return BKE_collection_duplicate(bmain, parent, collection, false, false); +} + +Collection *BKE_collection_duplicate(Main *bmain, Collection *parent, Collection *collection, const bool do_hierarchy, const bool do_deep_copy) +{ /* It's not allowed to copy the master collection. */ if (collection->flag & COLLECTION_IS_MASTER) { - BLI_assert("!Master collection can't be copied"); + BLI_assert("!Master collection can't be duplicated"); return NULL; } @@ -241,6 +296,10 @@ Collection *BKE_collection_copy(Main *bmain, Collection *parent, Collection *col } } + if (do_hierarchy) { + collection_duplicate_recursive(bmain, NULL, collection_new, (do_deep_copy) ? U.dupflag : 0); + } + BKE_main_collection_sync(bmain); return collection_new; diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 3c57a5f7086..c4d80fdd5e2 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -1427,6 +1427,240 @@ Object *BKE_object_copy(Main *bmain, const Object *ob) return ob_copy; } +Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag) +{ + Material ***matarar; + ID *id; + int a, didit; + Object *obn = BKE_object_copy(bmain, ob); + + /* 0 == full linked. */ + if (dupflag == 0) { + return obn; + } + +#define ID_NEW_REMAP_US(a) if ( (a)->id.newid) { (a) = (void *)(a)->id.newid; (a)->id.us++; } +#define ID_NEW_REMAP_US2(a) if (((ID *)a)->newid) { (a) = ((ID *)a)->newid; ((ID *)a)->us++; } + + /* duplicates using userflags */ + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &obn->id, true); + } + + if (dupflag & USER_DUP_MAT) { + for (a = 0; a < obn->totcol; a++) { + id = (ID *)obn->mat[a]; + if (id) { + ID_NEW_REMAP_US(obn->mat[a]) + else { + obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a])); + } + id_us_min(id); + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true); + } + } + } + } + if (dupflag & USER_DUP_PSYS) { + ParticleSystem *psys; + for (psys = obn->particlesystem.first; psys; psys = psys->next) { + id = (ID *) psys->part; + if (id) { + ID_NEW_REMAP_US(psys->part) + else { + psys->part = ID_NEW_SET(psys->part, BKE_particlesettings_copy(bmain, psys->part)); + } + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, &psys->part->id, true); + } + + id_us_min(id); + } + } + } + + id = obn->data; + didit = 0; + + switch (obn->type) { + case OB_MESH: + if (dupflag & USER_DUP_MESH) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_CURVE: + if (dupflag & USER_DUP_CURVE) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_SURF: + if (dupflag & USER_DUP_SURF) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_FONT: + if (dupflag & USER_DUP_FONT) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_MBALL: + if (dupflag & USER_DUP_MBALL) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LAMP: + if (dupflag & USER_DUP_LAMP) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_ARMATURE: + DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY); + if (obn->pose) + BKE_pose_tag_recalc(bmain, obn->pose); + if (dupflag & USER_DUP_ARM) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); + BKE_pose_rebuild(bmain, obn, obn->data, true); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LATTICE: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_CAMERA: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_LIGHTPROBE: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_SPEAKER: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + case OB_GPENCIL: + if (dupflag != 0) { + ID_NEW_REMAP_US2(obn->data) + else { + obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); + didit = 1; + } + id_us_min(id); + } + break; + } + + /* Check if obdata is copied. */ + if (didit) { + Key *key = BKE_key_from_object(obn); + + Key *oldkey = BKE_key_from_object(ob); + if (oldkey != NULL) { + ID_NEW_SET(oldkey, key); + } + + if (dupflag & USER_DUP_ACT) { + BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true); + if (key) { + BKE_animdata_copy_id_action(bmain, (ID *)key, true); + } + } + + if (dupflag & USER_DUP_MAT) { + matarar = give_matarar(obn); + if (matarar) { + for (a = 0; a < obn->totcol; a++) { + id = (ID *)(*matarar)[a]; + if (id) { + ID_NEW_REMAP_US((*matarar)[a]) + else { + (*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a])); + } + id_us_min(id); + } + } + } + } + } + +#undef ID_NEW_REMAP_US +#undef ID_NEW_REMAP_US2 + + BKE_libblock_relink_to_newid(&obn->id); + + /* DAG_relations_tag_update(bmain); */ /* caller must do */ + + if (ob->data != NULL) { + DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS); + } + + /* BKE_main_id_clear_newpoins(bmain); */ /* Called must do. */ + + return obn; +} + void BKE_object_make_local_ex(Main *bmain, Object *ob, const bool lib_local, const bool clear_proxy) { bool is_local = false, is_lib = false; |