Endless, procedural curved mesh generation in Unity – Part 2

In part one I showed you the basics of mesh generation and how to create a mesh for a simple Bezier curve. The goal of this tutorial is to create endless terrain, so in part two we’re going to refactor that code to create a mesh for a Composite Bezier curve – a curve that is continuous, smooth, and can be of any length.

This is a two part tutorial. If you haven’t already, check out part one.

Now our current code only generates a single Bezier curve given four Vector3 points. It would be pretty simple to modify the code to create multiple curves. But how can we ensure that each curve will connect nicely to the next? If we want to create smooth, endless terrain then we need continuity at the end of each curve, and the beginning of the one that follows. To make it more clear, take a look at this.

The vector of the first curve, formed by the last control point end point matches the vector of the second curve, formed by the start point first control point. The connecting point between the two curves is the midpoint between C and X. When generating our curves, as long as these two things are true, then we can guarantee our terrain will be smooth.

Next, the code. Let’s duplicate our TerrainGenerator script from the previous tutorial. Give it a new name and make the following changes.

public class TerrainGeneratorEndless : MonoBehaviour
{
    Mesh mesh;
    // This becomes a list of curves
    List<Vector3[]> curves = new List<Vector3[]> ();
    List<Vector3> vertices = new List<Vector3> ();
    List<int> triangles = new List<int> ();
 
    void Start ()
    {
        var filter = GetComponent<MeshFilter> ();
        mesh = filter.mesh;
        mesh.Clear ();
 
        var xPos = 0f;
        // For simplicity let's generate 10 connected curves
        for (int c = 0; c < 10; c++) {
            var curve = new Vector3[4];
            for (int i = 0; i < curve.Length; i++) {
                Vector3[] prev = null;
                if (curves.Count > 0) {
                    prev = curves [curves.Count - 1];
                }
                if (prev != null && i == 0) {
                    // Start of a new curve
                    // Set to the last point of the previous
                    curve [i] = prev [curve.Length - 1];
                } else if (prev != null && i == 1) {
                    // First control point of a new curve
                    // Use the end of the previous curve to calculate
                    curve [i] = 2f * 
                                prev [curve.Length - 1] - 
                                prev [curve.Length - 2];
                } else {
                    // Generate random point
                    curve [i] = new Vector3 (xPos, Random.Range (1f, 2f), 0f);
                }
                xPos += 0.5f;
            }
            curves.Add (curve);
        }
 
        // Same drawing code as before but now in a loop
        foreach (var curve in curves) {
            int resolution = 20;
            for (int i = 0; i < resolution; i++) {
                float t = (float)i / (float)(resolution - 1);
                Vector3 p = CalculateBezierPoint (t, curve [0], curve [1], curve [2], curve [3]);
                AddTerrainPoint (p);
            }
        }
 
        mesh.vertices = vertices.ToArray (); 
        mesh.triangles = triangles.ToArray (); 
    }
 
...

Just like before, create a new Quad and add this script to it. When you press play you will see 10 curves that are all smoothly connected. You can find the updated code and demo scenes for Part 1 and Part 2 on Github.