Hiding Node Previews for all Redshift Nodes in Redshift Materials
-
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()
-
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()
-
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~
DunHouRenderer
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()
-
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 toFalse
(just as in @Dunhou'smaxon
example, but you could condense there some code usingmaxon.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,
FerdinandPS: 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 notQUERY_FLAGS.MATCH_MATCH_MAYBE
, will fix thatResult
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:
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()
-
@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 <d_keith@maxon.net> (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()
-
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 oneApplyDescription
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,
FerdinandPS: 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.