Home Reference Houdini Object Model HOM Cookbook 

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

Load paths.hip and hit play. Examine the nodes inside /obj/net. Then, run hou.session.build("PythonRocks") in a Python shell, hit play again, and look at the new contents of /obj/net.

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()

See also: hou.node, hou.ObjNode.displayNode, hou.SopNode.geometry, hou.Geometry.iterPoints, hou.Point.position, hou.Vector3, hou.hscriptExpression, primuv, hou.Node.parm, hou.Parm.eval, hou.Node.createNode, hou.Node.moveToGoodPosition, hou.SopNode.setDisplayFlag, hou.Node.setFirstInput, hou.Node.layoutChildren, hou.Parm.setExpression