Asset Management (Explorer, Pipeline, and Database)

Document Overview

This document will briefly overview all aspects of our "asset pipeline", which covers how users create, import, edit, save, combine, find, use (or drag-drop/create an instance of) "Assets."

What is an Asset?

Basically, an Asset is any object (or piece of data/content) which 1) can be saved (serialized or persisted to a file on disk) and 2) which is added into the Asset Database (shown in Asset Explorer, and uniquely identified across all project/package databases by a NameID string).


What Types of Assets Are There?

Media Assets

  • This usually refers to content which is often imported from 3rd party content creation tools
  • Examples: Model, Textures, Sounds, Music, Materials, Skeletons (Animation Sets), Animations, and possibly Scene Layouts
  • These assets will often have a "SourceFile" that they were originally imported from, and can be looked up by, or syncronized with (by reimporting when that file changes)
  • These assets are often "referenced" or "shared" by the Scene Objects (or entities/components) which use them, instead of being "instanced" or "cloned" for each place they are used

Components

  • This usually refers to reusable "building blocks" (often instances of user-defined classes or "scripts") which are usually "attached" to an "owner" or "composite" component which "owns" them.
  • Examples: scripts, and agents for AI, physics, etc.
  • This is a very broad (or general) term, and can encompass (or also refer to) other more specialized types of components (eg. Entities, Media Assets, and SceneObjects)
    • Technically, components are instances of a class which implements the Visual3D.IEntityComponent interface (eg. by deriving from the Visual3D.EntityComponent base class)
    • However, the term can also be applied to any object which is represented by a "node" (or folder item) in the Asset Explorer or World Explorer, which is done by either of the following:
      • Attaching the "Component" to an Entity ("owner" or "composite" component), such as via Entity.Components.Add().
    • Or, any object exposed via property (defined by a component class) which has [ComponentTypeAttribute] applied to that property.

Asset Item (aka. Prototype)

  • This refers to a saveable object which is directly added to the Asset Database, and can therefore be looked up (found) by an ID
    • An ID (or NameID) is a string uniquely identifying it from all other asset items in every package/project in the Asset Database. 
  • The term "Prototype" is often used to describe an asset item, because most assets are a centralized definition of something which can be found/edited in one place which is then "instanced" (cloned or has a copy created of it) wherever it is used. 
    • However, technically, some Media Assets - like a Model - might not be considered a "Prototype" since they are just shared/referenced instead of cloned/instanced, however, there are still Media Assets which can be cloned (for per-instance state or overriden settings) such as is sometimes the case with Materials, Animations, and Skeletons.

Entities (aka. Component Owner/Composite Component)

  • High-level "Game object" or "simulation object" which is "built" from multiple "Components" which are attached to it (and owned by it).  Therefore thisoften scripted, AI driven, or which represents a major "actor" (participent) in the game/simulation or visible world. 
    • Often this should be relatively lightweight, user-defined, application-specific class, and should usually not be derived from classes like SceneObject or Actor
  • Examples include: Avatar, Unit, Character, Player, Mission, Scenario, Objective, Goal, Aircraft, Vehicle
    • In some cases, depending on the application requirements, this may also include: Props/Inventory Items (like Weapons) and Terrain Features (aka. World Geometry/Objects, like buildings, roads, trees, etc.)

Data Objects


  • This usually refers to "anything other kind of object" which can be saved (serialized) and which is added to the Asset Database, but which is not technically a "Component" (or any other specialized/pre-defined Visual3D asset type)


What Is the Benefit of Using Asset Database?


Asset Management (Finding, Organizing, and Using by Drag & Drop)

Any Asset Item (or object which is added to the Asset Database), will then be able to be edited, saved, and shown in Asset Explorer, as well as drag-dropped, and shown/found in Asset Selector Dialogs and Add New/Add Instance context menus. 
Objects added into Asset Explorer are, in most cases, automatically "persisted" (found, saved, and loaded), can be more reliably found and referenced (via a primarily unique and constant NameID string).

Persistence References

Soon, we will handle automatic "cross-asset" (or cross-file/system) reference persistence, allowing assets to transparently reference other assets (via saved properties) without resulting in these referenced assets being "duplicated" in the file each using asset is saved to.  This also applies for networking, allowing consistent referencing across multiple computers (eg. clients and servers).
Loading asset items by a unique NameID allows avoiding complex, slow and error prone "file name-based" references and file system access, and avoids some of the problems with files, such as them being moved, renamed, modifed, or being ambigous (with wrong file beign used due to same file name in different paths/directories).

