Tutorial: Creating a Scaffold Generator in Blender

This tutorial will walk you through how to create a scaffold generator in Blender that takes simple edge geometry as input and constructs a fully procedural scaffold on it.

Tutorial: Creating a Scaffold Generator in Blender

0. Introduction

This is a revised version of a three-year-old blog post that hopefully aged well enough to still be relevant and compatible with today's Blender. In it, I'll walk you through the main principles behind my Scaffolding Generator. Due to the sheer number of nodes involved, I won't be able to explain everything to the finest detail. However, I still want to give you a broad overview of my thought process and how things work. Here is a quick preview of what the result can look like:

The base idea of the generator is simple. As input we use mesh edges, which are easy to move around and extrude, and as output we get a scaffold. All we need is the magic happening between the group input and output. We want the generator to be able to adapt to corners, and there should also be input parameters for the amount of floors, for their height and for the depth of the scaffold. With these inputs, we can control the generator from the modifiers tab without having to poke around in the geometry nodes graph. The scaffold should consist of floors (obviously) and of poles that connect everything in all three dimensions. Once that is figured out, we can move on to ladders and barriers and any other optional doo-dads that come to mind.

When solving complex problems like these, it is important to break them down into smaller and smaller chunks until you can solve them bit by bit. So let's start building from the bottom up.

1. Creating a base floor

Scaffoldings are repetitive, so it makes sense to start with the most basic structure: a single floor. Our goal is to turn turn the input edges into a plane with parallel sides that resemble a floor.

There's an easy way to extrude curves, and it's called Curve to Mesh. That means we first need to convert the input edges to curves by using the Mesh to Curves modifier. As the extrusion profile we use Curve Line that is rotated orthogonally to the direction of the input curve. The length of the profile curve then corresponds to the width of our scaffold.

The result looks promising already. However, we can see that the corners don't look quite right yet; this is because the profile curve has been rotated by 45° at that point and not scaled accordingly. This results in the extrusion being too thin. Luckily for us there is a relatively easy fix for this, at least for angles between 90-180° (which is what we are interested in; most scaffoldings have >90° angles). You are free to dive down the rabbit hole by following this great tutorial by Blender Bash, but I will show an easier way here.

Since we're dealing with a curve as input, we can take advantage of the Set Curve Radius node to scale every point of the curve according to the angle of its adjacent edges. Make sure to set the curve radius prior to doing the extrusion operation or this will not work. To access the angle between two curve segments, all we need to do is to calculate the dot product of its vectors like so:

Now let's use this dot product to adjust the curve radius. Remember, the absolute value of the dot product equals 0 for 90° angles and 1 for 180° angles. The base radius at every point along the curve is 1, and the adjusted radius at a 90° angle should equal 1.414. The keen reader will recognize this number; it is the square root of 2. Assume a right triangle, where both legs are of length 1. Using good ol' Pythagoras, the length of the hypothenuse then equals the square root of 2 (or the sum of both sides squared). We could also go the accurate route and implement the following formula:

angle = acos(dot_product)
radius_adjustment = 1 / cos(angle / 2)

The easier but slightly inaccurate route that I took (I'm lazy) is to use a Map Range node that turns the range 0-1 into a range of 1.414-1. Its a linear interpolation, but its close enough (and it's unlikely anyone would notice).

To prevent the endpoints of the curve from being affected by this operation, simply run the radius adjustment through a Switch node with an Endpoint Selection as toggle. If a point either belongs to start or end, set the radius to 1. And that is how to adjust the radius at each point to create a (somewhat) even extrusion of our base floor.

2. Up we go

The base floor we just created will help us in two ways:

  1. We can use it as actual floors if we manage to duplicate it upwards along the Z axis, and
  2. we can extrude it to create the geometry that will later be turned into poles.

Using our Floor Height input parameter as the offset scale of the Extrude Mesh node, we can achieve something like this:

An easy way of creating arrays in geometry nodes is the Duplicate Elements node. We pass our number of floors into its Amount socket, und use the provided Duplicate Index to drive the offset. The Duplicate Elements node can work on any domain (points, edges, faces, etc), but here I chose the Instance domain. That means we first have to convert the incoming geometry into an instance. With this, we are then able to use Translate Instances with the Duplicate Index being responsible for the upwards translation. Multiply this with the Floor Height and we got ourselves a group that creates arrays along the Z axis:

Make sure to package these nodes into a group so we can then later repeat the process with other geometry (like the ladders).

Time to turn our first prototype into poles. Same as with the ground floor/shape, we use the Curve to Mesh modifier, but this time with a small curve circle as the profile. It's a good idea to wrap these nodes into their own group to re-use them for the ladders (I will call it the solidify group). Don't forget to turn your mesh into curves before the extrusion. The result should look like this:

3. Adding cuts

We can see that we need to add sections to our scaffold. Right now there are no subdivisions: the poles stretch as far as the base curve, and that's not really how scaffolds work. Time to move back to the early stages of our generator where we can operate on a few simple base curves. There is a Subdivide Curve modifier that allows us to set the amount of cuts we want to place inside a single curve. Right now, all the base curve segments are connected to a single curve, which means any cuts that we perform will be applied to the base curve as a whole (so the length of a section would wrap around corners, which is no good). To treat every segment separately, we have to use Split Edges first, before our input edges are turned into the base curve:

Left: treating the base curve as a single curve results in bad sectioning near the corners. Right: Splitting the base curve into individual curves via "Split Edges" gives even sections.

