Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r--source/blender/blenkernel/BKE_asset_engine.h241
-rw-r--r--source/blender/blenkernel/BKE_global.h4
-rw-r--r--source/blender/blenkernel/BKE_library.h26
-rw-r--r--source/blender/blenkernel/CMakeLists.txt1
-rw-r--r--source/blender/blenkernel/intern/asset_engine.c458
-rw-r--r--source/blender/blenkernel/intern/blender.c3
-rw-r--r--source/blender/blenkernel/intern/library.c5
-rw-r--r--source/blender/blenkernel/intern/library_asset.c272
-rw-r--r--source/blender/blenkernel/intern/node.c2
9 files changed, 1006 insertions, 6 deletions
diff --git a/source/blender/blenkernel/BKE_asset_engine.h b/source/blender/blenkernel/BKE_asset_engine.h
index 6ad9de6918d..137b7b3958d 100644
--- a/source/blender/blenkernel/BKE_asset_engine.h
+++ b/source/blender/blenkernel/BKE_asset_engine.h
@@ -36,8 +36,244 @@
extern "C" {
#endif
-struct AssetUUID;
+#include "DNA_space_types.h"
+
+struct AssetEngine;
+struct AssetEngineType;
+struct AssetUUIDList;
+struct bContext;
+struct FileDirEntryArr;
+struct FileDirEntry;
+struct FileDirEntryVariant;
+struct FileDirEntryRevision;
+struct FileDirEntryView;
+struct ExtensionRNA;
+struct ID;
+struct IDProperty;
+struct ListBase;
struct Main;
+struct ReportList;
+struct uiLayout;
+
+enum {
+ AE_STATUS_VALID = 1 << 0, /* Asset engine is "OK" (if unset engine won't be used). */
+ AE_STATUS_RUNNING = 1 << 1, /* Asset engine is performing some background tasks... */
+};
+
+#define AE_FAKE_ENGINE_ID "NONE"
+
+extern ListBase asset_engines;
+
+/**************************************************************************************/
+/* ***** Those callbacks help manage engine and its jobs. ***** */
+/**************************************************************************************/
+
+/* AE instance/job is valid, is running, is idle, etc. */
+typedef int (*ae_status)(struct AssetEngine *engine, const int job_id);
+
+/* Report progress ([0.0, 1.0] range) of given job. */
+typedef float (*ae_progress)(struct AssetEngine *engine, const int job_id);
+
+/* To force end of given job (e.g. because it was cancelled by user...). */
+typedef void (*ae_kill)(struct AssetEngine *engine, const int job_id);
+
+/**************************************************************************************/
+/* ***** All callbacks below shall be non-blocking (i.e. return immediately). ***** */
+/**************************************************************************************/
+
+/* Those callbacks will be called from a 'fake-job' start *and* update functions (i.e. main thread,
+ * working one will just sleep).
+ *
+ * If given id is not AE_JOB_ID_UNSET, engine should update from a running job if available,
+ * otherwise it should start a new one. It is the responsability of the engine to start/stop
+ * background processes to actually perform tasks as/if needed.
+ *
+ * If the engine returns AE_JOB_ID_INVALID as job id, then code assumes whole execution was done in
+ * that single first call (i.e. allows engine that do not need it to not bother with whole async
+ * crap - they should then process the whole request in a very short amount of time (typically
+ * below 100ms).
+ *
+ * Most of those callbacks shall not return actual entries, but only (mainly) total number of
+ * available entries. The only callback returning actual entries should be ae_entries_block_get()
+ * (which returns a limited subset of all available ones, the visible ones usually), and
+ * ae_entries_uuid_get().
+ */
+#define AE_JOB_ID_UNSET 0
+#define AE_JOB_ID_INVALID -1
+
+/* FILEBROWSER - List everything available at given root path - only returns numbers of entries!
+ * Note that asset engine may change root_path here too. */
+typedef int (*ae_list_dir)(struct AssetEngine *engine,
+ const int job_id,
+ struct FileDirEntryArr *entries_r);
+
+/* FILEBROWSER - Get previews of given entries.
+ * XXX WARNING! Currently, only asset part of uuids is valid here (because fileentries only store
+ * this one)... Think this makes more sense anyway, or do we want different previews per variants
+ * or revisions too? */
+typedef int (*ae_previews_get)(struct AssetEngine *engine,
+ const int job_id,
+ struct AssetUUIDList *uuids);
+
+/* 'update' hook, called to prepare updating of given entries (typically after a file (re)load).
+ * Engine should check whether given assets are still valid, if they should be updated, etc.
+ * uuids tagged as needing reload will then be reloaded as new ones
+ * (ae_load_pre, then actual lib loading, then ae_load_post).
+ * \warning This callback is expected to handle **real** UUIDS (not 'users' filebrowser ones),
+ * i.e. calling ae_load_pre with those shall **not** alter them in returned direntries
+ * (else 'link' between old IDs and reloaded ones would be broken). */
+typedef int (*ae_update_check)(struct AssetEngine *engine,
+ const int job_id,
+ struct AssetUUIDList *uuids);
+
+/* Ensure given assets (uuids) are really available for append/link (some kind of 'anticipated
+ * loading'...). Note: Engine should expect any kind of UUIDs it produced here (i.e. real ones as
+ * well as 'virtual' filebrowsing ones). */
+typedef int (*ae_ensure_uuids)(struct AssetEngine *engine,
+ const int job_id,
+ struct AssetUUIDList *uuids);
+
+/**************************************************************************************/
+/* ***** All callbacks below are blocking. They shall be completed upon return. ***** */
+/**************************************************************************************/
+
+/* FILEBROWSER - Perform sorting and/or filtering on engines' side.
+ * Note that engine is assumed to feature its own sorting/filtering settings!
+ * Number of available filtered entries is to be set in entries_r.
+ */
+typedef bool (*ae_sort_filter)(struct AssetEngine *engine,
+ const bool sort,
+ const bool filter,
+ struct FileSelectParams *params,
+ struct FileDirEntryArr *entries_r);
+
+/* FILEBROWSER - Return specified block of entries in entries_r. */
+typedef bool (*ae_entries_block_get)(struct AssetEngine *engine,
+ const int start_index,
+ const int end_index,
+ struct FileDirEntryArr *entries_r);
+
+/* FILEBROWSER - Return specified entries from their uuids, in entries_r. */
+typedef bool (*ae_entries_uuid_get)(struct AssetEngine *engine,
+ struct AssetUUIDList *uuids,
+ struct FileDirEntryArr *entries_r);
+
+/* 'pre-loading' hook, called before opening/appending/linking/updating given entries.
+ * Note first given uuid is the one of 'active' entry, and first entry in returned list will be
+ * considered as such too. E.g. allows the engine to ensure entries' paths are actually valid by
+ * downloading requested data, etc. If is_virtual is True, then there is no requirement that
+ * returned paths actually exist. Note that the generated list shall be simpler than the one
+ * generated by ae_list_dir, since only the path from active revision is used, no need to bother
+ * with variants, previews, etc. This allows to present 'fake' entries to user, and then import
+ * actual data.
+ */
+typedef bool (*ae_load_pre)(struct AssetEngine *engine,
+ struct AssetUUIDList *uuids,
+ struct FileDirEntryArr *entries_r);
+
+/* 'post-loading' hook, called after opening/appending/linking/updating given entries.
+ * E.g. allows an advanced engine to make fancy scripted operations over loaded items. */
+/* TODO */
+typedef bool (*ae_load_post)(struct bContext *C,
+ struct AssetEngine *engine,
+ struct AssetUUIDList *uuids);
+
+/* Check if given dirpath is valid for current asset engine, it can also modify it if do_change is
+ * true. r_dir is assumed to be least FILE_MAX. returns true if path in r_dir is valid. */
+typedef bool (*ae_check_dir)(struct AssetEngine *engine, char *r_dir, bool do_change);
+
+typedef struct AssetEngineType {
+ struct AssetEngineType *next, *prev;
+
+ /* type info */
+ char idname[64]; /* best keep the same size as BKE_ST_MAXNAME */
+ int version;
+
+ char name[64];
+ int flag;
+
+ /* API */
+ ae_status status;
+ ae_progress progress;
+
+ ae_kill kill;
+
+ ae_list_dir list_dir;
+ ae_sort_filter sort_filter;
+ ae_entries_block_get entries_block_get;
+ ae_entries_uuid_get entries_uuid_get;
+
+ ae_previews_get previews_get;
+
+ ae_ensure_uuids ensure_uuids;
+
+ ae_load_pre load_pre;
+ ae_load_post load_post;
+
+ ae_update_check update_check;
+
+ ae_check_dir check_dir;
+
+ /* RNA integration */
+ struct ExtensionRNA ext;
+} AssetEngineType;
+
+typedef struct AssetEngine {
+ AssetEngineType *type;
+ void *py_instance;
+
+ /* Custom sub-classes properties. */
+ IDProperty *properties;
+
+ int flag;
+ int refcount;
+
+ struct ReportList *reports;
+} AssetEngine;
+
+/* AssetEngine->flag */
+enum {
+ AE_DIRTY_FILTER = 1 << 0,
+ AE_DIRTY_SORTING = 1 << 1,
+};
+
+/* Engine Types */
+void BKE_asset_engines_init(void);
+void BKE_asset_engines_exit(void);
+
+AssetEngineType *BKE_asset_engines_find(const char *idname);
+AssetEngineType *BKE_asset_engines_get_default(char *r_idname, const size_t len);
+
+/* Engine Instances */
+AssetEngine *BKE_asset_engine_create(AssetEngineType *type, struct ReportList *reports);
+AssetEngine *BKE_asset_engine_copy(AssetEngine *engine);
+void BKE_asset_engine_free(AssetEngine *engine);
+
+struct AssetUUIDList *BKE_asset_engine_entries_load_pre(AssetEngine *engine,
+ struct FileDirEntryArr *r_entries);
+struct FileDirEntryArr *BKE_asset_engine_uuids_load_pre(AssetEngine *engine,
+ struct AssetUUIDList *r_uuids);
+
+/* File listing utils... */
+
+typedef enum FileCheckType {
+ CHECK_NONE = 0,
+ CHECK_DIRS = 1 << 0,
+ CHECK_FILES = 1 << 1,
+ CHECK_ALL = CHECK_DIRS | CHECK_FILES,
+} FileCheckType;
+
+void BKE_filedir_view_free(struct FileDirEntryView *view);
+
+void BKE_filedir_revision_free(struct FileDirEntryRevision *rev);
+
+void BKE_filedir_variant_free(struct FileDirEntryVariant *var);
+
+void BKE_filedir_entry_free(struct FileDirEntry *entry);
+void BKE_filedir_entry_clear(struct FileDirEntry *entry);
+struct FileDirEntry *BKE_filedir_entry_copy(struct FileDirEntry *entry);
+
+void BKE_filedir_entryarr_clear(struct FileDirEntryArr *array);
#define ASSETUUID_SUB_EQUAL(_uuida, _uuidb, _member) \
(memcmp((_uuida)->_member, (_uuidb)->_member, sizeof((_uuida)->_member)) == 0)
@@ -56,7 +292,10 @@ unsigned int BKE_asset_uuid_hash(const void *key);
bool BKE_asset_uuid_cmp(const void *a, const void *b);
void BKE_asset_uuid_print(const struct AssetUUID *uuid);
+void BKE_asset_uuid_list_free(struct AssetUUIDList *uuid_list);
+
void BKE_asset_main_search(struct Main *bmain, struct AssetUUID *uuid);
+struct AssetUUIDList *BKE_asset_main_list(struct Main *bmain);
#ifdef __cplusplus
}
diff --git a/source/blender/blenkernel/BKE_global.h b/source/blender/blenkernel/BKE_global.h
index f6cae6d8a9c..5bd1961ca89 100644
--- a/source/blender/blenkernel/BKE_global.h
+++ b/source/blender/blenkernel/BKE_global.h
@@ -114,6 +114,10 @@ enum {
G_FLAG_EVENT_SIMULATE = (1 << 3),
G_FLAG_USERPREF_NO_SAVE_ON_EXIT = (1 << 4),
+ G_ASSETS_NEED_RELOAD = (1 << 10),
+ G_ASSETS_FAIL = (1 << 11),
+ G_ASSETS_QUIET = (1 << 12),
+
G_FLAG_SCRIPT_AUTOEXEC = (1 << 13),
/** When this flag is set ignore the prefs #USER_SCRIPT_AUTOEXEC_DISABLE. */
G_FLAG_SCRIPT_OVERRIDE_PREF = (1 << 14),
diff --git a/source/blender/blenkernel/BKE_library.h b/source/blender/blenkernel/BKE_library.h
index 7883d740b0a..f19fa0becfe 100644
--- a/source/blender/blenkernel/BKE_library.h
+++ b/source/blender/blenkernel/BKE_library.h
@@ -31,11 +31,37 @@
extern "C" {
#endif
+struct AssetEngineType;
+struct AssetUUID;
+struct BlendThumbnail;
+struct GHash;
+struct ID;
struct Library;
struct Main;
void BKE_library_filepath_set(struct Main *bmain, struct Library *lib, const char *filepath);
+void BKE_library_asset_repository_init(struct Library *lib,
+ const struct AssetEngineType *aet,
+ const char *repo_root);
+void BKE_library_asset_repository_clear(struct Library *lib);
+void BKE_library_asset_repository_free(struct Library *lib);
+struct AssetRef *BKE_library_asset_repository_asset_add(struct Library *lib, const void *idv);
+void BKE_library_asset_repository_asset_remove(struct Library *lib, const void *idv);
+struct AssetRef *BKE_library_asset_repository_asset_find(struct Library *lib, const void *idv);
+void BKE_library_asset_repository_subdata_add(struct AssetRef *aref, const void *idv);
+void BKE_library_asset_repository_subdata_remove(struct AssetRef *aref, const void *idv);
+
+void BKE_libraries_asset_subdata_remove(struct Main *bmain, const void *idv);
+void BKE_libraries_asset_repositories_clear(struct Main *bmain);
+void BKE_libraries_asset_repositories_rebuild(struct Main *bmain);
+struct AssetRef *BKE_libraries_asset_repository_uuid_find(struct Main *bmain,
+ const struct AssetUUID *uuid);
+struct Library *BKE_library_asset_virtual_ensure(struct Main *bmain,
+ const struct AssetEngineType *aet);
+
+#define IS_TAGGED(_id) ((_id) && (((ID *)_id)->tag & LIB_TAG_DOIT))
+
#ifdef __cplusplus
}
#endif
diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt
index 92e0904c568..cf6f97cf306 100644
--- a/source/blender/blenkernel/CMakeLists.txt
+++ b/source/blender/blenkernel/CMakeLists.txt
@@ -150,6 +150,7 @@ set(SRC
intern/lib_query.c
intern/lib_remap.c
intern/library.c
+ intern/library_asset.c
intern/light.c
intern/lightprobe.c
intern/linestyle.c
diff --git a/source/blender/blenkernel/intern/asset_engine.c b/source/blender/blenkernel/intern/asset_engine.c
index b309db81f88..5bd88bee145 100644
--- a/source/blender/blenkernel/intern/asset_engine.c
+++ b/source/blender/blenkernel/intern/asset_engine.c
@@ -29,25 +29,434 @@
* \ingroup bke
*/
+#include <stddef.h>
+#include <stdlib.h>
#include <string.h>
-#include "BLI_utildefines.h"
+#include "MEM_guardedalloc.h"
+
+#include "RNA_access.h"
+#include "RNA_types.h"
+
+#include "BLT_translation.h"
+
+#include "BLI_fileops_types.h"
#include "BLI_hash_mm2a.h"
#include "BLI_listbase.h"
+#include "BLI_string.h"
+#include "BLI_utildefines.h"
+
+#include "PIL_time.h"
#include "BKE_asset_engine.h"
+#include "BKE_global.h"
+#include "BKE_idprop.h"
+#include "BKE_library.h"
#include "BKE_main.h"
+#include "BKE_report.h"
#include "IMB_imbuf.h"
-#include "DNA_ID.h"
-
-#include "MEM_guardedalloc.h"
+#include "DNA_space_types.h"
#ifdef WITH_PYTHON
# include "BPY_extern.h"
#endif
+/* Asset engine types (none intern!). */
+
+ListBase asset_engines = {NULL, NULL};
+
+void BKE_asset_engines_init(void)
+{
+ /* We just add a dummy engine, which 'is' our intern filelisting code from space_file! */
+ AssetEngineType *aet = MEM_callocN(sizeof(*aet), __func__);
+
+ BLI_strncpy(aet->idname, AE_FAKE_ENGINE_ID, sizeof(aet->idname));
+ BLI_strncpy(aet->name, "None", sizeof(aet->name));
+
+ BLI_addhead(&asset_engines, aet);
+}
+
+void BKE_asset_engines_exit(void)
+{
+ AssetEngineType *type, *next;
+
+ for (type = asset_engines.first; type; type = next) {
+ next = type->next;
+
+ BLI_remlink(&asset_engines, type);
+
+ if (type->ext.free) {
+ type->ext.free(type->ext.data);
+ }
+
+ MEM_freeN(type);
+ }
+}
+
+AssetEngineType *BKE_asset_engines_find(const char *idname)
+{
+ AssetEngineType *type;
+
+ type = BLI_findstring(&asset_engines, idname, offsetof(AssetEngineType, idname));
+
+ return type;
+}
+
+AssetEngineType *BKE_asset_engines_get_default(char *r_idname, const size_t len)
+{
+ AssetEngineType *type = asset_engines.first;
+
+ BLI_assert(type);
+
+ if (r_idname) {
+ BLI_strncpy(r_idname, type->idname, len);
+ }
+
+ return type;
+}
+
+/* Asset engine instances. */
+
+/* Create, Free */
+
+AssetEngine *BKE_asset_engine_create(AssetEngineType *type, ReportList *reports)
+{
+ AssetEngine *engine;
+
+ BLI_assert(type);
+
+ engine = MEM_callocN(sizeof(AssetEngine), __func__);
+ engine->type = type;
+ engine->refcount = 1;
+
+ /* initialize error reports */
+ if (reports) {
+ engine->reports = reports; /* must be initialized already */
+ }
+ else {
+ engine->reports = MEM_mallocN(sizeof(ReportList), __func__);
+ BKE_reports_init(engine->reports, RPT_STORE | RPT_FREE);
+ }
+
+ return engine;
+}
+
+/** Shalow copy only (i.e. memory is 100% shared, just increases refcount). */
+AssetEngine *BKE_asset_engine_copy(AssetEngine *engine)
+{
+ engine->refcount++;
+ return engine;
+}
+
+void BKE_asset_engine_free(AssetEngine *engine)
+{
+ if (engine->refcount-- == 1) {
+#ifdef WITH_PYTHON
+ if (engine->py_instance) {
+ BPY_DECREF_RNA_INVALIDATE(engine->py_instance);
+ }
+#endif
+
+ if (engine->properties) {
+ IDP_FreeProperty(engine->properties);
+ }
+
+ if (engine->reports && (engine->reports->flag & RPT_FREE)) {
+ BKE_reports_clear(engine->reports);
+ MEM_freeN(engine->reports);
+ }
+
+ MEM_freeN(engine);
+ }
+}
+
+/* API helpers. */
+
+static void asset_engine_load_pre(AssetEngine *engine,
+ AssetUUIDList *r_uuids,
+ FileDirEntryArr *r_entries)
+{
+ FileDirEntry *en;
+ AssetUUID *uuid;
+ const int nbr_entries = r_entries->nbr_entries ? r_entries->nbr_entries : r_uuids->nbr_uuids;
+
+ if (r_entries->nbr_entries) {
+ BLI_assert(r_uuids->uuids == NULL);
+ r_uuids->uuids = MEM_callocN(sizeof(*r_uuids->uuids) * nbr_entries, __func__);
+ r_uuids->nbr_uuids = nbr_entries;
+ r_uuids->asset_engine_version = engine->type->version;
+
+ for (en = r_entries->entries.first, uuid = r_uuids->uuids; en; en = en->next, uuid++) {
+ memcpy(uuid->uuid_repository, en->uuid_repository, sizeof(uuid->uuid_repository));
+
+ memcpy(uuid->uuid_asset, en->uuid, sizeof(uuid->uuid_asset));
+
+ FileDirEntryVariant *var = BLI_findlink(&en->variants, en->act_variant);
+ BLI_assert(var);
+ memcpy(uuid->uuid_variant, var->uuid, sizeof(uuid->uuid_variant));
+
+ FileDirEntryRevision *rev = BLI_findlink(&var->revisions, var->act_revision);
+ BLI_assert(rev);
+ memcpy(uuid->uuid_revision, rev->uuid, sizeof(uuid->uuid_revision));
+
+ memcpy(uuid->uuid_view, en->entry->uuid, sizeof(uuid->uuid_view));
+ }
+ }
+
+ BKE_filedir_entryarr_clear(r_entries);
+
+ if (!engine->type->load_pre(engine, r_uuids, r_entries)) {
+ /* If load_pre returns false (i.e. fails), clear all paths! */
+ /* TODO: report!!! */
+ BKE_filedir_entryarr_clear(r_entries);
+
+ MEM_freeN(r_uuids->uuids);
+ r_uuids->uuids = NULL;
+ r_uuids->nbr_uuids = 0;
+ return;
+ }
+
+ /* load_pre may change things, we have to rebuild our uuids list from returned entries. */
+ r_entries->nbr_entries = r_uuids->nbr_uuids = BLI_listbase_count(&r_entries->entries);
+ r_uuids->uuids = MEM_reallocN(r_uuids->uuids, sizeof(*r_uuids->uuids) * r_uuids->nbr_uuids);
+ for (en = r_entries->entries.first, uuid = r_uuids->uuids; en; en = en->next, uuid++) {
+ FileDirEntryVariant *var;
+ FileDirEntryRevision *rev;
+ FileDirEntryView *view;
+
+ memcpy(uuid->uuid_repository, en->uuid_repository, sizeof(uuid->uuid_repository));
+
+ memcpy(uuid->uuid_asset, en->uuid, sizeof(uuid->uuid_asset));
+
+ var = BLI_findlink(&en->variants, en->act_variant);
+ BLI_assert(var);
+ memcpy(uuid->uuid_variant, var->uuid, sizeof(uuid->uuid_variant));
+
+ rev = BLI_findlink(&var->revisions, var->act_revision);
+ BLI_assert(rev);
+ memcpy(uuid->uuid_revision, rev->uuid, sizeof(uuid->uuid_revision));
+
+ view = BLI_findlink(&rev->views, rev->act_view);
+ BLI_assert(view);
+ memcpy(uuid->uuid_view, view->uuid, sizeof(uuid->uuid_view));
+ }
+}
+
+/** Call load_pre for given entries, and return new uuids/entries. */
+AssetUUIDList *BKE_asset_engine_entries_load_pre(AssetEngine *engine, FileDirEntryArr *r_entries)
+{
+ AssetUUIDList *uuids = MEM_callocN(sizeof(*uuids), __func__);
+
+ asset_engine_load_pre(engine, uuids, r_entries);
+
+ return uuids;
+}
+
+/** Call load_pre for given uuids, and return new uuids/entries. */
+FileDirEntryArr *BKE_asset_engine_uuids_load_pre(AssetEngine *engine, AssetUUIDList *r_uuids)
+{
+ FileDirEntryArr *entries = MEM_callocN(sizeof(*entries), __func__);
+
+ asset_engine_load_pre(engine, r_uuids, entries);
+
+ return entries;
+}
+
+/* FileDirxxx handling. */
+
+void BKE_filedir_view_free(FileDirEntryView *view)
+{
+ if (view->name) {
+ MEM_freeN(view->name);
+ }
+ if (view->description) {
+ MEM_freeN(view->description);
+ }
+ MEM_freeN(view);
+}
+
+void BKE_filedir_revision_free(FileDirEntryRevision *rev)
+{
+ if (rev->comment) {
+ MEM_freeN(rev->comment);
+ }
+
+ if (!BLI_listbase_is_empty(&rev->views)) {
+ FileDirEntryView *view, *view_next;
+
+ for (view = rev->views.first; view; view = view_next) {
+ view_next = view->next;
+ BKE_filedir_view_free(view);
+ }
+
+ BLI_listbase_clear(&rev->views);
+ }
+ MEM_freeN(rev);
+}
+
+void BKE_filedir_variant_free(FileDirEntryVariant *var)
+{
+ if (var->name) {
+ MEM_freeN(var->name);
+ }
+ if (var->description) {
+ MEM_freeN(var->description);
+ }
+
+ if (!BLI_listbase_is_empty(&var->revisions)) {
+ FileDirEntryRevision *rev, *rev_next;
+
+ for (rev = var->revisions.first; rev; rev = rev_next) {
+ rev_next = rev->next;
+ BKE_filedir_revision_free(rev);
+ }
+
+ BLI_listbase_clear(&var->revisions);
+ }
+ MEM_freeN(var);
+}
+
+void BKE_filedir_entry_clear(FileDirEntry *entry)
+{
+ if (entry->name) {
+ MEM_freeN(entry->name);
+ }
+ if (entry->description) {
+ MEM_freeN(entry->description);
+ }
+ if (entry->relpath) {
+ MEM_freeN(entry->relpath);
+ }
+ if (entry->image) {
+ IMB_freeImBuf(entry->image);
+ }
+ /* For now, consider FileDirEntryRevision::poin as not owned here, so no need to do anything
+ * about it */
+
+ if (!BLI_listbase_is_empty(&entry->variants)) {
+ FileDirEntryVariant *var, *var_next;
+
+ for (var = entry->variants.first; var; var = var_next) {
+ var_next = var->next;
+ BKE_filedir_variant_free(var);
+ }
+
+ BLI_listbase_clear(&entry->variants);
+ }
+ else if (entry->entry) {
+ MEM_freeN(entry->entry);
+ }
+
+ memset(entry, 0, sizeof(*entry));
+}
+
+void BKE_filedir_entry_free(FileDirEntry *entry)
+{
+ BKE_filedir_entry_clear(entry);
+ MEM_freeN(entry);
+}
+
+/** Perform and return a full (deep) duplicate of given entry. */
+FileDirEntry *BKE_filedir_entry_copy(FileDirEntry *entry)
+{
+ FileDirEntry *entry_new = MEM_dupallocN(entry);
+
+ if (entry->name) {
+ entry_new->name = MEM_dupallocN(entry->name);
+ }
+ if (entry->description) {
+ entry_new->description = MEM_dupallocN(entry->description);
+ }
+ if (entry->relpath) {
+ entry_new->relpath = MEM_dupallocN(entry->relpath);
+ }
+ if (entry->image) {
+ entry_new->image = IMB_dupImBuf(entry->image);
+ }
+ /* For now, consider FileDirEntryRevision::poin as not owned here, so no need to do anything
+ * about it */
+
+ entry_new->entry = NULL;
+ if (!BLI_listbase_is_empty(&entry->variants)) {
+ FileDirEntryVariant *var;
+ int act_var;
+
+ BLI_listbase_clear(&entry_new->variants);
+ for (act_var = 0, var = entry->variants.first; var; act_var++, var = var->next) {
+ FileDirEntryVariant *var_new = MEM_dupallocN(var);
+ const bool is_act_var = (act_var == entry->act_variant);
+
+ if (var->name) {
+ var_new->name = MEM_dupallocN(var->name);
+ }
+ if (var->description) {
+ var_new->description = MEM_dupallocN(var->description);
+ }
+
+ BLI_listbase_clear(&var_new->revisions);
+ FileDirEntryRevision *rev;
+ int act_rev;
+ for (act_rev = 0, rev = var->revisions.first; rev; act_rev++, rev = rev->next) {
+ FileDirEntryRevision *rev_new = MEM_dupallocN(rev);
+ const bool is_act_rev = (act_rev == var->act_revision);
+
+ if (rev->comment) {
+ rev_new->comment = MEM_dupallocN(rev->comment);
+ }
+
+ BLI_listbase_clear(&rev_new->views);
+ FileDirEntryView *view;
+ int act_view;
+ for (act_view = 0, view = rev->views.first; view; act_view++, view = view->next) {
+ FileDirEntryView *view_new = MEM_dupallocN(view);
+ const bool is_act_view = (act_view == rev->act_view);
+
+ if (view->name) {
+ view_new->name = MEM_dupallocN(view->name);
+ }
+ if (view->description) {
+ view_new->description = MEM_dupallocN(view->description);
+ }
+
+ BLI_addtail(&rev_new->views, view_new);
+
+ if (is_act_var && is_act_rev && is_act_view) {
+ entry_new->entry = view_new;
+ }
+ }
+
+ BLI_addtail(&var_new->revisions, rev_new);
+ }
+
+ BLI_addtail(&entry_new->variants, var_new);
+ }
+ }
+ else if (entry->entry) {
+ entry_new->entry = MEM_dupallocN(entry->entry);
+ }
+
+ BLI_assert(entry_new->entry != NULL);
+
+ /* TODO: tags! */
+
+ return entry_new;
+}
+
+void BKE_filedir_entryarr_clear(FileDirEntryArr *array)
+{
+ FileDirEntry *entry, *entry_next;
+
+ for (entry = array->entries.first; entry; entry = entry_next) {
+ entry_next = entry->next;
+ BKE_filedir_entry_free(entry);
+ }
+ BLI_listbase_clear(&array->entries);
+ array->nbr_entries = 0;
+ array->nbr_entries_filtered = 0;
+}
+
/* Various helpers */
void BKE_asset_uuid_runtime_reset(AssetUUID *uuid) {
uuid->preview_image_buffer = NULL;
@@ -106,6 +515,12 @@ void BKE_asset_uuid_print(const AssetUUID *uuid)
uuid->uuid_view[3]);
}
+void BKE_asset_uuid_list_free(struct AssetUUIDList *uuid_list)
+{
+ MEM_SAFE_FREE(uuid_list->uuids);
+ MEM_freeN(uuid_list);
+}
+
/** Search the whole Main for a given asset uuid.
*
* \note if found, ID is put into uuid->id pointer. */
@@ -125,3 +540,38 @@ void BKE_asset_main_search(Main *bmain, AssetUUID *uuid)
}
FOREACH_MAIN_ID_END;
}
+
+/** Get a list of all IDs/assets and their uuids present in current bmain
+ * (includes local ones and linked ones).
+ * Returned uuid list should be freed with \a BKE_asset_uuid_list_free. */
+struct AssetUUIDList *BKE_asset_main_list(struct Main *bmain)
+{
+ ID *id;
+ AssetUUIDList *uuid_list = MEM_callocN(sizeof(*uuid_list), __func__);
+
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->uuid != NULL) {
+ uuid_list->nbr_uuids++;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+
+ if (uuid_list->nbr_uuids == 0) {
+ return uuid_list;
+ }
+
+ uuid_list->uuids = MEM_mallocN(sizeof(*uuid_list->uuids) * (size_t)uuid_list->nbr_uuids,
+ __func__);
+ int uuid_index = 0;
+ FOREACH_MAIN_ID_BEGIN (bmain, id) {
+ if (id->uuid != NULL) {
+ uuid_list->uuids[uuid_index] = *id->uuid;
+ uuid_list->uuids[uuid_index].id = id;
+ uuid_index++;
+ }
+ }
+ FOREACH_MAIN_ID_END;
+ BLI_assert(uuid_index == uuid_list->nbr_uuids);
+
+ return uuid_list;
+}
diff --git a/source/blender/blenkernel/intern/blender.c b/source/blender/blenkernel/intern/blender.c
index e8aa13a8beb..1b113de3799 100644
--- a/source/blender/blenkernel/intern/blender.c
+++ b/source/blender/blenkernel/intern/blender.c
@@ -33,10 +33,13 @@
#include "BLI_string.h"
#include "BLI_utildefines.h"
+#include "RNA_access.h"
+
#include "IMB_imbuf.h"
#include "IMB_moviecache.h"
#include "BKE_addon.h"
+#include "BKE_asset_engine.h"
#include "BKE_blender.h" /* own include */
#include "BKE_blender_user_menu.h"
#include "BKE_blender_version.h" /* own include */
diff --git a/source/blender/blenkernel/intern/library.c b/source/blender/blenkernel/intern/library.c
index 64ffea22363..adb672b579e 100644
--- a/source/blender/blenkernel/intern/library.c
+++ b/source/blender/blenkernel/intern/library.c
@@ -79,6 +79,11 @@ IDTypeInfo IDType_ID_LI = {
void BKE_library_filepath_set(Main *bmain, Library *lib, const char *filepath)
{
+ if (lib->flag & LIBRARY_FLAG_VIRTUAL) {
+ BLI_assert(!"Setting path for virtual libraries makes no sense.");
+ return;
+ }
+
/* in some cases this is used to update the absolute path from the
* relative */
if (lib->name != filepath) {
diff --git a/source/blender/blenkernel/intern/library_asset.c b/source/blender/blenkernel/intern/library_asset.c
new file mode 100644
index 00000000000..515e0f4a693
--- /dev/null
+++ b/source/blender/blenkernel/intern/library_asset.c
@@ -0,0 +1,272 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2015,2016 by Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Bastien Montagne.
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/blenkernel/intern/library_asset.c
+ * \ingroup bke
+ *
+ * Contains asset-related management of ID's and libraries.
+ */
+
+#include <string.h>
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_utildefines.h"
+
+#include "RNA_types.h"
+
+#include "BKE_asset_engine.h"
+#include "BKE_lib_id.h"
+#include "BKE_lib_query.h"
+#include "BKE_library.h"
+#include "BKE_main.h"
+
+/* Asset managing - TODO: we most likely want to turn this into a hashing at some point, could
+ * become a bit slow when having huge assets (or many of them)... */
+void BKE_library_asset_repository_init(Library *lib,
+ const AssetEngineType *aet,
+ const char *repo_root)
+{
+ BKE_library_asset_repository_free(lib);
+ lib->asset_repository = MEM_mallocN(sizeof(*lib->asset_repository), __func__);
+
+ BLI_strncpy(lib->asset_repository->asset_engine,
+ aet->idname,
+ sizeof(lib->asset_repository->asset_engine));
+ lib->asset_repository->asset_engine_version = aet->version;
+ BLI_strncpy(lib->asset_repository->root, repo_root, sizeof(lib->asset_repository->root));
+
+ BLI_listbase_clear(&lib->asset_repository->assets);
+}
+
+void BKE_library_asset_repository_clear(Library *lib)
+{
+ if (lib->asset_repository) {
+ for (AssetRef *aref; (aref = BLI_pophead(&lib->asset_repository->assets));) {
+ BLI_freelistN(&aref->id_list);
+ MEM_freeN(aref);
+ }
+ }
+}
+
+void BKE_library_asset_repository_free(Library *lib)
+{
+ if (lib->asset_repository) {
+ BKE_library_asset_repository_clear(lib);
+ MEM_freeN(lib->asset_repository);
+ lib->asset_repository = NULL;
+ }
+}
+
+AssetRef *BKE_library_asset_repository_asset_add(Library *lib, const void *idv)
+{
+ const ID *id = idv;
+ BLI_assert(id->uuid != NULL);
+
+ AssetRef *aref = BKE_library_asset_repository_asset_find(lib, idv);
+ if (!aref) {
+ aref = MEM_callocN(sizeof(*aref), __func__);
+ aref->uuid = *id->uuid;
+ BKE_library_asset_repository_subdata_add(aref, idv);
+ BLI_addtail(&lib->asset_repository->assets, aref);
+ }
+
+ return aref;
+}
+
+AssetRef *BKE_library_asset_repository_asset_find(Library *lib, const void *idv)
+{
+ const ID *id = idv;
+ BLI_assert(id->uuid != NULL);
+
+ for (AssetRef *aref = lib->asset_repository->assets.first; aref; aref = aref->next) {
+ if (ASSETUUID_EQUAL(&aref->uuid, id->uuid)) {
+#ifndef NDEBUG
+ LinkData *link = aref->id_list.first;
+ BLI_assert(link && (link->data == idv));
+#endif
+ return aref;
+ }
+ }
+ return NULL;
+}
+
+void BKE_library_asset_repository_asset_remove(Library *lib, const void *idv)
+{
+ AssetRef *aref = BKE_library_asset_repository_asset_find(lib, idv);
+ BLI_remlink(&lib->asset_repository->assets, aref);
+ BLI_freelistN(&aref->id_list);
+ MEM_freeN(aref);
+}
+
+void BKE_library_asset_repository_subdata_add(AssetRef *aref, const void *idv)
+{
+ if (BLI_findptr(&aref->id_list, idv, offsetof(LinkData, data)) == NULL) {
+ BLI_addtail(&aref->id_list, BLI_genericNodeN((void *)idv));
+ }
+}
+
+void BKE_library_asset_repository_subdata_remove(AssetRef *aref, const void *idv)
+{
+ LinkData *link = BLI_findptr(&aref->id_list, idv, offsetof(LinkData, data));
+ if (link) {
+ BLI_freelinkN(&aref->id_list, link);
+ }
+}
+
+void BKE_libraries_asset_subdata_remove(Main *bmain, const void *idv)
+{
+ const ID *id = idv;
+
+ if (id->lib == NULL) {
+ return;
+ }
+
+ ListBase *lb = &bmain->libraries;
+ for (Library *lib = lb->first; lib; lib = lib->id.next) {
+ if (lib->asset_repository) {
+ for (AssetRef *aref = lib->asset_repository->assets.first; aref; aref = aref->next) {
+ LinkData *subdata = aref->id_list.first;
+ /* Skip first one, it's main asset, not subdata! */
+ for (subdata = subdata->next; subdata; subdata = subdata->next) {
+ if (subdata->data == idv) {
+ BLI_freelinkN(&aref->id_list, subdata);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+
+void BKE_libraries_asset_repositories_clear(Main *bmain)
+{
+ ListBase *lb = &bmain->libraries;
+ for (Library *lib = lb->first; lib; lib = lib->id.next) {
+ BKE_library_asset_repository_clear(lib);
+ }
+ BKE_main_id_tag_all(bmain, LIB_TAG_ASSET, false);
+}
+
+static int library_asset_dependencies_rebuild_cb(LibraryIDLinkCallbackData *data)
+{
+ if (!data->id_pointer || !*data->id_pointer) {
+ return IDWALK_RET_NOP;
+ }
+
+ AssetRef *aref = data->user_data;
+ ID *id = *data->id_pointer;
+
+ if (id->uuid) {
+ return IDWALK_RET_STOP_RECURSION;
+ }
+
+ printf("%s (from %s)\n", id->name, data->id_self->name);
+
+ BKE_library_asset_repository_subdata_add(aref, (const void *)id);
+ id->tag |= LIB_TAG_ASSET;
+ return IDWALK_RET_NOP;
+}
+
+static void library_asset_dependencies_rebuild(ID *asset)
+{
+ Library *lib = asset->lib;
+
+ asset->tag |= LIB_TAG_ASSET;
+
+ if (!(lib && lib->asset_repository)) {
+ /* 'local' definition of an asset, nothing else to do. */
+ return;
+ }
+
+ AssetRef *aref = BKE_library_asset_repository_asset_add(lib, asset);
+
+ /* TODO: pass main and use Main->relations? */
+ BKE_library_foreach_ID_link(
+ NULL, asset, library_asset_dependencies_rebuild_cb, aref, IDWALK_RECURSE);
+}
+
+void BKE_libraries_asset_repositories_rebuild(Main *bmain)
+{
+ ListBase *lbarray[MAX_LIBARRAY];
+ ID *id;
+ int a;
+
+ BKE_libraries_asset_repositories_clear(bmain);
+
+ a = set_listbasepointers(bmain, lbarray);
+ while (a--) {
+ for (id = lbarray[a]->first; id; id = id->next) {
+ if (id->uuid) {
+ library_asset_dependencies_rebuild(id);
+ }
+ }
+ }
+}
+
+AssetRef *BKE_libraries_asset_repository_uuid_find(Main *bmain, const AssetUUID *uuid)
+{
+ ListBase *lb = &bmain->libraries;
+ for (Library *lib = lb->first; lib; lib = lib->id.next) {
+ for (AssetRef *aref = lib->asset_repository->assets.first; aref; aref = aref->next) {
+ if (ASSETUUID_EQUAL(&aref->uuid, uuid)) {
+#ifndef NDEBUG
+ LinkData *link = aref->id_list.first;
+ BLI_assert(link && ((ID *)link->data)->uuid &&
+ ASSETUUID_EQUAL(((ID *)link->data)->uuid, uuid));
+#endif
+ return aref;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** Find or add the 'virtual' library datablock matching this asset engine, used for non-blend-data
+ * assets. */
+Library *BKE_library_asset_virtual_ensure(Main *bmain, const AssetEngineType *aet)
+{
+ Library *lib;
+ ListBase *lb = &bmain->libraries;
+
+ for (lib = lb->first; lib; lib = lib->id.next) {
+ if (!(lib->flag & LIBRARY_FLAG_VIRTUAL) || !lib->asset_repository) {
+ continue;
+ }
+
+ if (STREQ(lib->asset_repository->asset_engine, aet->idname) &&
+ lib->asset_repository->asset_engine_version == aet->version) {
+ return lib;
+ }
+ }
+
+ lib = BKE_libblock_alloc(bmain, ID_LI, "VirtualLib", 0);
+ BKE_library_asset_repository_init(lib, aet, "");
+ lib->flag |= LIBRARY_FLAG_VIRTUAL;
+ return lib;
+}
diff --git a/source/blender/blenkernel/intern/node.c b/source/blender/blenkernel/intern/node.c
index 48c6727add5..c0b908ff9d2 100644
--- a/source/blender/blenkernel/intern/node.c
+++ b/source/blender/blenkernel/intern/node.c
@@ -3214,7 +3214,7 @@ void BKE_node_clipboard_add_node(bNode *node)
node_info->id = node->id;
if (node->id) {
BLI_strncpy(node_info->id_name, node->id->name, sizeof(node_info->id_name));
- if (ID_IS_LINKED(node->id)) {
+ if (ID_IS_LINKED_DATABLOCK(node->id)) { /* Don't want virtual libraries here... */
BLI_strncpy(
node_info->library_name, node->id->lib->filepath, sizeof(node_info->library_name));
}