Jump to content

Recursive subdivision effect


Eyteneyer

Recommended Posts

images?q=tbn:ANd9GcSRQ5FmtUy1rlV6yqByEDt

Hello dear Houdini Legion.
I am trying to create this effect from the picture above. Try to copy on the extracted points geo, but it doesn't work properly. Please help me. I followed Matt Taylor tutorial on recursive subdivision.

My goal is to use images from COPS to copy into every polygon center, and it should scale with polygon in time.

Thanks for any advice.

recursive_sub_v01.hip

Link to comment
Share on other sites

//detail
float size_mult        = chf("size_mult");
float min_size         = chf("min_size");
float max_size         = chf("max_size");

float normal_offset    = chf("normal_offset");

int use_nonuniform     = chi("use_nonuniform");
float thickness_scale  = chf("thickness_scale");

int colorize           = chi("colorize");
int random_color       = chi("random_color");

int keep_only_polygons = chi("keep_only_polygons");


// ------------------------------------------------------------
// SAFE DEFAULTS
// ------------------------------------------------------------

size_mult       = max(size_mult, 0.00001);
min_size        = max(min_size, 0.0);
max_size        = max(max_size, min_size + 0.00001);
thickness_scale = max(thickness_scale, 0.00001);


// ------------------------------------------------------------
// STORAGE ARRAYS
// ------------------------------------------------------------

vector centers[];
vector normals[];
vector ups[];
vector scales[];
vector colors[];

float pscales[];
int srcprims[];
float areas[];


// ------------------------------------------------------------
// READ ALL PRIMITIVES FIRST
// ------------------------------------------------------------

int npr = nprimitives(0);

for (int pr = 0; pr < npr; pr++)
{
    string typename = primintrinsic(0, "typename", pr);

    if (keep_only_polygons == 1)
    {
        if (typename != "Poly" && typename != "Polygon" && typename != "Mesh")
            continue;
    }

    int nv = primvertexcount(0, pr);

    if (nv < 3)
        continue;

    // --------------------------------------------------------
    // Polygon center
    // --------------------------------------------------------

    vector C = set(0.0, 0.0, 0.0);

    for (int i = 0; i < nv; i++)
    {
        int vtx = vertexindex(0, pr, i);
        int pt = vertexpoint(0, vtx);
        vector P = point(0, "P", pt);

        C += P;
    }

    C /= float(nv);


    // --------------------------------------------------------
    // Polygon normal using Newell method
    // More stable than relying only on prim_normal.
    // --------------------------------------------------------

    vector N = set(0.0, 0.0, 0.0);

    for (int i = 0; i < nv; i++)
    {
        int vtx0 = vertexindex(0, pr, i);
        int vtx1 = vertexindex(0, pr, (i + 1) % nv);

        int pt0 = vertexpoint(0, vtx0);
        int pt1 = vertexpoint(0, vtx1);

        vector P0 = point(0, "P", pt0);
        vector P1 = point(0, "P", pt1);

        N.x += (P0.y - P1.y) * (P0.z + P1.z);
        N.y += (P0.z - P1.z) * (P0.x + P1.x);
        N.z += (P0.x - P1.x) * (P0.y + P1.y);
    }

    if (length(N) < 0.000001)
        N = prim_normal(0, pr, 0.5, 0.5);

    if (length(N) < 0.000001)
        N = set(0.0, 1.0, 0.0);

    N = normalize(N);


    // --------------------------------------------------------
    // Find longest polygon edge as local X axis
    // This gives stable orientation for Copy to Points.
    // --------------------------------------------------------

    vector X = set(1.0, 0.0, 0.0);
    float longest = -1.0;

    for (int i = 0; i < nv; i++)
    {
        int vtx0 = vertexindex(0, pr, i);
        int vtx1 = vertexindex(0, pr, (i + 1) % nv);

        int pt0 = vertexpoint(0, vtx0);
        int pt1 = vertexpoint(0, vtx1);

        vector P0 = point(0, "P", pt0);
        vector P1 = point(0, "P", pt1);

        vector E = P1 - P0;
        float L = length(E);

        if (L > longest)
        {
            longest = L;
            X = E;
        }
    }

    if (length(X) < 0.000001)
        X = set(1.0, 0.0, 0.0);

    // Remove any normal component from X.
    X = normalize(X - N * dot(X, N));

    if (length(X) < 0.000001)
    {
        vector tmp = set(1.0, 0.0, 0.0);

        if (abs(dot(tmp, N)) > 0.95)
            tmp = set(0.0, 0.0, 1.0);

        X = normalize(cross(tmp, N));
    }

    vector Y = normalize(cross(N, X));

    if (length(Y) < 0.000001)
        Y = set(0.0, 1.0, 0.0);


    // --------------------------------------------------------
    // Polygon area / perimeter scale
    // --------------------------------------------------------

    float area = primintrinsic(0, "measuredarea", pr);

    if (area <= 0.000001)
    {
        // Fallback area approximation from triangulation to center.
        area = 0.0;

        for (int i = 0; i < nv; i++)
        {
            int vtx0 = vertexindex(0, pr, i);
            int vtx1 = vertexindex(0, pr, (i + 1) % nv);

            int pt0 = vertexpoint(0, vtx0);
            int pt1 = vertexpoint(0, vtx1);

            vector P0 = point(0, "P", pt0);
            vector P1 = point(0, "P", pt1);

            area += length(cross(P0 - C, P1 - C)) * 0.5;
        }
    }

    float uniform_size = sqrt(max(area, 0.0)) * size_mult;
    uniform_size = clamp(uniform_size, min_size, max_size);


    // --------------------------------------------------------
    // Non-uniform scale from local polygon bounds
    // X/Y are measured in polygon local plane.
    // --------------------------------------------------------

    float minx =  1e18;
    float maxx = -1e18;
    float miny =  1e18;
    float maxy = -1e18;

    for (int i = 0; i < nv; i++)
    {
        int vtx = vertexindex(0, pr, i);
        int pt = vertexpoint(0, vtx);
        vector P = point(0, "P", pt);

        vector D = P - C;

        float lx = dot(D, X);
        float ly = dot(D, Y);

        minx = min(minx, lx);
        maxx = max(maxx, lx);
        miny = min(miny, ly);
        maxy = max(maxy, ly);
    }

    float sx = max(maxx - minx, min_size) * size_mult;
    float sy = max(maxy - miny, min_size) * size_mult;
    float sz = uniform_size * thickness_scale;

    sx = clamp(sx, min_size, max_size);
    sy = clamp(sy, min_size, max_size);
    sz = clamp(sz, min_size, max_size);

    vector S = set(uniform_size, uniform_size, uniform_size);

    if (use_nonuniform == 1)
        S = set(sx, sy, sz);


    // --------------------------------------------------------
    // Color
    // --------------------------------------------------------

    vector Cd = set(1.0, 1.0, 1.0);

    if (random_color == 1)
    {
        float r = rand(float(pr) * 13.17 + 0.11);
        float g = rand(float(pr) * 41.73 + 0.37);
        float b = rand(float(pr) * 91.27 + 0.71);

        Cd = set(r, g, b);
    }
    else
    {
        float a_norm = fit(uniform_size, min_size, max_size, 0.0, 1.0);
        Cd = lerp(set(0.08, 0.25, 1.0), set(1.0, 0.55, 0.05), a_norm);
    }


    // --------------------------------------------------------
    // Store arrays
    // --------------------------------------------------------

    append(centers, C + N * normal_offset);
    append(normals, N);
    append(ups, Y);
    append(scales, S);
    append(pscales, uniform_size);
    append(srcprims, pr);
    append(areas, area);
    append(colors, Cd);
}


