HOM Paths Example: Dynamically Creating Object Nodes
Overview
This example takes an arbitrary piece of text and builds a set of letters that get moved from random starting positions to target end positions. The starting positions are randomly chosen from points on an object (/obj/start_locations) and the end positions are distributed along a curve (/obj/target_path). The example creates a path between the start and end locations, creates one object per letter, and makes the letter objects follow the path at offset time interfaces. Finally, another object (/obj/shuttle) follows the whole path, giving the appearance of shuttling the letters to their destinations.
Location
Supporting files for this example are in $HFS/mozilla/documents/hom/cookbook/paths, also found in the cookbook/paths directory of cookbook_files.tar.gz.
Running the Example
Exploring the Code
hou.session contents:
import random def randomStartLocation(): """Return a random start location for a letter. Note that, because a random seed isn't specified, this function will return a different value each time it runs.""" geo = hou.node("/obj/start_locations").displayNode().geometry() point_num = random.randint(0, len(geo.iterPoints())-1) return hou.Vector3(geo.iterPoints()[point_num].position()) def targetPathLocation(u): """Given a normalized parametric value, return the corresponding position along the target path. """ target_path = hou.node("/obj/target_path").displayNode() return hou.Vector3( [hou.hscriptExpression('primuv("%s", 0, "P", %d, %f, 0)' % (target_path.path(), i, u)) for i in range(3)]) def addPositionToCurve(curve, position): """Given a curve sop and a position, add the position to the sop's list of points. """ position_str = ",".join(str(x) for x in position) coords = curve.parm("coords") coords.set(coords.eval() + " " + position_str) def build(text): """Build the path for some arbitrary text.""" # If /obj/net already exists, destroy it and recreate a new subnet. net = hou.node("/obj/net") if net is not None: net.destroy() net = hou.node("/obj").createNode("subnet", "net") net.moveToGoodPosition() # Create an object for the path, and put a curve SOP inside it. curve_geo = net.createNode("geo", "path_curve") curve_geo.setDisplayFlag(False) curve = curve_geo.createNode("curve") curve.setDisplayFlag(True) # Create objects for each letter in the text. for index in range(len(text)): # Compute the parametric positions along the curve where the letters # will start and stop moving. u_start = float(index) / len(text) u_end = (index + 0.5) / len(text) # Find the start and end positions of this letter, and add those # positions to the path. start_pos = randomStartLocation() end_pos = targetPathLocation(float(index) / (len(text)-1)) addPositionToCurve(curve, start_pos) addPositionToCurve(curve, end_pos) # Create a geometry object for the letter, and put a font SOP inside # it. Append a transform axis sop to rotate the letter. letter = net.createNode("geo") font = letter.createNode("font") font.parm("text").set(text[index]) font.parm("fontsize").set(3) xform_axis = letter.createNode("xformaxis") xform_axis.parm("rot").set(90) xform_axis.setFirstInput(font) xform_axis.setDisplayFlag(True) letter.layoutChildren() # Set the letter object to follow the path at the right times. letter.parm("pathobjpath").set(curve_geo.path()) letter.parm("pos").setExpression( "clamp($F/500, %f, %f)" % (u_start, u_end)) letter.parm("pathorient").set(0) # Add a final location for the shuttle to go to. addPositionToCurve(curve, randomStartLocation()) net.layoutChildren()