Jump to content

Violation of strict nesting of blocks...

Recommended Posts

Hi, can someone help me figure out how to fix the compile block error? I have a nested For Each Loop, and I likely need a Block Begin somewhere set to Fetch Input, but I can't figure out where.



Share this post

Link to post
Share on other sites

Hi !
So, Compile Blocks.. Super useful, but a handful to work with. I'll try to explain what's going on.


Part 1 - The foreach_end2 loop.

You do not actually need a Fetch Input for the loop, the problem is the references that are made from within the foreach_end2 loop.
I could not have figured this out from this message alone. From working with Compile Blocks a bit, I know that references are much more strict than normal.

So, knowing that, I tried to strip out the references out from anything that reference out of it, and anything that references into it. Based on the dotted blue lines, the two places that goes "in" and "out" are on the "generate_line_points" (out), and "bend1" (in).
For now, we'll just strip out the out reference, and will go back to it later. But for the Spare Input 1 of the bend1, we can replace it with the "grass_height_mult" node.
Notice that I remove the expression on the Length parameter; I took a note of it, and will use it later.

The rationale for the bend1 case is that if you reference a forloop_begin, its data will change at each loop. Without a Compile Block it's no big deal, since the loop will just finish, and the reference will just have the data of the last loop. But in a Compile Block which has to know everything in advance, it does not like this at all. In your case, referencing the input works perfectly, so that fixes this issue.


Part 2 - The "distanceAlongGeometry" nodes.

At this point, the Compile End should have another message. Something about the "distancealonggeometry1", some reference stuff.

Okay, we'll ignore that for now. Bypass. The Compile now complains about the "distancealonggeometry2". Right okay, complains for one, complains for the other, makes sense. Bypass.
And now, huh ! The Compile Block actually has no error anymore ! But, uhm, the result is now nowhere near the original one.


Part 3 - Bring the result back on par with the original.

Alright, now that we've got the culprits down, we can start working on rehabilitating making them Compile-friendly.
I will be doing a few tricks to get mostly the same result as you, but I won't go in all the details, as this already-long answer would go on forever.
(After finishing, looks like I gave all the details anyways.)


Part 3.1 - The foreach_end2.

I suggest this guy to not be a loop at all. Instead of iterating over each points individually, making a line and copying the line on the point, we can get rid of the copytopoints and the loop using one trick with the add node.
We can specify an attribute to say "hey, I want the add to connect the points with this same attribute value". So, if we've got an attribute, say "class", that is the same on each points of the lines, the add will only connect those together, ridding us of the loop.
With a bit of a modification to the wrangle, we can get there.

Here's the new wrangle (Running over Points) :

float length = point(-1, 'height', 0) * 0.85 * f@height_mult;
int npt = 5;
float increment = length / npt;
vector pos = @P;

for (int i = 0; i <= npt; i++) {
    int pt = addpoint(0, pos);
    setpointattrib(0, "class", pt, @ptnum);
    pos.y += increment;
removepoint(0, @ptnum);

Note that I got rid of all spare parameters, and added back only one, referencing the main loop's "foreach_begin1". So, for the length computation, instead of doing it in a parameter, I do it in the wrangle itself, and I don't need the second spare input, since the height_mult value is already accessible on the current point of the wrangle.
Doing the "removepoint" at the end might seem weird, but you'll notice in the gif that once adding the class attribute in the add node, it connects the bottom points. That's because all the original points are still there, with class being 0. Removing the point is the quick and dirty way to do it, but there are other ways to do it. No matter though, as this works just fine.


Part 3.2 - The distanceAlongGeometry1 node.

This is a bit tricky. Actually, both are, in a different way.
For the (1), what you do with it is compute the distance from the bottom to the top, map it to the longest strand and multiply the result with a curve.

We'll have to do this manually.
So first, we need to know what's the length of the longest strand. A measure, set to perimeter, will give me the length of each strand as an attribute named "perimeter". I can then promote it to detail, set to Maximum to get the largest value.
Second, we will need to know where each points are on its primitive. This is known as the curveu in Houdini. This is a value between 0 and 1. This can be computed using the resample node. Untick "Maximum Segment Length", and tick "Curve U Attribute".
Third, we need to get the "multiply the result with a curve" section in. A Wrangle will take care of this.

Wrangle over Points, first input is the resample, second input is attribpromote.

float max = detail(1, "maxPerimeter");
float dist = prim(0, "perimeter", @primnum);

float sample = fit(f@curveu * dist, 0, max, 0, 1);
f@peak_mask = chramp("Remap", sample);


Alright, looking good.


Part 3.3 - The distanceAlongGeometry2 node.

Let's break down what you are doing : you are giving a profile to a narrow strip of polygon, forming a single leaf.
I would like to suggest another way of doing the same thing. Start from a single line instead, and then leverage the sweep node to do the profile.

We then start with a line node. Add a spare parameter to reference the main loop's foreach_begin1, and then copy-paste the expression in sizey from the grid into line1's Length. Set the Points to 5, matching the Grid's Rows.

Now, onto the sweep. Place one under the Line.
The Shape is a Ribbon.
Its Construction Plane is different from the Grid, so in Construction, in Up Vectors, set the Target Up Vector to Z Axis.
We need UVs, so in UVs and Attributes, tick Compute UVs. While comparing the UVs with the original version, I found that we need to untick Length-Weighted UVs, and untick Flip Computed UVs.
Back to Surface, set the Columns to 1, as we don't want subdivisions along the leaf.
We then want to scale along the curve, which is the name of the parameter that we want. Apply Scale Along Curve. You can copy the original curve's values into this one.
At this point it works, but we're missing the width scale. So add a spare parameter, once again pointing to the foreach_begin1, and copy the grid's sizex expression into the sweep's Width. While templating "pinch_along_length", I found while tweaking the multiplication at the end that I needed to do *2 (instead of the original *0.4). Which makes sense, since you then multiply by 5 inside pinch_along_length, and 0.4*5 = 2.

And voilà, the result should now be the same as before, and the Compile Block should work just fine !

.. Although I said that, you'll notice a difference between the original one and the Compiled version.
This is due to the bend1, referencing the first point from "grass_height_mult". Remember what I said about the non-compiled version referencing the data from the last iteration of the nested for loop ? To prove this, we can change the point expression on the bend1 that fetches the height_mult to get the data from the last point. But this is hard-coded, and the last point is based on the "grass_amount". This is just to prove a point, I don't believe it's actually important to fetch the last point's information for this.

Phew, that was a long-winded one. I hope that made sense !
Here's the scene - but I don't have some of your plugin, so I recommend you doing those changes in your scene instead. And my version is Apprentice.


Edited by Alain2131

Share this post

Link to post
Share on other sites

@Alain2131Thank you kindly for your help. I should have clarified that this is a bit of a contrived setup as part of a learning exercise for compile blocks - I specifically want to learn how to reliably get them to work with complicated nested blocks.

The generate_line_points wrangle was only created because I wanted to try plugging Block Begin into it, and the Line SOP doesn't have an input.

That whole for each loop block is not required, and can be sorted with something simple like pscale, that will drive the line length as I need it.

The Distance Along Geometry SOP just needs an internal named reference replaced with a spare input and it will compile. It's a minor oversight on SideFX's part; I've put in an RFE and they should fix it soon hopefully.

Once again, thanks for your time, this forum is amazing.

  • Like 1

Share this post

Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now