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

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