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.
1. ATTRIBUTE MANIPULATIONS
- All kind of VEX attributes (int, float, vector, etc.)
- Creating string attributes from integer values
- Blurring attributes with VEX
2. CONDITIONAL LOGIC
- If condition based on user specified threshold
- Actions based on event
- Compare strings
3. GEOMETRY OPERATIONS
- Copy stamping different objects
- Creating lines between two points
- Creating a circle in VEX
- Fix points on a moving object - intersect function
- Fix points on a moving object - xyzdist and primuv
- Move objects outside of a collision geo
- Compare points of a primitive
- Find near points and neighbors
- Keep a certain number of simulated points
- Volume bounding box for packed primitives
4. STRING OPERATIONS
- Manipulating strings in VEX
5. GROUP OPERATIONS
- Create, check and expand groups
- Group points based on normal direction
6. LOOPS
- Various loops
7. MATHEMATICAL OPERATIONS
- Exponential pscale distribution
- Save unique values to an array
- Random orient attribute
- Random Exponential Pscale
8. SCENE OPTIMIZATION
- Change volume visualization
- Delete geometry outside of camera frustum
ATTRIBUTE MANIPULATIONS
All kind of VEX attributes (int, float, vector, etc.)
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
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);
Blurring attributes with VEX
// Useful for blurring attributes and do other operations with the point cloud. 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");
CONDITIONAL LOGIC
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
}
Actions based on event
// Do something just in specific frames
int frames[] = {2, 10, 40, 41};
if (find(frames, @Frame) >= 0) {
// do something
}
Compare strings
// Sometimes you need to check if a string attribute matches a certain string/word.
// The match function returns 1 if the string attribute matches a string, else it returns 0
int i = match("name", s@group);
GEOMETRY OPERATIONS
Copy stamping different objects
- Connect all the different objects to copy in a switch node, then connect the switch node to the source input of the CopySOP.
- In the 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 copying three different objects, "n" needs to be 2.
- In the switch node, write this expression: `stamp("../copy1","id",1)`.
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);
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;
}
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);
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;
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;
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 constraints
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
}
Find near points and neighbors
// 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 reaching 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 = 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
Keep a certain number of simulated points
// Did you ever have the need to instance some objects on simulated points and you are never happy with 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);
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;
STRING OPERATIONS
Manipulating strings in VEX
// Few useful functions for creating, modifying and cutting strings in VEX
string path = chs("path");
string pathsplit[] = split(path, "/"); // splits the string into pieces based on the "/" symbol
string filename = pop(pathsplit); // removes the last element from the array and puts it in a new variable
string newextension = sprintf("_%04d", @ptnum); // sets ptnum as a 4-digit number (0001, 0002, 0003...)
string newpath = join(pathsplit, "_"); // merges the elements of an array with "_" between elements
GROUP OPERATIONS
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)
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;
}
LOOPS
Various loops
// For loop
int i = 0;
for (i; i < npoints(0); i++) {
// do something
}
// For each loop - short form
// Here "i" stands 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" stands 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
}
MATHEMATICAL OPERATIONS
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;
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, it's 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 = numuniqueval(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
}
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);
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"));
SCENE OPTIMIZATION
Change volume visualization
// Some shelf tools and nodes generate volumes that are hidden but could be useful to see.
// This expression allows you to see this volume, turning them from "invisible" to "iso"
setprimintrinsic(0, "volumevisualmode", @primnum, "iso", "set");
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);
}
Comments