Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Hom Class


Published on

Published in: Technology
  • Be the first to comment

  • Be the first to like this

Hom Class

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