2023-05-31 16:19:06 +02:00
|
|
|
/* SPDX-FileCopyrightText: 2001-2002 NaN Holding BV. All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
2020-02-10 17:10:17 +01:00
|
|
|
|
|
|
|
|
/** \file
|
|
|
|
|
* \ingroup bke
|
|
|
|
|
*
|
|
|
|
|
* Contains code specific to the `Library` ID type.
|
|
|
|
|
*/
|
|
|
|
|
|
2024-07-01 12:45:35 +02:00
|
|
|
#include <optional>
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
#include "CLG_log.h"
|
|
|
|
|
|
2020-02-10 17:10:17 +01:00
|
|
|
/* all types are needed here, in order to do memory operations */
|
|
|
|
|
#include "DNA_ID.h"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "DNA_collection_types.h"
|
|
|
|
|
#include "DNA_scene_types.h"
|
2020-02-10 17:10:17 +01:00
|
|
|
|
|
|
|
|
#include "BLI_utildefines.h"
|
|
|
|
|
|
2024-02-22 17:11:39 +01:00
|
|
|
#include "BLI_ghash.h"
|
2025-01-28 15:27:34 +01:00
|
|
|
#include "BLI_listbase.h"
|
|
|
|
|
#include "BLI_path_utils.hh"
|
2024-02-22 17:11:39 +01:00
|
|
|
#include "BLI_set.hh"
|
2025-01-28 15:27:34 +01:00
|
|
|
#include "BLI_string.h"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BLI_vector_set.hh"
|
2020-02-10 17:10:17 +01:00
|
|
|
|
2024-02-09 18:59:42 +01:00
|
|
|
#include "BLT_translation.hh"
|
2020-03-06 11:27:29 +01:00
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
#include "BLO_read_write.hh"
|
|
|
|
|
|
2024-02-09 19:23:03 +01:00
|
|
|
#include "BKE_bpath.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_id_hash.hh"
|
2024-01-20 19:17:36 +01:00
|
|
|
#include "BKE_idtype.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_key.hh"
|
2025-02-07 17:47:16 +01:00
|
|
|
#include "BKE_lib_id.hh"
|
2024-01-18 12:20:42 +01:00
|
|
|
#include "BKE_lib_query.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_lib_remap.hh"
|
2023-12-05 11:52:54 +01:00
|
|
|
#include "BKE_library.hh"
|
2023-12-01 19:43:16 +01:00
|
|
|
#include "BKE_main.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_main_invariants.hh"
|
2023-11-27 16:21:49 +01:00
|
|
|
#include "BKE_main_namemap.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_node.hh"
|
2024-08-08 15:13:14 +02:00
|
|
|
#include "BKE_packedFile.hh"
|
2025-09-26 10:53:40 +02:00
|
|
|
#include "BKE_report.hh"
|
2020-02-10 17:10:17 +01:00
|
|
|
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
struct BlendDataReader;
|
|
|
|
|
|
2025-06-12 02:20:00 +02:00
|
|
|
static CLG_LogRef LOG = {"lib.library"};
|
2025-02-07 17:47:16 +01:00
|
|
|
|
2025-09-26 10:53:40 +02:00
|
|
|
using namespace blender::bke;
|
2025-02-07 17:47:16 +01:00
|
|
|
using namespace blender::bke::library;
|
|
|
|
|
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
static void library_runtime_reset(Library *lib)
|
|
|
|
|
{
|
Refactor: Core: ID's 'namemap' used to generate unique ID names.
Note: This commit is essentially non-behavioral change, expect in some
fairly rare edge cases.
This commit does a few things:
* Move the whole BKE_main_namemap code to modern C++.
* Split API calls to work with the global namemap, or the local ones.
* Simplify and make the code easier to follow and understand.
* Reduce 'default' memory usage by using growing BitVector for numeric
suffix management, instead of a fixed 1K items.
* Fix inconsistent handling of 'same base name and numeric suffix,
different name' issues (e.g. 'Foo.1' and 'Foo.001'), see
`re_create_equivalent_numeric_suffixes` new unittest.
* Fix completely broken handling of `global` namemaps. This was
(probably!) OK so far because of their currently very limited
use-cases.
It also adds a few minor improvements to existing behavior (essentially
in exotic rare edge cases):
* Names that get too long are now only shortened by one char at a time,
trying to modify the requested base name as little as possible.
* Names that are short, but for which all the manageable numeric suffixes
are already in use, are extended with an (increasing) number, instead
of being shortened.
This work also allowed to detect a few (apparently harmless?) bugs in
existing code, which have been fixed already in 4.4 and main, or in this
commit as well when they depend on changes in namemap code itself.
About performances: This commit introduces a minor slow-down. Some tests
heavily relying on this code (like `bl_id_management` and `blendkernel`
e.g.) get slightly slower (resp. about 1% and 5%). This seems to come
mostly from the added complexity to handle correctly multiple different
names with the same base and numeric suffix value ('Foo.1' and
'Foo.001', but also in the global namemap context where IDs from
different libraries can have the same name).
Pull Request: https://projects.blender.org/blender/blender/pulls/135199
2025-02-28 17:58:58 +01:00
|
|
|
BKE_main_namemap_destroy(&lib->runtime->name_map);
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
}
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
static void library_init_data(ID *id)
|
|
|
|
|
{
|
|
|
|
|
Library *library = reinterpret_cast<Library *>(id);
|
|
|
|
|
library->runtime = MEM_new<LibraryRuntime>(__func__);
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 11:27:29 +01:00
|
|
|
static void library_free_data(ID *id)
|
2020-02-10 17:10:17 +01:00
|
|
|
{
|
2020-03-06 11:27:29 +01:00
|
|
|
Library *library = (Library *)id;
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
library_runtime_reset(library);
|
2025-02-07 17:47:16 +01:00
|
|
|
MEM_delete(library->runtime);
|
2020-03-06 11:27:29 +01:00
|
|
|
if (library->packedfile) {
|
|
|
|
|
BKE_packedfile_free(library->packedfile);
|
2020-02-10 17:10:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-01 12:45:35 +02:00
|
|
|
static void library_copy_data(Main *bmain,
|
|
|
|
|
std::optional<Library *> owner_library,
|
|
|
|
|
ID *id_dst,
|
|
|
|
|
const ID *id_src,
|
|
|
|
|
int /*flag*/)
|
|
|
|
|
{
|
|
|
|
|
/* Libraries are always local IDs. */
|
|
|
|
|
BLI_assert(!owner_library || *owner_library == nullptr);
|
2024-07-03 15:14:18 +02:00
|
|
|
UNUSED_VARS_NDEBUG(bmain, owner_library);
|
2024-07-01 12:45:35 +02:00
|
|
|
|
|
|
|
|
const Library *library_src = reinterpret_cast<const Library *>(id_src);
|
|
|
|
|
|
|
|
|
|
/* Libraries are copyable now, but there should still be only one library ID for each linked
|
|
|
|
|
* blendfile (based on absolute filepath). */
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_assert(!bmain ||
|
|
|
|
|
!search_filepath_abs(&bmain->libraries, library_src->runtime->filepath_abs));
|
2024-07-01 12:45:35 +02:00
|
|
|
|
|
|
|
|
Library *library_dst = reinterpret_cast<Library *>(id_dst);
|
|
|
|
|
if (library_src->packedfile) {
|
|
|
|
|
library_dst->packedfile = BKE_packedfile_duplicate(library_src->packedfile);
|
|
|
|
|
}
|
2025-10-03 17:10:54 +02:00
|
|
|
|
|
|
|
|
/* Only explicitely copy a sub-set of the runtime data. */
|
|
|
|
|
library_dst->runtime = MEM_new<LibraryRuntime>(__func__);
|
|
|
|
|
BLI_strncpy(library_dst->runtime->filepath_abs,
|
|
|
|
|
library_src->runtime->filepath_abs,
|
|
|
|
|
sizeof(library_dst->runtime->filepath_abs));
|
|
|
|
|
library_dst->runtime->parent = library_src->runtime->parent;
|
|
|
|
|
library_dst->runtime->tag = library_src->runtime->tag;
|
|
|
|
|
library_dst->runtime->versionfile = library_src->runtime->versionfile;
|
|
|
|
|
library_dst->runtime->subversionfile = library_src->runtime->subversionfile;
|
|
|
|
|
library_dst->runtime->colorspace = library_src->runtime->colorspace;
|
2024-07-01 12:45:35 +02:00
|
|
|
}
|
|
|
|
|
|
2020-05-12 18:20:49 +02:00
|
|
|
static void library_foreach_id(ID *id, LibraryForeachIDData *data)
|
|
|
|
|
{
|
|
|
|
|
Library *lib = (Library *)id;
|
2025-09-26 10:53:40 +02:00
|
|
|
const LibraryForeachIDFlag foreach_flag = BKE_lib_query_foreachid_process_flags_get(data);
|
2025-02-07 17:47:16 +01:00
|
|
|
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, lib->runtime->parent, IDWALK_CB_NEVER_SELF);
|
2025-09-26 10:53:40 +02:00
|
|
|
|
|
|
|
|
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
|
|
|
|
|
/* Archive library must have a parent, this can't be nullptr. */
|
|
|
|
|
if (lib->archive_parent_library) {
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(
|
|
|
|
|
data, lib->archive_parent_library, IDWALK_CB_NEVER_SELF | IDWALK_CB_NEVER_NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Archive libraries should never 'own' other archives. */
|
|
|
|
|
BLI_assert(lib->runtime->archived_libraries.is_empty());
|
|
|
|
|
if (foreach_flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) {
|
|
|
|
|
for (Library *&lib_p : lib->runtime->archived_libraries) {
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(
|
|
|
|
|
data, lib_p, IDWALK_CB_NEVER_SELF | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* Regular libraries should never have an archive parent. */
|
|
|
|
|
BLI_assert(!lib->archive_parent_library);
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(data, lib->archive_parent_library, IDWALK_CB_NEVER_SELF);
|
|
|
|
|
|
|
|
|
|
if (foreach_flag & IDWALK_DO_INTERNAL_RUNTIME_POINTERS) {
|
|
|
|
|
for (Library *&lib_p : lib->runtime->archived_libraries) {
|
|
|
|
|
BKE_LIB_FOREACHID_PROCESS_ID(
|
|
|
|
|
data, lib_p, IDWALK_CB_NEVER_SELF | IDWALK_CB_INTERNAL | IDWALK_CB_LOOPBACK);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-12 18:20:49 +02:00
|
|
|
}
|
|
|
|
|
|
Refactor BKE_bpath module.
The main goal of this refactor is to make BPath module use `IDTypeInfo`,
and move each ID-specific part of the `foreach_path` looper into their
own IDTypeInfo struct, using a new `foreach_path` callback.
Additionally, following improvements/cleanups are included:
* Attempt to get better, more consistent namings.
** In particular, move from `path_visitor` to more standard `foreach_path`.
* Update and extend documentation.
** API doc was moved to header, according to recent discussions on this
topic.
* Remove `BKE_bpath_relocate_visitor` from API, this is specific
callback that belongs in `lib_id.c` user code.
NOTE: This commit is expected to be 100% non-behavioral-change. This
implies that several potential further changes were only noted as
comments (like using a more generic solution for
`lib_id_library_local_paths`, addressing inconsistencies like path of
packed libraries always being skipped, regardless of the
`BKE_BPATH_FOREACH_PATH_SKIP_PACKED` `eBPathForeachFlag` flag value,
etc.).
NOTE: basic unittests were added to master already in
rBdcc500e5a265093bc9cc.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D13381
2021-11-29 14:20:58 +01:00
|
|
|
static void library_foreach_path(ID *id, BPathForeachPathData *bpath_data)
|
|
|
|
|
{
|
|
|
|
|
Library *lib = (Library *)id;
|
|
|
|
|
|
|
|
|
|
/* FIXME: Find if we should respect #BKE_BPATH_FOREACH_PATH_SKIP_PACKED here, and if not, explain
|
|
|
|
|
* why. */
|
2023-07-17 10:46:26 +02:00
|
|
|
if (lib->packedfile !=
|
|
|
|
|
nullptr /*&& (bpath_data->flag & BKE_BPATH_FOREACH_PATH_SKIP_PACKED) != 0 */)
|
Refactor BKE_bpath module.
The main goal of this refactor is to make BPath module use `IDTypeInfo`,
and move each ID-specific part of the `foreach_path` looper into their
own IDTypeInfo struct, using a new `foreach_path` callback.
Additionally, following improvements/cleanups are included:
* Attempt to get better, more consistent namings.
** In particular, move from `path_visitor` to more standard `foreach_path`.
* Update and extend documentation.
** API doc was moved to header, according to recent discussions on this
topic.
* Remove `BKE_bpath_relocate_visitor` from API, this is specific
callback that belongs in `lib_id.c` user code.
NOTE: This commit is expected to be 100% non-behavioral-change. This
implies that several potential further changes were only noted as
comments (like using a more generic solution for
`lib_id_library_local_paths`, addressing inconsistencies like path of
packed libraries always being skipped, regardless of the
`BKE_BPATH_FOREACH_PATH_SKIP_PACKED` `eBPathForeachFlag` flag value,
etc.).
NOTE: basic unittests were added to master already in
rBdcc500e5a265093bc9cc.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D13381
2021-11-29 14:20:58 +01:00
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-06-23 10:09:01 +10:00
|
|
|
if (BKE_bpath_foreach_path_fixed_process(bpath_data, lib->filepath, sizeof(lib->filepath))) {
|
Refactor BKE_bpath module.
The main goal of this refactor is to make BPath module use `IDTypeInfo`,
and move each ID-specific part of the `foreach_path` looper into their
own IDTypeInfo struct, using a new `foreach_path` callback.
Additionally, following improvements/cleanups are included:
* Attempt to get better, more consistent namings.
** In particular, move from `path_visitor` to more standard `foreach_path`.
* Update and extend documentation.
** API doc was moved to header, according to recent discussions on this
topic.
* Remove `BKE_bpath_relocate_visitor` from API, this is specific
callback that belongs in `lib_id.c` user code.
NOTE: This commit is expected to be 100% non-behavioral-change. This
implies that several potential further changes were only noted as
comments (like using a more generic solution for
`lib_id_library_local_paths`, addressing inconsistencies like path of
packed libraries always being skipped, regardless of the
`BKE_BPATH_FOREACH_PATH_SKIP_PACKED` `eBPathForeachFlag` flag value,
etc.).
NOTE: basic unittests were added to master already in
rBdcc500e5a265093bc9cc.
Reviewed By: brecht
Differential Revision: https://developer.blender.org/D13381
2021-11-29 14:20:58 +01:00
|
|
|
BKE_library_filepath_set(bpath_data->bmain, lib, lib->filepath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
static void library_blend_write_data(BlendWriter *writer, ID *id, const void *id_address)
|
|
|
|
|
{
|
|
|
|
|
Library *library = reinterpret_cast<Library *>(id);
|
|
|
|
|
const bool is_undo = BLO_write_is_undo(writer);
|
|
|
|
|
|
|
|
|
|
/* Clear runtime data. */
|
|
|
|
|
library->runtime = nullptr;
|
|
|
|
|
|
|
|
|
|
BLO_write_id_struct(writer, Library, id_address, id);
|
|
|
|
|
BKE_id_blend_write(writer, id);
|
|
|
|
|
|
|
|
|
|
/* Write packed file if necessary. */
|
|
|
|
|
if (library->packedfile) {
|
|
|
|
|
BKE_packedfile_blend_write(writer, library->packedfile);
|
|
|
|
|
if (!is_undo) {
|
2025-06-12 02:20:03 +02:00
|
|
|
CLOG_DEBUG(&LOG, "Write packed .blend: %s", library->filepath);
|
2025-02-07 17:47:16 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-20 11:30:25 +10:00
|
|
|
static void library_blend_read_data(BlendDataReader * /*reader*/, ID *id)
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
{
|
2025-02-07 17:47:16 +01:00
|
|
|
Library *lib = reinterpret_cast<Library *>(id);
|
|
|
|
|
lib->runtime = MEM_new<LibraryRuntime>(__func__);
|
IDManagement: Speedup ID unique name assignment by tracking used names/basenames/suffixes
An implementation of T73412, roughly as outlined there:
Track the names that are in use, as well as base names (before
numeric suffix) plus a bit map for each base name, indicating which
numeric suffixes are already used. This is done per-Main/Library,
per-object-type.
Timings (Windows, VS2022 Release build, AMD Ryzen 5950X):
- Scene with 10k cubes, Shift+D to duplicate them all: 8.7s -> 1.9s.
Name map memory usage for resulting 20k objects: 4.3MB.
- Importing a 2.5GB .obj file of exported Blender 3.0 splash scene
(24k objects), using the new C++ importer: 34.2s-> 22.0s. Name map
memory usage for resulting scene: 8.6MB.
- Importing Disney Moana USD scene (almost half a million objects):
56min -> 10min. Name map usage: ~100MB. Blender crashes later on
when trying to render it, in the same place in both cases, but
that's for another day.
Reviewed By: Bastien Montagne
Differential Revision: https://developer.blender.org/D14162
2022-07-20 14:27:14 +03:00
|
|
|
}
|
|
|
|
|
|
2025-09-26 10:53:40 +02:00
|
|
|
static void library_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id)
|
|
|
|
|
{
|
|
|
|
|
Library *lib = reinterpret_cast<Library *>(id);
|
|
|
|
|
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
|
|
|
|
|
BLI_assert(lib->archive_parent_library);
|
|
|
|
|
lib->archive_parent_library->runtime->archived_libraries.append(lib);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-03-06 11:27:29 +01:00
|
|
|
IDTypeInfo IDType_ID_LI = {
|
2025-05-05 18:41:03 +02:00
|
|
|
/*id_code*/ Library::id_type,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*id_filter*/ FILTER_ID_LI,
|
2024-02-15 18:34:17 +01:00
|
|
|
/*dependencies_id_types*/ FILTER_ID_LI,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*main_listbase_index*/ INDEX_ID_LI,
|
|
|
|
|
/*struct_size*/ sizeof(Library),
|
|
|
|
|
/*name*/ "Library",
|
2023-10-04 02:53:31 +02:00
|
|
|
/*name_plural*/ N_("libraries"),
|
2023-07-17 10:46:26 +02:00
|
|
|
/*translation_context*/ BLT_I18NCONTEXT_ID_LIBRARY,
|
2024-07-01 12:45:35 +02:00
|
|
|
/*flags*/ IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NEVER_UNUSED,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*asset_type_info*/ nullptr,
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
/*init_data*/ library_init_data,
|
2024-07-01 12:45:35 +02:00
|
|
|
/*copy_data*/ library_copy_data,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*free_data*/ library_free_data,
|
|
|
|
|
/*make_local*/ nullptr,
|
|
|
|
|
/*foreach_id*/ library_foreach_id,
|
|
|
|
|
/*foreach_cache*/ nullptr,
|
|
|
|
|
/*foreach_path*/ library_foreach_path,
|
2025-08-29 20:39:08 +02:00
|
|
|
/*foreach_working_space_color*/ nullptr,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*owner_pointer_get*/ nullptr,
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
/*blend_write*/ library_blend_write_data,
|
2023-07-17 10:46:26 +02:00
|
|
|
/*blend_read_data*/ library_blend_read_data,
|
2025-09-26 10:53:40 +02:00
|
|
|
/*blend_read_after_liblink*/ library_blend_read_after_liblink,
|
2023-07-17 10:46:26 +02:00
|
|
|
|
|
|
|
|
/*blend_read_undo_preserve*/ nullptr,
|
|
|
|
|
|
|
|
|
|
/*lib_override_apply_post*/ nullptr,
|
2020-03-06 11:27:29 +01:00
|
|
|
};
|
|
|
|
|
|
2020-02-10 17:10:17 +01:00
|
|
|
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
|
|
|
|
|
{
|
|
|
|
|
/* in some cases this is used to update the absolute path from the
|
|
|
|
|
* relative */
|
2020-06-23 09:54:14 +10:00
|
|
|
if (lib->filepath != filepath) {
|
2023-05-09 12:50:37 +10:00
|
|
|
STRNCPY(lib->filepath, filepath);
|
2020-02-10 17:10:17 +01:00
|
|
|
}
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
STRNCPY(lib->runtime->filepath_abs, filepath);
|
2020-02-10 17:10:17 +01:00
|
|
|
|
2020-06-23 09:54:07 +10:00
|
|
|
/* Not essential but set `filepath_abs` is an absolute copy of value which
|
|
|
|
|
* is more useful if its kept in sync. */
|
2025-02-07 17:47:16 +01:00
|
|
|
if (BLI_path_is_rel(lib->runtime->filepath_abs)) {
|
2023-02-09 11:30:25 +11:00
|
|
|
/* NOTE(@ideasman42): the file may be unsaved, in this case, setting the
|
2020-06-23 09:54:07 +10:00
|
|
|
* `filepath_abs` on an indirectly linked path is not allowed from the
|
2020-02-10 17:10:17 +01:00
|
|
|
* outliner, and its not really supported but allow from here for now
|
2021-07-03 23:08:40 +10:00
|
|
|
* since making local could cause this to be directly linked.
|
2020-02-10 17:10:17 +01:00
|
|
|
*/
|
|
|
|
|
/* Never make paths relative to parent lib - reading code (blenloader) always set *all*
|
2020-06-23 09:54:14 +10:00
|
|
|
* `lib->filepath` relative to current main, not to their parent for indirectly linked ones. */
|
2021-12-14 21:32:14 +11:00
|
|
|
const char *blendfile_path = BKE_main_blendfile_path(bmain);
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_path_abs(lib->runtime->filepath_abs, blendfile_path);
|
2020-02-10 17:10:17 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-22 17:11:39 +01:00
|
|
|
|
|
|
|
|
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. */
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_assert(best_parent_lib == nullptr || best_parent_lib->runtime->temp_index > 0);
|
2024-02-22 17:11:39 +01:00
|
|
|
best_parent_lib = from_id_lib;
|
|
|
|
|
do_break = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-02-07 17:47:16 +01:00
|
|
|
if (!from_id_lib->runtime->parent) {
|
2024-02-22 17:11:39 +01:00
|
|
|
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, from_id_lib);
|
|
|
|
|
}
|
2024-04-08 13:08:36 +02:00
|
|
|
if (!best_parent_lib ||
|
2025-02-07 17:47:16 +01:00
|
|
|
best_parent_lib->runtime->temp_index > from_id_lib->runtime->temp_index)
|
2024-04-08 13:08:36 +02:00
|
|
|
{
|
2024-02-22 17:11:39 +01:00
|
|
|
best_parent_lib = from_id_lib;
|
2025-02-07 17:47:16 +01:00
|
|
|
if (best_parent_lib->runtime->temp_index == 0) {
|
2024-02-22 17:11:39 +01:00
|
|
|
/* 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) {
|
2025-02-07 17:47:16 +01:00
|
|
|
lib->runtime->parent = best_parent_lib;
|
|
|
|
|
lib->runtime->temp_index = best_parent_lib->runtime->temp_index + 1;
|
2024-02-22 17:11:39 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2025-02-07 17:47:16 +01:00
|
|
|
lib->runtime->parent = nullptr;
|
|
|
|
|
lib->runtime->temp_index = 0;
|
2024-02-22 17:11:39 +01:00
|
|
|
directly_used_libs.add(lib);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void BKE_library_main_rebuild_hierarchy(Main *bmain)
|
|
|
|
|
{
|
|
|
|
|
BKE_main_relations_create(bmain, 0);
|
|
|
|
|
|
2024-10-08 17:02:51 +02:00
|
|
|
/* Reset all values, they may have been set to irrelevant values by other processes (like the
|
|
|
|
|
* liboverride handling e.g., see #lib_override_libraries_index_define). */
|
|
|
|
|
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
2025-02-07 17:47:16 +01:00
|
|
|
lib_iter->runtime->temp_index = 0;
|
2024-10-08 17:02:51 +02:00
|
|
|
}
|
|
|
|
|
|
2024-02-22 17:11:39 +01:00
|
|
|
/* 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;
|
|
|
|
|
}
|
2025-02-07 17:47:16 +01:00
|
|
|
id_iter->lib->runtime->temp_index = 0;
|
2024-02-22 17:11:39 +01:00
|
|
|
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);
|
2025-02-07 17:47:16 +01:00
|
|
|
id_iter->lib->runtime->parent = nullptr;
|
2024-02-22 17:11:39 +01:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FOREACH_MAIN_ID_END;
|
|
|
|
|
|
|
|
|
|
LISTBASE_FOREACH (Library *, lib_iter, &bmain->libraries) {
|
|
|
|
|
/* A directly used library. */
|
|
|
|
|
if (directly_used_libs.contains(lib_iter)) {
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_assert(lib_iter->runtime->temp_index == 0);
|
2024-02-22 17:11:39 +01:00
|
|
|
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. */
|
2025-02-07 17:47:16 +01:00
|
|
|
if (lib_iter->runtime->parent) {
|
|
|
|
|
if (lib_iter->runtime->temp_index > 0) {
|
2024-02-22 17:11:39 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
blender::Vector<Library *> parent_libraries;
|
|
|
|
|
for (Library *parent_lib_iter = lib_iter;
|
2025-02-07 17:47:16 +01:00
|
|
|
parent_lib_iter && parent_lib_iter->runtime->temp_index == 0;
|
|
|
|
|
parent_lib_iter = parent_lib_iter->runtime->parent)
|
2024-02-22 17:11:39 +01:00
|
|
|
{
|
|
|
|
|
parent_libraries.append(parent_lib_iter);
|
|
|
|
|
}
|
2025-02-07 17:47:16 +01:00
|
|
|
int parent_temp_index = parent_libraries.last()->runtime->temp_index +
|
2024-04-08 13:08:36 +02:00
|
|
|
int(parent_libraries.size()) - 1;
|
2024-02-22 17:11:39 +01:00
|
|
|
for (Library *parent_lib_iter : parent_libraries) {
|
|
|
|
|
BLI_assert(parent_lib_iter != parent_libraries.last() ||
|
2025-02-07 17:47:16 +01:00
|
|
|
parent_lib_iter->runtime->temp_index == parent_temp_index);
|
|
|
|
|
parent_lib_iter->runtime->temp_index = parent_temp_index--;
|
2024-02-22 17:11:39 +01:00
|
|
|
}
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Otherwise, it's an indirectly used library with no known parent, another loop is needed to
|
2024-02-26 10:23:12 +11:00
|
|
|
* ensure all known hierarchy has valid indices when trying to find the best valid parent
|
2024-02-22 17:11:39 +01:00
|
|
|
* 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)) {
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_assert(lib_iter->runtime->temp_index == 0);
|
2024-02-22 17:11:39 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-07 17:47:16 +01:00
|
|
|
if (lib_iter->runtime->parent) {
|
|
|
|
|
BLI_assert(lib_iter->runtime->temp_index > 0);
|
2024-02-22 17:11:39 +01:00
|
|
|
}
|
|
|
|
|
else {
|
2025-02-07 17:47:16 +01:00
|
|
|
BLI_assert(lib_iter->runtime->temp_index == 0);
|
2024-02-22 17:11:39 +01:00
|
|
|
rebuild_hierarchy_best_parent_find(bmain, directly_used_libs, lib_iter);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_main_relations_free(bmain);
|
|
|
|
|
}
|
2025-02-07 17:47:16 +01:00
|
|
|
|
|
|
|
|
Library *blender::bke::library::search_filepath_abs(ListBase *libraries,
|
|
|
|
|
blender::StringRef filepath_abs)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (Library *, lib_iter, libraries) {
|
|
|
|
|
if (filepath_abs == lib_iter->runtime->filepath_abs) {
|
|
|
|
|
return lib_iter;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
2025-09-26 10:53:40 +02:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Add a new 'archive' copy of the given reference library. It is used to store linked packed IDs.
|
|
|
|
|
*/
|
|
|
|
|
static Library *add_archive_library(Main &bmain, Library &reference_library)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert((reference_library.flag & LIBRARY_FLAG_IS_ARCHIVE) == 0);
|
|
|
|
|
/* Cannot copy libraries using generic ID copying functions, so create the copy manually. */
|
|
|
|
|
Library *archive_library = static_cast<Library *>(
|
|
|
|
|
BKE_id_new(&bmain, ID_LI, BKE_id_name(reference_library.id)));
|
|
|
|
|
|
|
|
|
|
/* Like in #direct_link_library. */
|
|
|
|
|
id_us_ensure_real(&archive_library->id);
|
|
|
|
|
|
|
|
|
|
archive_library->archive_parent_library = &reference_library;
|
|
|
|
|
constexpr uint16_t copy_flag = ~LIBRARY_FLAG_IS_ARCHIVE;
|
|
|
|
|
archive_library->flag = (reference_library.flag & copy_flag) | LIBRARY_FLAG_IS_ARCHIVE;
|
|
|
|
|
BKE_library_filepath_set(&bmain, archive_library, reference_library.filepath);
|
|
|
|
|
|
|
|
|
|
archive_library->runtime->parent = reference_library.runtime->parent;
|
|
|
|
|
/* Only copy a subset of the reference library tags. E.g. an archive library should never be
|
|
|
|
|
* considered as writable, so never copy #LIBRARY_ASSET_FILE_WRITABLE. This may need further
|
|
|
|
|
* tweaking still. */
|
|
|
|
|
constexpr uint16_t copy_tag = (LIBRARY_TAG_RESYNC_REQUIRED | LIBRARY_ASSET_EDITABLE |
|
|
|
|
|
LIBRARY_IS_ASSET_EDIT_FILE);
|
|
|
|
|
archive_library->runtime->tag = reference_library.runtime->tag & copy_tag;
|
|
|
|
|
/* By definition, the file version of an archive library containing only packed linked data is
|
|
|
|
|
* the same as the one of its Main container. */
|
|
|
|
|
archive_library->runtime->versionfile = bmain.versionfile;
|
|
|
|
|
archive_library->runtime->subversionfile = bmain.subversionfile;
|
|
|
|
|
|
|
|
|
|
reference_library.runtime->archived_libraries.append(archive_library);
|
|
|
|
|
|
|
|
|
|
return archive_library;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-07 16:11:47 +02:00
|
|
|
Library *blender::bke::library::ensure_archive_library(
|
|
|
|
|
Main &bmain, ID &id, Library &reference_library, const IDHash &id_deep_hash, bool &is_new)
|
2025-09-26 10:53:40 +02:00
|
|
|
{
|
2025-10-07 16:11:47 +02:00
|
|
|
BLI_assert(ID_IS_LINKED(&id));
|
|
|
|
|
BLI_assert((reference_library.flag & LIBRARY_FLAG_IS_ARCHIVE) == 0);
|
2025-09-26 10:53:40 +02:00
|
|
|
|
|
|
|
|
Library *archive_library = nullptr;
|
2025-10-07 16:11:47 +02:00
|
|
|
for (Library *lib_iter : reference_library.runtime->archived_libraries) {
|
2025-09-26 10:53:40 +02:00
|
|
|
BLI_assert((lib_iter->flag & LIBRARY_FLAG_IS_ARCHIVE) != 0);
|
|
|
|
|
BLI_assert(lib_iter->archive_parent_library != nullptr);
|
2025-10-07 16:11:47 +02:00
|
|
|
BLI_assert(lib_iter->archive_parent_library == &reference_library);
|
2025-09-26 10:53:40 +02:00
|
|
|
/* Check if current archive library already contains an ID of same type and name. */
|
2025-10-07 16:11:47 +02:00
|
|
|
if (BKE_main_namemap_contain_name(bmain, lib_iter, GS(id.name), BKE_id_name(id))) {
|
2025-09-26 10:53:40 +02:00
|
|
|
#ifndef NDEBUG
|
|
|
|
|
ID *packed_id = BKE_libblock_find_name_and_library(
|
2025-10-07 16:11:47 +02:00
|
|
|
&bmain, GS(id.name), BKE_id_name(id), BKE_id_name(lib_iter->id));
|
2025-09-26 10:53:40 +02:00
|
|
|
BLI_assert_msg(
|
2025-10-07 16:11:47 +02:00
|
|
|
packed_id && packed_id->deep_hash != id_deep_hash,
|
2025-09-26 10:53:40 +02:00
|
|
|
"An already packed ID with same deep hash as the one to be packed, should have already "
|
2025-09-26 21:59:45 +10:00
|
|
|
"be found and used (deduplication) before reaching this code-path");
|
2025-09-26 10:53:40 +02:00
|
|
|
#endif
|
2025-10-07 16:11:47 +02:00
|
|
|
UNUSED_VARS_NDEBUG(id_deep_hash);
|
2025-09-26 10:53:40 +02:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
archive_library = lib_iter;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!archive_library) {
|
2025-10-07 16:11:47 +02:00
|
|
|
archive_library = add_archive_library(bmain, reference_library);
|
|
|
|
|
is_new = true;
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
is_new = false;
|
2025-09-26 10:53:40 +02:00
|
|
|
}
|
2025-10-07 16:11:47 +02:00
|
|
|
BLI_assert(reference_library.runtime->archived_libraries.contains(archive_library));
|
2025-09-26 10:53:40 +02:00
|
|
|
return archive_library;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void pack_linked_id(Main &bmain,
|
|
|
|
|
ID *linked_id,
|
|
|
|
|
const id_hash::ValidDeepHashes &deep_hashes,
|
|
|
|
|
blender::Map<IDHash, ID *> &already_packed_ids,
|
|
|
|
|
blender::VectorSet<ID *> &ids_to_remap,
|
|
|
|
|
blender::bke::id::IDRemapper &id_remapper)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(linked_id->newid == nullptr);
|
|
|
|
|
|
|
|
|
|
const IDHash linked_id_deep_hash = deep_hashes.hashes.lookup(linked_id);
|
|
|
|
|
ID *packed_id = already_packed_ids.lookup_default(linked_id_deep_hash, nullptr);
|
|
|
|
|
|
|
|
|
|
if (packed_id) {
|
|
|
|
|
/* Exact same ID (and all of its dependencies) have already been linked and packed before,
|
|
|
|
|
* re-use these packed data. */
|
|
|
|
|
|
|
|
|
|
auto existing_id_process = [&deep_hashes, &id_remapper](ID *linked_id, ID *packed_id) {
|
|
|
|
|
BLI_assert(packed_id);
|
|
|
|
|
BLI_assert(ID_IS_PACKED(packed_id));
|
|
|
|
|
/* Note: linked_id and packed_id may have the same deep hash while still coming from
|
|
|
|
|
* different original libraries. This easily happens copying an asset file such that each
|
|
|
|
|
* asset exists twice. */
|
|
|
|
|
BLI_assert(packed_id->deep_hash == deep_hashes.hashes.lookup(linked_id));
|
|
|
|
|
UNUSED_VARS_NDEBUG(deep_hashes);
|
|
|
|
|
|
|
|
|
|
id_remapper.add(linked_id, packed_id);
|
|
|
|
|
linked_id->newid = packed_id;
|
|
|
|
|
/* No need to remap this packed ID - otherwise there would be something very wrong in
|
|
|
|
|
* packed IDs state. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
existing_id_process(linked_id, packed_id);
|
|
|
|
|
|
|
|
|
|
/* Handle 'fake-embedded' ShapeKeys IDs. */
|
|
|
|
|
Key *linked_key = BKE_key_from_id(linked_id);
|
|
|
|
|
if (linked_key) {
|
|
|
|
|
Key *packed_key = BKE_key_from_id(packed_id);
|
|
|
|
|
BLI_assert(packed_key);
|
|
|
|
|
existing_id_process(&linked_key->id, &packed_key->id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
/* This exact version of the ID and its dependencies have not been packed before, creates a
|
|
|
|
|
* new copy of it and pack it. */
|
|
|
|
|
|
|
|
|
|
/* Find an existing archive Library not containing a 'version' of this ID yet (to prevent names
|
|
|
|
|
* collisions). */
|
2025-10-07 16:11:47 +02:00
|
|
|
bool is_new;
|
|
|
|
|
Library *archive_lib = ensure_archive_library(
|
|
|
|
|
bmain, *linked_id, *linked_id->lib, linked_id_deep_hash, is_new);
|
2025-09-26 10:53:40 +02:00
|
|
|
|
|
|
|
|
auto copied_id_process =
|
|
|
|
|
[&archive_lib, &deep_hashes, &ids_to_remap, &id_remapper, &already_packed_ids](
|
|
|
|
|
ID *linked_id, ID *packed_id) {
|
|
|
|
|
BLI_assert(packed_id);
|
|
|
|
|
BLI_assert(ID_IS_PACKED(packed_id));
|
|
|
|
|
BLI_assert(packed_id->lib == archive_lib);
|
|
|
|
|
UNUSED_VARS_NDEBUG(archive_lib);
|
|
|
|
|
|
|
|
|
|
packed_id->deep_hash = deep_hashes.hashes.lookup(linked_id);
|
|
|
|
|
id_remapper.add(linked_id, packed_id);
|
|
|
|
|
ids_to_remap.add(packed_id);
|
|
|
|
|
already_packed_ids.add(packed_id->deep_hash, packed_id);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
packed_id = BKE_id_copy_in_lib(&bmain,
|
|
|
|
|
archive_lib,
|
|
|
|
|
linked_id,
|
|
|
|
|
std::nullopt,
|
|
|
|
|
nullptr,
|
|
|
|
|
LIB_ID_COPY_DEFAULT | LIB_ID_COPY_ID_NEW_SET |
|
|
|
|
|
LIB_ID_COPY_NO_ANIMDATA);
|
|
|
|
|
id_us_min(packed_id);
|
|
|
|
|
copied_id_process(linked_id, packed_id);
|
|
|
|
|
|
|
|
|
|
/* Handle 'fake-embedded' ShapeKeys IDs. */
|
|
|
|
|
Key *linked_key = BKE_key_from_id(linked_id);
|
|
|
|
|
if (linked_key) {
|
|
|
|
|
Key *embedded_key = BKE_key_from_id(packed_id);
|
|
|
|
|
BLI_assert(embedded_key);
|
|
|
|
|
copied_id_process(&linked_key->id, &embedded_key->id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Pack given linked IDs. Low-level code, assumes all given IDs are valid and safe to pack.
|
|
|
|
|
*
|
|
|
|
|
* Will set final packed ID into each ID::newid pointers.
|
|
|
|
|
*/
|
|
|
|
|
static void pack_linked_ids(Main &bmain, const blender::Set<ID *> &ids_to_pack)
|
|
|
|
|
{
|
|
|
|
|
blender::VectorSet<ID *> final_ids_to_pack;
|
|
|
|
|
blender::VectorSet<ID *> ids_to_remap;
|
|
|
|
|
blender::bke::id::IDRemapper id_remapper;
|
|
|
|
|
|
|
|
|
|
for (ID *id : ids_to_pack) {
|
|
|
|
|
BLI_assert(ID_IS_LINKED(id));
|
|
|
|
|
if (ID_IS_PACKED(id)) {
|
|
|
|
|
/* Should not happen, but also not critical issue. */
|
|
|
|
|
CLOG_ERROR(&LOG,
|
|
|
|
|
"Trying to pack an already packed ID '%s' (from '%s')",
|
|
|
|
|
id->name,
|
|
|
|
|
id->lib->runtime->filepath_abs);
|
|
|
|
|
/* Already packed. */
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
final_ids_to_pack.add(id);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const id_hash::IDHashResult hash_result = id_hash::compute_linked_id_deep_hashes(
|
|
|
|
|
bmain, final_ids_to_pack.as_span());
|
|
|
|
|
if (const auto *errors = std::get_if<id_hash::DeepHashErrors>(&hash_result)) {
|
|
|
|
|
if (!errors->missing_files.is_empty()) {
|
|
|
|
|
CLOG_ERROR(&LOG,
|
|
|
|
|
"Trying to pack IDs that depend on missing linked libraries: %s",
|
|
|
|
|
errors->missing_files[0].c_str());
|
|
|
|
|
}
|
|
|
|
|
if (!errors->updated_files.is_empty()) {
|
|
|
|
|
CLOG_ERROR(&LOG,
|
|
|
|
|
"Trying to pack linked ID that has been modified on disk: %s",
|
|
|
|
|
errors->updated_files[0].c_str());
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const auto &deep_hashes = std::get<id_hash::ValidDeepHashes>(hash_result);
|
|
|
|
|
|
|
|
|
|
blender::Map<IDHash, ID *> already_packed_ids;
|
|
|
|
|
{
|
|
|
|
|
ID *id;
|
|
|
|
|
FOREACH_MAIN_ID_BEGIN (&bmain, id) {
|
|
|
|
|
if (ID_IS_PACKED(id)) {
|
|
|
|
|
already_packed_ids.add(id->deep_hash, id);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
FOREACH_MAIN_ID_END;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (ID *linked_id : final_ids_to_pack) {
|
|
|
|
|
pack_linked_id(bmain, linked_id, deep_hashes, already_packed_ids, ids_to_remap, id_remapper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BKE_libblock_relink_multiple(
|
|
|
|
|
&bmain, ids_to_remap.as_span(), ID_REMAP_TYPE_REMAP, id_remapper, 0);
|
|
|
|
|
BKE_main_ensure_invariants(bmain);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void blender::bke::library::pack_linked_id_hierarchy(Main &bmain, ID &root_id)
|
|
|
|
|
{
|
|
|
|
|
BLI_assert(ID_IS_LINKED(&root_id));
|
|
|
|
|
BLI_assert(!ID_IS_PACKED(&root_id));
|
|
|
|
|
|
|
|
|
|
blender::Set<ID *> ids_to_pack;
|
|
|
|
|
ids_to_pack.add(&root_id);
|
|
|
|
|
BKE_library_foreach_ID_link(
|
|
|
|
|
&bmain,
|
|
|
|
|
&root_id,
|
|
|
|
|
[&ids_to_pack](LibraryIDLinkCallbackData *cb_data) -> int {
|
|
|
|
|
if (cb_data->cb_flag & IDWALK_CB_LOOPBACK) {
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
if (cb_data->cb_flag & (IDWALK_CB_EMBEDDED | IDWALK_CB_EMBEDDED_NOT_OWNING)) {
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ID *self_id = cb_data->self_id;
|
|
|
|
|
ID *referenced_id = *cb_data->id_pointer;
|
|
|
|
|
if (!referenced_id) {
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
if (!ID_IS_LINKED(referenced_id)) {
|
|
|
|
|
CLOG_ERROR(&LOG, "Linked data-block references non-linked data-block");
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
if (ID_IS_PACKED(referenced_id)) {
|
|
|
|
|
/* A linked ID can use another packed linked ID, as long as it is not from the same
|
|
|
|
|
* library. */
|
|
|
|
|
BLI_assert(referenced_id->lib && referenced_id->lib->archive_parent_library);
|
|
|
|
|
if (referenced_id->lib->archive_parent_library == self_id->lib) {
|
|
|
|
|
CLOG_ERROR(&LOG,
|
|
|
|
|
"Non-packed data-block references packed data-block from the same library, "
|
|
|
|
|
"which is not allowed");
|
|
|
|
|
}
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
if (referenced_id->newid && ID_IS_PACKED(referenced_id->newid)) {
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
if (GS(referenced_id->name) == ID_KE) {
|
|
|
|
|
/* Shape keys cannot be directly linked, from linking code PoV they behave as embedded
|
|
|
|
|
* data (i.e. their owning data is responsible to handle them). */
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ids_to_pack.add(referenced_id);
|
|
|
|
|
return IDWALK_RET_NOP;
|
|
|
|
|
},
|
|
|
|
|
nullptr,
|
|
|
|
|
IDWALK_READONLY | IDWALK_RECURSE);
|
|
|
|
|
|
|
|
|
|
pack_linked_ids(bmain, ids_to_pack);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void blender::bke::library::main_cleanup_parent_archives(Main &bmain)
|
|
|
|
|
{
|
|
|
|
|
LISTBASE_FOREACH (Library *, lib, &bmain.libraries) {
|
|
|
|
|
if (lib->flag & LIBRARY_FLAG_IS_ARCHIVE) {
|
|
|
|
|
BLI_assert(!lib->runtime || lib->runtime->archived_libraries.is_empty());
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
int i_read_curr = 0;
|
|
|
|
|
int i_insert_curr = 0;
|
|
|
|
|
for (; i_read_curr < lib->runtime->archived_libraries.size(); i_read_curr++) {
|
|
|
|
|
if (!lib->runtime->archived_libraries[i_read_curr]) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (i_insert_curr < i_read_curr) {
|
|
|
|
|
lib->runtime->archived_libraries[i_insert_curr] =
|
|
|
|
|
lib->runtime->archived_libraries[i_read_curr];
|
|
|
|
|
}
|
|
|
|
|
i_insert_curr++;
|
|
|
|
|
}
|
|
|
|
|
BLI_assert(i_insert_curr <= i_read_curr);
|
|
|
|
|
if (i_insert_curr < i_read_curr) {
|
|
|
|
|
lib->runtime->archived_libraries.resize(i_insert_curr);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|