Houdini VEX
Collection of VEX snippets for Houdini
Creates two point attributes. One 0 - 1 and one 0 - 1 - 0 along each curve, based on point number / vertex order for each primitive.
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[];
float umap, umap_half;
addattrib(geoself(), "point", "umap", 0.0);
addattrib(geoself(), "point", "umap_half", 0.0);
prim_points = primpoints(geoself(), @primnum);
for ( int i = 0; i < len(prim_points); i++ ){
umap = float(i) / (len(prim_points) - 1);
umap_half = clamp(umap, 0, 0.5) * clamp(umap * -1 + 1, 0, 0.5) * 4;
setattrib(geoself(), "point", "umap", prim_points[i], 0, umap, "set");
setattrib(geoself(), "point", "umap_half", prim_points[i], 0, umap_half, "set");
}
Creates a normal along a single curve, two point based, unnormalized
Run in a Wrangle SOP (Run over - Points)
vector pos, nor_a, nor_b;
if ( @ptnum == 0) { getattribute(@OpInput1, pos, "point", "P", @ptnum + 1, 0); @N = @P - pos; } else if ( @ptnum == (npoints(@OpInput1) - 1) ) { getattribute(@OpInput1, pos, "point", "P", @ptnum - 1, 0); @N = pos - @P; } else { getattribute(@OpInput1, pos, "point", "P", @ptnum - 1, 0); nor_a = pos - @P; getattribute(@OpInput1, pos, "point", "P", @ptnum + 1, 0); nor_b = @P - pos; @N = (nor_a + nor_b) / 2; }
Curve Normals - Runs over several primitives
Creates a normal along a curve for each primitive, single point based, normalized
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[]; vector pos_A, pos_B, dir;
addattrib(geoself(), "point", "N", {0, 0, 0});
prim_points = primpoints(geoself(), @primnum);
for ( int i = 0; i < len(prim_points); i++ ){
getattribute(@OpInput1, pos_A, "point", "P", prim_points[i], 0);
if ( i == 0) {
getattribute(@OpInput1, pos_B, "point", "P", prim_points[1], 0);
dir = normalize(pos_B - pos_A);
}
else {
getattribute(@OpInput1, pos_B, "point", "P", prim_points[i - 1], 0);
dir = normalize(pos_A - pos_B);
}
setattrib(geoself(), "point", "N", prim_points[i], 0, dir, "set");
}
Curve length
Creates two attributes, one primitive attribute for the total length of each curve and one point attribute for the partial length of the curve up to that point
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[]; float length; vector pos_A, pos_B;
addattrib(geoself(), "point", "length_partial", 0.0);
prim_points = primpoints(geoself(), @primnum);
length = 0;
for (int i = 1; i < len(prim_points); i++){ getattribute(@OpInput1, pos_A, "point", "P", prim_points[i - 1], 0); getattribute(@OpInput1, pos_B, "point", "P", prim_points[i], 0);
length += distance(pos_A, pos_B);
setattrib(geoself(), "point", "length_partial", prim_points[i], 0, length, "set");
}
f@length = length;
Curve Orient - Runs over several primitives
Creates a orient attribute along each curve. A point normal vector attribute pointing along the curve is needed. Uncomment line 21 if you have a unique up vector point attribute for each curve that you want to use.
Run in a Wrangle SOP (Run over - Primitives)
addattrib(geoself(), "point", "up", {0, 0, 0}); addattrib(geoself(), "point", "side", {0, 0, 0}); addattrib(geoself(), "point", "orient", {0, 0, 0, 0});
int curve_pts[];
matrix3 matx; vector v_align_to, v_align_from, v_up, v_side; vector4 quat;
vector v_ref = {1, 0, 0};
curve_pts = primpoints(geoself(), @primnum);
for (int i = 0; i < len(curve_pts); i++) { if ( i == 0 ) { // For the first point getattribute(@OpInput1, v_align_to, "point", "N", curve_pts[0], 0); //getattribute(@OpInput1, v_ref, "point", "up", curve_pts[0], 0);
v_up = cross(normalize(v_align_to), normalize(v_ref));
v_side = cross(normalize(v_align_to), normalize(v_up));
setattrib(geoself(), "point", "up", curve_pts[0], 0, v_up, "set");
setattrib(geoself(), "point", "side", curve_pts[0], 0, v_side, "set");
matx = lookat({ 0, 0, 0 }, v_align_to, v_up);
quat = quaternion(matx);
setattrib(geoself(), "point", "orient", curve_pts[0], 0, quat, "set");
}
// Get the current points normal
getattribute(@OpInput1, v_align_from, "point", "N", curve_pts[i], 0);
matx = dihedral(v_align_to, v_align_from);
v_align_to = normalize(v_align_from);
v_up = normalize(v_up * matx);
v_side = normalize(v_side * matx);
setattrib(geoself(), "point", "up", curve_pts[i], 0, v_up, "set");
setattrib(geoself(), "point", "side", curve_pts[i], 0, v_side, "set");
matx = lookat({ 0, 0, 0 }, v_align_to, v_up);
quat = quaternion(matx);
setattrib(geoself(), "point", "orient", curve_pts[i], 0, quat, "set");
}
Curve Multi Carve
Let's you carve several curves at one based on a unique primitive carve attribute for each curve. Based on point number and depends on a point U Map attribute between 0-1 on each curve.
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[], carve_point_start, carve_point_end; float carve_start, carve_end, umap; vector pos_u;
prim_points = primpoints(geoself(), @primnum);
//carve_start = f@carve_start; //carve_end = f@carve_start;
carve_start = ch("carve_start"); carve_end = ch("carve_end");
if ( carve_start >= carve_end ) removeprim(geoself(), @primnum, 1); else { carve_point_start = -1; carve_point_end = -1;
// Carve Start
for ( int i = len(prim_points)-1; i >= 0; i-- ){
getattribute(@OpInput1, umap, "point", "umap", prim_points[i], 0);
if ( umap < carve_start ){
carve_point_start = i;
if ( i != len(prim_points) + 1 ){
for ( int j = i - 1; j >= 0; j-- ){
removepoint(geoself(), prim_points[j]);
}
break;
}
}
}
// Carve End
for ( int i = 0; i < len(prim_points); i++ ){
getattribute(@OpInput1, umap, "point", "umap", prim_points[i], 0);
if ( umap > carve_end ){
carve_point_end = i;
if ( i != len(prim_points) + 1 ){
for ( int j = i + 1; j < len(prim_points); j++ ){
removepoint(geoself(), prim_points[j]);
}
break;
}
}
}
if ( carve_point_start >= 0 ){
pos_u = primuv(@OpInput1, "P", @primnum, set(carve_start, 0, 0));
setattrib(geoself(), "point", "P", prim_points[carve_point_start], 0, pos_u, "set");
}
if ( carve_point_end >= 0 ){
pos_u = primuv(@OpInput1, "P", @primnum, set(carve_end, 0, 0));
setattrib(geoself(), "point", "P", prim_points[carve_point_end], 0, pos_u, "set");
}
}
Multi Skin
A prim_id primitive attribute is needed to separate each group of primitives
Run in a Wrangle SOP (Run over - Primitives)
int prim_A_points[], prim_B_points[], newPrim_points[], prim, num_prim, prim_id[];
num_prim = nprimitives(@OpInput1);
removeprim(geoself(), @primnum, 0);
if ( @primnum < num_prim - 1 ){
getattribute(@OpInput1, prim_id[0], "primitive", "prim_id", @primnum, 0);
getattribute(@OpInput1, prim_id[1], "primitive", "prim_id", @primnum + 1, 0);
if ( prim_id[0] == prim_id[1] ){
prim_A_points = primpoints(geoself(), @primnum);
prim_B_points = primpoints(geoself(), @primnum + 1);
for ( int i = 0; i < len(prim_A_points); i++ ){
prim = addprim(geoself(), "poly");
if ( i == len(prim_A_points) - 1 ){
addvertex(geoself(), prim, prim_B_points[i]);
addvertex(geoself(), prim, prim_B_points[0]);
addvertex(geoself(), prim, prim_A_points[0]);
addvertex(geoself(), prim, prim_A_points[i]);
}
else {
addvertex(geoself(), prim, prim_B_points[i]);
addvertex(geoself(), prim, prim_B_points[i + 1]);
addvertex(geoself(), prim, prim_A_points[i + 1]);
addvertex(geoself(), prim, prim_A_points[i]);
}
}
}
}
Curve Sections
Creates a unique primitive for each section of a curve
Run in in a Wrangle SOP (Run Over - Primitive)
int prim_points[], prim, point_A, point_B; vector pos_A, pos_B; prim_points = primpoints(geoself(), @primnum);
for ( int i = 1; i < len(prim_points); i++ ) { prim = addprim(geoself(), "polyline");
pos_A = attrib( 0, "point", "P", prim_points[i - 1] );
pos_B = attrib( 0, "point", "P", prim_points[i] );
point_A = addpoint( geoself(), pos_A );
point_B = addpoint( geoself(), pos_B );
addvertex(geoself(), prim, point_A );
addvertex(geoself(), prim, point_B );
} removeprim(geoself(), @primnum, 1);
Add a point at the center of each primitive
Adds a point at the center of each primitive and removes the primitive, useful for a bunch of stuff.
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[]; vector accum_pos, pos; accum_pos = {0, 0, 0};
for (int i = 0; i < primvertexcount(geoself(), @primnum); i++) { int vtx_index = vertexindex(geoself(), @primnum, i); int vtx_point = vertexpoint(geoself(), vtx_index); prim_points[i] = vtx_point; getattribute(@OpInput1, pos, "point", "P", vtx_point, 0); accum_pos += pos; }
addpoint(geoself(), accum_pos / len(prim_points)); removeprim(geoself(), @primnum, 1);
Create a poly line between adjacent points
Good for creating constraints. Similar to Connect Adjacent Pieces SOP. Beware though it creates overlapping primitives, i.e. twice as many lines as you probably want. See the next VEX snippet for a solution that removes overlapping primitives.
Run in a Wrangle SOP (Run over - Points)
float radius_max = chf("radius"); int points_max = chi("connections");
int points[] = nearpoints( 0, @P, radius_max, points_max );
int prim; string name;
for ( int i = 0; i < len(points); i++ ) { if ( points[i] <= @ptnum ) continue;
name = attrib( 0, "point", "name", points[i] );
if ( name == s@name )
continue;
prim = addprim( geoself(), "polyline" );
addvertex( geoself(), prim, @ptnum );
addvertex( geoself(), prim, points[i] );
@Cd = {1, 0, 0};
}
Remove overlapping primitives
Mainly a complement to the previous VEX snippet.
Run in a Wrangle SOP (Run over - Primitives)
int prim_points[] = primpoints( geoself(), @primnum ); vector pos_accum = 0;
for ( int i = 0; i < len(prim_points); i++ ) { pos_accum += attrib( 0, "point", "P", prim_points[i] ); }
pos_accum /= len(prim_points);
int xyz_prim; vector xyz_uv;
float xyz_dist = xyzdist( 0, pos_accum, xyz_prim, xyz_uv);
if ( xyz_prim > @primnum && xyz_dist < 0.001 ) removeprim(geoself(), @primnum, 1); else if ( xyz_prim < @primnum && xyz_dist < 0.001 ) removeprim(geoself(), xyz_prim, 1);
Random text as a point string attribute
s@text = alphabet[int(fit01(rand(@ptnum), 0, len(alphabet) - 1))];
Random Groups
int my_rand = int(rand(@ptnum) * 10);
setpointgroup(geoself(), sprintf("points_%g", (@ptnum + my_rand) % ch("num_groups") ), @ptnum, 1, "set");
Simple smooth operation that supports point groups
Put it in a For Each SOP and disable Merge Results and set it to For - Each Number, to get iterations
Run in in a Wrangle SOP (Run Over - Detail)
for ( int j = 0; j < npoints(@OpInput1); j++ ){
if ( inpointgroup(@OpInput1, "holes", j )){
int pt_neighbours[];
vector neighbour_pos, neighbour_pos_accum, pos;
pt_neighbours = neighbours(@OpInput1, j);
neighbour_pos_accum = 0;
for ( int k = 0; k < len(pt_neighbours); k++ ){
getattribute(@OpInput1, neighbour_pos, "point", "P", pt_neighbours[k], 0);
neighbour_pos_accum += neighbour_pos;
}
neighbour_pos_accum = neighbour_pos_accum / len(pt_neighbours);
getattribute(@OpInput1, pos, "point", "P", j, 0);
pos = lerp(pos, neighbour_pos_accum, 0.5);
setattrib(geoself(), "point", "P", j, 0, pos, "set");
}
}
Point Cloud - Base
Base code for point cloud opertations
int pc_ptnum, pc_points; float pc_attr, accum; vector pc_pos;
pc_points = 40; pc_attr = 0; accum = 0;
int handle = pcopen(@OpInput1, "P", @P, 1000, pc_points);
while(pciterate(handle)) {
pcimport(handle, "point.number", pc_ptnum);
if (pc_ptnum == @ptnum)
continue;
pcimport(handle, "Alpha", pc_attr);
accum += pc_attr;
} pcclose(handle); f@Alpha = accum / pc_points;
Find opposite point on a quadrilateral (4 points) primitive
I had to squash a primitive together, from on point towards the opposite of the point (the one point that wasn't connected to the "main" point). For this I needed the direction and distance to move the point.
Run in in a Wrangle SOP (Run Over - Primitive)
int prim_points[], point_A, point_B, connected, pt_neighbours[]; vector pos_A, pos_B, dir_opposite;
prim_points = primpoints(geoself(), @primnum);
for ( int i = 0; i < len(prim_points); i++ ){
if ( inpointgroup(@OpInput1, "main", prim_points[i] )){
point_A = prim_points[i];
break;
}
}
for ( int i = 0; i < len(prim_points); i++ ){
if ( prim_points[i] == point_A )
continue;
connected = 0;
pt_neighbours = neighbours(@OpInput1, prim_points[i]);
for ( int j = 0; j < len(pt_neighbours); j++ ){
if ( pt_neighbours[j] == point_A )
connected = 1;
}
if ( connected == 0 ){
point_B = prim_points[i];
//printf("Main point %g opposite point is %g \n", point_A, point_B);
break;
}
}
getattribute(@OpInput1, pos_A, "point", "P", point_A, 0); getattribute(@OpInput1, pos_B, "point", "P", point_B, 0);
dir_opposite = pos_B - pos_A;
addattrib(geoself(), "point", "dir_opposite", {0, 0, 0}); setattrib(geoself(), "point", "dir_opposite", point_A, 0, dir_opposite, "set");
Velocity vector
Calculate a velocity vector, same result as if you would take a Trail SOP and set it to Compute Velocity.
Run in a Wrangle SOP (Run over - Points)
@v = (new_pos - @P) * (1 / @Timeinc);
Read velocity as units / second or meter / second
Run in a Wrangle SOP (Run over - Points)
f@speed = length(@v) * @Timeinc;
Camera Orient
Object Merge the cameras camOrigin into SOP's with two extra point added on the add node in the camera
Point 0: 0, 0, 0 Point 1: 0, 1, 0 Point 2: 0, 0, -1
Run in a Wrangle SOP (Run over - Points)
if ( @ptnum == 0 ){ vector front, up, pos_front, pos_up; matrix3 matx; vector4 orient;
getattribute(@OpInput1, pos_up, "point", "P", 1, 0);
getattribute(@OpInput1, pos_front, "point", "P", 2, 0);
up = normalize(pos_up - @P);
front = normalize(pos_front - @P);
matx = lookat({ 0, 0, 0 }, front, up);
orient = quaternion(matx);
p@orient = orient;
} else { removepoint(geoself(), @ptnum); }
Reset orient
If you use orient to align objects before a Bullet simulation, you need to multiply the orient attribute you get from bullet with your initial orient attribute. To get a orient attribute that works correctly with transform pieces SOP or copy to point SOP.
Run in a Wrangle SOP (Run over - Points)
p@orient = qmultiply( p@orient, p@orient_init);
Point unique id
In DOP's when creating new points with a Wrangle node in a SOP Solver, it's tricky to assign unique id attribute when several points are created on the same time step. Assign -1 as the id on all the points that is created and run them trough a second Wrangle node. You need a detail attribute named id_max with the largest current id value
Run in a Wrangle SOP (Run over - Detail)
int num_new_children, new_child, new_child_id, id_max;
id_max = detail( @OpInput1, "id_max", 0);
num_new_children = findattribvalcount( @OpInput1, "point", "id", -1);
for ( int i = 0; i < num_new_children; i++ ){
new_child = findattribval( @OpInput1, "point", "id", -1, i);
new_child_id = id_max + i + 1;
setattrib(geoself(), "point", "id", new_child, 0, new_child_id, "set");
}
Cavity map
Calculates how much a deformed geometry is squashed and skewed. For each point it takes the angle between perpendicular N and each point neighbor and compares the value between rest and deformed geometry.
Run in a Wrangle SOP (Run over - Points). Plug in the rest geometry into the first input and the deformed geometry into the other. Point order has to match.
int points[];
points = neighbours( 0, @ptnum );
float angle_A_accum, angle_B_accum; vector pos, nrm, dir;
angle_A_accum = 0;
for( int i = 0; i < len(points); i++ )
{
pos = attrib( 0, "point", "P", points[i] );
nrm = @N;
dir = pos - @P;
nrm = normalize(nrm);
dir = normalize(dir);
angle_A_accum += dot(nrm, dir);
}
points = neighbours( 1, @ptnum );
angle_B_accum = 0;
for( int i = 0; i < len(points); i++ )
{
pos = attrib( 1, "point", "P", points[i] );
nrm = attrib( 1, "point", "N", @ptnum );
dir = pos - attrib( 1, "point", "P", @ptnum );
nrm = normalize(nrm);
dir = normalize(dir);
angle_B_accum += dot(nrm, dir);
}
angle_A_accum = abs( angle_A_accum ); angle_B_accum = abs( angle_B_accum );
f@angle_A_accum = angle_A_accum; f@angle_B_accum = angle_B_accum;
float angle;
angle = abs( angle_A_accum - angle_B_accum );
angle = fit( angle, 0, 1, 0, 1);
f@angle = angle * -1 + 1;
@Cd = chramp("color", angle );
VEX