Articles

Snippets, tutorials, and other things to say.

HDA Menu Scripts

5 min read

A list of different menu scripts which make HDAs easier to use in Houdini. Note that you need to change the Menu Script Language to Python vs HScript for all of these. Updated as I create them.


Group Selection

Fairly straightforward. Generates a menu with the existing groups of a particular type. There are a couple variations of the code depending on if you want to use a different parameter to control the type being selected (which is how the group node works).

Simple Group Selection (Primitive groups)

The only modification needed is to potentially change hou.geometryType.Primitive to one of these other variations depending what you want to select.

node = kwargs["node"]
menu = []

# change group_types to equal whatever kind of geometry you want
if node:
    menu = node.geometry().generateGroupMenu(group_types=hou.geometryType.Primitives)

return menu

Full Group Selection

Note that you'll need to modify and remove lines to make this work, but designed to work the same way as a group node does in terms of selection. So it wants a "group" string parameter which is likely what this is connected to, as well as a "grouptype" string parameter with menu options for what kind of menu you want to generate.

node = kwargs["node"]
type = node.parmTuple("grouptype")[0]
# Alternatively, the above line can be...
# kwargs['geometrytype'] = hou.geometryType.GEOMETRYTYPEHERE(Points, Edges, Primitives, Vertices)
geo = node.geometry()
menu = []

# Select the groups to work with based on the selected group type
# The expectation is that "type" is coming from a SOP group node, the grouptype parm.
if node:
    if type == "vertices":
        menu = geo.generateGroupMenu(group_types=hou.geometryType.Vertices)
    elif type == "points":
        menu = geo.generateGroupMenu(group_types=hou.geometryType.Points)
    elif type == "edges":
        menu = geo.generateGroupMenu(group_types=hou.geometryType.Edges)
    elif type == "prims":
        menu = geo.generateGroupMenu(group_types=hou.geometryType.Primitives)
    else:
        menu = geo.generateGroupMenu()

return menu

Attribute Selection

Simple Attribute Selection (Point Attributes)

The only modification needed is to select the type of attributes to find. Options listed here.

node = kwargs['node']
input = node.input(0)

if input:
    geo = input.geometry()
    menu = geo.generateAttribMenu(attrib_type=hou.attribType.Point)
    return menu
    
else:
    return ["", "No Input Connected"]

Full Attribute Selection

By default, the below script will select Point, Prim, and Vertex Attributes. The datatypes (options listed here) vary, as do the min and max sizes in order to demonstrate different ways to change or extend the code.

node = kwargs['node']
input = node.input(0)

if input:
    geo = input.geometry()
    menu = list(geo.generateAttribMenu(attrib_type=hou.attribType.Point, data_type=hou.attribData.Int, min_size=3))
    menu += list(geo.generateAttribMenu(attrib_type=hou.attribType.Prim, data_type=hou.attribData.String, max_size=1))
    menu += list(geo.generateAttribMenu(attrib_type=hou.attribType.Vertex, data_type=hou.attribData.Float, min_size=2, max_size=3))
    return menu

else:
    return ["", "No Input Connected"]

Attribute Values as Menu

Once in a while, you need to create a menu of the values of a specific attribute. An example could be if you want certain transform attributes to only apply to say, the polygons with their variant attribute set to "dog". The expectation in the below code is that you have a string attribute (called "targetAttrib" in this case) being used to say which attribute to target, but you could also hard-set it by typing
attribName = "variant"
for example.

node = kwargs["node"]
attribName = node.parm('targetAttrib').evalAsString()
input = node.input(0)

menu = []

if node:
    geo = input.geometry()
    findAttrib = geo.findPointAttrib(attribName)

    if findAttrib:
        type = geo.findPointAttrib(attribName).dataType()
        list_menu = None
        if type == hou.attribData.String:
            list_menu = geo.pointStringAttribValues(attribName)
        elif type == hou.attribData.Int:
            list_menu = geo.pointIntAttribValues(attribName)

        list_menu = list(dict.fromkeys(list_menu))
        list_menu = sorted(list_menu)
            
        for value in list_menu:
            if value not in menu:
                menu.append(str(value))
                menu.append(str(value))

return menu

List all Parameters on a node (Int and Float only)

This splits parameter names so they appear in an order designed to find "random" controls first. Sorts by parameters that have the following in their names, in this order: seed -> iter -> parameters with size 1 -> parameters with size 3.
FURTHERMORE, it is designed to work inside of a multiparm so you will see kwargs['script_multiparm_index'] which represents the mparm number. It also adds separators into the menu if you don't know how to do that.

def allParmTemplates(group_or_folder):
    """
    Creates a generator (iterator) I can use to access ALL parmTemplates in a folder, including the folders.
    
    """
    for parm_template in group_or_folder.parmTemplates():
        yield parm_template

    # Note that we don't want to return parm templates inside multiparm
    # blocks, so we verify that the folder parm template is actually
    # for a folder.
        if parm_template.type() == hou.parmTemplateType.Folder and parm_template.isActualFolder():
            for sub_parm_template in allParmTemplates(parm_template):
                yield sub_parm_template

hda = hou.pwd()
node = hda.parm(f"node_var{kwargs['script_multiparm_index']}").evalAsNode()
seeds = []
iters = []
parms = []
vectors = []
if node:
    for parm_template in allParmTemplates(node.parmTemplateGroup()):   
        
        if parm_template.type() in [hou.parmTemplateType.Int, hou.parmTemplateType.Float]:
            if parm_template.numComponents() > 1:
                parm_name = f"{parm_template.name()} "
                if parm_template.namingScheme() == hou.parmNamingScheme.Base1:
                    parm_name += "123 (Choose One)"
                if parm_template.namingScheme() == hou.parmNamingScheme.XYZW:
                    parm_name += "xyz (Choose One)"
                if parm_template.namingScheme() == hou.parmNamingScheme.XYWH:
                    parm_name += "xywh (Choose One)"
                if parm_template.namingScheme() == hou.parmNamingScheme.UVW:
                    parm_name += "uvw (Choose One)"
                if parm_template.namingScheme() == hou.parmNamingScheme.RGBA:
                    parm_name += "rgba (Choose One)"
                vectors.append(parm_name)
                vectors.append(parm_name)
            else:
                if parm_template.name().find("seed") != -1:
                    seeds.append(parm_template.name())
                    seeds.append(parm_template.name())
                elif parm_template.name().find("iter") != -1:
                    iters.append(parm_template.name())
                    iters.append(parm_template.name())
                else:
                    parms.append(parm_template.name())
                    parms.append(parm_template.name())
    if len(seeds) > 0 and len(iters+parms+vectors) > 0:
        seeds.append("_separator_")
        seeds.append("_separator_")
    if len(iters) > 0 and len(parms+vectors) > 0:
        iters.append("_separator_")
        iters.append("_separator_")
    if len(parms) > 0 and len(vectors) > 0:
        parms.append("_separator_")
        parms.append("_separator_")
    return seeds+iters+parms+vectors

else:
    return ("Node is invalid", "Node is invalid")
Tagged: HDA · Menu Script · Tool Building · UI UX
Comments

No Comments.

Leave a replyReply to

Contact

Calgary, AB | Canada
edward@jaworenko.design | Email
LinkedIn | Connect with me
Milanote | Knowledge Dump
Sketchfab | Tutorials in 3D
Thingiverse | 3D Printers & Physical Design
Artstation | 3D Art (updating)
Behance | Formal Design (updating)
Vimeo | Motion & Animation
Pinterest | Things that inspire me
Instagram | Sometimes I post things

© Edward Jaworenko 2023

Back to top Arrow