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)