VEX AND EXPRESSIONS: TIPS AND TRICKS

I come from a biology background, so the main school subjects weren't about computers or codes. I remember when I started to see  the first lines of VEX, I couldn't understand even a lot of basic things. I was telling to myself things like "why should I put a semicolon at the end of a line? It makes no sense"

So I started to write down all the functions that I was finding useful and that I was using often. After months and months, this exercise book has become my personal Houdini bible. Now I have four of them and since sooner or later carry them around will not be an option anymore, I want to write them down here and seize the opportunity to share it.

It's not meant to be a step by step guide. If you are serious about learning VEX, I highly recommend this website  by Matt Estela.

00. All kind of VEX attributes:

float        ->  f@attribute

vector2   ->  u@attribute

vector     ->  v@attribute

vector4   ->  p@attribute

integer   ->  i@attribute

matrix2   ->  2@attribute

matrix3   ->  3@attribute

matrix4   ->  4@attribute

string      ->  s@attribute

 

01. If condition based on user specified threshold:

 

// This function creates a random value (between 0 and 1) per point using the point number as seed.

// If this random value is bigger than the threshold, the function will do what is specified for the point.

float threshold= chf("threshold");

if( rand( @ptnum) > threshold){

         //do something

}

02. Creating string attributes from integer values:

 

// This function creates a string attribute with different strings based on a specified numeric attribute.

/* In the case below it creates a string attribute called name. %d, which is between brackets, will be replaced  by the integer attribute specified as second argument, in this case is represented by the attribute i@class. The result of this code will be: piece_00, piece_01, piece_02 ...

*/

s@name = sprintf("piece_%d", i@class);

03. Copy stamping different objects

 

- Connect all the different objects to copy in a switch node, then the switch node to the source input of the copySOP

- In copy SOP check the option "stamp inputs"

- In CopySOP -> Variable1 write id

- In CopySOP -> Value1 write (rand(@ptnum)) * n . Substitute n to be the number of different objects to copy minus 1. For example for copy three different objects, n needs to be 2.

- In the switch node write this expression: stamp("../copy1","id",1)

04. Exponential pscale distribution

 

// Useful for setting a believable pscale for things like debris. The number of bigger pieces should be a lot less than smaller ones.

float min = chf("min");

float max = chf("max");

float val = rand(@ptnum);

val = fit01( pow( val, 3), min, max);

f@pscale = val;

05. Blurring attributes with VEX

 

// Useful for blurring attributes and do other operations with the point cloud.

//NB: There is also the node called "attribute blur"

float maxdist = chf("maxdist");

int maxpts = chi("maxpoints");

int pc = pcopen(0, "P", @P, maxdist, maxpts);

// In this case I am setting Cd as attribute to blur

v@Cd = pcfilter( pc, "Cd");

06. Create, check and expand groups

 

//Set groups

 

@group_mygroup = 1;

setpointgroup(int geohandle, string name, int point_num, int value, string mode="set")

setvertexgroup(int geohandle, string name, int prim_num, int vertex_num, int value,string mode="set")

setprimgroup(int geohandle, string name, int prim_num, int value, string mode="set")

// Check if is in group (1 if true, 0 if false)

inpointgroup(int opinput, string groupname, int pointnum)

int  invertexgroup(int opinput, string groupname, int vertexnum)

int  inprimgroup(int opinput, string groupname, int primnum)

// Get all elements in the group ( these return arrays)

int [] expandpointgroup(int opinput, string groupname)

int [] expandvertexgroup(int opinput, string groupname)

int [] expandprimgroup(int opinput, string groupname)

07. Group points based on normal direction

 

//Looks at the points normal and compare it to a vector. In this case it compares it to the "up" vector. This is possible thanks to the dot //function. What you have to remember is to normalize the input vectors, otherwise the result will be wrong. The acos functions and the //degrees function are there to convert the value to a readable number, so we can compare it to a common angle which goes from 0 to //360 degrees for example.

vector up = set(0,1,0);

vector N = v@N;

float angle = acos( dot(normalize(up), normalize(N)));

angle = degrees(angle);

if(angle<chf("threshold")){

        @group_mygroup = 1;

}

08. Actions based on event

 

//Do something just in specific frames

int frames[] = {2,10,40,41};

if( find( frames, @Frame) >= 0){

       //do something

}

09. Creating lines between two points

 

// example creating two points and then creating a primitive between them.

 

// Run on detail mode

vector pos0 = chv("pos0");
vector pos1 = chv("pos1");

//add points

int pt0 = addpoint( 0, pos0);
int pt1 = addpoint( 0, pos1);


// add a primitive
int prim = addprim( 0, "polyline");

​//connect points to prim through vertex
addvertex( 0, prim, pt0);
addvertex( 0, prim, pt1);

10. Creating a circle in VEX

 

