Maxon Developers Maxon Developers
    • Documentation
      • Cinema 4D Python API
      • Cinema 4D C++ API
      • Cineware API
      • ZBrush GoZ API
      • Code Examples on Github
    • Forum
    • Downloads
    • Support
      • Support Procedures
      • Registered Developer Program
      • Plugin IDs
      • Contact Us
    • Categories
      • Overview
      • News & Information
      • Cinema 4D SDK Support
      • Cineware SDK Support
      • ZBrush 4D SDK Support
      • Bugs
      • General Talk
    • Unread
    • Recent
    • Tags
    • Users
    • Register
    • Register
    • Login
    1. Maxon Developers Forum
    2. d_keith
    3. Topics
    • Profile
    • Following 0
    • Followers 0
    • Topics 7
    • Posts 16
    • Best 2
    • Controversial 0
    • Groups 0

    Topics created by d_keith

    • d_keithD

      Finding, Renaming, and Updating all References to Assets

      Cinema 4D SDK
      • python 2025 • • d_keith
      3
      0
      Votes
      3
      Posts
      92
      Views

      d_keithD

      Thanks for the response!

      I appreciate the ASSETDATA_FLAG_MULTIPLEUSE tip.

      My hope was that there was some sort of existing RenameAsset SDK method that would also update all references to that asset without implementing custom handling for various element types (Redshift Nodal Material, Cinema 4D Classic Material, Arnold Dome Light, XRef, etc). Evidently such a public python SDK method doesn't exist.

      My example code was texture specific as that's the part of the problem that's most pressing to me and I was able to implement with custom per-type handling - if I manage to get other types working, I'll attempt to update over time.

    • d_keithD

      Unable to Sign In to Old Forum to Generate Plugin ID

      Cinema 4D SDK
      • python • • d_keith
      3
      0
      Votes
      3
      Posts
      176
      Views

      d_keithD

      Thanks Ferdinand - Up and running again!

    • d_keithD

      Adding an Object to a New Cinema 4D Asset Database

      Cinema 4D SDK
      • python macos 2025 • • d_keith
      5
      0
      Votes
      5
      Posts
      751
      Views

      i_mazlovI

      Hi @d_keith,

      I would kindly ask you to check our Support Procedures, namely the "How To Ask Questions" paragraph:

      Singular Question: The initial posting of a support topic must contain a singular question. Do not ask ten things at once, that makes it extremely hard to answer topics. Break up your questions into multiple topics.

      Here you effectively have 4 different questions about Asset Browser, and these are candidates for 4 different threads on the forum. In your further postings please try to follow the aforementioned rules.

      Regarding your questions:

      Should I be able to mount a directory and have it auto-create a DB?

      Mounting database is effectively executing the AssetDataBasesInterface.SetDatabases. It has nothing to do with creating database neither semantically, nor is this mentioned in the docu. If you need to create repository, please use maxon.AssetInterface.CreateRepositoryFromUrl(), it will scan the directory for being already a database and create proper dir structure if it's not.

      Any idea why I need to re-try creating the Repo for it to work?

      If you face any errors in the script, please always attach at least the error message! In this case I assume you receive the following error, when executing the maxon.AssetRepositoryTypes.AssetDatabase() for the first time after Cinema 4D started. Looks like a bug to me, I've created a bug report for that (ITEM#585831).

      The error message:

      Traceback (most recent call last): File "console", line 1, in <module> File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\interface.py", line 5049, in __call__ self._cachedObj.R = _maxon_mapping.GetAssociatedDataType(dt) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Exception: unable to find datatype

      As a workaround you can just execute the following line first in your main():

      maxon.AssetRepositoryTypes.AssetDatabase() ... about multiple DBs ...

      You're giving your databases unique IDs with the line repo_id = maxon.AssetInterface.MakeUuid(str(url), True). If you need them to be the same, just pick one instead, e.g. repo_id = maxon.Id('net.maxon.sdk.cool.things.repo')

      ... revealing assets doesn't work...

      I've created a bug report for that, as it looks like a bug with refreshing in the Asset Browser. As a workaround you can reload databases and reopen the AB before reveling your assets, e.g.:

      def RepenAssetBrowser(): CID_ASSET_BROWSER = 1054225 # Close the Asset Browser if it's alredy opened if c4d.IsCommandChecked(CID_ASSET_BROWSER): c4d.CallCommand(CID_ASSET_BROWSER) # Open again c4d.CallCommand(CID_ASSET_BROWSER) def main(): # ... # assets = [] # ... # assets.append(asset) # ... maxon.AssetDataBasesInterface.ReloadAssetRepositories(True) RepenAssetBrowser() maxon.AssetManagerInterface.RevealAsset(assets)

      Cheers,
      Ilia

    • d_keithD

      Hiding Node Previews for all Redshift Nodes in Redshift Materials

      Cinema 4D SDK
      • python 2025 • • d_keith
      6
      0
      Votes
      6
      Posts
      770
      Views

      ferdinandF

      Hey,

      there is no NOT in the query syntax, if that is what you are asking for, although that could be a cool idea. What you would have to do, is apply two partial descriptions in one ApplyDescription call, one that first selects all nodes with an enabled preview and disables it, and then one which selects the end node and disables its preview. Or just carry out two successive ApplyDescription calls.

      Cheers,
      Ferdinand

      PS: You can of course do whatever you want with your module attributes, and things like __author__, __license__, __email__, etc are not formally standardized, but the quasi standard module attribute is called __author__, there is no __authors__. When you have multiple authors, you usually put them semicolon separated into the __author__ field. When you use __authors__, tools which support that quasi standard won't find your attribute and the whole purpose of these module attributes is being machine readable, as you could otherwise also just write things into the doc string of the module.

      See:
      The original Python Enhancement Proposal by Guido van Rossum (the now famous pep8) which introduced the concept of module dunder attributes and one of the many guides of how people interpret this, what this has evolved into.

    • d_keithD

      Traversing Asset Categories

      Cinema 4D SDK
      • python 2023 • • d_keith
      3
      0
      Votes
      3
      Posts
      557
      Views

      ferdinandF

      Hello @d_keith,

      Thank you for reaching out to us. So, I had a look, and I could not find a bug with ExpandAssetCategoryId. I tried built-in categories, custom categories, and custom categories inside a custom database. My traversal method does return everything what I would expect to be returned. Find a more elaborate variant of ExpandAssetCategoryId below (I turned it into a class for easier inspection of the data).

      There are a few things which could go wrong here:

      You misunderstood the purpose of ExpandAssetCategoryId, it expands a category tree in a top-down fashion. E.g., for A->B->C, ExpandAssetCategoryId(B) will yield B, C but not A. It could certainly be done differently but that would be up to you. There is a bug in your code in another place. There might exist special conditions in the environment you are working in, but that is nothing for the forum.

      Find below my example which will expand asset category trees from a root in a more visual and therefore easier to check manner.

      Cheers,
      Ferdinand

      The result:

      -------------------------------------------------------------------------------- AssetCategoryHandler at 0X7FD2F3350B90 (repo = database@U1pOb6rnHdar41NpWahfnn): Managed IDs: (maxon.Id('category@3d2621c1bc48485aa2b7ceead989e421'), maxon.Id('category_32a5e2eefcc10592'), maxon.Id('category_3e39b2c258ae9313'), maxon.Id('category_f7cb610b3f172a93'), maxon.Id('category_8f51f303e1bbf875'), maxon.Id('category_bb4c48de0af7b8b3'), maxon.Id('category_28801c2ab359e01e'), maxon.Id('category_fd68f940d0b615ab'), maxon.Id('category_993d6d73f3c2629e'), maxon.Id('category_fce5f1171ae19de7'), maxon.Id('category_50bbe4f91e99090b'), maxon.Id('category_074c78c3e2d70ab9'), maxon.Id('category_c92b8c257f9e7517'), maxon.Id('category@23efef776140438f80b9ca1b0eef53f7'), maxon.Id('category_a800e5a243f9f385'), maxon.Id('category_9676f914364e4e27'), maxon.Id('category_e024f65cd280795e'), maxon.Id('category_2ab08ca489795a07'), maxon.Id('category_c04b37955dcb3895'), maxon.Id('category_34ed541e2020f38e'), maxon.Id('category_f7316fed1fc11137'), maxon.Id('category_efb8eae1116900c2'), maxon.Id('category_c0e06630879697c2'), maxon.Id('category_f4afa4f900193b95'), maxon.Id('category_476ff23d55425ca4'), maxon.Id('category_58e6a054402a25fd'), maxon.Id('category_ddcdf72383424d7e'), maxon.Id('category_ca1600eb107c6082'), maxon.Id('category_1b0948c77cb2f61e'), maxon.Id('category@c19d76abc1bf46f296ed837fd9e8a215'), maxon.Id('category_f40806b20b84f759'), maxon.Id('category_8ea059e3d503175a'), maxon.Id('category_9147f39c273d8342'), maxon.Id('category@6608dc0e700442f196c788a53170a078'), maxon.Id('category@d8567d0c206145979ef94bf495128c00'), maxon.Id('category@1bc8d3a8b3764acfb82bf5409646fe9f'), maxon.Id('category@8b5f3e8dfbb2489caff67db176800a50'), maxon.Id('category@d1b7bff693344889aa2b98b2f77e80ac'), maxon.Id('category@edad962d94254a26a90a492fae589221'), maxon.Id('category@7cd25e2b9d5c408986cded4f33d59c47'), maxon.Id('category@ed2c2d5352cc461389526a4d9006febc'), maxon.Id('category@5daaf21dde924b7abf18a25256282b69'), maxon.Id('category@338d4b421ddb4ac1a1d7df21c0376911'), maxon.Id('category@c0455a450d0e40eba0c09ad0673d876a'), maxon.Id('category@88057ec2780148708363b96272234ded'), maxon.Id('category@9d1a886ec1584d9f8794c59187c3d3d1'), maxon.Id('category@cec99ab7a44f4b0bac4cedd538f44e02'), maxon.Id('category@13549430f6a54bee9562d3541cb85614'), maxon.Id('category@323d8d63b88b4ab4ba5bed368d895048'), maxon.Id('category@a28830367831413f9fa5b66e4a5dd65f'), maxon.Id('category@94fc8cb5718a492d9fd34f907b89720d'), maxon.Id('category@bae2bf9f00d949c7b5df05cd6cba7966'), maxon.Id('category_bb2ed911f3303be2'), maxon.Id('category_4c6f230c27d35ad7'), maxon.Id('category_e2204ff5b9252604'), maxon.Id('category_0359297d725f327a'), maxon.Id('category_4cc30dbe0f38a155'), maxon.Id('category_1fee6083977acfee'), maxon.Id('category_5904e55a542a55eb'), maxon.Id('category@3448f9feaed44cedac87d499a4567a26'), maxon.Id('category_248c7a50c5c70f4c'), maxon.Id('category_8509ac933bece593'), maxon.Id('category@3be598900c7a428eb0fcbced997655ba'), maxon.Id('category_64f67f3727141f1a'), maxon.Id('category@e33ef358a9b24d6cab80454a4179e419'), maxon.Id('category_63ebef6aab44bd4d'), maxon.Id('category_045526cd4a11b6dc'), maxon.Id('category@a91d4cacaeba4170bf0c202d583fe2e0'), maxon.Id('category@35ee4e3871414784a3767fc2037c305b'), maxon.Id('category_04cdfd3b558329b6'), maxon.Id('category_24b5ae0d89e20ccc'), maxon.Id('category_cd5ad0a08c825d60'), maxon.Id('category_1c643f453b71129e'), maxon.Id('category_f9bd2fcc800c8279'), maxon.Id('category_468266206e683e39'), maxon.Id('category_172e168c2640e535'), maxon.Id('category@c9f25306ba024d939ae97d0999d70995'), maxon.Id('category_ab975c5373b7ba07'), maxon.Id('category@5e2b516fbc0541ad9d59781d675ad8f9'), maxon.Id('category_0133f046d03a46e7'), maxon.Id('category_74ec3ae6ecaab0de'), maxon.Id('category_fcf83cd1ff7e0fea'), maxon.Id('category_32750a41ff5ae804'), maxon.Id('category_f3dfa1fc22e1760f'), maxon.Id('category_8c28445d28cba3b6'), maxon.Id('category_65263c7c1c8c423c'), maxon.Id('category_67a056c194d9897c'), maxon.Id('category_73d82da307ebac4f'), maxon.Id('category_fa850c478d745f9e'), maxon.Id('category@780d427523e243028086a1aa33745b66'), maxon.Id('category_8cbecf2e6c1c53e6'), maxon.Id('category_66c6a982b7227e77'), maxon.Id('category_a2fde34845c656bd'), maxon.Id('category_95b2c4bc77dbeb2d'), maxon.Id('category@f7ac3097518e4141a80ec19bc0c8548b'), maxon.Id('category@93c3e0df81e54acfae70a129ad8f7fc0'), maxon.Id('category@9812f958facf4bc39d90fb79cc4fe783'), maxon.Id('category_ecfd5ef0b32fdc6e'), maxon.Id('category@4d87bd4452b8493aa2ffd9e9500cba56'), maxon.Id('category_de343673ae7ff879'), maxon.Id('category_2aa95f92d383989e'), maxon.Id('category_aded6c15b065a4a5'), maxon.Id('category_fe8c8340d4cfebef'), maxon.Id('category_2d90a72a03048a0a'), maxon.Id('category_8191022b0575a8c8'), maxon.Id('category_94bc6daab0db3454'), maxon.Id('category@7f080054d14f4758aefb36dfae705b88'), maxon.Id('category@d7bd914db4194b13baebbb49f981f395'), maxon.Id('category_bdac9c772d258396'), maxon.Id('category@ee5c708a346740a591544bb9be87cf8d'), maxon.Id('category_f72dcc94be532fa4'), maxon.Id('category_d6e2d304d1c0eb76'), maxon.Id('category_819c3f327c46106c'), maxon.Id('category_a7dd93c148997ea2'), maxon.Id('category_adbf1096d2400c47'), maxon.Id('category_6bf21b118e626b8f'), maxon.Id('category_66478df5e0c296b2'), maxon.Id('category_5ff49c9a181cad8c'), maxon.Id('category_06eaa38e9750e5e3'), maxon.Id('category_732f5bf076c873c7'), maxon.Id('category_781d1c68184a4319'), maxon.Id('category_b6d6fd4c864f01d8'), maxon.Id('category_bcc53614ed7486b6'), maxon.Id('category_20a0233895be23f3'), maxon.Id('category_d42b5d6450873989'), maxon.Id('category_cea3e864eb47b596'), maxon.Id('category_3385066daeaf9819'), maxon.Id('category@1445293c87e64c7684011d9c0477754e'), maxon.Id('category@1f03b25c815e421789036e5a84a3ccf1'), maxon.Id('category@9a2779a603bc4e1f93b9577abe5ecad1'), maxon.Id('category@3064bcb4ff4e4ae890730837caea6f10'), maxon.Id('category@1ba9498066ff42b6b2916242f8d8e7bb'), maxon.Id('category@ef89ec1e9b5a47a5b245e05c5c8b1418'), maxon.Id('category@b674f3ffaa9247f390fdbd49a08111a7'), maxon.Id('category@9a75f45dc5984049b0307b5dc1aea91e'), maxon.Id('category_ef962b4216a17f64'), maxon.Id('category_5a9141a0036aa0e2'), maxon.Id('category_e682fa27d7eb619d'), maxon.Id('category_899783790d811f25'), maxon.Id('category@beb1c0ee13d948faa8e2b084e6b23ab6')) Tree: '/Objects' '/Objects/Info Graphics' '/Objects/Humans' '/Objects/Humans/3D Posable Silhouettes' '/Objects/Humans/3D People - For Animation' '/Objects/Humans/3D People [Low Resolution]' '/Objects/Humans/3D People [Medium Resolution]' '/Objects/Humans/Cutout' '/Objects/Packaging' '/Objects/Tools' '/Objects/Vehicles' '/Objects/Periodicals' '/Objects/Appliances' '/Objects/Eyewear' '/Objects/Garments' '/Objects/Finance' '/Objects/Plants' '/Objects/Plants/European Trees Young' '/Objects/Plants/Garden & Exotic' '/Objects/Plants/European Trees Mature' '/Objects/Plants/Houseplants' '/Objects/Plants/Cutouts' '/Objects/Plants/Grass Elements' '/Objects/Plants/Grass Elements/Low Resolution' '/Objects/Plants/Grass Elements/Medium Resolution' '/Objects/Pots' '/Objects/Shelving' '/Objects/Shelving/Modular Cabinets & Doors' '/Objects/Shelving/Modular Cabinets & Doors/Misc Cabinet Examples' '/Objects/Shelving/Modular Cabinets & Doors/Cabinet & Door Pieces' '/Objects/Shelving/Modular Cabinets & Doors/Misc Door Examples' '/Objects/Shelving/Living Room & Bedroom' '/Objects/Celebration' '/Objects/Kitbash' '/Objects/Kitbash/Piping' '/Objects/Kitbash/Piping/Corners' '/Objects/Kitbash/Piping/Pipes' '/Objects/Kitbash/Piping/Pipe' '/Objects/Kitbash/Details' '/Objects/Kitbash/Details/Squares' '/Objects/Kitbash/Details/Crosses' '/Objects/Kitbash/Details/Geometric' '/Objects/Kitbash/Details/Triangles' '/Objects/Kitbash/Details/Hexagons' '/Objects/Kitbash/Details/Arrows' '/Objects/Kitbash/Details/Rectangles' '/Objects/Kitbash/Details/Circles' '/Objects/Kitbash/Details/Pattern' '/Objects/Kitbash/Tubes' '/Objects/Kitbash/Connectors' '/Objects/Kitbash/Joints' '/Objects/Toys' '/Objects/Outdoor Objects' '/Objects/Outdoor Objects/Scaffolds' '/Objects/Outdoor Objects/Trash Cans' '/Objects/Outdoor Objects/Buildings' '/Objects/Outdoor Objects/Buildings/Houses' '/Objects/Outdoor Objects/Buildings/Cityscape' '/Objects/Outdoor Objects/Buildings/Misc' '/Objects/Outdoor Objects/Wall Decor' '/Objects/Outdoor Objects/Manholes' '/Objects/Outdoor Objects/Miscellaneous' '/Objects/Outdoor Objects/Traffic Lights' '/Objects/Outdoor Objects/Antennas' '/Objects/Outdoor Objects/Pavement' '/Objects/Outdoor Objects/Street Lights' '/Objects/Outdoor Objects/Bus Stops' '/Objects/Outdoor Objects/Fire Escapes' '/Objects/Outdoor Objects/Infrastructure' '/Objects/Outdoor Objects/Road Signs' '/Objects/Outdoor Objects/Barriers & Barricades' '/Objects/Tables' '/Objects/Tables/Bedside Tables' '/Objects/Tables/Game Tables' '/Objects/Tables/Office Tables' '/Objects/Tables/Coffee Tables' '/Objects/Tables/Dining Tables' '/Objects/Arts & Crafts' '/Objects/Stairs' '/Objects/Cogwheel Objects' '/Objects/Cogwheel Objects/Saws' '/Objects/Cogwheel Objects/Gears' '/Objects/Cogwheel Objects/Miscellaneous' '/Objects/Cogwheel Objects/Clutches' '/Objects/Cogwheel Objects/Ratchet' '/Objects/Cogwheel Objects/Watch Gears' '/Objects/Kitchen' '/Objects/Kitchen/Cutlery' '/Objects/Kitchen/Dinnerware' '/Objects/Kitchen/Accessories' '/Objects/Kitchen/Cookware' '/Objects/Kitchen/Serveware' '/Objects/Kitchen/Glassware' '/Objects/Screws' '/Objects/Landscape' '/Objects/Landscape/Wood' '/Objects/Landscape/Stones' '/Objects/Vases' '/Objects/Window Treatments' '/Objects/Seating' '/Objects/Seating/Sofas' '/Objects/Seating/Waiting Areas' '/Objects/Seating/Benches' '/Objects/Seating/Chairs' '/Objects/Lighting & Ceiling Fans' '/Objects/Lighting & Ceiling Fans/Ceiling Lighting' '/Objects/Lighting & Ceiling Fans/Home Safety & Security' '/Objects/Lighting & Ceiling Fans/Outdoor Lighting' '/Objects/Lighting & Ceiling Fans/Wall Lighting' '/Objects/Lighting & Ceiling Fans/Ceiling Fans' '/Objects/Lighting & Ceiling Fans/Lamps' '/Objects/Lighting & Ceiling Fans/Light Bulbs' '/Objects/Lighting & Ceiling Fans/Light Stands' '/Objects/Music' '/Objects/Bedroom' '/Objects/Bath' '/Objects/Bath/Toilets' '/Objects/Bath/Bathroom Vanities' '/Objects/Bath/Sinks' '/Objects/Bath/Towels' '/Objects/Bath/Bathroom Accessories' '/Objects/Bath/Bathtubs & Showers' '/Objects/Bath/Faucets' '/Objects/Gambling' '/Objects/Stationary' '/Objects/Sculpting Base Meshes' '/Objects/Weather' '/Objects/Home Decor' '/Objects/Home Decor/Candles' '/Objects/Home Decor/Books' '/Objects/Home Decor/Picture Frames' '/Objects/Home Decor/Decorative Storage' '/Objects/Home Decor/Candleholders' '/Objects/Home Decor/Decorative Boxes' '/Objects/Home Decor/Clocks' '/Objects/Electronics & Technology' '/Objects/Miscellaneous' '/Objects/Sports Items' '/Objects/Food' '/Objects/Doors & Windows' -------------------------------------------------------------------------------- AssetCategoryHandler at 0X7FD2F3368910 (repo = net.maxon.repository.userprefs): Managed IDs: (maxon.Id('category@0ad833e88d774ac1ae4fd277e57ecacc'), maxon.Id('category@d8bc777f959e48bda659d1674bb1d5f3'), maxon.Id('category@0399404fe6fc46558b8f98f63d49ce28'), maxon.Id('category@08ab3faa66e348fe86574469cbabb2c7'), maxon.Id('category@793e20a462f04c64a5cea28209f9a38b')) Tree: '/MyUserCategory' '/MyUserCategory/Blah' '/MyUserCategory/Blah/Blub' '/MyUserCategory/Blub' '/MyUserCategory/Blub/Blah' -------------------------------------------------------------------------------- AssetCategoryHandler at 0X7FD2F1CBED10 (repo = database@CGBLAN4TBpzrDfyA9gjMTn): Managed IDs: (maxon.Id('category@d1233ed9975945debc8942a30edfd706'), maxon.Id('category@cfce060493cd4c61b92a115624d33eb1'), maxon.Id('category@fa80f5a53cea42b7a72f494f0d673377'), maxon.Id('category@ae9c2c99dee74deeb8a7a7d711d57ffe'), maxon.Id('category@ffc383ed61454affa841f13931f93125')) Tree: '/AlsoUserCategory' '/AlsoUserCategory/Foo' '/AlsoUserCategory/Foo/Bar' '/AlsoUserCategory/Blah' '/AlsoUserCategory/Blah/Blub'

      The code:

      """Provides a type to expand asset categories into trees. """ import c4d import maxon import typing import pprint class AssetCategoryHandler: """Handles an asset category that is a root to zero to many descendant categories. """ def __init__(self, asset: maxon.AssetDescription, categories: list[maxon.AssetDescription] = [], language: maxon.LanguageRef = maxon.Resource.GetCurrentLanguage(), path: str = ""): """Initializes the handler. Args: asset (maxon.AssetDescription): The category asset to expand. categories (list[maxon.AssetDescription], optional): A list of categories which should be respected for expansion, when the empty list is passed, the assets will be gathered from the repository #asset is attached to. Defaults to []. language (maxon.LanguageRef, optional): The language to retrieve asset strings in. Defaults to maxon.Resource.GetCurrentLanguage(). path (str, optional): [internal] The current parent path. Defaults to "". Raises: TypeError: On type assertion failures. """ # Type checks and retrieving some data from the asset. if not isinstance(asset, maxon.AssetDescription) or asset.IsNullValue(): raise TypeError(f"{asset = }") if not isinstance(categories, list): raise TypeError(f"{categories = }") if not isinstance(language, maxon.LanguageRef) or language.IsNullValue(): raise TypeError(f"{language = }") self._asset: maxon.AssetDescription = asset self._aid: maxon.Id = maxon.Id(asset.GetId()) if isinstance(asset.GetId(), str) else asset.GetId() self._name: str = asset.GetMetaString(maxon.OBJECT.BASE.NAME, language, "") self._path: str = f"{path}/{self._name}" self._repo: maxon.AssetRepositoryRef = asset.GetRepository() self._categories: list[maxon.AssetDescription] = categories self._language: maxon.LanguageRef = language self._children: list["AssetCategoryHandler"] = [] # Populate _categories when empty. if len(self._categories) < 1: self._categories = self._repo.FindAssets( maxon.AssetTypes.Category(), maxon.Id(),maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) # Expand the tree and cache the IDs. self.__expand__() self._ids: tuple[maxon.Id] = tuple(self.__yieldids__()) def __repr__(self) -> str: """Returns a string representation of the handler. """ return f"{self.__class__.__name__} at {str(hex(id(self))).upper()}" def __expand__(self): """Expands the handler into a tree of handlers, one for each of the descendant categories of the wrapped category asset. """ for asset in self._categories: if self._aid != maxon.CategoryAssetInterface.GetParentCategory(asset): continue child = AssetCategoryHandler(asset, self._categories, self._language, self._path) self._children.append(child) def __yieldids__(self) -> typing.Generator[maxon.Id, None, None]: """Provides a generator for all asset IDs associated with this handler. Use the property Ids instead, unless rebuilding this data is desired. """ yield self._aid for child in self._children: for aid in child.__yieldids__(): yield aid @property def Ids (self) -> tuple[maxon.Id]: """Returns a tuple of all asset IDs associated with this handler. """ return self._ids def PrintTree(self, indent: int = 0) -> None: """Prints an asset tree for this handler. """ print(f"{' ' * indent}'{self._path}'") for handler in self._children: handler.PrintTree(indent + 1) def main() -> None : """Runs the example. """ # Get the user preferences asset repository, in a production environment it will contain all # relevant assets as of 2023.1.0. if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading(): raise RuntimeError("Could not load asset databases.") repo: maxon.AssetRepositoryInterface = maxon.AssetInterface.GetUserPrefsRepository() if not repo: raise RuntimeError("Unable to retrieve user repository.") # Used to shorten the call signatures of the FindLatestAsset() calls below. kwargs: dict = { "type": maxon.AssetTypes.Category(), "version": maxon.Id(), "findMode": maxon.ASSET_FIND_MODE.LATEST } # Get a couple of root level asset categories. categoryRoots: list[maxon.AssetDescription] = [ # The /Objects category in the Asset Browser, a built-in category. repo.FindLatestAsset(aid=maxon.Id("category@3d2621c1bc48485aa2b7ceead989e421"), **kwargs), # # /Example Scenes # repo.FindLatestAsset(aid= maxon.Id("category@4e785a69ef3749738bfd9d2b191535d5"), **kwargs), # # /Materials # repo.FindLatestAsset(aid= maxon.Id("category_a1ba084a9eeedb9b"), **kwargs), # # /Nodes # repo.FindLatestAsset(aid= maxon.Id("category@52d8f01357834200aa0dc28f0e61bbb3"), **kwargs), # # /Textures # repo.FindLatestAsset(aid= maxon.Id("category@8c76a408c56f4b5ca9f585dbe0ece9b7"), **kwargs), # Custom category trees, they will be filtered out on other machines then mine, since other # machines won't find these assets. # /MyUserCategory (stored in the database in the user prefs) repo.FindLatestAsset(aid= maxon.Id("category@0ad833e88d774ac1ae4fd277e57ecacc"), **kwargs), # /AlsoUserCategory (stored in a custom database) repo.FindLatestAsset(aid= maxon.Id("category@d1233ed9975945debc8942a30edfd706"), **kwargs), ] # Wrap each one of them into a handler. categoryHandlerList: list["AssetCategoryHandler"] = [ AssetCategoryHandler(asset) for asset in categoryRoots if isinstance(asset, maxon.AssetDescription) and not asset.IsNullValue()] # Iterate over the handlers and inspect their data. for handler in categoryHandlerList: print ("\n", "-" * 80) print (f"{handler} (repo = {handler._asset.GetRepositoryId()}):\n") print ("\nManaged IDs:\n") pprint.pprint(handler.Ids) print ("\nTree:\n") handler.PrintTree(indent=1) if __name__ == "__main__": main()
    • d_keithD

      Get All Assets in Category

      Cinema 4D SDK
      • python • • d_keith
      3
      0
      Votes
      3
      Posts
      825
      Views

      ferdinandF

      Hello @d_keith,

      thank you for reaching out to us and your extensive documentation efforts. Much appreciated! What you did there is correct, but in the spirit of simplicity, I think the code could be a bit condensed. There snuck in a few unused (and unrelated) includes and being so generous with separating things into help functions can impact readability and execution times. I also added the "expand asset category into sub-categories" thing you wanted.

      In short: I think this is a good occasion to use a receiver callback function, as this structures the code naturally. Find my take below.

      Cheers,
      Ferdinand

      The result:

      categoryIds = [maxon.Id('category@b9c32d04a12d449ca1c758ddb3c695b0'), maxon.Id('category@985a9913c47341e4a373ca71d8e73b18')] name = 'Cloudy - VHDRI.hdr', asset.GetId() = 'file_e02d0a81b02be4fa' name = 'Default HDR.hdr', asset.GetId() = 'file_2792c7829905f40d' name = 'Desert.exr', asset.GetId() = 'file_0b3eb8e7595c1b5d' name = 'jhdri-v1_ext_sunset_seastar_acescg.exr', asset.GetId() = 'file_dc156adf0cc146e8' name = 'HDR012.hdr', asset.GetId() = 'file_b1db2c38badb130c'

      The code:

      """Retrieves all MediaImage assets that are attached to the "Textures/HDR" category or one of its child categories. This is an application of what I dubbed "Filtered Asset searches" in the C++ Docs, find the Python example here [1]. References: [1] https://github.com/PluginCafe/cinema4d_py_sdk_extended/blob/master/scripts/05_modules/assets/asset_databases_r26.py#L416 """ import typing import maxon def ExpandAssetCategoryId(repo: maxon.AssetRepositoryInterface, cid: maxon.Id) -> list[maxon.Id]: """Retrieves the IDs of all descendant asset categories of #cid. """ # Get all asset category assets in #repo. categoryAssets: maxon.AssetDescription = repo.FindAssets( maxon.AssetTypes.Category(), maxon.Id(),maxon.Id(), maxon.ASSET_FIND_MODE.LATEST) # The IDs we have currently to check for descendants and the final result list. idsToCheck: list[maxon.Id] = [cid] results: list[maxon.Id] = [] # Try to empty #idsToCheck while popping elements from it and adding new ones. while idsToCheck: # Remove the first element and add it to the results. cid: maxon.Id = idsToCheck.pop() results.append(cid) # Find all category assets which have #cid as their parent category and add their IDs as # to be resolved category IDs. We stop the search here early, i.e., we assume the category # tree to be mono-hierarchical (one category can only be attached to one category). There # is not really anything in the Asset API which would enforce this, but it is how asset # categories are currently handled. for asset in categoryAssets: if cid == maxon.CategoryAssetInterface.GetParentCategory(asset): aid: typing.Any = asset.GetId() idsToCheck.append(aid if isinstance(aid, maxon.Id) else maxon.Id(aid)) break return results def main() -> None : """Runs the example. """ # Make sure the asset databases have been loaded and get the user repository, i.e., the # repository where more or less all content can be found. if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading(): raise RuntimeError("Could not load asset databases.") repo: maxon.AssetRepositoryRef = maxon.AssetInterface.GetUserPrefsRepository() if not repo: raise RuntimeError("Unable to retrieve user repository.") # The final #results list where we put all the asset descriptions of image assets which are # parented to category assets with any of the IDs in #categoryIds. results: list[tuple[str, maxon.AssetDescription]] = [] # The root category we are interested in, this is the id for the "Textures/HDR" category. rootCategoryId: maxon.Id = maxon.Id("category@b9c32d04a12d449ca1c758ddb3c695b0") # Expand #rootCategoryId into a list of all its attached child categories. categoryIds: list[maxon.Id] = ExpandAssetCategoryId(repo, rootCategoryId) # The language Cinema 4D is running in, use GetDefaultLanguage() to evaluate things in engUS. currentLanguage: maxon.LanguageRef = maxon.Resource.GetCurrentLanguage() def onSearchHit(asset: maxon.AssetDescription) -> bool: """Called for each asset by the FindAssets() call below. Assets which match the search criteria are written to #results. """ # Get the metadata of the asset and assert that its subtype is MediaImage, step over an asset # when these operations fail. metadata: maxon.AssetMetaData = asset.GetMetaData() if metadata is None: return True sid: maxon.InternedId = maxon.InternedId(metadata.Get(maxon.ASSETMETADATA.SubType, maxon.Id())) if sid != maxon.ASSETMETADATA.SubType_ENUM_MediaImage: return True # Append the name of the asset and the asset itself to the results when #asset is parented # to a category asset with an ID which is contained ins #categoryIds. if maxon.CategoryAssetInterface.GetParentCategory(asset) in categoryIds: results.append((asset.GetMetaString(maxon.OBJECT.BASE.NAME, currentLanguage, ""), asset)) return True # Search #repo for all file type assets (this includes MediaImage assets) with any id and pass # the data through onSearchHit. repo.FindAssets( assetType=maxon.AssetTypes.File(), aid=maxon.Id(), version=maxon.Id(), findMode=maxon.ASSET_FIND_MODE.LATEST, receiver=onSearchHit) # Print the expanded category IDs and the first five asset matches associated with them. print (f"{categoryIds = }") for name, asset in results[:5]: print (f"{name = }, {asset.GetId() = }") if __name__ == "__main__": main()
    • d_keithD

      Looking for Scene Nodes Capsules Creators

      General Talk
      • • • d_keith
      3
      1
      Votes
      3
      Posts
      744
      Views

      d_keithD

      Hi All,

      Thank you so much for your interest and taking the time to apply. We ended up with more applicants than could fit in one workshop. If you received an email from me (Maybe in your spam folder? look for d_keith ) you've likely been accepted. If not, I do intend to host similar workshops in the future. Candidates were given priority based on:

      Prior programming expertise in C/C++/Python or similar languages. Interest and ability to actively participate in the lectures/homework projects. Availability to potentially create capsules for the Maxon Capsules Library in the future on a freelance basis. And for auditors, special preference was given to MAXON employees who will be documenting/developing/training Scene Nodes workflows.

      I hope to host similar workshops in the future and will prioritize previous applicants at that time.
      Sincerely,

      Donovan Keith