Your SlideShare is downloading. ×
Hom Class
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Saving this for later?

Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime - even offline.

Text the download link to your phone

Standard text messaging rates apply

Hom Class

571
views

Published on

Published in: Technology

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
571
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
4
Comments
0
Likes
0
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide

Transcript

  • 1. Python in Houdini for Technical Directors Luke Moore, Senior Software Developer, Side Effects Software http://www.sidefx.com/masterclasses/
  • 2. Content Covered
    • The places you can put Python code
    • How to integrate Houdini into your Python-based pipeline
    • How to learn Houdini's new Python API
    • Some examples
  • 3. Assumptions
    • You know Python
    • You know Houdini
    • You have at least a rough knowledge of Hscript
  • 4. A New Scripting Interface in Houdini 9
    • Python scripts will replace Hscript scripts, but Hscript is still supported for backwards compatibility
    • Python has many advantages over Hscript. It:
      • is a well used and proven language
      • comes with a large set of modules
      • is popular in the industry
  • 5. Overview of Houdini's Python Scripting
    • Houdini embeds the Python interpreter
    • Houdini will invoke Python scripts and snippets
    • Your Python scripts have full access to Python's modules
    • With the hou Python module, you can control Houdini
  • 6.  
  • 7. Learning Python
    • As I mentioned, I'm assuming you already know Python
    • If you're learning Python, I highly recommend the online Python tutorial at http://docs.python.org/tut
      • The tutorial is all you need to know to start scripting Houdini
      • After reading the tutorial, you can explore the standard library as you're looking for modules
      • After you've started writing some Python, go back and read the tutorial again
  • 8. Experimenting with Python in Houdini
    • Python shell
      • Can be in a floating window or in a pane
      • The best place to explore the hou module
      • Good for interactively prototyping parts of a script
    • If you use Python for nothing else, use a Python shell as a calculator
  • 9. Experimenting with Python in Houdini
    • Python 2.5.1 (r251:54863, Jul 17 2007, 14:40:58)
    • [GCC 4.1.3 20070629 (prerelease) (Debian 4.1.2-13) on linux2
    • Type “help”, “copyright”, “credits” or “license” for more info.
    • >>> g = hou.node(“/obj/geo1”)
    • >>> g
    • <hou.ObjNode of type geo at /obj/geo1>
    • >>> g.path()
    • '/obj/geo1'
    • >>> tx = g.parm('tx')
    • >>> tx
    • <hou.Parm tx in /obj/geo>
    • >>> tx.eval()
    • 0.0
    • >>> tx.set(3.5); tx.eval()
    • 3.5
  • 10. Accessing Houdini from Python: The hou Module
    • The hou module is written in C++ and hooks directly into Houdini
    • It provides a brand new API called the Houdini Object Model (HOM)
      • Contains classes for the conceptual entities in Houdini (e.g. nodes, parameters, keyframes, panes, vectors, Houdini digital asset definitions, etc.)
    • Houdini also ships with other modules written in Python that build on the hou module.
  • 11. A Simple Example: Changing Path Prefixes
    • Suppose we want to change all the paths in all the file parameters in all the nodes in the scene
    • If the file starts with '/home/luke/project', we'll replace that part with '$HIP'.
  • 12. A Simple Example: Changing Path Prefixes
    • to
  • 13. A Simple Example: Changing Path Prefixes
    • Let's start by building up our script in the Python shell, one piece at a time. First, let's write the part that changes the value of a file parameter.
  • 14. A Simple Example: Changing Path Prefixes
    • >>> p = hou.parm(“/obj/geo1/file1/file”)
    • >>> p
    • <hou.Parm file in /obj/geo1/file1>
    • >>> p.unexpandedString()
    • '/home/luke/project/image.jpg'
    • >>> p.unexpandedString()[18:]
    • '/image.jpg'
    • >>> '$HIP' + p.unexpandedString()[18:]
    • '$HIP/image.jpg'
  • 15. A Simple Example: Changing Path Prefixes
    • >>> t = p.parmTemplate()
    • >>> t.type()
    • parmTemplateType.String
    • >>> t.stringType()
    • stringParmType.FileReference
    • >>> def isFileParm(parm):
    • ... t = parm.parmTemplate()
    • ... return (t.type() == hou.parmTemplateType.String and
    • ... t.stringType() == hou.stringParmType.FileReference)
    • >>> if isFileParm(p) and p.unexpandedString().startswith(
    • '/home/luke/project'):
    • parm.set('$HIP' + parm.unexpandedString()[18:])
  • 16. A Simple Example: Changing Path Prefixes
    • Let's write a function to generalize this logic:
    • >>> def fixFilePrefix(parm, from_prefix, to_prefix):
    • ... if isFileParm(parm) and parm.unexpandedString().startswith(
    • ... from_prefix):
    • ... parm.set(to_prefix +
    • ... parm.unexpandedString()[len(from_prefix):])
    • >>> fixFilePrefix(
    • ... hou.parm('/obj/geo1/file1/file'),
    • ... '/home/luke/project', '$HIP')
  • 17. hou.session Module
    • We can fix a bug in a function we've written by redefining a new definition in the Python shell.
    • Redefining long functions gets tedious, though. Luckily, we can write (and edit!) functions inside Houdini's Python Source Editor window and then test them out from the Python shell.
    • The code we write in the source editor window will become part of a module named hou.session.
  • 18. hou.session Module
  • 19. A Simple Example: Changing Path Prefixes
    • Let's continue with our example and modify the function to work with all the parameters of a node.
    • >>> hou.node(“/obj/geo1”).parms()
    • (<hou.Parm stdswitcher1 in /obj/geo1>, <hou.Parm stdswitcher2 in /obj/geo1>, ..., <hou.Parm vm_computeN in /obj/geo1>)
  • 20. A Simple Example: Changing Path Prefixes
    • def fixFilePrefix(node, from_prefix, to_prefix):
    • for parm in node.parms():
    • if (isFileParm(parm) and
    • parm.unexpandedString().startswith(
    • from_prefix)):
    • parm.set(to_prefix +
    • parm.unexpandedString()[len(from_prefix):])
  • 21. A Simple Example: Changing Path Prefixes
    • Now all that's left is to call fixFilePrefix() for all the nodes in the scene
    • >>> hou.node('/obj').children()
    • (<hou.ObjNode of type geo at /obj/geo1>, <hou.ObjNode of type geo at /obj/geo2>, <hou.ObjNode of type cam at /obj/cam1>)
    • >>> def printNodes(node):
    • ... print node.path(),
    • ... for child in node.children():
    • ... printNodes(child)
    • >>> printNodes(hou.node('/'))
    • / /obj /obj/geo1 /obj/geo2 /obj/cam1 /out /part /ch /shop ...
  • 22. A Simple Example: Changing Path Prefixes
    • def fixFilePrefix(node, from_prefix, to_prefix):
    • for parm in node.parms():
    • if (isFileParm(parm) and
    • parm.unexpandedString().startswith(
    • from_prefix)):
    • parm.set(to_prefix +
    • parm.unexpandedString()[len(from_prefix):])
    • for child in node.children():
    • fixFilePrefix(child, from_prefix, to_prefix)
    • >>> hou.session.fixFilePrefix(
    • hou.node('/'), '/home/luke/project', '$HIP')
  • 23. Interpreting Tracebacks
    • At some point, we're all bound to make a typo or have a bug in our code. Hscript users will appreciate Python tracebacks.
  • 24.  
  • 25. Exploring the hou Module
    • Houdini's Python shell popup help
      • Attribute
      • completion
      • Function/
      • method
      • help
  • 26. Exploring the hou Module
      • Function/
      • method-
      • specific
      • argument
      • autocompletion
  • 27. Exploring the hou Module
    • The Python shell also supports tab completion
    • The traditional Python dir() and help() functions also help you explore the hou module
    • >>> dir(hou.Node)
    • ['__class__', ..., 'allowEditingOfContents', 'addSpareParmTuple', 'appendComment', 'changeNodeType', 'childTypeCategory', 'children', 'clearParmAliases', 'collapseIntoSubnet', 'color', 'comment', 'cook', 'copyNetworkBox', 'createDigitalAsset', 'createNetworkBox', 'createNode', 'creator', 'destroy', 'digitsInName', 'evalParm', 'evalParmTuple', 'expressionLanguage', 'extractAndDelete', 'findNetworkBox', 'findNetworkBoxes', 'hdaModule', 'indirectInputs', 'inputAncestors', 'inputConnections', 'inputConectors', 'inputs', 'insertInput', isCurrent', 'isInsideLockedHDA', ...]
  • 28. Exploring the hou Module
    • Houdini's help browser's help
      • Contains an introduction covering the material covered here
      • Contains reference help for all the module functions, classes, and methods
      • Also lists functions and methods that are not implemented but are planned for implementation in future versions of Houdini
  • 29.  
  • 30. Python for the Hscripter
    • Most Hscript commands and expression functions have a “replaced by” section listing the hou module functions/methods with the same functionality
    • The hou module contains hou.hscript() and hou.hscriptExpression() functions that let you call arbitrary Hscript functions and expressions
    • Houdini 9 contains a new Hscript “python” command, including “python -c”, so you can invoke Python from hscript
  • 31. Python for the Hscripter
    • Tips
      • Use Python variables to store nodes where you would have “cd'd” in Hscript
          • cd /obj/geo1
          • opadd box
          • opadd sphere
          • cd /obj
        • Could be done with:
          • hou.cd('/obj/geo1')
          • hou.pwd().createNode('box')
          • hou.pwd().createNode('sphere')
          • hou.cd('/obj')
  • 32. Python for the Hscripter
    • Tips (continued)
        • But you could instead write:
          • n = hou.node('/obj/geo1')
          • n.createNode('box')
          • n.createNode('sphere')
  • 33. Python for the Hscripter
    • Tips (continued)
      • The names in the hou module are more descriptive, making Python scripts using hou much easier to read than Hscript scripts. However, they are consequently more verbose.
      • You can use Python tricks to make it less verbose. Consider a module named 'simplehou':
        • import hou
        • n = hou.node
        • p = hou.parm
        • hou.Node.create = hou.Node.createNode
        • >>> from simplehou import *
        • >>> n('/obj/geo1').create('box')
  • 34. Invoking Python from Houdini
    • There are many places where Houdini will invoke the Python interpreter. Here are nine of them:
    • Houdini's Python shell
    • Use the hou.session module to store and edit one-off functions
    • A couple of Python statements can easily invoke scripts. For example, import a module from disk and call a function in it, or use execfile() to run a script
  • 35. Shelf/Tab Menu
    • The shelf/tab menu
    • A place to easily attach Python code to a button
    • A number of supporting modules used by the shelves can be found in $HFS/houdini/scripts/python
    • Most, if not all, of Houdini's builtin shelf scripts are written in Python, so they provide a wealth of examples
  • 36. HDA Button Callback
    • An HDA Button Callback
    • The PythonModule HDA section.
    • hou.Node.hdaModule and hou.NodeType.hdaModule
  • 37.  
  • 38.  
  • 39. HDA Button Callback
    • Not a good idea to call hou.session functions from button callbacks, since hou.session is intended to store hip-file specific functions. Instead, you need to store the callback script with the HDA.
  • 40. HDA Event Handler
    • An HDA's Event Handler
    • There currently aren't Python versions of HDA event handler scripts (OnCreated, OnUpdated, etc.)
    • However, you can use the python hscript command to call functions in the PythonModule section
    • def onCreated(node): print “created HDA”, node.path()
    • python -c “hou.node('$arg1').hdaModule().onCreated(hou.node('$arg1'))”
  • 41. Parameters
    • You can write Python expressions in a node's parameter
    • Each node's expression language is set to either Hscript expressions or Python
    • When you type an expressionless parameter, the expression will be in the node's language
  • 42. Parameters
    • Once a parameter has an expression, that expression's language will not change when you change the node's language
    • If the expression's language is different from the node's language, it will appear in red
    • You can change a parameter's language by right-clicking on it and choosing “Expression -> Change Language to ...”
  • 43. Parameters
    • “ from hou import *” is implicitly run before evaluating a Python expression
      • You don't need the 'hou.' prefix
      • This way, common expressions like cubic(), linear(), ch(), etc. work in both Python and Hscript expressions
    • You can call functions in the hou.session module, and “from hou.session import *” is also implicitly run
      • For example, you could write a hou.session function, say foo(), that returns a tuple of 3 values, and put 'foo()[0]', 'foo()[1]', 'foo()[2]' into each of tx, ty, and tz
  • 44. Parameters
    • Parameters in nodes inside an HDA can call functions in the PythonModule section
    • For example, if foo() is defined in PythonModule, /obj/myhda1 is the HDA, and you could write the following Python expression in /obj/myhda1/geo1/tx: node('..').hdaModule().foo()
  • 45. Parameters
    • You can also put the bodies of Python functions inside expressions
    • Simple rule to remember: single lines are expressions and multiple lines are function bodies
  • 46. When Houdini Starts Up
    • When Houdini Starts Up
    • There are Python versions of 123.cmd and 456.cmd: 123.py and 456.py
    • Houdini will look through $HOUDINI_SCRIPT_PATH, looking for the first directory with 123.* or 456.*. Once it finds a script, it will call it and stop looking.
    • 123.{cmd,py} is called when Houdini starts up without a hip file
    • 456.{cmd,py} is called every time a file is loaded or the session is cleared
  • 47. When Houdini Starts Up
    • There is a new file that's called only once, every time Houdini starts: pythonrc.py
    • Houdini will search $HOUDINI_SCRIPT_PATH for houdini/pythonrc.py and run each of the files found
    • Use pythonrc.py to store functions, aliases, etc.
  • 48. Python-Based SOPs
    • Python-based SOPs
    • “ File -> New Operator Type”, choose “Python type” and “Geometry Operator”, and write code in the “Cook” tab
  • 49.  
  • 50. The Help Browser
    • In addition to the RunHCommand(), there are new RunPythonStatements() and RunPythonExpression() javascript functions
    • <script src=”resource:///res/RunHCommand.js” />
    • <script>
    • alert(RunPythonExpression(“hou.node('/obj').children()”));
    • </script>
  • 51. From Another Process
    • From Another Process
    • Send XML to openport socket
      • The RunPythonStatements() javascript function is just sending XML to Houdini's openport socket
      • <xml version=”1.0”><python_statements>print “hello”</python_statements>
    • Use houxmlrpc module
      • From Houdini:
        • houxmlrpc.run(port=8888)
      • From the other process:
        • s = houxmlrpc.ServerProxy('http://localhost:8888')
        • hou = s.hou
        • # access the hou module like you normally would
  • 52. Loading Python Scripts from External Files
    • You can use the standard Python execfile function to run a script on disk
    • The hou.findFile() function can help locate a file in $HOUDINI_PATH
    • It's usually better to import a module than to use execfile, though
    • Houdini will go through all directories in $HOUDINI_SCRIPT_PATH , adding python subdirectories to sys.path.
    • For example, Houdini will look in the $HOME/houdini9.0/scripts/python directory when importing modules
  • 53. Accessing Houdini from a Regular Python Shell
    • You can import the hou module into a standard Python 2.5 shell
    • You'll need to add $HFS/houdini/scripts/python to sys.path
    • When hou is imported, it will initialize and create a Houdini session
    • It's very easy to bring Houdini into an existing Python-based pipeline
  • 54. hython
    • hython is a program that ships with Houdini
    • It's just a regular Python shell, but it automatically imports the hou module
    • You can pass hip files on the hython command line before any .py files
    • It also supports tab completion
  • 55. Example: Loading a hip File and Running a ROP
    • #!/usr/bin/python2.5
    • import sys
    • sys.path.append(os.environ['HFS']+“/houdini/scripts/python”)
    • import hou
    • try:
    • hou.hipFile.load(sys.argv[1])
    • except hou.LoadWarning, e:
    • print e
    • except hou.OperationFailed:
    • sys.exit(“Could not load “ + sys.argv[1])
    • rop = hou.node(“/out/OUT”)
    • rop.render()
  • 56. Example: A Simple Python SOP
    • geo = hou.pwd().geometry()
    • bbox = geo.boundingBox()
    • cd_attrib = geo.addAttrib(hou.attribType.Point, 'Cd',
    • default_value=(1.0, 1.0, 1.0))
    • for point in geo.points():
    • dist_vec = hou.Vector3(point.position()) - bbox.center()
    • color = [0.5 + dist_vec[i] / bbox.sizevec()[i]
    • for i in range(3)]
    • point.setAttribValue(cd_attrib, color)
  • 57. Example: Node Layout
    • import pygraphviz
    • def layout(network, scale=1.0):
    • graph = pygraphviz.AGraph(directed=True)
    • graph.graph_attr['ordering'] = 'in'
    • # Add graph nodes for each Houdini node in the network.
    • gv_nodes, nodes = {}, {}
    • for node in network.children():
    • graph.add_node(node.name())
    • gv_node = graph.nodes()[-1]
    • gv_node.attr['width'] = str(0.5 + len(node.name()) * 0.05)
    • gv_node.attr['height'] = '0.12'
    • gv_node.attr['fixedsize'] = 'true'
    • gv_nodes[node] = gv_node
    • nodes[gv_node] = node
  • 58. Example: Node Layout (continued)
    • # Now add graph edges for all the wires in the network
    • for node in network.children():
    • for input_node in node.inputs():
    • graph.add_edge(gv_nodes[input_node], gv_nodes[node])
    • edge = graph.edges()[-1]
    • edge.attr['headport'] = 'n'
    • edge.attr['tailport'] = 's'
    • # Layout the graph and set the Houdini node positions
    • graph.layout(prog='neato')
    • scale *= 0.04
    • for gv_node in graph.nodes():
    • nodes[gv_node].setPosition(
    • [float(v) * scale
    • for v in gv_node.attr['pos'].split(',')])
  • 59. Example: Node Layout (continued)
    • # Call layout on the current network in the network editor
    • network_editor = hou.ui.curDesktop().paneTabOfType(
    • hou.paneTabType.NetworkEditor)
    • layout(network_editor.pwd())
  • 60. Houdini's Default Layout
  • 61. Graphviz: 'dot' algorithm
  • 62. Graphviz: 'neato-hier' algorithm
  • 63. Graphviz: 'neato' algorithm