Asset Metadata

Read and write asset metadata to describe and organize assets.

Overview

Assets are represented as asset descriptions in asset repositories that contain the meta-information that describe an asset. An asset description exposes most importantly the Url, asset identifier, and version of an asset. The identifier and version of an asset identify it in an asset repository and the Url denotes the physical location of the primary data of the asset. The Url for an media-image asset points for example to the image file that is being wrapped by the asset. The file format of the file at the asset Url depends on the asset type. File assets for materials, objects and scenes will be stored in the Cinema 4D format and can be opened with Cinema 4D without any additional steps required. All file type assets of subtype media, wrapping images or videos, and plain file assets, wrapping arbitrary file types as for example a PDF or JSON file, will store their primary data in the wrapped file format. The file format of other asset types like preset assets or node template assets is not readable by third parties without the Asset API and hidden by the asset type implementations. When that primary data of an asset is stored in a local database, it will always be placed in a file named "asset".

An asset description also contains a dictionary like object exposing the name of an asset, an annotation, keywords are more descriptive metadata. The metadata entries that can be contained in that dictionary depends on the type of an asset. For example, only media-image assets carry metadata describing the resolution of their content and only object and scene assets carry metadata about the point count of their content. The possible metadata entries an asset can have are defined in dedicated namespaces and can be extended by third party developers.

Fig I: Most asset metadata is accessible with the Detail Area of the Asset Browser. The SDK Cube asset stores descriptive meta-information as its name, parent category or the assigned keywords, as well as meta-information of more administrative nature, as its asset database, file size, or asset dependencies, in its metadata. The categories an asset is placed in, shown as folders in the Asset Browser, are assets themselves and assets can store a reference to a single category asset as its parent category. There is therefore no explicitly stored asset category tree in a singular location which could be browsed and the assets categories shown in the Asset Browser and the tree they form depend on the asset databases which have been mounted.

Technical Overview

An AssetDescriptionInterface reference exposes the identifier, type identifier, version, Url, and repository of an asset with its methods GetId, GetTypeId, GetVersion, GetUrl, and GetRepository. It also provides read access to the metadata of an asset with its method GetMetaData, returning an AssetMetaDataInterface reference, a dictionary like object granting key-value pair read access to the metadata.

Metadata entries in a AssetMetaDataInterface reference must be read with AssetMetaDataInterface::Get and written with AssetDescriptionInterface::StoreMetaData of the AssetDescriptionInterface reference holding the asset metadata. An exception to that rule are metadata entries of type String which should be read with GetMetaString and written with StoreMetaString, as these methods simplify handling the containers that contain the localized String metadata entries. One of the entries in a metadata dictionary is stored under the key ASSETMETADATA::MetaProperties and returns a sub-container of metadata entries, containing primarily the asset type dependent meta information as for example media resolution or total point count.

The keys for metadata entries are exposed as attributes in the namespace maxon::ASSETMETADATA and the keys for the meta-properties are exposed in the namespace ASSET::METAPROPERTIES. The ASSET namespace also contains other asset related attributes but they are only rarely useful for SDK users. The interfaces KeywordAssetInterface and CategoryAssetInterface provide static convenience functions which can be used to add category and keyword metadata to asset descriptions.

Fig II: A simplified overview for the asset data access model. An asset description exposes all the relevant data and metadata of an asset. Fundamental information like the identifier, version or repository of an asset is implemented in dedicated interfaces which are shared by both asset descriptions and asset implementations. The location of the content of an asset, the asset Url, is attached to the AssetDescription. The vast majority of asset metadata can be found in the AssetMetaData attached to an AssetDescription.

Related Topics

Articles Asset API Provide content as reusable assets served with the Cinema 4D Asset Browser.
Entity Creation

Explains the managed memory environment of the maxon API with interfaces and references.

OBJECT::BASE Contains the attributes NAME and ANNOTATIONS which are also used as metadata keys for the name an annotation of an asset.
ASSETMETADATA Contains the declarations of asset metadata keys.
ASSET::METAPROPERTIES Contains the declarations of asset metaproperty keys.
AssetDescriptionInterface Represents an asset over its metadata in an asset repository.
AssetMetaDataInterface Contains the metadata of an asset.
AssetDependencyStruct Provides a data structure to express asset dependencies used in asset creation.
CategoryAssetInterface Represents an Asset Browser category as an asset.
KeywordAssetInterface

Represents an Asset Browser keyword as an asset.

SDK Plugins Asset API Examples

Showcases basic interactions with the Asset API to modify asset databases and their content.

