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)