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
    • Login
    1. Home
    2. d_keith
    • Profile
    • Following 0
    • Followers 0
    • Topics 5
    • Posts 12
    • Best 2
    • Controversial 0
    • Groups 0

    d_keith

    @d_keith

    Product Manager, Assets Producer

    2
    Reputation
    15
    Profile views
    12
    Posts
    0
    Followers
    0
    Following
    Joined Last Online
    Website www.maxon.net Location Los Angeles, CA

    d_keith Unfollow Follow

    Best posts made by d_keith

    • RE: Get All Assets in Category

      Prints a list of Media Assets in the Cinema 4D Assets Browser to the Console [GitHub Gist]

      """Name-en-US: Print Media in Category
      Description-en-US: Prints a list of all media assets belonging to a category ID to the console.
      
      References:
      https://developers.maxon.net/forum/topic/14214/get-asset-from-asset-browser-python/4
      
      ## License
      
      MIT No Attribution
      
      Copyright 2022 Donovan Keith
      
      Permission is hereby granted, free of charge, to any person obtaining a copy of this
      software and associated documentation files (the "Software"), to deal in the Software
      without restriction, including without limitation the rights to use, copy, modify,
      merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      permit persons to whom the Software is furnished to do so.
      
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      """
      
      # Imports
      
      from importlib.metadata import metadata
      from multiprocessing.sharedctypes import Value
      import c4d
      import maxon
      from typing import Optional
      
      # Helper Functions
      
      
      def FindAssetsByType(type) -> Optional[list[maxon.Asset]]:
          repository = maxon.AssetInterface.GetUserPrefsRepository()
          if not repository:
              raise RuntimeError("Unable to get User Repository.")
      
          # Find Assets:
          # https://developers.maxon.net/docs/py/2023_2/modules/maxon_generated/frameworks/asset/interface/maxon.AssetRepositoryInterface.html?highlight=findassets#maxon.AssetRepositoryInterface.FindAssets
          assets = repository.FindAssets(
              assetType=maxon.AssetTypes.File(),
              aid=maxon.Id(),
              version=maxon.Id(),
              findMode=maxon.ASSET_FIND_MODE.LATEST,
              receiver=None
          )
      
          return assets
      
      
      def FindFileAssets():
          return FindAssetsByType(type=maxon.AssetTypes.File())
      
      
      def GetAssetCategory(asset: maxon.AssetInterface):
          if not asset:
              raise ValueError("No asset provided.")
      
          meta_data = asset.GetMetaData()
          if not meta_data:
              raise ValueError("Unable to get asset meta data.")
      
          return meta_data.Get(maxon.ASSETMETADATA.Category)
      
      
      def GetAssetName(asset: maxon.AssetDescription) -> Optional[str]:
          if not asset:
              return
      
          metadata: maxon.AssetMetaData = asset.GetMetaData()
          if metadata is None:
              return
      
          name: str = asset.GetMetaString(
              maxon.OBJECT.BASE.NAME, maxon.Resource.GetCurrentLanguage(), "")
      
          return name
      
      
      def IsAssetAnImage(asset: maxon.AssetDescription) -> bool:
          if not asset:
              return
      
          metadata: maxon.AssetMetaData = asset.GetMetaData()
          if metadata is None:
              return
      
          sub_type: maxon.Id = metadata.Get(maxon.ASSETMETADATA.SubType, None)
          if (sub_type is None or maxon.InternedId(sub_type) != maxon.ASSETMETADATA.SubType_ENUM_MediaImage):
              return False
      
          return True
      
      
      def GetCategoryIdFromUser() -> str:
          imperfections_id_string = "category@e780d216ed404547942dcbfcbbe009e5"
      
          category_id_string = c4d.gui.InputDialog(
              "Input Category ID", preset=imperfections_id_string)
          if not category_id_string:
              raise ValueError("Invalid ID String")
      
          return maxon.Id(category_id_string)
      
      
      def main():
          category_id = GetCategoryIdFromUser()
          file_assets = FindFileAssets()
          imperfections_assets = [asset for asset in file_assets if (
              IsAssetAnImage(asset) and GetAssetCategory(asset) == category_id)]
          for asset in imperfections_assets:
              print(GetAssetName(asset))
      
      
      if __name__ == "__main__":
          main()
      
      
      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • Looking for Scene Nodes Capsules Creators

      Hi!

      I'm a Product Manager and Assets Producer at Maxon. We recently collaborated with the team at Rocket Lasso to produce some great new FUI Spline Capsules (

      , &
      ) and we're hoping to replicate that success with additional third party partners.

      Are you a plugin developer or technical director? Have you been interested in Scene Nodes, but not sure where or why to start?

      We're looking to grow the number of qualified C4D Scene Nodes capsule creators, and improve scene nodes workflows in Cinema 4D. To that end, we're training/supporting a small group of interested developers/technical directors over a ~4 week period. If you're interested in participating, please fill out this form before 2022-06-20:

      https://forms.office.com/r/Mqyq37fXST

      Specifics will largely depend on the group we select, but we're looking to provide:

      • Live weekly video training/Q&A sessions (Time TBD, but probably Friday nights in Europe / Friday mornings in the US), with possible supplemental pre-recorded videos.
      • Best practices documentation.
      • Direct access to a Maxon product manager.
      • Suggestions for how to improve your node setups and UX to match C4D performance/UX standards.
      • A private forum for discussing ideas/issues with other Scene Nodes Assets creators.
      • A private repo of utility node groups for solving common issues.

      You'll be asked to:

      • Complete 1+ Spline/Geo Primitive, Geo Modifier, Selection Modifier, or Node Group that feels and functions like a native Cinema 4D primitive/deformer/node.
      • Document your development process.
      • Provide honest feedback about Scene Nodes workflows and documentation.
      • Log any bugs/UX issues you encounter as you work.
      posted in General Talk
      d_keithD
      d_keith

    Latest posts made by d_keith

    • RE: Adding an Object to a New Cinema 4D Asset Database

      Update: I've found found a workable solution, but still have some questions (listed at bottom)

      @ferdinand - I tried to edit the original post to avoid a "diary" post, but ran into the following error:

      You are only allowed to edit posts for 3600 second(s) after posting

      """Name-en-US: Import Directory as Object Assets Database
      Description-en-US: Creates a new C4D Asset Database named `_my-database.db` on your desktop, and loads the first object of each .c4d project in `_assets`
      
      References:
      https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_databases_r26.py
      https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_types_r26.py
      
      """
      
      ## Imports
      
      import c4d
      import os
      import maxon
      
      ## User Inputs
      
      # TODO: Add input paths, otherwise default paths will be used
      
      INPUT_PROJECTS_DIR = ""
      OUTPUT_ASSETS_DB = ""
      
      ## Helpers
      
      def CreateRepoFromPath(path) -> maxon.UpdatableAssetRepositoryRef:
          # Wait for all existing dbs to load
          # Not sure if this is needed or superstitious
          if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
              return RuntimeError("Could not load asset databases.")
      
          url = maxon.Url(path)
          if url.IoDetect() == maxon.IODETECT.ERRORSTATE:
              raise RuntimeError(f"Directory {url} is invalid")
      
          repo_id = maxon.AssetInterface.MakeUuid(str(url), True)
          bases = maxon.BaseArray(maxon.AssetRepositoryRef)
          try:
              repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                  repo_id,
                  maxon.AssetRepositoryTypes.AssetDatabase(),
                  bases,
                  url,
                  True,
                  False,
                  False,
              )
          except Exception as e:
              # If at first you don't succeed, try... try... again.
              repository = maxon.AssetInterface.CreateRepositoryFromUrl(
                  repo_id,
                  maxon.AssetRepositoryTypes.AssetDatabase(),
                  bases,
                  url,
                  True,
                  False,
                  False,
              )
      
          if not repository:
              raise RuntimeError("Could not create Repository.")
      
          return repository
      
      def MountAssetDatabase(path):
          # Wait for all existing dbs to load
          if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
              return RuntimeError("Could not load asset databases.")
      
          # Build a maxon url from the path
          url = maxon.Url(path)
          databaseCollection = maxon.AssetDataBasesInterface.GetDatabases()
      
          # Check if DB is already mounted
          for database in databaseCollection:
              print(database)
      
              if database._dbUrl == url:
                  database._active = True
                  maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
                  return database
      
          database = maxon.AssetDatabaseStruct(url)
          databaseCollection.append(database)
          maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
      
          return database
      
      def AddObjectToRepository(
          repo: maxon.UpdatableAssetRepositoryRef,
          doc: c4d.documents.BaseDocument,
          obj: c4d.BaseObject,
      ):
          if repo is None:
              raise ValueError("Invalid repo")
      
          if obj is None:
              raise ValueError("Input obj does not exist")
      
          asset_id = maxon.AssetInterface.MakeUuid(prefix="object", compact=False)
          asset_name = obj.GetName()
          asset_version = (
              "0.1.0"  # Using Semantic Versioning, as we rarely get it right the first time.
          )
          asset_metadata = maxon.AssetMetaData()
          asset_category_id = maxon.Id("net.maxon.assetcategory.uncategorized")
      
          store_asset_struct = maxon.StoreAssetStruct(asset_category_id, repo, repo)
          asset = maxon.AssetCreationInterface.CreateObjectAsset(
              obj,
              doc,
              store_asset_struct,
              asset_id,
              asset_name,
              asset_version,
              asset_metadata,
              True,
          )
      
          return asset
      
      
      def OpenAssetBrowser():
          # The command id for the Asset Browser.
          CID_ASSET_BROWSER = 1054225
      
          # Open the Asset Browser when it is not already open.
          if not c4d.IsCommandChecked(CID_ASSET_BROWSER):
              c4d.CallCommand(CID_ASSET_BROWSER)
      
      
      ## Main
      
      
      def main():
          # Get the user's desktop path
          desktop_path = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP)
      
          # Get the Input File Path
          assets_dir = INPUT_PROJECTS_DIR
          if not assets_dir:
              assets_dir = os.path.join(desktop_path, "_assets")
      
          # Define the output database path
          database_name = "_my-database.db"
          database_path = OUTPUT_ASSETS_DB
          if not database_path:
              database_path = os.path.abspath(os.path.join(desktop_path, database_name))
      
          # Create the database if it doesn't exist
          if not os.path.exists(database_path):
              os.makedirs(database_path, exist_ok=True)
      
          repository = CreateRepoFromPath(database_path)
      
          doc = c4d.documents.GetActiveDocument()
          obj = doc.GetActiveObject()
      
          # Iterate through all *.c4d files in the assets directory
          assets = []
          for file_name in os.listdir(assets_dir):
              if file_name.endswith(".c4d"):
                  file_path = os.path.join(assets_dir, file_name)
      
                  # Load the C4D file silently
                  loaded_doc = c4d.documents.LoadDocument(
                      file_path, c4d.SCENEFILTER_OBJECTS | c4d.SCENEFILTER_MATERIALS, None
                  )
                  if loaded_doc is None:
                      print(f"Failed to load {file_path}")
                      continue
      
                  # Get the first object in the loaded document
                  obj = loaded_doc.GetFirstObject()
                  if obj is None:
                      print(f"No objects found in {file_path}")
                      continue
      
                  # Add the object to the repository
                  asset = AddObjectToRepository(repository, loaded_doc, obj)
                  if asset is None:
                      raise RuntimeError(f"Unable to ingest {file_name}")
      
                  assets.append(asset)
      
                  # Unload/close the loaded document
                  c4d.documents.KillDocument(loaded_doc)
      
          database = MountAssetDatabase(database_path)
      
          maxon.AssetManagerInterface.RevealAsset(assets)
          c4d.EventAdd()
      
          c4d.storage.ShowInFinder(database_path, open=True)
      
      
      
      # Execute the script
      if __name__ == "__main__":
          main()
      
      

      Questions

      1. Should I be able to mount a directory and have it auto-create a DB?
      2. Any idea why I need to re-try creating the Repo for it to work?
      3. If I run this script multiple times, I end up with multiple DBs in the same directory - any way to get CreateRepoFromUrl() to detect that there's already a repo so it doesn't need to create one, and can instead just load it?
      4. Show Assets doesn't seem to be doing what I want. I typically have to manually close/reopen the Assets Browser a couple of times to see an update. Is there an event I need to add to immediately show the assets?

      Thanks!

      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • Adding an Object to a New Cinema 4D Asset Database

      Hi,

      I'm attempting to write a script that will allow me to bulk-import the contents of a directcory full of .c4d files as Object Assets in a new Cinema 4D Asset Databse.

      I've found what feels like a perfect starting point by @ferdinand:
      https://github.com/Maxon-Computer/Cinema-4D-Python-API-Examples/blob/master/scripts/05_modules/assets/asset_databases_r26.py

      In the documentation for MountDatabases(), he mentions:

      When the selected path does not contain an asset database, the necessary metadata will be created in that location by Cinema 4D.

      However, when I attempt to do something similar in a script, the folder mounts, but no metadata files are created and C4D doesn't react when I attempt to disable/delete the "database stub".
      46ca82ad-cc20-4793-a4f5-b1f23e909280-image.png
      e5cc6f01-1e88-49d8-b0b8-ac3326704e21-image.png

      Here's my current source code - if it looks practically identical to Ferdinand's that's because I re-wrote a lot of his methods by hand (hoping to better understand the API).

      """Name-en-US: Create Test Database
      Description-en-US: Creates a new C4D Asset Database named `_my-database.db` on your desktop."""
      
      import c4d
      import os
      import maxon
      
      def MountAssetDatabase(path):
          # Wait for all existing dbs to load
          if not maxon.AssetDataBasesInterface.WaitForDatabaseLoading():
              return RuntimeError("Could not load asset databases.")
      
          # Build a maxon url from the path
          url = maxon.Url(path)
          databaseCollection = maxon.AssetDataBasesInterface.GetDatabases()
      
          # Check if DB is already mounted
          for database in databaseCollection:
              print(database)
      
              if database._dbUrl == url:
                  database._active = True
                  maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
                  return database
      
          database = maxon.AssetDatabaseStruct(url)
          databaseCollection.append(database)
          maxon.AssetDataBasesInterface.SetDatabases(databaseCollection)
          print(f"Created new DB for '{url}'")
      
          return database
      
      def main():  
          # Get the user's desktop path
          desktop_path = c4d.storage.GeGetC4DPath(c4d.C4D_PATH_DESKTOP)
          
          # Define the database file path
          name = "_my-database.db"
          path = os.path.join(desktop_path, name)
          
          # Create the database if it doesn't exist
          if not os.path.exists(path):
              os.makedirs(path, exist_ok=True)
      
          database = MountAssetDatabase(path)
          c4d.storage.ShowInFinder(path, open=True)
      
      # Execute the script
      if __name__ == '__main__':
          main()
      

      Am I misunderstanding what should happen - or is it not working as expected?

      posted in Cinema 4D SDK python macos 2025
      d_keithD
      d_keith
    • RE: Hiding Node Previews for all Redshift Nodes in Redshift Materials

      @Dunhou - Thanks so much for the sample code, it works brilliantly. Your Renderer lib looks incredible, I love how compact/accessible that version is. Unfortunately I'll need to share these scripts with a variety of external parners and I'm not sure I can trust they'll have Boghma installed.

      @ferdinand - I'll avoid diary-style requests in the future. Thanks for pointing me to Graph Descriptions. I knew that something like this existed, but I couldn't track it down. Perhaps this would make a good SDK example?

      @ferdinand - How would you query all nodes except the main Output node?

      Here's a fully working script:

      """Name-en-US: Hide RS Material Node Previews
      Description-en-US: Finds all previews in Redshift nodal materials except for their final Output node.
      
      ## References
      
      - [Hiding Node Previews for all Redshift Nodes in Redshift Materials](https://developers.maxon.net/forum/topic/16119/hiding-node-previews-for-all-redshift-nodes-in-redshift-materials)
      
      ## Change Log
      
      - v1.0.0: Converted to script and added undo support, documentation, and reference.
      - v0.9.0: Initial working version by Dunhou.
      - v0.1.0: Prototype by Donovan.
      
      ## Thanks
      
      Special thanks to Dunhou and Ferdinand.
      
      """
      
      __authors__ = [
          "Dunhou (https://www.boghma.com/)",
          "Donovan Keith <[email protected]> (https://www.maxon.net/en/capsules)",
      ]
      
      __version__ = "1.0.0"
      
      import c4d
      import maxon
      
      doc: c4d.documents.BaseDocument
      
      
      def main():
          global doc
      
          if doc is None:
              print("No active document.")
              return
      
          doc.StartUndo()
          for mat in doc.GetMaterials():
              doc.AddUndo(c4d.UNDOTYPE_CHANGE, mat)
      
              nodeMaterial: c4d.NodeMaterial = mat.GetNodeMaterialReference()
              if not nodeMaterial.HasSpace(maxon.NodeSpaceIdentifiers.RedshiftMaterial):
                  continue
      
              graph: maxon.NodesGraphModelRef = nodeMaterial.GetGraph(
                  maxon.NodeSpaceIdentifiers.RedshiftMaterial
              )
              root: maxon.GraphNode = graph.GetViewRoot()
              nimbusRef: maxon.NimbusBaseRef = mat.GetNimbusRef(
                  maxon.NodeSpaceIdentifiers.RedshiftMaterial
              )
              endNodePath: maxon.NodePath = nimbusRef.GetPath(
                  maxon.NIMBUS_PATH.MATERIALENDNODE
              )
              endNode: maxon.GraphNode = graph.GetNode(endNodePath)
      
              with graph.BeginTransaction() as transaction:
                  node: maxon.GraphNode
                  for node in root.GetInnerNodes(
                      mask=maxon.NODE_KIND.NODE, includeThis=False
                  ):
                      node.SetValue(maxon.NODE.BASE.DISPLAYPREVIEW, maxon.Bool(False))
      
                  endNode.SetValue(maxon.NODE.BASE.DISPLAYPREVIEW, maxon.Bool(True))
      
                  transaction.Commit()
          doc.EndUndo()
      
          c4d.EventAdd()
      
      
      if __name__ == "__main__":
          main()
      
      
      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • RE: Hiding Node Previews for all Redshift Nodes in Redshift Materials

      Another hacky approach that at least gets me a semi-automated solution. I broke my previous hack into two scripts to get around a limit in the number of CallCommands you can invoke with predictable results. If you dock the icons for the two scripts next to each other, you can click your way to all previews for all materials being closed. Doesn't solve for keeping the final Output node's preview up.

      1. Select Next Material: Selects the next material in the Material Manager.

      """Name-en-US: Select Next Material
      Description-en-US: Selects the material after the first active material.
      
      A bit hacky as it is using CallCommand.
      """
      
      import c4d
      
      def main():
          doc = c4d.documents.GetActiveDocument()
          materials = doc.GetMaterials()
          active_material = doc.GetActiveMaterial()
      
          if not active_material:
              c4d.gui.MessageDialog("No active material found.")
              return
      
          active_material = doc.GetActiveMaterial()
          next_material = None
          if not active_material:
              next_material = doc.GetFirstMaterial()
          else:
              doc.AddUndo(c4d.UNDOTYPE_CHANGE_SELECTION, active_material)
              next_material = active_material.GetNext()
              if not next_material:
                  next_material = doc.GetFirstMaterial()
      
          if not next_material:
              c4d.gui.MessageDialog("No materials in this scene.")
              return
          
          doc.StartUndo()
          doc.AddUndo(c4d.UNDOTYPE_CHANGE_SELECTION, next_material)
          doc.SetActiveMaterial(next_material)
          doc.EndUndo()
          c4d.EventAdd()
          c4d.CallCommand(16297) # Scroll To Selection
      
      if __name__ == '__main__':
          main()
      

      2. Toggle Node Previews: Selects all nodes in the active material, toggles the preview, and then deselects all.

      """Name-en-US: Hide Node Previews
      Descripton-en-US: Toggles the Show Preview command for all nodes.
      
      Might not actually hide if all previews start closed.
      Use in conjunction with select-next-material.py to quickly
      toggle all materials in a scene.
      """
      
      import c4d
      
      def main():
          c4d.CallCommand(465002309) # Select All Nodes
          c4d.CallCommand(465002350) # Show Preview
          c4d.CallCommand(465002310) # Deselect All
      
      if __name__ == "__main__":
          main()
      
      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • Hiding Node Previews for all Redshift Nodes in Redshift Materials

      Hi,

      I'm trying to write a python script that hides material previews for all nodes in all Redshift materials except for the final Output node.

      In python pseudocode, it would look something like this:

      materials = doc.GetAllMaterials()
      for mat in materials:
         if mat.GetType() != "redshift":
             continue
          nodes = mat.GetAllNodes()
          for node in nodes:
              if node.GetType() == "Output":
                 continue
              node.SetPreview(False)
      

      Unfortunately, it seems it is quite a bit trickier to do this than I hoped.
      I got as far as finding the GraphModelHelper but there doesn't seem to be a way to retrieve all nodes without a filter; and I'm not sure how to find list of IDs for user-visible nodes.

      There are some 3rd party libs on GitHub that would seem to make some of this easier, but I'd like to use native methods if possible.

      Any suggestions/guidance would be greatly appreciated!

      Edit: Here's an ultra-hacky script that doesn't do what I hoped either.

      """Hide All Node Previews in Materials
      
      Hacky script to hide all node previews by invoking the user-visible commands in the Node Editor.
      
      I grabbed the CallCommand IDs from the C4D Script Log.
      
      Doesn't seem to work.
      """
      
      import c4d
      
      doc: c4d.documents.BaseDocument # The active document.
      
      def main():
          materials = doc.GetMaterials()
          if not materials:
              return
          
          # Open the Node Editor
          c4d.CallCommand(465002211) # Node Editor
      
          for mat in materials:
              doc.SetActiveMaterial(mat)
              c4d.EventAdd()
      
              c4d.CallCommand(465002309) # Select All Nodes
              c4d.CallCommand(465002350) # Show Preview
      
          c4d.EventAdd()
      
      if __name__ == "__main__":
          main()
      

      Edit 2: A little closer

      Okay, I'm a bit closer now thanks to this post by @m_adam

      """Hide All Node Previews in Materials
      """
      
      import c4d
      import maxon
      
      doc: c4d.documents.BaseDocument # The active document.
      
      def main():
          materials = doc.GetMaterials()
          if not materials:
              return
          
          doc.StartUndo()
      
          for material in materials:
              # Reference
              # https://developers.maxon.net/forum/topic/16108/automating-colorspace-change-for-textures-in-redshift/3
              nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
              if not nodeMaterial.HasSpace(maxon.NodeSpaceIdentifiers.RedshiftMaterial):
                  continue
      
              graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(material, maxon.NodeSpaceIdentifiers.RedshiftMaterial)
      
              doc.AddUndo(c4d.UNDOTYPE_CHANGE, material)
              with graph.BeginTransation() as transaction:
                  # TODO: Get all nodes that have Previews
                  # TODO: Iterate over all nodes
                  # TODO: Hide all node previews
                  pass
      
              transaction.Commit()
      
          doc.EndUndo()
          c4d.EventAdd()
      
      if __name__ == "__main__":
          main()
      
      posted in Cinema 4D SDK python 2025
      d_keithD
      d_keith
    • RE: Traversing Asset Categories

      Update: There seems to be a bug in ExpandAssetCategoryId as I can retrieve all the assets (even those in custom DBs) if I start from a manually generated list of category Ids, but I only geta couple of the categories using this method.

      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • Traversing Asset Categories

      Hi @ferdinand - Thank you so much for this!

      This works great for Maxon's built-in assets databases. How would I retrieve all assets that belong to a category, even if they live in custom user-mounted databases?

      GetUserPrefsRepository() seems like the best bet, but it doesn't seem to include the many assets I have in custom DBs, and I don't see any other promising methods exposed in the SDK.

      edit: @ferdinand - Forked from Get All Assets in Category, although the other thread was named appropriately, it was more about general asset traversal and not the specific makeup of asset category trees.

      posted in Cinema 4D SDK python 2023
      d_keithD
      d_keith
    • RE: Get All Assets in Category

      Prints a list of Media Assets in the Cinema 4D Assets Browser to the Console [GitHub Gist]

      """Name-en-US: Print Media in Category
      Description-en-US: Prints a list of all media assets belonging to a category ID to the console.
      
      References:
      https://developers.maxon.net/forum/topic/14214/get-asset-from-asset-browser-python/4
      
      ## License
      
      MIT No Attribution
      
      Copyright 2022 Donovan Keith
      
      Permission is hereby granted, free of charge, to any person obtaining a copy of this
      software and associated documentation files (the "Software"), to deal in the Software
      without restriction, including without limitation the rights to use, copy, modify,
      merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
      permit persons to whom the Software is furnished to do so.
      
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
      INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
      PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
      HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
      OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
      SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
      """
      
      # Imports
      
      from importlib.metadata import metadata
      from multiprocessing.sharedctypes import Value
      import c4d
      import maxon
      from typing import Optional
      
      # Helper Functions
      
      
      def FindAssetsByType(type) -> Optional[list[maxon.Asset]]:
          repository = maxon.AssetInterface.GetUserPrefsRepository()
          if not repository:
              raise RuntimeError("Unable to get User Repository.")
      
          # Find Assets:
          # https://developers.maxon.net/docs/py/2023_2/modules/maxon_generated/frameworks/asset/interface/maxon.AssetRepositoryInterface.html?highlight=findassets#maxon.AssetRepositoryInterface.FindAssets
          assets = repository.FindAssets(
              assetType=maxon.AssetTypes.File(),
              aid=maxon.Id(),
              version=maxon.Id(),
              findMode=maxon.ASSET_FIND_MODE.LATEST,
              receiver=None
          )
      
          return assets
      
      
      def FindFileAssets():
          return FindAssetsByType(type=maxon.AssetTypes.File())
      
      
      def GetAssetCategory(asset: maxon.AssetInterface):
          if not asset:
              raise ValueError("No asset provided.")
      
          meta_data = asset.GetMetaData()
          if not meta_data:
              raise ValueError("Unable to get asset meta data.")
      
          return meta_data.Get(maxon.ASSETMETADATA.Category)
      
      
      def GetAssetName(asset: maxon.AssetDescription) -> Optional[str]:
          if not asset:
              return
      
          metadata: maxon.AssetMetaData = asset.GetMetaData()
          if metadata is None:
              return
      
          name: str = asset.GetMetaString(
              maxon.OBJECT.BASE.NAME, maxon.Resource.GetCurrentLanguage(), "")
      
          return name
      
      
      def IsAssetAnImage(asset: maxon.AssetDescription) -> bool:
          if not asset:
              return
      
          metadata: maxon.AssetMetaData = asset.GetMetaData()
          if metadata is None:
              return
      
          sub_type: maxon.Id = metadata.Get(maxon.ASSETMETADATA.SubType, None)
          if (sub_type is None or maxon.InternedId(sub_type) != maxon.ASSETMETADATA.SubType_ENUM_MediaImage):
              return False
      
          return True
      
      
      def GetCategoryIdFromUser() -> str:
          imperfections_id_string = "category@e780d216ed404547942dcbfcbbe009e5"
      
          category_id_string = c4d.gui.InputDialog(
              "Input Category ID", preset=imperfections_id_string)
          if not category_id_string:
              raise ValueError("Invalid ID String")
      
          return maxon.Id(category_id_string)
      
      
      def main():
          category_id = GetCategoryIdFromUser()
          file_assets = FindFileAssets()
          imperfections_assets = [asset for asset in file_assets if (
              IsAssetAnImage(asset) and GetAssetCategory(asset) == category_id)]
          for asset in imperfections_assets:
              print(GetAssetName(asset))
      
      
      if __name__ == "__main__":
          main()
      
      
      posted in Cinema 4D SDK
      d_keithD
      d_keith
    • Get All Assets in Category

      Question

      How can I retrieve all image assets that live in a given category (and sub-categories) in the assets browser?

      651f1f86-0df5-47c1-a993-ef189fa3c946-image.png

      Ideally, I could use something like Python glob syntax:

      glog.glob("./Textures/Imperfections/**/*.png")

      And get back a list of assets

      [
      "si-v1_deposits_01_15cm.png",
      "si-v1_desposits_02_15cm.png",
      ...
      ]
      

      Related

      • Asset Metadata - Search for Asset Categories by Path : Cinema 4D C++ SDK
      • Can I download a Tex in Asset Browser and add into select RS node mat? | PluginCafé
      • Get asset from asset browser python | PluginCafé
      • Get All Assets in Category | PluginCafé
      • Asset API : Cinema 4D C++ SDK
      • cinema4d_py_sdk_extended/scripts/05_modules/assets at master · PluginCafe/cinema4d_py_sdk_extended · GitHub
      posted in Cinema 4D SDK python
      d_keithD
      d_keith
    • RE: Looking for Scene Nodes Capsules Creators

      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:

      1. Prior programming expertise in C/C++/Python or similar languages.
      2. Interest and ability to actively participate in the lectures/homework projects.
      3. Availability to potentially create capsules for the Maxon Capsules Library in the future on a freelance basis.
      4. 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

      posted in General Talk
      d_keithD
      d_keith