Examples Access Asset Description Data Access important data attached to an asset description.
Add Asset Versions Add versions to an asset.
Search for Asset Categories by Name Search for a category asset by its name.
Search for Asset Categories by Path Search for a category asset by a path of parent categories.
Generate Asset Identifiers Generate identifiers for assets in a predictable manner.
Iterate over Asset Metadata Iterate over all metadata in an asset metadata dictionary.
Read Asset Metadata Read commonly used metadata from asset descriptions.
Write Asset Metadata Write commonly used metadata to asset descriptions.
Declare Custom Metadata Attributes Declare a custom metadata attribute.

Examples

The examples shown here are all related to the "Metadata" category in the Asset API Examples plugin. With the plugin you can run these examples to see their output. Each example shown here has a link bar at its bottom, linking to the code on GitHub, as well as to a calling context on GitHub. A calling context is a function which showcases how to gather the required inputs for that specific example and is targeted at users who want to know how to carry out a specific task with zero prerequisites. The context link is named "How to call this example ...".

Access Asset Description Data

Accesses the data attached to an asset description.

Highlights the meta information provided by an asset description for its asset that is not being accessed with the asset metadata container.

maxon::Result<void> AccessAssetDescriptionData(const maxon::AssetDescription& assetDescription)
{
ApplicationOutput("Asset Description Data for: @\n", assetDescription);
// The most important properties of an asset are the id, uniquely identifying the asset within its
// database, the type id, denoting the kind of asset it is, and the Url, pointing to the location
// of the primary data of the asset.
const maxon::Id assetId = assetDescription.GetId();
const maxon::Id assetTypeId = assetDescription.GetTypeId();
// This is the raw asset Url, it should NOT BE USED unless one wants to reference the asset
// location in the physical database; the Url will be in the ramdisk scheme, e.g.,
// "ramdisk://A9C0F37BAE5146F2/file_3b194acc5a745a2c/1/asset.jpg".
const maxon::Url rawAssetUrl = assetDescription.GetUrl() iferr_return;
// This is the asset Url which should be used when referencing an asset location when linking it.
// It will be in the asset Url scheme, e.g.,
// "asset:///file_37cd8c8dadea1a6a~.c4d?name=SDK Cube&db=sdkdatabase.db".
// The query parameters behind the question mark are optional and only used by Cinema 4D
// when the asset URL does not resolve to show user friendly error messages. When referencing
// assets, the old scheme without the parameters is still supported (without the nicer error
// messages then):
// "asset:///file_37cd8c8dadea1a6a~.c4d"
const maxon::Url assetUrl = maxon::AssetInterface::GetAssetUrl(assetDescription, true) iferr_return;
// This is the human readable form of the asset Url. It cannot be used to reference the asset,
// but it can be used to display that asset as a resource name in a GUI. The name will be in the
// assetdb scheme, e.g., "assetdb:///tex/Surfaces/Dirt Scratches & Smudges/RustPaint0291_M.jpg".
// All three of the asset URLs mentioned here as examples reference the "RustPaint0291_M.jpg"
// asset, but it is only #assetUrl which can be used when linking to that asset with a maxon::Url.
const maxon::String humanReadbleAssetUrlString = assetUrl.ConvertToUiNameWithRepository(
maxon::CONVERTTOUINAMEFLAGS::NONE, assetDescription.GetRepository()) iferr_return;
ApplicationOutput("\tAsset Id: @", assetId);
ApplicationOutput("\tAsset Type Id: @", assetTypeId);
ApplicationOutput("\tRaw Asset Url: @", rawAssetUrl);
ApplicationOutput("\tAsset Url: @", assetUrl);
ApplicationOutput("\tHuman Readble Asset Url String: @", humanReadbleAssetUrlString);
// Assets can have multiple versions which all then share intentionally the same id. The version
// of an asset description is also stored in its metadata, but can more easily be accessed with
// GetVersion() and GetVersionAndId. With the later returning an identifer that allows to
// distinguish multiple versions of an asset.
const maxon::Id assetVersion = assetDescription.GetVersion();
const maxon::IdAndVersion assetIdVersion = assetDescription.GetIdAndVersion();
ApplicationOutput("\tAsset Version: @", assetVersion);
ApplicationOutput("\tAsset IdAndVersion: @", assetIdVersion);
// Important properties of an asset are also its repository and metadata container. The latter
// contains most of the descriptive and administrative metadata associated with an asset.
const maxon::AssetRepositoryRef assetRepository = assetDescription.GetRepository();
const maxon::Id assetRepositoryId = assetDescription.GetRepositoryId();
const maxon::AssetMetaData assetMetadata = assetDescription.GetMetaData();
ApplicationOutput("\tAsset Repository Id: @", assetRepositoryId);
ApplicationOutput("\tAsset Metadata: @", assetMetadata);
// Also the reference to the AssetInterface for an asset description can be be loaded. It provides
// access to more data (which overlaps with the data exposed in the asset description) and gives
// access to the asset implementation. The UpdatePreviewThumbnail() example for the dots preset
// asset type implementation provides a usage scenario for both accessing the base and type
// specific asset interface to call the asset implementation. In non-implementation usage both
// the base and type specific asset interface of an asset have to be accessed only rarely.
maxon::Asset asset = assetDescription.Load() iferr_return;
ApplicationOutput("\tLoaded asset interface: @", asset);
const maxon::Id alsoAssetTypeId = asset.GetTypeId();
ApplicationOutput("\tAsset Type: @", alsoAssetTypeId);
// When necessary, the asset interface can be cast to its type specific asset interface.
if (assetTypeId == (maxon::AssetTypes::File().GetId()))
{
maxon::FileAsset fileAsset = maxon::Cast<maxon::FileAsset>(asset);
if (!fileAsset)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not cast asset to file asset."_s);
ApplicationOutput("\tCast asset base interface to file asset interface: @", fileAsset);
}
return maxon::OK;
}
int base
Definition: abstract.h:611
NONE
Definition: asset_command.h:0
Definition: lib_net.h:547
Definition: lib_net.h:560
Definition: c4d_string.h:39
static MAXON_METHOD Result< Url > GetAssetUrl(const AssetDescription &asset, Bool isLatest)
Definition: apibaseid.h:253
Definition: url.h:942
CONVERTTOUINAMEFLAGS
See ConvertToUiName() and ConvertToUiNameWithRepository()
Definition: url.h:67
#define ApplicationOutput(formatString,...)
Definition: debugdiagnostics.h:210
The maxon namespace contains all declarations of the MAXON API.
Definition: autoweight.h:14
Tuple< Id, Id > IdAndVersion
IdAndVersion is used for assets to store the asset id (first) and version (second) in a single tuple.
Definition: idandversion.h:13
#define iferr_scope
Definition: resultbase.h:1374
#define iferr_return
Definition: resultbase.h:1465
const char const char const char * file
Definition: object.h:439