After splitting the edges, we can now resample our curve using the Resample Curve node, set to Length mode. As length input, we use our Section Length group input that controls how far apart the sections should appear. As a last step, we need to reconnect all individual curve segments that we split earlier. To do that, we turn our curve into a mesh again so that we can use the Merge by Distance modifier. To round things off, we convert the merged mesh back into a curve. To summarize, the nodes will look like this:

Just capturing the curve tangent here as well while we're at it

4. Ladders

Okay, this part is going to be node so easy (I am not sorry). Let's first think about where we want to place ladders. We don't want to have ladders in every segment of our scaffold, but they should be placed in a regular distance along the base curve. That means we have to tell every segment of our base curve (after we subdivided it in section 3) whether or not a ladder should be placed on that segment or not.

We can use the base curve to spawn ladders with the help of the Mesh to Points node, set to Edges. This modifier replaces all incoming edges with a point at the edge's center, on which we can then instance our ladders. And because Instance on Points allows us to pass a selection as a Boolean value, we can store a Boolean attribute for each of the base edges to tell it whether a ladder should be spawned there or not. We do that after we subdivided our base curves and turned them into a mesh (aka edges) again. I call this attribute is_ladder. To get a repeating selection, we pass the Index of our edges into a modulo operation (for example with a modulo of 3), and compare the result with an integer (like 0). We now have a Boolean selection that is true for every third segment of our base curve and otherwise false.

Next comes the actual ladder. It should consist of two vertical poles, connected by a number of rungs. I guess you could model a ladder by hand, duplicate it upwards and then instance it on the point we created in the previous step. But that would be boring as heck. The height of the ladder should adapt to the floor height dynamically, and the amount of rungs should also adapt accordingly. The ladder should be tilted in an alternating pattern and spawn on every floor except for the topmost one (you can see what I mean by scrolling down a bit). For the ladders we can re-use our solidify group to turn curves into solid geometry. I can't explain every detail of the ladder nodes here or this tutorial would never end, so let me try to summarize the steps:

  • Create a single vertical curve line and set the X values of its start and end positions so that it is slightly tilted and adapts to the section length (the smaller the section length, the less the ladder is tilted).
  • Resample the curve line based on the floor height (using a couple of math nodes).
  • The created points of the curve can then be used to instance rungs (again just curve lines).
  • Using our initial vertical curve line, we move it left and right by half the rung width to create the side poles of the ladder (and we join it with our instanced rungs).
  • The resulting ladder consists only of curves for now (now mesh geometry), so we can easily scale it along the Y axis to adapt it to the width of our scaffold.
  • Now solidify the ladder curves to actual poles, using the solidify group we created earlier
  • Stack the ladders using our floor stacking group (with the count being one less than the total floor count). The result is a single vertical stack of ladders.
  • Every other ladder-stack instance is then rotated by 180°, driven by a floor_index attribute that needs to be captured for every instance of the stacked ladders (inside our stacking group).
  • After realizing the ladder instances, we can then instance the whole ladder stack on every point of our base curve (with the is_ladder selection applied).
  • To rotate the ladder stacks according to the base curve, we need to store a curve tangent value for every base curve segment and pass it through an "Align Euler to Vector" node.

The result of our effort will look something like this:

Too much? Okay, sorry. Here's the full ladder graph:

5. Drawing the rest of the owl

There is certainly a lot more that can be done to further improve our little generator. However, I won't go into too much detail here anymore, because many of the remaining details simply repeat the processes I've described earlier. Think barriers at the endpoints, feet for the scaffold to stand on, or wooden side panels (extrusion is your friend).

One of the more important aspects yet to do is to add those small outer offsets of the poles. Remember when we turned the mesh into curves that later became our poles? We can offset the start- and endpoints of these curves (separately) with the Set Position node with the help of the Curve Tangent attribute. The tangent is the vector that aligns with the direction of the curve. All we have to do is to normalize this tangent, so we don't end up with differently sized offsets, and use it as the offset of our Set Position node. You can then change the amount the poles are extended by multiplying it with a small value.

Poles getting extended

Some scaffolds have diagonal poles in addition to the normal poles. We can create these using the Triangulate modifier on the faces we created right after we extruded and stacked our floors.

To add barriers to the endpoints of our scaffold, we can again first create a single barrier object by manipulating curves (as we did with the ladders), solidify it with our solidify group, then stack it and instance these stacks on the endpoints of our base curve.

To add connector pieces where the poles intersect, we simple craft a little connector object with a small cylinder and a few extrusion and scaling operators, and then instance it on the vertices of the our stacked pole geometry. This should be done before we add the outer offsets of our poles, because we don't want to add the connectors at the very endpoints of our poles if they have offsets. We then rotate the connectors according to the tangent of the curve they were spawned on.

I added a few more details like feet (simply instanced on the vertices of our base floor) and side panels (extrusions of the base floor with deleted upper and lower faces). The result looks like this:

And here is my abomination of a node graph:

don't look too closely, it will make your eyes hurt

6. Wrapping up

Well that was quite the ride, wasn't it? We started out with a single curve. We extruded it to form a base floor and fixed the corner width with completely inaccurate math tweaks. We extruded the base floor to generate pole geometry, which we then stacked to get a basic scaffold shape. We added diagonals and ladders and solidified everything to turn it into poles. At the end we merged our different branches of geometry together.

Thank you for taking the time to read my blog post to the end and I hope you learned a few things here and there. I am sure there exist smarter ways to do many of these operations, so hit me up on Mastodon with your thoughts and ideas. Critique and feedback is always welcome.

And now I wish you happy blendering!