In this article I will show you how you can use python to work with Redshift materials. I will assume that you already understand how to program with python in general.
C4D Version note: All examples require Cinema 4D 2024 or later. The SetPortValue() and GetPortValue() methods used throughout are available from 2024.0 onwards. In earlier versions these were called SetDefaultValue() / GetDefaultValue().
1. The Two Modules You Always Need
Cinema 4D's Python API is split across two separate modules. Every script in this guide begins with both of these imports:
import c4d
import maxon
import c4d loads the classic Cinema 4D module, objects, documents, materials, tags, everything you can see in the Object Manager.
import maxon loads the newer Maxon framework, which handles the node-based side of things: graphs, nodes, ports, connections. It is a completely separate module and will not be available if you forget to import it.
- Note: If you see "NameError: name 'maxon' is not defined", you forgot import maxon. It is never imported automatically.
2. Getting a Material
Before touching any nodes, you need a reference to the material itself. There are a few ways to do that. The simplest way is to search for it by name.
Example 2a - Find a material by name
import c4d
import maxon
material = doc.SearchMaterial("MyMat")
print(material.GetName())
doc is a built-in variable inside Cinema 4D's Script Manager that always refers to the currently open scene.
SearchMaterial("MyMat") scans every material in that scene and returns the first one whose name matches. If nothing matches, it returns None.
material.GetName() returns the name of the material as a plain string, which we then print.
Example 2b - Loop through all materials
Using a single material might not cut it for you. If you want to act on every material in the scene instead of a specific one then you can use doc.GetMaterials() with an S at the end instead, and loop trough all of them.
import c4d
import maxon
for material in doc.GetMaterials():
print(material.GetName())
doc.GetMaterials() returns a Python list containing every material in the scene. In my example above we loop through that list with a for loop. material.GetName() in turn gives us the name of each one.
3. Accessing the Material Node Graph with Python
A Cinema 4D material can hold multiple node graphs, every renderer has its own graph type. To get the Redshift graph specifically, you need two things: ( a) the node material reference, and ( b ) the namespace ID that identifies Redshift.
Example 3a - Get the Redshift graph
import c4d
import maxon
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(
maxon.Id("com.redshift3d.redshift4c4d.class.nodespace"))
if graph.IsNullValue():
print("Not a Redshift node material")
else:
print("Graph found")
This code will allows us to work with whatever exists inside your Redshift material node system.
GetNodeMaterialReference() gives you the node-aware version of the material. Not every material in C4D uses nodes. The legacy C4D standard materials for example do not. This call gives you access to the part that does.
GetGraph(...) this will retrieve the node graph for a specific renderer. You identify the renderer by passing its namespace ID as a maxon.Id.
maxon.Id("com.redshift3d.redshift4c4d.class.nodespace") is the unique identifier for the Redshift node space. Think of it as Redshift's permanent internal name, it never changes regardless of version.
graph.IsNullValue() this will check if the graph came back empty or not. This happens when the material exists in the scene but was never set up as a Redshift node material. Always check this before trying to use the graph. If you don't you risk getting a hard error.
Example 3b - Extract the namespace ID into a constant
Before we continue, you might want to refactor some things. You can opt to clean up the code that we looked at above a little bit. I'm sure you agree with me, the namespace string is long and easy to mistype. Storing it once at the top of your script is much cleaner, and is the pattern you'll see throughout the rest of this article:
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
if graph.IsNullValue():
print("Not a Redshift node material")
From this point on, all examples use RS as that constant.
- Note: A "constant" here just means a variable you set once and never change. There's nothing special about the name RS. It's simply short for Redshift. When coding in python its common do do that in capital letters.
4. Listing All Nodes in the Graph
The graph contains a root. A root is a top-level container that holds all the visible nodes on your canvas. To get the nodes themselves, you basically always ask for the root's children.
Example 4a - Print all node paths
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
for node in nodes:
print(node.GetPath())
In this example we want to print 'all the nodes that exists inside your material' to the console.
The GIF above shows that this material has 3 nodes inside of it. The code prints out:
standardmaterial@AGt7LhqVHF0gGWmeGP0PNX
tiles@N3NjwC$dIJ1iZ9UWpCZ0AW
output@Z1Yf8gCmEN7n0fwhK$YGmW
nodes = [] creates an empty list. GetChildren() will fill it.
graph.GetViewRoot() will return the root of the graph. That's the invisible canvas that all your nodes sit inside.
GetChildren(nodes, maxon.NODE_KIND.NODE) fills the nodes list with everything directly inside that root. The second argument, maxon.NODE_KIND.NODE, is a filter. It tells the method to only include actual nodes, not ports or wires.
node.GetPath() returns the full internal path of the node, which looks something like standardmaterial@VTYjos3F.... The part before the @ is the node type, the part after is a unique instance ID.
Understanding NODE_KIND
As a little sidestep, let's investigate that NODE_KIND a bit deeper. Every entity inside a node graph, whether it's a visible node, an input port, or the container that holds those ports is technically a GraphNode. NODE_KIND is simply the filter you pass to GetChildren() to tell it which of those entities you want back. Basically we are specifying, get THESE types of children.
The simplest mental model: think of each true node on your canvas as a box. That box has two lists attached to it. One for inputs, one for outputs. Inside each list live the individual ports. NODE_KIND lets you ask for the boxes, the lists, or the ports inside them.
pythongraph.GetViewRoot().GetChildren(result, maxon.NODE_KIND.NODE)
Thuis code for example gives you the boxes themselves. The actual visible nodes sitting on the canvas (your material node system). This is what you use in almost every script.
pythonnode.GetInputs().GetChildren(ports, maxon.NODE_KIND.INPORT)
This second example gives you the individual input ports of one specific node in other words, its parameters. Equivalent to calling GetChildren(ports) with no filter at all.
pythongraph.GetViewRoot().GetChildren(result, maxon.NODE_KIND.ALL_MASK)
This gives you absolutely everything, nodes, port lists, and individual ports. Useful only when you want to print the entire internal tree of a graph to understand its structure. You won't need this day-to-day.
The masks are just convenience shortcuts for when you want to grab more than one kind at once in a single call. For example, PORT_MASK is equivalent to saying "give me INPORT or OUTPORT" any port, regardless of direction. IN_MASK means "give me INPORT or INPUTS" anything on the input side. You'll almost never need these directly. The three cases that cover nearly everything are: NODE when you want the visible nodes, no filter when you're already inside GetInputs() or GetOutputs() and just want the ports, and ALL_MASK when you want to dump the entire contents of a graph for debugging.
Quick reference - all available values:pythonmaxon.NODE_KIND.NODE # A visible node on the canvas - Standard Material, Texture Sampler, etc.maxon.NODE_KIND.INPORT # One specific input port - i.e. a single parameter like "metalness"maxon.NODE_KIND.OUTPORT # One specific output port - i.e. "outColor" on a Texture Samplermaxon.NODE_KIND.INPUTS # The container that holds ALL input ports of a node (not the ports themselves)maxon.NODE_KIND.OUTPUTS # The container that holds ALL output ports of a node
maxon.NODE_KIND.PORT_MASK # Shortcut: INPORT + OUTPORT (any port, either direction)maxon.NODE_KIND.IN_MASK # Shortcut: INPORT + INPUTS (anything on the input side)maxon.NODE_KIND.OUT_MASK # Shortcut: OUTPORT + OUTPUTS (anything on the output side)maxon.NODE_KIND.ALL_MASK # Everything - nodes, ports, containers, the whole treemaxon.NODE_KIND.NONE # Empty/null - only appears when something has no kind set
Example 4b - Print node type and instance ID
The path gives you one string, but there are two separate concepts buried inside it: the type (what kind of node it is) and the instance ID (which specific copy it is). Here's how to read both:
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
for node in nodes:
instance_id = node.GetId()
asset_id = node.GetValue("net.maxon.node.attribute.assetid")[0]
label = node.GetValue("net.maxon.node.base.name")
print(f"{label} | type: {asset_id} | id: {instance_id}")
In this example we are printing a list of all the nodes inside your material. We are printing its type as well as its human readable name.
This example results in human readable versions list of the nodes that are used.
RS Standard | type: com.redshift3d.redshift4c4d.nodes.core.standardmaterial | id: standardmaterial@AGt7LhqVHF0gGWmeGP0PNX
None | type: com.redshift3d.redshift4c4d.nodes.core.tiles | id: tiles@N3NjwC$dIJ1iZ9UWpCZ0AW
Output | type: com.redshift3d.redshift4c4d.node.output | id: output@Z1Yf8gCmEN7n0fwhK$YGmW
node.GetId() returns the unique instance identifier for this specific node. This is useful for finding the same node again later.
node.GetValue("net.maxon.node.attribute.assetid")[0] returns the node's type. This is the permanent string that identifies what kind of node it is, such as com.redshift3d.redshift4c4d.nodes.core.standardmaterial. The [0] at the end is needed because this call returns a tuple, and we only want the first item inside it.
node.GetValue("net.maxon.node.base.name") returns the human-readable label visible in the Node Editor. The name you see above the node on the canvas.
5. Discovering Port IDs - The Drag-and-Drop Trick
Before you can read or write a specific material node parameter, you need its exact ID string. Guessing them is impractical, but Cinema 4D has a fast built-in way to reveal them.
Method 1: Enable IDs in the Node Editor
Go to Edit → Preferences → Node Editor and enable the IDs option. From that point on, hovering over any port in the Node Editor shows its full ID string in the overlay at the bottom left of the editor. Inside your material. Then make sure to enable "Menu → View → Show Info
Method 2: Drag a parameter to the Script Manager console
This is the faster method for parameters that appear in the Attribute Manager. Open the Python console via Extensions → Console, then simply drag the parameter label from the Attribute Manager directly onto the console window. Cinema 4D prints the full port path:
MyMat["com.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalness"]
The string inside the brackets is the full port ID. From this you can derive the shorter form by taking everything after the last dot: in this case, metalness.
You can also drag them directly from inside any node in the material as well. In the example above when dragging the Metalness parameter into the console it will result in:
>>> RSStandard["com.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalness"]
6. Reading Port Values
Every parameter on a node is a port. To read its current value, you first need to get the port object, then call GetPortValue() on it.
Example 6a - Read a specific port by its full ID
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "standardmaterial" not in asset_id:
continue
port = node.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalness")
print(port.GetPortValue())
This code example finds the material called "MyMat" in your scene, opens its Redshift node graph, and loops through all the nodes inside it. For each node, it checks whether that node is a Standard Material. It skips it if not. If it is, it grabs the Metalness port and prints its current value.
The this GIF example above, i change the MyMat, string to MyMat2 (making it select the metal sphere material instead). The wood shows 0 Metalness, whereas the shiny sphere shows 1 Metalness in the console when running the same script.
- Note: Please remember that all these examples are working on a material called
MyMat. Your material will likely be called something different. If it cannot find the material the script is expecting it will give you the following error:Traceback (most recent call last):File "scriptmanager", line 7, in <module>AttributeError: 'NoneType' object has no attribute 'GetNodeMaterialReference'
node.GetInputs() returns a special object representing all input ports of that node.
FindChild("full.port.id") searches through those inputs and returns the port with that exact ID. This is how you target one specific parameter.
GetPortValue() returns the current value stored on that port, a float, a color, a boolean, depending on what the parameter represents.
The if "standardmaterial" not in asset_id: continue line skips any node that isn't the Standard Material, so we only look at the right node.
- Note: "continue" inside a for loop means: skip the rest of this iteration and jump straight to the next item. It's a clean way to filter without nesting extra if/else blocks.
Example 6b - Read all input ports of a node
To see every parameter on a node at once, loop through all its input children:
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "standardmaterial" not in asset_id:
continue
ports = []
node.GetInputs().GetChildren(ports)
for port in ports:
short_id = str(port.GetId()).split(".")[-1]
value = port.GetPortValue()
print(f"{short_id}: {value}")
This code example finds "MyMat" in your scene, opens its Redshift node graph, and loops through all nodes looking for the Standard Material. Once found, it collects every single input port on that node and loops through them all. For each port it prints the short parameter name alongside its current value. This will give you a complete readout of every parameter on that Standard Material.
This code results in:
base_color: R:0.20880480110645294, G:0.3256475031375885, B:0.5644869208335876
base_color_weight: 1
diffuse_model: 1
diffuse_roughness: 0
metalness: 0
refl_color: R:1.0, G:1.0, B:1.0
refl_weight: 0.4
refl_roughness: 0.7
refl_ior: 1.5
refl_aniso: 0
refl_aniso_rotation: 0
refl_samples: 16
refr_color: R:1.0, G:1.0, B:1.0
refr_weight: 0
refr_roughness: 0
refr_samples: 8
ss_depth: 0
ss_scatter_color: R:0.0, G:0.0, B:0.0
ss_phase: 0
ss_samples: 16
refr_abbe: 0
ms_color: R:1.0, G:1.0, B:1.0
ms_amount: 0
ms_radius: R:1.0, G:1.0, B:1.0
ms_radius_scale: 1
ms_phase: 0
ms_mode: 2
ms_samples: 64
ms_include_mode: 1
sheen_color: R:1.0, G:1.0, B:1.0
sheen_weight: 0.6
sheen_roughness: 0.08
sheen_samples: 64
thinfilm_ior: 1.5
thinfilm_thickness: 0
coat_color: R:1.0, G:1.0, B:1.0
coat_weight: 0
coat_roughness: 0
coat_ior: 1.5
coat_aniso: 0
coat_aniso_rotation: 0
coat_samples: 16
coat_bump_input: None
emission_color: R:1.0, G:1.0, B:1.0
emission_weight: 0
opacity_color: R:1.0, G:1.0, B:1.0
refr_thin_walled: false
overall_color: R:1.0, G:1.0, B:1.0
bump_input: None
overallaffectsemission: false
depth_override: false
refl_depth: 3
refl_enablecutoff: false
refl_cutoff: 0.01
refr_depth: 5
refr_enablecutoff: false
refr_cutoff: 0.01
combined_depth: 6
diffuse_direct: 1
diffuse_indirect: 1
refl_direct: 1
refl_indirect: 1
refl_isglossiness: false
refl_endmode: 1
refr_direct: 1
shadow_affected_by_fresnel_mode: 2
shadow_opacity: 0
refr_isglossiness: false
affects_alpha: true
block_volumes: true
refr_legacy_light_sampling: false
sheen_direct: 1
sheen_indirect: 1
sheen_isglossiness: false
coat_direct: 1
coat_indirect: 1
coat_isglossiness: false
refl_enablemultiscattercomp: true
anisotropy_orientation: 2
anisotropy_uvchannel:
anisotropy_tangentchannel:
rs_uv_context: Nonenode.GetInputs().GetChildren(ports) fills the ports list with every input port on this node.
str(port.GetId()).split(".")[-1] takes the full port ID string, splits it at every dot character, and takes the last piece. A full ID like com.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalness becomes simply metalness.
- Note: "
split('.')" on a string breaks it apart at every dot and gives you a list of the pieces. "[-1]" accesses the last item in that list. So "a.b.c".split(".")[-1] gives you "c". split() is a very common python function. I can recommend watching any of the thousands YouTube video's about it.
7. Writing Port Values
All modifications to a node graph must happen inside a transaction. Think of it as a save operation: you open the transaction, make your changes, then commit. Only then does Cinema 4D apply them. If you forget to commit, nothing changes.
Example 7a - Set a float parameter
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
with graph.BeginTransaction() as t:
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "standardmaterial" not in asset_id:
continue
port = node.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalness")
port.SetPortValue(0.7)
t.Commit()
c4d.EventAdd()
This code example finds "MyMat", opens its Redshift node graph, and loops through all nodes looking for the Standard Material. Once found, it opens a transaction Cinema 4D's way of batching graph changes. It then sets the Metalness port to 0.7 (quite metallic), then commits the transaction to apply the change. Finally it calls c4d.EventAdd() to tell Cinema 4D to refresh the viewport and material preview.
In this GIF you see the meatless value changing inside the Metalness parameter as well as on the visual shader balls.
with graph.BeginTransaction() as t: opens a transaction and assigns it to the variable t. Everything indented beneath it is part of that transaction.
port.SetPortValue(0.7) sets the metalness to 0.7 (quite metallic). You can pass any Python value that matches the port's type, floats, booleans, etc.
t.Commit() finalises the transaction and applies all the changes to the graph. Without this line, none of your changes take effect.
c4d.EventAdd() tells Cinema 4D to refresh, update the viewport, material previews, and Attribute Manager to reflect the new values. Always call this after making changes.
- Note: "with ... as t:" is Python's context manager syntax. It runs setup code before your block and cleanup code after, even if an error occurs. Here it opens the transaction before your indented code runs and handles cleanup afterwards.
Example 7b - Set a colour parameter
Colour values in the Maxon API use maxon.Color, not plain Python tuples. Here's how to set the base colour of a Standard Material:
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
with graph.BeginTransaction() as t:
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "standardmaterial" not in asset_id:
continue
port = node.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_color")
port.SetPortValue(maxon.Color(1.0, 0.2, 0.0))
t.Commit()
c4d.EventAdd()
This example has the same structure as before, it finds "MyMat", locates the Standard Material node, and sets a port value inside a transaction. The only difference is that instead of a float it's setting the Base Color port to an orange-red, using maxon.Color(1.0, 0.2, 0.0). The three numbers being red, green, and blue between 0 and 1. It then commits and refreshes Cinema 4D.
maxon.Color(r, g, b) creates a colour value using red, green, and blue channels expressed as floats between 0.0 and 1.0. maxon.Color(1.0, 0.2, 0.0) is a vivid orange-red.
Example 7c - Set a texture path
File paths in the Maxon API are expressed as maxon.Url objects, not plain strings. The Texture Sampler node has a nested port structure, the path lives inside a bundle called tex0:
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
with graph.BeginTransaction() as t:
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "texturesampler" not in asset_id:
continue
path_port = node.GetInputs() \
.FindChild("com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0") \
.FindChild("path")
path_port.SetPortValue(maxon.Url("file:///C:/textures/test.jpg"))
t.Commit()
c4d.EventAdd()
This example shows the same pattern again. It finds "MyMat", this time looks for a Texture Sampler node instead of a Standard Material. Because the file path lives inside a nested port bundle called tex0, it has to call FindChild() twice to drill down to the actual path port inside it. It then sets that path to the specified texture file using maxon.Url(), commits, and refreshes.
The double .FindChild() call navigates into the nested port bundle. tex0 is the bundle that holds the texture settings, and path is the actual file path port inside it.
maxon.Url("file:///C:/textures/my_texture.png") wraps the path string in the correct type. On macOS the path would be "file:///Users/name/textures/test.jpg".
- Note: The "\ " at the end of a Python line is a line continuation. It tells Python that the expression continues on the next line. It's purely cosmetic here, used to keep long lines readable.
8. Adding Nodes to the Graph
You can also create new nodes from scratch using graph.AddChild(). Like all write operations, this must happen inside a transaction.
Example 8a - Add a Texture Sampler node
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
TEX = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
with graph.BeginTransaction() as t:
new_node = graph.AddChild("", TEX, maxon.DataDictionary())
t.Commit()
c4d.EventAdd()
This code example finds "MyMat", opens its Redshift graph, and creates a brand new Texture Sampler node inside it. AddChild() is what actually creates the node, with TEX telling it which type to create. The empty string lets Cinema 4D generate the instance ID automatically, and DataDictionary() is just an empty container the API requires. It then commits and refreshes.
Did you notice that we stored TEX at the top as a constant? TEX is just a variable name, it holds the full node type ID string for a Texture Sampler. What makes it slightly confusing is that it looks like it is the node, when really it's just the recipe, it tells AddChild() what kind of node to create, not the node itself. The actual node only exists after AddChild() runs and returns it into new_node.
You could swap TEX out for any other node type ID to create a different kind of node entirely:
Texture Sampler
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
Color Mix
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rscolormix")
Bump Map
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.bumpmap")
Maxon Noise
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.maxonnoise")
Standard Material
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial")
and so on...
graph.AddChild("", TEX, maxon.DataDictionary()) creates a new node in the graph. The three arguments are:
- The first argument is a custom
local ID string. Passing an empty string "" lets Cinema 4D generate one automatically, which is almost always what you want. - The second argument is the
asset IDof the node type to create. In our case, the Texture Sampler. - The third argument is a
maxon.DataDictionary(). This is an optional set of initial settings. Passing an empty one is fine; the node will use its defaults.
The call returns a reference to the newly created node, stored in new_node.
- Note: A "DataDictionary" is essentially an empty container for key-value settings. Think of it like an empty Python dict {}. It's required by the API but you don't need to put anything in it.
9. Connecting Nodes with Wires
Connecting two nodes means wiring an output port of one node to an input port of another. You call Connect() on the output port, passing the target input port as the argument.
Example 9a - Wire a Texture Sampler to the Standard Material
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
TEX = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.texturesampler")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
result = []
maxon.GraphModelHelper.FindNodesByAssetId(
graph,
maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial"),
True,
result)
std_mat = result[0]
with graph.BeginTransaction() as t:
tex_node = graph.AddChild("", TEX, maxon.DataDictionary())
out_port = tex_node.GetOutputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.texturesampler.outcolor")
in_port = std_mat.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_color")
out_port.Connect(in_port)
t.Commit()
c4d.EventAdd()
This code example finds "MyMat" and then searches the entire graph for an existing Standard Material node using FindNodesByAssetId(), storing it in std_mat so it can be referenced later. Inside the transaction it creates a brand new Texture Sampler node, then finds the color output port on that new node and the Base Color input port on the Standard Material. It then calls Connect() to draw a wire between them, and commits. The result being a Texture Sampler wired into the Base Color of the Standard Material.
This GIF shows that it now adds a texture node again, but this time connected to the Base Color input of the Standard Material node.
maxon.GraphModelHelper.FindNodesByAssetId(graph, id, True, result) searches the graph for all nodes of a specific type and appends them to the result list. The third argument True means "search recursively". We then take the first match with result[0].
tex_node.GetOutputs().FindChild("...outcolor") navigates to the colour output port of the new Texture Sampler.
std_mat.GetInputs().FindChild("...base_color") navigates to the Base Color input of the Standard Material.
out_port.Connect(in_port) draws the wire between them. The connection goes from the output port (left side) to the input port (right side).
10. Renaming a Node
To change the label that appears above a node in the Node Editor, use SetValue() with the name attribute. This also goes inside a transaction.
Example 10a - Rename a node
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
with graph.BeginTransaction() as t:
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "texturesampler" not in asset_id:
continue
node.SetValue("net.maxon.node.base.name", maxon.String("Diffuse Texture"))
t.Commit()
c4d.EventAdd()
This example code finds "MyMat", loops through all nodes in its Redshift graph, and skips anything that isn't a Texture Sampler. For every Texture Sampler it finds, it renames it to "Diffuse Texture"
node.SetValue("net.maxon.node.base.name", maxon.String("Diffuse Texture")) sets the display name. The value must be wrapped in maxon.String() rather than passed as a plain Python string. The API requires this type for text attributes.
11. Checking Connections on a Port
Sometimes you need to know whether a port already has a wire connected to it before deciding what to do with it. We can use GetConnections() for this.
Example 11a - Check if a port has an incoming connection
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if "standardmaterial" not in asset_id:
continue
port = node.GetInputs().FindChild(
"com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_color")
connections = port.GetConnections(maxon.PORT_DIR.INPUT)
if connections:
print("base_color has a wire connected")
else:
print("base_color is unconnected")
This one finds "MyMat", locates the Base Color port on the Standard Material node, and checks whether anything is wired into it. GetConnections() returns a list of incoming connections. If that list isn't empty there's a wire, if it is empty there isn't. It then prints the result accordingly.
This GIF shows that there is indeed a wire connected to the Base Color slot. So it prints out the following result to the console:
base_color has a wire connected
port.GetConnections(maxon.PORT_DIR.INPUT) returns a list of all wires coming into this port. Passing maxon.PORT_DIR.INPUT means we're asking about incoming connections specifically (as opposed to PORT_DIR.OUTPUT for outgoing ones like we looked at before).
If the returned list is empty, there is no wire. Since an empty list evaluates as False in Python, the plain if connections: check works cleanly.
12. Looping All Materials - (also checking for a Redshift Graph)
When processing an entire scene, some materials may not be Redshift node materials at all. This is how to skip those safely:
Example 12a - Process only Redshift node materials
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
for material in doc.GetMaterials():
if not material.GetNodeMaterialReference().HasSpace(RS):
continue
graph = material.GetNodeMaterialReference().GetGraph(RS)
if graph.IsNullValue():
continue
print(f"Processing: {material.GetName()}")
This code loops through every material in the scene and skips any that aren't Redshift node materials, then skips any whose graph comes back empty. These two checks together are the standard safety guard for scene-wide loops. For every material that passes both checks, it simply prints its name.
material.GetNodeMaterialReference().HasSpace(RS) returns True if this material has a Redshift node graph set up. It's a faster, cleaner check than calling GetGraph() and testing for null.
The double continue pattern, check HasSpace, then check IsNullValue, is the standard guard used in any scene-wide loop. It ensures you never call graph methods on an empty or incompatible material.
13. Full Project: Scan All Materials for Changed Parameters
Everything covered so far comes together in this final script. For debugging purposes this can be a handy script. It scans every Redshift material in your scene and prints out every parameter that has been changed from its factory default.
This is genuinely useful for auditing a complex scene, documenting a material setup, or debugging unexpected render results.
Why comparison against defaults is necessary
You might assume that a "modified" parameter would carry some kind of timestamp or dirty flag. It doesn't. When Cinema 4D creates a Redshift material, it writes all default values explicitly into the graph. This means that every port has a value from the moment the material is created. The only reliable way to know whether a value was changed by a user is to compare it against a freshly created node of the same type. And that si exactly what we will do in the example below.
Example 13a - Scan a single material
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
def get_port_values(node):
ports = []
node.GetInputs().GetChildren(ports)
return {str(port.GetId()).split(".")[-1]: port.GetPortValue() for port in ports}
def values_match(a, b):
try:
if hasattr(b, "x"):
return (round(a.x,5)==round(b.x,5) and
round(a.y,5)==round(b.y,5) and
round(a.z,5)==round(b.z,5))
return a == b
except:
return True
material = doc.SearchMaterial("MyMat")
graph = material.GetNodeMaterialReference().GetGraph(RS)
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
fresh = c4d.BaseMaterial(c4d.Mmaterial)
fresh.GetNodeMaterialReference().CreateDefaultGraph(RS)
fresh_graph = fresh.GetNodeMaterialReference().GetGraph(RS)
defaults = {}
with fresh_graph.BeginTransaction() as t:
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
fresh_node = fresh_graph.AddChild("", maxon.Id(asset_id), maxon.DataDictionary())
defaults[asset_id] = get_port_values(fresh_node)
t.Commit()
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if asset_id not in defaults:
continue
node_type = asset_id.split(".")[-1]
user_label = node.GetValue("net.maxon.node.base.name")
ports = []
node.GetInputs().GetChildren(ports)
changed = []
for port in ports:
short_name = str(port.GetId()).split(".")[-1]
default_val = defaults[asset_id].get(short_name)
if default_val is None:
continue
if not values_match(port.GetPortValue(), default_val):
changed.append(f" CHANGED: {short_name} = {port.GetPortValue()}")
if changed:
print(f"\nNode: {node_type} ({user_label})")
for line in changed:
print(line)
What does this script do? The script's goal is to find every parameter in "MyMat" that has been changed from its factory default. The challenge it's solving is that Redshift writes all default values explicitly into the graph when a material is created, so there's no dirty flag to check. The only reliable approach is to compare against a freshly created reference. To build that reference, it creates a brand new blank material in memory. This one is never inserted into the scene, and uses it as a sandbox. For every node type found in the real material, it creates a fresh copy of that same node type inside the sandbox graph and captures all its port values into the defaults dictionary. That gives you a clean set of factory defaults to compare against.
The get_port_values() function is a helper that collects all input port values of a node into a dictionary keyed by short port name, it's called both when building the defaults and when reading the real material's values. The values_match() function handles the comparison, with a special case for colours and vectors: because those have .x, .y, .z components, a plain == check can fail due to tiny floating-point rounding differences, so it rounds both sides to 5 decimal places before comparing. The second loop then goes through every node in the real material, compares each port value against the stored default, and collects any that differ into a changed list. If a node has any changed ports, it prints the node type, its label, and every parameter that was modified. This will give you a complete human-readable report of what was actually touched in this material by any artist.
Let's walk through the new parts:
def get_port_values(node): defines a reusable function that returns a dictionary of all input port values for a given node. The keys are short port names (the last segment of the full ID), and the values are whatever GetPortValue() returns for each.
- Note: A "dictionary" (dict) in Python maps keys to values. Here port names like "
metalness" map to their current values like 0.0. You retrieve a value by writing dict["key"].
def values_match(a, b): is a comparison helper. We can't use a plain == check for everything because colours and vectors, which have .x, .y, .z attributes, can fail equality checks due to floating-point rounding. The hasattr(b, "x") check detects those types and compares component by component, rounded to 5 decimal places.
- Note: "
hasattr(obj, 'x')" returns True if the object has an attribute called x. It's a safe way to check what type of value you're dealing with without knowing in advance.
fresh = c4d.BaseMaterial(c4d.Mmaterial) creates a new blank C4D material in memory. It is never inserted into the document, it only exists temporarily as a reference.
fresh.GetNodeMaterialReference().CreateDefaultGraph(RS) sets up the Redshift node graph inside that blank material, populating it with the same default nodes Cinema 4D creates when you make a new Redshift material normally.
Inside the transaction on the fresh graph, we loop through every node type we found in the real material and call AddChild() on the fresh graph to create a default copy of each. get_port_values() then captures those defaults into the defaults dictionary, keyed by asset ID.
The second loop compares the real material's port values against those defaults. When a value doesn't match, it's appended to the changed list and printed.
Example 13b - Scan all materials in the scene
This is the complete, scene-wide version. The key efficiency improvement is the defaults_cache, in this one we only ever create a fresh reference node once per node type, even if that type appears in dozens of materials. This is because we only need 1 to check what the default values are of that particular type of node that was used.)
import c4d
import maxon
RS = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
def get_port_values(node):
ports = []
node.GetInputs().GetChildren(ports)
return {str(port.GetId()).split(".")[-1]: port.GetPortValue() for port in ports}
def values_match(a, b):
try:
if hasattr(b, "x"):
return (round(a.x,5)==round(b.x,5) and
round(a.y,5)==round(b.y,5) and
round(a.z,5)==round(b.z,5))
return a == b
except:
return True
fresh = c4d.BaseMaterial(c4d.Mmaterial)
fresh.GetNodeMaterialReference().CreateDefaultGraph(RS)
fresh_graph = fresh.GetNodeMaterialReference().GetGraph(RS)
defaults_cache = {}
for material in doc.GetMaterials():
if not material.GetNodeMaterialReference().HasSpace(RS):
continue
graph = material.GetNodeMaterialReference().GetGraph(RS)
if graph.IsNullValue():
continue
nodes = []
graph.GetViewRoot().GetChildren(nodes, maxon.NODE_KIND.NODE)
new_ids = [str(node.GetValue("net.maxon.node.attribute.assetid")[0])
for node in nodes
if str(node.GetValue("net.maxon.node.attribute.assetid")[0])
not in defaults_cache]
if new_ids:
with fresh_graph.BeginTransaction() as t:
for asset_id in new_ids:
fresh_node = fresh_graph.AddChild("", maxon.Id(asset_id), maxon.DataDictionary())
defaults_cache[asset_id] = get_port_values(fresh_node)
t.Commit()
mat_printed = False
for node in nodes:
asset_id = str(node.GetValue("net.maxon.node.attribute.assetid")[0])
if asset_id not in defaults_cache:
continue
node_type = asset_id.split(".")[-1]
user_label = node.GetValue("net.maxon.node.base.name")
ports = []
node.GetInputs().GetChildren(ports)
changed = []
for port in ports:
short_name = str(port.GetId()).split(".")[-1]
default_val = defaults_cache[asset_id].get(short_name)
if default_val is None:
continue
if not values_match(port.GetPortValue(), default_val):
changed.append(f" CHANGED: {short_name} = {port.GetPortValue()}")
if changed:
if not mat_printed:
print(f"\n{"="*50}")
print(f"MATERIAL: {material.GetName()}")
print(f"{"="*50}")
mat_printed = True
print(f" Node: {node_type} ({user_label})")
for line in changed:
print(line)
This version does the same thing as the previous one but runs across every material in the scene instead of just one, using the same safety guards from earlier. The key efficiency improvement is defaults_cache. It lives outside the material loop, so if the same node type (like Standard Material) appears across dozens of materials, its defaults are only ever built once and reused. The mat_printed flag ensures the material name header only prints once per material, right before the first changed node, rather than repeating it for every node with changes.
This version of the code now checks for non-default parameters inside ALL of the materials in the scene.
It now results the following to the console:
==================================================
MATERIAL: Basic Fabric
==================================================
Node: standardmaterial (RS Standard)
CHANGED: base_color = R:0.20880480110645294, G:0.3256475031375885, B:0.5644869208335876
CHANGED: diffuse_model = 1
CHANGED: refl_weight = 0.2
CHANGED: refl_roughness = 0.45
CHANGED: sheen_weight = 0.6
CHANGED: sheen_roughness = 0.08
Node: bumpmap (None)
CHANGED: scale = 0.35
CHANGED: inputtype = 1
==================================================
MATERIAL: Basic Metal Coated
==================================================
Node: standardmaterial (RS Standard)
CHANGED: base_color = R:0.18434515595436096, G:0.31946784257888794, B:0.7767977714538574
CHANGED: metalness = 1
CHANGED: refl_color = R:0.22698341310024261, G:0.3987724483013153, B:0.9166548252105713
CHANGED: refl_roughness = 0.18
CHANGED: refl_ior = 1.54
CHANGED: refl_aniso = 0.684
CHANGED: anisotropy_orientation = 1
==================================================
MATERIAL: Basic Plastic
==================================================
Node: standardmaterial (RS Standard)
CHANGED: base_color = R:0.3671082556247711, G:0.08240959048271179, B:0.05641081929206848
CHANGED: refl_roughness = 0.06
CHANGED: refl_ior = 1.54
==================================================
MATERIAL: Basic Stone
==================================================
Node: bumpmap (None)
CHANGED: scale = 1.5
CHANGED: inputtype = 1
Node: standardmaterial (RS Standard)
CHANGED: base_color = R:0.18, G:0.18, B:0.18
CHANGED: refl_weight = 0.7
CHANGED: refl_roughness = 0.6
CHANGED: refl_ior = 1.55
CHANGED: ms_color = R:0.8999999999999999, G:0.8999999999999999, B:0.8999999999999999
CHANGED: refr_thin_walled = true
==================================================
MATERIAL: Basic Wood
==================================================
Node: rsmathrange (roughness)
CHANGED: new_min = 0.7
CHANGED: new_max = 0.1
Node: bumpmap (None)
CHANGED: scale = 0.02
Node: standardmaterial (RS Standard)
CHANGED: base_color = R:0.3671082556247711, G:0.08240959048271179, B:0.05641081929206848
CHANGED: refl_roughness = 0.11
CHANGED: refl_ior = 1.54
==================================================
MATERIAL: MyMat
==================================================
Node: standardmaterial (RS Standard)
CHANGED: metalness = 0.034
CHANGED: refl_weight = 0.16
defaults_cache = {} is a dictionary that lives outside the material loop. Once we've built the defaults for a given node type, they're stored here and reused for every subsequent material that contains that same type.
The list comprehension that builds new_ids collects only the asset IDs we haven't seen before, so we only run the transaction for genuinely new node types.
mat_printed = False is a flag that tracks whether we've already printed the material header for the current material. The header only prints once per material, on the first node that has changes.
- Note: A "flag" is just a boolean variable used to track whether something has happened yet. Setting it to False at the start and True once the event occurs is a common pattern for printing headers exactly once.
Appendix: Common Node and Port IDs
These are the most frequently used IDs in Redshift node materials. All have been verified against the C4D SDK documentation and developer forums.
Node Space
com.redshift3d.redshift4c4d.class.nodespace
Node Types
com.redshift3d.redshift4c4d.nodes.core.standardmaterialcom.redshift3d.redshift4c4d.nodes.core.texturesamplercom.redshift3d.redshift4c4d.nodes.core.rscolormixcom.redshift3d.redshift4c4d.nodes.core.bumpmapcom.redshift3d.redshift4c4d.nodes.core.maxonnoisecom.redshift3d.redshift4c4d.nodes.core.material
Standard Material Input Ports (selection)
com.redshift3d.redshift4c4d.nodes.core.standardmaterial.base_colorcom.redshift3d.redshift4c4d.nodes.core.standardmaterial.metalnesscom.redshift3d.redshift4c4d.nodes.core.standardmaterial.refl_roughnesscom.redshift3d.redshift4c4d.nodes.core.standardmaterial.opacity_colorcom.redshift3d.redshift4c4d.nodes.core.standardmaterial.emission_colorcom.redshift3d.redshift4c4d.nodes.core.standardmaterial.bump_input
Texture Sampler Ports
com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0 (bundle)com.redshift3d.redshift4c4d.nodes.core.texturesampler.tex0 > path (nested)com.redshift3d.redshift4c4d.nodes.core.texturesampler.outcolor (output)
Node Attributes
net.maxon.node.attribute.assetid (node type ID)net.maxon.node.base.name (display label)
Appendix: Material Information
A material object in Cinema 4D is an instance of BaseMaterial, which itself inherits from BaseList2D, GeListNode, and C4DAtom. That means you get methods from all four levels and use them with your materials. In the same way when we typed material.GetName() at the very beginning of this article. Now known how many methods it inherited:
Here's the full picture of what kind of info we can get from a material:
Methods specific to BaseMaterial
These are the only ones that exist purely on a material and nothing else.material.Compare(other_mat) - True if both materials have identical contents (name can differ)material.GetPreview() - Returns the material's preview thumbnail as a bitmapmaterial.GetAverageColor(channel) - Returns the average colour of the material preview as a Vector (r,g,b)material.Update(preview, rttm) - Forces Cinema 4D to recalculate the thumbnail and/or realtime texturematerial.GetRenderInfo() - Returns flags describing what the material needs from the renderer (reflections, transparency, etc.)material.GetNodeMaterialReference() - The one you already know - gives access to the node graph
Methods from BaseList2D (the parent class)
These are shared by materials, objects, tags, shaders, and most other C4D things.
Namematerial.GetName() - Returns the material's name as a stringmaterial.SetName("NewName") - Renames the materialmaterial.GetTypeName() - Returns the human-readable category name, e.g. "Redshift Material"material.GetBubbleHelp() - Returns the tooltip text for this material type
Data / Parametersmaterial.GetData() - Returns a copy of the material's internal data container (a BaseContainer)material.GetDataInstance() - Returns a live reference to the data container (changes affect the material directly)material.SetData(bc) - Replaces or merges the material's data containermaterial[c4d.SOME_ID] - Shorthand bracket access to get a parameter valuematerial[c4d.SOME_ID] = val - Shorthand bracket access to set a parameter value
Layersmaterial.GetLayerObject(doc) - Returns the layer this material is assigned to (or None)material.GetLayerData(doc) - Returns a dict of the layer's properties (color, visibility, lock status, etc.)material.SetLayerObject(layer) - Assigns the material to a layermaterial.SetLayerData(doc, data) - Sets layer properties
Shaders (relevant for classic C4D materials, less so for Redshift node materials)material.GetFirstShader() - Returns the first shader attached to this materialmaterial.InsertShader(shader) - Inserts a shader into the material's shader list
Animation tracksmaterial.GetCTracks() - Returns all animation tracks on this material as a listmaterial.GetFirstCTrack() - Returns the first animation trackmaterial.FindCTrack(desc_id) - Finds the track for a specific parameter IDmaterial.InsertTrackSorted(track) - Inserts an animation track in the correct ordermaterial.GetCTrackRoot() - Returns the root of the track hierarchy
Keyframe selectionmaterial.SetKeyframeSelection(id, bool) - Marks a parameter as selected for keyframingmaterial.FindKeyframeSelection(id) - Checks if a parameter is in the keyframe selectionmaterial.ClearKeyframeSelection() - Clears all keyframe selectionsmaterial.KeyframeSelectionContent() - True if any keyframe selections are active
User Datamaterial.AddUserData(bc) - Adds a custom user data field, returns its IDmaterial.GetUserDataContainer() - Returns all user data as an iterable of (id, container) pairsmaterial.RemoveUserData(id) - Deletes a user data fieldmaterial.SetUserDataContainer(id, bc) - Sets user data at a specific ID
Bits (internal state flags, rarely needed)material.GetBit(mask) - Checks whether a specific bit flag is setmaterial.SetBit(mask) - Sets a bit flag (e.g. BIT_ACTIVE to select it)material.DelBit(mask) - Clears a bit flagmaterial.ToggleBit(mask) - Flips a bit flagmaterial.GetAllBits() - Returns all bits as a single integermaterial.SetAllBits(bits) - Sets all bits at once
Miscmaterial.GetIcon() - Returns the material's icon as a bitmap dictmaterial.GetInfo() - Returns internal info flags (type-dependent meaning)material.Edit() - Programmatically triggers "open this material for editing"material.GetMain() - Goes up one level in the hierarchy (e.g. from material to document)material.Scale(scale) - Scales the object (rarely meaningful on materials)material.IsNodeBased() - True if this material uses a node graph (any renderer)material.GetNimbusRef(nodeSpaceId) - Returns the NimbusRef for a specific node space (low-level node graph access)material.GetAllNimbusRefs() - Returns all node spaces this material has graphs formaterial.RemoveNimbusRef(nodeSpaceId) - Removes a node space graph from the material
Methods from GeListNode (grandparent)
These handle the material's position in the scene's list structure.material.GetDocument() - Returns the document this material belongs tomaterial.GetNext() - Returns the next material in the Material Manager listmaterial.GetPred() - Returns the previous material in the listmaterial.GetUp() - Returns the parent (almost always the document itself)material.GetDown() - Returns the first child (materials don't usually have children)material.Remove() - Removes this material from the document (does not delete it from memory)material.InsertBefore(other) - Moves this material to just before another one in the listmaterial.InsertAfter(other) - Moves this material to just after another one in the listmaterial.GetListHead() - Returns the list head this material is attached tomaterial.GetBranchInfo() - Returns info about which other nodes this material contains internallymaterial.GetNBit(bit) - Gets a 64-bit node state flagmaterial.SetNBit(bit, val) - Sets a 64-bit node state flag
Methods from C4DAtom (great-grandparent)
These are the most universal - every single object in the C4D API has these.material.GetType() - Returns the integer type ID (e.g. c4d.Mmaterial for a standard material)material.GetClassification() - Returns the broader class IDmaterial.IsInstanceOf(type) - True if this material is of a given typematerial.GetParameter(id, flags) - Safe way to get a parameter (returns None instead of crashing if missing)material.SetParameter(id, val, flags) - Safe way to set a parametermaterial.GetDescription(flags) - Returns the full description of all parameters on this materialmaterial.Message(type, data) - Sends an internal message to the material (e.g. MSG_UPDATE)material.FindUniqueID(appid) - Returns a unique identifier for this material nodematerial.CopyTo(dest, flags, trans) - Copies this material's data into another materialmaterial.GetDirty(flags) - Returns a dirty checksum. It changes when the material is modifiedmaterial.SetDirty(flags) - Manually marks the material as dirty (forces a recalc)material.MultiMessage(flags, type, data) - Sends a message to this material and all its children
The ones you'll actually use day-to-day are probably GetName(), SetName(), GetNext() / GetPred() for iterating, GetType() to check what kind of material it is, GetNodeMaterialReference() for Redshift work, and GetLayerObject() / SetLayerObject() if you're doing layer management. The rest are there when you need them.
Comments
0 comments
Please sign in to leave a comment.