new blog

I created new (and hopefully better) version of this blog:

I will also maintain a Github of custom Python and VEX functions:
Thank you very much for your visits on this blog, I hope that I do it right and that it helped anyone. This “old” will be closed soon, but all the info from this blog is transferred.

UI markup

I have extended the set of VEX UI markup. Python script reads comments in the wrangle snippet and generates parms:

  • toggle parm, when the chi() comment is // toggle
  • chs() Node type or File type reference parm, if the comment is // node or // file
  • separators
  • labels

I detect also // parent or // global comment to reference parameter to a global settings node, even a ramp (I had to do opmultiparm).

I also have a “preview” toggle system, and the toggle can be global (or parent) so you can have more “lowres” parameters inside Geo or anywhere in the /obj.

Still working

I am still working on the “channels from midi” tool (alongside with current jobs), I am really looking forward to publish it. I decided to detect used custom functions in wrangles and create useful buttons on the fly.

Network Editor, remember the previously flagged node and unflag to it

I like to walk through the nodes and preview different states of the stream. Houdini has its own logic to “unflag” the Display Flag. It doesn’t jump back to its last position. I customized my own unflagging to the previously flagged node. If you like it, you may use my script (and hotkey it). Or you may customize it to fit your needs, for example always unflag to the last node.

import hou
import toolutils

parm_pane = hou.ui.curDesktop().paneTabOfType(hou.paneTabType.Parm)
parmnode = parm_pane.currentNode()
containername = parmnode.parent().name()

nodetype = "multiflag"

if hou.sopNodeTypeCategory() == parmnode.parent().childTypeCategory():
    nodetype = "singleflag"

if hou.dopNodeTypeCategory() == parmnode.parent().childTypeCategory():
    nodetype = "singleflag"

## unflag to the last flagged ##

if nodetype == "singleflag":

    # get this
    if len(hou.selectedNodes()) == 0:
        thisnode = parmnode
        thisnode = hou.selectedNodes()[0]
    thispath = thisnode.path()

    # get last
    lastpath = hou.getenv("unflagged", thispath)
        #node exists
        if hou.node(lastpath).parent().name() != containername:
            lastpath = thispath
        #node was deleted / renamed
        lastpath = thispath
    # get flag
    flagpath = hou.getenv("flag", thispath)
        #node exists    
        if hou.node(flagpath).parent().name() != containername:
            flagpath = thispath
        #node was deleted / renamed
        flagpath = thispath

        flag = hou.node(thispath).isDisplayFlagSet()
        flag = 2

    if flag == 0 :
        # setting clean
        setpath = thispath
        unflag = flagpath
        hou.putenv("unflagged", unflag )

        # set
        hou.putenv("flag", setpath )    
        setnode = hou.node( setpath )

    if flag == 1 :
        # setting flagged
        setpath = lastpath
        unflag = thispath
        hou.putenv("unflagged", unflag )

        # set
        hou.putenv("flag", setpath )    
        setnode = hou.node( setpath )


#####  not SOP, just swap  #####

if nodetype == "multiflag":

    # none is selected
    if len(hou.selectedNodes()) == 0:
        parmnode.setDisplayFlag(not parmnode.isDisplayFlagSet())
    # for all selected    
    for node in hou.selectedNodes():
            node.setDisplayFlag(not node.isDisplayFlagSet())
            hasnoflag = True


Network Editor, Layout All and respect “pinned” nodes’ positions

tldr; I scripted my custom “layout all nodes” which respects fixed positions of “pinned” nodes. The code is below.

I was still struggling with the Network Editor’s “layout all” nodes. I liked it most of the times but sometimes I wanted some nodes (or branches) to stay where they were. The best solution I came with was this:

  • hotkey to pin(unpin) selected node(s) to their position … I do this by setComment
  • I pin just one node in a branch (or single node, camera, light, ropnet) … sometimes the last “OUT”, sometimes the first “Object merge” …
  • I have replaced the L hotkey to run “my layout tool” which goes through these python steps:
    • 1) find all the pinned nodes and remember their positions
    • 2) classic layoutChildren(), which gives nice layout to the branches
    • 3) reposition the branches to their previously stored pins
    • 4) as a bonus, it places chopnets next to appropriate channelsop (naming convention applies)


  • Michael Goldfarb filed two RFEs (thanks again) and if you like this idea, you may refer to them:
    • RFE – #87728 – pin nodes
    • RFE – #87729 – link node positions

This script pins/unpins the selected node(s)
I use the Ctrl-L hotkey

### swap pinned / unpinned state
for node in hou.selectedNodes():

    if node.comment() == "`" :

This script layouts all the nodes
I use the L hotkey

import hou
import toolutils

def getConnectedNodes(node,got):
    iteration = []

    inout = []
        inout += node.inputs()
        has_no_inputs = 1
        inout += node.outputs()
        has_no_outputs = 1

    for n in inout:
        if n not in got:
            iteration += getConnectedNodes(n,got)

    return iteration

########   the pins    #########

parm_pane = hou.ui.curDesktop().paneTabOfType(hou.paneTabType.Parm)
containernode = parm_pane.currentNode().parent()

childrenArr = containernode.children()
pinStatesArr = []
pinNodesArr = []
positionsArr = []

# store all positions

for child in childrenArr:
    pinned = 0
    if child.comment() == "`" :
        pinned = 1
        pinNodesArr.append( child )
    pinStatesArr.append( pinned )
    positionsArr.append( child.position() )
# store pinned positions

for pinNode in pinNodesArr:
    pos = pinNode.position()
    comment = str(pos[0]) + "," + str(pos[1])

# classic layoutChildren()


# reposition the branches

for pinNode in pinNodesArr:

    pinpos = pinNode.comment()
    pinpos = pinpos.split(",")
    pinpos = [ float(pinpos[0]) , float(pinpos[1]) ]
    allConnected = []
    allConnected = getConnectedNodes(pinNode,allConnected)
    pos = pinNode.position()
    move = [ pinpos[0] - pos[0] , pinpos[1] - pos[1] ]
    for connected in allConnected:
            no_connected = 1
    # cleanup
########  chop to chan  ########

# define offset
offsetx = 3
offsety = 0

containerpath = containernode.path()

# find all chopnets
chopnets = hou.node(containerpath).glob("chopnet_*")

# for all
for ch in chopnets:
    xxx,task ="_")
    # appropriate channel node
    path_channel = containerpath + "/channel_" + task
    path_chopnet = containerpath + "/chopnet_" + task
    node_channel = hou.node(path_channel)
    node_chopnet = hou.node(path_chopnet)    
    # get
    posx = node_channel.position()[0] + offsetx
    posy = node_channel.position()[1] + offsety

    # set
    node_chopnet.setPosition( [posx,posy] )

Custom VEX functions

I store them here, on Windows:

I include them in a wrangle like this:
#include "qq.vfl"

Here is the content of the qq.vfl:

void clamp01(float value)
    value = clamp(value,0,1);    

float falloff_linear(vector sample_pos, from, to) //unclamped
    vector u = normalize(to - from);
    vector v = sample_pos - from;

    float eval = dot(u,v) / length(to - from);
    return eval;

float falloff_radial(vector sample_pos, center, radius) //unclamped
    float max = length(radius - center);
    float eval = length(sample_pos - center) / max;
    return eval;

vector farthest_point_position(vector from) //run over detail of input 0
    int count = npoints(0);
    float maxdist = 0;
    vector eval;

    for (int pt = 0; pt < count; pt++)
        vector sample_pos = point(0,"P",pt);
        float distance = length(sample_pos - from);
        if (distance > maxdist)    
            maxdist = distance;
            eval = sample_pos;
    return eval;

vector farthest_point_distance(vector from) //pointcloud of input 0
    int maxpts = npoints(0);
    int handle = pcopen(0, "P", from, radius, maxpts);
    float eval = pcfarthest(handle);
    return eval;

 void bounding_vector (vector direction_from, direction_to, result_from, result_to) //run over detail of input 0
    vector u, v, pos, center;
    float distance, mindist, maxdist;
    int count = npoints(0);

    u = normalize(direction_to - direction_from);
    mindist = 1e10;
    maxdist = -1e10;

    for (int pt = 0; pt < count; pt++)
        pos = point(0,"P",pt);
        v = pos - direction_from;
        distance = dot(u,v);

        maxdist = max(distance, maxdist);
        mindist = min(distance, mindist);

    v = getbbox_center(0) - direction_from;
    center = dot(u,v);

    result_to = (maxdist - center) * u + getbbox_center(0) ;
    result_from = (mindist - center) * u + getbbox_center(0) ;

float float_hittest(float try, stored, maxdist) //clamped
    float eval, trywide;
    trywide = fit01(try, -maxdist , 1+maxdist);

    if ( maxdist>0 )
        float omin = stored - maxdist;
        float omax = stored + maxdist;
        eval = fit(trywide, omin, omax, 0, 1);
    } else {
        if ( trywide > stored )
            eval = 1;
        } else {
            eval = 0;

    return eval;


toolset: generate animation channels from notes (music) work in progress

Little vex/python toolset to generate animation from ♪♫ notes.

For now, the notes can be
– added manually
– generated from tuples (python)

Next step is to
– implement MIDI import
– implement MIDI velocities

Please, I have to decide which software should I buy and learn, to edit MIDI.
Should I buy Ableton? It also has nice “Audio to MIDI” feature.

Comments are REALLY highly welcome, especially from anyone who does music. The tool is intended to be free for anybody. Download link is here. But it really is under development, so excuse the “fragile construction”.

Object Merge SOP, new node references the selection

# define
offsetx = 3
offsety = 0
color = hou.Color(0.0, 0.0, 0.0)

node_src = hou.selectedNodes()[0]
name_src =
name_src = name_src.split("_")[-1]

posx = node_src.position()[0] + offsetx
posy = node_src.position()[1] + offsety

#create, name, pos
container = node_src.parent().path()
name_mrg = "IN_" + name_src
node_mrg = hou.node(container).createNode('object_merge',name_mrg)
node_mrg.setPosition( [posx,posy] )

path_src = node_src.path()
parm = node_mrg.parm("objpath1")



Toolset: “code shortcuts” into “vex snippets” and interface update (chf, chi, chramp) from comments

I did a little python toolset to speed up my VEX writing. It replaces “code shortcuts” into “code snippets” and also updates the parm interface (default values, channel ranges from comments, ramps are generated from custom library). You can build your own libraries of snippets with specific channel ranges and defaults.

All the information and source files are stored here, on odforce.

The initial idea comes from here, Matt Estela.

Little Python Shelf Tools

I have few little python snippets like this, in a shelf. Some of them bound to hotkeys. Btw with the Autohotkey, you can also remap to any Key + Scroll wheel. Thanks goes to the community. I did not built those scripts from scratch, other smart people did (graham, galagast, mestela, and others).

Toggle manual / auto update

Thanks to berniebernie!

import hou
mode = hou.updateModeSetting().name()
if mode == 'AutoUpdate':
if mode == 'Manual':

Activate OpenCL on all subchildren

for node in hou.selectedNodes():
    for subnode in node.allSubChildren():
        for p in subnode.parms():
                except hou.PermissionError: 

Scope all animated channels

nodeArr = hou.node("/").allSubChildren()
for n in nodeArr:
    if not n.isInsideLockedHDA():
        parmArr = n.parms()
        for p in parmArr:
            if p.isTimeDependent():

Toggle Display points with global hotkey

pane = hou.ui.curDesktop().paneTabOfType(hou.paneTabType.SceneViewer)
settings = pane.curViewport().settings()
markersDisplayModel = settings.displaySet(hou.displaySetType.DisplayModel)
markersSceneObject = settings.displaySet(hou.displaySetType.SceneObject)

# Toggle the markers (visible when inside /obj/...)
markersDisplayModel.showPointMarkers(not markersDisplayModel.isShowingPointMarkers())

# Unify with the scene (visible when in top level "/obj")

Little snippets to navigate the network view with kbd

# hotkey alt-shift-r
hou.playbar.setRealTime(not hou.playbar.isRealTime())

# hotkey ctrl-ins
for node in hou.selectedNodes():
    node.setRenderFlag(not node.isRenderFlagSet())

# hotkey ins
for node in hou.selectedNodes():
    node.setDisplayFlag(not node.isDisplayFlagSet())

# hotkey ctrl-del
for node in hou.selectedNodes():
    node.bypass(not node.isBypassed())

# hotkey ctrl-shift-ins
for node in hou.selectedNodes():
    node.setTemplateFlag(not node.isTemplateFlagSet())

# hotkey Z
pane = hou.ui.curDesktop().paneTabOfType(hou.paneTabType.SceneViewer)

# hotkey shift-Z
pane = hou.ui.curDesktop().paneTabOfType(hou.paneTabType.SceneViewer)

Wedge HScript Render Takes Automation (filename, cam, framerange)

In the current project, I have lots of takes, multiple cameras and different frame ranges. So I use the Wedge ROP (By Take all the takes) and I have named my takes this way:

  • name_startframe_endframe, e.g.:
    • “magnetic_400_2200”
    • “terrain_600_1800”

Name is descriptive, for further comping. The first number is Start of the frame range, the second number is the End of the frame range.

I have many different ROPs to set up the renders. In the ROP, I automate the file names and frame ranges with HScript expressions. For example this one is to extract the Start Frame or End Frame from the name of the take:

    string take = chsop("take");
    float first = index(take, "_");
    float last = rindex(take, "_");
    float start = first + 1;
    float length = last - first - 1;
    string startFrameString = substr(take, start, length);
    float  startFrame = atof(startFrameString);
    float start = last+1;
    float length = strlen(take) - last - 1;
    string endFrameString = substr(take, start, length);
    float  endFrame = atof(endFrameString);
    return startFrame;

This expression is to generate the folder and filename (Output Image) e.g.

    string take = chsop("take");
    string camera = chsop("camera");
    float first = index(take, "_");
    float start = 0;
    float length = first;
    string takeName = substr(take, start, length);

    string camName = substr(camera, 5, 5);
    string takeName += "_" + camName;
    string output = "$HIP/render/";
    string output += takeName + "/" + takeName;
    string output += "_$F4.png";
    return output;


VEX Banked Turn

Thanks to Javier Toledo for the initial script. I have added the side_mult_error multiplier and few other things.

Polyframe the @tangentu, correct @up and gen @side

//create perpendicular SIDE
v@up = {0,1,0};
v@side = cross(v@up, v@tangentu);

//correct the UP
v@up = cross(v@tangentu, v@side);
v@N = v@tangentu;


Add Curvature and then Smooth

int prevId = clamp(@ptnum-1,0,@numpt);
int nextId = clamp(@ptnum+1,0,@numpt);

vector prevPos = point(0,"P",prevId);
vector nextPos = point(0,"P",nextId);

vector prevSide = point(0,"side",prevId);
vector nextSide = point(0,"side",nextId);

float side_mult_error = chf("side_mult_error") * 0.001;

vector prevDisp = prevPos + prevSide * side_mult_error;
vector nextDisp = nextPos + nextSide * side_mult_error;

float dist = length(prevPos - nextPos);
float dispDist = length(prevDisp - nextDisp);

//ratio by distances
f@ratio = (dispDist/dist) - 1;

f@ratio *= chf("curvature_mult") * 10;
f@ratio /= side_mult_error;
f@ratio = clamp(f@ratio, -chf("curvature_clamp"), chf("curvature_clamp"));

//rotate matrix
matrix3 rot = ident();
rotate(rot, f@ratio, v@tangentu);

v@side *= rot;
v@up *= rot;


VEX Carve

Thanks a lot to Andrew for the initial script. I have rewritten it to work on open curves. I found the setpointattrib to be slow. UV texture is faster to get the uv.x, instead of computing the arclength in the prim wrangle.

vector getlinepos(int primnum; float u) {
    vector pos = primuv(0, "P", primnum, set(u,0,0));
    return pos;

int    createpoint(int primnum; vector pos) {
    int ptnum = addpoint(primnum, pos+{0.5,0,0});
    int vertex = addvertex(0, primnum, ptnum);
    return vertex;

int oldprim = @primnum;
int count = primvertexcount(0,@primnum)-1;

if(!(hasprimattrib(0, "start"))) f@start = ch("start");
if(!(hasprimattrib(0, "end"))) f@end = ch("end");

if (@start<@end){
    int newprim = addprim(0, "polyline");
    createpoint(newprim, getlinepos(oldprim, @start));
    //from prim
    int startvert, endvert;
    startvert  = 1 + floor(@start * count);
    endvert    = 1 + floor(@end * count);
    endvert    = min(endvert, count);
    for (int i = startvert; i < endvert; i++)
        createpoint(newprim, point(0, "P", i));
    createpoint(newprim, getlinepos(oldprim, @end));
} else {
    int newprim;
    newprim = addprim(0, "polyline");

    //from prim
    int startvert, endvert;
    startvert  = 1 + floor(@start * count);
    endvert    = 1 + floor(@end * count);
    endvert    = min(endvert, count);
    for (int i = 0; i < endvert; i++)
        createpoint(newprim, point(0, "P", i));
    createpoint(newprim, getlinepos(oldprim, @end));

    newprim = addprim(0, "polyline");
    createpoint(newprim, getlinepos(oldprim, @start));

    for (int i = startvert; i <= count; i++)
        createpoint(newprim, point(0, "P", i));


removeprim(0, oldprim, 1);


DOP Rigid Body Simulation

DOP theory

  • w attribute = angular velocity
  • understand the dop object as container, which can contain thousands of rigid body packed fragments (or packed geometries)
  • pack name – “piece”
  • pack fragments vs packed geometries – packed fragments stored in one memory location and fragment is sublocation
  • the Bullet solver automatically detects objects that are initially overlapping and prevents them from colliding/exploding until they separate (since H12.5)
  • passive objects can be collided with by active objects, but don’t move, and are not affected by forces
  • walls/floors, raying a grid to get the shape and then extruding the faces into unconnected little cubes speeds up collisions

Fractured Rigids SOP

Pack SOP
  • no materials until unpack
  • orientation etc. stored in Primitive Intrinsics
  • fast view as Point Cloud
  • path: op:`opfullpath(‘.’)`
Connectivity SOP
  • attribs from polygon connectivity
Partition SOP
  • groups from rule
  • group_$CLASS (@class being prim attrib created by connectivity SOP)
Exploded view SOP
  • poly connectivity from attribs
Packed Edit SOP
  • can change visibility etc
Name SOP
  • name from group
Voronoi SOP
  • poly connectivity from VDB/scatter
Voronoi Fracture Points SOP
  • adds points to surface/interior depending on position of Impact point
Assemble SOP
  • finds connected islands of geometry
  • gives name to pieces
  • packs into Packed Fragments (not packed geometries)

Fractured Rigids DOP

RBD Packed Object DOP
  • use SOP path
  • Geometry Representation (Convex Concave etc)
  • BULLET is made for Convex, Concave is slow with thousands of pieces
  • Collision Compound
    • VDB to spheres, Bake ODE, Merge
  • use Collision Padding (0 = same as geo, 0.1 = far from)
RBD Object DOP
  • same as above
  • use SOP path
RBD Point Object
  • creates a new simulation object at each point of a source geometry object
  • useful for simulating a large collection of identical objects
Static Object
  • its motion is not controlled by the simulation
Rigid Body Solver DOP
  • solver types
  • RBD was built in solver, not used much anymore
  • ODE never use
  • Fracture – we don’t use
Merge DOP
  • defines collision
 POP forces
  • can be used to drive rigid body simulation (packed as points)
  • wire node into Post-solve or Pre-solve
Gravity DOP
  • does not create force attribute on point
  • creates “../Forces/Gravity”


Input path

//this digits

//this path

//parent's digits, 0 is the input

//string detail from the first input
`details(0, "myname")`

//from opname "node_MY" to "MY"
`strreplace(opname("."), "node_", "")`
Relative op: references inside POP VEX expression

The path after op: must be an absolute path (that is, starting from the root of the scene hierarchy with /). However, you can work around this by converting a relative path to an absolute path using the opfullpath expression function inside backticks:



VEX snippets

regex string replace

thanks to F1!

@name = re_replace(r'(.+?)\d+', r'\1', @name);

from space – to space – and back again

thanks to F1!

vector P = ptransform("/obj/cam1", @P);

vector pos = set(abs(P.x), abs(P.y), @Time);
vector noise = efit(vector(noise(pos)), 0, 1, -1, 1);
vector mirror = set(sign(P.x), sign(P.y), 1);

@P = ptransform("/obj/cam1", "space:current", P + noise * mirror);

for each group

thanks to acey195!

string grps[] = detailintrinsic(0, "primitivegroups");
foreach(string grp; grps)
    int groupCenterPt = addpoint(0,getbbox_center(0, grp));


to and from camera space

thanks to F1!

@P = toNDC(chs("camera"), @P);
@P.z = -ch("depth");
@P = fromNDC(chs("camera"), @P);

evaluate @ as string

thanks to Jake Rice!

int selector = chi("test");
string group = "\@class=" + itoa(selector);
int handle = pcfind(0, group, "P", @P, 10, 10);


Find min / max value

thanks to petz!

setdetailattrib(geoself(), "min_val", @uv.y, "min");
setdetailattrib(geoself(), "max_val", @uv.y, "max");


List of Primitive neighbours by half edges

thanks to petz!

int     prim_edge, edge, prim, i, n, num;
string  neighbours = "";

i = 0;
prim_edge = primhedge(@OpInput1, @primnum);
while(i < primvertexcount(@OpInput1, @primnum))
    num = hedge_equivcount(@OpInput1, prim_edge);
    n = 0;
    while(n < num)
        edge = hedge_nextequiv(@OpInput1, prim_edge);
        prim = hedge_prim(@OpInput1, edge);
        if(prim != @primnum)
            neighbours += sprintf("%g ", prim);

        prim_edge = edge;

    prim_edge = hedge_next(@OpInput1, prim_edge);

s@neighbours = neighbours;


Insert a point to the middle of the curve

thanks to awong from discord

int prim_num = 0;
int new_point = addpoint(0, {0,0,0});
int insert_position = 3;

// store the old vertices
int old_vertices[] = primvertices(0, prim_num);

// add a placeholder using an arbitrary point
addvertex(0, prim_num, 0);

// replace the vertex at the desired position
setvertexpoint(0, prim_num, insert_position, new_point);

// replace the vertices after the inserted position
for(int i = insert_position; i < len(old_vertices); i++)
    setvertexpoint(0, prim_num, i + 1, old_vertices[i]);

U coordinate on closed prim

thanks to f1480187

// Detail wrangle.
float length = primintrinsic(0, "measuredperimeter", 0);

// Set to false if value of 1.0 is unwanted for the last point
// of closed prims and instead need to be treated as if point with
// u=1 is coincident with the first point (like in unrolled prims).
int as_open = true;

// Compensate for closing auto-edge length.
if (as_open && primintrinsic(0, "closed", 0))
    vector auto_edge = point(0, "P", 0) - point(0, "P", @numpt-1);
    length -= length(auto_edge);

// Compute curve u.
float passed = 0;
for (int i = 1; i < @numpt; i++)
    vector d = point(0, "P", i) - point(0, "P", i-1);
    passed += length(d);
    setpointattrib(0, "u", i, passed/length);

Print to console

printf("OUT=%f;  ", out); //  %s string,   %i integer

Compilable For Each Material

s@shop_materialpath = "/obj/butterflies/shop/hud";
s@shop_materialpath += itoa( detail(1,"iteration") %10 );

Random unit vector in the direction

thanks to Javier

vector center = {0,0,1};
float  maxangle = chf("maxangle");
v@N = sample_direction_cone(center, maxangle, rand(@ptnum) );

Other inputs

v@P.y = v@opinput1_P.y;
f@y = v@opinput1_P.y;

//2015: @opinput1_P binds as a float


@group_my = 1;
if (@group_my) v@N = {0,1,0};

Group adhoc syntax / expression

@myattr="foo bar"
@ptnum%(@numpt-1)==0 // first and last point


s@name = sprintf("piece%d", @class);
s@name = sprintf("nameX%dY%d",@P.x,@P.y);

// padzero
int padzero = chi("padzero");
string format = '%0' + itoa(padzero) + 'd';
s@frame = sprintf(format, @Frame) ;

Time Globals

@Time //Float time ($T)
@Frame //Float frame ($FF)
@SimTime //Float simulation time ($ST), only present in DOP contexts.
@SimFrame //Float simulation frame ($SF), only present in DOP contexts.
@TimeInc //Float time step (1/$FPS)


vector min, max;
getbbox(0, min, max);
v@center = (min+max)/2;

Attrib type info

setattribtypeinfo(0, "point", "myvector", "vector");


==, !=, <, <=, >, >=

The logical (&&, ||, and !) and bitwise (& |, ^, and ~) operators are only defined for integers. AND OR:

if( res.x<precis || t>tmax ) break;

Shortcut to if statement

float a = condition? b: c;




Find and delete ngons

int np[] = primpoints(0,@primnum);
int lnp = len(np);

if( lnp > 3 ) {
    removeprim(0, @primnum, 1); // 1 = and points

P smooth

int maxpoints = chi("maxpoints");
float radius = chf("radius");

int handle = pcopen(0, "P", @P, radius, maxpoints);
@P = pcfilter(handle,"P");

Init Attrib Interpolate

//random prim
int prim = floor( rand(@ptnum) * nprimitives(1) );
i@sourceprim = prim;

//random speed, looping
float speed = fit01( rand(@ptnum), chf("speed_min"), chf("speed_max") );
float dist = (@Time * speed) % 1;
v@sourceprimuv = set(dist,0.5,1.0);

Prims from point array

thanks to @petz from odforce:

int point = addpoint(0, @P);
int points[] = primpoints(0, @primnum);
for(int i = 0; i < len(points); i++)
    int point_array[] = array(points[i - 1], points[i], point);
    int prim = addprim(0, "poly", point_array); 
removeprim(0, @primnum, 0);


The geometry functions, table from the docs

vertexpoint(), pointvertex(), vertexnext(), vertexprev(), vertexindex(), primvertexcount(), vertexprim(), vertexprimindex()

Write point numbers to an array

thanks to @petz from odforce

//in detail mode
i[]@points = expandpointgroup(@OpInput1, "!");

//in point mode
int point[] = array(@ptnum);
setdetailattrib(geoself(), "points", point, "append");


#include <groom.h>

adjustPrimLength(0, @primnum, @perimeter, @perimeter*@dist);


thanks to

@gl_wireframe = true;
@gl_lit = true;

Split string

thanks to Chris,

//Split based on '_'
string bars[] = split(s@shop_materialpath, "_");

//Use last element as attribute value
s@mtlGrpName = bars[-1];

0-360 angle between vectors

thanks to f1480187

#define PI 3.1415926535897932384

float angle = acos(dot(v@up, v@aim));
int first_half = sign(dot(v@z, cross(v@up, v@aim))) >= 0;
angle = first_half ? angle : 2*PI - angle;

@angle = degrees(angle);


append() // end
push() // end
insert() // index (negative from the end, inserting past the end will grow the array)


VEX Quaternion orient, N, up

Without @orient:

v@N; // +Z axis of the copy
v@up; // +Y axis of the copy

Not ambiguous @orient, thanks to Matt Estela!

matrix3 m = maketransform(@N,@up);
@orient = quaternion(m);

Representing a quaternion from an angle and axis. The angle is specified in radians:

float angle = radians(90);
vector axis = set(0, 1, 0);
p@rot = quaternion(angle, axis);

Representing a quaternion from a 3×3 rotational matrix:

float angle = radians(90);
vector axis = {0,1,0};
matrix3 m = ident();
rotate(m, angle, axis);
@orient = quaternion(m);

PI eight digits

#include "math.h";
float e = M_E; //2.7182818
f@angle = PI; //3.1415926
f@angle = radians(180);

quaternion to euler rotations
thanks to DJ

vector  qToE(vector4 q_value){
    float   q_0 = q_value.w ; 
    float   q_1 = q_value.x ; 
    float   q_2 = q_value.y ; 
    float   q_3 = q_value.z ; 
    vector  out = {0,0,0} ; 
            out.x = degrees(atan2(2*(q_0*q_1+q_2*q_3), (1-2*(q_1*q_1+q_2*q_2)))) ; 
            out.y = degrees(asin(2*(q_0*q_2-q_3*q_1))) ;
            out.z = degrees(atan2(2*(q_0*q_3+q_1*q_2), (1-2*(q_2*q_2+q_3*q_3)))) ;
    return out ; 

v@ro = qToE(p@orient);

VEX Arrays and Matrices

To create:

f[]@myFarray = {0,1,2,3,4,5,6};
i[]@myIarray = {0,1,2,3,4,5,6};
v[]@myVarray = {{1,2,3},{4,5,6}};
v@readvect = @myVarray[1];

3[]@myMatrixarray = { {0,0,0,0,0,0,0,0,0}, {0,0,0,0,0,0,0,0,0}};

matrix3 mat = {1,2,3,4,5,6,7,8,9};
3[]@myMatrixarray[0] = mat; //assign

Read from prim:

3[]@rotmatrixarray[index] = prim(0, "rotmatrix", index );

Read from attrib:

vector array[] = v[]@atrail;
vector position = array[i];


float solve_beats[] = {};
for (int i = 0; i<700; i++)
    if (val_curr >= 0.1) append(solve_beats, i);
int lenght = len(solve_beats);

Create empty, resize, faster:

resize(@array, len);

Pipeline notes

C4D camera properties

Sensor Size (Film Gate) = Aperture
Focal Length = Focal Length
1920 × 1080 – Focal Length = 150 mm (full = full)
1920 × 360 – Focal Length = 50 mm (third = third)
1920 × 360 – right cam = Screen Window X/Y = 1
1920 × 360 – left cam = Screen Window X/Y = -1


  "font_size": 11,
  "close_windows_when_empty": true,
  "translate_tabs_to_spaces": true,
Key bindings:
  { "keys": ["alt+shift+up"], "command": "swap_line_up" },
  { "keys": ["alt+shift+down"], "command": "swap_line_down" },  
  { "keys": ["ctrl+alt+q"], "command": "toggle_record_macro" },
  { "keys": ["ctrl+q"], "command": "close" },
  { "keys": ["f1"], "command": "vex_helpcard" },

  { "keys": ["alt+pagedown"], "command": "next_view" },
  { "keys": ["alt+pageup"], "command": "prev_view" },
  { "keys": ["ctrl+tab"], "command": "next_view" },
  { "keys": ["ctrl+shift+tab"], "command": "prev_view" },

Alt F3 – multiple cursors
Ctrl D – search and select
Ctrl Shift P – search command palette

Numbers, multiple cursors

Text Pastry

Package settings

Preferences > Package Settings > VEX

    "popup_max_width": 900,
    "popup_max_height": 900,
External editor file watcher





all the content in braces

all multilines

mozilla stylish

code {
    font-size: 15px;
    background-color: rgba(100, 100, 190, 0.2);
    padding: 0.1em;





Graphical Function Explorer url

VEX and PY gurus repositories (URLs)

Manuel Köster,
Tosin Akinwoye,
Elisha Hung,
Anastacia Opara
Stefan Sietzen,
Animatrix, Pusat
Matt Estela
Junichiro Horikawa

Shelf Tools and Hotkeys

This is just my little repository of custom hotkeys and little python tools



    #  Desktops            Ctrl+Alt+NUM

h.desktop05 "Desktop 5" "Switch to Desktop 5"    Alt+Ctrl+1
h.desktop06 "Desktop 6" "Switch to Desktop 6"    Alt+Ctrl+4
h.desktop07 "Desktop 7" "Switch to Desktop 7"    Alt+Ctrl+5
h.desktop08 "Desktop 8" "Switch to Desktop 8"    Alt+Ctrl+6

    #  Render              F12

h.tool:start_ren	start_ren	"Shelf Tool: start_ren"	 F12
h.tool:kill_ren	kill_ren	"Shelf Tool: kill_ren"	 F11

    #  Network             F

h.pane.wsheet.home_selected "Home Selected" "Home selected OP tiles"     Shift+H G F
h.pane.wsheet.frame_selected    "Frame Selected"    "Frame selected OP tiles"    Shift+F

    #  Network             Backspace

h.pane.chedit.toggle_list   "Toggle Channel List"   "Toggle channel list"    Tab
h.pane.gview.operator_menu  "Toolbar Menu"  "Pop up toolbar menu"    Tab
h.pane.jump_back    "Jump Back" "Jump back one step"     Alt+LeftArrow Backspace
h.pane.wsheet.add_op    "Add Operator"  "Add Operator"   Tab

    #  Network  Display                 Ins
    #  Network  Display + Render        Ctrl Ins
    #  Network  Bypass                  Ctrl Del    
    #  Network  Template                Ctrl Shift Ins
    #  Network  Selectable Template     Ctrl Alt Ins (TODO)
    #  Network  Clear templates         Ctrl Shift Del

h.tool:node_disp    node_disp   "Shelf Tool: node_disp"  Insert
h.tool:node_byp node_byp    "Shelf Tool: node_byp"   Ctrl+Del
h.tool:node_ren node_ren    "Shelf Tool: node_ren"   Ctrl+Insert
h.tool:toggle_realtime  toggle_realtime "Shelf Tool: toggle_realtime"    Alt+Shift+T
h.tool:node_tpl node_tpl    "Shelf Tool: node_tpl"   Ctrl+Shift+Insert
h.tool:toggle_pts   toggle_pts  "Shelf Tool: toggle_pts"     Alt+Shift+W
h.textport  Textport    "Open texport"  
h.floatpanel    "Floating Panel"    "New floating panel"    

h.pane.gview.cleartemplates "Clear All templates"   "Clear all template flags"   Ctrl+Shift+Del

    # Insert fix
h.pane.take.list.insert "Insert New Take"   "Insert New Take"   
h.pane.projectm.new_folder  "Add New Folder"    "Add New Folder"    
h.pane.gview.handle.xform.pivot_mode    "Pivot Mode"    "Toggle pivot mode"  \"

    #  Network  Left Right Page Down

h.pane.wsheet.left  "Set Current Left"  "Set Current left"   Shift+PageUp , Ctrl+PageUp
h.pane.wsheet.right "Set Current Right" "Set Current right"  Shift+PageDown . Ctrl+PageDown
h.pane.gview.left   "Move To Left Sibling"  "Move to left sibling operation"     Shift+PageUp , Ctrl+PageUp
h.pane.gview.right  "Move To Right Sibling" "Move to right sibling operation"    Shift+PageDown . Ctrl+PageDown
h.pane.nexttab  "Next Tab"  "Next pane tab"  Ctrl+Tab
h.pane.prevtab  "Previous Tab"  "Previous pane tab"  Ctrl+Shift+Tab

    #  Network  Color    Shift-C
    #  Network  Align    C

h.pane.wsheet.align_horizontal  "Align Nodes Horizontally"  "Align the selected nodes horizontally"  C
h.pane.wsheet.color_palette "Color Palette" "Open color palette"     Shift+C

    #  Network        collapse to subnet          Ctrl-N

h.pane.wsheet.collapse  "Collapse Selected" "Collapse selected operators"    Ctrl+N   New "Delete all contents"    Alt+N

    #  Network        drag Render                 Z
    #  Network        drag Template               X
    #  Network        drag Visualise              V
    #  Network        drag Bypass                 B

h.pane.wsheet.visualize_mode    "Visualize Output"  "Hold this key and select a node or output connector to visualize the output"    V
h.pane.wsheet.flag2_mode    "Set Secondary Flag"    "Hold this key and select a node to toggle the secondary flag value"     X
h.pane.wsheet.flag3_mode    "Set Third Flag"    "Hold this key and select a node to toggle a third flag value"   Z
h.pane.wsheet.scope_chans   "Scope Channels"    "Scope channels"    

    #  Network align     E
h.pane.wsheet.align_vertical    "Align Nodes Vertically"    "Align the selected nodes vertically"    E

    #  Houdini           Maximize Pane          ' 
    #  Houdini           Maximize To Split      \
    #  Houdini           Next Camera      ]

h.pane.gview.handle.xform.handle_geometry_detachment    "Toggle Handle-Geometry Detachment" "Toggle handle-geometry detachment" 
h.pane.maximize_full    Maximize    "Toggle pane full screen"    Alt+\\' Ctrl+B \\'
h.pane.maximize_split   "Maximize in Split Direction"   "Toggle pane full size horizontally or vertically"   \\
h.pane.gview.nextcamera "Next Camera"   "Look through the next camera in the list"   ]

    #  Geometry Quad       Alt-W

h.pane.gview.state.view.toggle_single_quad  "Toggle Single/Quad"    "Toggle single/quad viewport layouts"    B Alt+W
h.rename_selected_nodes "Rename Selected Nodes" "Rename selected nodes"
    #  Restart Selecting Group                             Ctrl+Shift+R
h.pane.gview.redoselection  "Restart Selecting" "Restart selecting"  Ctrl+Shift+R
h.pane.gview.state.new_view "View Tool" "Invoke the view tool"   `

    #  Network   Create Nested Channel Groups              Ctrl+Shift+A

h.pane.wsheet.create_channel_groups "Create Nested Channel Groups"  "Create nested channel groups that correspond to parameter folders"  Ctrl+Shift+A

    #  Houdini    Copy Paste Relative Refs                 Alt+C   Alt+V

h.pane.parms.copy_parm  "Copy Parameter"    "Copy parameter"     Alt+C
h.pane.parms.paste_rel_refs "Paste Copied Relative Refs"    "Paste copied relative refs"     Alt+V
h.paste Paste   "Paste Selection"    Ctrl+V
h.copy  Copy    "Copy selection"     Ctrl+C

    #  Houdini    Timeline        End of Range              Ctrl+DownArrow

h.range_end "Jump To End Of Range"  "Jump to end of range"   Ctrl+DownArrow

    #  Recook All Sims                                       Alt+Ctrl+S

h.reset_sim "Reset Simulations" "Recook simulations in open viewers"     Alt+Ctrl+S

    #  External Editor

h.pane.parms.edit_expression_external   "Edit in External Editor"   "Custom Menu Operation: Edit in External Editor"     Alt+E

    #  Edit Parameter Interface

h.pane.wsheet.opmenu.spareparms "Edit Parameter Interface"  "Edit the node's parameter interface"    Ctrl+Shift+I


Python snippets

create attribute

Thanks to F1!

geo = hou.pwd().geometry()
new_p = geo.addAttrib(hou.attribType.Point, 'new_p', 0.0)
old_p = geo.findPointAttrib('old_p')

for point in geo.points():
    val = point.attribValue(old_p)
    point.setAttribValue(new_p, val)

### Or do it this way:

geo = hou.pwd().geometry()
new_p = geo.addAttrib(hou.attribType.Point, 'new_p', 0.0)

values = geo.pointFloatAttribValues('old_p')
geo.setPointFloatAttribValues('new_p', values)


first keyframe’s time

thanks to Toadstorm!

keyframes = hou.node('/path/to/node').parm('someParameter').keyframes()
min = 999999
for k in keyframes:
    if k.frame() < min:
        min = k.frame()
return min


event callback on parm change

thanks to MrScienceOfficer!

node.addEventCallback(hou.nodeEventType.ParmTupleChanged, call_func)


all the tabs

thanks to Bonsak!

tabs = hou.ui.paneTabs() # Get all the tabs

for tab in tabs: # Loop over them
    if tab.type() == hou.paneTabType.NetworkEditor: # Test the type
        tab.setPref('showdep','2') # Set


pane under cursor

thanks to Varomix!

def getNetworkType():
    # get desktop
    curdesk = hou.ui.curDesktop()
    activepane = curdesk.paneTabUnderCursor()
    return activepane.type()

Bind light’s “Enable” checkbox to the display flag

obj = hou.node(".")
return obj.isDisplayFlagSet()

Reference SOP input from chopnet Geometry

chopnode = hou.node("..")
chopname =                  # chopnet_smooth
chopnet,task = chopname.split('_')          # chopnet smooth
channelnodepath = "../../channel_" + task   # ../../channel_smooth
channelnode = hou.node(channelnodepath)
inputpath = channelnode.inputs()[0].path()
return inputpath

instances() nodes of type in the scene

thanks to julian johnson!

node_type = hou.objNodeTypeCategory().nodeTypes()['bone']
for x in node_type.instances():
    print x

hou.playbar.setPlaybackRange(self, start, end)

print node.type().name() # file, filecache, delete, solver, dopimport, …
node = hou.pwd()
path = node.path()
objContextNodeName = path.split(‘/)[-2]