Procedural fences with Houdini and Unreal Engine 4 (UE4)

September 11, 2020 -
Back

Quick Showcase Examples

Procedural fences - Example 1

Another case of use to showcase the tool

Procedural fences - Example 2

Another case of use to showcase the tool

Procedural fences - Example 3

Another case of use to showcase the tool

Technical Breakdown

Game ready fences inside Unreal Engine 4 (UE4) with Houdini

"...that fence looks awesome, what a pity that you need to redo everything"


Creating fences for a game or animation project can be either really simple or pretty complex task.

If you take the blue pill, you can create a fence template by hand out of a handful of wooden planks or metal sheets (modules) so that you got an assembled template. Then creating one or two more templates, where you scale and orient the modules differently, you got enough variation for creating a reasonably long fence without too much visual repetition. 

This technique is widely used in 3D industry within both videogame and animation projects. It is a reasonably well balanced solution in terms of time spent vs visual result. In addition, it is optimized because it relies on instancing: with the original four or five wooden boards (or whatever module) you can create the hole perimeter.

On the contrary, if you take the red pill it is because you (or your art supervisor) hates repetition and wants actual variation: different positions for the tiny screws, some modules missing here ant there, adding some litter and tiny plants in corners and so on.

___________________________________

Q: “So what´s the problem? I used this approach within a ton of projects..”

A: “Oh, that fence you´ve been working on for two weeks time looks awesome,..it is just a pity that the layout has changed and you need to redo everything in half the time…” 

___________________________________

There might be a better solution… keep reading.

Other procedural fences and my proposal: Procedural generation through HDA

As usual, I googled a bit before addressing the issue to find what is currently available about procedurally generating a fence.

Most results where related to procedural modelling which outputs a ton of geometry, suitable for animation and cinema, and a few houdini tools for gamedev purposes, specially for Unity.

The fences created meet level design flexibility and outputs final art at once

Most of these solutions were based on assembled templates like this one.

Besides, they where cartoony projects where instancing the same template again and again is ok.

So I decided to create my own game-ready version for Unreal through the Houdini engine plugin whilst able to output great visuals:

  • Game ready: optimized, instancing-based.
  • Able to fit level design requirements.
  • Able to output final art.

Keep reading for technical breakdown!

 

The result: Finished complex fences in seconds

Procedural fences - HDA technical Breakdown

...xyzdist() is like a submarine sending an acoustic beep to detect a wall


The goal is designing a tool that allows the user to create finished game ready fence with minimum effort and maximum flexibility.

 

Design guidelines:

  • Able to output game ready  and optimized final art within fluent and responsive workflow 
  • The assets will be fed dynamically from editor’s content browser. That means, the tool must be unaware of the modules it is gonna be receiving as input.

(For this demo, I used Megascan assets)

There will be three types of inputs:

  • Curve:
    Unreal path to draw the perimeter
  • Modules:
    I used wooden spikes and boards mainly, but you can feed whatever uasset existing within your project like metallic panels or the asset of your choice
  • Pillars
    For both inputs, the user will be able to feed in as many variations as required. That means you are able to feed the tool with one, two, twenty different uassets and they will be scattered randomly.
  • Doors
    I included the option of adding an actual door uasset or blueprint for both aesthetic and gameplay reasons
  • Terrain
    A collection of actors chosen by the user from world outliner on top of which the fence will be generated.

One of the niceties of the tool is to be able to use whatever actor you tell it to use as “terrain”. This allows building the fence on top of boxes, slopes or rocky mountains, basically any actor within the level. I rely on Unreal’s curve as starting point for generating the path, but it is not safe to use it the way it comes. What if the user extends the curve beyond the layout (resulting from picking actors from the level – boxes and stairs in this example)? Or underneath it? Or inside it? If I just ray the points it is pretty hard to predict what will be happening, they might intersect something, they might not, which would lead to unstability. Notice the picture below: there is original Z-shaped curve accross 3d space (meants to be unreal curve), a grid with some noise (unreal actors pick), and resulting projected curve. Pay attention to orange boxes and projected curve: only safe parts of the curve are projected. The rest is ignored. As a good practice, I keep trying different approaches to solve the same issue. Most times I use the intersect_all VEX function

int intersect_all=()

but lately I’m pretty satisfied with some kind of double-ray technique which I promoted to HDA. It basically combines using the ray node twice, raying the points against the collision geo along +N and -N, and the hit_prim attribute. If there is no collision at all in both directions, then remove the point.

string attname1=chs("Hitprim_attr_name_1");
string attname2=chs("Hitprim_attr_name_2");
attname1=sprintf("%s",attname1);
attname2=sprintf("%s",attname2);

int hit1=point(0,attname1,@ptnum);
int hit2=point(0,attname2,@ptnum);

if(hit1<0 && hit2<0) removepoint(0,@ptnum);

There are a lot of ways you can set the orientation of an instanced asset. You can aim for the simplest but limited way (through @N attribute) or aim for accuracy (@orient attribute). Thumb rule is go for simplest option if it covers expectations, but chances are @N method becomes insuficcient easily. I do an indeep VEX code breakdown of a method to rotate uassets through @orient within Procedural Bridge project, if you are eager to meet matrices and quaternions. Therefore, in order to tell you something new, I will explain here a tool I used for adding randomness to fence modules orientation. I’m talking about my friend rand_orient HDA, just a poor little point wrangle… However, it eases things a lot, because provides a quick and reusable way to randomly rotate instanced meshes whilst keeping control over the amount of rotation. The approach is building the @orient attribute first with @N and @up. Then, ask the user about “angles” and assemble them into a rotation quaternion. Finally, modify the original value of @orient with that rotation quaternion.

