Houdini VEX

From bernie's
Jump to navigation Jump to search

Remap curve

Kh9phrT.png

Edit the point placement along the length of a (parametric) curve using a remap curve

vector uv;
int prim;
xyzdist(0,@P,prim,uv);
float umapped = chramp("remap",uv[0]);
uv[0] = umapped;
@P = primuv(0,"P",prim,uv);


Visualize parameter

Why look at the animation editor when you can have a shittier version in the viewport. Don't ask questions, it helped me.

T5kLfEM.png

//second input of detail wrangle should be frozen frame, 'parm' channel is a path to the parm you want to see graphed, which can be an expression

vector curPos;
//---graph--

vector scalept(vector normalizedpt){
    vector bs = getbbox_size(1);
    float size = max(bs[0],max(bs[1],bs[2]))/3;
    vector bb = getbbox_center(1);
    return ((normalizedpt-{.5,-.1,0})*size+bb);
}

int start = `$FSTART`;
int end = `$FEND`;
int pts[] = {};
for(int i=0;i<end-start+1;i++){
    float val = ch(chs('parm'),float(i+start)*@TimeInc);
    vector pos = set(float(i)/(end-start),val,0);
    int newpoint = addpoint(0,scalept(pos));
    append(pts,newpoint);
    if(i+start==@Frame){
        curPos = pos;
    }
}
addprim(0,"polyline",pts);

//-----------

pts  = {};
float y = chf('yMax');
int ptA =  addpoint(0,scalept(set(0.0,y,0)));
int ptB =  addpoint(0,scalept(set(1.0,y,0)));
append(pts,ptA);
append(pts,ptB);
addprim(0,"polyline",pts);

//---x-------

pts  = {};
ptA =  addpoint(0,scalept(curPos+set(-.01,-.01,0)));
ptB =  addpoint(0,scalept(curPos+set(.01,.01,0)));
append(pts,ptA);
append(pts,ptB);
int line = addprim(0,"polyline",pts);
setprimattrib(0, 'Cd', line, set(1,0,0), 'set');
pts  = {};
ptA =  addpoint(0,scalept(curPos+set(.01,-.01,0)));
ptB =  addpoint(0,scalept(curPos+set(-.01,.01,0)));
append(pts,ptA);
append(pts,ptB);
line = addprim(0,"polyline",pts);
setprimattrib(0, 'Cd', line, set(1,0,0), 'set');

Connect random points by direction

WIP-ish but was enough for what I needed might polish later (I did add some error checking though why the fuck...)

This code takes two points or two group of points (of equal size) and attemps to connect points that are in the direction of a,b pairs given a maximum cone angle. This can be useful if you have randomly sorted points and the Connect Adjacent Pieces + Find Shortest Path SOPSs arent doing what you want. Which was my case, or I might have over-engineered something stupid.

uBQPOSL.gif

TBD:

  • Allow for adaptive cone width (if you get increasingly sharper 'turns')
  • Alternatively read from point value (like injecting a sharp change of direction)
  • Give preference to an axis (imagine a tightly wound spiral) so that it still works and doesn't pick up weird points
  • Give priorities to points that have confidence in the previous points, remove them from the available point list (would correct the example in my video)
    int a[],b[],bigAssPointsArray[];
    vector dirs[];
    vector posA,posB;
    
// channel values values hit the button to create -->
    string grp = chs('group');
    float dist = ch('max_search_distance');     // 1
    float cone = radians(ch('cone_degrees'));   // 10       
    int iterations = chi('iteration');          // 4
    // V use direction if no second set of point(s) found: 
    vector dir = normalize(chv('start_dir'));   // 1 0 0

// grp cleaning
    string error = '';
    if(grp != ''){
        //if we found groups:
        if(find(grp,' ')>0){
            //if there is more than one it means we have two separate lists (or two points)
            string grps[] = split(grp,' ',1);
            if(len(grps)>2){
                error = 'more than two groups found!';
            }else{
                a = expandpointgroup(0,grps[0],"ordered");
                b = expandpointgroup(0,grps[1],"ordered");
                if(len(a)!=len(b)){
                    error = 'the two groups have different point counts';
                }
            }
        }else{
            b = expandpointgroup(0,grp,"ordered");
        }
    }else{
        error = 'at least group or point needed';
    }

    
    if(error!=''){
        error(error);
        return;
    }

