Houdini: Tips and Tricks
General expressions:
$F - current frame (int)
$FF - floating point frame
$FSTART - start frame
$FEND - end frame
$HIP - current directory relative to scene file $NGRP - total number of groups in geo
Other useful stuff:
Got a lot of imported geo containers (for example from FBX import) and want them all in one geo subnet?
Object merge, then use wildcard! "../../pattern*
)
Fix flipped primitive normals
As you might know, primitive normals can be visualized in the viewport, but are not a geometry attribute such as "N" on vertices or points. That makes it hard to set them, especially when fixing stuff you cannot control. In my case OSM data had randomly flipped prims - luckily all should point in one direction. Grouping them with the VEXpression @N.y < 0
or somehing and then reverse
-ing the group did what I wanted.
Padded file outs:
$HIP/out`padzero(4,$F)`.jpg
will produce out0001.jpg.
Get # of groups
argc(primgrouplist("../box1"))
Camera focus on object (for DOF)
vlength(vtorigin(“.”, “../focus_object”))
Output node naming
# parent node named meaningfully
$HIP/small/`opinput(".", 0)`.png
$HIP/large/`opinput(".", 0)`.png
Reload viewport textures
in Houdini texport
glcache -c
Suggestion: Add this to the 'Clear Texture Cache' Button in the Texture tab. (Would not hurt if sidefx did the same)
Random switch input
(Change expression language to python and add a integer parameter called seed)
import random
ninputs = len(pwd().inputs())
seed = ch("seed")
random.seed(seed)
return random.randint(0, ninputs-1)
Stamp inside a switch
This is very handy if you want to switch between random inputs to create alternating copies. At first, let's prepare the copy SOP expression (in the stamp value field, of course)
fit01(rand($PT),0,(opninputs("../switch1")))
That means: output a random value between the number of inputs in the switch. Let's call this value "sw". Let's head back to "switch1" and enter the following in the "Select Input" field: stamp("../copy1","sw",0)
Lazy guy tip: If you want to alter probabilities but you are to lazy to do so in a function: Just connect the same input multiple times - it will of course be picked up with a higher chance by the copy SOP!
Select the first group of n groups (great for stamping between groups)
`arg(primgrouplist("../sop_node"),0)`
(backticks are for use in a string field, as in a group input)
Use Cop/Imp op as texture
put as image name:
op:/img/comp1/mosaic1
Proper curve sweep
Let's jump through some hoops to do copySOP-Curve-Magic:
- Create a curve.
- Resample it
- Drop a polyframe node: Normal Name: 'up', Tangent Name 'N'
- if you do not want the path to bank, drop an attribWrangle:
@up = {0,1,0};
- feed into copySOP
- endless joy
Stamp strings
`points("../nodeToGetStrFrom",stamp("../copySop","num",0),"type")`
in copy SOP: stamp num, $PT
Choose random group
Ever had that problem? You have 20 parts imported from an obj or something and want to scatter a random one over something?
There may be a really easy way, but this python snippet (don't forget to add a 'seed' channel) will do the trick.
import random
node = hou.pwd()
geo = node.geometry()
def choose_randomly(list_of_stuff, seed):
return random.Random(hou.frame() + seed).choice(list_of_stuff)
parts_available = []
groups = geo.primGroups()
for group in groups:
parts_available.append(group)
chosen_part = choose_randomly(parts_available, hou.ch('seed'))
deletelist = []
for group in parts_available:
if not group == chosen_part:
for p in group.prims():
deletelist.append(p)
geo.deletePrims(deletelist)
It will also delete all geometry not belonging to the chosen group.
Fetch attribute from other node
point("../myNode",0,"myAttribute",0)
Use "if" in expression field, e.g. in AttribCreate
if($ATTR > 1,5,0)
if the value of ATTR is above 1, set it to 5.
If can of course be nested:
if($ATTR > 1,if($ATTR < 4,0,5),0)
If ATTR is bigger than one and smaller than 4, set to 5, else set to zero.
For Each
# for each number
stamp("..", "FORVALUE",0)
# for each group
opdigits(stamps("..", "FORVALUE",""))
# seed when iterating groups
stamp("..", "FORIDXVALUE",0)
OSX: Return of the delete key
If you are vainly hammering on the delete key, trying to get rid of some sad node and all you see is a list of operators, this will help.
Edit > Hotkeys...
/Houdini/Panes/Network Editor/Add Operator
Delete Backspace as entry
/Houdini/Panes/Network Editor/Delete:
Add Backspace as entry
Slow save speeds over fast network?
Edit houdini.env!
Windows: %HOME%/houdiniX.X/houdini.env
Mac: ~/Library/Preferences/houdini/X.X/houdini.env
Linux: ~/houdiniX.X/houdini.env
use HOUDINI_BUFFEREDSAVE = 1
and HOUDINI_DISABLE_SAVE_THUMB = 1
First and last curve point
Sometimes (for example in curves) I want to select the first (easy: '0' :) and the last point. There has to be a better way, but $NPT-1 did not work for me. Instead I used
`npoints("../node")-1`
of a node in the tree above to select the last point by number.
A second method would be to group the points and set
0 $N
as pattern. Seems to be a better option.
Length of a curve
arclen("../curve1", 0, 0, 1)
Ramp parameter
The ramp parameter is a great and easy way to map a value and control it interactively. That way, procedural effects (e.g. in animations) that look very linear can look more natural. Also, it's an easy way to quickly reverse a progression, pingpong it or any other modification.
# usage:
chramp(ramp_path, position, component_index)
# example:
chramp("local_rampparam", ch("../controller/myProgressionValue"), 0)
The last value is the component index, so using a float ramp, that would be a '0'. If it's an RGB ramp, it corresponds to the color components. Also note that the position must range from 0 to 1, so taking the above example, a practil way of using chramp() would be
where oldmin would be the minimum of ch("../controller/myProgressionValue") and oldmax it's maximum.