Procedural Canal Generation

General information

Date completed June 2021
Role in team Solo Project / Developer / Technical Artist
Environment Houdini 18.0.597 || Unity 2019.3.0f2
Language C#

Description

During my first year at the HKU, I was already mesmerized by the possibilities of procedural generation. Being able to click a few buttons and have a completely unique house be generated seemed like magic. During my final semester, I decided I wanted to harness that magic by learning the software Houdini and developing my own procedural generation tool to generate canals inspired by the gorgeous Venetian canals.


House generation

The biggest part of my project was setting up the house generator. It allows the user to change more than a 100 variables in the editor to get the precise look they want, but also gives a simple editor interface to change the larger shapes of the building. For example, the user can select the top part of the building by hovering over it with their mouse and then type a number on their numpad between 1 and 9 to get that amount of floors generated.

A similar process can be used to change the width and depth of the house, to add ivy, to change the amount of windows, to change (or remove) the position of the larger inside arches or to randomize the window positions.

Editor script snippet for the house editor

/// <summary>
/// Draws buttons using the xPos as middle point
/// </summary>
private Rect[] DrawButtons(float xPos, float yPos, float width, float height, float distance, params string[] buttons) {
    Rect[] r = new Rect[buttons.Length];

    for (int i = 0; i < buttons.Length; i++) {
        float pos = xPos - (width + distance) * buttons.Length / 2f + (width + distance) * i;
        Rect rect = new Rect(pos, yPos, width, height);
        GUI.color = Color.white;
        GUI.Box(rect, buttons[i]);
        r[i] = rect;
    }

    return r;
}
switch (currentChoiceSelection) {
  case ChoiceSelection.None:
      break;
  case ChoiceSelection.Color:
      nrButtons = DrawButtons(Screen.width / 2f, Screen.height - 200f, 100f, 50f, 50f, "1 - WHITE", "2 - BEIGE", "3 - PURPLE", "4 - ORANGE");
      break;
  case ChoiceSelection.Windows:
      nrButtons = DrawButtons(Screen.width / 2f, Screen.height - 200f, 100f, 50f, 50f, "1 - BALCONY", "2 - INSIDE FLOOR", "3 - OBSTRUCTION");
      switch (currentWindowChoice) {
          case WindowChoiceSelection.None:
              break;
          case WindowChoiceSelection.Balcony:
              nrButtons2 = DrawButtons(Screen.width / 2f, Screen.height - 300f, 100f, 50f, 50f, "1 - STONE", "2 - PILLARS", "3 - IRON", "0 - RETURN");
              break;
          case WindowChoiceSelection.InsideFloor:
              nrButtons2 = DrawButtons(Screen.width / 2f, Screen.height - 300f, 100f, 50f, 50f, "1 - FIRST FLOOR", "2 - SECOND FLOOR", "3 - THIRD FLOOR", "4 - FOURTH FLOOR", "5 - FIFTH FLOOR", "6 - SIXTH FLOOR", "7 - SEVENTH FLOOR", "8 - EIGTH FLOOR", "9 - OFF", "0 - RETURN");
              break;
          case WindowChoiceSelection.Obstruction:
              nrButtons2 = DrawButtons(Screen.width / 2f, Screen.height - 300f, 100f, 50f, 50f, "1 - LEFT", "2 - RIGHT", "3 - BACK", "0 - RETURN");
              break;
          default:
              break;
      }
      break;
  case ChoiceSelection.Ivy:
      nrButtons = DrawButtons(Screen.width / 2f, Screen.height - 200f, 100f, 50f, 50f, "1 - OFF", "2 - ON", "3 - ON ALL");
      break;
  default:
      break;
}

Canal generation

Shockingly, my canal generation tool needed a canal generation part. This was a relatively simple tool inside of Houdini, but gave me the chance to make my first self-made material in Substance Designer: procedural bricks.


Fence generation

The fence generator works in a similar way, where the user gets to draw the points along which the fence is generated. However, it has many more variables that can be altered, such as fence radius, height, and space between parts.


Terrace generation

The terrace generator allows the user to place a cube prefab, change its dimensions, position and rotation and then press a button to convert that cube into a terrace table with the same dimensions. This allows the user to quickly and organically generate tables of different sizes, with different amounts of chairs and decide with their height whether umbrellas should be covering them.


Using an abstract GeneratorCube as parent for Terrace- and LanternCubes

public abstract class GeneratorCube<T> : MonoBehaviour where T : Component
{
    protected static Transform[] currentlyCooking;
    protected static bool cookNow = false;


    protected static HEU_HoudiniAsset MyHoudiniAsset;
    protected static HEU_Parameters MyParameters;
    
    public abstract void ConvertAllCubes();

    protected void CheckHoudiniAsset() {
        MyHoudiniAsset = FindObjectOfType<T>().GetComponent<HEU_HoudiniAssetRoot>()._houdiniAsset;
    }
    
    protected static void SetNewInput(Transform[] cubes) {
        MyHoudiniAsset.GetInputNodeByIndex(0).RemoveAllInputEntries();
        foreach (Transform t in cubes) {
            MyHoudiniAsset.GetInputNodeByIndex(0).AddInputEntryAtEnd(t.GetChild(0).gameObject);
        }

        cookNow = true;
        MyHoudiniAsset.RequestCook(true, false, false, true);
    }
}

Lantern generation

The lanterns can also be generated using cubes and converting those cubes into similarly placed and sized lanterns.

Implementing abstract class GeneratorCube for LanternCube

public class LanternCube : GeneratorCube<LanternGeneratorObject>
{
    public override void ConvertCubes(Transform[] cubes) {
        CheckHoudiniAsset();
        SetNewInput(cubes);
        currentlyCooking = cubes;
        CheckHoudiniAssetLanternLight();
        SetNewInput(cubes);
    }
}

Final additions

To complete the product, I also added a bridge generator, a gondola generator, a rope generator and a pillar generator for in the water, to bind the gondolas to.




%d bloggers like this: