Recently I needed to add Scalar User Data to 200 Cinema 4D Redshift materials (bitmap) in order to control their opacity with effectors and fields. It was extremely time consuming and boring process so I decided to write an useful Cinema 4D Python Script which does it automatically in seconds to all materials. Basically It will add Scalar User Data populated by Weight and connect it to each material’s opacity input.
Here is the Cinema 4D Python Script:
Python
import c4d
import maxon
from c4d import documents, gui
# Redshift Node Space and IDs
RS_NODE_SPACE_ID = maxon.Id("com.redshift3d.redshift4c4d.class.nodespace")
RS_MATERIAL_ID = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.standardmaterial")
RS_SCALAR_USERDATA_ID = maxon.Id("com.redshift3d.redshift4c4d.nodes.core.rsuserdatascalar")
RS_USER_ATTR_TOKEN = "RSMGWeight" # The attribute token for Weight
RS_ATTRIBUTE_PARAM_ID = maxon.Id("attributename") # Parameter ID for attribute
RS_OPACITY_PORT_ID = maxon.Id("opacity_color") # Opacity input for Standard Material
def get_rs_material_node(graph):
"""Retrieve the Redshift Standard Material node from the graph."""
try:
result = []
maxon.GraphModelHelper.FindNodesByAssetId(graph, RS_MATERIAL_ID, True, result)
if result:
return result[0]
print("No Redshift Standard Material node found in graph")
except Exception as e:
print(f"Error in get_rs_material_node: {e}")
import traceback
traceback.print_exc()
return None
def find_scalar_userdata_node(graph, attr_token):
"""Find a Scalar User Data node with the specified attribute token."""
try:
result = []
maxon.GraphModelHelper.FindNodesByAssetId(graph, RS_SCALAR_USERDATA_ID, True, result)
for node in result:
# Try to get the attribute parameter using full port ID
attr_port = node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsuserdatascalar.attribute")
if attr_port:
param = attr_port.GetDefaultValue()
print(f"Scalar User Data node found, Attribute: {param}")
if param == attr_token:
return node
print("No Scalar User Data node with matching attribute found")
except Exception as e:
print(f"Error in find_scalar_userdata_node: {e}")
import traceback
traceback.print_exc()
return None
def get_node_material(mat):
"""Get the node material reference from a material."""
node_mat = mat.GetNodeMaterialReference()
return node_mat
def main():
# Verify Cinema 4D version
c4d_version = c4d.GetC4DVersion()
print(f"Cinema 4D Version: {c4d_version}")
doc = documents.GetActiveDocument()
if not doc:
gui.MessageDialog("No active document found.")
return
mats = doc.GetMaterials()
print(f"Found {len(mats)} materials in the scene")
if not mats:
gui.MessageDialog("No materials found in the scene.")
return
added_count = 0
for mat in mats:
print(f"\nProcessing material: {mat.GetName()} (Type: {mat.GetType()})")
# Check if the material is a standard material
if mat.GetType() != c4d.Mmaterial:
print(f"Skipping non-standard material: {mat.GetName()}")
continue
# Get node material reference
try:
node_mat = get_node_material(mat)
if not node_mat:
print(f"Failed to get NodeMaterial for {mat.GetName()}")
continue
# Add Redshift node space if it doesn't exist
if not node_mat.HasSpace(RS_NODE_SPACE_ID):
print(f"Adding Redshift node space for {mat.GetName()}")
node_mat.AddSpace(RS_NODE_SPACE_ID)
mat.Update(True, True)
# Get the graph
graph = node_mat.CreateDefaultGraph(RS_NODE_SPACE_ID)
if not graph:
print(f"Failed to get Redshift node graph for {mat.GetName()}")
continue
print(f"Successfully accessed Redshift node graph for {mat.GetName()}")
except Exception as e:
print(f"Error accessing node material for {mat.GetName()}: {e}")
import traceback
traceback.print_exc()
continue
# Begin transaction for graph modifications
with graph.BeginTransaction() as transaction:
try:
# Check for existing Scalar User Data node
scalar_node = find_scalar_userdata_node(graph, RS_USER_ATTR_TOKEN)
if not scalar_node:
# Create a new Scalar User Data node
try:
scalar_node = graph.AddChild(maxon.Id(), RS_SCALAR_USERDATA_ID)
# Check if node was created successfully by trying to access it
if scalar_node:
# Set the attribute name using the full port ID
attr_port = scalar_node.GetInputs().FindChild("com.redshift3d.redshift4c4d.nodes.core.rsuserdatascalar.attribute")
if attr_port:
attr_port.SetPortValue(RS_USER_ATTR_TOKEN)
added_count += 1
print(f"Created Scalar User Data node for {mat.GetName()}")
else:
print(f"Failed to find attribute port for Scalar User Data node in {mat.GetName()}")
else:
print(f"Failed to create Scalar User Data node for {mat.GetName()}")
continue
except Exception as e:
print(f"Error creating Scalar User Data node for {mat.GetName()}: {e}")
import traceback
traceback.print_exc()
continue
else:
print(f"Scalar User Data node with RSMGWeight already exists in {mat.GetName()}")
# Find RS Material node
mat_node = get_rs_material_node(graph)
if not mat_node:
print(f"Failed to find Redshift Standard Material node in {mat.GetName()}")
continue
# Connect Scalar User Data 'Out' to Material 'Opacity'
try:
# Get output port from scalar node
scalar_outputs = scalar_node.GetOutputs()
if not scalar_outputs:
print(f"No output ports found on Scalar User Data node in {mat.GetName()}")
continue
# Get first output port
out_port = None
for port in scalar_outputs.GetChildren():
out_port = port
break
if not out_port:
print(f"Failed to get output port from Scalar User Data node in {mat.GetName()}")
continue
# Get opacity input port from material node
mat_inputs = mat_node.GetInputs()
# Try common Redshift material opacity port IDs
opacity_port = mat_inputs.FindChild("com.redshift3d.redshift4c4d.nodes.core.standardmaterial.opacity_color")
if not opacity_port:
# Try alternative port name
opacity_port = mat_inputs.FindChild("opacity_color")
if not opacity_port:
print(f"Failed to get opacity port for Material in {mat.GetName()}")
continue
# Check if already connected
connections = opacity_port.GetConnections(maxon.PORT_DIR.INPUT)
already_connected = False
for conn in connections:
already_connected = True
break
if already_connected:
print(f"Opacity port already connected in {mat.GetName()}")
else:
# Make the connection
out_port.Connect(opacity_port)
print(f"Connected Scalar User Data to opacity in {mat.GetName()}")
except Exception as e:
print(f"Connection error in {mat.GetName()}: {e}")
import traceback
traceback.print_exc()
continue
# Commit the transaction
transaction.Commit()
except Exception as e:
print(f"Error modifying graph for {mat.GetName()}: {e}")
import traceback
traceback.print_exc()
continue
# Update the material
mat.Update(True, True)
c4d.EventAdd()
gui.MessageDialog(f"Added Scalar User Data 'Weight (RSMGWeight)' to {added_count} materials and connected to opacity.")
if __name__ == "__main__":
main()