// do the work
    
    if(len(a)==0){
        //no second group, fill directions array with initial start dir
        for(int i = 0 ; i < len(b) ; i ++){
            append(dirs,dir);
        }
    }else{
        //if there are two destinct groups, create a vector array with initial directions
        for(int i = 0 ; i < len(b) ; i ++){
            posA = point( 0 , 'P' , a[i] );
            posB = point( 0 , 'P' , b[i] );
            vector direction = normalize(posB-posA);
            append(dirs,direction);
        }
    }
    
    for(int j=0 ; j<len(b) ; j++){
        //for every point of the second group start finding out if there are points in the dir direction
        posB = point(0,'P',b[j]);
        dir = dirs[j];
        if(len(a)>0){
            append(bigAssPointsArray,a[j]);
        }
        append(bigAssPointsArray,b[j]);
        for(int i=1; i<=iterations;i++){
            int points_found[] = pccone_radius(0,"P","none",0.0,posB,dir,cone,dist,2);
            if(len(points_found)==1){
                //if nothing is found during the point cloud cone search, color the last point and break forloop
                setpointattrib(0,"Cd",points_found[0],{1,0,0},"set");
                break;
            }else{
                //we found a point ! use it for the next iteration
                int chosenpoint = points_found[1];
                append(bigAssPointsArray,chosenpoint);
                posA = posB;
                posB = point(0,"P",chosenpoint);
                dir = normalize(posB - posA);
            }
        }
        /* we finished the max iterations, we could add the poly prim in the
        for loop but houdini doesn't like it ? And no way to have multi
        dimensional array so let's fill up a big ass one and split with
        a non-existing point number -1 */
            
        append(bigAssPointsArray,-1);
    }
    
    
//build the poly lines
    int pointsArray[] = {};
    int pr = addprim(0,'polyline');
    for(int i=0;i<len(bigAssPointsArray)-1;i++){
        int pt = bigAssPointsArray[i];
        if(pt == -1){
            pr = addprim(0,'polyline');
        }else{
            int newpt = addpoint(0,pt);
            addvertex(0,pr,pt);
        }
    }

Radial sort

sorting

vector bbcenter = getpointbbox_center(0);
float dx = bbcenter.x-@P.x;
float dz = bbcenter.z-@P.z;
@angle = atan2(dx,dz); //radians

Then sort sop

Galaxy like animation with particles

ZyzlOia.gif

vector bbcenter = set(0,0,0);
float dx = bbcenter.x-@P.x;
float dz = bbcenter.z-@P.z;
float mag = distance(@P,bbcenter);
@angle = atan2(dx,dz); //radians

float ringmult = snoise(@P);
ringmult = fit(ringmult,-1,1,.8,1.6);

@t=ringmult;
@angle += @Time * fit(rand(@ptnum),0,1,.5,.75) * 1 / pow(mag,ch('exp')) * ringmult;

@P.x = cos(@angle) * mag;
@P.z = sin(@angle) * mag;

Gradient Lattice

Simple XYZ<>splines deformer

M1XaRdH.png

vector bb = relbbox(0,@P);
vector m = getbbox_min(0);
vector M = getbbox_max(0);
@P.x = lerp(m.x,M.x,chramp("x", bb.r));
@P.y = lerp(m.y,M.y,chramp("y", bb.g));
@P.z = lerp(m.z,M.z,chramp("z", bb.b));

Move points to sdf surface

cDlX1px.gif

The second input is a number of iterations to refine the stickiness

//move points towards the surface of the sdf using gradient
vector p = @P;
int max = chi('max');
for(int i=0;i<max;i++){
    vector noise = vector(noise(p)) - .5;
    noise *= ch('noise') ;
    float reach = volumesample(1,0,p+noise);
    p -= normalize(volumegradient(1,0,p+noise))*reach*(1.0*(i+1)/max)*ch('mult');
}
@P = p;

