Wednesday 25 November 2020

changing strings with vex

 Some really useful vex things

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

 

 This tip and the following were taken from :
https://andreasbabo93.wixsite.com/sbabovfx/useful-vex-and-expressions

 

 

 

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;

 

Friday 20 November 2020

instance path

Say you have some points and you want to instance an object from disk to them..

in a point wrangle-

 s@instancepath=chs("path");

create the input box and type in the path of your object eg: c:\dave\ball.obj


beneath the point wrangle, add an instance node. ta da!

Thursday 19 November 2020

clamp frame ranges for backplates

 S:/PROJECTS/BLABLABLA13893/3D/Plates/D001_C006_20201113_R1/D001_C006_20201113_R1.`clamp($F,1001,1325)`.jpg

`clamp($F,1001,1325)`

 

that's the important bit. the ` ` makes houdini evaluate

Tuesday 10 March 2020

delaying per point.... using CHOPS

For some reason, the timeshift node doesn't work within For Loops. Eg. you have a bunch of points going from A to B and you want to offset each point by an arbitrary number of frames.

Matt Estella mentions -
The core issue is that the for-begin node doesn't split and cook the geo as you'd expect. Instead you have to set the begin node into 'fetch input', so it just grabs everything, then use a split sop to get the element you want, using the metadata node to read the value or iteration detail attribute.

To be honest, I don't really understand the problem & his example file doesn't quiiiiite do what I want. One of his other ones does though, albeit with a little tweaking. Bastian J Schiffer's video is very useful.
(https://vimeo.com/280537925)

At SOP level, make an integer point attribute called something like "timeshift" with some values. Make a null after this and call it TO_CHOPS


Then what we want to do is access a point attribute in CHOPS. The method isn't immediately obvious.
Make a chopnet, and two geometry nodes. Point them to TO_CHOPS  and make sure they're both "animated" and not "static. With the first one (geometry-transform), leave the default P, tx,ty,tz values... With the second (geometry-timeshift), set the Attribute scope to "timeshift" and the Rename scope to "timeshift" too.



Next make a channel wrangle. This behaves like a point wrangle, for the most part..there are, however, some CHOPS-only variables. Stick the geo-transform into input 1, and the geo-timeshift into input2.
Type this into the vex-box -

V=chinput(0,C, I+chinput(1,C,I));
"V" stands for value.
"chinput" is like the "point" function, but here it fetches channels instead of attributes. There're a few ways to use the function, but here we're going for the chinput(0,C,I) easy mode...

The first 0 is the 0th input (geo-transform), it fetches C, the channels (which are tx, ty, tz) and....
"I" is the index/iteration...we modify this by chinputting the geo-timeshift input and grabbing it's channels (the timeshift attribute)
Finally add a null, name it OUT (ignore the math/noise node for now, that's some other crap)
Jump up to SOP level and put a channel node to the TO_CHOPS, switch it to Animate mode. The default value of t[xyz] and P are fine here.

Your points should now be delayed....
You could make the delay a bit more fancy by adding a multiplier, either to the expression above, or in the bit where you define the timeshift attribute in the first place..

Monday 9 March 2020

orient point attribute


I always seem to forget/ignore the orient attribute, and it always bites me in the ass.. (think instancing objects onto points with random rotations etc) Usually I randomise/mess up the target points' normals, but if you forget to give your source object clean normals, this can lead to some messy point normals..
If instead, you mess up the target points' orient, you avoid this nonsense. It's worth creating a custom vector attribute display to visualise the orients!

Another snippet stolen from Rich Lord's notes/tools page - https://www.richlord.com/tools/

I suspect that there's a shortcut for building the matrix at the end, but from my understanding this setup should be quite clean..

// create an axis and an up vector.
// The axis will represent the Z direction of the final matrix
vector axis = chv("axis");
vector up   = chv("up");
// normalise these two vectors
axis = normalize(axis);
up = normalize(up);
// build a vector for each axis, then build a matrix out of it.
// I may be normalizing a bit much here, but it cant hurt.
vector zaxis = axis;
vector xaxis = normalize(cross(up, axis));
vector yaxis = normalize(cross(axis, xaxis));
matrix3 default = set(xaxis, yaxis, zaxis);
// create the orient quaternion as a point attribute
p@orient = quaternion(default);

Rotation in vex

Rotation round an arbitrary axis, in vex

nicked from Rich Lord's handy notes and scenes - https://www.richlord.com/tools/
Placed here, jusssst in case it all vanishes!
The general gist is -


  1. define an axis to rotate around
  2. specify the angle of rotation
  3. create an identity matrix
  4. rotate the matrix by the angle, along the axis
  5. multiply the Position of each point by the transformed matrix

Here's the code -
vector axis = normalize(chv("rotate_axis"));
float angle = radians(chf("rotate_angle"));
// create the matrix representing the rotation, and rotate the points
matrix3 m = ident();
rotate(m, angle, axis);
@P *= m;
Be sure to press the create slider/input button!

A separate example Rich provided has the points rotate around a point in space -

The same technique applies, except a little bit of subtraction/addition of the point position (we move the points-to-be-rotated to the position-of-the-rotation-origin first). Pretty useful, as you're probably not just doing everything at the origin :D

Code -
vector rotate_point = chv("rotate_point"); //this is the origin of the rotation
vector axis = normalize(chv("rotate_axis")); //the axis
float angle = radians(chf("rotate_angle")); //the angle of rotation...
// move the points before the rotation
@P -= rotate_point;
// build the matrix that does the rotation and apply it to the points
matrix3 m = ident();
rotate(m, angle, axis);
@P *= m;
// move the points back to their original position
@P += rotate_point;

Friday 21 February 2020

time shifting copy stamps

Needed to time offset an alembic which was being copy-to-points.
The methodology is the same as a previously posted thing, but the copy-to-points node needs to be situated inside the For Each loop



if you squint, you'll see there's an attribrandomize node. I've made an integer attribute called TimeOffset and ..randomised it..
In the timeshift node, I make a spare input and point it to ../PT, which holds said TimeOffset values

The value for the timeshift is : $FF+point(-1, 0, "timeOffset", 0)

so, we take the current frame and add the individual time offset value to it.

Shweet.

Wednesday 12 February 2020

cryptomatte AOV with arnold/HTOA

Another simple thing, which I always forget.

The most common crypto attribute I use is crypto_asset.
It seems to work best assigned at primitive level. So - in a primitive wrangle for example -

s@crypto_asset="poop"+itoa(@primnum);


The next part is to create an Arnold Shader Network, call it something like "CRYPTO"
Inside this, make an AOV Output  node. Create a cryptomatte node and hook this up to the aov_shader input. You may need to create a user_data_string with "crypto_asset" and plug that into the "crypto_asset" input of the cryptomatte node.


The last bit, is in the Arnold ROP you're using. In the AOV section, where it says "AOV Shaders", point it to they CRYPTO shop you made earlier -  /shop/CRYPTO

then add a new AOV and select "crypto_asset" from the list.

Hopefully this works!

Crypto_object is used when things are in different Houdini Objects... it isn't something that I usually do though..

Friday 7 February 2020

distort undistort stmap

This stuff is so boring.

original source 4448x3096
plug into undistortion node to get undistorted res 4560 x 3174 - ST MAP will be this res

plug 4560x3174 into redistort node to get 4448x3096 - ST MAP will be this res

Friday 31 January 2020

Getting the name of the parent node

Sometimes you'll need the name of the parent/container node that you're working in. It might be a "car_01" or something like that.

What you need to do is trick a wrangle into using houdiniscript. We do this by creating a parameter/channel and then typing in the script we wish to run, enclosed with backticks ` `

opname is the function we want to run and .. will get us up a level.
so in the wrangle use

s@string_name=chs("whatever");

hit the create-channel-box button, then type this in it

`opname("..")`

you now have a string value of the parent node that you could strip/atoi, whatever.

Tuesday 28 January 2020

transparent points/ points with alpha, Arnold

Plug Alpha attribute using User Data Float into OPACITY of Standard shader (in geometry section)

Monday 27 January 2020

POP streams, for POPs or even RBDs.

Bit of a waffly post here.. mostly for my own ref..

I had a case where there were a bunch of colour swatch cards in a display case of sorts. They needed to fly out of their resting position, but also had to vibrate and wiggle a little before this.
For context - I chose to use RBDs & the Bullet Solver, packed primitives for all of this, as there didn't need to be any clothy/plastic Vellum defomations.

So-

1) 10-15 frames run-up sim to settle
2) Cards gradually wiggle, not all at once.
3) Some cards shoot out of their position, not all at once.

The challenge is to effectively create an activation for the individual forces that effect the cards.

At first I was using the RBD-branded wind and fan forces - which did provide the necessary dynamic effect, but I couldn't figure out a way to use groups, or to trigger their activations on a per-packed-piece-point basis. These forces were connected one after another in a chain.


The solution is to use POP-Streams. In my case, we can plug this into an Rigid Body Solver (the 3rd input), as it already has a multi-solver, bullet solver and some other clever bits built into it.
We need to create a master POP stream with the rule set to include all the calculated points. You can/should initialise any values in this stream to account for any points that aren't picked up by the other streams. This might include making them active or not...
From this we can then connect secondary POP streams, each with their own rules and POP force nodes.

I tended to use the random stream option as a way to gradually and naturally add points to the stream.
Something I tried to get working was splitting streams one step further.. eg. A splits off to B ...but B then splits into C & D. This didn't quite go as planned.

I was trying to achieve a slow random addition to a stream/group and then for the cards to go either to the Left or Right, using POP forces. A bit of an inelegant solution, but I ended up creating two streams instead of B producing it's own two, (A divides into B AND C) using SOP bounding boxes. To achieve the random addition effect, I animated a threshold, which determined when the cards would activate. Each stream would then have its own POP forces. Much more control overall.