Add Asset Versions

Adds a version to a file asset of subtype object.

A version of an asset is an asset that is stored under the same identifier as another asset but a different version. Such assets will be displayed as a singular asset in the Asset Browser and the different versions of the asset are only accessible in the 'Detail Area' of the Asset Browser. When that asset is loaded, and not specified differently, always the most recent version of the asset is loaded. Different versions of an asset must match in type but can otherwise be entirely different. The example adds an asset version to object assets that only contains a parametric sphere, overwriting all other content in the most recent version of the target asset.

const maxon::AssetDescription& asset)
{
// This example must run on the main thread due to it modifying the active document.
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Not on main thread."_s);
// Validate the asset to add a version to.
if (!asset)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Uninitialized asset description."_s);
if (asset.GetTypeId() != maxon::AssetTypes::File().GetId())
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Invalid asset type."_s);
const maxon::AssetRepositoryRef repository = asset.GetRepository();
if (!repository)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Asset without repository."_s);
if (!repository.IsWritable())
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION,
"The repository the asset is attached to is read-only."_s);
const maxon::AssetMetaData metadata = asset.GetMetaData();
const maxon::Id subtype = metadata.Get<
decltype(maxon::ASSETMETADATA::SubType)>().GetValueOrNull() iferr_return;
if (subtype != maxon::ASSETMETADATA::SubType_ENUM_Object)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Invalid asset sub-type."_s);
// Create a sphere object and insert it into the active document.
if (sphere == nullptr)
return maxon::OutOfMemoryError(MAXON_SOURCE_LOCATION, "Could not allocate sphere object."_s);
doc->InsertObject(sphere, nullptr, nullptr, false);
// Get the asset id of the passed asset, the new version will be stored under the same Id but
// a different version identifier.
const maxon::Id assetId = asset.GetId();
// Get the category the asset is parented to.
const maxon::Id categoryId = metadata.Get<
decltype(maxon::ASSETMETADATA::Category)>().GetValueOrNull() iferr_return;
// The asset version, it could also be a string like "v1", but it is safer to use UUIDs.
const maxon::StoreAssetStruct storeAsset{ categoryId, repository, repository };
const maxon::AssetMetaData newMetadata;
// Store an asset for the sphere object under the same id but a different version.
const maxon::AssetDescription assetVersion = maxon::AssetCreationInterface::CreateObjectAsset(
sphere, doc, storeAsset, assetId, "Sphere"_s, version.ToString(), newMetadata, true)
if (!assetVersion)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not write asset version."_s);
ApplicationOutput("Original asset: @", asset);
ApplicationOutput("Added asset version: @", assetVersion);
return std::move(assetVersion);
}
BaseDocument * GetActiveDocument(void)
Bool GeIsMainThread()
Definition: c4d_basedocument.h:498
Definition: c4d_baseobject.h:225
static BaseObject * Alloc(Int32 type)
static MAXON_METHOD Result< AssetDescription > CreateObjectAsset(BaseObject *op, BaseDocument *activeDoc, const StoreAssetStruct &storeAssetStruct, const Id &assetId, const String &assetName, const String &assetVersion, const AssetMetaData &copyMetaData, Bool addAssetsIfNotInThisRepository)
static MAXON_METHOD Result< Id > MakeUuid(const Char *prefix, Bool compact)
String ToString(const FormatStatement *formatStatement=nullptr) const
const Id & Get() const
Definition: apibaseid.h:185
#define Osphere
Sphere.
Definition: ge_prepass.h:1096
#define MAXON_SOURCE_LOCATION
Definition: memoryallocationbase.h:67
const char * doc
Definition: pyerrors.h:226