Remove geometry that isn't in the other stream

better edit: keeping the code below if I have an edge case but it's faster (?) or at least clearer to use idtoprim() or idtopoint(), example here: How do I work on two objects that don't have the same number of points/prims?

For each prim check that it exists in the second input, if not, delete.

I use this when I have proxy geometry which I have deleted and need the same deletion on the hirez packed geo (so I can use the transform pieces) as shown in the image below

zp4sYEe.png

for(int i=0;i<nprimitives(0);i++){
    string name = prim(0,"name",i);
    if( findattribvalcount(1,"prim","name",name) < 1 ){
        removeprim(0,i,1);
    }
}

In case of points, let's say you split the stream, froze one bit at one frame, deleted a few particles, duplicated that setup a few times, here's one way to take the original stream and only remove particles that don't exist in the second input (i have special id called uid)

for(int i=0;i<npoints(0);i++){
    int uid = point(0,"uid",i);
    if( findattribvalcount(1,"point","uid",uid) < 1 ){
        removepoint(0,i,1);
    }
}

Remove duplicate prims

Only keep a single instance of a primitive that has specific attribute values on its corresponding points (think constraints):

Walkthrough:

  • We first retrieve the prim's points' values and merge them
//in a prim wrangle
int pt_ids[] = sort(primpoints(0,@primnum));
string point_A = point(0,"name",pt_ids[0]);
string point_B = point(0,"name",pt_ids[1]);
s@linkedpoints = join(sort(array(point_A,point_B)),"+");
  • We sort prims by that concatenated attribute (like "nameA+nameB")

'Sort' sop by prim attribute "linkedpoints"

  • We finally go through the prims and look if the preceding prim already has that attribute value. If so, we deleted the prim
//in a detail wrangle
string previous_linkedpoints = prim(0,"linkedpoints",0);
for(int i=1;i<nprimitives(0);i++){
     string current_linkedpoints = prim(0,"linkedpoints",i);
     if( current_linkedpoints == previous_linkedpoints ){
        removeprim(0,i,1);
     }
     previous_linkedpoints = current_linkedpoints;
}

Hanging wire (parabola/catanery)

GDzkzii.png 1Wg0G53.gif

https://en.wikipedia.org/wiki/Catenary

// takes points and creates hanging 'wire' points between said points, using the catenery formula.  
// Use in a detail wrangle, make sure to hit the 'Create spare parameter on the right >' and add 
// values like curve factor 3 and Rez 10 

vector pts[];
int data[];

float curve = ch('curve_factor');                        // > 0, gets close to a flat line when you go above 5  
int rez = chi('rez');                                    //number of points to create between given points (assumes the points are at uniform-ish distances)
rez+=1;

vector firstpointpos = point(0,'P',0);
append(pts,firstpointpos);
append(data,1);
removepoint(0,0); 

//go through initial points and append catenary coordinates to array with given resolution
for(int i=1;i<npoints(0);i++){
    vector a = point(0,'P',i-1);
    vector b = point(0,'P',i);
    for(int j=1;j<rez;j++){
        float lerpV = j*1.0/rez;                         // ( 0 < lerpV < 1 ) 
        vector mix = lerp(a,b,lerpV);
        mix.y = mix.y + curve * cosh((lerpV-.5)/curve);
        mix.y -= curve * cosh(-.5/curve);
        append(pts,mix);
        append(data,0);
    }
    append(pts,b);
    append(data,1);
    removepoint(0,i);                                   //delete original point
}

//create points by reading array, adding group attributes as we go

int newpts[] = {}; 
for (int k = 0; k<len(pts);k++){
    int newpoint = addpoint(0,pts[k]);
    append(newpts,newpoint);
    setpointgroup(0, "original", newpoint, data[k], "set");
    if(data[k]){
        setpointattrib(0,"Cd",newpoint,{1,0,0});    
    }else{
        setpointattrib(0,"Cd",newpoint,{0,0,1});    
    }
}

//join with polyline
addprim(0,"polyline",newpts);

Torus / Helix (Toroidal Helical Coil)

R0QNZx9.png

