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:

.. code-block:: sh

   $ katana --script myScript.py 

Passing arguments to a script:

.. code-block:: sh

   $ katana --script myScript.py arg1 arg2 arg3

Inside the script the arguments can be interrogated as regular Python 
command-line arguments:

.. code-block:: python

   import sys
   
   arg1 = sys.argv[1]
   arg2 = sys.argv[2]
   arg3 = sys.argv[3]

Nodegraph & Parameters
----------------------

Create a new node

.. code-block:: python

   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

.. code-block:: python

   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

.. code-block:: python

   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 

.. code-block:: python

   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 

.. code-block:: python

   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 

.. code-block:: python

   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 

.. code-block:: python

   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

.. code-block:: python

   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

.. code-block:: python

   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

.. code-block:: python

   # 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:

.. code-block:: python

   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)