I'm looking for a way to easily create vent tunnels for a game. Is there a way to dynamically turn this mesh: enter image description here

Into something like this? enter image description here

Things I'm looking for:

  • You can't see in the second picture, but the faces actually have some thickness to them, since the object will seen from the outside and the inside.
  • Ideally the control mesh would not need manual subdividing.
  • I'm also hoping for something that's not too slow to calculate.

Things I tried:

  • The skin modifier: couldn't figure out how to avoid the "smoothing" effect around the intersections.
  • Geometry Nodes: My plan was to distribute cubes along the edges, but I could not figure out how to stretch a cube to fit an edge, so I resigned to subdividing the guide mesh, instancing one cube for each vertex and then using a boolean node to merge the instances. This works but is pretty slow due to the number of cubes in the boolean operation.

Kind regards!

  • $\begingroup$ If you want it to be aligned to a grid, you could spawn voxels, remove internal faces and merge by distance. And then solidfiy. $\endgroup$ Jan 20 at 15:07

1 Answer 1


(Using Blender 3.6.5 and Geometry Nodes)


Assuming a mesh made of connected edges, cubes are put at vertices for junctions and corridor ends, and at edges mid-point with a scaling factor for corridors. Two such objects are made with different cube sizes. The smallest is removed by Boolean operation from the largest to make thick walls.

Initial geometry

A strong hypothesis is that edges are aligned with X, Y or Z axis (ref. Hyp(1)). So no rotation is required, only scaling along edges main direction. It is also mandatory that the distance between two connected vertices is larger than twice the smallest cube size (i.e. there is enough room to put one cube between two junction/end cubes, ref. Hyp(2)).

The geometry is initialized with a single vertex, that is subsequently extruded in Edit Mode in X, Y or Z to place junctions between corridors or to place corridors end. The Geometry Nodes modifier is added to this set of edges and vertices.


The output geometry of the Geometry Nodes modifier still has some Interior Faces "inside" the walls, generated by the Boolean operation: Results from GN

But these do not affect outside or inside rendering: Results from inside

Furthermore, these can be removed in Edit Mode, after applying the modifier: Results after deleting interior faces

Geometry Nodes Graph


GN main graph 1. The inner tube is generated by a Node Group named Tunnel. Its width/height is controlled by the Group Input socket labelled Size. A pre-defined material is set on faces for later selection in Edit Mode.
2. The outer tube is generated the same way, but its size is increased by the wall Thickness value controlled through the Group Input node.
3. The volume of the inner tube is removed from the outer tube using a Mesh Boolean node set with Difference operation.

Tunnel builder

Tunnel builder graph 1. The basic object to duplicate is generated with a Cube node. Its edges length is recovered from the Group Input node.
2. Using an Instance on Points node, the cube from step 1 is duplicated at every vertex position recovered from the input geometry through a Mesh to Points node with domain set on Vertices. To manipulate individual copies, a Realize Instances node follows.
3. The length and orientation of edges are computed by subtracting the position of end vertices. These are recovered by an Edge Vertices node. Length and orientation are coded in a single vector. Because of Hyp(1), only one coordinate is different from 0.
4. The scaling factor along the edge direction, applied at step 7 to the basic cube, is initialized by dividing the edge length by the cube size. Because of Hyp(2), this value is greater or equal to 2.
5. To account for the two half cubes already put at the vertices position at step 2, the unitary vector aligned with the edge is subtracted from the scaling factor from step 4.
6. After step 5, the vector coding the length and orientation of edges is full of 0, except one coordinate aligned with the edge, that is greater or equal to 1. To make a scaling vector from this, a Maximum node comparing to the vector (1,1,1) is used. The 0s are replaced by 1s, and the remaining value is unmodified. (NB: if Hyp(2) is removed because a scaling factor lower than 1 is required, a mask could be computed from the vector after step 3 and used in a linear combination to achieve the same result). The scaling vector is registered in Edge domain using a Store Named Attribute node.
7. Using an Instance on Points node, the cube from step 1 is duplicated at every mid-edge position recovered from the input geometry through a Mesh to Points node with domain set on Edges. In the process, the scaling vector from step 6 is transferred to the Point domain. Thus this value is available to scale individual instances. To manipulate individual copies, a Realize Instances node follows.
NB: Only one face out of two is removed by a Merge by Distance node at the interface between a cube and a corridor. So Interior Faces "inside" the geometry remain. To remove both, these should be deleted before joining cubes and corridors.
8. Using a Raycast node, rays are emitted from all corridors faces generated at step 7, using Position and Normal nodes to recover source position and ray direction from corridors, toward the cubes faces generated at step 2, specified as Target Geometry. Ray Length is limited to one-hundredth of the basic cube size, so only superimposed faces are hit. Those from corridors hitting cubes are deleted.
9. The same way, faces from cubes generated at step 2 are tested against corridors faces generated at step 7 to be possibly deleted.
10. Remaining cubes and corridors faces are joined in a single geometry using a Join Geometry node before duplicated vertices and edges are merged. (NB: for robustness, the Distance parameter of the Merge by Distance node should be a fraction of the basic cube size instead of a fixed value).

Post Geometry Nodes Modifier edition

The Boolean difference generates some faces inside the walls, connecting inner and outer faces, as illustrated in the following figure where some outer faces are hidden. Interior faces before delation

To remove those Interior Faces, the process can be: Results after apply In Object Mode:
1. Duplicate the initial geometry and Apply the modifier to the copy.
In Edit Mode/Face selection mode:
2. From the menu Select, choose Select All by Trait/Interior Faces.
3. From the menu Mesh, choose Delete/Faces.


  • $\begingroup$ This works pretty well for me! $\endgroup$
    – Luke B.
    Jan 20 at 23:54

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .