Merge branch 'blender-v4.1-release'
This commit is contained in:
@@ -15,3 +15,9 @@ struct Library;
|
||||
struct Main;
|
||||
|
||||
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath);
|
||||
|
||||
/**
|
||||
* Rebuild the hierarchy of libraries, after e.g. deleting or relocating one, often some indirectly
|
||||
* linked libraries lose their 'parent' pointer, making them wrongly directly used ones.
|
||||
*/
|
||||
void BKE_library_main_rebuild_hierarchy(Main *bmain);
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "BKE_lib_override.hh"
|
||||
#include "BKE_lib_query.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
#include "BKE_material.h"
|
||||
@@ -1916,6 +1917,8 @@ void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
|
||||
/* Resync overrides if needed. */
|
||||
if (!USER_EXPERIMENTAL_TEST(&U, no_override_auto_resync)) {
|
||||
BlendFileReadReport report{};
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_lib_override.hh"
|
||||
#include "BKE_lib_remap.hh"
|
||||
#include "BKE_library.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_main_namemap.hh"
|
||||
|
||||
@@ -210,9 +211,15 @@ void BKE_id_free_us(Main *bmain, void *idv) /* test users */
|
||||
}
|
||||
|
||||
if (id->us == 0) {
|
||||
const bool is_lib = GS(id->name) == ID_LI;
|
||||
|
||||
BKE_libblock_unlink(bmain, id, false);
|
||||
|
||||
BKE_id_free(bmain, id);
|
||||
|
||||
if (is_lib) {
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,6 +227,8 @@ static size_t id_delete(Main *bmain,
|
||||
blender::Set<ID *> &ids_to_delete,
|
||||
const int extra_remapping_flags)
|
||||
{
|
||||
bool has_deleted_library = false;
|
||||
|
||||
/* Used by batch tagged deletion, when we call BKE_id_free then, id is no more in Main database,
|
||||
* and has already properly unlinked its other IDs usages.
|
||||
* UI users are always cleared in BKE_libblock_remap_locked() call, so we can always skip it. */
|
||||
@@ -318,6 +327,10 @@ static size_t id_delete(Main *bmain,
|
||||
* remapping code, depending on order in which these are handled). */
|
||||
id->us = ID_FAKE_USERS(id);
|
||||
|
||||
if (!has_deleted_library && GS(id->name) == ID_LI) {
|
||||
has_deleted_library = true;
|
||||
}
|
||||
|
||||
id_free(bmain, id, free_flag, false);
|
||||
}
|
||||
|
||||
@@ -325,6 +338,10 @@ static size_t id_delete(Main *bmain,
|
||||
BKE_layer_collection_resync_allow();
|
||||
BKE_main_collection_sync_remap(bmain);
|
||||
|
||||
if (has_deleted_library) {
|
||||
BKE_library_main_rebuild_hierarchy(bmain);
|
||||
}
|
||||
|
||||
bmain->is_memfile_undo_written = false;
|
||||
return size_t(ids_to_delete.size());
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_blenlib.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "BLT_translation.hh"
|
||||
|
||||
@@ -132,3 +134,158 @@ void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
|
||||
BLI_path_abs(lib->filepath_abs, blendfile_path);
|
||||
}
|
||||
}
|
||||
|
||||
static void rebuild_hierarchy_best_parent_find(Main *bmain,
|
||||
blender::Set<Library *> &directly_used_libs,
|
||||
Library *lib)
|
||||
{
|
||||
BLI_assert(!directly_used_libs.contains(lib));
|
||||
|
||||
Library *best_parent_lib = nullptr;
|
||||
bool do_break = false;
|
||||
ListBase *lb;
|
||||
ID *id_iter;
|
||||
FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) {
|
||||
FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) {
|
||||
if (!ID_IS_LINKED(id_iter) || id_iter->lib != lib) {
|
||||
continue;
|
||||
}
|
||||
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
|
||||
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
|
||||
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
|
||||
ID *from_id = item->id_pointer.from;
|
||||
if (!ID_IS_LINKED(from_id)) {
|
||||
BLI_assert_unreachable();
|
||||
continue;
|
||||
}
|
||||
Library *from_id_lib = from_id->lib;
|
||||
if (from_id_lib == lib) {
|
||||
continue;
|
||||
}
|
||||
if (directly_used_libs.contains(from_id_lib)) {
|
||||
/* Found the first best possible candidate, no need to search further. */
|
||||
BLI_assert(best_parent_lib == nullptr || best_parent_lib->temp_index > 0);
|
||||
best_parent_lib = from_id_lib;
|
||||
do_break = true;
|
||||
break;
|
||||
}
|
||||
if (!from_id_lib->parent) {
|
||||
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, from_id_lib);
|
||||
}
|
||||
if (!best_parent_lib || best_parent_lib->temp_index > from_id_lib->temp_index) {
|
||||
best_parent_lib = from_id_lib;
|
||||
if (best_parent_lib->temp_index == 0) {
|
||||
/* Found the first best possible candidate, no need to search further. */
|
||||
BLI_assert(directly_used_libs.contains(best_parent_lib));
|
||||
do_break = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (do_break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_ID_END;
|
||||
if (do_break) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_LISTBASE_END;
|
||||
|
||||
/* NOTE: It may happen that no parent library is found, e.g. if after deleting a directly used
|
||||
* library, its indirect dependency is still around, but none of its linked IDs are used by local
|
||||
* data. */
|
||||
if (best_parent_lib) {
|
||||
lib->parent = best_parent_lib;
|
||||
lib->temp_index = best_parent_lib->temp_index + 1;
|
||||
}
|
||||
else {
|
||||
lib->parent = nullptr;
|
||||
lib->temp_index = 0;
|
||||
directly_used_libs.add(lib);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_library_main_rebuild_hierarchy(Main *bmain)
|
||||
{
|
||||
BKE_main_relations_create(bmain, 0);
|
||||
|
||||
/* Find all libraries with directly linked IDs (i.e. IDs used by local data). */
|
||||
blender::Set<Library *> directly_used_libs;
|
||||
ID *id_iter;
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id_iter) {
|
||||
if (!ID_IS_LINKED(id_iter)) {
|
||||
continue;
|
||||
}
|
||||
id_iter->lib->temp_index = 0;
|
||||
if (directly_used_libs.contains(id_iter->lib)) {
|
||||
continue;
|
||||
}
|
||||
MainIDRelationsEntry *entry = static_cast<MainIDRelationsEntry *>(
|
||||
BLI_ghash_lookup(bmain->relations->relations_from_pointers, id_iter));
|
||||
for (MainIDRelationsEntryItem *item = entry->from_ids; item; item = item->next) {
|
||||
if (!ID_IS_LINKED(item->id_pointer.from)) {
|
||||
directly_used_libs.add(id_iter->lib);
|
||||
id_iter->lib->parent = nullptr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
|
||||
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
||||
/* A directly used library. */
|
||||
if (directly_used_libs.contains(lib_iter)) {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Assume existing parent is still valid, since it was not cleared in previous loop above.
|
||||
* Just compute 'hierarchy value' in temp index, if needed. */
|
||||
if (lib_iter->parent) {
|
||||
if (lib_iter->temp_index > 0) {
|
||||
continue;
|
||||
}
|
||||
blender::Vector<Library *> parent_libraries;
|
||||
for (Library *parent_lib_iter = lib_iter;
|
||||
parent_lib_iter && parent_lib_iter->temp_index == 0;
|
||||
parent_lib_iter = parent_lib_iter->parent)
|
||||
{
|
||||
parent_libraries.append(parent_lib_iter);
|
||||
}
|
||||
int parent_temp_index = parent_libraries.last()->temp_index + int(parent_libraries.size()) -
|
||||
1;
|
||||
for (Library *parent_lib_iter : parent_libraries) {
|
||||
BLI_assert(parent_lib_iter != parent_libraries.last() ||
|
||||
parent_lib_iter->temp_index == parent_temp_index);
|
||||
parent_lib_iter->temp_index = parent_temp_index--;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Otherwise, it's an indirectly used library with no known parent, another loop is needed to
|
||||
* ansure all knwon hierarcy has valid indices when trying to find the best valid parent
|
||||
* library. */
|
||||
}
|
||||
|
||||
/* For all libraries known to be indirect, but without a known parent, find a best valid parent
|
||||
* (i.e. a 'most directly used' library). */
|
||||
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
||||
/* A directly used library. */
|
||||
if (directly_used_libs.contains(lib_iter)) {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lib_iter->parent) {
|
||||
BLI_assert(lib_iter->temp_index > 0);
|
||||
}
|
||||
else {
|
||||
BLI_assert(lib_iter->temp_index == 0);
|
||||
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, lib_iter);
|
||||
}
|
||||
}
|
||||
|
||||
BKE_main_relations_free(bmain);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user