Fix #147037: copying node referencing packed id does not work

The solution is to remember the deep hash of the copied data-block pointers
instead of just relying on the library file path.

Pull Request: https://projects.blender.org/blender/blender/pulls/147275
This commit is contained in:
Jacques Lucke
2025-10-08 19:39:53 +02:00
parent dd75c0b5ec
commit 66dd8beef3
2 changed files with 46 additions and 9 deletions

View File

@@ -418,6 +418,11 @@ Library *blender::bke::library::search_filepath_abs(ListBase *libraries,
blender::StringRef filepath_abs)
{
LISTBASE_FOREACH (Library *, lib_iter, libraries) {
if (lib_iter->flag & LIBRARY_FLAG_IS_ARCHIVE) {
/* Skip archive libraries because there may be multiple of those for the same path and there
* should also be a non-archive one. */
continue;
}
if (filepath_abs == lib_iter->runtime->filepath_abs) {
return lib_iter;
}

View File

@@ -43,6 +43,11 @@ struct NodeClipboardItemIDInfo {
*/
std::string library_path;
/**
* Packed IDs are identified by the #ID.deep_hash.
*/
std::optional<IDHash> packed_id_hash;
/** The validated ID pointer (may be the same as the original one, or a new one). */
std::optional<ID *> new_id = {};
};
@@ -116,21 +121,36 @@ struct NodeClipboard {
Map<std::string, Library *> libraries_path_to_id;
for (NodeClipboardItemIDInfo &id_info : this->old_ids_to_idinfo.values()) {
id_info.new_id.reset();
if (!id_info.library_path.empty() && !libraries_path_to_id.contains(id_info.library_path)) {
if (!id_info.packed_id_hash.has_value() && !id_info.library_path.empty() &&
!libraries_path_to_id.contains(id_info.library_path))
{
libraries_path_to_id.add(
id_info.library_path,
blender::bke::library::search_filepath_abs(&bmain.libraries, id_info.library_path));
}
}
/* Prepare a map of packed IDs, to avoid quadratic lookups below. */
Map<IDHash, ID *> packed_id_by_hash;
{
ID *id;
FOREACH_MAIN_ID_BEGIN (&bmain, id) {
if (ID_IS_PACKED(id)) {
packed_id_by_hash.add(id->deep_hash, id);
}
}
FOREACH_MAIN_ID_END;
}
/* Find a new valid ID pointer for all ID usages in given node.
*
* NOTE: Due to the fact that the clipboard survives file loading, only name (including IDType)
* and library-path pairs can be used here.
* - UID cannot be trusted across file load.
* - ID pointer itself cannot be trusted across undo/redo and file-load. */
auto validate_id_fn = [this, &is_valid, &bmain, &bmain_id_map, &libraries_path_to_id](
LibraryIDLinkCallbackData *cb_data) -> int {
auto validate_id_fn =
[this, &is_valid, &bmain, &bmain_id_map, &libraries_path_to_id, &packed_id_by_hash](
LibraryIDLinkCallbackData *cb_data) -> int {
ID *old_id = *(cb_data->id_pointer);
if (!old_id) {
return IDWALK_RET_NOP;
@@ -147,14 +167,22 @@ struct NodeClipboard {
if (!bmain_id_map) {
bmain_id_map = BKE_main_idmap_create(&bmain, false, nullptr, MAIN_IDMAP_TYPE_NAME);
}
Library *new_id_lib = libraries_path_to_id.lookup_default(id_info.library_path, nullptr);
if (id_info.library_path.empty() || new_id_lib) {
id_info.new_id = BKE_main_idmap_lookup_name(
bmain_id_map, GS(id_info.id_name.c_str()), id_info.id_name.c_str() + 2, new_id_lib);
if (id_info.packed_id_hash.has_value()) {
id_info.new_id = packed_id_by_hash.lookup_default(*id_info.packed_id_hash, nullptr);
}
else {
/* No matching library found, so there is no possible matching ID either. */
id_info.new_id = nullptr;
Library *new_id_lib = libraries_path_to_id.lookup_default(id_info.library_path, nullptr);
BLI_assert(!new_id_lib || !(new_id_lib->flag & LIBRARY_FLAG_IS_ARCHIVE));
if (id_info.library_path.empty() || new_id_lib) {
id_info.new_id = BKE_main_idmap_lookup_name(bmain_id_map,
GS(id_info.id_name.c_str()),
id_info.id_name.c_str() + 2,
new_id_lib);
}
else {
/* No matching library found, so there is no possible matching ID either. */
id_info.new_id = nullptr;
}
}
}
if (*(id_info.new_id) == nullptr) {
@@ -243,6 +271,7 @@ struct NodeClipboard {
auto ensure_id_info_fn = [this](LibraryIDLinkCallbackData *cb_data) -> int {
ID *old_id = *(cb_data->id_pointer);
if (!old_id) {
return IDWALK_RET_NOP;
}
if (this->old_ids_to_idinfo.contains(old_id)) {
return IDWALK_RET_NOP;
@@ -253,6 +282,9 @@ struct NodeClipboard {
id_info.id_name = old_id->name;
if (ID_IS_LINKED(old_id)) {
id_info.library_path = old_id->lib->runtime->filepath_abs;
if (ID_IS_PACKED(old_id)) {
id_info.packed_id_hash = old_id->deep_hash;
}
}
}
this->old_ids_to_idinfo.add(old_id, std::move(id_info));