float t = 1.0*@elemnum/@numelem ;

float completion = ch('completion') * 2 * $PI;
float coils = ch('coils');
float R = ch('outerRadius');
float r = ch('innerRadius');

float u = t * completion * coils ;
float v = t * completion ;

float x = cos(v)*(R+r*cos(u));
float y = sin(v)*(R+r*cos(u));
float z = r * sin(u);

@P = set(x,y,z);

List of things to Read

* Jake rice primUvs / split / greeble: https://github.com/jakericedesigns/Poly-Splitting-Blog
* Un-catmullclarking https://dspace5.zcu.cz/bitstream/11025/6630/1/Laquentin.pdf

Get primitive angles

s3TEwHc.png

Calculates the minimum angle (sharp) of each triangle

//if each prim = triangle

//get the 3 prim points
int pp[] = primpoints(0,@primnum);

//grab their positions
vector a = point(0,"P",pp[0]);
vector b = point(0,"P",pp[1]);
vector c = point(0,"P",pp[2]);

//get the vectors to calculate angle
vector ab = b-a;
vector ac = c-a;
vector cb = b-c;

//get angles from law of cosines: arccos(Ab.Ac) where Ab and Ac are normalized vectors
float angleA = degrees(acos(dot(normalize(ab),normalize(ac))));
float angleB = degrees(acos(dot(normalize(-ab),normalize(-cb))));
float angleC = 180-angleA-angleB;


//@angleC = degrees(acos(dot(normalize(-ac),normalize(cb))));
//@angletotal = @angleA+@angleB+@angleC; // <-- should always be 180!

@minAngle = min(angleA,min(angleB,angleC));

Oriented bounding box matrix transform

(2024 edit: I think this is broken I will look into it, in the meantime there is a labs sop that does the same thing, better) Applying https://vimeo.com/214584753 with wrangle: rotates points with given perpendicular vectors expects oriented BB from 'box' as a second input

29RqKNQ.png

vector p0 = point(1,"P",0);
vector p3 = point(1,"P",3);
vector p4 = point(1,"P",4);

vector x = normalize(p3-p0);
vector z = normalize(p4-p0);
vector y = cross(x,z);

matrix m = set(x[0],x[1],x[2],0,y[0],y[1],y[2],0,z[0],z[1],z[2],0,0,0,0,0);
@P = invert (m)*@P;

Plexus like effect

o81IHkV.png

Full copypastable code on http://pastebin.com/raw/nB9GLeiZ

Trails can be done with the entagma tutorial: http://www.entagma.com/creating-geometry-with-vex/


//create an attribute on prims that will store the unique hash so we can remove duplicates later on
if(!hasprimattrib(0,'hash'))
{
    addprimattrib(0,'hash', 0, 'int');
}

//fetch the neighbours in an array
int neighbours[] = nearpoints(0, @P, ch('radius'), (chi('neighbours')+1) )[1:];
int neighbourCount = len(neighbours);

//only create triangles if there's more than one neighbour
if (neighbourCount > 1)
{
    //for each neighbour, create all possible triangles with current point (ie for 'a' and b,c,d => abc abd acd)
    for(int i=0;i<neighbourCount;i++)
    {       
        for(int j=i+1;j<neighbourCount;j++)
        {
            //create an array with the 3 current points to create a triangle
            int sortPoints[];
            sortPoints[0] = @ptnum;
            sortPoints[1] = neighbours[i];
            sortPoints[2] = neighbours[j];
            sortPoints = sort(sortPoints);
            
            //create triangle
            int prim = addprim(0,'poly');
            addvertex(0,prim,sortPoints[0]);
            addvertex(0,prim,sortPoints[1]);
            addvertex(0,prim,sortPoints[2]);
            
            //generate a 'hash' of the triangle prim, so we can remove duplicates later on
            //for points 1,3,0 it will be hash(013) --> 67429030
            int rhash = random_ihash(atoi(itoa(sortPoints[0])+itoa(sortPoints[1])+itoa(sortPoints[2])));
            setprimattrib(0, 'hash', prim, rhash, 'set');
        }
    }
}