diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/BKE_asset_engine.h | 241 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_global.h | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_library.h | 26 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/asset_engine.c | 458 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/blender.c | 3 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/library.c | 5 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/library_asset.c | 272 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/node.c | 2 |
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)); } |