Refactor duplicate of data-blocks.

Main change from user side, besides that all pointers should now be
properly remapped to new IDs, is that linked objects are no longer
preserved when doing a full copy of scenes.

Will open a task to check whether we actually still want that behavior
(and re-code it in a more correct way then).

This is the main part of work done here, it aims at uniformizing and
sanitizing that 'deep copy' process for supported IDs (currently scenes,
collections and objects).

Note that there will be more follow up commits after that one, but this
should be the most risky and changing one.
This commit is contained in:
Bastien Montagne 2020-06-17 15:02:04 +02:00
parent 514f80b0c4
commit ad6cccf058
Notes: blender-bot 2023-02-14 06:19:41 +01:00
Referenced by issue #78330, [Duplicate Objects] 2.90.0 Duplicating parented objects have a bug
10 changed files with 308 additions and 253 deletions

View File

@ -62,8 +62,8 @@ bool BKE_collection_delete(struct Main *bmain, struct Collection *collection, bo
struct Collection *BKE_collection_duplicate(struct Main *bmain,
struct Collection *parent,
struct Collection *collection,
const bool do_objects,
const bool do_obdata);
const uint duplicate_flags,
const uint duplicate_options);
/* Master Collection for Scene */

View File

@ -144,6 +144,16 @@ struct ID *BKE_libblock_find_name(struct Main *bmain,
const short type,
const char *name) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL();
/**
* Duplicate (a.k.a. deep copy) common processing options.
* See also eDupli_ID_Flags for options controlling what kind of IDs to duplicate.
*/
typedef enum eLibIDDuplicateFlags {
/** This call to a duplicate function is part of another call for some parent ID.
* Therefore, this sub-process should not clear `newid` pointers, nor handle remapping itself. */
LIB_ID_DUPLICATE_IS_SUBPROCESS = 1 << 0,
} eLibIDDuplicateFlags;
/* lib_remap.c (keep here since they're general functions) */
/**
* New freeing logic options.

View File

@ -138,8 +138,9 @@ 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 uint dupflag);
struct Object *ob,
const uint dupflag,
const uint duplicate_options);
void BKE_object_obdata_size_init(struct Object *ob, const float scale);

View File

@ -326,15 +326,16 @@ bool BKE_collection_delete(Main *bmain, Collection *collection, bool hierarchy)
static Collection *collection_duplicate_recursive(Main *bmain,
Collection *parent,
Collection *collection_old,
const bool do_objects,
const bool do_obdata)
const eDupli_ID_Flags duplicate_flags,
const eLibIDDuplicateFlags duplicate_options)
{
Collection *collection_new;
bool do_full_process = false;
const int object_dupflag = (do_obdata) ? U.dupflag : 0;
const bool is_collection_master = (collection_old->flag & COLLECTION_IS_MASTER) != 0;
const bool is_collection_liboverride = ID_IS_OVERRIDE_LIBRARY(collection_old);
const bool do_objects = (duplicate_flags & USER_DUP_OBJECT) != 0;
if (is_collection_master) {
/* We never duplicate master collections here, but we can still deep-copy their objects and
* collections. */
@ -391,8 +392,8 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
if (ob_new == NULL) {
ob_new = BKE_object_duplicate(bmain, ob_old, (eDupli_ID_Flags)object_dupflag);
ID_NEW_SET(ob_old, ob_new);
ob_new = BKE_object_duplicate(
bmain, ob_old, duplicate_flags, duplicate_options | LIB_ID_DUPLICATE_IS_SUBPROCESS);
}
collection_object_add(bmain, collection_new, ob_new, 0, true);
@ -410,7 +411,7 @@ static Collection *collection_duplicate_recursive(Main *bmain,
}
collection_duplicate_recursive(
bmain, collection_new, child_collection_old, do_objects, do_obdata);
bmain, collection_new, child_collection_old, duplicate_flags, duplicate_options);
collection_child_remove(collection_new, child_collection_old);
}
@ -420,7 +421,9 @@ static Collection *collection_duplicate_recursive(Main *bmain,
/**
* Make a deep copy (aka duplicate) of the given collection and all of its children, recusrsively.
*
* \warning This functions will clear all \a bmain id.idnew pointers.
* \warning This functions will clear all \a bmain #ID.idnew pointers, unless \a
* LIB_ID_DUPLICATE_IS_SUBPROCESS duplicate option is passed on, in which case caller is reponsible
* to reconstruct collection dependencies informations (i.e. call #BKE_main_collection_sync).
*
* \param do_objects: If true, it will also make copies of objects.
* \param do_obdata: If true, it will also make duplicates of objects,
@ -430,23 +433,44 @@ static Collection *collection_duplicate_recursive(Main *bmain,
Collection *BKE_collection_duplicate(Main *bmain,
Collection *parent,
Collection *collection,
const bool do_objects,
const bool do_obdata)
eDupli_ID_Flags duplicate_flags,
eLibIDDuplicateFlags duplicate_options)
{
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
if (!is_subprocess) {
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
Collection *collection_new = collection_duplicate_recursive(
bmain, parent, collection, do_objects, do_obdata);
bmain, parent, collection, duplicate_flags, duplicate_options);
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
BKE_libblock_relink_to_newid(&collection_new->id);
if (!is_subprocess) {
/* `collection_duplicate_recursive` will also tag our 'root' collection, whic is not required
* unless its duplication is a subprocess of another one. */
collection_new->id.tag &= ~LIB_TAG_NEW;
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
BKE_libblock_relink_to_newid(&collection_new->id);
BKE_main_collection_sync(bmain);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
if (id_iter->tag & LIB_TAG_NEW) {
BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
}
}
FOREACH_MAIN_ID_END;
#endif
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
BKE_main_collection_sync(bmain);
}
return collection_new;
}