Search for Asset Categories by Name

Finds a all category assets with a given name and category.

Category names are not unique as there can be two assets named 'B' within the asset category 'A'. This example returns all categories that have a given name and optionally a specific parent category. Passing in the empty id for the parent category will match categories at the root level.

This approach of retrieving asset categories is error prone, since there can be multiple AssetCategoryInterface instances for a single location which live in different repositories. It should only be used when a category identifier can neither be predicted nor hard-coded by looking it up in the Asset Browser. See Related Topics - Development Tools for details on retrieving asset identifiers in the Asset Browser. See the Generate Asset Identifiers example for details on predictable asset identifier generation.

maxon::Result<void> FindCategoryAssetsByName(
const maxon::AssetRepositoryRef& repository, maxon::BaseArray<maxon::AssetDescription>& results,
const maxon::String& serachName, const maxon::Id& category, const maxon::Bool& testCategory)
{
// It makes no sense to search for asset categories with the empty string as their name as such
// categories should not exist.
if (serachName.IsEmpty())
return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION, "Invalid category name."_s);
// Get the default language of Cinema 4D (en-US). It is used here since the Asset API examples
// are targeting an English speaking audience, with example inputs being in English, while
// their Cinema 4D installations are not necessarily running in en-Us. Retrieving the
// current language would then cause the example to fail.
const maxon::LanguageRef defaultLanguage = maxon::Resource::GetDefaultLanguage();
// Attempt to find a category asset with the given name and parent category.
repository.FindAssets(maxon::AssetTypes::Category(), maxon::Id(), maxon::Id(),
[&results, &serachName, &category, &testCategory, &defaultLanguage]
(maxon::AssetDescription asset) -> maxon::Result<maxon::Bool>
{
// Continue searching on name mismatch.
maxon::String assetName = asset.GetMetaString(
maxon::OBJECT::BASE::NAME, defaultLanguage) iferr_return;
if (assetName.Compare(serachName) != maxon::COMPARERESULT::EQUAL)
return true;
// Continue searching on parent category mismatch.
if (testCategory)
{
maxon::Id assetCategory = asset.GetMetaData().Get(
maxon::ASSETMETADATA::Category, maxon::Id()) iferr_return;
if (assetCategory != category)
return true;
}
// Continue searching when the asset is already contained in the results (this is required
// for the example FindCategoryAssetsByPath() shown below).
if (results.Contains(asset))
return true;
// New matching category found.
results.Append(asset) iferr_return;
return true;
// Report the results.
maxon::Int count = results.GetCount();
if (count == 0)
{
"Could not find any category assets with the name '@' and parent '@'.", serachName, category);
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, msg);
}
"Found @ category assets with the '@' name '@' and the parent category '@'."_s :
"Found @ category asset with the '@' name '@' and the parent category '@'."_s;
ApplicationOutput(msg, count, serachName, category);
return maxon::OK;
}
Py_ssize_t count
Definition: abstract.h:640
Definition: basearray.h:412
MAXON_ATTRIBUTE_FORCE_INLINE ResultRef< T > Append(ARG &&x)
Definition: basearray.h:677
MAXON_ATTRIBUTE_FORCE_INLINE Int GetCount() const
Definition: basearray.h:573
MAXON_ATTRIBUTE_FORCE_INLINE Bool Contains(typename ByValueParam< VALUETYPE >::type v) const
Definition: collection.h:555
static MAXON_METHOD LanguageRef GetDefaultLanguage()
Definition: string.h:1235
Bool IsEmpty() const
Definition: string.h:1440
Int64 Int
signed 32/64 bit int, size depends on the platform
Definition: apibase.h:188
bool Bool
boolean type, possible values are only false/true, 8 bit
Definition: apibase.h:181
return OK
Definition: apibase.h:2667
@ EQUAL
result is equal
#define FormatString(...)
Definition: string.h:2100
const Class< R > & Get(const Id &cls)
Definition: objectbase.h:2073
@ LATEST
Set this flag to obtain only the latest version of the asset.
const char const char * msg
Definition: object.h:438

