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.
Note
These Python scripts described here are different to those used in the AttributeScript node. The scripts described here are for creating and editing node graphs. Attribute scripts are for inspecting and modifying scenegraph attributes when the Katana recipe is being evaluated.
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¶
Launch a new render of the specfied node
from Katana import NodegraphAPI, RenderManager
allSelectedNodes = NodegraphAPI.GetAllSelectedNodes()
if len(allSelectedNodes)!=1:
raise RuntimeError, 'Please select 1 node.'
selectedNode = allSelectedNodes[0]
RenderManager.StartRender(selectedNode)