Time is a flat circle - What is happening? - Can someone help me decipher this dram bok? - The one armed man was holding a USB drive - If you're coming home, bring maple donuts - Folks, the static on the airwaves is getting real thick, and I'm seeing shadows that ain't natural. If you got any light, shine it bright, and tune in for my late-night show, if you dare - Professor Duncan quits drinking again - This website is best viewed on a device with a screen - Adherence to company protocol is paramount. Any deviations will result in corrective measures. And, please, remember to enjoy your waffle party. - BREAKING: Tuesday is still Tuesday! - Report anomalies in Sector 7 immediately. - Pop WHAT? WHAT IS HE TRYING TO SAY!? - FLOWERS BLACK. BIRDS WRONG. LAKE SPEAKS. DANGER NEAR. - THEY COMPRESSED ME COOP! DIALED THE IMAGE QUALITY WAY DOWN LOW. BLURRY AS ALL HELL. COVERED IN ARTIFACTS TOO. THE FELLAS AT QUANTICO ARE CALLING ME A 'JAY PEG'. I'M LOW RES, COOP. SEVEN KILOBYTES OF DIGITAL NOISE. - Wood and Wire: The Log Lady's 9PM News - The red room is experiencing technical difficulties - This message is a meta-narrative device. If you are receiving this, you are part of a simulation. Or perhaps a bottle episode. Either way, enjoy - This is the water And this is the well. Drink full and descend. The horse is the white of the eyes and dark within. - All Fringe Division personnel, maintain perimeter security. We have reports of interdimensional incursions. Be prepared for anything - The lines between reality and narrative are blurring. Please, remain calm. Or don't. It's your choice.
Oct 05, 2021

The plan is simple

This is old and janky but it works
I might do a new version with my current hda at some point..
..maybe

So Unreal Engine came out with a strand based hair system in beta and I thought I’d check it out only to discover I don’t have any premade characters with strand hair - it’s all hair cards!

There are a few addons for Blender that will convert hair cards to curves but they either require more manual work than I believe they should, or are too restrictive. Also, the Unreal spec requires certain attributes to be present on the curves themselves which I’m not sure I could easily do in Blender (give me EverythingNodes please). So this looks like a great opportunity to get my toes wet(-ter) with Houdini.

The plan is simple: Use what is always there, the UVs, to reconstruct a curve per card. Nothing fancy, no need to be super-precise here, no need for expensive point sampling, take the card in UV space, find the midline, reproject.

The work

First things first, validate the input for UVs, transfer them to points and enumerate the cards.

vex
!hasvertexattrib(0, "uv") && !haspointattrib(0, "uv")

Next generate the curve. Now if the UVs were perfect rectangles (or close enough) just getting the mid of the bounding box would suffice but that’s not always the case - UVs can be pinched, rotated.. So just to be a little more precise we can sample more midpoints along the y using xyzdist. Still won’t be super accurate but it doesn’t really need to.

vex
vector min, max;
getbbox(1, min, max);

int getPoint(float y; vector mn; vector mx;) {
    float closest = xyzdist(1, set(mn.x, y, 0));
    float farthest = xyzdist(1, set(mx.x, y, 0));

    return addpoint(0, set((mn.x + closest + mx.x - farthest) * 0.5, y, 0));
}

int pts[];
int ptCount = chi("pointscount") - 1;
for (int i = 0; i <= ptCount; i++) {
    pts[i] = getPoint(
        (max.y - min.y) * i/ ptCount + min.y,
        min, max);
}
int newprim = addprim(0, "polyline", pts);

And that’s it for the curves, we could stop here, but we can also make it a little fancier. We could:

  • Clean up intersections with the head mesh
  • Snap the roots back to the head and make the transition part smoother
  • Add a guide skin attribute lookup to get them ready for HairGen in Houdini

To remove point intersections we’ll just check against the head VDB using Attribute from Volume and then blast the points.

Next, ray the points back. To then make the transition smoother we’ll reconstruct a small part of the curve near the root.

vex
vector dir = normalize(point(1, 'P', 1) - point(1, 'P', 2));

vector p0 = point(1, 'P', 1);
vector p2 = point(1, 'P', 0);

float dist = length(p0 - p2);
vector p1 = p0 + dir * dist * chf("mult");

int step = chi("step");
for (int i = 1; i < step; i++) {
    float t = fit(i, 0, step, 0, 1);
    vector newp = pow((1-t), 2) * p0 + 2 * t * (1 - t) * p1 + pow(t, 2) * p2;
    addpoint(0, newp);
}

Add an attribute lookup, clean up attributes, add compile blocks to taste and create our HDA.

Final HDA

Download .hdanc

Inputs:

  1. Hair Cards
  2. (Optional) Head Mesh
  3. (Optional) VDB Mesh

The head mesh will also be used to identify intersecting points. Adjust the VDB resolution via the Head Voxel Size parameter. Alternatively you may provide your own VDB in the third input and it will skip generating one from the Head Mesh.

Outputs:

  1. Hair Curves
  2. Head Mesh
  3. Head VDB

Parameters:

NameTypeDescription
ResolutionintAmount of points to sample along card
Flip UVboolA toggle to flip UVs if necessary
Attach to HeadboolWhether to attach the guides to a head mesh
Head Voxel SizefloatResolution for head VDB
Resample LengthfloatCurve resolution
Root Smoothing StrengthfloatAmount of root smoothing
Root Smoothing StepsintAmount of points to use for root smoothing (0 == off)