This is an updated version of the design proposal for integrating asset concept into Blender, as well as report of current work done so far and roadmap of future development.
Some areas are already quite well defined (and partly implemented), others remain more fuzzy. Design always evolve while you actually implement it, anyway, issues you did not foresee need to be fixed, new ideas come while writing (and using!) code, etc.
Any comment/critic/suggestion is welcome. This page will be regularly updated as work progress, unless some radical change in plans happen, in which case a new version shall rather be added.
Also, a “code design” part is available at the end - this one is really technical and can totally be skipped if you’re not interested in dirty implementation details.
Here are the key ideas behind current proposal:
- We do not define an asset management tool in Blender, only an API to communicate with asset engines.
- Blender does not need to define what is an asset, how it behaves, etc. It globally sticks to our current library system (link/append).
- We define three 'areas' of asset handling: browsing/loading them, 'editing' local version of already loaded assets, and editing/defining assets themselves.
- Hence, only a few parts of Blender are asset-aware. Currently, the file browser, and probably the outliner too (for locally used assets management). And our loading/linking/appending code needs some changes, of course, but current general behavior/principles are kept.
- Thus, we keep .blend as data storage.
- However, we introduce the concept of 'virtual library', to allow mere image/fonts/videos/audio files to be directly considered as assets too.
What is an asset? Nothing and everything. From Blender PoV, there are no assets. Blender only knows of datablocks (objects, meshes, materials, textures, images, etc.). From user side, an asset can be anything he would like to manage 'as a whole', to store it somewhere for later reuse, to share, to import, etc. Can be materials, objects or groups defining a character, …
And there are also some really advanced and specialized asset concepts, like e.g. parametric ones used for 'interior design' (to build your own kitchen from basic pre-defined 'bricks'), with complex behavior each time you add or even move those 'assets' in your scene.
Blender cannot define an 'asset' type (datablock) that would handle all that kind of possibilities. It's just out of the scope. Thus, the concept of asset shall be as shallow and minimum as possible within Blender.
"What" is an asset, how it behaves, etc. is defined by the asset engine.
However, there is something about assets that must be defined from Blender side: a minimum set of metadata, used to identify and list them. Here are currently proposed ones, based on mere file metadata (i.e. what we already have in current filebrowser):
- repositories have an optional path.
- assets have a name and description.
- assets have tags (for filter/categorization purposes).
- assets have preview.
- variants (e.g. lowpoly, highpoly, …).
- variants have a name and description.
- revisions (for asset engines preserving history, like BAM…).
- revisions have no name (their uuid shall be enough usually).
- revisions have a comment (e.g. commit message).
- revisions have a timestamp.
- views (e.g. jpg, png and exr versions of a same image texture).
- views have a name and description.
- views have a timestamp (which shall be the same as the parent revision one) and size info.
Repositories, assets, variants, revisions and views all have uuids (128bits numbers).
Blender identifies an asset by its asset engine and its repository/asset/variant/revision/views uuids.
It should be noted that while this five-levels tree data structure does define to some extent an asset storage layout, it is absolutely not required to follow that layout. For the most part, Blender does not care at all about what those five levels actually are, nor what they actually mean, so asset engine are free to use them pretty much as they like, generate dummy data for levels they do not use, etc. The only strong, mandatory requirement is that the asset engine should always be able to return the path to a single, unique data-block to link, given a set of five uuids.
We may also want to add some kind of 'priority' concept to Blender's datablocks. The idea behind it is that you may not want to see by default all your loaded datablocks in the Outliner and/or ID listings. IDs would then get a priority level, and user could chose up to which level to show/list IDs. Asset engines could use this trick e.g. to only show the main item of their asset, and 'hide' dependencies from user by default.
I’m not quite convinced we really need this, however, afraid it would end making both programmer's and user's life harder, by adding yet another parameter to handle…
Conceptually, an asset engine is similar to a render engine. Blender defines a generic "asset API", and each engine implements it as it needs/can.
An asset engine does not handle loading of data itself (as in, it won't directly give Blender a new mesh data-block, etc.). It is mostly here to gather metadata available (see above) and present it to the user through the API. It can also be used to expose data only available remotely, and ensure it's made available locally (as .blend files!) when user actually decides to use it. It may perform some actions on 'assets' once appended/linked, but it does not handle appending/linking itself.
In other words: it is similar to some kind of specialized filesystem/database, a bridge between a given asset management system and Blender.
This allows three things:
- Keep a system that works already quite nicely.
- Ensure (to some extent) some security fallback - that is, that .blend files using 'assets' from a given engine do not become completely broken and unusable as soon as this asset
engine becomes unreachable for some reason.
- Blender can integrate with (ideally) any kind of asset management workflow, from a mere local FS based one to a complex remote storage system, using files or database to store metadata, etc.
This implies asset engines are always able to provide real valid paths to their data on request, directly usable by Blender core code.
Aside from that, an asset engine can be:
- A light and simple storage of metadata together with actual .blend files on a local filesystem (or in a single archive even).
- An interface to some network service, that allows browsing and downloading asset from the web.
- An interface to some VCS-like asset management/storage system (like BAM).
- An advanced specialized tool that adds procedural scripting to imported assets (the 'kitchen design' type ;) ).
We can also imagine more exotic applications, like a procedural object generator (pretty similar to 'add objects' addons, but it would benefit from a full space to show/list and pre-configure 'assets' before actually doing the generative work). E.g. a 3D fractal generator, a tree generator, …
Since asset engines are mere addons, nothing prevent them to add more panels where they need them to provides extra features (like external render engines do, e.g. a material-specialized asset engine could add direct access in material buttons, etc.).
Here are some generic use-case scenarios demonstrating how current work is expected to behave.
See also T46059.
That is, browsing them, loading them, updating them… that is the part work has been focused on, so far.
- Open FileBrowser
- Select desired asset engine
- Browser requests asset engine to list a root directory ('/' by default).
- Asset engine returns a list of entries, which are fake files and directories.
- Keep navigating as in a regular file browser that way.
- Filter shown entries by type, name etc. as currently, but also select a variant and revision for each entry (how to do so UI-wise is still WIP, aside from mere list buttons there are also ideas like global time 'scrolling' for revisions…) - filtering and sorting is provided by the asset engine (such that it can customize it to its needs).
- (Optionally) request the asset engine to 'preload' some entries (assets). This is mainly intended for asset engines handling remote storage, to avoid having to download locally the whole selection at once at the end, but could be used for any other heavy/long pre-processing.
- Once actually importing the selection:
- asset engine's pre-import callback is called with selected (entry/variant/revision) items. This callback is expected to return a list of paths to real data.
- import op actually imports those data (pretty much exactly the same way as current code, the only difference is that a reference to asset engine and asset uuid is stored within IDs).
- asset engines's post-import callback is called with imported IDs. This is for advanced processing, like e.g. applying scripts on those IDs (auto-place/resize objects, whatever).
When saving a .blend file, there is nothing special to do. Datablocks from asset engines are saved like any other appended/linked one.
When loading a saved file with some data from asset engine, load code itself is not changed. If the local files from asset engine are still available, asset datablocks will be loaded just like any appended/linked one, otherwise new place-holders system will be used, just as with 'regular' .blend libs (see missing-libs branch).
However, it is quite likely that some lib files will be missing, or a new version of some asset has become available, etc. To handle that, a second pass (after file itself is loaded) will be run, that will inquire relevant asset engine for datablocks that 'belong' to it, and try to reload them if needed (can also check for asset's revisions, to check the latest one, etc.).
Loaded Asset Management
That is, remove an asset from current file, or change its variant/revision. This should be handled by the Outliner (to be consistent with current behavior, outliner is for 'current' data, while filebrowser is to add new data).
There is nothing really specific here, aside one key point (which is also needed for the 'reload' behavior described above): for a given ID, we must be able to reload its data. This would also be more than handy even with current system, since it would allow to 'fix' missing libs easily (see id-remap branch).
Note that in addition to always using a manually specified variant/revision of an asset, there will also be an option to use 'default' variant, and 'latest' revision.
That is, creating, editing, deleting assets from the proposed collection. This is the second step of the plan, goal is to first have a relatively 'feature complete' version of 'Using Assets' before tackling it. Also, this may need a new space - not totally convinced it would be wise to extend current filebrowser to support that - and yet it would probably share a lot of code with it.
Main task would be to control metadata of the assets (tags, variants, revisions).
There are no detailed plans for this part yet, general idea would be to have two views, one showing the 'asset engine' content (i.e. available assets), the other a 'real' file browser. One could then select datablocks from the 'file' part to define a new asset, or add to an existing one, etc.
Editing the content itself of the asset (its data) could be done e.g. by launching a new Blender instance to make the edits (using a 'forged' Blender file to contain all the asset data, and only the asset data?). Think this should be left to the asset engine though.
Current Status and Roadmap
Current work done (in addition to the full filebrowser rewrite already merged in Blender 2.76) is visible in asset-engine, missing-libs and id-remap branches. The first one contains work done on asset engine itself, the second one the 'place-holder IDs' used to not lose anymore a datablock from a .blend file when it cannot be found on load time, and the third one some (WIP) code to allow replacing a given datablock by another all over Blender.
Now in master, most efforts have been spent so far on enhancing the filebrowser (better previews, better behavior when listing datablocks, more filtering options, etc.), and making it ready for assets (reworking how files are listed…).
In asset-engine branch, a first version of needed API has been written, alongside with a (basically working) asset engine demo, codename Amber.
First goal currently is to finish work in id-remap (think missing-libs is pretty much master-ready), to allow to relocate libs and reload datablocks from them in real time, without having to save and re-open .blend file.
Next step is to finish basics of asset engine part I (that is, browsing/importing libs - already mostly done - and reloading from asset-engine on next opening of the .blend - mostly TODO still, depends on id-remap/missing-libs work too).
Once this is done, a quick & dirty 'asset editing' would be nice, to help testers to really play with Amber (since manual editing of JSon files is not that fun…).
Less urgent TODOs include:
- Look at BAM! Think it currently basically is an enhanced blendfiles-aware VCS system (no tags nor variants), but we want to support it as asset engine at the end, so will have to seriously look at it.
Comments and suggestions are obviously most welcome on any part of above document, but here are some points that still require serious investigation, and some rather speculative possible extensions of asset concept (for when basics are up and running ;) ).
- Appended data status is still unclear here. Do we want/need to keep 'asset references' to them? Or do we keep current stance, which is 'once appended, data belongs to current file and is considered 100% local'? Would go for second choice here…
- Proxies. I lack knowledge of this part of the code, yet I suspect it could be troublesome?
- UI: as said in roadmap section, it will need some serious work (though it’s still a bit early yet imho), will keep UI team updated as soon as we can really start working on this (not a big fan of preliminary mockups, those often forget too much about reality of the code behind UI ;) ).
- Do we stick to strict ID datablocks only, or do we accept some other simple storage types directly (like fonts, images, video, audio, etc. files)? In the former case, do we need some more ID types (e.g. for pose collections)? In the later case, this implies our append/link code to be extended slightly. Presets could also be a popular 'asset' type.
- Custom asset types? Thinking about advanced, preset/procedural assets here. E.g. we could have assets defining 'clouds' and other smoky things, it should be possible to hack around using current ID types, but maybe defining its own custom types could be useful here. Same goes e.g. if someone wants to propose 'mesh effects' as assets, would take the mesh and apply to it some scripted operations (in post-load callback). Those could be linked to a fake, 'NULL' ID type e.g. during import phase. Could be convenient, but on the other hand I suspect it would add quite a bit of complications, not sure it’s worth it. This is for later anyway.
There have already been some design proposals about assets in Blender, which helped defining/refining current one, found at least those two:
- Elubie's quite advanced work: http://wiki.blender.org/index.php/User:Elubie/AssetBrowser
- A proposal found during random browsing, by blurymind (Todor Imreorov): http://wiki.blender.org/index.php/Dev:Ref/Proposals/UI/Asset_Manager
This part is rather technical, it aims at describing how things (will) work code-wise.
This part of the work has been mostly merged in master for blender 2.76. The core concepts of the changes are (listing only those directly related to asset engine project):
- Separate completely items listing on browser side (the FileDirEntry... structs in DNA_space_types.h) from actual entries listing (which depends on backend/asset engine used - for mere file browsing, it uses private FileListIntern, which is a shallow wrapper around built-in basic direntry struct from BLI_fileops_types.h).
- Bring back direntry/BLI dir listing to what they should be - a shallow wrapper around OS-centric stat struct (previously it was way too overloaded by filebrowser-specific data).
- Heavily reduce weight on filebrowser side by only storing a sliding window of the whole available entries (the currently visible ones with some margin, to simplify).
- Introduce the entry/variant/revision concept in filebrowser data (by-passed in case of mere file listing of course).
Thus, filebrowser is now ready to work with asset engines, at least on the basic level.
Appending/Linking and Lib Handling
Here are the points known so far that need to be fixed/improved/implemented in our library handling code (see also T45351):
- Being able to import several datablocks at once, of different types and from different files (done, this is in master for Blender 2.76 now).
- Being resilient to missing lib data on file opening, and ability to reload those later (mostly done, see below for details).
- Being able to query the asset engine off a linked asset to load it again, update it to its latest version, etc. (TODO, will happen through AE API calls and use above work).
Addressing Missing Linked Data
- Adding a 'palceholder' ID when a linked datablock cannot be found. This placeholder has the same type as missing real data, but is mostly empty/set to default values. It is tagged by a specific flag, so that user can easily find them (in outliner e.g.). This was rather simple and straightforward to support.
- Adding support to 'remap' an ID by another (without having to reload the .blend file). Basics of this idea are easy to implement, but it's a bit tricky to ensure it's fully working, due to the complex interactions between IDs. In essence, current code uses BKE_library_foreach_ID_link() to update all old pointers to some datablock to new ones, and uses/extends UI ID free callbacks (like free_notifier_reference_cb, and replaces free_editor_id_reference_cb by remap_editor_id_reference_cb…).
From there it’s rather trivial to add options in Outliner (and/or maybe even any ID UI block) to replace a given ID by another.
The Sub-Datablocks Issue
Sub-datablocks or dependency datablocks (sub-IDs), that is, the ones indirectly linked because they are used by an asset (the materials, textures, etc. of an object e.g.).
Those create a problem when you want to update/reload an asset:
- If the reloaded asset keeps using same sub-IDs, it's OK, nothing to do.
- If the reloaded asset uses new/different sub-IDs, we want to delete current ones, and load new ones.
- But, if a sub-ID is used by another asset, or is made a 'direct link' by user, then we want to both keep current one and load new one for reloaded asset. No, we do not! See below.
- And last but not least, some sub-IDs are made 'direct links' during linking process (objects in particular, because they have to be instantiated in current scene). In that specific case, we do not want to keep old instantiated copy!
To address that issue, we need a way to know a posteriori which ID is used by which asset-ID (even indirectly). We could do that using some BKE's library_query stuff (though I'd expect some complications with the 'objects-made-direct-links' case), or we could cache it (e.g. in a linked list of ID pointers in each ID) during read/link time.
Solution is being worked on, here are current proposed points:
- No guarantee of any kind is put on sub-IDs of an asset. Users may use those at their own risk, that’s not recommended at all, if not forbidden.
- Maybe we should hide sub-IDs from user? That sounds rather complex to setup though...
- When reloading an asset, we always (try to) totally update all its sub-IDs, even their non-asset usages.
- A same sub-ID may be shared between different assets. However, there is always only one version of a sub-ID in a same .blend file (in other words, reloading one asset that’d update a sub-ID will automatically update that sub-ID for all other assets using it).
- Assets break dependency chain. That is, if an asset uses another asset, then that other asset is not considered as a sub-ID (since for assets we have versioning etc., all above issues do not apply anymore). It may be independently directly linked/used by user, there may be several version of it in a same .blend file, etc.
So asset engines (repository) should make real assets of any sub-IDs user could want to use independently (typical examples: armature object of rigged characters, materials, textures...). Non-asset sub-IDs should only be used by stuff very unlikely to be shared (e.g.: shape keys, probably anim data, etc.).
This is relatively restricted in current code: only affects datablocks that are thin wrappers around external files (image, font, text and sound). To implement this:
- A new 'variant' of Library datablock was added, so-called 'virtual', which basically does not have any path to a Blender file, it only stores some asset repository data.
- Then, linking (and asset update) are extended a bit to accept paths of files of relevant types (images, videos, etc.), and:
- Call relevant datablock loader.
- Attach newly loaded ID to virtual library, add its uuid data, etc.
The most important change here is in write/readfile:
- Virtual libraries are split from Main as the other ones.
- However, their datablocks are written as regular datablocks, and not as ID_ID 'placeholders'.
Asset Engines will be mere python addons, communicating with Blender through a generic API, quite similar to how Render Engines (like Cycles) work. There isn’t much to say code-wise here, this is mostly boring RNA mapping code, and usual type/instance structs with helpers in dedicated BKE_asset area…
Two data structs will be modified for asset needs currently:
- ID: an AssetUUID pointer is added to every IDs (left to NULL for non-assets datablocks). This contains three 128bit uuids for asset itself, its variant and revision. Blender only handle those as opaque numbers, they only mean something for the relevant asset engine. They must be unique however (used e.g. by filebrowser to handle selections…).
- Library: those have reference to the asset engine/repository (engine idname, engine version, and path to repository root), used to communicate later with the engine.