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
    • Login

    Discovering Channel Identifiers of a Substance Shader

    Cinema 4D SDK
    python r25
    1
    1
    274
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • ferdinandF
      ferdinand
      last edited by ferdinand

      Dear community,

      The following code example demonstrates how to discover the channel identifiers of the "Channel" parameter of a Substance shader, so that the channel can be changed programmatically for a substance asset unknown at the time of writing the script.

      9b05b0e3-39d3-471b-855f-4c0e1e3dcdea-image.png

      This question reached us via mail, but since answering it requires no confidential data, we are sharing the solution here. The "trick" is to traverse the description of the shader, as these identifiers depend on the substance.

      Cheers,
      Ferdinand

      The result (the example script will randomly select a channel, but with the data provided, channels can also be selected by their name or a substring match as for example "diffuse"):

      substance_channels.gif

      The code:

      """Example for discovering the channels of a Substance shader.
      
      The solution is a bit hacky by traversing the description of the shader but should work.
      """
      
      import c4d
      import random
      
      def GetSubstanceChannels(shader: c4d.BaseShader) -> dict[int:str]:
          """Returns all channels of the substance loaded into #shader as a dictionary of id-label pairs.
          """
          if not isinstance(shader, c4d.BaseShader) or (shader.GetType() != c4d.Xsubstance):
              raise TypeError(f"{shader} is not a substance shader.")
      
          # Get the data for the "Channel" dropdown element from the description of the shader.
          description = shader.GetDescription(c4d.DESCFLAGS_DESC_NONE)
          channelData = description.GetParameter(c4d.SUBSTANCESHADER_CHANNEL)
      
          # Get the elements in the drop down menu.
          elements = channelData[c4d.DESC_CYCLE]
          if not isinstance(elements, c4d.BaseContainer):
              raise RuntimeError(f"Could not access Channel parameter description in {shader}.")
          
          # Pack the data into a dictionary and return it.
          return {id: label for id, label in elements}
      
      def main(doc: c4d.documents.BaseDocument):
          """
          """
          # Get the active material.
          material = doc.GetActiveMaterial()
          if not isinstance(material, c4d.BaseMaterial):
              raise RuntimeError("Please select a material.")
      
          # Get the substance shader loaded into the color channel of the material.
          shader = material[c4d.MATERIAL_COLOR_SHADER]
          channelData = GetSubstanceChannels(shader)
      
          for id, label in channelData.items():
              print (f"id: {id}, label: {label}")
      
          # To select a specific channel, one would have to do a string comparison here to find keywords as
          # "Color" or "Metal" in the channel label. I am just randomly selecting a channel instead.
          channelId = random.choice(tuple(channelData.keys()))
          channelLabel = channelData[channelId]
          print (f"Setting substance to channel '{channelLabel}({channelId})'")
          shader[c4d.SUBSTANCESHADER_CHANNEL] = channelId
      
          c4d.EventAdd()
      
      if __name__=='__main__':
          main(doc)
      

      MAXON SDK Specialist
      developers.maxon.net

      1 Reply Last reply Reply Quote 3
      • First post
        Last post