Prototype Inheritance (Smart Copies, Compact Files)

Also, soon, Asset Items will support "prototype inheritance", which basically means that all instances of the asset will be a "cheap copies" which are very concisely defined (eg. using as little as a few bytes), by only having those property values (or components/settings) which differ from those of their prototype being saved.  This will also allow you to have a "centralized" definition of an asset, so that you can modify the prototype (eg. via Object Editor, Entity Editor, or Asset Explorer), and have all instances (which don't override the property which had been modified) to "inherit" that property change.

Source File Syncronization (and Virtual File System)

Asset items which have a "Source File" they were imported from (eg. such as is often the case for Media Assets like Models and Textures), will automatically be associated with that source file, so that they can be looked up by file name/path. And, in many cases, when a source file has been "modified" or "updated", it will be automatically "reimported" - syncronizing changes made in Architect with those made in a 3rd party tool and/or replacing the asset with the newly imported version, depending on the asset type.  In some cases, the user will at least be notified of the modified/updated asset, and prompted as to whether it should be reimported (especially important if this would replace changes made to the asset in Architect, instead of just syncronizing changes).

Asset items can be used (by developers/content creators) and deployed (distributed to end-users) without needing the "source files" to be downloaded or deployed as well, reducing download time, disk space used, and start/content loading time (by avoiding need for time consuming importing or caching), and restricting unauthorized modification or use of those assets. 

Collaborative Editing

During development (by game developers and content creators), each asset is saved to a XML-based .v3dx files, which can be more easily debugged, hand-edited, and used by other tools/applications.  Also, this format allowing them to be stored in version control systems as as Subversion (SVN) or CVS, and allowing multiple users/team members to make modifications to different assets at the same time, or even modifications to the same asset, in cases.  More robust logic for "merging" changes made by multiple users to the same asset at the same time, will soon be implemented.  However, "syncronizing" (updating and commiting) changes often (eg. daily or even automatically, every several minutes) can greatly help in reducing conflicts in "collaborative editing" or "collaborative world building."

Deployment and Digital Rights Management (DRM)

The many assets which are each saved into their own .v3dx XML file during development will be combined into

Structure of Asset Database

Asset Database has distributed structure. It consists from several parts which are situated in the same locations where project packages are.
Single Asset Database package consists from file named with pattern "<PackageName>.manifest.v3dx" (which is an database index for this part of database) and folder "#Assets" which is situated in the same location as manifest is. The folder contains serialized assets (object's data exactly), when manifest contains only short descriptions of assets to make their populating fast.
Parts of Asset Database can be connected to or disconnected from your project by defining/removing connected packages in project's config file (Tech Demos.v3d for Tech Demos):
<ContentPackages>
    <ContentPackageInfo Name="Standard Assets" ShowProjectFolder="true" />
    <ContentPackageInfo Name="Demo Assets" ShowProjectFolder="true" />
    <ContentPackageInfo Name="Tech Demos" ShowProjectFolder="true" />
</ContentPackages>
Using this example, 3 packages are connected to the project: "Standard Assets", "Demo Assets" and "Tech Demos".
For this case, Asset Database can be situated in such locations:
  1. File "Projects\Standard Assets\Standard Assets.manifest.v3dx" and folder "Projects\Standard Assets\#Assets" (if exists).
  2. File "Projects\Demo Assets\Demo Assets.manifest.v3dx" and folder "Projects\Demo Assets\#Assets".
  3. File "Projects\Tech Demos\Tech Demos.manifest.v3dx" and folder "Projects\Tech Demos\#Assets".

One of the connected packages should be default. Default package name defined in project's config like this:
<DefaultObjectDBProject>Demo Assets</DefaultObjectDBProject>
(Note: DefaultObjectDBProject will be renamed to DefaultAssetPackage).
Default package will be used for saving new assets created in the project when Asset Database is saved.
For saving Asset Database you should:
  • Press "Save World" button
  • Press Ctrl-S hotkey
  • When closing Architect, answer "Yes" on question "Would you like to save changes in Asset Explorer?"

For all this 3 cases, Asset Database, current scene and terrain/environment will be saved.

How Do I Make A Class Saveable (or Serializable)?

So long as you have your class "marked up" with standard .NET attributes (or implementing a standard .NET interface) for serialization, then that class can be added to the Asset Database, and any changes made to instances of it will be persisted to disk when you perform "Save World" (and if the asset is explicitly saved by other actions such as "Save Scene", "Save Terrain" or "Save Game").

The suggested way of doing this is by applying [DataContractAttribute] to your asset/component type (eg. class) and only applying [DataMemberAttribute] to those fields or properties which should be saved, which can include non-public (eg. private) members.  

You should also be sure that your base classes (which you have derived from directly or indirectly) are marked up in a similar way (usually using the same types of attributes or interfaces) as your own class. 
Classes which are marked up with [SerializableAttribute] (as was often done prior to .NET 3.0 for use with BinaryFormatter), are also supported, in which case all fields (whether public or non-public) are saved by default, and you have to mark those which should not be saved with [NonSerializedAttribute]. 

Classes which implement ISerializable or IXmlSerializable are also saved, though using the custom logic defined in those interface implementations.  Usually user-defined derived classes will not be able to add additional members (fields or properties) which are able to be saved, however.
Enums or Enumerations are automatically saveable, however, you can apply [EnumMemberAttribute] to define the name used for saving each enum member/value, as well as [DisplayNameAttribute] to control what text is shown for that enum member/value in Object Editor.

If a class does not have [DataContractAttribute] or [SerializableAttribute] applied to it, and does not implement ISerializable or IXmlSerializable, then usually it will default to serializing all read/writable public members (fields which aren't constant or readonly, and properties with a getter and setter).  In this case, you can apply [IgnoreDataMemberAttribute] to public members which should not be saved (ie. excluded from serialization).  This is similar to how XmlSerializer works, in serializing all public members which do not have [XmlIgnoreAttribute] applied to them, except that attributes for XmlSerializer are not supported by default for our default saving format, which is based on .NET 3.0 WCF Serialization (eg. via NetDataContractSerializer or DataContractSerializer).


How Do Add/Load/Get Assets from the Asset Database?

Adding/Getting Prototypes Using Code

To create an "instance" (clone or smart copy) of a "prototype" asset item, you should do the following:
 
AssetInfo prototypeInfo;
string prototypeID = "Default Particle System 1";
if(Library.Database.TryGet(prototypeID, out prototypeInfo))
{
    ParticleSystem instance = prototypeInfo.CreateInstance();
} else
{
    ParticleSystem prototype = new ParticleSystem();
    //configure and attach components to this prototype here..

    //get the "Asset Database Entry" defining the unique ID and metadata for the asset item (eg. prototype) it encapsulates (or "wraps")
    prototypeInfo = Library.Database.AddItem(prototype, "The Default Particle System");

    //get the ID, which is auto-assigned by the database to ensure that it is globally unique
    prototypeID = prototypeInfo.ID; //or prototypeInfo.UniqueName
}

 
This logic will be simplified with some overloads and utility methods soon. 

The "old" way of doing this (which will work in some cases, but will soon be removed), is as follows:

//this works for some classes, but not all, and this technique is going to be removed soon.
ParticleSystem prototype;

if(Library.Database.Has(out prototype))
{
    ParticleSystem instance = prototype.CreateInstance(); //or if not implemented, you can use Reflector.DeepClone() or Reflector.MemberwiseClone()
    instance.TypeInfo = prototype; //set the "prototype" or asset from which this instance was created (and inherits its definition/settings from).
}
 
Also, in some cases we now use "serialization" instead of (Memberwise/Shallow or Custom/Deep) cloning,  however, soon, we will use serialization instead to create instances of a prototype in almost all cases, and will remove or ignore implementation of IInstanced.CreateInstance().
However, this is automatically done, however, for instances created by drag-drop, Add New/Instance context menus, and certain Asset Selector dialogs).
 
The IEntityComponent.TypeInfo property is automatically set to the AssetInfo (or database entry/metadata which "wraps" the prototype/asset item) for which an object was created (or "instanced") from. This is needed for Prototype Inheritance, so that we can have "smart copies" which automatically update when their prototype (centralized definition) is modified (unless they override those properties with a different value), and this is needed so that we can therefore have very compact (or compressed) saved asset (eg. .v3dx or .v3d) files and serialized networking commands (or messages).  These enhancements are currently under development.
The reference to the prototype's AssetInfo is serialized by the EntityComponent.TypeInfoRef (or TypeInfoID) property, which, like other Asset references, is represented by a NameID string.

Adding New Prototypes Into Asset Database Using Attributes

1. Adding New Prototypes Into Asset Database At Architect Startup

For adding prototypes of specified or derived types into Asset Database, you can apply [ComponentTypeAttribute] to the type (base class or interface), specifying Group and Category properties to define where created instance will be added. Also you can specify item's Name and name of the method, which will be invoked for post-initialization after creating instance. Type to which attribute is applied, should have default parameterless constructor (even private). Example of [ComponentTypeAttribute] usage:
[ComponentType(Name = "Bullet Vehicle", Group = "Physics", Category = "Entities", IsInherited = false, InitMethodName = "_InitComponentMethod")]
public class BulletVehicle : PhysicalEntity, IVehicle
{
        ...
        private void _InitComponentMethod()
        {
        }
        ...
}

Inspection of [ComponentTypeAttribute] is executed during Architect startup and only for types that contained in assemblies, that defined in V3D config (for eg, Tech Demos.v3d) in section <ContentPackages>:
  <ContentPackages>
        <ContentPackageInfo Name="Visual3D.Engine" />
        <ContentPackageInfo Name="Visual3D.Physics" />
  </ContentPackages>

In this example, will be inspected only types, contained in assemblies Visual3D.Engine and Visual3D.Physics, other types will be ignored.
Asset Database is loaded before attributes inspecting. If instance of type already present in Asset Database but to this type [ComponentTypeAttribute] is applied, it just ignored. This guarantees that instance will not be created twice if it was already created and saved in last Architect session.
To prevent creation of instances of all derived types, you can specify IsInherited = false or apply [IgnoreComponentTypeAttribute] to derived type.

2. Adding New Prototypes Into Asset Database On Demand

You can apply [CommonComponentTypeAttribute] to base type or interface to allow creation of instance of this type and adding it to Asset Database on-demand.
In this case instance of type will not be created on Architect startup, instead you can create it at any time when need by right-click on any group or category folder in Asset Explorer and choose "Add New Custom Type" context menu item.
The dialog form with a list of types allowed for creation will be shown. By default, the form shows only types that have default parameterless constructors (even private). But you can allow showing all types to which [CommonComponentTypeAttribute] is applied, even if they don't have default constructors. To make this, need to set checkbox "Include types without default constructors". Note: objects of these types will be created unitialized, all fields will have default values (null for object types).
Here is an example of [CommonComponentTypeAttribute] usage:
[CommonComponentType(InitMethodName = "InitializePrototype")]
public partial class Explosion : SceneObject, IInstanced
{
        private void InitializePrototype()
        {
            ...
        }
        ...
}

Here is screenshot of the assets creation form:


You can type first symbols of type name to find it more quickly. Also there is possibly to change Name, which will be used as DisplayName for created instance.
Use [CommonComponentTypeAttribute(IsInherited=false)] in base type or apply [IgnoreCommonComponentTypeAttribute] to derived types to disable creation of instances of derived types.
By default, [CommonComponentTypeAttribute] is applied to SceneObject. And [IgnoreCommonComponentTypeAttribute] is applied to RootSceneNode type. So any derived from SceneObject type, which not derived from RootSceneNode, will be shown in assets creation form.

Adding Prototypes Into Asset Database From Existing Instances

  • Right-click on any existing asset in Asset Explorer and choose "Create Derived Copy". New instance of selected asset will be created. You can rename it and move to another group/category folder by drag-drop.
  • Right-click on any group/category folder where you want to create new instance then choose "Add Instance". Existing assets of type that allowed for creation in selected group will be shown in drop-down menus. Choose one of them, new instance of the asset will be created.

Adding Prototypes Into Asset Database From Base Types (Templates)


There is the group named "Base Types" at the top of Asset Explorer, which contains templates for creation new instances.
You can create your own templates from any asset in Asset Explorer just by drag-drop the asset into Base Types group. Then you can rename it and move to another category folder. Also you can specify custom parameters for template using Object Editor.
For using templates, right-click on any group/category folder where you want to create new instance then choose "Add New". Base Type assets of type that allowed for creation in selected group will be shown in drop-down menus. Choose one of them, new instance of the asset will be created:

Also you can use
button in the toolbar at the top of Asset Explorer. In this case, context menu with all Base Type items will be shown. You can choose any of them, then asset instance will be created in the predefined group.

Adding Assets Into Asset Database From Scene or World Explorer

You can drag-drop asset from World Explorer into Asset Explorer. In this case, new instance of dropped asset will be created and added to Asset Database (and thus, shown in Asset Explorer).
Also, you can right-click on asset in World Explorer or in Scene View (in Design mode) and choose "Add To Asset Database"-> then select group/category folder where you want to add this asset. This feature may be unavailable if no groups that can contain asset of this type are present.


Adding Assets Into Asset Database From External Files (Importing)

1. Importing Database Assets

When you excluded some assets from Asset Explorer (by right-click -> Exclude) or if you have some serialized files for assets but don't have them in manifest for some reason, you might want to restore excluded assets then.
To make this, select any group/category folder, where you want to put missing assets and choose "Importing Database Assets".
In shown dialog form, choose assets you want to import, setting checkboxes opposite choosen assets, then press "Import" button.

2. Importing Media Assets

You can import your textures or models into Asset Database. Usually this is needed when you have some media assets in your folders that are situated outside of V3D path (for eg, in "My Documents").
To import them into V3D, you can:
  • Right-click on group/category folder that allows importing of assets of these types: Media/Models, Media/Textures or Media/GDAL Textures.
  • Choose "Import Media Assets" context menu item.
  • In shown dialog, find and select textures/models that you want to import.
  • Press "Open" button.
Imported assets will be copied into "#Source Assets" (GDAL Textures) or "#Assets" (everything else) folder under your project path. If asset with the same name already exists in target folder, imported asset will be renamed by adding suffix " #1", " #2" etc at the end of the name (for eg, "My Texture #5.jpg").
Also, you can import your media assets just from Asset Selector. For making this, press "Import" button (if available).

Design-Time Support

Click support

If [ClickSupportAttribute] is applied to a type, then specified method in instance of this type will be invoked, after clicking on this asset in Asset Explorer. You can specify what method will be invoked and when: after single or double click:
[ClickSupport(ActionMethod = "HandleClick", HandleDoubleClick = true)]
public class DoubleClickableAsset
{
        private bool HandleClick()
        {
            ...
            return true;
        }
}
Note that specified method should return bool value, indicates whether click was handled inside method.

Adding Actions to (Right Click) Context Menu for Editor and/or In-Game Use

You can easily extend the toolset functionality with your own methods defined in your entity and component C# classes, by showing them as actions in the right-click context menu shown in the Visual3D All-in-One Development Tool (toolset) when right clicking on an entity or component of your class type (in World Explorer, Object Editor, Asset Explorer, Scene Editor viewport, etc.) as well as have buttons shown in Object Editor when the component or entity is selected in the toolset. 

Similarly, you can have these methods exposed in the in-game (right click) context menus in addition to or instead of showing them in the editor context menus. can be shown by right clicking on any entity which has one or more context menu actions defined for them.  

Also, you can define buttons to be shown in Object Editor for methods you define in your entity and component C# classes.
Checkout our Buttons and Context Menu Actions for Extending Editors and Gameplay documentation for details.


Components Extensibility

You can make some of assets expandable in Asset Explorer/World Explorer. After expanding, some specified properties will be shown.
To make this, you should
  • Make type implementing System.ComponentModel.IComponent interface.
  • Apply [Designer(typeof(ComponentModel.EntityComponentDesigner))] or with EntityComponentDesigner-derived designer specified.
  • Also you can apply [ComponentAttribute] to properties that will be shown as virtual components when asset expanded.
  • Or you can apply [ComponentListAttribute] to properties of IEnumerable-implementing types to show all it's items as components.
[Designer(typeof(ComponentModel.EntityComponentDesigner))]
public class Environment : EntityComponent, IAttachable<Scene>, Architect.IConvertibleResolver
{
        ...
        [Component("Terrain")]
        public TerrainSettings Terrain { get;set; }

        [Component("Water")]
        public WaterSettings Water { get;set; }
}

[Designer(typeof(ComponentModel.EntityComponentDesigner))]
public class TerrainSettings : Asset
{
        ...
        [ComponentList("SplattingLayer")]
        public SplattingLayerSettings[] SplattingLayers { get;set; }
}

Defining Asset Selector For a Type

You can define that property's value of specified type can be changed by using Asset Selector.
To make this, you should apply [AssetSelectorAttribute] to the type:
[AssetSelector(AssetTypeName = "LandCover", FilterType = typeof(LandCover))]
public partial class LandCover
{
}

If property is of type AssetRef<T> and type T have AssetSelectorAttribute applied, the same Asset Selector will be used for property of AssetRef<T> via converter.



updated by  Roman V. Gudinov