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

    Hiding Node Previews for all Redshift Nodes in Redshift Materials

    Cinema 4D SDK
    python 2025
    3
    6
    202
    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_keithD
      d_keith
      last edited by d_keith

      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()
      
      1 Reply Last reply Reply Quote 0
      • d_keithD
        d_keith
        last edited by d_keith

        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()
        
        DunhouD 1 Reply Last reply Reply Quote 0
        • DunhouD
          Dunhou @d_keith
          last edited by

          Hey @d_keith ,

          I'll hide all nodes preview and un-hide the output preview, I write two examples.

          • First one uses my Renderer lib, if you installed boghma plugin manager, it will out of the box to use.
          • Second one uses maxon codes, it should work more widely.

          Cheers~
          DunHou

          Renderer

          import c4d
          import maxon
          import Renderer
          
          def mylib_solution():
              doc = c4d.documents.GetActiveDocument()
              for mat in doc.GetMaterials():
                  with Renderer.EasyTransaction(mat) as tr:
                      tr.FoldPreview(tr.GetAllShaders(), False)
                      tr.FoldPreview(tr.GetOutput(), True)
              c4d.EventAdd()
          

          maxon

          import c4d
          import maxon
          def maxon_solution():
              doc = c4d.documents.GetActiveDocument()
              for mat in doc.GetMaterials():
                  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()
          
              c4d.EventAdd()
          

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

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

            Hey @d_keith,

            Thank you for your question. Please remember our forum rules, which prohibit diary style postings. I understand that you did this under the banner of being documentative, but too much noise in a posting can confuse future readers.

            From Support Procedures: How to Ask Questions:

            Singular Posting: Users often discover additional information or even a solution before we can answer. Please consolidate your questions into a singular posting by editing your last posting. It is otherwise not only for us but also for future readers hard to follow the topic. Once we replied to a topic you are of course then free to reply in a new posting. But when you have follow-up questions, all information should again be consolidated in the most recent posting until we answer.

            The preview of a material graph node is just an attribute with the ID net.maxon.node.base.preview. You would have to iterate over all nodes in a graph and set it to False (just as in @Dunhou's maxon example, but you could condense there some code using maxon.GraphDescription.GetMaterialGraphs). You can use @Dunhou's Nodes API wrapper which provides some nice abstractions for users who do not want to go too deep into the Nodes API, but at the same time stay within the domain of it.

            An alternative would be Graph Descriptions, specifically their graph query feature which is more aimed at the technical complexity artists are usually more comfortable with. I have provided an example below.

            Cheers,
            Ferdinand

            PS: I just realized that there is a typo in one of the graph query code examples in the manual, it should be of course QUERY_FLAGS.MATCH_MAYBE and not QUERY_FLAGS.MATCH_MATCH_MAYBE, will fix that 🙂

            Result

            The state of a document with three very simple Redshift materials after the graph description has been bulk applied, all nodes have a collapsed preview:
            b4b54b82-ab97-400a-85ff-0e3fc0031912-image.png

            Code

            """Provides a simple example for a graph description query to modify all nodes in all Redshift 
            material graphs in a document which have a set of specific properties, here at the example of 
            the "Show Preview" property.
            """
            
            import c4d
            import maxon
            
            doc: c4d.documents.BaseDocument # The currently active document.
            
            def main() -> None:
                """
                """
                # For each material in the document which has a Redshift material node space, get its graph and ...
                for graph in maxon.GraphDescription.GetMaterialGraphs(doc, maxon.NodeSpaceIdentifiers.RedshiftMaterial):
                    # ... apply a graph description to the graph, that ...
                    maxon.GraphDescription.ApplyDescription(
                        graph, {
                            # ... defines a query for the passed graph that can match many to none nodes ...
                            "$query": {
                                "$qmode": (maxon.GraphDescription.QUERY_FLAGS.MATCH_ALL | 
                                           maxon.GraphDescription.QUERY_FLAGS.MATCH_MAYBE),
                                # ... where each matched node has the following properties (we could add here
                                # more properties) ...
                                "Basic/Show Preview": True # Node has its preview enabled.
                            },
                            # ... so that we can then write into each node new properties, here we simply disable
                            # the preview for all matched nodes.
                            "Basic/Show Preview": False
                        }
                    )
                
                # Push an update event (not really necessary in this case, but good practice at the end of scripts)
                c4d.EventAdd()
                    
            if __name__=='__main__':
                main()
            

            MAXON SDK Specialist
            developers.maxon.net

            1 Reply Last reply Reply Quote 0
            • d_keithD
              d_keith
              last edited by

              @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()
              
              
              1 Reply Last reply Reply Quote 0
              • ferdinandF
                ferdinand
                last edited by ferdinand

                Hey,

                there is no NOT in the query syntax, if that is what you are asking for, although that could be a cool idea. What you would have to do, is apply two partial descriptions in one ApplyDescription call, one that first selects all nodes with an enabled preview and disables it, and then one which selects the end node and disables its preview. Or just carry out two successive ApplyDescription calls.

                Cheers,
                Ferdinand

                PS: You can of course do whatever you want with your module attributes, and things like __author__, __license__, __email__, etc are not formally standardized, but the quasi standard module attribute is called __author__, there is no __authors__. When you have multiple authors, you usually put them semicolon separated into the __author__ field. When you use __authors__, tools which support that quasi standard won't find your attribute and the whole purpose of these module attributes is being machine readable, as you could otherwise also just write things into the doc string of the module.

                See:
                The original Python Enhancement Proposal by Guido van Rossum (the now famous pep8) which introduced the concept of module dunder attributes and one of the many guides of how people interpret this, what this has evolved into.

                MAXON SDK Specialist
                developers.maxon.net

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