// ------------------------------------------------------------
// CLEAR INPUT GEOMETRY
// ------------------------------------------------------------

for (int pr = nprimitives(0) - 1; pr >= 0; pr--)
{
    removeprim(0, pr, 0);
}

for (int pt = npoints(0) - 1; pt >= 0; pt--)
{
    removepoint(0, pt);
}


// ------------------------------------------------------------
// CREATE OUTPUT TEMPLATE POINTS
// ------------------------------------------------------------

for (int i = 0; i < len(centers); i++)
{
    int p = addpoint(0, centers[i]);

    vector N = normals[i];
    vector UP = ups[i];

    // Rebuild clean orientation.
    vector X = normalize(cross(UP, N));

    if (length(X) < 0.000001)
        X = set(1.0, 0.0, 0.0);

    UP = normalize(cross(N, X));

    matrix3 m = set(X, UP, N);
    vector4 q = quaternion(m);

    setpointattrib(0, "N", p, N, "set");
    setpointattrib(0, "up", p, UP, "set");
    setpointattrib(0, "orient", p, q, "set");

    setpointattrib(0, "pscale", p, pscales[i], "set");
    setpointattrib(0, "scale", p, scales[i], "set");

    setpointattrib(0, "srcprim", p, srcprims[i], "set");
    setpointattrib(0, "area", p, areas[i], "set");
    setpointattrib(0, "id", p, srcprims[i], "set");

    if (colorize == 1)
        setpointattrib(0, "Cd", p, colors[i], "set");

    setpointgroup(0, "copy_centers", p, 1, "set");
}

 

kay00.gif

  • Like 2
Link to comment
Share on other sites

On 6/27/2026 at 1:24 AM, Eyteneyer said:

images?q=tbn:ANd9GcSRQ5FmtUy1rlV6yqByEDt

Hello dear Houdini Legion.
I am trying to create this effect from the picture above. Try to copy on the extracted points geo, but it doesn't work properly. Please help me. I followed Matt Taylor tutorial on recursive subdivision.

My goal is to use images from COPS to copy into every polygon center, and it should scale with polygon in time.

Thanks for any advice.

recursive_sub_v01.hip 220.02 kB · 4 downloads

you're almost there:

 

the way you tried to get the scale from the point is incorrect

Do this instead, everything you need to change is visible in the wrangle

This works because the extracted centers have the same element number as the primitives they're representing.

And the bounding box can be used directly to scale a unit box to the size of that primitive. You can also remove measure SOP.

Link to comment
Share on other sites

Posted (edited)

OMG thank you so much! Wow! Antonio, could you please help me with growing effect? Is there any way to grow this from, one big, lets say rerctangle and in 145 or more frames one by one divide gently this rectangles as i mention in reference picture))

Edited by Eyteneyer
  • Like 1
Link to comment
Share on other sites

Posted (edited)
20 hours ago, Eyteneyer said:

OMG thank you so much! Wow! Antonio, could you please help me with growing effect? Is there any way to grow this from, one big, lets say rerctangle and in 145 or more frames one by one divide gently this rectangles as i mention in reference picture))

You should start by fixing the original code so that the prims are all oriented the same way, and so that down the line, X always means world X, and Y always means  world Y. This is left as exercise. This makes it simpler to debug or control if everything is nice and aligned to start with, all oriented the same.

Other than that, it should be doable by linking the iteration count to the currents values of X and  Y , with two separate ramps functions, with something like this (not tested):

   float iter = detail(1,"iteration",0)/ float(detail(1,"numiterations",0));

and use that iter value to read from a chramp

With low iter values, you should return values close to something between 0 and 0.5

and the higher it gets, the closer to 0 or 1 it should go; this way, only one prim will dominate (visually) in the subdivision

Getting it right is the tricky part though

 

Edited by AntoineSfx
  • Like 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...