Enable (deep) copy of overrides.

This commit enables basic copy of overrides on generic ID level, as well
as from (deep) copy operators for objects and collections.

So e.g. if your linked overridden caracter is in a collection, you can
now (from the outliner) Duplicate that override collection to get a new
overriding copy of the character.

We still need operators (new or modifying existing ones) to handle that
from 3DView e.g.

Note that deep copy code for objects/collections (and incidently
animdata) had to be modified to avoid duplicating/making local IDs that
remain linked ones being used by overrides ones.
This commit is contained in:
Bastien Montagne 2020-06-10 16:43:25 +02:00
parent 1d0017089c
commit 74ec37b70c
6 changed files with 204 additions and 178 deletions

View File

@ -56,7 +56,9 @@ bool BKE_lib_override_library_is_enabled(void);
struct IDOverrideLibrary *BKE_lib_override_library_init(struct ID *local_id, struct IDOverrideLibrary *BKE_lib_override_library_init(struct ID *local_id,
struct ID *reference_id); struct ID *reference_id);
void BKE_lib_override_library_copy(struct ID *dst_id, const struct ID *src_id); void BKE_lib_override_library_copy(struct ID *dst_id,
const struct ID *src_id,
const bool do_full_copy);
void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, const bool do_id_user); void BKE_lib_override_library_clear(struct IDOverrideLibrary *override, const bool do_id_user);
void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user); void BKE_lib_override_library_free(struct IDOverrideLibrary **override, const bool do_id_user);

View File

