Procedural Tree Generator Series – Houdini + Unity – Part-2

Last Updated by

Brief

The aim of this series is to create a modular and procedural tree generator tool that can be used with Unity Game Engine with the help of Houdini Engine. You can read the part-1 of this series here.
In this part we will be covering below points:

  1. Adding Gravity Force To The Branches.
  2. Preventing Stretching Caused By Gravity.
  3. Making Multi-Level Branches With Iteration.
  4. Shift To Spherical Coordinates And Control Growth Orientation.

Adding Gravity Force To The Branches

  • As tree branches usually do, they take parabolic type rotation due to gravity. The higher a branch more gravity force is exerted on it.
  • First we set a “base_pos” attribute from the current branch position using an “Attribute Wrangle” Node with Vex Expression: [email protected]_pos = @P
  • Also we would need to clear up this “base_pos” attribute in the “copy” node as it is not needed in the following iterations. But as we did for “level” attribute, we need to promote it to primitive attribute by using “Attribute Promote” node.
  • We now use an “Attribute Wrangle” node to create custom gravity force with configurable direction vector. We call this “gravity”.
vector dir = chv("dir");
float mag = length(dir);
dir = normalize(dir);

int prim = pointprims(@OpInput1, @ptnum)[0];
vector base = prim(@OpInput1, "base_pos", prim);
base.y = 0;

vector delta = set(@P.x, 0, @P.z) - base;
float dist2 = length2(delta);
@P += 0.025 * dist2 * mag * dir;
  • Now we have a directional gravity force for all the branches, but when we increase the direction of gravity the branches stetches. They should have just bent more rather than stretching.
  • We will solve this issue in next step.
Custom Gravity Force Output with Stretching Issue.

Preventing Stretching Caused By Gravity

  • The Gravity Force created in previous step uses a direct delta variable that is actually a difference vector between current position and base position.
  • To resolve the stretching problem what we actually need is a “dot product” calculation which then results in a new vector which is in the direction where the point should be after it is effected by the gravity.
  • Below is the updated VEX code for the “gravity” node.
vector dir = chv("dir");
float mag = length(dir);
dir = normalize(dir);

int prim = pointprims(@OpInput1, @ptnum)[0];
vector base = prim(@OpInput1, "base_pos", prim);
vector delta = @P - base;
float delta_length = length(delta);
float dist2 = length2(delta - dir * dot(delta, dir));
vector new_pos = @P + 0.025 * dist2 * mag * dir;
vector new_delta = normalize(new_pos - base);
new_pos = base + new_delta * delta_length;
@P = new_pos;

Making Multi-Level Branches With Iteration

  • What ever we have done till previous step can be iterated without creating extra custom nodes for each branch level. This is proceduralism at its best.
  • We use “forloop with feedback” node to iterate all process uptill previous step.

All the nodes after restructuring and iterating.

All Nodes Restructured and Iterated to Create Multi-Level Branches

Shift To Spherical Coordinates And Control Growth Orientation

  • We need to convert the Polar Coordinates system used for setting branch rotation to Spherical Coordinates system as their current rotation are wrong and isn’t physically right.
  • For Polar Coordinates we were using the formula: P = cos(phi), 0, sin(phi), but for Spherical Coordinates we will be using the formula: P = cos(phi)*cos(theta), sin(theta), sin(phi)*cos(theta).
Output of new Spherical Coordinates in Gravity Force Module.
  • As you see from the above output we don’t have much control over the growth orientation of the branches. They are still rotating around the base branch rather than their parent branch.
  • To solve this first we include a “PolyFrame” geometry node just above the “scattering_points” node. In here we name the Normal as “side”, Tangent as “up” and BiTangent as “forward”.
  • Now we will use these new attributes in the “scatter_points” vex expression.
[shortcode language=”python”] // The updated “scatter_points” wrangle node Vex code.

int seed = chi(“../info/seed”) + 974;
int steps = chi(“steps”);
int branches_per_step = chi(“branches_per_step”);
float min_u = ch(“min_u”);
float max_u = ch(“max_u”);
float random_offset = ch(“random_offset”);
float skip_chance = ch(“skip_chance”);

int current_pt;
vector current_pos;
float current_grad, current_width;
vector current_side, current_up, current_forward;

int branches = steps * branches_per_step;

float step = (max_u – min_u) / steps;
float current_step = min_u + step/2;

for (int i = 0; i < branches; i++) { if (i != 0 && i % branches_per_step == 0) current_step += step; if (rand(@primnum + i + 912 + seed) < skip_chance) continue; float u = current_step + fit01(rand(@primnum + i + seed + 149), -random_offset, random_offset); prim_attribute(@OpInput1, current_pos, "P", @primnum, u, 0.0); prim_attribute(@OpInput1, current_grad, "grad", @primnum, u, 0.0); prim_attribute(@OpInput1, current_width, "width", @primnum, u, 0.0); prim_attribute(@OpInput1, current_side, "side", @primnum, u, 0.0); prim_attribute(@OpInput1, current_up, "up", @primnum, u, 0.0); prim_attribute(@OpInput1, current_forward, "forward", @primnum, u, 0.0); current_pt = addpoint(geoself(), current_pos); setpointattrib(geoself(), "grad", current_pt, current_grad); setpointattrib(geoself(), "width", current_pt, current_width); setpointattrib(geoself(), "side", current_pt, current_side); setpointattrib(geoself(), "up", current_pt, current_up); setpointattrib(geoself(), "forward", current_pt, current_forward); } [/shortcode]

  • After setting up the point attributes for “side”, “up” and “forward”, we need to rotate the branches into the frame so that they are rotated around their parent branch always.
  • For this we use a “PointToVOP” node where we do “Rotation Matrix” with the primitive normals and the above three attributes to get proper rotations.
  • Now we have correct individual branch rotation, gravity force and parent based branch rotations.

What’s next?

  • Next we will try to convert this houdini network into a digital asset so that it can be used in Game Engines.
  • We will connect all the level parameters by creating custom gradient type ramp UIs and sliders to control the tree generation.
  • After a fully customizable digital asset (HDA) has been created, we will create Base of the Meshing System to convert splines to polygons.
  • We would also need to create runtime UV generation system for each branches.
  • Then we will proceed to create points for leaves and adding leaf geometry.
  • We then have to re-setup these new updates in our HDA UI interface and make a reusable Digital Asset for future use.
  • See you in next part-3 of this series.