Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/windowmanager/intern/wm_files_link.c')
-rw-r--r--source/blender/windowmanager/intern/wm_files_link.c691
1 files changed, 644 insertions, 47 deletions
diff --git a/source/blender/windowmanager/intern/wm_files_link.c b/source/blender/windowmanager/intern/wm_files_link.c
index 606c9252ff9..29e34313be5 100644
--- a/source/blender/windowmanager/intern/wm_files_link.c
+++ b/source/blender/windowmanager/intern/wm_files_link.c
@@ -35,7 +35,9 @@
#include "MEM_guardedalloc.h"
#include "DNA_ID.h"
+#include "DNA_collection_types.h"
#include "DNA_key_types.h"
+#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
@@ -50,15 +52,21 @@
#include "BLO_readfile.h"
+#include "BKE_armature.h"
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_key.h"
#include "BKE_layer.h"
#include "BKE_lib_id.h"
#include "BKE_lib_override.h"
+#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_object.h"
#include "BKE_report.h"
+#include "BKE_rigidbody.h"
+#include "BKE_scene.h"
#include "BKE_idtype.h"
@@ -137,6 +145,14 @@ static short wm_link_append_flag(wmOperator *op)
if (RNA_boolean_get(op->ptr, "link")) {
flag |= FILE_LINK;
}
+ else {
+ if (RNA_boolean_get(op->ptr, "use_recursive")) {
+ flag |= FILE_APPEND_RECURSIVE;
+ }
+ if (RNA_boolean_get(op->ptr, "set_fake")) {
+ flag |= FILE_APPEND_SET_FAKEUSER;
+ }
+ }
if (RNA_boolean_get(op->ptr, "instance_collections")) {
flag |= FILE_COLLECTION_INSTANCE;
}
@@ -153,6 +169,10 @@ typedef struct WMLinkAppendDataItem {
*libraries; /* All libs (from WMLinkAppendData.libraries) to try to load this ID from. */
short idcode;
+ /** Type of action to do to append this item, and other append-specific information. */
+ char append_action;
+ char append_tag;
+
ID *new_id;
void *customdata;
} WMLinkAppendDataItem;
@@ -167,10 +187,32 @@ typedef struct WMLinkAppendData {
*/
int flag;
+ /** Allows to easily find an existing items from an ID pointer. Used by append code. */
+ GHash *new_id_to_item;
+
/* Internal 'private' data */
MemArena *memarena;
} WMLinkAppendData;
+typedef struct WMLinkAppendDataCallBack {
+ WMLinkAppendData *lapp_data;
+ WMLinkAppendDataItem *item;
+ ReportList *reports;
+
+} WMLinkAppendDataCallBack;
+
+enum {
+ WM_APPEND_ACT_UNSET = 0,
+ WM_APPEND_ACT_KEEP_LINKED,
+ WM_APPEND_ACT_REUSE_LOCAL,
+ WM_APPEND_ACT_MAKE_LOCAL,
+ WM_APPEND_ACT_COPY_LOCAL,
+};
+
+enum {
+ WM_APPEND_TAG_INDIRECT = 1 << 0,
+};
+
static WMLinkAppendData *wm_link_append_data_new(const int flag)
{
MemArena *ma = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, __func__);
@@ -184,6 +226,10 @@ static WMLinkAppendData *wm_link_append_data_new(const int flag)
static void wm_link_append_data_free(WMLinkAppendData *lapp_data)
{
+ if (lapp_data->new_id_to_item != NULL) {
+ BLI_ghash_free(lapp_data->new_id_to_item, NULL, NULL);
+ }
+
BLI_memarena_free(lapp_data->memarena);
}
@@ -213,6 +259,7 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp
item->libraries = BLI_BITMAP_NEW_MEMARENA(lapp_data->memarena, lapp_data->num_libraries);
item->new_id = NULL;
+ item->append_action = WM_APPEND_ACT_UNSET;
item->customdata = customdata;
BLI_linklist_append_arena(&lapp_data->items, item, lapp_data->memarena);
@@ -221,6 +268,568 @@ static WMLinkAppendDataItem *wm_link_append_data_item_add(WMLinkAppendData *lapp
return item;
}
+/* -------------------------------------------------------------------- */
+/** \name Library appending helper functions.
+ *
+ * FIXME: Deduplicate code with similar one in readfile.c
+ * \{ */
+
+static bool object_in_any_scene(Main *bmain, Object *ob)
+{
+ LISTBASE_FOREACH (Scene *, sce, &bmain->scenes) {
+ if (BKE_scene_object_find(sce, ob)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool object_in_any_collection(Main *bmain, Object *ob)
+{
+ LISTBASE_FOREACH (Collection *, collection, &bmain->collections) {
+ if (BKE_collection_has_object(collection, ob)) {
+ return true;
+ }
+ }
+
+ LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) {
+ if (scene->master_collection != NULL &&
+ BKE_collection_has_object(scene->master_collection, ob)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Shared operations to perform on the object's base after adding it to the scene.
+ */
+static void wm_append_loose_data_instantiate_object_base_instance_init(
+ Object *ob, bool set_selected, bool set_active, ViewLayer *view_layer, const View3D *v3d)
+{
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
+
+ if (v3d != NULL) {
+ base->local_view_bits |= v3d->local_view_uuid;
+ }
+
+ if (set_selected) {
+ if (base->flag & BASE_SELECTABLE) {
+ base->flag |= BASE_SELECTED;
+ }
+ }
+
+ if (set_active) {
+ view_layer->basact = base;
+ }
+
+ BKE_scene_object_base_flag_sync_from_base(base);
+}
+
+static ID *wm_append_loose_data_instantiate_process_check(WMLinkAppendDataItem *item)
+{
+ /* We consider that if we either kept it linked, or re-used already local data, instantiation
+ * status of those should not be modified. */
+ if (!ELEM(item->append_action, WM_APPEND_ACT_COPY_LOCAL, WM_APPEND_ACT_MAKE_LOCAL)) {
+ return NULL;
+ }
+
+ ID *id = item->new_id;
+ if (id == NULL) {
+ return NULL;
+ }
+
+ if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) {
+ BLI_assert(ID_IS_LINKED(id));
+ id = id->newid;
+ if (id == NULL) {
+ return NULL;
+ }
+
+ BLI_assert(!ID_IS_LINKED(id));
+ return id;
+ }
+
+ BLI_assert(!ID_IS_LINKED(id));
+ return id;
+}
+
+static void wm_append_loose_data_instantiate_ensure_active_collection(
+ WMLinkAppendData *lapp_data,
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ Collection **r_active_collection)
+{
+ /* Find or add collection as needed. */
+ if (*r_active_collection == NULL) {
+ if (lapp_data->flag & FILE_ACTIVE_COLLECTION) {
+ LayerCollection *lc = BKE_layer_collection_get_active(view_layer);
+ *r_active_collection = lc->collection;
+ }
+ else {
+ *r_active_collection = BKE_collection_add(bmain, scene->master_collection, NULL);
+ }
+ }
+}
+
+/* TODO: De-duplicate this code with the one in readfile.c, think we need some utils code for that
+ * in BKE. */
+static void wm_append_loose_data_instantiate(WMLinkAppendData *lapp_data,
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d)
+{
+ if (scene == NULL) {
+ /* In some cases, like the asset drag&drop e.g., the caller code manages instantiation itself.
+ */
+ return;
+ }
+
+ LinkNode *itemlink;
+ Collection *active_collection = NULL;
+ const bool do_obdata = (lapp_data->flag & FILE_OBDATA_INSTANCE) != 0;
+
+ const bool object_set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0;
+ /* Do NOT make base active here! screws up GUI stuff,
+ * if you want it do it at the editor level. */
+ const bool object_set_active = false;
+
+ /* First pass on obdata to enable their instantiation by default, then do a second pass on
+ * objects to clear it for any obdata already in use. */
+ if (do_obdata) {
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = wm_append_loose_data_instantiate_process_check(item);
+ if (id == NULL) {
+ continue;
+ }
+ const ID_Type idcode = GS(id->name);
+ if (!OB_DATA_SUPPORT_ID(idcode)) {
+ continue;
+ }
+
+ id->tag |= LIB_TAG_DOIT;
+ }
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = item->new_id;
+ if (id == NULL || GS(id->name) != ID_OB) {
+ continue;
+ }
+
+ Object *ob = (Object *)id;
+ Object *new_ob = (Object *)id->newid;
+ if (ob->data != NULL) {
+ ((ID *)(ob->data))->tag &= ~LIB_TAG_DOIT;
+ }
+ if (new_ob != NULL && new_ob->data != NULL) {
+ ((ID *)(new_ob->data))->tag &= ~LIB_TAG_DOIT;
+ }
+ }
+ }
+
+ /* First do collections, then objects, then obdata. */
+
+ /* NOTE: For collections we only view_layer-instantiate duplicated collections that have
+ * non-instantiated objects in them. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = wm_append_loose_data_instantiate_process_check(item);
+ if (id == NULL || GS(id->name) != ID_GR) {
+ continue;
+ }
+
+ /* We do not want to force instantiation of indirectly appended collections. Users can now
+ * easily instantiate collections (and their objects) as needed by themselves. See T67032. */
+ /* We need to check that objects in that collections are already instantiated in a scene.
+ * Otherwise, it's better to add the collection to the scene's active collection, than to
+ * instantiate its objects in active scene's collection directly. See T61141.
+ *
+ * NOTE: We only check object directly into that collection, not recursively into its
+ * children.
+ */
+ Collection *collection = (Collection *)id;
+ bool do_add_collection = false;
+ LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
+ Object *ob = coll_ob->ob;
+ if (!object_in_any_scene(bmain, ob)) {
+ do_add_collection = true;
+ break;
+ }
+ }
+ if (do_add_collection) {
+ wm_append_loose_data_instantiate_ensure_active_collection(
+ lapp_data, bmain, scene, view_layer, &active_collection);
+
+ /* In case user requested instantiation of collections as empties, we do so for the one they
+ * explicitly selected (originally directly linked IDs). */
+ if ((lapp_data->flag & FILE_COLLECTION_INSTANCE) != 0 &&
+ (item->append_tag & WM_APPEND_TAG_INDIRECT) == 0) {
+ /* BKE_object_add(...) messes with the selection. */
+ Object *ob = BKE_object_add_only_object(bmain, OB_EMPTY, collection->id.name + 2);
+ ob->type = OB_EMPTY;
+ ob->empty_drawsize = U.collection_instance_empty_size;
+
+ BKE_collection_object_add(bmain, active_collection, ob);
+
+ const bool set_selected = (lapp_data->flag & FILE_AUTOSELECT) != 0;
+ /* TODO: why is it OK to make this active here but not in other situations?
+ * See other callers of #object_base_instance_init */
+ const bool set_active = set_selected;
+ wm_append_loose_data_instantiate_object_base_instance_init(
+ ob, set_selected, set_active, view_layer, v3d);
+
+ /* Assign the collection. */
+ ob->instance_collection = collection;
+ id_us_plus(&collection->id);
+ ob->transflag |= OB_DUPLICOLLECTION;
+ copy_v3_v3(ob->loc, scene->cursor.location);
+ }
+ else {
+ /* Add collection as child of active collection. */
+ BKE_collection_child_add(bmain, active_collection, collection);
+
+ if ((lapp_data->flag & FILE_AUTOSELECT) != 0) {
+ LISTBASE_FOREACH (CollectionObject *, coll_ob, &collection->gobject) {
+ Object *ob = coll_ob->ob;
+ Base *base = BKE_view_layer_base_find(view_layer, ob);
+ if (base) {
+ base->flag |= BASE_SELECTED;
+ BKE_scene_object_base_flag_sync_from_base(base);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* NOTE: For objects we only view_layer-instantiate duplicated objects that are not yet used
+ * anywhere. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = wm_append_loose_data_instantiate_process_check(item);
+ if (id == NULL || GS(id->name) != ID_OB) {
+ continue;
+ }
+
+ Object *ob = (Object *)id;
+
+ if (object_in_any_collection(bmain, ob)) {
+ continue;
+ }
+
+ wm_append_loose_data_instantiate_ensure_active_collection(
+ lapp_data, bmain, scene, view_layer, &active_collection);
+
+ CLAMP_MIN(ob->id.us, 0);
+ ob->mode = OB_MODE_OBJECT;
+
+ BKE_collection_object_add(bmain, active_collection, ob);
+
+ wm_append_loose_data_instantiate_object_base_instance_init(
+ ob, object_set_selected, object_set_active, view_layer, v3d);
+ }
+
+ if (!do_obdata) {
+ return;
+ }
+
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = wm_append_loose_data_instantiate_process_check(item);
+ if (id == NULL) {
+ continue;
+ }
+ const ID_Type idcode = GS(id->name);
+ if (!OB_DATA_SUPPORT_ID(idcode)) {
+ continue;
+ }
+ if ((id->tag & LIB_TAG_DOIT) == 0) {
+ continue;
+ }
+
+ wm_append_loose_data_instantiate_ensure_active_collection(
+ lapp_data, bmain, scene, view_layer, &active_collection);
+
+ const int type = BKE_object_obdata_to_type(id);
+ BLI_assert(type != -1);
+ Object *ob = BKE_object_add_only_object(bmain, type, id->name + 2);
+ ob->data = id;
+ id_us_plus(id);
+ BKE_object_materials_test(bmain, ob, ob->data);
+
+ BKE_collection_object_add(bmain, active_collection, ob);
+
+ wm_append_loose_data_instantiate_object_base_instance_init(
+ ob, object_set_selected, object_set_active, view_layer, v3d);
+
+ copy_v3_v3(ob->loc, scene->cursor.location);
+ }
+}
+
+/** \} */
+
+static int foreach_libblock_append_callback(LibraryIDLinkCallbackData *cb_data)
+{
+ if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK)) {
+ return IDWALK_RET_NOP;
+ }
+
+ WMLinkAppendDataCallBack *data = cb_data->user_data;
+ ID *id = *cb_data->id_pointer;
+
+ if (id == NULL) {
+ return IDWALK_RET_NOP;
+ }
+
+ if (!BKE_idtype_idcode_is_linkable(GS(id->name))) {
+ return IDWALK_RET_NOP;
+ }
+
+ WMLinkAppendDataItem *item = BLI_ghash_lookup(data->lapp_data->new_id_to_item, id);
+ if (item == NULL) {
+ item = wm_link_append_data_item_add(data->lapp_data, id->name, GS(id->name), NULL);
+ item->new_id = id;
+ /* Since we did not have an item for that ID yet, we now user did not selected it explicitly,
+ * it was rather linked indirectly. This info is important for instantiation of collections. */
+ item->append_tag |= WM_APPEND_TAG_INDIRECT;
+ BLI_ghash_insert(data->lapp_data->new_id_to_item, id, item);
+ }
+
+ /* NOTE: currently there is no need to do anything else here, but in the future this would be
+ * the place to add specific per-usage decisions on how to append an ID. */
+
+ return IDWALK_RET_NOP;
+}
+
+/* Perform append operation, using modern ID usage looper to detect which ID should be kept linked,
+ * made local, duplicated as local, re-used from local etc.
+ *
+ * TODO: Expose somehow this logic to the two other parts of code performing actual append
+ * (i.e. copy/paste and `bpy` link/append API).
+ * Then we can heavily simplify #BKE_library_make_local(). */
+static void wm_append_do(WMLinkAppendData *lapp_data,
+ ReportList *reports,
+ Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ const View3D *v3d)
+{
+ BLI_assert((lapp_data->flag & FILE_LINK) == 0);
+
+ const bool do_recursive = (lapp_data->flag & FILE_APPEND_RECURSIVE) != 0;
+ const bool set_fakeuser = (lapp_data->flag & FILE_APPEND_SET_FAKEUSER) != 0;
+
+ LinkNode *itemlink;
+
+ /* Generate a mapping between newly linked IDs and their items. */
+ lapp_data->new_id_to_item = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__);
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+ BLI_ghash_insert(lapp_data->new_id_to_item, id, item);
+ }
+
+ /* NOTE: Since we append items for IDs not already listed (i.e. implicitly linked indirect
+ * dependencies), this list will grow and we will process those IDs later, leading to a flatten
+ * recursive processing of all the linked dependencies. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+ BLI_assert(item->customdata == NULL);
+
+ /* Clear tag previously used to mark IDs needing post-processing (instantiation of loose
+ * objects etc.). */
+ id->tag &= ~LIB_TAG_DOIT;
+
+ if (item->append_action != WM_APPEND_ACT_UNSET) {
+ /* Already set, pass. */
+ }
+ if (GS(id->name) == ID_OB && ((Object *)id)->proxy_from != NULL) {
+ CLOG_INFO(&LOG, 3, "Appended ID '%s' is proxified, keeping it linked...", id->name);
+ item->append_action = WM_APPEND_ACT_KEEP_LINKED;
+ }
+ else if (id->tag & LIB_TAG_PRE_EXISTING) {
+ CLOG_INFO(&LOG, 3, "Appended ID '%s' was already linked, need to copy it...", id->name);
+ item->append_action = WM_APPEND_ACT_COPY_LOCAL;
+ }
+ else {
+ /* In future we could search for already existing matching local ID etc. */
+ CLOG_INFO(&LOG, 3, "Appended ID '%s' will be made local...", id->name);
+ item->append_action = WM_APPEND_ACT_MAKE_LOCAL;
+ }
+
+ /* Only check dependencies if we are not keeping linked data, nor re-using existing local data.
+ */
+ if (do_recursive &&
+ !ELEM(item->append_action, WM_APPEND_ACT_KEEP_LINKED, WM_APPEND_ACT_REUSE_LOCAL)) {
+ WMLinkAppendDataCallBack cb_data = {
+ .lapp_data = lapp_data, .item = item, .reports = reports};
+ BKE_library_foreach_ID_link(
+ bmain, id, foreach_libblock_append_callback, &cb_data, IDWALK_NOP);
+ }
+ }
+
+ /* Effectively perform required operation on every linked ID. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+
+ ID *local_appended_new_id = NULL;
+ switch (item->append_action) {
+ case WM_APPEND_ACT_COPY_LOCAL: {
+ BKE_lib_id_make_local(
+ bmain, id, LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_COPY);
+ local_appended_new_id = id->newid;
+ break;
+ }
+ case WM_APPEND_ACT_MAKE_LOCAL:
+ BKE_lib_id_make_local(bmain,
+ id,
+ LIB_ID_MAKELOCAL_FULL_LIBRARY | LIB_ID_MAKELOCAL_FORCE_LOCAL |
+ LIB_ID_MAKELOCAL_OBJECT_NO_PROXY_CLEARING);
+ BLI_assert(id->newid == NULL);
+ local_appended_new_id = id;
+ break;
+ case WM_APPEND_ACT_KEEP_LINKED:
+ /* Nothing to do here. */
+ break;
+ case WM_APPEND_ACT_REUSE_LOCAL:
+ /* We only need to set `newid` to ID found in previous loop, for proper remapping. */
+ ID_NEW_SET(id->newid, item->customdata);
+ /* This is not a 'new' local appended id, do not set `local_appended_new_id` here. */
+ break;
+ case WM_APPEND_ACT_UNSET:
+ CLOG_ERROR(
+ &LOG, "Unexpected unset append action for '%s' ID, assuming 'keep link'", id->name);
+ break;
+ default:
+ BLI_assert(0);
+ }
+
+ if (local_appended_new_id != NULL) {
+ if (GS(local_appended_new_id->name) == ID_OB) {
+ BKE_rigidbody_ensure_local_object(bmain, (Object *)local_appended_new_id);
+ }
+ if (set_fakeuser) {
+ if (!ELEM(GS(local_appended_new_id->name), ID_OB, ID_GR)) {
+ /* Do not set fake user on objects nor collections (instancing). */
+ id_fake_user_set(local_appended_new_id);
+ }
+ }
+ }
+ }
+
+ /* Remap IDs as needed. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+
+ if (item->append_action == WM_APPEND_ACT_KEEP_LINKED) {
+ continue;
+ }
+
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+ if (item->append_action == WM_APPEND_ACT_COPY_LOCAL) {
+ BLI_assert(ID_IS_LINKED(id));
+ id = id->newid;
+ if (id == NULL) {
+ continue;
+ }
+ }
+
+ BLI_assert(!ID_IS_LINKED(id));
+
+ BKE_libblock_relink_to_newid_new(bmain, id);
+ }
+
+ /* Instantiate newly created (duplicated) IDs as needed. */
+ wm_append_loose_data_instantiate(lapp_data, bmain, scene, view_layer, v3d);
+
+ /* Attempt to deal with object proxies.
+ *
+ * NOTE: Copied from `BKE_library_make_local`, but this is not really working (as in, not
+ * producing any useful result in any known use case), neither here nor in
+ * `BKE_library_make_local` currently.
+ * Proxies are end of life anyway, so not worth spending time on this. */
+ for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
+ WMLinkAppendDataItem *item = itemlink->link;
+
+ if (item->append_action != WM_APPEND_ACT_COPY_LOCAL) {
+ continue;
+ }
+
+ ID *id = item->new_id;
+ if (id == NULL) {
+ continue;
+ }
+ BLI_assert(ID_IS_LINKED(id));
+
+ /* Attempt to re-link copied proxy objects. This allows appending of an entire scene
+ * from another blend file into this one, even when that blend file contains proxified
+ * armatures that have local references. Since the proxified object needs to be linked
+ * (not local), this will only work when the "Localize all" checkbox is disabled.
+ * TL;DR: this is a dirty hack on top of an already weak feature (proxies). */
+ if (GS(id->name) == ID_OB && ((Object *)id)->proxy != NULL) {
+ Object *ob = (Object *)id;
+ Object *ob_new = (Object *)id->newid;
+ bool is_local = false, is_lib = false;
+
+ /* Proxies only work when the proxified object is linked-in from a library. */
+ if (!ID_IS_LINKED(ob->proxy)) {
+ CLOG_WARN(&LOG,
+ "Proxy object %s will lose its link to %s, because the "
+ "proxified object is local",
+ id->newid->name,
+ ob->proxy->id.name);
+ continue;
+ }
+
+ BKE_library_ID_test_usages(bmain, id, &is_local, &is_lib);
+
+ /* We can only switch the proxy'ing to a made-local proxy if it is no longer
+ * referred to from a library. Not checking for local use; if new local proxy
+ * was not used locally would be a nasty bug! */
+ if (is_local || is_lib) {
+ CLOG_WARN(&LOG,
+ "Made-local proxy object %s will lose its link to %s, "
+ "because the linked-in proxy is referenced (is_local=%i, is_lib=%i)",
+ id->newid->name,
+ ob->proxy->id.name,
+ is_local,
+ is_lib);
+ }
+ else {
+ /* we can switch the proxy'ing from the linked-in to the made-local proxy.
+ * BKE_object_make_proxy() shouldn't be used here, as it allocates memory that
+ * was already allocated by object_make_local() (which called BKE_object_copy). */
+ ob_new->proxy = ob->proxy;
+ ob_new->proxy_group = ob->proxy_group;
+ ob_new->proxy_from = ob->proxy_from;
+ ob_new->proxy->proxy_from = ob_new;
+ ob->proxy = ob->proxy_from = ob->proxy_group = NULL;
+ }
+ }
+ }
+
+ BKE_main_id_newptr_and_tag_clear(bmain);
+}
+
static void wm_link_do(WMLinkAppendData *lapp_data,
ReportList *reports,
Main *bmain,
@@ -263,6 +872,11 @@ static void wm_link_do(WMLinkAppendData *lapp_data,
struct LibraryLink_Params liblink_params;
BLO_library_link_params_init_with_context(
&liblink_params, bmain, flag, id_tag_extra, scene, view_layer, v3d);
+ /* In case of append, do not handle instantiation in linking process, but during append phase
+ * (see #wm_append_loose_data_instantiate ). */
+ if ((flag & FILE_LINK) == 0) {
+ liblink_params.flag &= ~BLO_LIBLINK_NEEDS_ID_TAG_DOIT;
+ }
mainl = BLO_library_link_begin(&bh, libname, &liblink_params);
lib = mainl->curlib;
@@ -325,9 +939,8 @@ static bool wm_link_append_item_poll(ReportList *reports,
idcode = BKE_idtype_idcode_from_name(group);
- /* XXX For now, we do a nasty exception for workspace, forbid linking them.
- * Not nice, ultimately should be solved! */
- if (!BKE_idtype_idcode_is_linkable(idcode) && (do_append || idcode != ID_WS)) {
+ if (!BKE_idtype_idcode_is_linkable(idcode) ||
+ (!do_append && BKE_idtype_idcode_is_only_appendable(idcode))) {
if (reports) {
if (do_append) {
BKE_reportf(reports,
@@ -501,28 +1114,7 @@ static int wm_link_append_exec(bContext *C, wmOperator *op)
/* append, rather than linking */
if (do_append) {
- const bool set_fake = RNA_boolean_get(op->ptr, "set_fake");
- const bool use_recursive = RNA_boolean_get(op->ptr, "use_recursive");
-
- if (use_recursive) {
- BKE_library_make_local(bmain, NULL, NULL, true, set_fake);
- }
- else {
- LinkNode *itemlink;
- GSet *done_libraries = BLI_gset_new_ex(
- BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__, lapp_data->num_libraries);
-
- for (itemlink = lapp_data->items.list; itemlink; itemlink = itemlink->next) {
- ID *new_id = ((WMLinkAppendDataItem *)(itemlink->link))->new_id;
-
- if (new_id && !BLI_gset_haskey(done_libraries, new_id->lib)) {
- BKE_library_make_local(bmain, new_id->lib, NULL, true, set_fake);
- BLI_gset_insert(done_libraries, new_id->lib);
- }
- }
-
- BLI_gset_free(done_libraries, NULL);
- }
+ wm_append_do(lapp_data, op->reports, bmain, scene, view_layer, CTX_wm_view3d(C));
}
wm_link_append_data_free(lapp_data);
@@ -652,20 +1244,20 @@ void WM_OT_append(wmOperatorType *ot)
*
* \{ */
-static ID *wm_file_link_datablock_ex(Main *bmain,
- Scene *scene,
- ViewLayer *view_layer,
- View3D *v3d,
- const char *filepath,
- const short id_code,
- const char *id_name,
- bool clear_pre_existing_flag)
+static ID *wm_file_link_append_datablock_ex(Main *bmain,
+ Scene *scene,
+ ViewLayer *view_layer,
+ View3D *v3d,
+ const char *filepath,
+ const short id_code,
+ const char *id_name,
+ const bool do_append)
{
/* Tag everything so we can make local only the new datablock. */
BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, true);
/* Define working data, with just the one item we want to link. */
- WMLinkAppendData *lapp_data = wm_link_append_data_new(0);
+ WMLinkAppendData *lapp_data = wm_link_append_data_new(do_append ? FILE_APPEND_RECURSIVE : 0);
wm_link_append_data_library_add(lapp_data, filepath);
WMLinkAppendDataItem *item = wm_link_append_data_item_add(lapp_data, id_name, id_code, NULL);
@@ -676,15 +1268,22 @@ static ID *wm_file_link_datablock_ex(Main *bmain,
/* Get linked datablock and free working data. */
ID *id = item->new_id;
- wm_link_append_data_free(lapp_data);
- if (clear_pre_existing_flag) {
- BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+ if (do_append) {
+ wm_append_do(lapp_data, NULL, bmain, scene, view_layer, v3d);
}
+ wm_link_append_data_free(lapp_data);
+
+ BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+
return id;
}
+/*
+ * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no
+ * instantiation of linked objects, collections etc. will be performed.
+ */
ID *WM_file_link_datablock(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@@ -693,10 +1292,14 @@ ID *WM_file_link_datablock(Main *bmain,
const short id_code,
const char *id_name)
{
- return wm_file_link_datablock_ex(
- bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
+ return wm_file_link_append_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
}
+/*
+ * NOTE: `scene` (and related `view_layer` and `v3d`) pointers may be NULL, in which case no
+ * instantiation of appended objects, collections etc. will be performed.
+ */
ID *WM_file_append_datablock(Main *bmain,
Scene *scene,
ViewLayer *view_layer,
@@ -705,14 +1308,8 @@ ID *WM_file_append_datablock(Main *bmain,
const short id_code,
const char *id_name)
{
- ID *id = wm_file_link_datablock_ex(
- bmain, scene, view_layer, v3d, filepath, id_code, id_name, false);
-
- /* Make datablock local. */
- BKE_library_make_local(bmain, NULL, NULL, true, false);
-
- /* Clear pre existing tag. */
- BKE_main_id_tag_all(bmain, LIB_TAG_PRE_EXISTING, false);
+ ID *id = wm_file_link_append_datablock_ex(
+ bmain, scene, view_layer, v3d, filepath, id_code, id_name, true);
return id;
}