Channel Pose Constraint with retime functionality

Hey there,

since a long time we see the need for a fast and intuitive way to retime objects. Houdinis default way is to use chops as the way to go. With the help of an channel and warp node, chops enables us to do exactly what we want. The downside of this approach is, that we need to care about units, sampling rates, start/end, etc. For a typical employee it takes some minutes to set everything up.

Our approach is much more simple and pragmatic. With the help of vtorigin and vrorigin we grab the world transforms of the target object, and retime the fcurves with the chf function. It’s as simple as that. Limited functionality, but no more worries about sampling or framerates. Here is a video that is demonstrating the script:


import toolutils
import hou

selected_node = ""
selected = hou.selectedNodes()

if selected:
    selected_node = selected[0]
    scene_viewer = toolutils.sceneViewer() 
    selected_objects = scene_viewer.selectObjects("Select the Object to Constrain. To finish press Enter. \n If nothing is selected, an Object will be generated.",allowed_types = ["*"])
    if selected_objects:
        selected_node = selected_objects[0]
        obj = hou.node("/obj")
        selected_node = obj.createNode("null", "globalTransform")
if selected_node:
    scene_viewer = toolutils.sceneViewer() 
    target_objects = scene_viewer.selectObjects("Select the Goalobject to Constrain to. To finish press Enter.",allowed_types = ["*"])
    if target_objects:
        target_object = target_objects[0]        
        hou.ui.displayMessage("Nothing Selected. Abort constraining")

if target_object:
    parm_group = selected_node.parmTemplateGroup()
    parm_folder = hou.FolderParmTemplate("extras", "Extras")
    parm_folder.addParmTemplate(hou.FloatParmTemplate("currentFrame", "Current Frame", 1))
    parm_folder.addParmTemplate(hou.FloatParmTemplate("target_t", "Target Translate", 3))
    parm_folder.addParmTemplate(hou.FloatParmTemplate("target_r", "Target Rotate", 3))
    selected_node.parm("target_tx").setExpression('vtorigin("", "' + str(target_object.path()) + '")[0]' )
    selected_node.parm("target_ty").setExpression('vtorigin("", "' + str(target_object.path()) + '")[1]' )
    selected_node.parm("target_tz").setExpression('vtorigin("", "' + str(target_object.path()) + '")[2]' )
    selected_node.parm("target_rx").setExpression('vrorigin("", "' + str(target_object.path()) + '")[0]' )
    selected_node.parm("target_ry").setExpression('vrorigin("", "' + str(target_object.path()) + '")[1]' )
    selected_node.parm("target_rz").setExpression('vrorigin("", "' + str(target_object.path()) + '")[2]' )
    selected_node.parm("currentFrame").setExpression('$FF' )
    selected_node.parm("tx").setExpression('chf("target_tx", ch("currentFrame"))' )
    selected_node.parm("ty").setExpression('chf("target_ty", ch("currentFrame"))' )
    selected_node.parm("tz").setExpression('chf("target_tz", ch("currentFrame"))' )
    selected_node.parm("rx").setExpression('chf("target_rx", ch("currentFrame"))' )
    selected_node.parm("ry").setExpression('chf("target_ry", ch("currentFrame"))' )
    selected_node.parm("rz").setExpression('chf("target_rz", ch("currentFrame"))' )

Houdini: Animation Editor with just animated channels


Everyone who is coming from a Maya/Softimage background is wondering why the Houdini ChannelList has this strange scoping behavior. It loads by default all parameters with auto-add into the ChannelList.

To get only the animated ones there is an option:

I have prepared a script, if you want to stay with the default behavior (Add Parameters With Auto-Add to Channel List flag), but want the option to load the Animation Editor with just the animated parms.

First select the Nodes with the animation you want to edit. Then run the script. You’ll notice the script scopes/unscopes the channels, and leave you with a clean animation editor.

The script was a nice finger exercise 🙂


import hou

