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

    Link StoreColorToAOV to Existing AOV (Python)

    Cinema 4D SDK
    windows 2025
    3
    11
    1.4k
    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.
    • DunhouD
      Dunhou @itstanthony
      last edited by

      Hi @itstanthony ,

      To add AOV to Redshift, you need use redshift.RendererSetAOVs to add a c4d.redshift.RSAOV object to your RS videopost.

      But I think the Redshift module has never been released and is not supported. I used this module to make a very clumsy wrapper( Renderer ), although not very elegant, it works.

      Hope the Redshift team can release the API as soon as possible.

      Cheers~
      DunHou

      https://boghma.com
      https://github.com/DunHouGo

      1 Reply Last reply Reply Quote 0
      • I
        itstanthony
        last edited by

        Hi @DunHou,

        Thanks again for your insights and for sharing your experience with the Redshift module!

        Just to clarify, I’m trying to understand how to retrieve the list of available AOVs already preconfigured in the AOV Manager, find one named “BaseColor” for example, and set it as the value for the “AOV Name 0” parameter in a Store Color To AOV node.

        I know full script solutions aren’t expected here, but if you could point me to relevant Maxon IDs or documentation to manipulate these parameters, it would be greatly appreciated.

        Thanks again for your help!
        Cheers,
        Anthony

        2025-05-21 08_02_39-Script Manager.png

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

          Hey @itstanthony,

          Thank you for reaching out to us. Please have a look at Support Procedures: How to ask questions, what you provided there is not reproducible for us, I mostly have to guess what you are doing.

          The code snippet you gave us unfortunately does not make too much sense, I also do not really understand what you mean with the phrase 'dynamically linked' you use multiple times. When I interpret a bit, my understanding is that you want to set the string literal 'BasseColor' on the 'AOV Name 0' input port of a Store Color To AOV node.

          Your line of code makes very little sense to me. Not only that you use SetValue (which is for attributes) and not SetPortValue, you are passing a DescID to SetValue which makes absolutely no sense at all. This code will raise an exception and I must assume you used to AI to generate it. Please see Scope of Support: About AI-Supported Development.

          When you really want to use our low level Nodes API, I would recommend having a look at our Python code examples. But you then also have to be able "to swim to some degree on your own". Setting an AOV name string literal on a Store Color to AOV node, would go something like this:

          storeAovNode: maxon.GraphNode # A 'Store Color to AOV' node you got hold of somehow.
          
          # Get the name part of the '0' port group (when we look into the resource editor, we can see that
          # these are not actually port bundles but just groups, so there is no nesting going on here). Then
          # set the name of the AOV to 'BaseColor'.
          namePort: maxon.GraphNode = storeAovNode.GetInputs().FindChild(
              "com.redshift3d.redshift4c4d.nodes.core.storecolortoaov.aov_input0.name")
          namePort.SetPortValue("BaseColor")
          

          I would however more recommend graph descriptions for you, because to put it bluntly, when you already struggle with this relatively simple task in the Nodes API, you are probably not going to have a good time with all the hidden complexity there.

          Cheers,
          Ferdinand

          Result

          6de74506-1c56-4a2d-bed2-4047d15b0a15-image.png

          Code

          """Creates a Redshift material with a simple graph, where a "Store Color To AOV" node is used to
          connect a color to the AOV input of the material.
          """
          
          import c4d
          import maxon
          
          doc: c4d.documents.BaseDocument  # The currently active document.
          op: c4d.BaseObject | None  # The primary selected object in `doc`. Can be `None`.
          
          def main() -> None:
              """Called by Cinema 4D when the script is being executed.
              """
              # Creates a material with the name "Hello World" and gets its Redshift graph.
              graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
                  name="Hello World", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
          
              # The #GetGraph call above created an empty graph, so we now fill it with some content with a
              # graph description, this mostly works in a WYSIWYG way where you can just type out the things
              # you see (but you still have to read the docs).
              maxon.GraphDescription.ApplyDescription(graph,
                  {
                      # Define the outmost node of the graph, the output end node.
                      "$type": "Output",
                      # Connect to its "Surface" input port an "Store Color To AOV" node.
                      "Surface": {
                          "$type": "Store Color To AOV",
                          # Connect to its "AOV Input 0" input port a "Color" node and give it an ID, as we
                          # want to reuse that node later.
                          "AOV Input 0": {
                              "$type": "Color",
                              "Inputs/Color": maxon.Vector(1, 0, 0),
                              "$id": "AoVColor"
                          },
                          # Set the name of the AOV to "BaseColor", this assumes that an AOV with that name
                          # already exists, it will not create the AOV for you.
                          "AOV Name 0": "BaseColor",
                          # As the input of the "Store Color To AOV", define a "Standard Material" node which
                          # also uses #AoVColor as its base color.
                          "Beauty Input":  {
                              "$type": "Standard Material",
                              "Base/Color": "#AoVColor",
                          }
                      }
                  }
              )
          
          if __name__ == '__main__':
              main()

          MAXON SDK Specialist
          developers.maxon.net

          1 Reply Last reply Reply Quote 0
          • I
            itstanthony
            last edited by

            Hi @ferdinand,

            Thanks a lot for the clarification and fair enough about the snippet I posted earlier, I get that it might’ve caused some confusion.

            I really appreciate you taking the time to share both code examples. That gives me a much clearer starting point, and I’ll take the time to go through them and explore the API more carefully.

            Thanks again for your help!

            Cheers,
            Anthony

            DunhouD 1 Reply Last reply Reply Quote 0
            • DunhouD
              Dunhou @itstanthony
              last edited by

              hey @itstanthony ,

              I don't quite understand what you're saying about “find one named “BaseColor” for example, and set it as the value for the “AOV Name 0” parameter in a Store Color To AOV node.”

              I assume you want to find an AOV named BaseColor in the AOV Manager and change its settings? I'm not sure if this is what you want.

              relevant Maxon IDs or documentation to manipulate these parameters - you should look at redshift resource folder, but if you only need some id about rs aov, you can also check redshift_id.py

              https://boghma.com
              https://github.com/DunHouGo

              1 Reply Last reply Reply Quote 1
              • I
                itstanthony
                last edited by

                Thanks @Dunhou for the pointers to redshift_id.py; it really helped me identify the ports properly.
                Also, thanks to @ferdinand for the earlier script and support—that was extremely helpful.

                I can connect standard PBR ports to storeColorToAOV without issues, but EmissionColor and OverallTint don’t connect in my test.

                However, connecting these textures directly to the material works fine.
                Could the order of connections affect their recognition? Or do these ports need to be exposed (made visible/available) before they can be connected via storeColorToAOV?

                Thanks again for your help!

                Here is a minimal example reproducing the issue:

                import c4d
                import maxon
                
                REDSHIFT_NODE_SPACE_ID = "com.redshift3d.redshift4c4d.class.nodespace"
                
                def main() -> None:
                    graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
                        name="Simple PBR Material", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
                
                    maxon.GraphDescription.ApplyDescription(graph,
                        {
                            "$type": "Output",
                            "Surface": {
                                "$type": "Store Color To AOV",
                
                                "AOV Input 0": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(1, 1, 1),
                                    "$id": "BaseColorNode"
                                },
                                "AOV Name 0": "BaseColor",
                
                                "AOV Input 1": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(0.0, 0.0, 0.0),
                                    "$id": "MetallicNode"
                                },
                                "AOV Name 1": "Metallic",
                
                                "AOV Input 2": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(0.5, 0.5, 0.5),
                                    "$id": "RoughnessNode"
                                },
                                "AOV Name 2": "Roughness",
                
                                "AOV Input 3": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(0.5, 0.5, 1),
                                    "$id": "NormalMapNode"
                                },
                                "AOV Name 3": "Normal",
                
                                "AOV Input 4": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(1, 1, 1),
                                    "$id": "AOColorNode"
                                },
                                "AOV Name 4": "AO",
                
                                "AOV Input 5": {
                                    "$type": "Color",
                                    "Inputs/Color": maxon.Vector(0, 0, 0),
                                    "$id": "EmissiveColorNode"
                                },
                                "AOV Name 5": "Emissive",
                
                                "Beauty Input":  {
                                    "$type": "Standard Material",
                
                                    "Base/Color": "#BaseColorNode",
                                    "Base/Metalness": "#MetallicNode",
                                    "Reflection/Roughness": "#RoughnessNode",
                                    "Geometry/Bump Map": "#NormalMapNode",
                                    "Geometry/Overall Tint": "#AOColorNode",
                                    "Emission/Color": "#EmissiveColorNode",
                                }
                            }
                        }
                    )
                
                if __name__ == '__main__':
                    main()
                

                The error message:

                Traceback (most recent call last):
                  File "D:\C4D Setup\scripts\PBRtoAOV\PBRtoAOV_Final.py", line 73, in <module>
                    print("Matériau PBR avec AOV créé avec succès!")
                    ^^^^^^
                  File "D:\C4D Setup\scripts\PBRtoAOV\PBRtoAOV_Final.py", line 10, in main
                    maxon.GraphDescription.ApplyDescription(graph,
                  File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\frameworks\nodes.py", line 548, in ApplyDescription
                    res: DataDictionary = GraphDescription._ApplyDescription(
                                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  File "C:\Program Files\Maxon Cinema 4D 2025\resource\modules\python\libs\python311\maxon\decorators.py", line 495, in Auto
                    ExecStaticMethod(*args)
                Exception: The input port reference 'Geometry/Overall Tint' has a value which resolves to more than one node. In:
                { <- Error in this scope ->
                   $type: Standard Material
                   Base/Color: #BaseColorNode
                   Base/Metalness: #MetallicNode
                   Reflection/Roughness: #RoughnessNode
                   Geometry/Bump Map: #NormalMapNode
                   Geometry/Overall Tint: #AOColorNode
                   Emission/Color: #EmissiveColorNode
                }
                
                # Result with these lines commented:   
                # Geometry/Overall Tint: #AOColorNode
                # Emission/Color: #EmissiveColorNode
                

                83a21098-e6f5-4893-8c34-3ca4ced84c87-image.png

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

                  Hey @itstanthony,

                  That is a bit of a odd one. So, your code is absolutely correct, and I was just as puzzled as you are why this is not working.

                  First of all, I wrote the error "The input port reference 'Geometry/Overall Tint' has a value which resolves to more than one node" a bit misleadingly, as it also covers the case where the a port reference matches nothing. I just fixed the error handling there, in a future release (not the next one) of Cinema 4D, this error will now be as follows (this also contains other changes I applied before to error handling in general and for graph descriptions in particular):

                  Exception: (net.maxon.error.parsing) The node reference '#AOColorNode' passed to the port 'Geometry/Overall Tint' is ambigous as it resolves to '0' nodes. In:
                  
                  { <- Error in this scope (keys might appear out of order) ->
                     "$type": "Standard Material"
                     "Base/Color": "#BaseColorNode"
                     "Base/Metalness": "#MetallicNode"
                     "Reflection/Roughness": "#RoughnessNode"
                     "Geometry/Bump Map": "#NormalMapNode"
                     "Geometry/Overall Tint": "#AOColorNode"
                     "Emission/Color": "#EmissiveColorNode"
                  }
                  

                  As to why this happens, I have absolutely no clue. I just stepped through the graph description code, and everything is fine. After the #NormalMapNode, the Nodes API just simply does not return the nodes created earlier there within the same transaction, and you therefore do not find them. When you comment out these last two lines, you will end up with a graph which contains these two nodes with the defined IDs. There must be some caching optimization or something like this be going on, but I have no idea where this is happening, I will have to take a deeper look at that.

                  But luckily, graph descriptions offer a way out of that: Partial descriptions. With them you can avoid having to embed nodes that are referenced multiple times into the same description and with that in the same transaction.

                  It will then look liket this (note that naming the nodes and the changes to the IDs are not necessary, I was just cleaning up the graph into a state of how I would write that):

                  import c4d
                  import maxon
                  
                  def main() -> None:
                      graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
                          name="Simple PBR Material", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
                  
                      maxon.GraphDescription.ApplyDescription(graph,
                          [
                              {
                                  "$type": "Color",
                                  "Basic/Name": "Base Color",
                                  "Inputs/Color": maxon.Vector(1, 1, 1),
                                  "$id": "base_color"
                              },
                              {
                                  "$type": "Color",
                                  "Basic/Name": "Metallic",
                                  "Inputs/Color": maxon.Vector(0.0, 0.0, 0.0),
                                  "$id": "metallic_color"
                              },
                              {
                                  "$type": "Color",
                                  "Basic/Name": "Roughness",
                                  "Inputs/Color": maxon.Vector(0.5, 0.5, 0.5),
                                  "$id": "roughness_color"
                              },
                              {
                                  "$type": "Color",
                                  "Basic/Name": "Normal",
                                  "Inputs/Color": maxon.Vector(0.5, 0.5, 1),
                                  "$id": "normal_color"
                              },
                              {
                                  "$type": "Color",
                                  "Basic/Name": "AO",
                                  "Inputs/Color": maxon.Vector(1, 1, 1),
                                  "$id": "ao_color"
                              },
                              {
                                  "$type": "Color",
                                  "Basic/Name": "Emissive",
                                  "Inputs/Color": maxon.Vector(0, 0, 0),
                                  "$id": "emissive_color"
                              },
                              {
                              "$type": "Output",
                              "Surface": {
                                  "$type": "Store Color To AOV",
                  
                                  "AOV Input 0": "#base_color",
                                  "AOV Name 0": "BaseColor",
                  
                                  "AOV Input 1": "#metallic_color",
                                  "AOV Name 1": "Metallic",
                  
                                  "AOV Input 2": "#roughness_color",
                                  "AOV Name 2": "Roughness",
                  
                                  "AOV Input 3": "#normal_color",
                                  "AOV Name 3": "Normal",
                  
                                  "AOV Input 4": "#ao_color",
                                  "AOV Name 4": "AO",
                  
                                  "AOV Input 5": "#emissive_color",
                                  "AOV Name 5": "Emissive",
                  
                                  "Beauty Input": {
                                      "$type": "Standard Material",
                  
                                      "Base/Color": "#base_color",
                                      "Base/Metalness": "#metallic_color",
                                      "Reflection/Roughness": "#roughness_color",
                                      "Geometry/Bump Map": "#normal_color",
                                      "Geometry/Overall Tint": "#ao_color",
                                      "Emission/Color": "#emissive_color",
                                  }
                              }
                          }
                          ]
                      )
                  
                  if __name__ == '__main__':
                      main()
                  

                  Resulting in this graph:

                  3100eb22-5684-4b3c-8358-82ba5de9f19b-image.png

                  I hope this helps and cheers,
                  Ferdinand

                  MAXON SDK Specialist
                  developers.maxon.net

                  1 Reply Last reply Reply Quote 0
                  • I
                    itstanthony
                    last edited by

                    Hey @ferdinand

                    Thanks a lot for the clear explanation, it really helped. Your tip about partial descriptions completely fixed the issue.

                    Also, the clarification on that confusing error message makes a lot more sense now. It’s great that it’ll be improved in a future Cinema 4D update. I really appreciate your transparency and all the work you’re doing to make this easier.

                    Thanks again for your help and for taking the time to explain everything so clearly!

                    Cheers,
                    Anthony

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

                      Good to hear, one thing I forgot to mention, and you might not be aware of (and are not yet reflected in the GraphDescription manual or the Nodes API examples), is that you are writing there render space values, i.e., likely ACEScg values and not sRGB values as you usually would as a user.

                      See open_color_io_2025_2.py for the technical details, but to write values as sRGB, as you probably intend to, you would have to do this:

                      import c4d
                      import maxon
                      
                      doc: c4d.documents.BaseDocument # The active document
                      
                      def main() -> None:
                          converter: c4d.modules.render.OcioConverter = doc.GetColorConverter()
                          graph: maxon.NodesGraphModelRef = maxon.GraphDescription.GetGraph(
                              name="Simple PBR Material", nodeSpaceId=maxon.NodeSpaceIdentifiers.RedshiftMaterial)
                          
                          # With Cinema 4D 2025.2.0 OCIO handling became mandatory. To define maxon colors/vectors
                          # in sRGB space, we have to do this. For pure white/black this is inconsequential, as there an
                          # an sRGB value is same value as in our standard render space of Cinema 4D: ACEScg. But the
                          # The sRGB color (1, 0, 0) is for example the ACEScg value (0.61, 0.07, 0.02).
                          cWhite: c4d.Vector = converter.TransformColor(
                              c4d.Vector(1), c4d.COLORSPACETRANSFORMATION_OCIO_SRGB_TO_RENDERING)
                          mWhite: maxon.Color = maxon.Color(cWhite.x, cWhite.y, cWhite.z)
                      
                          cBlack: c4d.Vector = converter.TransformColor(
                              c4d.Vector(0), c4d.COLORSPACETRANSFORMATION_OCIO_SRGB_TO_RENDERING)
                          mBlack: maxon.Color = maxon.Color(cBlack.x, cBlack.y, cBlack.z)
                      
                          cGray: c4d.Vector = converter.TransformColor(
                              c4d.Vector(0.5), c4d.COLORSPACETRANSFORMATION_OCIO_SRGB_TO_RENDERING)
                          mGray: maxon.Color = maxon.Color(cGray.x, cGray.y, cGray.z)
                      
                          cPurple: c4d.Vector = converter.TransformColor(
                              c4d.Vector(0.5, 0.5, 1.0), c4d.COLORSPACETRANSFORMATION_OCIO_SRGB_TO_RENDERING)
                          mPurple: maxon.Color = maxon.Color(cPurple.x, cPurple.y, cPurple.z)
                      
                          # Clear the graph to start fresh
                      
                          maxon.GraphDescription.ApplyDescription(graph,
                              [
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "Base Color",
                                      "Inputs/Color": mWhite,
                                      "$id": "base_color"
                                  },
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "Metallic",
                                      "Inputs/Color": mBlack,
                                      "$id": "metallic_color"
                                  },
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "Roughness",
                                      "Inputs/Color": mGray,
                                      "$id": "roughness_color"
                                  },
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "Normal",
                                      "Inputs/Color": mPurple,
                                      "$id": "normal_color"
                                  },
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "AO",
                                      "Inputs/Color": mWhite,
                                      "$id": "ao_color"
                                  },
                                  {
                                      "$type": "Color",
                                      "Basic/Name": "Emissive",
                                      "Inputs/Color": mBlack,
                                      "$id": "emissive_color"
                                  },
                                  {
                                  "$type": "Output",
                                  "Surface": {
                                      "$type": "Store Color To AOV",
                      
                                      "AOV Input 0": "#base_color",
                                      "AOV Name 0": "BaseColor",
                      
                                      "AOV Input 1": "#metallic_color",
                                      "AOV Name 1": "Metallic",
                      
                                      "AOV Input 2": "#roughness_color",
                                      "AOV Name 2": "Roughness",
                      
                                      "AOV Input 3": "#normal_color",
                                      "AOV Name 3": "Normal",
                      
                                      "AOV Input 4": "#ao_color",
                                      "AOV Name 4": "AO",
                      
                                      "AOV Input 5": "#emissive_color",
                                      "AOV Name 5": "Emissive",
                      
                                      "Beauty Input": {
                                          "$type": "Standard Material",
                      
                                          "Base/Color": "#base_color",
                                          "Base/Metalness": "#metallic_color",
                                          "Reflection/Roughness": "#roughness_color",
                                          "Geometry/Bump Map": "#normal_color",
                                          "Geometry/Overall Tint": "#ao_color",
                                          "Emission/Color": "#emissive_color",
                                      }
                                  }
                              }
                              ]
                          )
                      
                      if __name__ == '__main__':
                          main()
                      

                      MAXON SDK Specialist
                      developers.maxon.net

                      1 Reply Last reply Reply Quote 0
                      • I
                        itstanthony
                        last edited by

                        Thank you very much for pointing this out.
                        I knew that Cinema 4D’s UI includes a “Linear Numeric Values” button to switch between color spaces, but I wasn’t aware of how to properly handle this conversion in code until you showed me.

                        Your reference to the open_color_io_2025_2.py example is really helpful for better understanding how to manage color spaces in scripts.

                        Thanks again for the great insight!

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