View File

@ -1758,31 +1758,35 @@ Object *BKE_object_copy(Main *bmain, const Object *ob)
* \note Caller MUST free \a newid pointers itself (#BKE_main_id_clear_newpoins()) and call updates
* of DEG too (#DAG_relations_tag_update()).
*/
Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag)
Object *BKE_object_duplicate(Main *bmain,
Object *ob,
const eDupli_ID_Flags dupflag,
const eLibIDDuplicateFlags duplicate_options)
{
const bool is_subprocess = (duplicate_options & LIB_ID_DUPLICATE_IS_SUBPROCESS) != 0;
if (!is_subprocess) {
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
Material ***matarar;
ID *id;
int a, didit;
ID *id, *id_new;
int a;
const bool is_object_liboverride = ID_IS_OVERRIDE_LIBRARY(ob);
Object *obn = BKE_object_copy(bmain, ob);
Object *obn;
BKE_id_copy(bmain, &ob->id, (ID **)&obn);
id_us_min(&obn->id);
if (is_subprocess) {
ID_NEW_SET(ob, obn);
}
/* 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);
@ -1791,19 +1795,16 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag)
if (dupflag & USER_DUP_MAT) {
for (a = 0; a < obn->totcol; a++) {
id = (ID *)obn->mat[a];
if (id) {
if (id && id->newid == NULL) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
ID_NEW_REMAP_US(obn->mat[a])
else
{
obn->mat[a] = ID_NEW_SET(obn->mat[a], BKE_material_copy(bmain, obn->mat[a]));
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, &obn->mat[a]->id, true);
}
BKE_id_copy_ex(bmain, id, &id_new, 0);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, id_new, true);
}
id_us_min(id);
}
}
}
@ -1811,205 +1812,152 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag)
ParticleSystem *psys;
for (psys = obn->particlesystem.first; psys; psys = psys->next) {
id = (ID *)psys->part;
if (id) {
if (id && id->newid == NULL) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
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);
}
BKE_id_copy_ex(bmain, id, &id_new, 0);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, id_new, true);
}
id_us_min(id);
}
}
}
id = obn->data;
didit = 0;
bool duplicated_obdata = false;
if (!is_object_liboverride || !ID_IS_LINKED(id)) {
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;
if (id && id->newid == NULL) {
if (!is_object_liboverride || !ID_IS_LINKED(id)) {
switch (obn->type) {
case OB_MESH:
if (dupflag & USER_DUP_MESH) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_CURVE:
if (dupflag & USER_DUP_CURVE) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_SURF:
if (dupflag & USER_DUP_SURF) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_FONT:
if (dupflag & USER_DUP_FONT) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_MBALL:
if (dupflag & USER_DUP_MBALL) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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:
if (dupflag != 0) {
DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY);
if (obn->pose) {
BKE_pose_tag_recalc(bmain, obn->pose);
break;
case OB_LAMP:
if (dupflag & USER_DUP_LAMP) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
break;
case OB_ARMATURE:
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);
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
}
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;
break;
case OB_LATTICE:
if (dupflag != 0) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_CAMERA:
if (dupflag != 0) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
case OB_LIGHTPROBE:
if (dupflag & USER_DUP_LIGHTPROBE) {
ID_NEW_REMAP_US2(obn->data)
else
{
obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data));
didit = 1;
break;
case OB_LIGHTPROBE:
if (dupflag & USER_DUP_LIGHTPROBE) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
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;
break;
case OB_SPEAKER:
if (dupflag != 0) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
case OB_GPENCIL:
if (dupflag & USER_DUP_GPENCIL) {
ID_NEW_REMAP_US2(obn->data)
else
{
obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data));
didit = 1;
break;
case OB_GPENCIL:
if (dupflag & USER_DUP_GPENCIL) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
case OB_HAIR:
if (dupflag & USER_DUP_HAIR) {
ID_NEW_REMAP_US2(obn->data)
else
{
obn->data = ID_NEW_SET(obn->data, BKE_hair_copy(bmain, obn->data));
didit = 1;
break;
case OB_HAIR:
if (dupflag & USER_DUP_HAIR) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
case OB_POINTCLOUD:
if (dupflag & USER_DUP_POINTCLOUD) {
ID_NEW_REMAP_US2(obn->data)
else
{
obn->data = ID_NEW_SET(obn->data, BKE_pointcloud_copy(bmain, obn->data));
didit = 1;
break;
case OB_POINTCLOUD:
if (dupflag & USER_DUP_POINTCLOUD) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
case OB_VOLUME:
if (dupflag & USER_DUP_VOLUME) {
ID_NEW_REMAP_US2(obn->data)
else
{
obn->data = ID_NEW_SET(obn->data, BKE_volume_copy(bmain, obn->data));
didit = 1;
break;
case OB_VOLUME:
if (dupflag & USER_DUP_VOLUME) {
BKE_id_copy(bmain, id, &id_new);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
duplicated_obdata = true;
}
id_us_min(id);
}
break;
break;
}
}
}
/* Check if obdata is copied. */
if (didit) {
if (duplicated_obdata) {
Key *key = BKE_key_from_object(obn);
Key *oldkey = BKE_key_from_object(ob);
@ -2018,7 +1966,7 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag)
}
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, (ID *)obn->data, true);
BKE_animdata_copy_id_action(bmain, id_new, true);
if (key) {
BKE_animdata_copy_id_action(bmain, (ID *)key, true);
}
@ -2029,29 +1977,49 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const uint dupflag)
if (matarar) {
for (a = 0; a < obn->totcol; a++) {
id = (ID *)(*matarar)[a];
if (id) {
if (id && id->newid == NULL) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
ID_NEW_REMAP_US((*matarar)[a])
else
{
(*matarar)[a] = ID_NEW_SET((*matarar)[a], BKE_material_copy(bmain, (*matarar)[a]));
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, &(*matarar)[a]->id, true);
}
BKE_id_copy_ex(bmain, id, &id_new, 0);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
if (dupflag & USER_DUP_ACT) {
BKE_animdata_copy_id_action(bmain, id_new, true);
}
id_us_min(id);
}
}
}
}
}
#undef ID_NEW_REMAP_US
#undef ID_NEW_REMAP_US2
if (!is_subprocess) {
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
BKE_libblock_relink_to_newid(&obn->id);
if (ob->data != NULL) {
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those flags. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
}
FOREACH_MAIN_ID_END;
#endif
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
if (obn->type == OB_ARMATURE) {
DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY);
if (obn->pose) {
BKE_pose_tag_recalc(bmain, obn->pose);
}
// BKE_pose_rebuild(bmain, obn, obn->data, true);
}
if (obn->data != NULL) {
DEG_id_tag_update_ex(bmain, (ID *)obn->data, ID_RECALC_EDITORS);
}

