Your browser does not seem to support JavaScript. As a result, your viewing experience will be diminished, and you have been placed in read-only mode.
Please download a browser that supports JavaScript, or enable it if it's disabled (i.e. NoScript).
@m_adam Hi, this partially works for me.
When I manually run your script for the assets we have already created, the preview images are set and displayed after a restart of Cinema 4D.
However if I insert the code directly into our script, then sometimes one object has the preview image correctly set and displayed after a restart. But most of the time it is the preview image created by Cinema 4D itself, which is almost completely black.
To clarify what we do: Within a batch process (a script executed within Cinema 4D) we create multiple textured objects and add each of those to a newly created database. The object to be added to the Database have Redshift materials.
So if we create for example 9 object within that batch process then all 9 object most of the time have a black image, sometimes one of those has the correct image.
The database is create this way:
bases = maxon.BaseArray(maxon.AssetRepositoryRef) assetDb = AssetRepositoryTypes.AssetDatabase assetDb = maxon.RegistryInterface.FindEntryValue(assetDb._registry, assetDb._ids) repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, assetDb, bases, maxon.Url(self.databasePath), True, False, False, None) if not repository: raise RuntimeError("Repository construction failed.")
In a loop we iterate objects and create an Asset from that object, at that part I added the above code.
for objectVaraint in variants: assetMetadata = maxon.AssetMetaData() assetCategoryId = maxon.Id(self.categoryId) # A StoreAssetStruct is a helper data structure for storing assets which bundles up an asset # category and storage and lookup repository for the operation. storeAssetStruct = maxon.StoreAssetStruct(assetCategoryId, self.repository, self.repository) # Use the convenience method AssetCreationInterface.CreateObjectAsset() to create and store an # object asset in one operation. The instantiation of a FileAsset for the object is hidden # away, and instead we deal directly with the AssetDescription which is representing the object # file asset. assetDescription = maxon.AssetCreationInterface.CreateObjectAsset( objectVaraint, doc, storeAssetStruct, assetId, assetName, assetVersion, assetMetadata, True) metadata = assetDescription.GetMetaData() imagepUrl = maxon.Url("C:/Users/Till Niese/picture.png") # Stores the Url Meta Data, if there was no preview stored previously (aka the default png file for the given category) then it will be refreshed right away # Otherwise Cinema 4D need to be restarted maxon.AssetDescriptionInterface.StoreUrlMetaData = AssetDescriptionInterface.StoreUrlMetaData assetDescription.StoreUrlMetaData(maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL, imagepUrl, maxon.AssetMetaData.KIND.PERSISTENT) # Turn off automatic preview metaProperties = metadata.Get(maxon.ASSETMETADATA.MetaProperties) metaProperties.Set(maxon.ASSET.METAPROPERTIES.BASE.AUTOMATICPREVIEW, False) assetDescription.StoreMetaData(maxon.ASSETMETADATA.MetaProperties, metaProperties, maxon.AssetMetaData.KIND.PERSISTENT)
Right after the imports I have added this:
# --- AssetDescriptionInterface monkey patching fix for AssetDescriptionInterface.StoreUrlMetaData() -------------------------- @maxon.interface.MAXON_INTERFACE(maxon.consts.MAXON_REFERENCE_COPY_ON_WRITE, "net.maxon.interface.assetdescription") class AssetDescriptionInterface(maxon.AssetBaseInterface): @maxon.interface.MAXON_FUNCTION_EXTEND("net.maxon.interface.assetdescription.StoreUrlMetaData") def StoreUrlMetaData(self, metaId, source, kind): return self.GetRepository().StoreUrlMetaData(self, metaId, source, kind) maxon.AssetDescriptionInterface.StoreUrlMetaData = AssetDescriptionInterface.StoreUrlMetaData @maxon.MAXON_REGISTRY("net.maxon.registry.assetrepositorytypes") class AssetRepositoryTypes(maxon.Registry): AssetDatabase = maxon.MAXON_DECLARATION("net.maxon.assets.repositorytype.database")
Kind regards, Till
Hi. In the UI of the Asset Browser there is the option to set a preview image using "Update Preview From File ...", and I would like to do the same thing this command does in Python without showing an File Chooser Dialog but by using a known path.
I know that I can get an existing preview image using this code:
metadata = assetDescription.GetMetaData() previewUrl = metadata.Get(maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL)
And setting other meta data of the asset works fine, but I don't know how to properly set/create/update the preview image.
I tried to set it with:
assetDescription.StoreMetaData(maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL, maxon.Url("file:///C:/path/to/image.png"), maxon.AssetMetaData.KIND.PERSISTENT)
or
assetDescription.StoreUrlMetaData(maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL, maxon.Url("file:///C:/path/to/image.png"), maxon.AssetMetaData.KIND.PERSISTENT)
I also tried to disable the automatic preview before setting ASSET_PREVIEWIMAGEURL using:
ASSET_PREVIEWIMAGEURL
metaProperties = metadata.Get(maxon.ASSETMETADATA.MetaProperties) metaProperties.Set(maxon.ASSET.METAPROPERTIES.BASE.AUTOMATICPREVIEW, False)
The only example code related to the ASSET_PREVIEWIMAGEURL that I was able to find was in the examples_dots.cpp so I tried to use that code and instead of maxon::Url newPreviewUrl = presetAsset.GeneratePreview(previewWidth, dummy, 0) iferr_return; I used newPreviewUrl = maxon.Url("file:///C:/path/to/image.png") in python, but that didn't work either.
examples_dots.cpp
maxon::Url newPreviewUrl = presetAsset.GeneratePreview(previewWidth, dummy, 0) iferr_return;
newPreviewUrl = maxon.Url("file:///C:/path/to/image.png")
I think assetDescription.StoreUrlMetaData and maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL is right, but I guess maxon.Url must be something different than a file:/// URL?
assetDescription.StoreUrlMetaData
maxon.ASSETMETADATA.ASSET_PREVIEWIMAGEURL
maxon.Url
file:///
Hi @m_adam, this works perfectly. Thank you for your fast response.
I just figured out that the API changed in 2023 from:
static MAXON_METHOD Result<UpdatableAssetRepositoryRef> CreateRepositoryFromUrl(const Id& rid, const Block<const AssetRepositoryRef>& bases, const Url& url, Bool writable, Bool derivedInPrefs, Bool tempRepository);
to:
static MAXON_METHOD Result<UpdatableAssetRepositoryRef> CreateRepositoryFromUrl(const Id& rid, const Class<AssetRepositoryRef>& repoType, const Block<const AssetRepositoryRef>& bases, const Url& url, Bool writable, Bool derivedInPrefs, Bool tempRepository, const BackgroundEntryTuple& backgroundJob = {});
In the c++ section of GitHub - PluginCafe there is an updated version:
maxon::UpdatableAssetRepositoryRef repository = maxon::AssetInterface::CreateRepositoryFromUrl( uuid, maxon::AssetRepositoryTypes::AssetDatabase(), bases, database._dbUrl, true, true, true) iferr_return;
I however don't know how to translate that to Python.
In particular, what should I use for maxon::AssetRepositoryTypes::AssetDatabase(), and BackgroundEntryTuple?
maxon::AssetRepositoryTypes::AssetDatabase()
BackgroundEntryTuple
Hi. I have some troubles with the example code on GitHub - PluginCafe that shows how to create a Repository using Python.
The code itself runs fine in R26 (R26.012 and R26.110), but results in an error in 2023.1.3.
Did something change from R26 to 2023 how a repository has to be created using CreateRepositoryFromUrl or is that a problem with the python API? If it is a problem with the API is there a way around that?
CreateRepositoryFromUrl
The code:
def CreateRepositories(): """Creates repositories for all user databases. Doing this is usually not necessary for performing light- to medium-sized asset operations, and the user preferences repository can then be used instead. Only when there is a substantial amount of assets that must be processed, a repository should be constructed to limit the search space for search operations. The method CreateRepositoryFromUrl() used in this example can also be used to create a repository and its underlying database from scratch when the provided URL points to location where no database has been established yet. """ # Wait for all asset databases to be loaded, abort when this is not possible. if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading(): return RuntimeError("Could not load asset databases.") # Get the default language of Cinema 4D (en-US) for retrieving the repository names. defaultLanguage = maxon.Resource.GetDefaultLanguage() # Iterate over all currently mounted databases and create an asset repository for each of them. # Doing this is usually not necessary as user asset databases are automatically part of the # the user preferences repository which is easier to retrieve. Creating a repository for a # specific user asset database can be useful to speed up asset searches. for database in maxon.AssetDataBasesInterface.GetDatabases(): # Create a unique identifier for the repository. rid = maxon.AssetInterface.MakeUuid(str(database._dbUrl), True) # Repositories can be composed out of other repositories which are called bases. In this # case no bases are used to construct the repository. But with bases a repository for all # user databases could be constructed for example. bases = maxon.BaseArray(maxon.AssetRepositoryRef) # Create a writable and persistent repository for the database URL. If #_dbUrl would point # to a location where no database has been yet stored, the necessary data would be created. repository = maxon.AssetInterface.CreateRepositoryFromUrl( rid, bases, database._dbUrl, True, False, False) if not repository: raise RuntimeError("Repository construction failed.") # Access some properties of the newly created repository. repoId = repository.GetId() isWriteable = repository.IsWritable() name = repository.GetRepositoryName(defaultLanguage) print(f"{repository} ({name}): id - {repoId}, writeable: {isWriteable}")
Error message I receive in C4D 2023:
Traceback (most recent call last): File "scriptmanager", line 558, in <module> File "scriptmanager", line 234, in CreateRepositories File "C:\Program Files\Maxon Cinema 4D 2023\resource\modules\python\libs\python39\maxon\decorators.py", line 383, in Auto ExecStaticMethod(*args) TypeError: unable to convert builtins.NativePyData to @net.maxon.interface.class-cR
Hello @ferdinand thank you for your response.
You have also tagged your posting as maxon API although the types you are mentioning, HyperFile and FileName, are both classic API. What are the maxon API types you are using?
maxon API
Yes, it should have been classic api and not maxon api.
classic api
maxon api
Your question is a bit hard to answer in this form. I assume you are in some kind of NodeData plugin and must serialize and deserialize some data manually with NodeData::Read and NodeData::Write. In this context you are encountering the described behavior of Windows 'changing' the path delimiters written on MacOS.
I guess that answers my question.
But just in case, here is a simplified version of the code that hopefully illustrates better what is done:
class MyData: public iCustomDataType<MyData> { Filename _assetPath; public: Bool Read(HyperFile* hf, Int32 level) { hf->ReadFilename(&_assetPath); // inpsecting _assetPath and _assetPath.GetFile() return TRUE; } Bool Write(HyperFile* hf, Int32 level) { // inpsecting _assetPath hf->WriteFilename(_assetPath); return TRUE; } }; class MyDataDataType : public CustomDataTypeClass { INSTANCEOF(MyDataDataType , CustomDataTypeClass) public: virtual Bool WriteData(const CustomDataType* d, HyperFile* hf) { return static_cast<const MyData*>(d)->Write(hf); } virtual Bool ReadData(CustomDataType* d, HyperFile* hf, Int32 level) { return static_cast<MyData*>(d)->Read(hf, level); } };
So yes WriteFilename and ReadFilename are called for the serialization/deserialize process.
WriteFilename
ReadFilename
For completeness a slightly rephrased description of the observation from the initial question:
Project created on windows:
WriteData
_assetPath
C:\Program Files\MyAssets\somefile.dat
Write
Read
_assetPath.GetFile()
somefile.dat
Project created on macOS:
/Library/Application Support/MyAssets/somefile.dat
\Library\Application Support\MyAssets\somefile.dat
But based on your answer I think this behavior is expected.
@m_magalhaes Thank you for your response.
So on the client-side, I always have to assume that I might get absolute paths that refer to the files on the server (MSG_MULTI_CLEARSUGGESTEDFOLDER is not called and MSG_RENAMETEXTURES is only called in case of name conflicts)?
MSG_MULTI_CLEARSUGGESTEDFOLDER
MSG_RENAMETEXTURES
If I'm confident that a file that is found on the client-side at such an absolute path matches the one on the server I could use it, but if I want to avoid mismatches of the assets between client and server I should check if net rendering is active for the document and if so only use the file part ( Filename::GetFile) of the file to check if the Asset is in the project directory and if not request it using only that file part through NetRenderGetFileFromServer?
Filename::GetFile
NetRenderGetFileFromServer
So a possible implementation of the Asset lookup in the plugin code would look like this?
Filename filename = /* retrieved e.g. from HyperFile */ // Check if net rendering is active for document NetRenderDocumentContent * const context = doc->GetNetRenderDocumentContext(); if (context != nullptr) { // net rendering: check if file is missing in project directory if (!GeFExists( doc->GetDocumentPath() + filename.GetFile() ) { Filename fnNet; // request file from the server if (NetRenderGetFileFromServer(context->_service, filename.GetFile(), fnNet) { // use path returned by NetRenderGetFileFromServer filename = fnNet; } else { // handle error case } } else { // use the file in the project directory filename = doc->GetDocumentPath() + filename.GetFile(); } } else { // no net rendering: regular check if asset exists at path + error handling }
Hello,
I have some questions about the messages sent for TeamRendering. I already found these topics, but those don't fully answer my question:
When TeamRendering is used (Add to Render Queue... and TeamRender to Picture Viewer...) MSG_GETALLASSETS is emmited and the Resources are transferred to the render client(s). But MSG_MULTI_CLEARSUGGESTEDFOLDER does not seem to be emitted (For Save Project with Assets... it is). Due to that, the paths on the render client are absolute referring to the file on the PC initiating TeamRendering.
Add to Render Queue...
TeamRender to Picture Viewer...
MSG_GETALLASSETS
Save Project with Assets...
Should one expect that this message is not sent? And if so is that due to the possibility to request assets on-demand from the server for which the absolute path would be required? Is there a case for which MSG_MULTI_CLEARSUGGESTEDFOLDER would be sent whenTeamRendering is used?
We use HyperFile to store some absolute paths related to our plugin. We, therefore, write the Filename using WriteFilename and upon loading read it back using ReadFilename.
HyperFile
Filename
When exchanging the saved c4d files between windows and macOS I noticed that the path delimiters behave differently.
If the Filename that is written to the HyperFile under Windows is C:\Program Files\MyAssets\somefile.dat and the c4d file is then loaded on macOS the Filename read is C:\Program Files\MyAssets\somefile.dat (path delimiter did not change).
If the Filename that is written to the HyperFile under macOS is /Library/Application Support/MyAssets/somefile.dat and the c4d file is then loaded on windows the Filename read is \Library\Application Support\MyAssets\somefile.dat (path delimiter changed from /, \)
/
\
Why is the delimiter only changing when loading a file saved on macOS on windows and not the other way round? Is that an expected behavior? And can this be made consistent (that the delimiter is changed on both or on none)?