BKE main: Add 'merge' utils to mergeone Main content into another. #115671
|
@ -72,6 +72,13 @@ enum {
|
|||
* the 'separate' mesh operator.
|
||||
*/
|
||||
ID_REMAP_FORCE_OBDATA_IN_EDITMODE = 1 << 7,
|
||||
/** Do remapping of `lib` Library pointers of IDs (by default these are completely ignored).
|
||||
*
|
||||
* WARNING: Use with caution. This is currently a 'raw' remapping, with no further processing. In
|
||||
* particular, DO NOT use this to make IDs local (i.e. remap a library pointer to NULL), unless
|
||||
* the calling code takes care of the rest of the required changes (ID tags & flags updates,
|
||||
* etc.). */
|
||||
ID_REMAP_DO_LIBRARY_POINTERS = 1 << 8,
|
||||
|
||||
/**
|
||||
* Don't touch the special user counts (use when the 'old' remapped ID remains in use):
|
||||
|
|
|
@ -33,6 +33,7 @@ struct IDNameLib_Map;
|
|||
struct ImBuf;
|
||||
struct Library;
|
||||
struct MainLock;
|
||||
struct ReportList;
|
||||
struct UniqueName_Map;
|
||||
|
||||
/**
|
||||
|
@ -258,6 +259,41 @@ struct Main {
|
|||
Main *BKE_main_new(void);
|
||||
void BKE_main_free(Main *mainvar);
|
||||
|
||||
/** Struct packaging log/report info about a Main merge result. */
|
||||
struct MainMergeReport {
|
||||
ReportList *reports = nullptr;
|
||||
|
||||
/** Number of IDs from source Main that have been moved into destination Main. */
|
||||
int num_merged_ids = 0;
|
||||
/** Number of (non-library) IDs from source Main that were expected to have a matching ID in
|
||||
* destination Main, but did not. These have not been moved, and their usages have been remapped
|
||||
* to null. */
|
||||
int num_unknown_ids = 0;
|
||||
/** Number of (non-library) IDs from source Main that already had a matching ID in destination
|
||||
* Main. */
|
||||
int num_remapped_ids = 0;
|
||||
/** Number of Library IDs from source Main that already had a matching Library ID in destination
|
||||
* Main. */
|
||||
int num_remapped_libraries = 0;
|
||||
};
|
||||
|
||||
/** Merge the content of `bmain_src` into `bmain_dst`.
|
||||
*
|
||||
* In case of collision (ID from same library with same name), the existing ID in `bmain_dst` is
|
||||
* kept, the one from `bmain_src` is left in its original Main, and its usages in `bmain_dst` (from
|
||||
* newly moved-in IDs) are remapped to its matching counterpart already in `bmain_dst`.
|
||||
*
|
||||
* Libraries are also de-duplicated, based on their absolute filepath, and remapped accordingly.
|
||||
* Note that local IDs in source Main always remain local IDs in destination Main.
|
||||
*
|
||||
* In case some source IDs are linked data from the blendfile of `bmain_dst`, they are never moved.
|
||||
* If a matching destination local ID is found, their usage get remapped as expected, otherwise
|
||||
* they are dropped, their usages are remapped to null, and a warning is printed.
|
||||
*
|
||||
* Since `bmain_src` is either empty or contains left-over IDs with (likely) invalid ID
|
||||
* relationships and other potential issues after the merge, it is always freed. */
|
||||
void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &reports);
|
||||
|
||||
/**
|
||||
* Check whether given `bmain` is empty or contains some IDs.
|
||||
*/
|
||||
|
|
|
@ -847,6 +847,7 @@ if(WITH_GTESTS)
|
|||
intern/lib_id_remapper_test.cc
|
||||
intern/lib_id_test.cc
|
||||
intern/lib_remap_test.cc
|
||||
intern/main_test.cc
|
||||
intern/nla_test.cc
|
||||
intern/tracking_test.cc
|
||||
)
|
||||
|
|
|
@ -487,13 +487,15 @@ static void libblock_remap_data(
|
|||
{
|
||||
IDRemap id_remap_data = {eIDRemapType(0)};
|
||||
const bool include_ui = (remap_flags & ID_REMAP_FORCE_UI_POINTERS) != 0;
|
||||
const int foreach_id_flags = (((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
|
||||
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
|
||||
IDWALK_NOP) |
|
||||
(include_ui ? IDWALK_INCLUDE_UI : IDWALK_NOP) |
|
||||
((remap_flags & ID_REMAP_NO_ORIG_POINTERS_ACCESS) != 0 ?
|
||||
IDWALK_NO_ORIG_POINTERS_ACCESS :
|
||||
IDWALK_NOP));
|
||||
const int foreach_id_flags =
|
||||
(((remap_flags & ID_REMAP_FORCE_INTERNAL_RUNTIME_POINTERS) != 0 ?
|
||||
IDWALK_DO_INTERNAL_RUNTIME_POINTERS :
|
||||
IDWALK_NOP) |
|
||||
(include_ui ? IDWALK_INCLUDE_UI : IDWALK_NOP) |
|
||||
|
||||
((remap_flags & ID_REMAP_NO_ORIG_POINTERS_ACCESS) != 0 ? IDWALK_NO_ORIG_POINTERS_ACCESS :
|
||||
IDWALK_NOP) |
|
||||
((remap_flags & ID_REMAP_DO_LIBRARY_POINTERS) != 0 ? IDWALK_DO_LIBRARY_POINTER : IDWALK_NOP));
|
||||
|
||||
id_remap_data.id_remapper = id_remapper;
|
||||
id_remap_data.type = remap_type;
|
||||
|
|
|
@ -9,27 +9,37 @@
|
|||
*/
|
||||
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
|
||||
#include "CLG_log.h"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mempool.h"
|
||||
#include "BLI_threads.h"
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_bpath.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_query.h"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_idmap.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
#include "BKE_report.h"
|
||||
|
||||
#include "IMB_imbuf.h"
|
||||
#include "IMB_imbuf_types.h"
|
||||
|
||||
static CLG_LogRef LOG = {"bke.main"};
|
||||
|
||||
Main *BKE_main_new()
|
||||
{
|
||||
Main *bmain = static_cast<Main *>(MEM_callocN(sizeof(Main), "new main"));
|
||||
|
@ -156,6 +166,271 @@ void BKE_main_free(Main *mainvar)
|
|||
MEM_freeN(mainvar);
|
||||
}
|
||||
|
||||
static bool are_ids_from_different_mains_matching(Main *bmain_1, ID *id_1, Main *bmain_2, ID *id_2)
|
||||
{
|
||||
/* Both IDs should not be null at the same time.
|
||||
*
|
||||
* NOTE: E.g. `id_1` may be null, in case `id_2` is a Library ID which path is the filepath of
|
||||
* `bmain_1`. */
|
||||
BLI_assert(id_1 || id_2);
|
||||
|
||||
/* Special handling for libraries, since their filepaths is used then, not their ID names.
|
||||
*
|
||||
* NOTE: In library case, this call should always return true, since given data should always
|
||||
* match. The asserts below merely ensure that expected conditions are always met:
|
||||
* - A given library absolute filepath should never match its own bmain filepath.
|
||||
* - If both given libraries are non-null:
|
||||
* - Their absolute filepath should match.
|
||||
* - Neither of their absolute filepaths should match any of the bmain filepaths.
|
||||
* - If one of the library is null:
|
||||
* - The other library should match the bmain filepath of the null library. */
|
||||
if ((!id_1 && GS(id_2->name) == ID_LI) || GS(id_1->name) == ID_LI) {
|
||||
BLI_assert(!id_1 || !ID_IS_LINKED(id_1));
|
||||
BLI_assert(!id_2 || !ID_IS_LINKED(id_2));
|
||||
|
||||
Library *lib_1 = reinterpret_cast<Library *>(id_1);
|
||||
Library *lib_2 = reinterpret_cast<Library *>(id_2);
|
||||
|
||||
if (lib_1 && lib_2) {
|
||||
BLI_assert(STREQ(lib_1->filepath_abs, lib_2->filepath_abs));
|
||||
}
|
||||
if (lib_1) {
|
||||
BLI_assert(!STREQ(lib_1->filepath_abs, bmain_1->filepath));
|
||||
if (lib_2) {
|
||||
BLI_assert(!STREQ(lib_1->filepath_abs, bmain_2->filepath));
|
||||
}
|
||||
else {
|
||||
BLI_assert(STREQ(lib_1->filepath_abs, bmain_2->filepath));
|
||||
}
|
||||
}
|
||||
if (lib_2) {
|
||||
BLI_assert(!STREQ(lib_2->filepath_abs, bmain_2->filepath));
|
||||
if (lib_1) {
|
||||
BLI_assert(!STREQ(lib_2->filepath_abs, bmain_1->filepath));
|
||||
}
|
||||
else {
|
||||
BLI_assert(STREQ(lib_2->filepath_abs, bmain_1->filepath));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Now both IDs are expected to be valid data, and caller is expected to have ensured already
|
||||
* that they have the same name. */
|
||||
BLI_assert(id_1 && id_2);
|
||||
BLI_assert(STREQ(id_1->name, id_2->name));
|
||||
|
||||
if (!id_1->lib && !id_2->lib) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (id_1->lib && id_2->lib) {
|
||||
if (id_1->lib == id_2->lib) {
|
||||
return true;
|
||||
}
|
||||
if (STREQ(id_1->lib->filepath_abs, id_2->lib->filepath_abs)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/* In case one Main is the library of the ID from the other Main. */
|
||||
|
||||
if (id_1->lib) {
|
||||
if (STREQ(id_1->lib->filepath_abs, bmain_2->filepath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (id_2->lib) {
|
||||
if (STREQ(id_2->lib->filepath_abs, bmain_1->filepath)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
BLI_assert_unreachable();
|
||||
return false;
|
||||
}
|
||||
|
||||
static void main_merge_add_id_to_move(Main *bmain_dst,
|
||||
blender::Map<std::string, blender::Vector<ID *>> &id_map_dst,
|
||||
ID *id_src,
|
||||
IDRemapper *id_remapper,
|
||||
blender::Vector<ID *> &ids_to_move,
|
||||
const bool is_library,
|
||||
MainMergeReport &reports)
|
||||
{
|
||||
const bool is_id_src_linked(id_src->lib);
|
||||
bool is_id_src_from_bmain_dst = false;
|
||||
if (is_id_src_linked) {
|
||||
BLI_assert(!is_library);
|
||||
blender::Vector<ID *> id_src_lib_dst = id_map_dst.lookup_default(id_src->lib->filepath_abs,
|
||||
{});
|
||||
/* The current library of the source ID would be remapped to null, which means that it comes
|
||||
* from the destination Main. */
|
||||
is_id_src_from_bmain_dst = !id_src_lib_dst.is_empty() && !id_src_lib_dst[0];
|
||||
}
|
||||
std::cout << id_src->name << " is linked from dst Main: " << is_id_src_from_bmain_dst << "\n";
|
||||
std::cout.flush();
|
||||
|
||||
if (is_id_src_from_bmain_dst) {
|
||||
/* Do not move an ID supposed to be from `bmain_dst` (used as library in `bmain_src`) into
|
||||
* `bmain_src`. Fact that no match was found is worth a warning, although it could happen
|
||||
* e.g. in case `bmain_dst` has been updated since it file was loaded as library in
|
||||
* `bmain_src`. */
|
||||
CLOG_WARN(&LOG,
|
||||
"ID '%s' defined in source Main as linked from destination Main (file '%s') not "
|
||||
"found in given destination Main",
|
||||
id_src->name,
|
||||
bmain_dst->filepath);
|
||||
BKE_id_remapper_add(id_remapper, id_src, nullptr);
|
||||
reports.num_unknown_ids++;
|
||||
}
|
||||
else {
|
||||
ids_to_move.append(id_src);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_main_merge(Main *bmain_dst, Main **r_bmain_src, MainMergeReport &reports)
|
||||
{
|
||||
Main *bmain_src = *r_bmain_src;
|
||||
/* NOTE: Dedicated mapping type is needed here, to handle propoerly the library cases. */
|
||||
blender::Map<std::string, blender::Vector<ID *>> id_map_dst;
|
||||
ID *id_iter_dst, *id_iter_src;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain_dst, id_iter_dst) {
|
||||
if (GS(id_iter_dst->name) == ID_LI) {
|
||||
/* Libraries need specific handling, as we want to check them by their filepath, not the IDs
|
||||
* themselves. */
|
||||
Library *lib_dst = reinterpret_cast<Library *>(id_iter_dst);
|
||||
BLI_assert(!id_map_dst.contains(lib_dst->filepath_abs));
|
||||
id_map_dst.add(lib_dst->filepath_abs, {id_iter_dst});
|
||||
}
|
||||
else {
|
||||
id_map_dst.lookup_or_add(id_iter_dst->name, {}).append(id_iter_dst);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
/* Add the current `bmain_dst` filepath in the mapping as well, as it may be a library of the
|
||||
* `bmain_src` Main. */
|
||||
id_map_dst.add(bmain_dst->filepath, {nullptr});
|
||||
|
||||
/* A dedicated remapper for libraries is needed because these need to be remapped _before_ IDs
|
||||
* are moved from `bmain_src` to `bmain_dst`, to avoid having to fix naming and ordering of IDs
|
||||
* afterwards (especially in case some source linked IDs become local in `bmain_dst`). */
|
||||
IDRemapper *id_remapper = BKE_id_remapper_create();
|
||||
IDRemapper *id_remapper_libraries = BKE_id_remapper_create();
|
||||
blender::Vector<ID *> ids_to_move;
|
||||
|
||||
FOREACH_MAIN_ID_BEGIN (bmain_src, id_iter_src) {
|
||||
const bool is_library = GS(id_iter_src->name) == ID_LI;
|
||||
|
||||
blender::Vector<ID *> ids_dst = id_map_dst.lookup_default(
|
||||
is_library ? reinterpret_cast<Library *>(id_iter_src)->filepath_abs : id_iter_src->name,
|
||||
{});
|
||||
if (is_library) {
|
||||
BLI_assert(ids_dst.size() <= 1);
|
||||
}
|
||||
if (ids_dst.is_empty()) {
|
||||
main_merge_add_id_to_move(
|
||||
bmain_dst, id_map_dst, id_iter_src, id_remapper, ids_to_move, is_library, reports);
|
||||
continue;
|
||||
}
|
||||
|
||||
bool src_has_match_in_dst = false;
|
||||
for (ID *id_iter_dst : ids_dst) {
|
||||
if (are_ids_from_different_mains_matching(bmain_dst, id_iter_dst, bmain_src, id_iter_src)) {
|
||||
/* There should only ever be one potential match, never more. */
|
||||
BLI_assert(!src_has_match_in_dst);
|
||||
if (!src_has_match_in_dst) {
|
||||
if (is_library) {
|
||||
BKE_id_remapper_add(id_remapper_libraries, id_iter_src, id_iter_dst);
|
||||
reports.num_remapped_libraries++;
|
||||
}
|
||||
else {
|
||||
BKE_id_remapper_add(id_remapper, id_iter_src, id_iter_dst);
|
||||
reports.num_remapped_ids++;
|
||||
}
|
||||
src_has_match_in_dst = true;
|
||||
}
|
||||
#ifdef NDEBUG /* In DEBUG builds, keep looping to ensure there is only one match. */
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
if (!src_has_match_in_dst) {
|
||||
main_merge_add_id_to_move(
|
||||
bmain_dst, id_map_dst, id_iter_src, id_remapper, ids_to_move, is_library, reports);
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
reports.num_merged_ids = int(ids_to_move.size());
|
||||
|
||||
/* Rebase relative filepaths in `bmain_src` using `bmain_dst` path as new reference, or make them
|
||||
* absolute if destination bmain has no filepath. */
|
||||
if (bmain_src->filepath[0] != '\0') {
|
||||
char dir_src[FILE_MAXDIR];
|
||||
BLI_path_split_dir_part(bmain_src->filepath, dir_src, sizeof(dir_src));
|
||||
BLI_path_normalize_native(dir_src);
|
||||
|
||||
if (bmain_dst->filepath[0] != '\0') {
|
||||
char dir_dst[FILE_MAXDIR];
|
||||
BLI_path_split_dir_part(bmain_dst->filepath, dir_dst, sizeof(dir_dst));
|
||||
BLI_path_normalize_native(dir_dst);
|
||||
BKE_bpath_relative_rebase(bmain_src, dir_src, dir_dst, reports.reports);
|
||||
}
|
||||
else {
|
||||
BKE_bpath_absolute_convert(bmain_src, dir_src, reports.reports);
|
||||
}
|
||||
}
|
||||
|
||||
/* Libraries need to be remapped before moving IDs into `bmain_dst`, to ensure that the sorting
|
||||
* of inserted IDs is correct. Note that no bmain is given here, so this is only a 'raw'
|
||||
* remapping. */
|
||||
BKE_libblock_relink_multiple(nullptr,
|
||||
ids_to_move,
|
||||
ID_REMAP_TYPE_REMAP,
|
||||
id_remapper_libraries,
|
||||
ID_REMAP_DO_LIBRARY_POINTERS);
|
||||
|
||||
for (ID *id_iter_src : ids_to_move) {
|
||||
BKE_libblock_management_main_remove(bmain_src, id_iter_src);
|
||||
BKE_libblock_management_main_add(bmain_dst, id_iter_src);
|
||||
}
|
||||
|
||||
/* The other data has to be remapped once all IDs are in `bmain_dst`, to ensure that additional
|
||||
* update process (e.g. collection hierarchy handling) happens as expected with the correct set
|
||||
* of data. */
|
||||
BKE_libblock_relink_multiple(bmain_dst, ids_to_move, ID_REMAP_TYPE_REMAP, id_remapper, 0);
|
||||
|
||||
BKE_reportf(
|
||||
reports.reports,
|
||||
RPT_INFO,
|
||||
"Merged %d IDs from '%s' Main into '%s' Main; %d IDs and %d Libraries already existed as "
|
||||
"part of the destination Main, and %d IDs missing from desination Main, were freed together "
|
||||
"with the source Main",
|
||||
reports.num_merged_ids,
|
||||
bmain_src->filepath,
|
||||
bmain_dst->filepath,
|
||||
reports.num_remapped_ids,
|
||||
reports.num_remapped_libraries,
|
||||
reports.num_unknown_ids);
|
||||
|
||||
/* Remapping above may have made some IDs local. So namemap needs to be cleared, and moved IDs
|
||||
* need to be re-sorted. */
|
||||
BKE_main_namemap_clear(bmain_dst);
|
||||
|
||||
BLI_assert(BKE_main_namemap_validate(bmain_dst));
|
||||
|
||||
BKE_id_remapper_free(id_remapper);
|
||||
BKE_id_remapper_free(id_remapper_libraries);
|
||||
BKE_main_free(bmain_src);
|
||||
*r_bmain_src = nullptr;
|
||||
}
|
||||
|
||||
bool BKE_main_is_empty(Main *bmain)
|
||||
{
|
||||
bool result = true;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
#include "testing/testing.h"
|
||||
|
@ -8,14 +8,14 @@
|
|||
#include "CLG_log.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_path_util.h"
|
||||
#include "BLI_string.h"
|
||||
|
||||
#include "BKE_collection.h"
|
||||
#include "BKE_idtype.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_library.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_main_namemap.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_collection_types.h"
|
||||
|
@ -78,10 +78,15 @@ TEST_F(BMainMergeTest, basics)
|
|||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
MainMergeReport reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
|
||||
EXPECT_EQ(2, reports.num_merged_ids);
|
||||
EXPECT_EQ(0, reports.num_unknown_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
|
||||
bmain_src = BKE_main_new();
|
||||
|
@ -94,12 +99,17 @@ TEST_F(BMainMergeTest, basics)
|
|||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
/* The second `Ob_src` object in `bmain_src` cannot be merged in `bmain_dst`, since its name
|
||||
* would collide with the first object. */
|
||||
EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
|
||||
EXPECT_EQ(1, reports.num_merged_ids);
|
||||
EXPECT_EQ(0, reports.num_unknown_ids);
|
||||
EXPECT_EQ(1, reports.num_remapped_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
|
||||
/* `Coll_src_2` should have been remapped to using `Ob_src` in `bmain_dst`, instead of `Ob_src`
|
||||
|
@ -110,12 +120,18 @@ TEST_F(BMainMergeTest, basics)
|
|||
|
||||
TEST_F(BMainMergeTest, linked_data)
|
||||
{
|
||||
constexpr char *DST_PATH = "/tmp/dst/dst.blend";
|
||||
constexpr char *SRC_PATH = "/tmp/src/src.blend";
|
||||
constexpr char *LIB_PATH = "/tmp/lib/lib.blend";
|
||||
#ifdef WIN32
|
||||
# define ABS_ROOT "C:" SEP_STR
|
||||
#else
|
||||
# define ABS_ROOT SEP_STR
|
||||
#endif
|
||||
constexpr char DST_PATH[] = ABS_ROOT "tmp" SEP_STR "dst" SEP_STR "dst.blend";
|
||||
constexpr char SRC_PATH[] = ABS_ROOT "tmp" SEP_STR "src" SEP_STR "src.blend";
|
||||
constexpr char LIB_PATH[] = ABS_ROOT "tmp" SEP_STR "lib" SEP_STR "lib.blend";
|
||||
|
||||
constexpr char *LIB_PATH_RELATIVE = "//lib/lib.blend";
|
||||
constexpr char *LIB_PATH_RELATIVE_ABS_SRC = "/tmp/src/lib/lib.blend";
|
||||
constexpr char LIB_PATH_RELATIVE[] = "//lib" SEP_STR "lib.blend";
|
||||
constexpr char LIB_PATH_RELATIVE_ABS_SRC[] = ABS_ROOT "tmp" SEP_STR "src" SEP_STR "lib" SEP_STR
|
||||
"lib.blend";
|
||||
|
||||
EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->libraries));
|
||||
EXPECT_TRUE(BLI_listbase_is_empty(&bmain_dst->collections));
|
||||
|
@ -144,7 +160,8 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
MainMergeReport reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_dst->objects));
|
||||
|
@ -152,6 +169,10 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_EQ(ob_1, bmain_dst->objects.first);
|
||||
EXPECT_EQ(lib_src_1, bmain_dst->libraries.first);
|
||||
EXPECT_EQ(ob_1->id.lib, lib_src_1);
|
||||
EXPECT_EQ(3, reports.num_merged_ids);
|
||||
EXPECT_EQ(0, reports.num_unknown_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
|
||||
/* Try another merge, with the same library path - second library should be skipped, destination
|
||||
|
@ -172,7 +193,8 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->objects));
|
||||
|
@ -182,6 +204,10 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_EQ(lib_src_1, bmain_dst->libraries.first);
|
||||
EXPECT_EQ(ob_1->id.lib, lib_src_1);
|
||||
EXPECT_EQ(ob_2->id.lib, lib_src_1);
|
||||
EXPECT_EQ(2, reports.num_merged_ids);
|
||||
EXPECT_EQ(0, reports.num_unknown_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_ids);
|
||||
EXPECT_EQ(1, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
|
||||
/* Use a relative library path. Since this is a different library, even though the object re-use
|
||||
|
@ -203,7 +229,8 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_TRUE(STREQ(lib_src_3->filepath, LIB_PATH_RELATIVE));
|
||||
EXPECT_TRUE(STREQ(lib_src_3->filepath_abs, LIB_PATH_RELATIVE_ABS_SRC));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
EXPECT_EQ(4, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->objects));
|
||||
|
@ -217,6 +244,10 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
EXPECT_EQ(ob_3->id.lib, lib_src_3);
|
||||
EXPECT_FALSE(STREQ(lib_src_3->filepath, LIB_PATH_RELATIVE));
|
||||
EXPECT_TRUE(STREQ(lib_src_3->filepath_abs, LIB_PATH_RELATIVE_ABS_SRC));
|
||||
EXPECT_EQ(3, reports.num_merged_ids);
|
||||
EXPECT_EQ(0, reports.num_unknown_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_ids);
|
||||
EXPECT_EQ(0, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
|
||||
/* Try another merge, with the library path set to the path of the destination bmain. That source
|
||||
|
@ -225,31 +256,35 @@ TEST_F(BMainMergeTest, linked_data)
|
|||
bmain_src = BKE_main_new();
|
||||
BLI_strncpy(bmain_src->filepath, SRC_PATH, sizeof(bmain_dst->filepath));
|
||||
|
||||
Collection *coll_4 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src_4"));
|
||||
Collection *coll_4 = static_cast<Collection *>(BKE_id_new(bmain_src, ID_GR, "Coll_src"));
|
||||
Object *ob_4 = static_cast<Object *>(BKE_id_new(bmain_src, ID_OB, "Ob_src_4"));
|
||||
BKE_collection_object_add(bmain_src, coll_4, ob_4);
|
||||
Library *lib_src_4 = static_cast<Library *>(BKE_id_new(bmain_src, ID_LI, DST_PATH));
|
||||
BKE_library_filepath_set(bmain_src, lib_src_4, DST_PATH);
|
||||
coll_4->id.lib = lib_src_4;
|
||||
ob_4->id.lib = lib_src_4;
|
||||
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->collections));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->objects));
|
||||
EXPECT_EQ(1, BLI_listbase_count(&bmain_src->libraries));
|
||||
|
||||
BKE_main_merge(bmain_dst, &bmain_src, nullptr);
|
||||
reports = {};
|
||||
BKE_main_merge(bmain_dst, &bmain_src, reports);
|
||||
|
||||
EXPECT_EQ(5, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(4, BLI_listbase_count(&bmain_dst->objects));
|
||||
/* `bmain_dst` is unchanged, since both `coll_4` and `ob_4` were defined as linked from
|
||||
* `bmain_dst`. */
|
||||
EXPECT_EQ(4, BLI_listbase_count(&bmain_dst->collections));
|
||||
EXPECT_EQ(3, BLI_listbase_count(&bmain_dst->objects));
|
||||
EXPECT_EQ(2, BLI_listbase_count(&bmain_dst->libraries));
|
||||
EXPECT_EQ(ob_1, bmain_dst->objects.first);
|
||||
/* `ob_4` is now local in `bmain_dst`, so should come before linked ones. */
|
||||
EXPECT_EQ(ob_4, ob_1->id.prev);
|
||||
EXPECT_EQ(lib_src_3, bmain_dst->libraries.first);
|
||||
EXPECT_EQ(lib_src_1, bmain_dst->libraries.last);
|
||||
EXPECT_EQ(ob_1->id.lib, lib_src_1);
|
||||
EXPECT_EQ(ob_2->id.lib, lib_src_1);
|
||||
EXPECT_EQ(ob_3->id.lib, lib_src_3);
|
||||
EXPECT_EQ(ob_4->id.lib, nullptr);
|
||||
EXPECT_EQ(0, reports.num_merged_ids);
|
||||
EXPECT_EQ(1, reports.num_unknown_ids);
|
||||
EXPECT_EQ(1, reports.num_remapped_ids);
|
||||
EXPECT_EQ(1, reports.num_remapped_libraries);
|
||||
EXPECT_EQ(nullptr, bmain_src);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue