HoudiniVex

From cgwiki

So you've pushed past hscript, ignored python for now, and seen the awesomeness of vops. You'll also probably start to get curious as to why there's at least 4 different ways to define point attributes, all with various idosyncrasies. Time to sweep all that away with Vex and Wrangle nodes.

Wrangle Sops let you write little bits of Vex code. They come in various flavours (point, prim, detail, attribute), they're all the same thing, just with the 'what do i operate on' menu switched to point/prim/detail.

Anyway, best way to get into vex and wrangle nodes is to put down a 40x40 grid, append a point wrangle, and follow along:

Create a new attribute

You want to define a new point float attribute 'foo'? Just type

@foo;


Hit ctrl-enter, look in the geometry spreadsheet, there you go, float attribute. The @ tells houdini that you want this to be an attribute, the default type is float.

You want it initialised?

@foo=1;


You want a vector attribute? Prepend 'v' before the '@'. To initialise, use curly braces:

v@myvector={1,0,3};

Get existing attribute values

You want myvector to get its values from the point position?

v@myvector=@P;


The @ symbol is used to get attributes as well as set them. All the attributes you see in the attribute spreadsheet, you can access them by sticking @ in front of their name.

You want it 1.5 times N?

v@myvector=@N*1.5;


To access an individual attribute of a vector or colour, use a @attribute.channel. Eg to get just the y value of each point and divide in half:

@foo=@P.y / 2;


To set it is similar. Eg to set the red channel of each point to the sine of double the x position:

@Cd.r = sin( @P.x * 2 );

Create UI controls

You want a slider automatically created to scale the sine wave for the previous example?

@Cd.r = sin( @P.x *  chf('scale')   );


Hit the little plug icon to the right of the text editor, Houdini scans the vex code, realises you've referred to a channel that doesn't have a UI yet, and makes a channel at the bottom of the wrangle UI named 'scale'. Start sliding it around, you'll see the colours update.

There's several channel types you can use. ch() and chf() both create a float channel. For the others:

// An int channel
i@myint = chi('myint');

// A vector channel
v@myvector = chv('awesome');

// A ramp channel
f@foo = chramp('myramp',@P.x);


The last one is really handy, in one line you get to read one value (@P.x), remap it via the UI ramp widget, and feed that to @foo. I end up using loads of these all through my setups. It assumes the incoming values are in the 0-1 range, you often have to fit the values before feeding to the ramp. The fit function goes fit( value, oldmin, oldmax, newmin, newmax). Eg, remap X between -0.5 and 6, then use a ramp to feed those values into the red channel:

float tmp = fit(@P.x, -0.5,6,0,1);
@Cd.r = chramp('myramp',tmp);


But why stop there? Define the start and end points with UI sliders!

float min = ch('min');
float max = ch('max');
float tmp = fit(@P.x, min,max,0,1);
@Cd = 0;  // lazy way to reset the colour to black before the next step
@Cd.r = chramp('myramp',tmp);

Customise the UI elements

The plug button is a convenience function, it just scans for any channel refrences, and creates the default type, with a default value. Once the channel is made, you can right.click on the wrangle node and choose 'edit parameter interface' (or use the gear menu from the parameter panel), and change float ranges, the default value, the labels, whatever you want.

A handy thing I often do is to convert the default vector channel to a colour channel. I'll make as many vector channels as I need in vex, eg:

v@colour1 = chv('col1');
v@colour2 = chv('col2');
v@colour3 = chv('col3');
v@colour4 = chv('col4');


Then I'll hit the plug button, edit the parameter interface, shift-select all the channels I made, and change the type field to 'color', and to be extra saucy, change 'show color as' to 'hsv sliders'. Most handy.

Why bother?

Well, vex is as a general rule always going to be faster than using hscript, and will scale much better. Vops are great, but often its the simple things that you can do in 2 lines in vex, that would take 7 or 8 nodes in vops. Its also nice that a single wrangle can do the work of several other nodes and UI tricks:

  • point sop
  • attribcreate
  • vop bind and vop bind export
  • promote parameters in vops
  • channel referencing in hscript

Mixing vex and vops with inline code

Often I'll start something in vops, then realise part of my network would be much neater as a few lines of vex. When that happens, I'll drop in an 'inline code' vop, and I get something that looks very similar to a wrangle node. I'll then code whatever I need to code, and it happily co-exists with the vops around it.

The syntax is a little different, took a few goes to get used to it. First is that you don't need the @'s, so @P.y is just P.y.

The second is how to define the output of the node. By default there are no outputs, at the bottom of the parameter interface you go to the first disabled output, set its type (eg vector), and give it a name (eg, P). You then see a 'P' appear on the right side of the node, ready for you to connect to other things. Cos I'm an idiot, I tend to name these outputs explicitly, eg 'outP' rather than 'P'.

To assign values to that output, use a $ prefix. Eg, here I want to drive P.y by Cd.r. I've defined the output as outP on the UI.

P.y=Cd.r;    
$outP=P;


Note that setting P.y like that in a wrangle (with the @ prefix of course) would immediately update the geo, it doesn't in an inline code vop. P here just exists within the node; we then have to be able to export it via outP, then plug it to the global output P variable to see any effect.