Search for Asset Categories by Path

Finds all category assets that match a given category path.

This approach of retrieving asset categories is error prone, since there can be multiple AssetCategoryInterface instances for a single location which live in different repositories. It should only be used when a category identifier can neither be predicted nor hard-coded by looking it up in the Asset Browser. Categories within the path must be separated by forward slashes. The category path "A/B/C"_s will return all categories named C with the ancestors named A and B, in the order A->B->C. See Related Topics - Development Tools for details on retrieving asset identifiers in the Asset Browser. See the Generate Asset Identifiers example for details on predictable asset identifier generation to 'guess' the identifier of a category asset which has been generated in such predictable manner.

maxon::Result<void> FindCategoryAssetsByPath(
const maxon::AssetRepositoryRef& repository, maxon::BaseArray<maxon::AssetDescription>& results,
const maxon::String& categoryPath, const maxon::Bool& relativePath)
{
// Searching for assets by their name comes with the problem that there are likely name collisions
// in a given repository, e.g., that there is more than one asset called "MyAsset". This holds
// especially true when one is searching for category assets. This can be mitigated by searching
// for a category path, e.g., 'tex/Surfaces/Wood', which will cover most cases of getting a
// specific category asset. There can however be technically two (or more) paths
// 'tex/Surfaces/Wood' when 'tex' has two children 'Surfaces', each with a child called 'Wood'.
// This is why this example returns a BaseArray and not a single asset description. To avoid this
// problem, asset identifiers must either be known beforehand (hardcoded) or generated in a
// deterministic way, so that one can predict the id of the asset one is looking for. See
// GenerateAssetIdentifiers() in this file for an example on such asset id generation.
// Split the category path by the / character.
if (categoryPath.IsEmpty())
return maxon::IllegalArgumentError(MAXON_SOURCE_LOCATION, "Invalid category path."_s);
categoryPath.Split("/"_s, true, pathTokens) iferr_return;
// Iterate over all tokens in the path and attempt to find a category asset with that name and
// a parent category with a name equal to the last token.
maxon::Bool isFirst = true;
for (maxon::String token : pathTokens)
{
// First token in the path.
if (isFirst)
{
FindCategoryAssetsByName(repository, results, token, maxon::Id(), !relativePath) iferr_return;
isFirst = false;
continue;
}
// All following tokens.
temp.CopyFrom(results) iferr_return;
results.Reset();
for (maxon::AssetDescription parent : temp)
{
FindCategoryAssetsByName(repository, results, token, parent.GetId(), true) iferr_return;
}
}
// Report on the found category assets that match the path.
const maxon::LanguageRef defaultLanguage = maxon::Resource::GetDefaultLanguage();
const maxon::String message = (
"Found category asset with the '@' label '@', matching the path '@', the id '@' and @"_s +
" attached assets."_s);
for (maxon::AssetDescription categoryAsset : results)
{
// Search for assets attached to the category asset.
repository.FindAssets(
[&categoryAsset, &count](maxon::AssetDescription child) -> maxon::Result<maxon::Bool>
{
// Since the empty Id is being passed for the asset type in FindAssets() above, retrieving
// the parent category of an asset can fail, as this search will also include the more
// abstract asset types which do not necessarily carry such metadata.
iferr (maxon::Id childCategory = child.GetMetaData().Get(
maxon::ASSETMETADATA::Category, maxon::Id()))
{
return true;
}
if (childCategory == categoryAsset.GetId())
count++;
return true;
}
maxon::String assetName = categoryAsset.GetMetaString(
maxon::OBJECT::BASE::NAME, defaultLanguage) iferr_return;
message, defaultLanguage, assetName, categoryPath, categoryAsset.GetId(), count);
}
return maxon::OK;
}
void Reset()
Deletes all elements (calls destructors and frees memory).
Definition: basearray.h:541
MAXON_ATTRIBUTE_FORCE_INLINE Result< void > CopyFrom(COLLECTION2 &&other, COLLECTION_RESIZE_FLAGS resizeFlags=COLLECTION_RESIZE_FLAGS::FIT_TO_SIZE)
Definition: collection.h:261
PyObject * token
Definition: context.h:69
const char * message
Definition: pyerrors.h:189
for(i=0;i< length;i++)
Definition: unicodeobject.h:61
int32_t Int32
32 bit signed integer datatype.
Definition: apibase.h:176
#define iferr(...)
Definition: errorbase.h:388
Definition: grammar.h:14

Generate Asset Identifiers

Demonstrates how to generate asset identifiers.