View File

@ -829,41 +829,79 @@ Scene *BKE_scene_duplicate(Main *bmain, Scene *sce, eSceneCopyMethod type)
/* Extra actions, most notably SCE_FULL_COPY also duplicates several 'children' datablocks. */
if (type == SCE_COPY_FULL) {
/* Scene duplication is always root of duplication currently. */
const bool is_subprocess = false;
ID *id, *id_new;
if (!is_subprocess) {
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
}
/* Copy Freestyle LineStyle datablocks. */
LISTBASE_FOREACH (ViewLayer *, view_layer_dst, &sce_copy->view_layers) {
LISTBASE_FOREACH (
FreestyleLineSet *, lineset, &view_layer_dst->freestyle_config.linesets) {
if (lineset->linestyle) {
if (is_scene_liboverride && ID_IS_LINKED(lineset->linestyle)) {
id = &lineset->linestyle->id;
if (id && id->newid == NULL) {
if (is_scene_liboverride && ID_IS_LINKED(id)) {
continue;
}
id_us_min(&lineset->linestyle->id);
BKE_id_copy_ex(
bmain, (ID *)lineset->linestyle, (ID **)&lineset->linestyle, LIB_ID_COPY_ACTIONS);
BKE_id_copy_ex(bmain, id, &id_new, LIB_ID_COPY_ACTIONS);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
}
}
}
/* Full copy of world (included animations) */
if (sce_copy->world) {
if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->world)) {
id_us_min(&sce_copy->world->id);
BKE_id_copy_ex(
bmain, (ID *)sce_copy->world, (ID **)&sce_copy->world, LIB_ID_COPY_ACTIONS);
id = &sce->world->id;
if (id && id->newid == NULL) {
if (!is_scene_liboverride || !ID_IS_LINKED(id)) {
BKE_id_copy_ex(bmain, id, &id_new, LIB_ID_COPY_ACTIONS);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
}
}
/* Full copy of GreasePencil. */
if (sce_copy->gpd) {
if (!is_scene_liboverride || !ID_IS_LINKED(sce_copy->gpd)) {
id_us_min(&sce_copy->gpd->id);
BKE_id_copy_ex(bmain, (ID *)sce_copy->gpd, (ID **)&sce_copy->gpd, LIB_ID_COPY_ACTIONS);
id = &sce->gpd->id;
if (id && id->newid == NULL) {
if (!is_scene_liboverride || !ID_IS_LINKED(id)) {
BKE_id_copy_ex(bmain, id, &id_new, LIB_ID_COPY_ACTIONS);
id_us_min(id_new);
ID_NEW_SET(id, id_new);
}
}
/* Deep-duplicate collections and objects (using preferences' settings for which sub-data to
* duplicate along the object itself). */
BKE_collection_duplicate(bmain, NULL, sce_copy->master_collection, true, true);
BKE_collection_duplicate(bmain,
NULL,
sce_copy->master_collection,
USER_DUP_OBJECT | U.dupflag,
LIB_ID_DUPLICATE_IS_SUBPROCESS);
if (!is_subprocess) {
/* This code will follow into all ID links using an ID tagged with LIB_TAG_NEW.*/
BKE_libblock_relink_to_newid(&sce_copy->id);
#ifndef NDEBUG
/* Call to `BKE_libblock_relink_to_newid` above is supposed to have cleared all those
* flags. */
ID *id_iter;
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
BLI_assert((id_iter->tag & LIB_TAG_NEW) == 0);
}
FOREACH_MAIN_ID_END;
#endif
/* Cleanup. */
BKE_main_id_tag_all(bmain, LIB_TAG_NEW, false);
BKE_main_id_clear_newpoins(bmain);
BKE_main_collection_sync(bmain);
}
}
else {
/* Remove sequencer if not full copy */

View File

@ -2797,8 +2797,12 @@ void OBJECT_OT_convert(wmOperatorType *ot)
/* used below, assumes id.new is correct */
/* leaves selection of base/object unaltered */
/* Does set ID->newid pointers. */
static Base *object_add_duplicate_internal(
Main *bmain, Scene *scene, ViewLayer *view_layer, Object *ob, const eDupli_ID_Flags dupflag)
static Base *object_add_duplicate_internal(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
Object *ob,
const eDupli_ID_Flags dupflag,
const eLibIDDuplicateFlags duplicate_options)
{
Base *base, *basen = NULL;
Object *obn;
@ -2807,7 +2811,7 @@ static Base *object_add_duplicate_internal(
/* nothing? */
}
else {
obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag));
obn = ID_NEW_SET(ob, BKE_object_duplicate(bmain, ob, dupflag, duplicate_options));
DEG_id_tag_update(&obn->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
base = BKE_view_layer_base_find(view_layer, ob);
@ -2851,7 +2855,8 @@ Base *ED_object_add_duplicate(
Base *basen;
Object *ob;
basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag);
basen = object_add_duplicate_internal(
bmain, scene, view_layer, base->object, dupflag, LIB_ID_DUPLICATE_IS_SUBPROCESS);
if (basen == NULL) {
return NULL;
}
@ -2882,7 +2887,8 @@ static int duplicate_exec(bContext *C, wmOperator *op)
const eDupli_ID_Flags dupflag = (linked) ? 0 : (eDupli_ID_Flags)U.dupflag;
CTX_DATA_BEGIN (C, Base *, base, selected_bases) {
Base *basen = object_add_duplicate_internal(bmain, scene, view_layer, base->object, dupflag);
Base *basen = object_add_duplicate_internal(
bmain, scene, view_layer, base->object, dupflag, 0);
/* note that this is safe to do with this context iterator,
* the list is made in advance */
@ -2976,7 +2982,7 @@ static int object_add_named_exec(bContext *C, wmOperator *op)
}
/* prepare dupli */
basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag);
basen = object_add_duplicate_internal(bmain, scene, view_layer, ob, dupflag, 0);
if (basen == NULL) {
BKE_report(op->reports, RPT_ERROR, "Object could not be duplicated");

View File

@ -581,7 +581,8 @@ static int collection_duplicate_exec(bContext *C, wmOperator *op)
"it won't be linked to any view layer");
}
BKE_collection_duplicate(bmain, parent, collection, true, !linked);
const eDupli_ID_Flags dupli_flags = USER_DUP_OBJECT | (linked ? 0 : U.dupflag);
BKE_collection_duplicate(bmain, parent, collection, dupli_flags, 0);
DEG_relations_tag_update(bmain);
WM_main_add_notifier(NC_SCENE | ND_LAYER, CTX_data_scene(C));

View File

@ -1150,6 +1150,13 @@ typedef enum eDupli_ID_Flags {
USER_DUP_HAIR = (1 << 14),
USER_DUP_POINTCLOUD = (1 << 15),
USER_DUP_VOLUME = (1 << 16),
USER_DUP_OBDATA = (~0) & ((1 << 24) - 1),
/* Those are not exposed as user preferences, only used internaly. */
USER_DUP_OBJECT = (1 << 24),
/* USER_DUP_COLLECTION = (1 << 25), */ /* UNUSED, keep because we may implement. */
} eDupli_ID_Flags;
/**

View File

@ -157,10 +157,10 @@ def check_linked_scene_copying():
extern_sce = get_scene("lib.blend", "Scene_lib")
# check node's props
# we made full copy from linked scene, so pointers must equal each other
# must point to own scene camera
abort_if_false(intern_sce.node_tree.nodes['Render Layers']["prop"] and
intern_sce.node_tree.nodes['Render Layers']["prop"] ==
extern_sce.node_tree.nodes['Render Layers']["prop"])
not (intern_sce.node_tree.nodes['Render Layers']["prop"] ==
extern_sce.node_tree.nodes['Render Layers']["prop"]))
def check_scene_copying():