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/blenloader/intern/readfile.c')
-rw-r--r--source/blender/blenloader/intern/readfile.c669
1 files changed, 452 insertions, 217 deletions
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index 7bc6410316d..2c341f97a66 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -124,6 +124,7 @@
#include "BKE_global.h" // for G
#include "BKE_group.h"
#include "BKE_library.h" // for which_libbase
+#include "BKE_library_query.h"
#include "BKE_idcode.h"
#include "BKE_material.h"
#include "BKE_main.h" // for Main
@@ -618,7 +619,9 @@ static Main *blo_find_main(FileData *fd, const char *filepath, const char *relab
m = BKE_main_new();
BLI_addtail(mainlist, m);
- lib = BKE_libblock_alloc(m, ID_LI, "lib");
+ /* Add library datablock itself to 'main' Main, since libraries are **never** linked data.
+ * Fixes bug where you could end with all ID_LI datablocks having the same name... */
+ lib = BKE_libblock_alloc(mainlist->first, ID_LI, "Lib");
BLI_strncpy(lib->name, filepath, sizeof(lib->name));
BLI_strncpy(lib->filepath, name1, sizeof(lib->filepath));
@@ -851,6 +854,12 @@ BHead *blo_nextbhead(FileData *fd, BHead *thisblock)
return(bhead);
}
+/* Warning! Caller's responsability to ensure given bhead **is** and ID one! */
+const char *bhead_id_name(const FileData *fd, const BHead *bhead)
+{
+ return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs);
+}
+
static void decode_blender_header(FileData *fd)
{
char header[SIZEOFBLENDERHEADER], num[4];
@@ -917,6 +926,41 @@ static int read_file_dna(FileData *fd)
return 0;
}
+static int *read_file_thumbnail(FileData *fd)
+{
+ BHead *bhead;
+ int *blend_thumb = NULL;
+
+ for (bhead = blo_firstbhead(fd); bhead; bhead = blo_nextbhead(fd, bhead)) {
+ if (bhead->code == TEST) {
+ const bool do_endian_swap = (fd->flags & FD_FLAGS_SWITCH_ENDIAN) != 0;
+ int *data = (int *)(bhead + 1);
+
+ if (bhead->len < (2 * sizeof(int))) {
+ break;
+ }
+
+ if (do_endian_swap) {
+ BLI_endian_switch_int32(&data[0]);
+ BLI_endian_switch_int32(&data[1]);
+ }
+
+ if (bhead->len < BLEN_THUMB_MEMSIZE_FILE(data[0], data[1])) {
+ break;
+ }
+
+ blend_thumb = data;
+ break;
+ }
+ else if (bhead->code != REND) {
+ /* Thumbnail is stored in TEST immediately after first REND... */
+ break;
+ }
+ }
+
+ return blend_thumb;
+}
+
static int fd_read_from_file(FileData *filedata, void *buffer, unsigned int size)
{
int readsize = read(filedata->filedes, buffer, size);
@@ -1080,6 +1124,33 @@ FileData *blo_openblenderfile(const char *filepath, ReportList *reports)
}
}
+/**
+ * Same as blo_openblenderfile(), but does not reads DNA data, only header. Use it for light access
+ * (e.g. thumbnail reading).
+ */
+static FileData *blo_openblenderfile_minimal(const char *filepath)
+{
+ gzFile gzfile;
+ errno = 0;
+ gzfile = BLI_gzopen(filepath, "rb");
+
+ if (gzfile != (gzFile)Z_NULL) {
+ FileData *fd = filedata_new();
+ fd->gzfiledes = gzfile;
+ fd->read = fd_read_gzip_from_file;
+
+ decode_blender_header(fd);
+
+ if (fd->flags & FD_FLAGS_FILE_OK) {
+ return fd;
+ }
+
+ blo_freefiledata(fd);
+ }
+
+ return NULL;
+}
+
static int fd_read_gzip_from_memory(FileData *filedata, void *buffer, unsigned int size)
{
int err;
@@ -1228,12 +1299,27 @@ void blo_freefiledata(FileData *fd)
/* ************ DIV ****************** */
+/**
+ * Check whether given path ends with a blend file compatible extension (.blend, .ble or .blend.gz).
+ *
+ * \param str The path to check.
+ * \return true is this path ends with a blender file extension.
+ */
bool BLO_has_bfile_extension(const char *str)
{
const char *ext_test[4] = {".blend", ".ble", ".blend.gz", NULL};
return BLI_testextensie_array(str, ext_test);
}
+/**
+ * Try to explode given path into its 'library components' (i.e. a .blend file, id type/group, and datablock itself).
+ *
+ * \param path the full path to explode.
+ * \param r_dir the string that'll contain path up to blend file itself ('library' path).
+ * \param r_group the string that'll contain 'group' part of the path, if any. May be NULL.
+ * \param r_name the string that'll contain data's name part of the path, if any. May be NULL.
+ * \return true if path contains a blend file.
+ */
bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, char **r_name)
{
/* We might get some data names with slashes, so we have to go up in path until we find blend file itself,
@@ -1290,6 +1376,40 @@ bool BLO_library_path_explode(const char *path, char *r_dir, char **r_group, cha
return true;
}
+/**
+ * Does a very light reading of given .blend file to extract its stored thumbnail.
+ *
+ * \param filepath The path of the file to extract thumbnail from.
+ * \return The raw thumbnail
+ * (MEM-allocated, as stored in file, use BKE_main_thumbnail_to_imbuf() to convert it to ImBuf image).
+ */
+BlendThumbnail *BLO_thumbnail_from_file(const char *filepath)
+{
+ FileData *fd;
+ BlendThumbnail *data;
+ int *fd_data;
+
+ fd = blo_openblenderfile_minimal(filepath);
+ fd_data = fd ? read_file_thumbnail(fd) : NULL;
+
+ if (fd_data) {
+ const size_t sz = BLEN_THUMB_MEMSIZE(fd_data[0], fd_data[1]);
+ data = MEM_mallocN(sz, __func__);
+
+ BLI_assert((sz - sizeof(*data)) == (BLEN_THUMB_MEMSIZE_FILE(fd_data[0], fd_data[1]) - (sizeof(*fd_data) * 2)));
+ data->width = fd_data[0];
+ data->height = fd_data[1];
+ memcpy(data->rect, &fd_data[2], sz - sizeof(*data));
+ }
+ else {
+ data = NULL;
+ }
+
+ blo_freefiledata(fd);
+
+ return data;
+}
+
/* ************** OLD POINTERS ******************* */
static void *newdataadr(FileData *fd, void *adr) /* only direct databocks */
@@ -1680,9 +1800,9 @@ void blo_end_packed_pointer_map(FileData *fd, Main *oldmain)
/* undo file support: add all library pointers in lookup */
-void blo_add_library_pointer_map(ListBase *mainlist, FileData *fd)
+void blo_add_library_pointer_map(ListBase *old_mainlist, FileData *fd)
{
- Main *ptr = mainlist->first;
+ Main *ptr = old_mainlist->first;
ListBase *lbarray[MAX_LIBARRAY];
for (ptr = ptr->next; ptr; ptr = ptr->next) {
@@ -1693,6 +1813,8 @@ void blo_add_library_pointer_map(ListBase *mainlist, FileData *fd)
oldnewmap_insert(fd->libmap, id, id, GS(id->name));
}
}
+
+ fd->old_mainlist = old_mainlist;
}
@@ -4498,15 +4620,15 @@ static void direct_link_latt(FileData *fd, Lattice *lt)
/* ************ READ OBJECT ***************** */
-static void lib_link_modifiers__linkModifiers(void *userData, Object *ob,
- ID **idpoin)
+static void lib_link_modifiers__linkModifiers(
+ void *userData, Object *ob, ID **idpoin, int cd_flag)
{
FileData *fd = userData;
*idpoin = newlibadr(fd, ob->id.lib, *idpoin);
- /* hardcoded bad exception; non-object modifier data gets user count (texture, displace) */
- if (*idpoin && GS((*idpoin)->name)!=ID_OB)
+ if (*idpoin != NULL && (cd_flag & IDWALK_USER) != 0) {
(*idpoin)->us++;
+ }
}
static void lib_link_modifiers(FileData *fd, Object *ob)
{
@@ -5486,16 +5608,10 @@ static void lib_link_scene(FileData *fd, Main *main)
}
}
if (seq->clip) {
- seq->clip = newlibadr(fd, sce->id.lib, seq->clip);
- if (seq->clip) {
- seq->clip->id.us++;
- }
+ seq->clip = newlibadr_us(fd, sce->id.lib, seq->clip);
}
if (seq->mask) {
- seq->mask = newlibadr(fd, sce->id.lib, seq->mask);
- if (seq->mask) {
- seq->mask->id.us++;
- }
+ seq->mask = newlibadr_us(fd, sce->id.lib, seq->mask);
}
if (seq->scene_camera) {
seq->scene_camera = newlibadr(fd, sce->id.lib, seq->scene_camera);
@@ -6437,6 +6553,7 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
else if (sl->spacetype == SPACE_FILE) {
SpaceFile *sfile = (SpaceFile *)sl;
sfile->op = NULL;
+ sfile->previews_timer = NULL;
}
else if (sl->spacetype == SPACE_ACTION) {
SpaceAction *saction = (SpaceAction *)sl;
@@ -6520,7 +6637,13 @@ void blo_lib_link_screen_restore(Main *newmain, bScreen *curscreen, Scene *cursc
BLI_mempool_iternew(so->treestore, &iter);
while ((tselem = BLI_mempool_iterstep(&iter))) {
- tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE);
+ /* Do not try to restore pointers to drivers/sequence/etc., can crash in undo case! */
+ if (TSE_IS_REAL_ID(tselem)) {
+ tselem->id = restore_pointer_by_name(newmain, tselem->id, USER_IGNORE);
+ }
+ else {
+ tselem->id = NULL;
+ }
}
if (so->treehash) {
/* rebuild hash table, because it depends on ids too */
@@ -6967,6 +7090,7 @@ static bool direct_link_screen(FileData *fd, bScreen *sc)
sfile->files = NULL;
sfile->layout = NULL;
sfile->op = NULL;
+ sfile->previews_timer = NULL;
sfile->params = newdataadr(fd, sfile->params);
}
else if (sl->spacetype == SPACE_CLIP) {
@@ -7079,11 +7203,7 @@ static void lib_link_speaker(FileData *fd, Main *main)
if (spk->id.flag & LIB_NEED_LINK) {
if (spk->adt) lib_link_animdata(fd, &spk->id, spk->adt);
- spk->sound= newlibadr(fd, spk->id.lib, spk->sound);
- if (spk->sound) {
- spk->sound->id.us++;
- }
-
+ spk->sound = newlibadr_us(fd, spk->id.lib, spk->sound);
spk->id.flag -= LIB_NEED_LINK;
}
}
@@ -7736,14 +7856,66 @@ static BHead *read_data_into_oldnewmap(FileData *fd, BHead *bhead, const char *a
static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID **r_id)
{
- /* this routine reads a libblock and its direct data. Use link functions
- * to connect it all
+ /* this routine reads a libblock and its direct data. Use link functions to connect it all
*/
ID *id;
ListBase *lb;
const char *allocname;
bool wrong_id = false;
-
+
+ /* In undo case, most libs and linked data should be kept as is from previous state (see BLO_read_from_memfile).
+ * However, some needed by the snapshot being read may have been removed in previous one, and would go missing.
+ * This leads e.g. to desappearing objects in some undo/redo case, see T34446.
+ * That means we have to carefully check whether current lib or libdata already exits in old main, if it does
+ * we merely copy it over into new main area, otherwise we have to do a full read of that bhead... */
+ if (fd->memfile && ELEM(bhead->code, ID_LI, ID_ID)) {
+ const char *idname = bhead_id_name(fd, bhead);
+
+ /* printf("Checking %s...\n", idname); */
+
+ if (bhead->code == ID_LI) {
+ Main *libmain = fd->old_mainlist->first;
+ /* Skip oldmain itself... */
+ for (libmain = libmain->next; libmain; libmain = libmain->next) {
+ /* printf("... against %s: ", libmain->curlib ? libmain->curlib->id.name : "<NULL>"); */
+ if (libmain->curlib && STREQ(idname, libmain->curlib->id.name)) {
+ Main *oldmain = fd->old_mainlist->first;
+ /* printf("FOUND!\n"); */
+ /* In case of a library, we need to re-add its main to fd->mainlist, because if we have later
+ * a missing ID_ID, we need to get the correct lib it is linked to!
+ * Order is crucial, we cannot bulk-add it in BLO_read_from_memfile() like it used to be... */
+ BLI_remlink(fd->old_mainlist, libmain);
+ BLI_remlink_safe(&oldmain->library, libmain->curlib);
+ BLI_addtail(fd->mainlist, libmain);
+ BLI_addtail(&main->library, libmain->curlib);
+
+ if (r_id) {
+ *r_id = NULL; /* Just in case... */
+ }
+ return blo_nextbhead(fd, bhead);
+ }
+ /* printf("nothing...\n"); */
+ }
+ }
+ else {
+ /* printf("... in %s (%s): ", main->curlib ? main->curlib->id.name : "<NULL>", main->curlib ? main->curlib->name : "<NULL>"); */
+ if ((id = BKE_libblock_find_name_ex(main, GS(idname), idname + 2))) {
+ /* printf("FOUND!\n"); */
+ /* Even though we found our linked ID, there is no guarantee its address is still the same... */
+ if (id != bhead->old) {
+ oldnewmap_insert(fd->libmap, bhead->old, id, GS(id->name));
+ }
+
+ /* No need to do anything else for ID_ID, it's assumed already present in its lib's main... */
+ if (r_id) {
+ *r_id = NULL; /* Just in case... */
+ }
+ return blo_nextbhead(fd, bhead);
+ }
+ /* printf("nothing...\n"); */
+ }
+ }
+
/* read libblock */
id = read_struct(fd, bhead, "lib block");
if (r_id)
@@ -7769,7 +7941,7 @@ static BHead *read_libblock(FileData *fd, Main *main, BHead *bhead, int flag, ID
if (id->flag & LIB_FAKEUSER) id->us= 1;
else id->us = 0;
id->icon_id = 0;
- id->flag &= ~(LIB_ID_RECALC|LIB_ID_RECALC_DATA|LIB_DOIT);
+ id->flag &= ~(LIB_ID_RECALC | LIB_ID_RECALC_DATA | LIB_DOIT | LIB_MISSING);
/* this case cannot be direct_linked: it's just the ID part */
if (bhead->code == ID_ID) {
@@ -8178,6 +8350,24 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
bfd->type = BLENFILETYPE_BLEND;
BLI_strncpy(bfd->main->name, filepath, sizeof(bfd->main->name));
+ if (G.background) {
+ /* We only read & store .blend thumbnail in background mode
+ * (because we cannot re-generate it, no OpenGL available).
+ */
+ const int *data = read_file_thumbnail(fd);
+
+ if (data) {
+ const size_t sz = BLEN_THUMB_MEMSIZE(data[0], data[1]);
+ bfd->main->blen_thumb = MEM_mallocN(sz, __func__);
+
+ BLI_assert((sz - sizeof(*bfd->main->blen_thumb)) ==
+ (BLEN_THUMB_MEMSIZE_FILE(data[0], data[1]) - (sizeof(*data) * 2)));
+ bfd->main->blen_thumb->width = data[0];
+ bfd->main->blen_thumb->height = data[1];
+ memcpy(bfd->main->blen_thumb->rect, &data[2], sz - sizeof(*bfd->main->blen_thumb));
+ }
+ }
+
while (bhead) {
switch (bhead->code) {
case DATA:
@@ -8196,26 +8386,10 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
bhead = NULL;
break;
- case ID_LI:
- /* skip library datablocks in undo, this works together with
- * BLO_read_from_memfile, where the old main->library is restored
- * overwriting the libraries from the memory file. previously
- * it did not save ID_LI/ID_ID blocks in this case, but they are
- * needed to make quit.blend recover them correctly. */
- if (fd->memfile)
- bhead = blo_nextbhead(fd, bhead);
- else
- bhead = read_libblock(fd, bfd->main, bhead, LIB_LOCAL, NULL);
- break;
case ID_ID:
- /* same as above */
- if (fd->memfile)
- bhead = blo_nextbhead(fd, bhead);
- else
- /* always adds to the most recently loaded
- * ID_LI block, see direct_link_library.
- * this is part of the file format definition. */
- bhead = read_libblock(fd, mainlist.last, bhead, LIB_READ+LIB_EXTERN, NULL);
+ /* Always adds to the most recently loaded ID_LI block, see direct_link_library.
+ * This is part of the file format definition. */
+ bhead = read_libblock(fd, mainlist.last, bhead, LIB_READ | LIB_EXTERN, NULL);
break;
/* in 2.50+ files, the file identifier for screens is patched, forward compatibility */
@@ -8228,10 +8402,10 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
}
/* do before read_libraries, but skip undo case */
- if (fd->memfile==NULL)
+ if (fd->memfile == NULL) {
do_versions(fd, NULL, bfd->main);
-
- do_versions_userdef(fd, bfd);
+ do_versions_userdef(fd, bfd);
+ }
read_libraries(fd, &mainlist);
@@ -8244,6 +8418,8 @@ BlendFileData *blo_read_file_internal(FileData *fd, const char *filepath)
link_global(fd, bfd); /* as last */
+ fd->mainlist = NULL; /* Safety, this is local variable, shall not be used afterward. */
+
return bfd;
}
@@ -8367,11 +8543,6 @@ static BHead *find_bhead_from_idname(FileData *fd, const char *idname)
#endif
}
-const char *bhead_id_name(const FileData *fd, const BHead *bhead)
-{
- return (const char *)POINTER_OFFSET(bhead, sizeof(*bhead) + fd->id_name_offs);
-}
-
static ID *is_yet_read(FileData *fd, Main *mainvar, BHead *bhead)
{
const char *idname= bhead_id_name(fd, bhead);
@@ -8457,7 +8628,7 @@ static void expand_doit_library(void *fdhandle, Main *mainvar, void *old)
}
}
-static void (*expand_doit)(void *, Main *, void *);
+static BLOExpandDoitCallback expand_doit;
// XXX deprecated - old animation system
static void expand_ipo(FileData *fd, Main *mainvar, Ipo *ipo)
@@ -8894,8 +9065,8 @@ static void expand_armature(FileData *fd, Main *mainvar, bArmature *arm)
#endif
}
-static void expand_object_expandModifiers(void *userData, Object *UNUSED(ob),
- ID **idpoin)
+static void expand_object_expandModifiers(
+ void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cd_flag))
{
struct { FileData *fd; Main *mainvar; } *data= userData;
@@ -9232,11 +9403,23 @@ static void expand_gpencil(FileData *fd, Main *mainvar, bGPdata *gpd)
expand_animdata(fd, mainvar, gpd->adt);
}
-void BLO_main_expander(void (*expand_doit_func)(void *, Main *, void *))
+/**
+ * Set the callback func used over all ID data found by \a BLO_expand_main func.
+ *
+ * \param expand_doit_func Called for each ID block it finds.
+ */
+void BLO_main_expander(BLOExpandDoitCallback expand_doit_func)
{
expand_doit = expand_doit_func;
}
+/**
+ * Loop over all ID data in Main to mark relations.
+ * Set (id->flag & LIB_NEED_EXPAND) to mark expanding. Flags get cleared after expanding.
+ *
+ * \param fdhandle usually filedata, or own handle.
+ * \param mainvar the Main database to expand.
+ */
void BLO_expand_main(void *fdhandle, Main *mainvar)
{
ListBase *lbarray[MAX_LIBARRAY];
@@ -9348,101 +9531,86 @@ static bool object_in_any_scene(Main *mainvar, Object *ob)
{
Scene *sce;
- for (sce= mainvar->scene.first; sce; sce= sce->id.next) {
- if (BKE_scene_base_find(sce, ob))
- return 1;
+ for (sce = mainvar->scene.first; sce; sce = sce->id.next) {
+ if (BKE_scene_base_find(sce, ob)) {
+ return true;
+ }
}
- return 0;
+ return false;
}
-static void give_base_to_objects(Main *mainvar, Scene *sce, Library *lib, const short idcode, const bool is_link, const short active_lay)
+static void give_base_to_objects(Main *mainvar, Scene *scene, View3D *v3d, Library *lib, const short flag)
{
Object *ob;
Base *base;
- const bool is_group_append = (is_link == false && idcode == ID_GR);
+ const unsigned int active_lay = (flag & FILE_ACTIVELAY) ? BKE_screen_view3d_layer_active(v3d, scene) : 0;
+ const bool is_link = (flag & FILE_LINK) != 0;
+
+ BLI_assert(scene);
/* give all objects which are LIB_INDIRECT a base, or for a group when *lib has been set */
for (ob = mainvar->object.first; ob; ob = ob->id.next) {
- if (ob->id.flag & LIB_INDIRECT) {
- /* IF below is quite confusing!
- * if we are appending, but this object wasnt just added along with a group,
- * then this is already used indirectly in the scene somewhere else and we didnt just append it.
- *
- * (ob->id.flag & LIB_PRE_EXISTING)==0 means that this is a newly appended object - Campbell */
- if (is_group_append==0 || (ob->id.flag & LIB_PRE_EXISTING)==0) {
- bool do_it = false;
-
- if (ob->id.us == 0) {
- do_it = true;
- }
- else if (idcode==ID_GR) {
- if ((is_link == false) && (ob->id.lib == lib)) {
- if ((ob->flag & OB_FROMGROUP) && object_in_any_scene(mainvar, ob)==0) {
- do_it = true;
- }
- }
- }
- else {
- /* when appending, make sure any indirectly loaded objects
- * get a base else they cant be accessed at all [#27437] */
- if ((is_link == false) && (ob->id.lib == lib)) {
- /* we may be appending from a scene where we already
- * have a linked object which is not in any scene [#27616] */
- if ((ob->id.flag & LIB_PRE_EXISTING)==0) {
- if (object_in_any_scene(mainvar, ob)==0) {
- do_it = true;
- }
- }
- }
- }
-
- if (do_it) {
- base = MEM_callocN(sizeof(Base), "add_ext_base");
- BLI_addtail(&sce->base, base);
-
- if (active_lay) ob->lay = sce->lay;
-
- base->lay = ob->lay;
- base->object = ob;
- base->flag = ob->flag;
+ if ((ob->id.flag & LIB_INDIRECT) && (ob->id.flag & LIB_PRE_EXISTING) == 0) {
+ bool do_it = false;
- CLAMP_MIN(ob->id.us, 0);
- ob->id.us += 1;
-
- ob->id.flag -= LIB_INDIRECT;
- ob->id.flag |= LIB_EXTERN;
+ if (ob->id.us == 0) {
+ do_it = true;
+ }
+ else if (!is_link && (ob->id.lib == lib) && (object_in_any_scene(mainvar, ob) == 0)) {
+ /* When appending, make sure any indirectly loaded objects get a base, else they cant be accessed at all
+ * (see T27437). */
+ do_it = true;
+ }
+
+ if (do_it) {
+ base = MEM_callocN(sizeof(Base), __func__);
+ BLI_addtail(&scene->base, base);
+
+ if (active_lay) {
+ ob->lay = active_lay;
}
+
+ base->lay = ob->lay;
+ base->object = ob;
+ base->flag = ob->flag;
+
+ CLAMP_MIN(ob->id.us, 0);
+ ob->id.us += 1;
+
+ ob->id.flag &= ~LIB_INDIRECT;
+ ob->id.flag |= LIB_EXTERN;
}
}
}
}
-static void give_base_to_groups(Main *mainvar, Scene *scene)
+static void give_base_to_groups(
+ Main *mainvar, Scene *scene, View3D *v3d, Library *UNUSED(lib), const short UNUSED(flag))
{
Group *group;
-
+ Base *base;
+ Object *ob;
+ const unsigned int active_lay = BKE_screen_view3d_layer_active(v3d, scene);
+
/* give all objects which are tagged a base */
for (group = mainvar->group.first; group; group = group->id.next) {
if (group->id.flag & LIB_DOIT) {
- Base *base;
- Object *ob;
-
/* any indirect group should not have been tagged */
- BLI_assert((group->id.flag & LIB_INDIRECT)==0);
-
+ BLI_assert((group->id.flag & LIB_INDIRECT) == 0);
+
/* BKE_object_add(...) messes with the selection */
ob = BKE_object_add_only_object(mainvar, OB_EMPTY, group->id.name + 2);
ob->type = OB_EMPTY;
- ob->lay = scene->lay;
-
+ ob->lay = active_lay;
+
/* assign the base */
base = BKE_scene_base_add(scene, ob);
base->flag |= SELECT;
- base->object->flag= base->flag;
+ base->object->flag = base->flag;
DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
scene->basact = base;
-
+
/* assign the group */
ob->dup_group = group;
ob->transflag |= OB_DUPLIGROUP;
@@ -9451,11 +9619,30 @@ static void give_base_to_groups(Main *mainvar, Scene *scene)
}
}
+static ID *create_placeholder(Main *mainvar, const char *idname, const short flag)
+{
+ const short idcode = GS(idname);
+ ListBase *lb = which_libbase(mainvar, idcode);
+ ID *ph_id = BKE_libblock_alloc_notest(idcode);
+
+ memcpy(ph_id->name, idname, sizeof(ph_id->name));
+ BKE_libblock_init_empty(ph_id);
+ ph_id->lib = mainvar->curlib;
+ ph_id->flag = flag | LIB_MISSING;
+ ph_id->us = (flag & LIB_FAKEUSER) ? 1 : 0;
+ ph_id->icon_id = 0;
+
+ BLI_addtail(lb, ph_id);
+ id_sort_by_name(lb, ph_id);
+
+ return ph_id;
+}
+
/* returns true if the item was found
* but it may already have already been appended/linked */
-static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, const short idcode)
+static ID *link_named_part(Main *mainl, FileData *fd, const short idcode, const char *name)
{
- BHead *bhead = find_bhead_from_code_name(fd, idcode, idname);
+ BHead *bhead = find_bhead_from_code_name(fd, idcode, name);
ID *id;
if (bhead) {
@@ -9491,8 +9678,10 @@ static ID *append_named_part(Main *mainl, FileData *fd, const char *idname, cons
return id;
}
-/* simple reader for copy/paste buffers */
-void BLO_library_append_all(Main *mainl, BlendHandle *bh)
+/**
+ * Simple reader for copy/paste buffers.
+ */
+void BLO_library_link_all(Main *mainl, BlendHandle *bh)
{
FileData *fd = (FileData *)(bh);
BHead *bhead;
@@ -9512,33 +9701,33 @@ void BLO_library_append_all(Main *mainl, BlendHandle *bh)
}
}
-
-static ID *append_named_part_ex(const bContext *C, Main *mainl, FileData *fd, const char *idname, const int idcode, const int flag)
+static ID *link_named_part_ex(
+ Main *mainl, FileData *fd, const short idcode, const char *name, const short flag,
+ Scene *scene, View3D *v3d)
{
- ID *id= append_named_part(mainl, fd, idname, idcode);
+ ID *id = link_named_part(mainl, fd, idcode, name);
if (id && (GS(id->name) == ID_OB)) { /* loose object: give a base */
- Scene *scene = CTX_data_scene(C); /* can be NULL */
if (scene) {
Base *base;
Object *ob;
-
- base= MEM_callocN(sizeof(Base), "app_nam_part");
+
+ base = MEM_callocN(sizeof(Base), "app_nam_part");
BLI_addtail(&scene->base, base);
-
+
ob = (Object *)id;
-
+
/* link at active layer (view3d if available in context, else scene one */
- if ((flag & FILE_ACTIVELAY)) {
- View3D *v3d = CTX_wm_view3d(C);
+ if (flag & FILE_ACTIVELAY) {
ob->lay = BKE_screen_view3d_layer_active(v3d, scene);
}
-
+
ob->mode = OB_MODE_OBJECT;
base->lay = ob->lay;
base->object = ob;
+ base->flag = ob->flag;
ob->id.us++;
-
+
if (flag & FILE_AUTOSELECT) {
base->flag |= SELECT;
base->object->flag = base->flag;
@@ -9547,47 +9736,91 @@ static ID *append_named_part_ex(const bContext *C, Main *mainl, FileData *fd, co
}
}
else if (id && (GS(id->name) == ID_GR)) {
- /* tag as needing to be instanced */
+ /* tag as needing to be instantiated */
if (flag & FILE_GROUP_INSTANCE)
id->flag |= LIB_DOIT;
}
-
+
return id;
}
-ID *BLO_library_append_named_part(Main *mainl, BlendHandle **bh, const char *idname, const int idcode)
+/**
+ * Link a named datablock from an external blend file.
+ *
+ * \param mainl The main database to link from (not the active one).
+ * \param bh The blender file handle.
+ * \param idcode The kind of datablock to link.
+ * \param name The name of the datablock (without the 2 char ID prefix).
+ * \return the linked ID when found.
+ */
+ID *BLO_library_link_named_part(Main *mainl, BlendHandle **bh, const short idcode, const char *name)
{
FileData *fd = (FileData*)(*bh);
- return append_named_part(mainl, fd, idname, idcode);
+ return link_named_part(mainl, fd, idcode, name);
}
-ID *BLO_library_append_named_part_ex(const bContext *C, Main *mainl, BlendHandle **bh, const char *idname, const int idcode, const short flag)
+/**
+ * Link a named datablock from an external blend file.
+ * Optionally instantiate the object/group in the scene when the flags are set.
+ *
+ * \param mainl The main database to link from (not the active one).
+ * \param bh The blender file handle.
+ * \param idcode The kind of datablock to link.
+ * \param name The name of the datablock (without the 2 char ID prefix).
+ * \param flag Options for linking, used for instantiating.
+ * \param scene The scene in which to instantiate objects/groups (if NULL, no instantiation is done).
+ * \param v3d The active View3D (only to define active layers for instantiated objects & groups, can be NULL).
+ * \return the linked ID when found.
+ */
+ID *BLO_library_link_named_part_ex(
+ Main *mainl, BlendHandle **bh,
+ const short idcode, const char *name, const short flag,
+ Scene *scene, View3D *v3d)
{
FileData *fd = (FileData*)(*bh);
- return append_named_part_ex(C, mainl, fd, idname, idcode, flag);
+ return link_named_part_ex(mainl, fd, idcode, name, flag, scene, v3d);
}
-static void append_id_part(FileData *fd, Main *mainvar, ID *id, ID **r_id)
+static void link_id_part(ReportList *reports, FileData *fd, Main *mainvar, ID *id, ID **r_id)
{
- BHead *bhead = find_bhead_from_idname(fd, id->name);
+ BHead *bhead = NULL;
+
+ if (fd) {
+ bhead = find_bhead_from_idname(fd, id->name);
+ }
+
+ id->flag &= ~LIB_READ;
if (bhead) {
- id->flag &= ~LIB_READ;
id->flag |= LIB_NEED_EXPAND;
// printf("read lib block %s\n", id->name);
read_libblock(fd, mainvar, bhead, id->flag, r_id);
}
+ else {
+ blo_reportf_wrap(
+ reports, RPT_WARNING,
+ TIP_("LIB ERROR: %s: '%s' missing from '%s', parent '%s'"),
+ BKE_idcode_to_name(GS(id->name)),
+ id->name + 2,
+ mainvar->curlib->filepath,
+ library_parent_filepath(mainvar->curlib));
+
+ /* Generate a placeholder for this ID (simplified version of read_libblock actually...). */
+ if (r_id) {
+ *r_id = create_placeholder(mainvar, id->name, id->flag);
+ }
+ }
}
/* common routine to append/link something from a library */
-static Main *library_append_begin(Main *mainvar, FileData **fd, const char *filepath)
+static Main *library_link_begin(Main *mainvar, FileData **fd, const char *filepath)
{
Main *mainl;
(*fd)->mainlist = MEM_callocN(sizeof(ListBase), "FileData.mainlist");
- /* clear for group instancing tag */
+ /* clear for group instantiating tag */
BKE_main_id_tag_listbase(&(mainvar->group), false);
/* make mains */
@@ -9606,77 +9839,72 @@ static Main *library_append_begin(Main *mainvar, FileData **fd, const char *file
return mainl;
}
-Main *BLO_library_append_begin(Main *mainvar, BlendHandle **bh, const char *filepath)
+/**
+ * Initialize the BlendHandle for linking library data.
+ *
+ * \param mainvar The current main database, e.g. G.main or CTX_data_main(C).
+ * \param bh A blender file handle as returned by \a BLO_blendhandle_from_file or \a BLO_blendhandle_from_memory.
+ * \param filepath Used for relative linking, copied to the \a lib->name.
+ * \return the library Main, to be passed to \a BLO_library_append_named_part as \a mainl.
+ */
+Main *BLO_library_link_begin(Main *mainvar, BlendHandle **bh, const char *filepath)
{
FileData *fd = (FileData*)(*bh);
- return library_append_begin(mainvar, &fd, filepath);
+ return library_link_begin(mainvar, &fd, filepath);
}
-
-/* Context == NULL signifies not to do any scene manipulation */
-static void library_append_end(const bContext *C, Main *mainl, FileData **fd, int idcode, short flag)
+/* scene and v3d may be NULL. */
+static void library_link_end(Main *mainl, FileData **fd, const short flag, Scene *scene, View3D *v3d)
{
Main *mainvar;
Library *curlib;
-
+
/* expander now is callback function */
BLO_main_expander(expand_doit_library);
-
+
/* make main consistent */
BLO_expand_main(*fd, mainl);
-
+
/* do this when expand found other libs */
read_libraries(*fd, (*fd)->mainlist);
-
+
curlib = mainl->curlib;
-
+
/* make the lib path relative if required */
if (flag & FILE_RELPATH) {
/* use the full path, this could have been read by other library even */
BLI_strncpy(curlib->name, curlib->filepath, sizeof(curlib->name));
-
+
/* uses current .blend file as reference */
BLI_path_rel(curlib->name, G.main->name);
}
-
+
blo_join_main((*fd)->mainlist);
mainvar = (*fd)->mainlist->first;
MEM_freeN((*fd)->mainlist);
mainl = NULL; /* blo_join_main free's mainl, cant use anymore */
-
+
lib_link_all(*fd, mainvar);
lib_verify_nodetree(mainvar, false);
fix_relpaths_library(G.main->name, mainvar); /* make all relative paths, relative to the open blend file */
-
- if (C) {
- Scene *scene = CTX_data_scene(C);
-
- /* give a base to loose objects. If group append, do it for objects too */
- if (scene) {
- const bool is_link = (flag & FILE_LINK) != 0;
- if (idcode == ID_SCE) {
- /* don't instance anything when linking in scenes, assume the scene its self instances the data */
- }
- else {
- give_base_to_objects(mainvar, scene, curlib, idcode, is_link, flag & FILE_ACTIVELAY);
-
- if (flag & FILE_GROUP_INSTANCE) {
- give_base_to_groups(mainvar, scene);
- }
- }
- }
- else {
- printf("library_append_end, scene is NULL (objects wont get bases)\n");
+
+ /* Give a base to loose objects. If group append, do it for objects too.
+ * Only directly linked objects & groups are instantiated by `BLO_library_link_named_part_ex()` & co,
+ * here we handle indirect ones and other possible edge-cases. */
+ if (scene) {
+ give_base_to_objects(mainvar, scene, v3d, curlib, flag);
+
+ if (flag & FILE_GROUP_INSTANCE) {
+ give_base_to_groups(mainvar, scene, v3d, curlib, flag);
}
}
+ else {
+ /* printf("library_append_end, scene is NULL (objects wont get bases)\n"); */
+ }
- /* clear group instancing tag */
+ /* clear group instantiating tag */
BKE_main_id_tag_listbase(&(mainvar->group), false);
-
- /* has been removed... erm, why? s..ton) */
- /* 20040907: looks like they are give base already in append_named_part(); -Nathan L */
- /* 20041208: put back. It only linked direct, not indirect objects (ton) */
-
+
/* patch to prevent switch_endian happens twice */
if ((*fd)->flags & FD_FLAGS_SWITCH_ENDIAN) {
blo_freefiledata(*fd);
@@ -9684,10 +9912,21 @@ static void library_append_end(const bContext *C, Main *mainl, FileData **fd, in
}
}
-void BLO_library_append_end(const bContext *C, struct Main *mainl, BlendHandle **bh, int idcode, short flag)
+/**
+ * Finalize linking from a given .blend file (library).
+ * Optionally instance the indirect object/group in the scene when the flags are set.
+ * \note Do not use \a bh after calling this function, it may frees it.
+ *
+ * \param mainl The main database to link from (not the active one).
+ * \param bh The blender file handle (WARNING! may be freed by this function!).
+ * \param flag Options for linking, used for instantiating.
+ * \param scene The scene in which to instantiate objects/groups (if NULL, no instantiation is done).
+ * \param v3d The active View3D (only to define active layers for instantiated objects & groups, can be NULL).
+ */
+void BLO_library_link_end(Main *mainl, BlendHandle **bh, short flag, Scene *scene, View3D *v3d)
{
FileData *fd = (FileData*)(*bh);
- library_append_end(C, mainl, &fd, idcode, flag);
+ library_link_end(mainl, &fd, flag, scene, v3d);
*bh = (BlendHandle*)fd;
}
@@ -9815,6 +10054,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
}
else {
mainptr->curlib->filedata = NULL;
+ mainptr->curlib->id.flag |= LIB_MISSING;
}
if (fd == NULL) {
@@ -9824,37 +10064,29 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
}
if (fd) {
do_it = true;
- a = set_listbasepointers(mainptr, lbarray);
- while (a--) {
- ID *id = lbarray[a]->first;
-
- while (id) {
- ID *idn = id->next;
- if (id->flag & LIB_READ) {
- ID *realid = NULL;
- BLI_remlink(lbarray[a], id);
-
- append_id_part(fd, mainptr, id, &realid);
- if (!realid) {
- blo_reportf_wrap(
- fd->reports, RPT_WARNING,
- TIP_("LIB ERROR: %s: '%s' missing from '%s', parent '%s'"),
- BKE_idcode_to_name(GS(id->name)),
- id->name + 2,
- mainptr->curlib->filepath,
- library_parent_filepath(mainptr->curlib));
- }
-
- change_idid_adr(mainlist, basefd, id, realid);
-
- MEM_freeN(id);
- }
- id = idn;
+ }
+ a = set_listbasepointers(mainptr, lbarray);
+ while (a--) {
+ ID *id = lbarray[a]->first;
+
+ while (id) {
+ ID *idn = id->next;
+ if (id->flag & LIB_READ) {
+ ID *realid = NULL;
+ BLI_remlink(lbarray[a], id);
+
+ link_id_part(basefd->reports, fd, mainptr, id, &realid);
+
+ BLI_assert(realid != NULL);
+
+ change_idid_adr(mainlist, basefd, id, realid);
+
+ MEM_freeN(id);
}
+ id = idn;
}
-
- BLO_expand_main(fd, mainptr);
}
+ BLO_expand_main(fd, mainptr);
}
mainptr = mainptr->next;
@@ -9862,6 +10094,7 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
}
/* test if there are unread libblocks */
+ /* XXX This code block is kept for 2.77, until we are sure it never gets reached anymore. Can be removed later. */
for (mainptr = mainl->next; mainptr; mainptr = mainptr->next) {
a = set_listbasepointers(mainptr, lbarray);
while (a--) {
@@ -9870,10 +10103,12 @@ static void read_libraries(FileData *basefd, ListBase *mainlist)
for (id = lbarray[a]->first; id; id = idn) {
idn = id->next;
if (id->flag & LIB_READ) {
+ BLI_assert(0);
BLI_remlink(lbarray[a], id);
blo_reportf_wrap(
basefd->reports, RPT_WARNING,
- TIP_("LIB ERROR: %s: '%s' unread lib block missing from '%s', parent '%s'"),
+ TIP_("LIB ERROR: %s: '%s' unread lib block missing from '%s', parent '%s' - "
+ "Please file a bug report if you see this message"),
BKE_idcode_to_name(GS(id->name)),
id->name + 2,
mainptr->curlib->filepath,