Quick start
Here’s a quick example of how to use HOM to accomplish a simple task in Houdini. Don’t worry if you don’t understand the details of this example – it will give you a favor of what scripting Houdini is like.
Choose Windows > Python Shell to open an interactive Python Shell window.
# Print out a tree of all the nodes in the scene: >>> def print_tree(node, indent=0): ... for child in node.children(): ... print " " * indent + child.name() ... print_tree(child, indent + 3) ... # Press Enter to finish the definition >>> print_tree(hou.node('/')) obj cam1 file1 properties standard out part ch shop img img1 vex
Getting Started
When you open Houdini’s Python shell, you’ll notice it greets you with
the >>> prompt and waits for you to enter Python expressions or
statements. Even if you don’t plan on writing large Python scripts, the
Python shell is invaluable as a handy calculator:
>>> 2 + 2 4 >>> 0.03 * 25.1 0.753 >>> min(hou.frame(), 7) * 3 3.0
What is hou.frame(), you might ask? Houdini’s Python API is implemented in
a module named hou, short for Houdini. Just like os.getcwd is a function
in the os module, hou.frame is a function in the hou module, and it
returns the current frame number. Note that you don’t need to write import
hou to use the hou module, since Houdini automatically imports the hou module
when it starts up.
Press Ctrl+D to close a floating Python shell window. See the main menu for the shortcut to open the floating Python shell window.
Python shells can be inside panes if you don’t want to use a floating window.
In the Python shell, Home and Ctrl+A will move to the beginning of the line, End and Ctrl+E will move to the end, and up and down will navigate through the history.
You can’t use Ctrl+C to copy from the Python shell, since Ctrl+C will send a KeyboardInterrupt exception. To copy text from a Python shell, right-click and select Copy.
Loading Hip Files
Use the hou.hipFile submodule to save/load the current session to/from hip files. Note that hou.hipFile.load will throw a hou.LoadWarning exception if there were warnings, even though the file was loaded successfully. The following code will print out warnings and continue the rest of the script.
# Print out load warnings, but continue on a successful load. try: hou.hipFile.load("myfile.hip") except hou.LoadWarning, e: print e
Accessing Nodes
Because Houdini is designed around nodes (e.g. SOPs, DOPs, Object nodes, etc.), you're likely to manipulate them in scripts. Here’s a brief primer to get started.
The hou.node function takes a path to a node and returns a hou.Node object, or None if the path is invalid.
# Empty out the current session. >>> hou.hipFile.clear() >>> hou.node('/obj') <hou.Node at /obj> >>> # hou.node returned a hou.Node object corresponding to the /obj node >>> n = hou.node('/asdfasdf') >>> # The node path was invalid, so n will be the None object. >>> print n None >>> g = hou.node('/obj').createNode('geo') >>> g <hou.ObjNode of type geo at /obj/geo1> >>> # g is hou.Node object corresponding to the newly created /obj/geo1 node. >>> # Note that g is actually a hou.ObjNode instance, which is a subclass of >>> # hou.Node. >>> # The parm method on hou.Node objects returns a hou.Parm object (or None >>> # if the parameter name is invalid). >>> tx = g.parm('tx') >>> tx <hou.Parm tx in /obj/geo1> >>> # Evaluate the parameter and change its value. >>> tx.eval() 0.0 >>> tx.set(3.5) >>> tx.eval() 3.5 >>> hou.node('/obj/geo1').parm('tx').eval() 3.5 >>> # hou.parm is a shortcut to access a parm directly. >>> hou.parm('/obj/geo1/tx').eval() 3.5 >>> # hou.evalParm is a shortcut to evaluate a parameter. >>> hou.evalParm('/obj/geo1/tx') 3.5 >>> # hou.ch is exactly the same as hou.evalParm. >>> hou.ch('/obj/geo1/tx') 3.5 >>> # hou.Parm.name returns the name of the parameter, and hou.Node.parms >>> # Returns a tuple of all the Node's parameters. >>> [p.name() for p in g.parms()] ['stdswitcher1', 'stdswitcher2', 'stdswitcher3', 'stdswitcher4', 'keeppos', 'pre_xform', 'xOrd', 'rOrd', 'tx', 'ty', 'tz', 'rx', 'ry', 'rz', 'sx', 'sy', 'sz', 'px', 'py', 'pz', 'scale', 'lookatpath', 'lookup', 'pathobjpath', 'roll', 'pos', 'uparmtype', 'pathorient', 'upx', 'upy', 'upz', 'bank', 'shop_materialpath', 'shop_materialopts', 'tdisplay', 'display', 'use_dcolor', 'dcolorr', 'dcolorg', 'dcolorb', 'picking', 'pickscript', 'caching', 'vport_shadeopen', 'vport_displayassubdiv', 'vm_phantom', 'vm_renderable', 'folder01', 'folder02', 'folder03', 'folder04', 'categories', 'reflectmask', 'lightmask', 'geo_velocityblur', 'vm_shadingquality', 'vm_rayshadingquality', 'vm_rmbackface', 'shop_geometrypath', 'vm_rendersubd', 'vm_renderpoints', 'vm_metavolume', 'vm_coving', 'vm_computeN'] >>> # hou.Parm tuples correspond to parameter groupings: >>> t = g.parmTuple('t') >>> t <hou.ParmTuple t in /obj/geo1> >>> tuple(t) (<hou.Parm tx in /obj/geo1>, <hou.Parm ty in /obj/geo1>, <hou.Parm tz in /obj/geo1>) >>> t.eval() (3.5, 0.0, 0.0) >>> t.set((1.0, 2.0, 3.0)) >>> t.eval() (1.0, 2.0, 3.0) >>> # Build a simple sop network. >>> hou.hipFile.clear() >>> geo = hou.node('/obj').createNode('geo') >>> box = geo.createNode('box') >>> subd = geo.createNode('subdivide') >>> subd.parm('iterations').set(3) >>> subd.setFirstInput(box) >>> subd.moveToGoodPosition() # Move the node tiles to avoid overlaps. >>> subd.setDisplayFlag(True) >>> subd.setRenderFlag(True) >>> subd.setCurrent(True, clear_all_selected=True)
Working with Animated Parameters and Keyframes
When you hear the term “animated parameter”, you typically think of keyframed
values and bezier curves and the animation graph editor. Recall from earlier,
though, that parameters with expressions are also considered animated
parameters. All animated parameters have at least one keyframe, and each
keyframe has an expression. Typical parameters with expressions simply have
one keyframe whose expression is something like sin($F) or cos(time()),
while typical animation curves have multiple keyframes whose expressions are
something like bezier().
So how does a function like bezier() evaluate to different values at
different times? Clearly there are no parameters passed to bezier that
vary from time to time, and there are no keyframe or slope values passed
in. The answer is that keyframes store more than just an expression.
A keyframe stores those values, slopes, and accelerations, and certain
functions, like bezier, access those values for the current keyframe and
the next one. For keyframes with expressions like sin($F), those
extra values are not set and are not used.
Each keyframe has an associated time. Using that time and the number of frames per second, you can derive the keyframe’s frame. You can think of the expression as being active between keyframes: Houdini evaluates the expression between its keyframe and the next keyframe. If there is no next keyframe, most animation functions (e.g. bezier, cubic, etc.) simply evaluate to their keyframe’s value. For the times before the first keyframe, the parameter evaluates to the value at the first keyframe’s time.
hou.Parm.keyframes values, slopes, and accelerations
If you set the in value and the (out) value is not set, it will be set to the same value. Setting the in value breaks the tie between the values. If neither of the in or (out) values are set, they are considered tied.
for example, to set a keyframe with the current value and slope, do not set the value or slope in the keyframe
or, to automatically determine the slopes, set a keyframe with the slope not set
times and expressions
in and out/values
tied values
asCode()
same syntax between Hscript expressions and Python
Working with Objects and Transformations
worldTransform(), setWorldTransform()
matrices, exploding
column vectors for transforms (p T1 T2), not (T2 T1 p)
see the object_xform cookbook example
Tips
Drag a node from the network editor into the Python shell to paste a hou.node expression. You may find this easier if the Python shell is inside a pane.
Use variables to store hou.Node, hou.Parm, and hou.ParmTuple objects instead of calling hou.node and hou.parm over and over again.
Use the output from hou.Node.asCode to help learn the parts of the HOM API that create nodes and set parameters and keyframes.