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
This commit is contained in:
@@ -157,6 +157,17 @@ class OUTLINER_MT_collection_view_layer(Menu):
|
||||
layout.operator("outliner.collection_holdout_clear")
|
||||
|
||||
|
||||
class OUTLINER_MT_collection_duplicate(Menu):
|
||||
bl_label = "Duplicate"
|
||||
|
||||
def draw(self, context):
|
||||
layout = self.layout
|
||||
|
||||
layout.operator("outliner.collection_duplicate", text="Collection")
|
||||
layout.operator("outliner.collection_duplicate_hierarchy", text="Hierarchy")
|
||||
layout.operator("outliner.collection_duplicate_linked_hierarchy", text="Linked Hierarchy")
|
||||
|
||||
|
||||
class OUTLINER_MT_collection_visibility(Menu):
|
||||
bl_label = "Visibility"
|
||||
|
||||
@@ -192,7 +203,7 @@ class OUTLINER_MT_collection(Menu):
|
||||
space = context.space_data
|
||||
|
||||
layout.operator("outliner.collection_new", text="New").nested = True
|
||||
layout.operator("outliner.collection_duplicate", text="Duplicate")
|
||||
layout.menu("OUTLINER_MT_collection_duplicate")
|
||||
|
||||
layout.separator()
|
||||
|
||||
@@ -345,6 +356,7 @@ classes = (
|
||||
OUTLINER_MT_editor_menus,
|
||||
OUTLINER_MT_edit_datablocks,
|
||||
OUTLINER_MT_collection,
|
||||
OUTLINER_MT_collection_duplicate,
|
||||
OUTLINER_MT_collection_new,
|
||||
OUTLINER_MT_collection_visibility,
|
||||
OUTLINER_MT_collection_view_layer,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -2208,20 +2208,14 @@ void OBJECT_OT_convert(wmOperatorType *ot)
|
||||
/* Does set ID->newid pointers. */
|
||||
static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, int dupflag)
|
||||
{
|
||||
#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++; }
|
||||
|
||||
Base *base, *basen = NULL;
|
||||
Material ***matarar;
|
||||
Object *obn;
|
||||
ID *id;
|
||||
int a, didit;
|
||||
|
||||
if (ob->mode & OB_MODE_POSE) {
|
||||
; /* nothing? */
|
||||
}
|
||||
else {
|
||||
obn = ID_NEW_SET(ob, BKE_object_copy(bmain, ob));
|
||||
obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag));
|
||||
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
|
||||
|
||||
base = BKE_view_layer_base_find(view_layer, ob);
|
||||
@@ -2249,214 +2243,8 @@ static Base *object_add_duplicate_internal(Main *bmain, Scene *scene, ViewLayer
|
||||
BKE_collection_object_add(bmain, collection, obn);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return basen;
|
||||
|
||||
#undef ID_NEW_REMAP_US
|
||||
#undef ID_NEW_REMAP_US2
|
||||
}
|
||||
|
||||
/* single object duplicate, if dupflag==0, fully linked, else it uses the flags given */
|
||||
|
||||
@@ -448,6 +448,8 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
|
||||
Main *bmain = CTX_data_main(C);
|
||||
SpaceOutliner *soops = CTX_wm_space_outliner(C);
|
||||
TreeElement *te = outliner_active_collection(C);
|
||||
bool hierarchy = strstr(op->idname, "hierarchy") != NULL;
|
||||
bool linked = strstr(op->idname, "linked") != NULL;
|
||||
|
||||
/* Can happen when calling from a key binding. */
|
||||
if (te == NULL) {
|
||||
@@ -467,7 +469,7 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
|
||||
case SO_SCENES:
|
||||
case SO_VIEW_LAYER:
|
||||
case SO_LIBRARIES:
|
||||
BKE_collection_copy(bmain, parent, collection);
|
||||
BKE_collection_duplicate(bmain, parent, collection, hierarchy, !linked);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -482,7 +484,37 @@ void OUTLINER_OT_collection_duplicate(wmOperatorType *ot)
|
||||
/* identifiers */
|
||||
ot->name = "Duplicate Collection";
|
||||
ot->idname = "OUTLINER_OT_collection_duplicate";
|
||||
ot->description = "Duplicate selected collections";
|
||||
ot->description = "Make a new collection with linked content (collection and objects)";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_duplicate_exec;
|
||||
ot->poll = ED_outliner_collections_editor_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
void OUTLINER_OT_collection_duplicate_hierarchy(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Duplicate Collection Hierarchy";
|
||||
ot->idname = "OUTLINER_OT_collection_duplicate_hierarchy";
|
||||
ot->description = "Duplicate entire hierarchy and make all content single user";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_duplicate_exec;
|
||||
ot->poll = ED_outliner_collections_editor_poll;
|
||||
|
||||
/* flags */
|
||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||
}
|
||||
|
||||
void OUTLINER_OT_collection_duplicate_linked_hierarchy(wmOperatorType *ot)
|
||||
{
|
||||
/* identifiers */
|
||||
ot->name = "Duplicate Linked Collection Hierarchy";
|
||||
ot->idname = "OUTLINER_OT_collection_duplicate_linked_hierarchy";
|
||||
ot->description = "Duplicate entire hierarchy with linked object data";
|
||||
|
||||
/* api callbacks */
|
||||
ot->exec = collection_duplicate_exec;
|
||||
|
||||
@@ -316,6 +316,8 @@ struct Collection *outliner_collection_from_tree_element(const TreeElement *te);
|
||||
|
||||
void OUTLINER_OT_collection_new(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_duplicate(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_duplicate_hierarchy(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_duplicate_linked_hierarchy(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_delete(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_objects_select(struct wmOperatorType *ot);
|
||||
void OUTLINER_OT_collection_objects_deselect(struct wmOperatorType *ot);
|
||||
|
||||
@@ -94,6 +94,8 @@ void outliner_operatortypes(void)
|
||||
/* collections */
|
||||
WM_operatortype_append(OUTLINER_OT_collection_new);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_duplicate);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_duplicate_hierarchy);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_duplicate_linked_hierarchy);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_delete);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_objects_select);
|
||||
WM_operatortype_append(OUTLINER_OT_collection_objects_deselect);
|
||||
|
||||
Reference in New Issue
Block a user