Files
test2/source/blender/blenkernel/BKE_blendfile_link_append.hh
Jacques Lucke 4e4976804e Core: Add packed linked data-blocks
This adds support for packed linked data. This is a key part of an improved
asset workflow in Blender.

Packed IDs remain considered as linked data (i.e. they cannot be edited),
but they are stored in the current blendfile. This means that they:
* Are not lost in case the library data becomes unavailable.
* Are not changed in case the library data is updated.

These packed IDs are de-duplicated across blend-files, so e.g. if a shot
file and several of its dependencies all use the same util geometry node,
there will be a single copy of that geometry node in the shot file.

In case there are several versions of a same ID (e.g. linked at different
moments from a same library, which has been modified in-between), there
will be several packed IDs.

Name collisions are averted by storing these packed IDs into a new type of
'archive' libraries (and their namespaces). These libraries:
* Only contain packed IDs.
* Are owned and managed by their 'real' library data-block, called an
  'archive parent'.

For more in-depth, technical design: #132167
UI/UX design: #140870

Co-authored-by: Bastien Montagne <bastien@blender.org>
Pull Request: https://projects.blender.org/blender/blender/pulls/133801
2025-09-26 10:53:40 +02:00

440 lines
17 KiB
C++

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
/** \file
* \ingroup bke
*/
#include <list>
#include <string>
#include "BLI_bit_vector.hh"
#include "BLI_function_ref.hh"
#include "BLI_map.hh"
#include "BLO_readfile.hh"
struct BlendHandle;
struct ID;
struct Library;
struct LibraryLink_Params;
struct MainLibraryWeakReferenceMap;
struct ReportList;
/* TODO: Rename file to `BKE_blendfile_import.hh`. */
/* TODO: Replace `BlendfileLinkAppend` prefix by `blender::bke::blendfile::import` namespace. */
/* TODO: Move these enums to scoped enum classes. */
/** Actions to apply to an item (i.e. linked ID). */
enum {
LINK_APPEND_ACT_UNSET = 0,
LINK_APPEND_ACT_KEEP_LINKED,
LINK_APPEND_ACT_REUSE_LOCAL,
LINK_APPEND_ACT_MAKE_LOCAL,
LINK_APPEND_ACT_COPY_LOCAL,
};
/** Various status info about an item (i.e. linked ID). */
enum {
/** An indirectly linked ID. */
LINK_APPEND_TAG_INDIRECT = 1 << 0,
/**
* An ID also used as liboverride dependency (either directly, as a liboverride reference, or
* indirectly, as data used by a liboverride reference). It should never be directly made local.
*
* Mutually exclusive with #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY_ONLY.
*/
LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY = 1 << 1,
/**
* An ID only used as liboverride dependency (either directly or indirectly, see
* #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY for precisions). It should not be considered during
* the 'make local' process, and remain purely linked data.
*
* Mutually exclusive with #LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY.
*/
LINK_APPEND_TAG_LIBOVERRIDE_DEPENDENCY_ONLY = 1 << 2,
};
/* NOTE: These three structs are currently exposed in header to allow for their usage in RNA.
* Regular C++ code should not access their content directly.
*
* TODO: Refactor these three structs into classes, and integrated the whole API into them. */
struct BlendfileLinkAppendContext;
/** A data-block (ID) entry in the `items` list from #BlendfileLinkAppendContext. */
struct BlendfileLinkAppendContextItem {
/**
* Link/Append context owner of this item. Used in RNA API, could be removed once RNA paths are
* functional.
*/
BlendfileLinkAppendContext *lapp_context;
/** Name of the ID (without the heading two-chars IDcode). */
std::string name;
/** All libraries (from #BlendfileLinkAppendContext.libraries) to try to load this ID from. */
blender::BitVector<> libraries;
/** ID type. */
short idcode;
/**
* Type of action to perform on this item, and general status tag information.
* NOTE: Mostly used by append post-linking processing.
*/
char action;
char tag;
/** Newly linked ID (nullptr until it has been successfully linked). */
ID *new_id;
/**
* Library ID from which the #new_id has been linked
* (nullptr until it has been successfully linked).
*/
Library *source_library;
/**
* Liboverride of the linked ID
* (nullptr until it has been successfully created or an existing one has been found).
*/
ID *liboverride_id;
/**
* Whether the item has a matching local ID that was already appended from the same source
* before, and has not been modified. In 'Append & Reuse' case, this local ID _may_ be reused
* instead of making linked data local again.
*/
ID *reusable_local_id;
/** Opaque user data pointer. */
void *userdata;
};
/** A blend-file library entry in the `libraries` vector from #BlendfileLinkAppendContext. */
struct BlendfileLinkAppendContextLibrary {
/** Absolute .blend file path. */
std::string path;
/** Blend file handle, if any. */
BlendHandle *blo_handle;
/** Whether the blend file handle is owned, or borrowed. */
bool blo_handle_is_owned;
/** The blend-file report associated with the `blo_handle`, if owned. */
BlendFileReadReport bf_reports;
};
/**
* General container for all relevant data for a library/linked-data related operation (linking,
* appending, library relocating, etc.).
*/
struct BlendfileLinkAppendContext {
/** List of library paths to search IDs in. */
blender::Vector<BlendfileLinkAppendContextLibrary> libraries;
/**
* List of all ID to try to link from #libraries. This is a linked list because iterators must
* not be invalidated when adding more items.
*/
std::list<BlendfileLinkAppendContextItem> items;
using items_iterator_t = std::list<BlendfileLinkAppendContextItem>::iterator;
/** Linking/appending parameters. Including `bmain`, `scene`, `viewlayer` and `view3d`. */
LibraryLink_Params *params = nullptr;
/**
* What is the current stage of the link/append process. Used mainly by the RNA wrappers for the
* pre/post handlers currently.
*/
enum class ProcessStage {
/**
* The context data is being filled with data (Libraries and IDs) to process. Nothing has been
* linked yet.
*/
Init = 0,
/** The context data is being used to linked IDs. */
Linking,
/**
* The context data is being used to append IDs (i.e. make local linked ones, or re-use already
* existing local ones).
*/
Appending,
/**
* The context data is being used to instantiate (loose) IDs (i.e. ensure that Collections,
* Objects and/or ObjectData IDs are added to the current scene).
*/
Instantiating,
/**
* All data has been linked or appended. The context state represents the final result of the
* process.
*/
Done,
/* NOTE: For the time being, liboverride step is not considered here (#BKE_blendfile_override).
* Mainly because it is only available through the BPY API currently. */
};
ProcessStage process_stage;
/** Allows to easily find an existing items from an ID pointer. */
blender::Map<ID *, BlendfileLinkAppendContextItem *> new_id_to_item;
/** Runtime info used by append code to manage re-use of already appended matching IDs. */
MainLibraryWeakReferenceMap *library_weak_reference_mapping = nullptr;
/** Embedded blendfile and its size, if needed. */
const void *blendfile_mem = nullptr;
size_t blendfile_memsize = 0;
};
/**
* Allocate and initialize a new context to link/append data-blocks.
*/
BlendfileLinkAppendContext *BKE_blendfile_link_append_context_new(LibraryLink_Params *params);
/**
* Free a link/append context.
*/
void BKE_blendfile_link_append_context_free(BlendfileLinkAppendContext *lapp_context);
/**
* Set or clear flags in given \a lapp_context.
*
* \param flag: A combination of:
* - #eFileSel_Params_Flag from `DNA_space_types.h` &
* - #eBLOLibLinkFlags * from `BLO_readfile.hh`.
* \param do_set: Set the given \a flag if true, clear it otherwise.
*/
void BKE_blendfile_link_append_context_flag_set(BlendfileLinkAppendContext *lapp_context,
int flag,
bool do_set);
/**
* Store reference to a Blender's embedded memfile into the context.
*
* \note This is required since embedded startup blender file is handled in `ED` module, which
* cannot be linked in BKE code.
*/
void BKE_blendfile_link_append_context_embedded_blendfile_set(
BlendfileLinkAppendContext *lapp_context, const void *blendfile_mem, int blendfile_memsize);
/** Clear reference to Blender's embedded startup file into the context. */
void BKE_blendfile_link_append_context_embedded_blendfile_clear(
BlendfileLinkAppendContext *lapp_context);
/**
* Add a new source library to search for items to be linked to the given link/append context.
*
* \param libname: the absolute path to the library blend file.
* \param blo_handle: the blend file handle of the library, `nullptr` if not available. Note that
* the ownership of this handle is always stolen, because readfile code may
* forcefully clear this handle after reading in some cases (endianness
* conversion, see usages of the #FD_FLAGS_SWITCH_ENDIAN flag).
*
* \note *Never* call #BKE_blendfile_link_append_context_library_add()
* after having added some items.
*/
void BKE_blendfile_link_append_context_library_add(BlendfileLinkAppendContext *lapp_context,
const char *libname,
BlendHandle *blo_handle);
/**
* Add a new item (data-block name and `idcode`) to be searched and linked/appended from libraries
* associated to the given context.
*
* \param userdata: an opaque user-data pointer stored in generated link/append item.
*
* TODO: Add a more friendly version of this function that combines it with the call to
* #BKE_blendfile_link_append_context_item_library_index_enable to enable the added item for all
* added library sources.
*/
BlendfileLinkAppendContextItem *BKE_blendfile_link_append_context_item_add(
BlendfileLinkAppendContext *lapp_context, const char *idname, short idcode, void *userdata);
#define BLENDFILE_LINK_APPEND_INVALID -1
/**
* Search for all ID matching given `id_types_filter` in given `library_index`, and add them to
* the list of items to process.
*
* \note #BKE_blendfile_link_append_context_library_add should never be called on the same
*`lapp_context` after this function.
*
* \param id_types_filter: A set of `FILTER_ID` bit-flags, the types of IDs to add to the items
* list.
* \param library_index: The index of the library to look into, in given `lapp_context`.
*
* \return The number of items found and added to the list, or `BLENDFILE_LINK_APPEND_INVALID` if
* it could not open the .blend file.
*/
int BKE_blendfile_link_append_context_item_idtypes_from_library_add(
BlendfileLinkAppendContext *lapp_context,
ReportList *reports,
uint64_t id_types_filter,
int library_index);
/**
* Enable search of the given \a item into the library stored at given index in the link/append
* context.
*/
void BKE_blendfile_link_append_context_item_library_index_enable(
BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item,
int library_index);
/**
* Check if given link/append context is empty (has no items to process) or not.
*/
bool BKE_blendfile_link_append_context_is_empty(BlendfileLinkAppendContext *lapp_context);
void *BKE_blendfile_link_append_context_item_userdata_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
ID *BKE_blendfile_link_append_context_item_newid_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
/**
* Replace the newly linked ID by another from the same library. Rarely used, necessary e.g. in
* some complex 'do version after setup' code when an ID is replaced by another one.
*/
void BKE_blendfile_link_append_context_item_newid_set(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item,
ID *new_id);
ID *BKE_blendfile_link_append_context_item_liboverrideid_get(
BlendfileLinkAppendContext *lapp_context, BlendfileLinkAppendContextItem *item);
short BKE_blendfile_link_append_context_item_idcode_get(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item);
enum eBlendfileLinkAppendForeachItemFlag {
/** Loop over directly linked items (i.e. those explicitly defined by user code). */
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_DIRECT = 1 << 0,
/**
* Loop over indirectly linked items (i.e. those defined by internal code, as dependencies of
* direct ones).
*
* IMPORTANT: Those 'indirect' items currently may not cover **all** indirectly linked data.
* See comments in #foreach_libblock_link_append_callback.
*/
BKE_BLENDFILE_LINK_APPEND_FOREACH_ITEM_FLAG_DO_INDIRECT = 1 << 1,
};
/**
* Iterate over all (or a subset) of the items listed in given #BlendfileLinkAppendContext,
* and call the `callback_function` on them.
*
* \param flag: Control which type of items to process (see
* #eBlendfileLinkAppendForeachItemFlag enum flags).
* \param userdata: An opaque void pointer passed to the `callback_function`.
*/
void BKE_blendfile_link_append_context_item_foreach(
BlendfileLinkAppendContext *lapp_context,
/**
* Called over each (or a subset of each) of the items in given #BlendfileLinkAppendContext.
*
* \return `true` if iteration should continue, `false` otherwise.
*/
blender::FunctionRef<bool(BlendfileLinkAppendContext *lapp_context,
BlendfileLinkAppendContextItem *item)> callback_function,
eBlendfileLinkAppendForeachItemFlag flag);
/**
* Called once the link/append process has been fully initialized (all of its data has been set).
*
* NOTE: Currently only used to call the matching handler.
*/
void BKE_blendfile_link_append_context_init_done(BlendfileLinkAppendContext *lapp_context);
/**
* Perform linking operation on all items added to given `lapp_context`.
*/
void BKE_blendfile_link(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Perform packing operation.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*/
void BKE_blendfile_link_pack(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Perform append operation, using modern ID usage looper to detect which ID should be kept
* linked, made local, duplicated as local, re-used from local etc.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*/
void BKE_blendfile_append(BlendfileLinkAppendContext *lapp_context, ReportList *reports);
/**
* Instantiate loose data in the scene (e.g. add object to the active collection).
*/
void BKE_blendfile_link_append_instantiate_loose(BlendfileLinkAppendContext *lapp_context,
ReportList *reports);
/**
* Finalize the link/append process.
*
* NOTE: Currently only used to call the matching handler..
*/
void BKE_blendfile_link_append_context_finalize(BlendfileLinkAppendContext *lapp_context);
/**
* Options controlling the behavior of liboverrides creation.
*/
enum eBKELibLinkOverride {
BKE_LIBLINK_OVERRIDE_INIT = 0,
/**
* Try to find a matching existing liboverride first, instead of always creating a new one.
*
* \note Takes into account the #BKE_LIBLINK_CREATE_RUNTIME flag too (i.e. only checks for
* runtime liboverrides if that flag is set, and vice-versa).
*/
BKE_LIBLINK_OVERRIDE_USE_EXISTING_LIBOVERRIDES = 1 << 0,
/**
* Create (or return an existing) runtime liboverride, instead of a regular saved-in-blend-files
* one. See also the #ID_TAG_RUNTIME tag of IDs in DNA_ID.h.
*
* \note Typically, usage of this flag implies that no linked IDs are instantiated, such that
* their usages remain indirect.
*/
BKE_LIBLINK_OVERRIDE_CREATE_RUNTIME = 1 << 1,
};
/**
* Create (or find existing) liboverrides from linked data.
*
* The IDs processed by this functions are the one that have been linked by a previous call to
* #BKE_blendfile_link on the same `lapp_context`.
*
* Control over how liboverrides are created is done through the extra #eBKELibLinkOverride flags.
*
* \warning Currently this function only performs very (very!) basic liboverrides, with no handling
* of dependencies or hierarchies. It is not expected to be directly exposed to users in its
* current state, but rather as a helper for specific use-cases like 'presets assets' handling.
*/
void BKE_blendfile_override(BlendfileLinkAppendContext *lapp_context,
const eBKELibLinkOverride flags,
ReportList *reports);
/**
* Try to relocate all linked IDs added to `lapp_context`, belonging to the given `library`.
*
* This function searches for matching IDs (type and name) in all libraries added to the given
* `lapp_context`.
*
* Typical usages include:
* - Relocating a library:
* - Add the new target library path to `lapp_context`.
* - Add all IDs from the library to relocate to `lapp_context`
* - Mark the new target library to be considered for each ID.
* - Call this function.
*
* - Searching for (e.g.missing) linked IDs in a set or sub-set of libraries:
* - Add all potential library sources paths to `lapp_context`.
* - Add all IDs to search for to `lapp_context`.
* - Mark which libraries should be considered for each ID.
* - Call this function.
*
* NOTE: content of `lapp_context` after execution of that function should not be assumed valid
* anymore, and should immediately be freed.
*/
void BKE_blendfile_library_relocate(BlendfileLinkAppendContext *lapp_context,
ReportList *reports,
Library *library,
bool do_reload);
/**
* Relocate a single linked ID.
*
* NOTE: content of `lapp_context` after execution of that function should not be assumed valid
* anymore, and should immediately be freed.
*/
void BKE_blendfile_id_relocate(BlendfileLinkAppendContext &lapp_context, ReportList *reports);