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

    Adding nodes to existing material won't auto arrange the node editor

    Cinema 4D SDK
    2024 python
    3
    7
    1.3k
    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.
    • D
      danielsian
      last edited by

      Hi there, I'm adding nodes to an existing node material and I'm not being able to get a nice and organized result in the node editor, even after all connections are done.
      However, if I create a material from scratch, then add the nodes and connect them the same way, the node editor finds an optimized way to organize the graph and the result is nice as expected.

      So, these are the 2 approaches and the result:
      1- add and connect nodes within an existing material - the new nodes are overlapping each other
      2- create the material and then add and connect the nodes within it - the new nodes are nicely organized

      How can I get a nice result with approach 1?

      I used the example create_redshift_nodematerial_2024.py for the approach 2.
      For the approach 1 I'm commenting the line:

      createRSMaterial()
      
      import c4d
      import maxon
      
      doc: c4d.documents.BaseDocument # The active document.
      
      def createRSMaterial():
        
        urlTexRust: maxon.Url = maxon.Url(r"asset:///file_edb3eb584c0d905c")
        urlTexSketch: maxon.Url = maxon.Url(r"asset:///file_3b194acc5a745a2c")
      
        # The node asset IDs
        idTextureNode: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
        idMixNode: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rscolormix")
        idRsStandardMaterial: maxon.Id = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial")
      
        # Instantiate a material, get its node material and the graph for the RS material space.
        material: c4d.BaseMaterial = c4d.BaseMaterial(c4d.Mmaterial)
        if not material:
          raise MemoryError(f"{material = }")
      
        nodeMaterial: c4d.NodeMaterial = material.GetNodeMaterialReference()
        graph: maxon.GraphModelRef = nodeMaterial.CreateDefaultGraph(
          maxon.Id("com.redshift3d.redshift4c4d.class.nodespace"))
        if graph.IsNullValue():
          raise RuntimeError("Could not add RS graph to material.")
      
        # Get the Standard Material node
        result: list[maxon.GraphNode] = []
        maxon.GraphModelHelper.FindNodesByAssetId(graph, idRsStandardMaterial, True, result)
        if len(result) < 1:
          raise RuntimeError("Could not find RS Standard node in material.")
        standardNode: maxon.GraphNode = result[0]
      
        with graph.BeginTransaction() as transaction:
          # Add two texture nodes and a blend node to the graph.
          rustTexNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idTextureNode)
          sketchTexNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idTextureNode)
          mixNode: maxon.GraphNode = graph.AddChild(maxon.Id(), idMixNode)
      
          mixAmount: maxon.GraphNode = mixNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.rscolormix.mixamount")
          mixAmount.SetDefaultValue(0.5)
      
          # Get the ports and set the values
          pathRustPort: maxon.GraphNode = rustTexNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0").FindChild("path")
          pathSketchPort: maxon.GraphNode = sketchTexNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0").FindChild("path")
          pathRustPort.SetDefaultValue(urlTexRust)
          pathSketchPort.SetDefaultValue(urlTexSketch)
      
          # Get the color output ports of the two texture nodes and the color blend node.
          rustTexColorOutPort: maxon.GraphNode = rustTexNode.GetOutputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.texturesampler.outcolor")
          sketchTexColorOutPort: maxon.GraphNode = sketchTexNode.GetOutputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.texturesampler.outcolor")
          mixColorOutPort: maxon.GraphNode = mixNode.GetOutputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.rscolormix.outcolor")
      
          # Get the ports
          mixInput1Port: maxon.GraphNode = mixNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.rscolormix.input1")
          mixInput2Port: maxon.GraphNode = mixNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.rscolormix.input2")
          stdBaseColorInPort: maxon.GraphNode = standardNode.GetInputs().FindChild(
            "com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_color")
      
          # Connect the nodes
          rustTexColorOutPort.Connect(mixInput1Port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
          sketchTexColorOutPort.Connect(mixInput2Port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
          mixColorOutPort.Connect(stdBaseColorInPort, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
      
          transaction.Commit()
      
        doc.InsertMaterial(material)
        doc.SetActiveMaterial(material)
        c4d.EventAdd()
      
      def main():
        # create a RS material
        createRSMaterial()
      
        # Get the material
        material = doc.GetActiveMaterial()
      
        # Check if the material exists and is a node material
        if not material or not material.IsInstanceOf(c4d.Mmaterial):
          return
      
        # Get the node material
        nodeMaterial = material.GetNodeMaterialReference()
      
        nodespaceId = c4d.GetActiveNodeSpaceId()
      
        rsNodeSpaceId = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
      
        if nodespaceId != rsNodeSpaceId:
          return
      
        # Get the graph
        try: graph = nodeMaterial.GetGraph(nodespaceId)
        except: return
      
        # Set the asset IDs
        idTextureNode = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
        idColorCorrection = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rscolorcorrection")
      
      
        # Find all the texture nodes
        tex_nodes = []
        maxon.GraphModelHelper.FindNodesByAssetId(graph, idTextureNode, True, tex_nodes)
      
        if not tex_nodes:
          return
      
        for node in tex_nodes:
          for out_port in node.GetOutputs().GetChildren():
            for dest_port, wires in out_port.GetConnections(maxon.PORT_DIR.OUTPUT, None, maxon.Wires.All(), maxon.WIRE_MODE.ALL):
              with graph.BeginTransaction() as transaction:
                # Remove existing connection
                maxon.GraphModelHelper.RemoveConnection(out_port, dest_port)
      
                # Create node
                colorCorrectionNode = graph.AddChild(maxon.Id(), idColorCorrection)
      
                # Get the ports
                cc_inPort = colorCorrectionNode.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rscolorcorrection.input")
                cc_outPort = colorCorrectionNode.GetOutputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rscolorcorrection.outcolor")
      
                # Connect the nodes
                cc_outPort.Connect(dest_port, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
                out_port.Connect(cc_inPort, modes=maxon.WIRE_MODE.NORMAL, reverse=False)
                transaction.Commit()
      
        doc.InsertMaterial(material)
        c4d.EventAdd()
      
        
      if __name__ == "__main__":
        main()
      
      DunhouD 1 Reply Last reply Reply Quote 0
      • DunhouD
        Dunhou @danielsian
        last edited by

        Hi @danielsian ,
        As @m_adam said, this is the "auto-layouter" didn't work, if we add something to existed materials, the new node will set at the center of the graph editor, seems nothing we can do here, one bad solution is call the "Arrange All Nodes", but this will break the existed layout, hope it will be a better solution asap.

        Cheers~
        DunHou

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

        1 Reply Last reply Reply Quote 0
        • D
          danielsian
          last edited by

          Thanks @Dunhou, I appreciate your reply confirms what seems to be a bug, and I wonder if @m_adam could confirm it based on the working code I provided, which demonstrates the issue in a simple way.

          1 Reply Last reply Reply Quote 0
          • M
            m_adam
            last edited by

            Hi thanks a lot, this is most likely the same bug that @Dunhou reported but that I was never able to reproduce.
            I will forward that to the responsible dev.

            Cheers,
            Maxime.

            MAXON SDK Specialist

            Development Blog, MAXON Registered Developer

            1 Reply Last reply Reply Quote 0
            • D
              danielsian
              last edited by

              Thanks @m_adam !

              1 Reply Last reply Reply Quote 0
              • D
                danielsian
                last edited by

                Hey guys, how is it going with this BUG?
                Is it gonna be fixed soon?
                Thanks

                1 Reply Last reply Reply Quote 0
                • M
                  m_adam
                  last edited by

                  Hi sadly no news, except we kind of know the issue, sadly this issue is somehow by design and will require an architectural change.

                  While we still want to fix this issue, everything is about priority and this is not a urgent priority at the moment so I can't tell you when it will be fixed. But it's on our list.
                  Cheers,
                  Maxime.

                  MAXON SDK Specialist

                  Development Blog, MAXON Registered Developer

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