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

    connect material to reference node

    Cinema 4D SDK
    2025 python
    2
    6
    708
    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.
    • W
      wen
      last edited by

      Hi Forum,

      I'm trying to connect a material to a reference node. Icouldn't find any examples with the reference node in the code examples. I'm assuming i do this with SetValue() similar to setting the name, like this:

      reference_node.SetValue(maxon.NODE.BASE.NAME, material_reference)
      

      So I'm trying things like:

      reference_input_port: maxon.GraphNode = reference_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.node.reference")
      reference_input_port.SetValue(maxon.NODE.BASE.CONNECTNODE, find_reference_material(reference_node))
      

      unfortunately that doesn't work as it's the wrong attr and crashes cinema4d.
      Can somebody help me and tell how i can findout what the attr is I should be using?

      Thanks,
      Bart

      ferdinandF 1 Reply Last reply Reply Quote 0
      • ferdinandF
        ferdinand @wen
        last edited by ferdinand

        Hey @wen,

        Thank you for reaching out to us. First of all, I assume you are talking about this:

        7e309149-f6f8-4361-92e7-29407d56a7eb-image.png

        I really struggle to make sense of your code example. I assume what you want to do, is set the value on a 'Material' input port of a Reference node. This is first of all not the same as setting the name of a node (which is an attribute and not a port). And you seem to use there the node template ID of the reference node to try to find the material port (i.e., the type ID of the node, not the ID of the port).

        In practice it would go something like this:

        referenceNode: maxon.GraphNode  # The true node for some RS Reference node.
        material: c4d.NodeMaterial      # Some RS node material to link/ref.
        nimbusRef: maxon.NimbusBaseRef  # Some nimbus reference you got hold of somehow.
        
        # The 'Material' input port of #referenceNode.
        materialInPort: maxon.GraphNode = referenceNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.node.reference.object")
        
        # Set the UUID of #material on the 'Material' input port of the reference node.
        materialUuid: maxon.Uuid = nimbusRef.BaseList2DToUuid(material)
        materialInPort.SetPortValue(materialUuid)
        

        Cheers,
        Ferdinand

        MAXON SDK Specialist
        developers.maxon.net

        W 1 Reply Last reply Reply Quote 0
        • W
          wen @ferdinand
          last edited by

          @ferdinand
          Hi Ferdinant,
          Thanks for helping out. I am however still not able to achieve what I want to do with your example. I see i forgot to specify the "object" port resulting in a null value.

          I am however still running in to the same error as before:

          Traceback (most recent call last):
            File "C:\C4D_LIB\scripts\SetupMUS.py", line 180, in <module>
              new_materials.append(create_redshift_material(line, keys))
                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            File "C:\C4D_LIB\scripts\SetupMUS.py", line 130, in create_redshift_material
              reference_input_port.SetValue(materialUuid)
          TypeError: GraphNode.SetValue() missing 1 required positional argument: 'value'
          

          I did not want to post the whole script as it's a bit messy still but perhaps it helps to clarify what I'm doing.
          I'm importing an excelsheet containing the names of the materials i want to setup. The creating of the materials works. I'm running into trouble trying to link other materials to the reference nodes i created.

          import c4d  # Import the Cinema 4D module
          import maxon
          import pandas as pd  # Import the pandas library for data manipulation
          import numpy as np  # Import the numpy library for numerical operations
          
          # Reference to the active Cinema 4D document
          doc: c4d.documents.BaseDocument
          
          def read_mus(file_path: str) -> tuple:
              """Reads an Excel file and returns its content as a list of lists and the column keys."""
          
              df = pd.read_excel(file_path)  # Read the Excel file into a pandas DataFrame
              keys = df.keys()
              mus = df.apply(lambda row: [int(value) if isinstance(value, np.int64) else value for value in row], axis=1).tolist() #process each row (axis=1) of the DataFrame and convert certain values to integers.
              if is_nan(mus):
                  c4d.gui.MessageDialog("MUS contains empty fields. Please fill those with a material.")
                  raise RuntimeError("{0} has NaN values".format(file_path))
              else:
                  return mus, keys
          
          def is_nan(list_of_lists):
              """check list for NaN values"""
              flattened_list = [item for sublist in list_of_lists for item in sublist]
              return (pd.Series(flattened_list).isna().any())
          
          
          def find_reference_material(reference_node) -> c4d.Material:
              scene_materials  = doc.GetMaterials()
              for mat in scene_materials:
                  if mat.GetName() == reference_node.GetValue(maxon.NODE.BASE.NAME):
                      return mat
          
          
          def get_materialUuid():
              pass
          
          def create_redshift_material(mus_line: list, keys: list) -> None:
              """Creates a Redshift material based on the provided data."""
              material_id, material_name, add_print, *cima_materials = mus_line
              cimas = keys[3:]
          
              # Define the node asset IDs for various Redshift nodes
              node_ids = {
                  "output": maxon.Id("com.redshift3d.redshift4c4d.node.output"),
                  "standard_material": maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial"),
                  "reference": maxon.Id("com.redshift3d.redshift4c4d.node.reference"),
                  "userdata_int": maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rsuserdatainteger"),
                  "shader_switch": maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rsshaderswitch"),
                  "material_blender": maxon.Id("com.redshift3d.redshift4c4d.nodes.core.materialblender"),
                  "texture": maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler"),
              }
          
              # Create a new material
              material: c4d.BaseMaterial = c4d.BaseMaterial(c4d.Mmaterial)
              if not material:
                  raise MemoryError("Material creation failed.")
          
              material.SetName("{0}_{1}".format(str(material_id).zfill(2),material_name ))
          
              # Create an empty graph for the Redshift material space
              node_material: c4d.NodeMaterial = material.GetNodeMaterialReference()
              graph: maxon.GraphModelRef = node_material.CreateEmptyGraph(maxon.Id("com.redshift3d.redshift4c4d.class.nodespace"))
              if graph.IsNullValue():
                  raise RuntimeError("Could not add Redshift graph to material.")
          
              # Start an undo operation
              if not doc.StartUndo():
                  raise RuntimeError("Could not start undo stack.")
          
              # Insert the material into the document
              doc.InsertMaterial(material)
              if not doc.AddUndo(c4d.UNDOTYPE_NEWOBJ, material):
                  raise RuntimeError("Could not add undo item.")
          
              # Define user data for the transaction
              user_data: maxon.DataDictionary = maxon.DataDictionary()
              user_data.Set(maxon.nodes.UndoMode, maxon.nodes.UNDO_MODE.ADD)
          
              # Retrieve the current node space Id
              nodespaceId = c4d.GetActiveNodeSpaceId()
          
              # Retrieve the Nimbus reference for a specific nodeSpace
              nimbusRef = material.GetNimbusRef(nodespaceId)
              if nimbusRef is None:
                  raise ValueError("Cannot retrieve the nimbus ref for that node space")
          
              # Begin a transaction to modify the graph
              with graph.BeginTransaction(user_data) as transaction:
                  out_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["output"])
                  out_surface_port: maxon.GraphNode = out_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.node.output.surface")
                  out_node.SetValue(maxon.NODE.BASE.NAME, material_name)
                  out_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.node.output.materialid").SetPortValue(material_id)
          
                  # Add user data integer node
                  userdata_int_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["userdata_int"])
                  userdata_int_output_port: maxon.GraphNode = userdata_int_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsuserdatainteger.out")
                  userdata_int_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsuserdatainteger.attribute").SetPortValue("CIMA")
                  userdata_int_node.SetValue(maxon.NODE.BASE.NAME, "CIMA SELECTOR")
          
                  # Add shader switch node
                  shader_switch_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["shader_switch"])
                  shader_switch_select_port: maxon.GraphNode = shader_switch_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsshaderswitch.selector")
                  shader_switch_output_port: maxon.GraphNode = shader_switch_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsshaderswitch.outcolor")
          
          
                  print_added = False
                  # Create and connect MaterialBlender and Reference nodes
                  for num, material_reference in enumerate(cima_materials):
                      scaffold_node : maxon.GraphNode = graph.AddChild(maxon.Id(), maxon.Id("net.maxon.node.scaffold"))
                      scaffold_node.SetValue(maxon.NODE.BASE.NAME, cimas[num])
          
                      material_blender_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["material_blender"])
                      material_blender_node.SetValue("net.maxon.node.attribute.scaffoldid", scaffold_node.GetId())
          
                      reference_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["reference"])
                      reference_node.SetValue("net.maxon.node.attribute.scaffoldid", scaffold_node.GetId())
                      reference_node.SetValue(maxon.NODE.BASE.NAME, cima_materials[num])
          
                      #use "material_blender_node.GetInputs().GetChildren()" to list al the ports
                      material_blender_base_port: maxon.GraphNode = material_blender_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.materialblender.basecolor")
                      material_blender_blend1_port: maxon.GraphNode = material_blender_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.materialblender.blendcolor1")
                      material_blender_material1_port: maxon.GraphNode = material_blender_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.materialblender.layercolor1")
                      material_blender_output_port: maxon.GraphNode = material_blender_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.materialblender.out")
          
                      reference_output_port: maxon.GraphNode = reference_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.node.reference.output.")
                      reference_input_port: maxon.GraphNode = reference_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.node.reference.object")
                      reference_output_port.Connect(material_blender_base_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
          
                      materialUuid: maxon.Uuid = nimbusRef.BaseList2DToUuid(find_reference_material(reference_node))
                      #reference_input_port.SetValue(materialUuid)
          
          
                      shader_switch_shader_port: maxon.GraphNode = shader_switch_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsshaderswitch.shader"+str(num))
          
                      if add_print:
                          if not print_added:
                                      print_added = True
                                      texture_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["texture"])
                                      texture_output_port: maxon.GraphNode = texture_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.texturesampler.outcolor")
                          reference_node: maxon.GraphNode = graph.AddChild(maxon.Id(), node_ids["reference"])
                          reference_node.SetValue(maxon.NODE.BASE.NAME, "Print Color")
                          reference_node.SetValue("net.maxon.node.attribute.scaffoldid", scaffold_node.GetId())
                          reference_output_port: maxon.GraphNode = reference_node.GetOutputs().FindChild("com.redshift3d.redshift4c4d.node.reference.output.")
                          texture_output_port.Connect(material_blender_blend1_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
                          reference_output_port.Connect(material_blender_material1_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
          
                      material_blender_output_port.Connect(shader_switch_shader_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
                      material_blender_node.SetValue(maxon.NODE.BASE.NAME, cimas[num])
          
                              # Optionally add texture node
          
                  # Connect the nodes
                  userdata_int_output_port.Connect(shader_switch_select_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
                  shader_switch_output_port.Connect(out_surface_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
          
                  # Commit the transaction
                  transaction.Commit()
          
          
              # End the undo operation
              if not doc.EndUndo():
                  raise RuntimeError("Could not end undo stack.")
          
              return material
          
          def alert_material_names(message, materials):
              """Pops up a message window with the message followed by the names of the materials"""
              for mat in materials:
                  message += (mat.GetName()+"\n")
              c4d.gui.MessageDialog(message)
          
          # Entry point of the script
          """Only run this if this script called directly and not imported"""
          if __name__ == "__main__":
              excel_path = r"C:\Users\wen\Desktop\MUS.xlsx"  # Specify the path to the Excel file
              #excel_path = c4d.storage.LoadDialog(title = "Load EXCEL Document")
              mus, keys = read_mus(excel_path)
              new_materials = []
              for line in mus:
                  new_materials.append(create_redshift_material(line, keys))
              alert_material_names("The following materials have been created: \n", new_materials)
          
              # Refresh the Cinema 4D interface
              c4d.EventAdd()
          
          ferdinandF 1 Reply Last reply Reply Quote 0
          • ferdinandF
            ferdinand @wen
            last edited by ferdinand

            Hey @wen

            It should be SetPortValue and not SetValue, my bad. I.e., that is why I pointed out that this is not the same as setting the name of a node (an attribute, i.e., SetValue) but setting the value of a port (SetPortValue). I fixed my example above. But just for clarity, I wrote my code example above blind since I had no executable code. And I still have not since this new code of yours is neither executable for us (it uses numpy and pandas (we will not touch 3rd party lib code) and some excel document).

            See create_redshift_nodematerial_2024.py for a general example for how to create RS node materials, including how to set a port to a literal. There are also Graph Descriptions which might make things a bit simpler.

            Cheers,
            Ferdinand

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • W
              wen
              last edited by

              Hi Ferdinant,
              I didn't understand what you meant regarding the difference of setting an attribute and a port. I was trying to set the object slot as an attr in SetValue. But the presence of a separate command makes it clear. I changed SetValue to SetPortValue and it works.
              I found the examples on github before but the graph descriptions page is new to me. I'll have a look.
              Thanks!
              Bart

              1 Reply Last reply Reply Quote 0
              • ferdinandF
                ferdinand
                last edited by ferdinand

                Hey,

                Node attributes are things that are directly present on a GraphNode. You could also call them fields, because a GraphNode like many things in our API has qualities of a DataDictionay, i.e., you can write values over keys into it, just like for Python's own dict.

                Such node attributes (for true nodes) are then rendered out quite similarly to port values in the Attribute Manager but they are fundamentally different from ports, as users cannot drive their value with a connection, i.e., the value of a port of another node. And a port also always requires another GraphNode attached to the true node¹ for which they are a port, while the attribute sits directly on the true node.

                An attribute, e.g., the name of a node, will also never show up in the node representation in the graph. In the screen below I for example right clicked on the RS Reference node and invoked Add Input > All; but things like Name, Color, Show Preview etc. are alle not listed.

                174097cf-d777-49d2-b0ea-0057c8474a5e-image.png

                That is because they are all attributes and not ports. There is unfortunately no absolute rule like 'everything in Basic is attributes and everything else is ports'. While it holds (for now) true that everything in Basic is an attribute, especially Redshift also puts attributes into other tabs. They usually only appear in more complex scenarios when dealing with port bundles and variadic ports, but they exist.

                And just for clarity, attributes do not only exist on nodes that are what our API calls 'true nodes'¹, but also on other GraphNode types, e.g., a GraphNode representing a port. It is just that they are usually not rendered out to the GUI there and more of an internal nature (nodes have a lot of internal attributes).

                Cheers,
                Ferdinand

                ¹ GraphNode instances that represent what the end user would consider a 'node', e.g., the RS Reference node, and with that excluding GraphNode instances that for example represent an input port.

                MAXON SDK Specialist
                developers.maxon.net

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