Asset identifiers must be unique within a repository without its bases. UUID-based identifiers generated by the operating system assure this quality but are also hard to reproduce at a later point of time or on another system. In some cases this quality of predictable asset identifiers is required to search for assets effectively or avoid ingesting duplicate assets. The example demonstrates with the cases of a category and image asset how such identifying asset hashes could be constructed with the maxon API in a sufficiently collision free manner.

maxon::Result<void> GenerateAssetIdentifiers()
{
// The common way to generate an asset identifier is AssetInterface::MakeUuid. Which wraps around
// Uuid::CreateId(), which in turn calls the OS specific Uuid generators. The prefix passed to
// this function is not formally defined, but it is good practice to pass in the lower-case name
// for the asset type. E.g., "category" for CategoryAsset identifiers, "file" for FileAsset
// identifiers and so on.
// Such id will be different every time it is being generated. When an asset with such an
// identifier is being searched for in a repository without its identifier being known
// beforehand, the search must be conducted over the properties of the asset. Which is slower
// and more labor-intensive then retrieving an asset description by its id.
ApplicationOutput("UUID identifier (different on each execution): @", systemId);
// The alternative is to hash the identifier for an asset manually. A category asset identifier
// could for example be hashed over its name and path; which will allow for predicting the id
// of any category asset created in this manner:
// Will be the same on each execution but will not allow multiple assets of the same name to be
// attached to the same parent category, as they will resolve to the same identifier. For
// such asset can be searched by 'predicting' its id over its known properties.
maxon::String assetName("Stone");
maxon::String categoryPath("MyStuff/Categories/");
categoryPath + assetName, maxon::StreamConversions::HashSHA256()) iferr_return;
maxon::Id categoryId;
categoryId.Init("category_"_s + hash.GetLeftPart(16)) iferr_return;
maxon::String msg ("Category identifier hashed over the category path: @");
ApplicationOutput(msg, categoryId);
// Note that the version of an asset usually does not have to be included in such special hashes,
// as there is the interface maxon::IdAndVersion for that purpose. A more complex example which
// does hash an (image) file asset over its origin file path, resolution and bit depth is shown
// below.
maxon::Url assetOriginPath("file://textures/stone/taking_things_for_granite.png"_s);
maxon::Int32 width = 4096; maxon::Int32 height = 4096;
maxon::Int32 bitDepth = 32;
maxon::String hashInput = FormatString("@.@.@.@", assetOriginPath, width, height, bitDepth);
hash = maxon::GetPasswordHash(hashInput, maxon::StreamConversions::HashSHA256()) iferr_return;
// The length of an id does not have to conform to the 16 character hash length as shown in the
// category asset example when a shortened hash is not sufficient.
maxon::Id imageFileId;
imageFileId.Init("file_"_s + hash) iferr_return;
"Media image asset identifier hashed over image properties: @", imageFileId);
// In special cases the asset id can also be a human readable id, as for example it is employed
// by the asset category "uncategorized".
maxon::Id uncategorizedId("net.maxon.assetcategory.uncategorized");
ApplicationOutput("Human readble identifier: @", uncategorizedId);
// When declaring such identifiers the inverted domain pattern common to all human readable
// maxon::Id instances should be applied, with the first element after the second level domain
// being "assetcategory", followed by the human readable identifier. Be careful not to introduce
// name collisions within your domain.
maxon::Id myId("com.mycompany.assetcategory.myhumanreableid");
ApplicationOutput("Reccommended pattern for human readable identifiers: @", myId);
return maxon::OK;
}
PyObject Py_hash_t hash
Definition: dictobject.h:35
UUID
C4DUuid.
Definition: ge_prepass.h:38
maxon::Int32 Int32
Definition: ge_sys_math.h:60
Result< String > GetPasswordHash(const String &password, const StreamConversionFactory &hashClass, const DataDictionary &settings=DataDictionary())
unsigned long Py_ssize_t width
Definition: pycore_traceback.h:88
Definition: object.h:105

Iterate over Asset Metadata

Iterates over all existing entries in an AssetMetadata instance.

maxon::Result<void> IterateAssetMetadata(const maxon::AssetDescription& assetDescription)
{
// Get the metadata.
const maxon::AssetMetaData metadata = assetDescription.GetMetaData();
// Access all existing entries and iterate over them.
maxon::BaseArray<MetaDataTuple> entries = metadata.GetExistingEntries() iferr_return;
ApplicationOutput("Metadata Entries for `@`:\n", assetDescription.GetId());
for (const MetaDataTuple& item : entries)
{
const maxon::InternedId key = item.first;
const maxon::Data value = metadata.Get(key) iferr_return;
ApplicationOutput("Key: @, Value: @, Kind: @", key, value, item.second);
}
return maxon::OK;
}
PyObject * value
Definition: abstract.h:715
PyObject * key
Definition: abstract.h:289
Definition: datatypebase.h:1199
Definition: datatypelib.h:27
Definition: tuple.h:611
PyObject PyObject * item
Definition: dictobject.h:42