if hou.selectedItems():
    oSel = hou.selectedItems()
    for oObj in oSel:
        for parm in oObj.parms():
            if parm.isTimeDependent():
                    if str(parm.expression()) == "linear()":
                    if str(parm.expression()) == "bezier()":
                    if str(parm.expression()) == "constant()":
    print "nothing selected"

animationEditor = hou.ui.curDesktop().createFloatingPanel(hou.paneTabType.ChannelEditor)
animationEditor.setName("Animation Editor - Selection")

Houdini: find all unused materials

Hey here is a python function that returns all unused materials of a scene. After a while Houdini scenes tend to have more and more materials that are not needed. This function helps to find all stuff that has no dependency in the scene. I’ve added a searchContext List that defines all the types of materials we were working with. So if you are working with other renderes (like vray or renderman), it is needed to add this type of materials to the list. If you don’t know your type of material, you can use the function in the third line. Simply put this into the python source editor, select your material and hit apply. The console should give you informations over the type.



import hou

#debug Line if you want to add new searchContexts
#print hou.selectedItems()[0].type()

#findUnUsedMaterails function
#returns a tupple of materialpathes
def findUnUsedMaterials():
    #define an empty list that will returned in the end
    outList = []
    #set the search Contexts
    searchContext = [["mat", "principledshader::2.0"],\
                    ["mat", "redshift_vopnet"],\
                    ["mat", "redshift::Material"], \
                    ["mat", "materialbuilder"],\
                    ["mat", "arnold_materialbuilder"],\
                    ["shop", "RS_Material"],\
                    ["shop", "vopsurface"],\
                    ["shop", "redshift_vopnet"],\
                    ["shop", "arnold_vopnet"]]
    #loop over the searchContext variable
    for inContext in searchContext:
        #define the node Context Type, mat or shop
        if inContext[0] == "mat":
            node_type = hou.nodeType(hou.vopNodeTypeCategory(), inContext[1])
        if inContext[0] == "shop":
            node_type = hou.nodeType(hou.shopNodeTypeCategory(), inContext[1])
        #get all Instances of the Mat type
        Mats = node_type.instances()
        for Mat in Mats:            
            #set a checker variable for adding materials to the outList
            checker = 0
            #get all dependencies of the current material instance
            allDepents = Mat.dependents() 
            #check if there are dependencies
            if allDepents:
                #loop over all dependencies of the material instance
                for currDepents in allDepents:
                    #if there is an dependency set the checker to 1
                    if (currDepents.type().name() != inContext[1]):
                        checker = 1 
                #if the checker is still == 0, meaning that there is no dependency append the material path to the outList
                if(checker == 0):
                #if there is no dependency append the material paht the outList
    #return the list of unused materaials
    return outList

print findUnUsedMaterials()


PS: If you are a tough guy, loop over the returned list and delete the nodes 😀

unusedMats = findUnUsedMaterials()
for unusedMat in unusedMats:

Function of the Week: sample_direction_cone



today I’ll establish my personal Node/Function of the week. This week it’s the vex function “sample_direction_cone”.


The help says: “Returns a unit vector, i.e. a vector of length 1, based on u. Given uniform random u pairs of values in [0,1), the returned unit vectors will be uniform random and continuous with respect to u on the surface of the unit sphere, in the area within maxangle of the direction indicated by center.”


So what to use this for? My usual cases are randomization of velocity vectors, ray cast operations or additive spread-transforms of scattered pieces. XSI operators will know this kind of function from the ICE function “randomize vector by cone” – This is the equal Houdini function.

Here is an point wrangle example, that demonstrates the function in combination with an raycast function.

v@dir = sample_direction_cone(chv("initVector"),ch("Angle") ,set(ch("spreadU")*rand(@ptnum*20), ch("spreadV")*rand(@ptnum)));
vector pos;
vector uvw;
int prim = intersect(1, @P, @dir*20, pos, uvw);
@P = primuv(1, "P", prim, uvw);