HOM Python Object Example: Transforms from Disk
Overview
This example contains an object written in Python that gets its transformation information from a file on disk. First, it loads in a list of transformation matrices. When it cooks, it looks up the transformation matrix corresponding to the current time, and sets its transform to that matrix.
This example illustrates how you can get raw transformation information, such as that generated from another software package or from a hardware device, into Houdini using Python.
Location
Supporting files for this example are in $HFS/mozilla/documents/hom/cookbook/xforms_from_disk, also found in the cookbook/xforms_from_disk directory of cookbook_files.tar.gz.
Viewing the Object’s Output
Exploring the Object’s Implementation
Right-click on /obj/xforms_from_disk1 and bring up it’s type properties. You’ll see that it contains two visible parameters: the file containing the transformation data, and a reload button to reload that file’s contents.
If you click on the code tab, you’ll see the Python code Houdini runs when the object needs to compute its transformation. The code determines which sequence of 16 float transformation values to use, builds a hou.Matrix4 object from them, and calls hou.ObjNode.setCookTransform.
Code for Python Object:
# This code is called when instances of this object cook. obj_node = hou.pwd() # Load in the transformation matrices from the file, with 1 per frame. # Multiple calls won't read the file each time, but will instead read from # cache. transforms = obj_node.hdaModule().loadTransformsFromFile(obj_node) # If there are no transformations in the file, make sure we have at least one. if len(transforms) == 0: transforms = [hou.hmath.identityTransform()] # Determine the index into the sequence based on the current frame. transform_index = max(int(round(hou.frame())), 1) - 1 # If the current frame is after the frames in the file, hold the last frame. if transform_index >= len(transforms): transform_index = len(transforms) - 1 # Construct a matrix out of the sequence of 16 floats, and set this node's # transform to that matrix. obj_node.setCookTransform(hou.Matrix4(transforms[transform_index]))
The cook tab code also calls a function in the PythonModule section, where the cached transformation data is stored. If you look at the sample motion.dat file, you’ll see it just contains a Python expression that builds a list of tuples of 16 float values. The loadTransformsFromFile function evalutes that Python expression with Python’s built-in eval function.
Also note that the PythonModule section contains a function to generate files like motion.dat from an existing object’s transformations.
PythonModule section:
# We cache the transform files we've read in. The cache is a Python dict, # mapping from file names to sequences of sequences of 16 floats. __cache = {} def loadTransformsFromFile(obj_node): """Load the transformations from the node's data file and return them.""" file_name = obj_node.evalParm("file") # See if the file is in the cache. if file_name in __cache: return __cache[file_name] # Use the Python eval() function to get the sequence of transformations # from a string containing the corresponding Python code. f = file(file_name, "r") transforms = eval(f.read()) f.close() # Add the file contents to the cache and return them. __cache[file_name] = transforms return transforms def reloadFile(obj_node): """Clear the cache contents for the currently loaded file. The file will be reread from disk when the node cooks. This function is called from the reload button.""" file_name = obj_node.evalParm("file") if file_name in __cache: del __cache[file_name] def saveTransformsForNode(obj_node, file_name, frame_range=(1, 240)): """Save the transformations on a node into a data file. This function can be used to quickly generate data files for testing.""" # Remember the current frame. orig_frame = hou.frame() # Evaluate the object's world transformation for the different frames. result = [] for frame in range(frame_range[0], frame_range[1]): hou.setFrame(frame) result.append(obj_node.worldTransform().asTuple()) # Restore the frame. hou.setFrame(orig_frame) # Write the transformation matrix values into the file. f = file(file_name, "w") f.write(repr(result)) f.close()
Finally, the reload button callback contains the following Python code:
Reload button callback:
hou.pwd().hdaModule().reloadFile(hou.pwd())
Creating a new Python Object
Note that if you want to create your own Python Object, you can do so by selecting File → New Operator Type and picking “Python Type” as the operator style and “Object Operator” as the network type.