On this page

Overview

You can create geometry node (SOP) assets that are defined by a Python script instead of a subnetwork of nodes (File ▸ New operator type, click Python type, set Network type to “Geometry”). This example defines a Python SOP that copies its input, creates a Cd (diffuse color) point attribute, and assigns each point a color based on the distance to a position.

Color Falloff

  1. Open $HFS/houdini/help/files/hom_cookbook/color_falloff.hip.

  2. Click the Handles tool.

  3. Move the location handle around and change the Falloff parameter to see how they affect the coloring.

  4. Right-click the color_falloff1 node and choose Type properties to open the asset’s type properties window.

  • We created Position and Falloff parameters in the Parameters tab, just as we would for a normal asset.

  • In the Handles tab we bound a translate handle to the pos parameter.

  • The Code tab contains the Python code implementing the node’s logic.

# When an instance of this Python SOP cooks, Houdini will have set hou.pwd()
# to the Python SOP instance.  Access the hou.Geometry object for this SOP.
# Since we're calling from hou.SopNode.geometry from a Python SOP
# implementation, we'll have write access to the geometry.
geo = hou.pwd().geometry()

# Create the "Cd" point attribute value, giving it a default value of white
# (1, 1, 1), and store the returned hou.Attrib object.
cd = geo.addAttrib(hou.attribType.Point, "Cd", (1.0, 1.0, 1.0))

# Evaluate the pos parm tuple, and create a hou.Vector3 out of it so we can
# later do vector subtraction.  Also evaluate the falloff value.
pos = hou.Vector3(hou.parmTuple("pos").eval())
falloff = max(hou.ch("falloff"), 0.0001)

for point in geo.points():
    # Compute the distance from this point to the position parameter, divide
    # the distance by the falloff value, and clamp it to be between 0 and 1.
    distance = (point.position() - pos).length()
    value = min(distance / falloff, 1.0)

    # Create a color object, and set the hue and value of the color based on
    # the normalized distance.
    color = hou.Color()
    color.setHSV((value * 256, 1.0, value))

    # Extract the RGB values from the color object and store them in this
    # point's Cd attribute value.
    point.setAttribValue(cd, color.rgb())

Color quills

This Python SOP copies its input, assigns a rainbow of colors to the input surface, and evenly distributes polylines normal to the surface, creating the appearance of “quills” on the surface.

  1. Open $HFS/houdini/help/files/hom_cookbook/surface_wires.hip.

  2. Right-click the surface_wires1 node inside grid_object1 and choose Type properties to open the asset’s type properties window.

  • We created parameters for the number of divisions and length in the Parameters tab, just as we would for a normal asset.

  • The Code tab contains the Python code implementing the node’s logic.

# When an instance of this Python SOP cooks, Houdini will have set hou.pwd()
# to the Python SOP instance.  Access the hou.Geometry object for this SOP.
# Since we're calling from hou.SopNode.geometry from a Python SOP
# implementation, we'll have write access to the geometry.
geo = hou.pwd().geometry()

# Create a Cd (color) point attribute.
color_attrib = geo.addAttrib(hou.attribType.Point, "Cd", (1.0, 1.0, 1.0))

# Assign each point a unique hue value, with constant saturation and value.
num_points = len(geo.iterPoints())
color = hou.Color()
for point in geo.points():
    fraction = float(point.number()) / num_points
    color.setHSV(((fraction * 255), 1, 1))
    # The attribute stores RGB values, so ask for the color in RGB format.
    point.setAttribValue(color_attrib, color.rgb())

# This sop requires that the first primitive is a surface.
surf = None
if len(geo.iterPrims()) != 0:
    surf = geo.iterPrims()[0]
if not isinstance(surf, hou.Surface):
    raise hou.Error("The first primitive must be a surface")

# Evaluate the surface at uniformly distributed positions.  For each
# point we evaluate, create a line facing outwards from the surface,
# and give it the color at the surface.
u_divisions = hou.evalParm("u_divisions")
v_divisions = hou.evalParm("v_divisions")
wire_length = hou.evalParm("wire_length")
for u_index in range(u_divisions + 1):
    u = float(u_index) / u_divisions
    for v_index in range(v_divisions + 1):
        v = float(v_index) / v_divisions

        # Compute the position of the surface, and add the normal
        # vector (scaled by 2) to define the two points of the line.
        pos0 = surf.positionAt(u, v)
        pos1 = pos0 + surf.normalAt(u, v) * wire_length

        # Compute the color of the polygon and create it.  It will
        # be open, since it's a line.
        color = surf.attribValueAt(color_attrib, u, v)
        poly = geo.createPolygon()
        poly.setIsClosed(False)

        # Create the points, set their position and color, and
        # add them to the polygon.
        for pos in pos0, pos1:
            point = geo.createPoint()
            point.setPosition(pos)
            point.setAttribValue(color_attrib, color)
            poly.addVertex(point)

HOM Cookbook Python SOPs