@ -381,14 +381,15 @@ bool BKE_animdata_copy_id(Main *bmain, ID *id_to, ID *id_from, const int flag)
void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid) void BKE_animdata_copy_id_action(Main *bmain, ID *id, const bool set_newid)
{ {
const bool is_id_liboverride = ID_IS_OVERRIDE_LIBRARY(id);
AnimData *adt = BKE_animdata_from_id(id); AnimData *adt = BKE_animdata_from_id(id);
if (adt) { if (adt) {
if (adt->action) { if (adt->action && (!is_id_liboverride || !ID_IS_LINKED(adt->action))) {
id_us_min((ID *)adt->action); id_us_min((ID *)adt->action);
adt->action = set_newid ? ID_NEW_SET(adt->action, BKE_action_copy(bmain, adt->action)) : adt->action = set_newid ? ID_NEW_SET(adt->action, BKE_action_copy(bmain, adt->action)) :
BKE_action_copy(bmain, adt->action); BKE_action_copy(bmain, adt->action);
} }
if (adt->tmpact) { if (adt->tmpact && (!is_id_liboverride || !ID_IS_LINKED(adt->tmpact))) {
id_us_min((ID *)adt->tmpact); id_us_min((ID *)adt->tmpact);
adt->tmpact = set_newid ? ID_NEW_SET(adt->tmpact, BKE_action_copy(bmain, adt->tmpact)) : adt->tmpact = set_newid ? ID_NEW_SET(adt->tmpact, BKE_action_copy(bmain, adt->tmpact)) :
BKE_action_copy(bmain, adt->tmpact); BKE_action_copy(bmain, adt->tmpact);

View File

@ -314,6 +314,7 @@ static Collection *collection_duplicate_recursive(Main *bmain,
Collection *collection_new; Collection *collection_new;
bool do_full_process = false; bool do_full_process = false;
const int object_dupflag = (do_obdata) ? U.dupflag : 0; const int object_dupflag = (do_obdata) ? U.dupflag : 0;
const bool is_collection_liboverride = ID_IS_OVERRIDE_LIBRARY(collection_old);
if (!do_hierarchy || collection_old->id.newid == NULL) { if (!do_hierarchy || collection_old->id.newid == NULL) {
BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new); BKE_id_copy(bmain, &collection_old->id, (ID **)&collection_new);
@ -359,6 +360,12 @@ static Collection *collection_duplicate_recursive(Main *bmain,
Object *ob_old = cob->ob; Object *ob_old = cob->ob;
Object *ob_new = (Object *)ob_old->id.newid; Object *ob_new = (Object *)ob_old->id.newid;
/* If collection is an override, we do not want to duplicate any linked data-block, as that
* would generate a purely local data. */
if (is_collection_liboverride && ID_IS_LINKED(ob_old)) {
continue;
}
if (ob_new == NULL) { if (ob_new == NULL) {
ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag); ob_new = BKE_object_duplicate(bmain, ob_old, object_dupflag);
ID_NEW_SET(ob_old, ob_new); ID_NEW_SET(ob_old, ob_new);
@ -430,7 +437,7 @@ Collection *BKE_collection_duplicate(Main *bmain,
Collection *collection_new = collection_duplicate_recursive( Collection *collection_new = collection_duplicate_recursive(
bmain, parent, collection, do_hierarchy, do_objects, do_obdata); bmain, parent, collection, do_hierarchy, do_objects, do_obdata);
/* This code will follows into all ID links using an ID tagged with LIB_TAG_NEW.*/ /* 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_libblock_relink_to_newid(&collection_new->id);
if (do_hierarchy) { if (do_hierarchy) {

View File

@ -64,6 +64,7 @@
#include "BKE_idtype.h" #include "BKE_idtype.h"
#include "BKE_key.h" #include "BKE_key.h"
#include "BKE_lib_id.h" #include "BKE_lib_id.h"
#include "BKE_lib_override.h"
#include "BKE_lib_query.h" #include "BKE_lib_query.h"
#include "BKE_lib_remap.h" #include "BKE_lib_remap.h"
#include "BKE_main.h" #include "BKE_main.h"
@ -1153,14 +1154,13 @@ void BKE_libblock_copy_ex(Main *bmain, const ID *id, ID **r_newid, const int ori
new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag); new_id->properties = IDP_CopyProperty_ex(id->properties, copy_data_flag);
} }
/* XXX Again... We need a way to control what we copy in a much more refined way. /* We may need our own flag to control that at some point, but for now 'no main' one should be
* We cannot always copy this, some internal copying will die on it! */ * good enough. */
/* For now, upper level code will have to do that itself when required. */ if ((orig_flag & LIB_ID_CREATE_NO_MAIN) == 0 && id->override_library != NULL) {
#if 0 /* We do not want to copy existing override rules here, as they would break the proper
if (id->override != NULL) { * remapping between IDs. Proper overrides rules will be re-generated anyway. */
BKE_override_copy(new_id, id); BKE_lib_override_library_copy(new_id, id, false);
} }
#endif
if (id_can_have_animdata(new_id)) { if (id_can_have_animdata(new_id)) {
IdAdtTemplate *iat = (IdAdtTemplate *)new_id; IdAdtTemplate *iat = (IdAdtTemplate *)new_id;

View File

@ -92,7 +92,7 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
if (ancestor_id != NULL && ancestor_id->override_library != NULL) { if (ancestor_id != NULL && ancestor_id->override_library != NULL) {
/* Original ID has a template, use it! */ /* Original ID has a template, use it! */
BKE_lib_override_library_copy(local_id, ancestor_id); BKE_lib_override_library_copy(local_id, ancestor_id, true);
if (local_id->override_library->reference != reference_id) { if (local_id->override_library->reference != reference_id) {
id_us_min(local_id->override_library->reference); id_us_min(local_id->override_library->reference);
local_id->override_library->reference = reference_id; local_id->override_library->reference = reference_id;
@ -110,8 +110,8 @@ IDOverrideLibrary *BKE_lib_override_library_init(ID *local_id, ID *reference_id)
return local_id->override_library; return local_id->override_library;
} }
/** Deep copy of a whole override from \a src_id to \a dst_id. */ /** Shalow or deep copy of a whole override from \a src_id to \a dst_id. */
void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id) void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id, const bool do_full_copy)
{ {
BLI_assert(src_id->override_library != NULL); BLI_assert(src_id->override_library != NULL);
@ -138,12 +138,15 @@ void BKE_lib_override_library_copy(ID *dst_id, const ID *src_id)
(ID *)src_id; (ID *)src_id;
id_us_plus(dst_id->override_library->reference); id_us_plus(dst_id->override_library->reference);
BLI_duplicatelist(&dst_id->override_library->properties, &src_id->override_library->properties); if (do_full_copy) {
for (IDOverrideLibraryProperty *op_dst = dst_id->override_library->properties.first, BLI_duplicatelist(&dst_id->override_library->properties,
*op_src = src_id->override_library->properties.first; &src_id->override_library->properties);
op_dst; for (IDOverrideLibraryProperty *op_dst = dst_id->override_library->properties.first,
op_dst = op_dst->next, op_src = op_src->next) { *op_src = src_id->override_library->properties.first;
lib_override_library_property_copy(op_dst, op_src); op_dst;
op_dst = op_dst->next, op_src = op_src->next) {
lib_override_library_property_copy(op_dst, op_src);
}
} }
dst_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK; dst_id->tag &= ~LIB_TAG_OVERRIDE_LIBRARY_REFOK;

View File

@ -1763,6 +1763,8 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
Material ***matarar; Material ***matarar;
ID *id; ID *id;
int a, didit; int a, didit;
const bool is_object_liboverride = ID_IS_OVERRIDE_LIBRARY(ob);
Object *obn = BKE_object_copy(bmain, ob); Object *obn = BKE_object_copy(bmain, ob);
/* 0 == full linked. */ /* 0 == full linked. */
@ -1790,6 +1792,9 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
for (a = 0; a < obn->totcol; a++) { for (a = 0; a < obn->totcol; a++) {
id = (ID *)obn->mat[a]; id = (ID *)obn->mat[a];
if (id) { if (id) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
ID_NEW_REMAP_US(obn->mat[a]) ID_NEW_REMAP_US(obn->mat[a])
else else
{ {
@ -1807,6 +1812,9 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
for (psys = obn->particlesystem.first; psys; psys = psys->next) { for (psys = obn->particlesystem.first; psys; psys = psys->next) {
id = (ID *)psys->part; id = (ID *)psys->part;
if (id) { if (id) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
ID_NEW_REMAP_US(psys->part) ID_NEW_REMAP_US(psys->part)
else else
{ {
@ -1823,179 +1831,181 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
id = obn->data; id = obn->data;
didit = 0; didit = 0;
switch (obn->type) { if (!is_object_liboverride || !ID_IS_LINKED(id)) {
case OB_MESH: switch (obn->type) {
if (dupflag & USER_DUP_MESH) { case OB_MESH:
ID_NEW_REMAP_US2(obn->data) if (dupflag & USER_DUP_MESH) {
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:
if (dupflag != 0) {
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) ID_NEW_REMAP_US2(obn->data)
else else
{ {
obn->data = ID_NEW_SET(obn->data, BKE_armature_copy(bmain, obn->data)); obn->data = ID_NEW_SET(obn->data, BKE_mesh_copy(bmain, obn->data));
BKE_pose_rebuild(bmain, obn, obn->data, true);
didit = 1; didit = 1;
} }
id_us_min(id); id_us_min(id);
} }
} break;
break; case OB_CURVE:
case OB_LATTICE: if (dupflag & USER_DUP_CURVE) {
if (dupflag != 0) { ID_NEW_REMAP_US2(obn->data)
ID_NEW_REMAP_US2(obn->data) else
else {
{ obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data)); didit = 1;
didit = 1; }
id_us_min(id);
} }
id_us_min(id); break;
} case OB_SURF:
break; if (dupflag & USER_DUP_SURF) {
case OB_CAMERA: ID_NEW_REMAP_US2(obn->data)
if (dupflag != 0) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_FONT:
break; if (dupflag & USER_DUP_FONT) {
case OB_LIGHTPROBE: ID_NEW_REMAP_US2(obn->data)
if (dupflag & USER_DUP_LIGHTPROBE) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_curve_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_lightprobe_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_MBALL:
break; if (dupflag & USER_DUP_MBALL) {
case OB_SPEAKER: ID_NEW_REMAP_US2(obn->data)
if (dupflag != 0) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_mball_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_speaker_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_LAMP:
break; if (dupflag & USER_DUP_LAMP) {
case OB_GPENCIL: ID_NEW_REMAP_US2(obn->data)
if (dupflag & USER_DUP_GPENCIL) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_light_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_gpencil_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_ARMATURE:
break; if (dupflag != 0) {
case OB_HAIR: DEG_id_tag_update(&obn->id, ID_RECALC_GEOMETRY);
if (dupflag & USER_DUP_HAIR) { if (obn->pose) {
ID_NEW_REMAP_US2(obn->data) BKE_pose_tag_recalc(bmain, obn->pose);
else }
{ if (dupflag & USER_DUP_ARM) {
obn->data = ID_NEW_SET(obn->data, BKE_hair_copy(bmain, obn->data)); ID_NEW_REMAP_US2(obn->data)
didit = 1; 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);
}
} }
id_us_min(id); break;
} case OB_LATTICE:
break; if (dupflag != 0) {
case OB_POINTCLOUD: ID_NEW_REMAP_US2(obn->data)
if (dupflag & USER_DUP_POINTCLOUD) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_lattice_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_pointcloud_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_CAMERA:
break; if (dupflag != 0) {
case OB_VOLUME: ID_NEW_REMAP_US2(obn->data)
if (dupflag & USER_DUP_VOLUME) { else
ID_NEW_REMAP_US2(obn->data) {
else obn->data = ID_NEW_SET(obn->data, BKE_camera_copy(bmain, obn->data));
{ didit = 1;
obn->data = ID_NEW_SET(obn->data, BKE_volume_copy(bmain, obn->data)); }
didit = 1; id_us_min(id);
} }
id_us_min(id); break;
} case OB_LIGHTPROBE:
break; 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;
}
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 & 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;
}
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;
}
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;
}
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;
}
id_us_min(id);
}
break;
}
} }
/* Check if obdata is copied. */ /* Check if obdata is copied. */
@ -2020,6 +2030,9 @@ Object *BKE_object_duplicate(Main *bmain, const Object *ob, const int dupflag)
for (a = 0; a < obn->totcol; a++) { for (a = 0; a < obn->totcol; a++) {
id = (ID *)(*matarar)[a]; id = (ID *)(*matarar)[a];
if (id) { if (id) {
if (is_object_liboverride && ID_IS_LINKED(id)) {
continue;
}
ID_NEW_REMAP_US((*matarar)[a]) ID_NEW_REMAP_US((*matarar)[a])
else else
{ {