Read Asset Metadata

Reads the metadata of an asset that are commonly required to be read.

maxon::Result<void> ReadAssetMetadata(const maxon::AssetDescription& assetDescription)
{
ApplicationOutput("Reading Asset Metadata for: @\n", assetDescription);
// Get the metadata from the asset description.
const maxon::AssetMetaData metadata = assetDescription.GetMetaData();
// Get the language Cinema 4D is currently running in.
const maxon::LanguageRef currentLanguage = maxon::Resource::GetCurrentLanguage();
// Get the name of the asset, i.e., the string that is representing the asset in the Asset
// Browser. The name could also retrieved be directly from the metadata, but for metadata of
// type string there is the convenience method GetMetaString() which simplifies handling
// localized strings.
const maxon::String assetName = assetDescription.GetMetaString(
maxon::OBJECT::BASE::NAME, currentLanguage, ""_s) iferr_return;
ApplicationOutput("\tName: @", assetName);
// Get the annotation of the asset shown in the info panel of the Asset Browser.
const maxon::String asssetAnnotation = assetDescription.GetMetaString(
maxon::OBJECT::BASE::ANNOTATIONS, currentLanguage, ""_s) iferr_return;
ApplicationOutput("\tAnnotation: @", asssetAnnotation);
// Get the "usage" of an asset which is a tuple of a time stamp and a usage counter, denoting
// how often the asset has been modified.
const maxon::ASSETMETADATA::AssetUsageType usage = metadata.Get(
maxon::ASSETMETADATA::Usage, maxon::ASSETMETADATA::AssetUsageType()) iferr_return;
ApplicationOutput("\tUsage-time-stamp: @, Usage-count: @", usage.first, usage.second);
// Get the asset dependencies of the asset.
const maxon::Array<maxon::AssetDependencyStruct> dependencyCollection = metadata.Get<
decltype(maxon::ASSETMETADATA::Dependencies)>().GetValueOrNull() iferr_return;
// The dependencies contain metadata about the assets that are used by the asset.
ApplicationOutput("\tAsset dependencies:");
for (maxon::AssetDependencyStruct dependency : dependencyCollection)
ApplicationOutput("\t\t@: @", dependency.originalName, dependency.assetIdAndVersion);
// Get the ids of the keyword assets associated with an asset. Keywords can be split over system
// defined and user defined keywords. This applies to assets stored in read-only repositories.
const maxon::Array<maxon::Id> keywords = metadata.Get<
decltype(maxon::ASSETMETADATA::Keywords)>().GetValueOrNull() iferr_return;
const maxon::Array<maxon::Id> userKeywords = metadata.Get<
decltype(maxon::ASSETMETADATA::UserKeywords)>().GetValueOrNull() iferr_return;
ApplicationOutput("\tKeywords: @", keywords);
ApplicationOutput("\tUser-Keywords: @", userKeywords);
// Get the id of the category the asset is parented to.
const maxon::Id category = metadata.Get<
decltype(maxon::ASSETMETADATA::Category)>().GetValueOrNull() iferr_return;
ApplicationOutput("\tCategory: @", category);
// When the name of an category or keyword is required, then the respective category or keyword
// asset(s) must be retrieved to read their metadata.
maxon::AssetRepositoryRef lookupRepo = maxon::AssetInterface::GetUserPrefsRepository();
if (!lookupRepo)
return maxon::UnexpectedError(MAXON_SOURCE_LOCATION, "Could not retrieve user repository."_s);
const maxon::AssetDescription categoryDescription = lookupRepo.FindLatestAsset(
maxon::AssetTypes::Category().GetId(), category, maxon::Id(),
const maxon::String categoryName = categoryDescription.GetMetaString(
maxon::OBJECT::BASE::NAME, currentLanguage, ""_s) iferr_return;
ApplicationOutput("\t\tCategory Name: @", categoryName);
// Get the sub type of an asset. This currently only applies to File assets. The subtype of
// a file asset expresses if it holds an object, material, scene, media image or media movie
// asset, or if it is a plain file asset, e.g., a PDF.
const maxon::Id subtype = metadata.Get<
decltype(maxon::ASSETMETADATA::SubType)>().GetValueOrNull() iferr_return;
ApplicationOutput("\tAsset Subtype: @", subtype);
// There is also a sub-container of metadata entries called the meta-properties of an asset. It
// contains more specific meta-information, as for example the total number of points for file
// assets which contain geometry.
maxon::DataDictionary metaProperties = metadata.Get(
maxon::ASSETMETADATA::MetaProperties, maxon::DataDictionary()) iferr_return;
// Get the point count from the asset MetaProperties.
const maxon::Int pointCount = metaProperties.Get(
maxon::ASSET::METAPROPERTIES::C4DFILE::POINTCOUNT, -1);
ApplicationOutput("\tPoint-count: @.", pointCount);
// Read the custom metadata attribute defined in the header file of the metadata examples.
maxon::Bool sdkTouched = metadata.Get(maxon::ASSETMETADATA::SDK_TOUCHED, false) iferr_return;
ApplicationOutput("\tCustom SDK metadata 'SDK_TOUCHED': @", sdkTouched);
return maxon::OK;
}
Definition: datadictionary.h:11
static MAXON_METHOD const UpdatableAssetRepositoryRef & GetUserPrefsRepository()
static MAXON_METHOD LanguageRef GetCurrentLanguage()
std::add_const< typename Pack::template At< I >::type >::type & Get() const
Returns the element at index I. Equivalent to TupleGet<I>(*this).
Definition: tuple.h:745
PyObject PyObject *const Py_ssize_t PyObject * keywords
Definition: abstract.h:55
Helper class to pass several arguments into asset creation functions.
Definition: assets.h:157

Write Asset Metadata

Writes the metadata of an asset that are commonly required to be written.

maxon::Result<void> WriteAssetMetadata(const maxon::AssetDescription& assetDescription)
{
ApplicationOutput("Writing Asset Metadata for: @", assetDescription);
// Other than reading asset metadata, writing asset metadata must be done from the
// AssetDescription of an asset.
// Get the current language, i.e., the language Cinema 4D is currently running in.
const maxon::LanguageRef currentLanguage = maxon::Resource::GetCurrentLanguage();
// Set the name of the asset. As for reading strings, for metadata entries of type string there
// is a specialized function for writing localized string entries which hides away the container
// holding the strings for all languages.
assetDescription.StoreMetaString(
maxon::OBJECT::BASE::NAME, "Hello World!"_s, currentLanguage) iferr_return;
// Set the annotation of the asset shown in the info panel of the Asset Browser.
assetDescription.StoreMetaString(
maxon::OBJECT::BASE::ANNOTATIONS, "C++ SDK Annotation"_s, currentLanguage) iferr_return;
// Some metadata properties as keywords and categories have dedicated convenience functions
// attached to their interfaces. Both could be written directly in the metadata, but it is
// usually easier to use the convenience functions.
// Set the category of the asset to the category "Asset API Examples" in the SDK-Database.
maxon::Id categegoryId ("category@a040014bbe784ba0ad315cbd22971f6b");
// Add the keyword "Abstract Shape" to the asset.
maxon::Id keywordId("keyword@05a41587b3094d08b77a5b6fbc61b4c6");
assetDescription, keywordId, true, assetDescription.GetRepository()) iferr_return;
// Write the custom metadata attribute defined in the header file into the the database in the
// user folder.
assetDescription.StoreMetaData(
maxon::ASSETMETADATA::SDK_TOUCHED, true, maxon::AssetMetaData::KIND::USER) iferr_return;
return maxon::OK;
}
USER
The meta data should be stored in user folder.
Definition: assets.h:5
static MAXON_METHOD Result< void > SetAssetCategory(const AssetDescription &asset, const Id &category)
static MAXON_METHOD Result< Bool > AddKeyword(const AssetDescription &target, const Id &keywordId, Bool userScope, const AssetRepositoryRef &updateUsageRepository)
OK
Ok.
Definition: ge_prepass.h:0
KIND
An enum to represent the different node space kinds (such as materials or scene nodes).
Definition: node_spaces.h:48

Declare Custom Metadata Attributes

Declares a custom metadata attribute which can be used in asset metadata dictionaries.

Formally, custom asset metadata can be written with any key object into a AssetMetaDataInterface instance, but the Asset API relies in some cases on metadata attributes properly being declared in the correct namespaces. Therefor any custom metadata attribute should be exposed in the namespace maxon::ASSETMETADATA and that attribute should only be referenced in metadata dictionaries with the key exposed in that namespace. Due to the involvement of the Source Processor in the declaration, metadata attributes must be declared in header files. See Write Asset Metadata for an example how the here declared attribute is written.

// Declares a custom metadata attribute that denotes if an asset has been "touched" by the SDK
// examples. The files generated by the source processor for this header file must be included
// for this to compile.
#include "examples_metadata1.hxx"
{
MAXON_ATTRIBUTE(maxon::Bool, SDK_TOUCHED, "net.maxonexample.asset.sdk_touched");
};
#include "examples_metadata2.hxx"
Definition: asset_command.h:35
MAXON_ATTRIBUTE(AssetCommandQueryStateDelegate, AssetCommandQueryState, "net.maxon.asset.command.querystate")