//From Anastasia Opara tutorial

// Run on detail mode

int sample = chi("sample");

float radius = ch(""radius");

vector origin = chv("origin");

float two_pi = $PI * 2;

float theta = 0;

float step_angle = two_pi/float(sample);

float x,z;

vector pos;

while( theta < two_pi){

       x = origin.x + cos(theta) * radius;

       z = origin.z + sin(theta) * radius;

       pos = set(x, origin.y, z);

       addpoint(0, pos);

       theta += step_angle;

}

 

11. Fix points on a moving object - intersect function

// One of the methods to fix scattered points on a moving object is to use the intersect function

// In a wrangle put on the first input the scattered points in a static position (for example the initial frame)

// In the second input connect the geometry at rest position (for example the initial frame)

// In the third input connect the moving object

// For this to work you need to have normals on the object before scattering points.

int prim;

vector primuv, staticP;

vector offset = @P - (v@N * 0.05);

prim = intersect(1, offset, v@N, staticP, primuv.x, primuv.y);

@P = primuv(2, "P", prim, primuv); 

 

12. Fix points on a moving object - xyzdist and primuv

// One of the methods to fix scattered points on a moving object is to use the combination of xyzdist and primuv

// In a wrangle put on the first input the scattered points in a static position (for example the initial frame)

// In the second input connect the geometry at rest position (for example the initial frame)

// In the third input connect the moving object

int posprim;

vector primuv;

float maxdist = 10;

// Find position and uvs of object in rest pos connected to input 1

float dist = xyzdist(1, @P, posprim, primuv, maxdist);

// Follow animated object connected to input 2

vector pos = primuv(2, "P", posprim, primuv);

@P = pos;

 

13. Move objects outside of a collision geo

// Avoid objects to intersect, useful for simulating bending

// In the second input of the wrangle you need to input a SDF volume representing the collision geo

 vector dir = volumegradient( 1, 0, @P);

vector dist = abs(volumesample( 1, 0, @P));

v@P += dir * dist;

 

14. Various loops

// For loop

int i = 0;

for( i; i < npoints(0); i++){

       //do something

}

// For each loop -  short form

// Here "i" stand for the value of the element of the array, not the number

foreach( int i; @my_array){

​       //do something for each element of the array

}

// For each loop -  long form

// Here "i" stand for the point number of the element outside of the foreach loop, the real point number

// "index" is the number of the element of the array ( 0, 1, 2, 3..., n)

foreach( int index; int i; @my_array){

​       //do something for each element of the array

}

// While loop

// do something a number of times until a condition is reached

int i = 0;

while( i < npoints(0)){

       //do something n times, where n is the number of points

}

 

15. Compare strings

//Sometimes you need to look if a string attributes match a certain string/word. 

//The match function return 1 if the string attribute match a string, else is 0

int i = match("name", s@group);

 

16. Compare points of a primitive

//  Each primitive is connected to some points, here is how to find them in a wrangle.

//  In this example I take the name attribute of the two points connected at the contraints

int vt0 = primvertex(0, @primnum, 0);

int vt1 = primvertex(0, @primnum, 1);

int pt0 = vertexpoint(0, vt0);

int pt1 = vertexpoint(0, vt1);

string name_pt0 = point(0, "name", pt0);

string name_pt1 = point(0, "name", pt1);

if(name_pt0 != name_pt1){

          //points names are different

}

 

17. Change volume visualization

//Some shelftools and nodes generate volumes that are hidden but could be useful to see.

//This expression allow you to see this volume, turning them from "invisible" to "iso"

setprimintrinsic(0,"volumevisualmode",@primnum,"iso","set");

 

18. Delete geometry outside of camera frustum

//This snippet is from jonidunno on odforce (link to topic)

v@ndcP= toNDC(chs("cam"),v@P);
float padding = chf("padding");
float nearclip = chf("nearclip");

float farclip = chf("farclip");


if(v@ndcP.x > (1 + padding) || v@ndcP.x < (0 - padding) || v@ndcP.y > (2 + padding) || v@ndcpos12321.y < (0 - padding) || -v@ndcP.z < nearclip || -v@ndcP.z > farclip){
    removepoint(0,@ptnum);
}

 

19. Save unique values to an array

// I haven't used this a lot but I've found "numiqueval" and "uniqueval" useful a couple of times, especially for loops.

// numiqueval is going to return the integer number of unique values stored in an attribute

// uniqueval returns the unique value at a certain index. For example if the unique values are 9 and in the function you put 9 as index, is // going to return the 9th and last unique value

//Hard to find a simple scenario. Let's say in this case let's say we have a certain number of particles sourcing 100+ particles each. Each //sourced particle is storing the point number of the particle it's sourced from. For some reasons we don't know the number of source //particles, but we need to know their values and put all of them in an array.

//Run over detail

int n = numiqueval(0, "point", "sourcept"); //the amount of unique values in the attribute "sourcept"

for( int i=0; i < n; i++){

   int inst = uniqueval(0,"point","sourcept", i); //the unique value at index "i" 

   append(i[]@arr_sourcept, inst); //put all the unique values in an array called @arr_sourcept

}

 

20. Manipulating strings in VEX

//Few useful functions for creating, modifying and cutting strings in VEX

string path = chs("path");

string pathsplith[] = split(path, "/"); //splits the string in pieces based on the "/" simbol

string filename = pop(pathsplit); // removes the last element from the array and put it in a new variable

string newextention = sprintf("_%04d", @ptnum); // set ptnum as a 4 digits number (0001, 0002, 0003...)

string newpath = join(pathsplit, "_"); //merges the elements of an array with "_" between elements

21. Find near points and neighbours

 

// There are several ways to find the closest points and there are a couple of things to keep in mind

//Nearpoints function returns the closest points to the current. We can specify how far we search and the maximum amount of points

// Pay attention that this function returns a list of all the points, so returns an array (that's why I added [] to the attribute name).

// If we look at the closest point in the same geometry coming from the first input ( so input number 0)  there is another thing to keep in // mind. For Houdini the closest point in the same geometry is going to be itself. If we need the real closest point, we have to take not

// the first, but the second element in the list (which has index number 1).

float maxdist = chf("maxdist");

int max_points = chi("maxpoints");

int near_pts[] = nearpoints(0,@P, max_dist, max_points); // create a list of all points in the radius until reaches max_points

int closest_point = near_pts[1]; //take the second element of the list, so index number 1

// There is also the nearpoint function which returns just an integer with the closest point. But as before if we run it in the same //geometry, we are not going to get the real closest point

int nearpoint = (0, @P);

// There is also the neighbours function which gives us a list of points connected to the point through some sort of primitives.

int neighbour_points = neighbours(0,@ptnum);

//If you are a fan of point clouds there is also a function called pcfind_radius. The cool thing is that it returns directly an array and not a //point cloud. The cool thing about this is that we can easily use the pscale as radius for the search and search just in a specific group of //points 

float maxdist = chf("maxdist");

int max_points = chi("maxpoints");

int near_pts[] = pcfind_radius(0, "P", "pscale", 1.0, v@P, max_dist, max_pts);

int near_pts[] = pcfind_radius(0, "group_name","P", "pscale", 1.0, v@P, max_dist, max_pts); //Alternative version looking in a point group

 

22. Random orient attribute

//Random ORIENT attribute. Found it in some hipfiles online probably, but I'm not sure where I got it from. Still very useful


float minX = chf("minX");
float maxX = chf("maxX");
float minY = chf("minY");
float maxY = chf("maxY");
float minZ = chf("minZ");
float maxZ = chf("maxZ");

float randomX = fit01(rand(@ptnum+chf("seed")),minX,maxX);
float randomY = fit01(rand(@ptnum+chf("seed")),minY,maxY);
float randomZ = fit01(rand(@ptnum+chf("seed")),minZ,maxZ);

float angleX = radians(randomX);
float angleY = radians(randomY);
float angleZ = radians(randomZ);

vector eulerAngle = set(angleX,angleY,angleZ);
@orient = eulertoquaternion(eulerAngle,0);

23. Random Exponential pscale

 

// Piece of code found in a hip from the amazing f1480187 on odforce 

float randomized_pscale = sample_exponential(ch("mean"), rand(@ptnum));

@pscale = lerp(@pscale, randomized_pscale, ch("randomize"));
@pscale *= ch("scale");
@pscale = min(max(ch("min_pscale"), @pscale), ch("max_pscale"));

24. Keep a certain number of simulated points

 

// Did you ever had the need to instance some objects on simulated points and you are never happy of the results so you have to //simulate them over and over again until you get a nice variation? If you don't need interactions, It's also possible to simulate a lot of //them once and then change the value of the seed in the code below until you get the variation you like with the number of points you //want. Really a time saver! 

float val = rand(@id + ch("seed"));
int npts = npoints(0);
if(val * npts > chi("max_pts")) removepoint(0,@ptnum);

25. Volume bounding box for packed primitives

 

// Not sure if there is already a node for that. Here is how to extract and calculate the volume for your scattered packed objects instead of unpacking, calculating and repacking. This is the equivalent of the bounding box of an object. It will change every frame if your objects are animated, watch out! 

 

float bounds[] = primintrinsic(0,"bounds",@ptnum);
vector min = set(bounds[0], bounds[2], bounds[4]);
vector max = set(bounds[1], bounds[3], bounds[5]);

float x = max.x - min.x;
float y = max.y - min.y;
float z = max.z - min.z;

float volume = x * y * z;

//v@min_bound = min;
//v@max_bound = max;
f@volume = volume;