float seed=chf("Seed");
@orient=quaternion(maketransform(v@N,v@up));

float minX=chf("Min_Angle_X");
float maxX=chf("Max_Angle_X");
float minY=chf("Min_Angle_Y");
float maxY=chf("Max_Angle_Y");
float minZ=chf("Min_Angle_Z");
float maxZ=chf("Max_Angle_Z");

float x,y,z;

y=radians(fit01(rand(@ptnum+seed+1),minY, maxY));
z=radians(fit01(rand(@ptnum+seed+2),minZ, maxZ));

//Check if user only wants extreme values. If so, rotation
//won´t grab full spectrum but ONLY either max or min
int fullRange_x=chi("Full_range_x");
if(fullRange_x==1)
x=radians(fit01(rand(@ptnum+seed),minX, maxX));
else
{
if(rand(@ptnum+seed)>0.5) x=radians(maxX);
else x=radians(minX);
}

int fullRange_y=chi("Full_range_y");
if(fullRange_y==1)
y=radians(fit01(rand(@ptnum+seed),minY, maxY));
else
{
if(rand(@ptnum+seed)>0.5) y=radians(maxY);
else y=radians(minY);
}

int fullRange_z=chi("Full_range_z");
if(fullRange_z==1)
z=radians(fit01(rand(@ptnum+seed),minZ, maxZ));
else
{
if(rand(@ptnum+seed)>0.5) z=radians(maxZ);
else z=radians(minZ);
}

//--------------------------------------------------------
vector angles=set(x,y,z);

vector4 rotation=eulertoquaternion(angles,0);
@orient=qmultiply(@orient,rotation);

When I first addressed the issue I thought: “I will deal with that pushing the points along its normals or using a carve node an playing with the FirstU parameter.” Well, this ended up not being that easy but keeping quite a nice found…and it was moving points along a curve through xyzdist() and primuv() functions. I know this is something commonly shipped within 3D packages, but I never created my own, so I enjoyed quiete a lot implementing it by myself. xyzdist() and primuv() belong to this kind of VEX functions you don’t want to hear about when you are new in Houdini: they look complex and hard to understand. However, when you realize what they do, you automatically fell in love with them (at least I did). It’s not only because they provide a ton of data but the fact that you can achieve amazing things when combining each other. Basic setup consists of running the wrangle on points (as first input) and feed the target prim (curve path) as second input. I first used the xyzdist() to query, “what is the parametric uv coordinate of the curve corresponding to each closest point?” It is like a submarine sending an acoustic beep to detect a wall. This way we know were the beep bounced (parametric uv), and based on this, here comes the second question: “what is the value of a certain attribute in this position?” (as if the submarine could ask what colour is the wall where the beep collided). In this example, I grabbed the value of the @curveu attribute (but you can query any attribute value) whose values goe from 0 to 1 along the curve and stored it a temporary variable (k); this is useful for remapping the value at will along the path. I assumed a value of 0.5 to mean no change, that is why I did the remapping from (-0.5 to 0.5)

vector uv;
float dist=xyzdist(1,v@P,0,uv);
float k=primuv(1,"curveu",0,uv);
k=fit01(chramp("Remap_Slide",k),-0.5,0.5);

float slide=chf("Slide")*k; //This is sensitivity

f@slide=(uv.x+slide)%1.0;

Finally added an slide treshold to the uv.x coordinate and did modulus by 1.0 which allows locating only the fractional part. That allows to always query data to the curve within a meaningful range (because the parametric uv value is always between 0-start of the curve and 1-en of the curve). Exported this remapped value in a new @slide attribute. Second wrangle does the magic, it uses the exported @slide attribute as parametric coordinate to query and assign the proper position.

vector uv=set(f@slide,0,0);
v@P=primuv(1,"P",0,uv);

Missing some anchor points…

I ‘m not completely satisfied with the result, because one of the design statements I tried to stick to is not to worry about the props alignment.

The tool aimed for an experience where the user doesn’t have to worry about uasset orientation. (i.e. the source uasset spike being aligned along Y axis or X axis).

After many tests, I realized that some orientations provided more rotation randomness than others which, for me, is like having an splint in the brain.

I guess it is ok to stick to some rules when creating a tool: to use a car you cannot fill its tank with water, it needs gasoline.

So it would be fine stating anchor points like:
“…for this tool to work properly you have to set all uasset facing +X”

…but it is something I hate doing, because despite of being reasonable and flattening behaviour issues, it affects the flexibility of a project. I also reckon I might push myself too much, trying any HDA I create to be the ultimate tool.

But…ey, we are Houdini nerds, aren’t we?…

I hope you found any useful information within this post. Please leave a comment with suggestions and feedback, they are welcome!

Disclaimer: This content is product of my personal work, free to use in personal, educational and commercial projects. If you find this content useful, support me by crediting www.reveron3d.com , this is not mandatory, though.

Follow on Twitter:
@reveronadolfo

I try to highlight challenges so don´t take these series as step by step tutorials, they were created for supporting movie makers, game developers and the Houdini community in my spare time. It is product of my own expertise so it might contain mistakes. Browse www.sidefx.com for official reference.

Leave a Reply

Your email address will not be published. Required fields are marked *


Play Cover Track Title
Track Authors