Example Scripts¶
These examples show Python scripts can be used to create and modify nodes in Katana. Examples of the use of these scripts include:
- Shelf scripts to automate common operations in the UI.
- Scripts to run at the command line using
katana --script
to create Katana node graph procedurally, and modify existing Katana projects. - Python commands to run in the interactive Python tab in the Katana UI.
- Python commands to run in a command line shell using
katana --shell
.
Examples of the types of operations you can do with these scripts include:
- Creating new nodes, and connect output ports of nodes to input ports of other nodes.
- Set parameter values on nodes, including creating expressions on parameters.
- Find all the nodes of a given type.
- Load and save Katana projects.
- Set off renders.
You can look at the Python code for any existing shelf script. Right-click on the shelf script to bring up its properties.
It is often easiest to create a custom user script, with the command
execfile("/tmp/foo.py")
. Each time you execute the script, the file will be
executed.
Running Katana in script mode¶
General syntax:
$ katana --script myScript.py
Passing arguments to a script:
$ katana --script myScript.py arg1 arg2 arg3
Inside the script the arguments can be interrogated as regular Python command-line arguments:
import sys
arg1 = sys.argv[1]
arg2 = sys.argv[2]
arg3 = sys.argv[3]
Nodegraph & Parameters¶
Create a new node
from Katana import NodegraphAPI
# Create a new node, as a child of root
myNode = NodegraphAPI.CreateNode('CameraCreate', NodegraphAPI.GetRootNode())
# Name the node
myNode.setName('myCamera')
Copy a node
from Katana import NodegraphAPI
from Katana import KatanaFile
# Find the node you want to copy
sourceNode = NodegraphAPI.GetNode('NameOfMySourceNode')
# To copy the node, Katana needs the node's XML (for efficiency, as an XmlIO element tree)
xmlNode = NodegraphAPI.BuildNodesXmlIO([sourceNode])
# Create the copy as child of the root node
# The 'Paste' function returns a list, since we're only asking for one node we grab the first element
newNode = KatanaFile.Paste(xmlNode, NodegraphAPI.GetRootNode())[0]
myNode.setName('myCamera')
Set parameters on a node
from Katana import NodegraphAPI
# Create a node
myNode = NodegraphAPI.CreateNode('CameraCreate', NodegraphAPI.GetRootNode())
myNode.setName('TestCam')
# Set a string parameter
myNode.getParameter('name').setValue('/root/world/cam/testcam', 0)
# Set a float parameter
myNode.getParameter('fov').setValue(55.0, 0)
# Set children of a group parameter
myNode.getParameter('transform.translate.x').setValue(100.0, 0)
myNode.getParameter('transform.translate.y').setValue(200.0, 0)
myNode.getParameter('transform.translate.z').setValue(300.0, 0)
Create a simple renderable scene procedurally and save it to disk
from Katana import NodegraphAPI
from Katana import KatanaFile
print "starting to create scene /tmp/pink_pony.katana"
cameraCreateNode = NodegraphAPI.CreateNode('CameraCreate', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(cameraCreateNode, (100,400))
cameraCreateNode.getParameter('transform.translate.x').setValue(14, 0)
cameraCreateNode.getParameter('transform.translate.y').setValue(18, 0)
cameraCreateNode.getParameter('transform.translate.z').setValue(16, 0)
cameraCreateNode.getParameter('transform.rotate.x').setValue(-18, 0)
cameraCreateNode.getParameter('transform.rotate.y').setValue(53, 0)
cameraCreateNode.getParameter('transform.rotate.z').setValue(0, 0)
ponyCreateNode = NodegraphAPI.CreateNode('PonyCreate', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(ponyCreateNode, (300,500))
materialNode = NodegraphAPI.CreateNode('Material', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(materialNode, (300,400))
materialNode.getParameter('name').setValue('pink_mat', 0)
materialNode.getParameter('namespace').setValue('geo', 0)
materialNode.addShaderType('prmanSurface')
materialNode.checkDynamicParameters()
materialNode.getParameter('shaders.prmanSurfaceShader.enable').setValue(1, 0)
materialNode.getParameter('shaders.prmanSurfaceShader.value').setValue('KatanaPhong', 0)
materialNode.checkDynamicParameters()
materialNode.getParameter('shaders.prmanSurfaceParams.Ks.enable').setValue(1, 0)
materialNode.getParameter('shaders.prmanSurfaceParams.Ks.value').setValue(0.7, 0)
materialNode.getParameter('shaders.prmanSurfaceParams.Kd_color.enable').setValue(1, 0)
materialNode.getParameter('shaders.prmanSurfaceParams.Kd_color.value.i0').setValue(2.0, 0)
materialNode.getParameter('shaders.prmanSurfaceParams.Kd_color.value.i1').setValue(0.3, 0)
materialNode.getParameter('shaders.prmanSurfaceParams.Kd_color.value.i2').setValue(0.5, 0)
materialNode.checkDynamicParameters()
materialNode.getInputPort('in').connect(ponyCreateNode.getOutputPort('out'))
lightCreateNode = NodegraphAPI.CreateNode('LightCreate', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(lightCreateNode, (500, 500))
lightCreateNode.getParameter('name').setValue('/root/world/lgt/light1', 0)
lightCreateNode.getParameter('transform.translate.x').setValue(23, 0)
lightCreateNode.getParameter('transform.translate.y').setValue(21, 0)
lightCreateNode.getParameter('transform.translate.z').setValue(-6, 0)
lightCreateNode.getParameter('transform.rotate.x').setValue(157, 0)
lightCreateNode.getParameter('transform.rotate.y').setValue(74, 0)
lightCreateNode.getParameter('transform.rotate.z').setValue(180, 0)
lightMaterialNode = NodegraphAPI.CreateNode('Material', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(lightMaterialNode, (500, 400))
lightMaterialNode.getParameter('name').setValue('spotLight_shader', 0)
lightMaterialNode.getParameter('namespace').setValue('lgt', 0)
lightMaterialNode.addShaderType('prmanLight')
lightMaterialNode.checkDynamicParameters()
lightMaterialNode.getParameter('shaders.prmanLightShader.enable').setValue(1, 0)
lightMaterialNode.getParameter('shaders.prmanLightShader.value').setValue('KatanaSpotlight', 0)
lightMaterialNode.checkDynamicParameters()
lightMaterialNode.getInputPort('in').connect(lightCreateNode.getOutputPort('out'))
mergeNode = NodegraphAPI.CreateNode('Merge', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(mergeNode, (300,300))
mergeNode.addInputPort('i0').connect(cameraCreateNode.getOutputPort('out'))
mergeNode.addInputPort('i1').connect(materialNode.getOutputPort('out'))
mergeNode.addInputPort('i2').connect(lightMaterialNode.getOutputPort('out'))
materialAssignNode = NodegraphAPI.CreateNode('MaterialAssign', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(materialAssignNode, (300,200))
materialAssignNode.getInputPort('input').connect(mergeNode.getOutputPort('out'))
materialAssignNode.getParameter('CEL').setValue('/root/world/geo/pony', 0)
materialAssignNode.getParameter('args.materialAssign.enable').setValue(1, 0)
materialAssignNode.getParameter('args.materialAssign.value').setValue('/root/materials/geo/pink_mat', 0)
lightMaterialAssignNode = NodegraphAPI.CreateNode('MaterialAssign', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(lightMaterialAssignNode, (300,100))
lightMaterialAssignNode.getInputPort('input').connect(materialAssignNode.getOutputPort('out'))
lightMaterialAssignNode.getParameter('CEL').setValue('/root/world/lgt/light1', 0)
lightMaterialAssignNode.getParameter('args.materialAssign.enable').setValue(1, 0)
lightMaterialAssignNode.getParameter('args.materialAssign.value').setValue('/root/materials/lgt/spotLight_shader', 0)
KatanaFile.Save('/tmp/pink_pony.katana')
print "finished creating scene /tmp/pink_pony.katana"
Create Prune node and set CEL parameter
from Katana import NodegraphAPI
# list of geometry that we want to prune
pruneList = [
'/root/world/geo/mycmpt/mycmpt_body_grp/mycmpt_lf_shoe/mycmpt_lf_shoelace',
'/root/world/geo/mycmpt/mycmpt_head_grp/mycmpt_cn_hat'
]
# Create a Prune node
myNode = NodegraphAPI.CreateNode('Prune', NodegraphAPI.GetRootNode())
# Get the CEL parameter (parameter is named 'cel')
celParam = myNode.getParameter('cel')
# Set the value
celParam.setValue('(' + ' '.join(pruneList) + ')', 0)
Create a series of lights, merge their outputs
from Katana import NodegraphAPI
# Take a list of nodes, and merge their outputs
def MergeNodes(nodes, parent):
merge = NodegraphAPI.CreateNode('Merge')
merge.setParent(parent)
for n in nodes:
output = n.getOutputPortByIndex(0)
if not output:
continue
numInputs = merge.getNumInputPorts()
inputPort = merge.addInputPort('i%d' % numInputs)
output.connect(inputPort)
return merge
# Create a few lightCreates
allLightNodes = []
for lightName in ['key','fill','rim','bounce']:
lightNode = NodegraphAPI.CreateNode('LightCreate', NodegraphAPI.GetRootNode())
lightNode.setName(lightName)
scenegraphLocation = '/root/world/lgt/%s' % lightName
lightNode.getParameter('name').setValue(scenegraphLocation, 0)
allLightNodes.append(lightNode)
# Merge the resulting nodes
mergeNode = MergeNodes(allLightNodes, NodegraphAPI.GetRootNode())
# Set reasonable positions
x,y = 0,0
for n in allLightNodes:
NodegraphAPI.SetNodePosition(n,(x, y))
x+=100
NodegraphAPI.SetNodePosition(mergeNode,(x-250,y-200))
Find all Material nodes, set to an updated surfaceShader
from Katana import NodegraphAPI
oldSurfaceShader = "KatanaBlinn"
newSurfaceShader = "KatanaPhong"
for node in NodegraphAPI.GetAllNodes():
if node.getType() != "Material":
continue
#if node.getParameterValue("materialType", 0) != "geometry":
# continue
param = node.getParameter("shaders.prmanSurfaceShader.value")
if param and param.getValue(NodegraphAPI.GetCurrentTime()) == oldSurfaceShader:
param.setValue(newSurfaceShader, 0)
name = node.getParameterValue("name", NodegraphAPI.GetCurrentTime())
print "Updated Material Shader:", name
# Trigger node to rebuild shader parameters based on new shader name
node.checkDynamicParameters()
Creating an Arnold shading material network
from Katana import NodegraphAPI
imageNode = NodegraphAPI.CreateNode('ArnoldShadingNode', NodegraphAPI.GetRootNode())
imageNode.getParameter('name').setValue('image_node', 0)
NodegraphAPI.SetNodePosition(imageNode, (-100, 100))
imageNode.getParameter('nodeType').setValue('image', 0)
imageNode.checkDynamicParameters()
imageNode.getParameter('parameters.filename').createChildString('hints', "{'widget': 'filename', 'dstPage': 'basics', 'dstName': 'image1'}")
standardNode = NodegraphAPI.CreateNode('ArnoldShadingNode', NodegraphAPI.GetRootNode())
standardNode.getParameter('name').setValue('standard_node', 0)
NodegraphAPI.SetNodePosition(standardNode, (0, 0))
standardNode.getParameter('nodeType').setValue('standard', 0)
standardNode.checkDynamicParameters()
standardNode.getParameter('parameters.Kd.enable').setValue(1, 0)
standardNode.getParameter('parameters.Kd.value').setValue(0.8, 0)
standardNode.getInputPort('Kd_color').connect(imageNode.getOutputPort('out'))
networkMaterialNode = NodegraphAPI.CreateNode('NetworkMaterial', NodegraphAPI.GetRootNode())
NodegraphAPI.SetNodePosition(networkMaterialNode, (100, -100))
networkMaterialNode.addInputPort('arnoldSurface')
networkMaterialNode.getInputPort('arnoldSurface').connect(standardNode.getOutputPort('out'))
Attach selected nodes to mouse as “floating” in Nodegraph panel
from Katana import NodegraphAPI
from Katana import UI4
# Get list of selected nodes
nodeList = NodegraphAPI.GetAllSelectedNodes()
# Find Nodegraph tab and float nodes
nodegraphTab = UI4.App.Tabs.FindTopTab('Node Graph')
if nodegraphTab:
nodegraphTab.floatNodes(nodeList)
Create a Group node containing a network that produces a scene graph with sphere and cube locations
# Constants
TIME = 0
HALF_DISTANCE_APART = 1.0
# Create the group at root level
rootNode = NodegraphAPI.GetRootNode()
groupNode = NodegraphAPI.CreateNode('Group', rootNode)
# Create the sphere at group level
sphereNode = NodegraphAPI.CreateNode('PrimitiveCreate', groupNode)
sphereNode.setName('Sphere')
sphereNode.getParameter('name').setValue("/root/world/geo/sphere", TIME)
sphereNode.getParameter('type').setValue('sphere', TIME)
sphereNode.getParameter('transform.translate.x').setValue(HALF_DISTANCE_APART, TIME)
NodegraphAPI.SetNodePosition(sphereNode, (-100, 100))
# Create the cube
cubeNode = NodegraphAPI.CreateNode('PrimitiveCreate', groupNode)
cubeNode.setName('Cube')
cubeNode.getParameter('name').setValue("/root/world/geo/cube", TIME)
cubeNode.getParameter('type').setValue('cube', TIME)
cubeNode.getParameter('transform.translate.x').setValue(- HALF_DISTANCE_APART, TIME)
NodegraphAPI.SetNodePosition(cubeNode, (100, 100))
# Create a Merge node at group level
mergeNode = NodegraphAPI.CreateNode('Merge', groupNode)
# Connect the two PrimitiveCreate nodes to a Merge node
mergeSphereInPort = mergeNode.addInputPort('sphere')
mergeCubeInPort = mergeNode.addInputPort('cube')
mergeSphereInPort.connect(sphereNode.getOutputPort('out'))
mergeCubeInPort.connect(cubeNode.getOutputPort('out'))
# Rename our merge node to 'Result' to make it clear that this is the final result.
mergeNode.setName('Result')
RenderManager¶
Start a Preview Render from a single selected node:
from Katana import NodegraphAPI, RenderManager
allSelectedNodes = NodegraphAPI.GetAllSelectedNodes()
if len(allSelectedNodes) != 1:
raise RuntimeError('Please select one node.')
selectedNode = allSelectedNodes[0]
RenderManager.StartRender(RenderManager.RenderModes.PREVIEW_RENDER, selectedNode)