This is a research I made for generating paths and roads. I got tired of all those complicated and useless road generators, so I decided to make a very simple on my own. Probably it will be used in some future games I will make (mostly in Unity, that will be ported to).

I guess that it might be also very useful to use it in your games too, so grab it here study it and learn from it.

Room for improvments:
1. Smooth curve calculation: Bezier algorithm seem to suck a bit, I will try a different algorithm for even distribution.
2. Find a way to control the width of the path (on each cuve point) to add narrow or wider varieties.
4. Add support for N handle points (now only limited to 4)
5. Add support for enclosed curve paths

```using System;
using System.Collections.Generic;
using System.Drawing;
using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.Input;

namespace ObjectOnBezier
{
public class Program : GameWindow
{
Matrix4 matrixProjection, matrixModelview;
Random rand = new Random();

#region Path properties
/// <summary>How many segments road has.</summary>
int pathSegments = 20;
/// <summary>The handles of the curve.</summary>
Vector3[] handles = new Vector3[4];
/// <summary>Generated path that is based on handles.</summary>
Vector3[] path;
/// <summary>The actual vertex geometry of the path.</summary>
Vector3[] geometry;
#endregion

/// <summary>This is the player movement along the path (added for test purposes only)</summary>
float cycle = 0f;

{
Width = 1024;
Height = 768;
GL.ClearColor(Color.DarkRed);
GL.Enable(EnableCap.DepthTest);

// Length of the path (for display purposes)
float size = 25f;
for (int i = 0; i < handles.Length; i++)
handles[i] = new Vector3(0f, 0f, size - (i * size/2f));

path = new Vector3[pathSegments];
geometry = new Vector3[pathSegments * 2];
}

protected override void OnResize(EventArgs e)
{
GL.Viewport(0, 0, Width, Height);
matrixProjection = Matrix4.CreatePerspectiveFieldOfView(
(float)Math.PI / 4, Width / (float)Height, 1f, 1000f);
GL.MatrixMode(MatrixMode.Projection);
}

protected override void OnUpdateFrame(FrameEventArgs e)
{
// When player reaches and of the path then reset his position and change positions of handles

cycle += (float)e.Time * 1f;

if (cycle > 1f)
{
cycle = 0f;

for (int i = 0; i < 4; i++)
handles[i].X = (float)rand.Next(-10, 10);
}
}

protected override void OnRenderFrame(FrameEventArgs e)
{
#region Camera
matrixModelview = Matrix4.LookAt(0f, 30f, -30f, 0f, 0f, 0f, 0f, 1f, 0f);
GL.MatrixMode(MatrixMode.Modelview);
#endregion

#region Draw point handles (yellow large points)
GL.PointSize(10f);
GL.Color3(Color.Yellow);
GL.Begin(BeginMode.Points);
for (int i = 0; i < 4; i++) GL.Vertex3(handles[i]);
GL.End();
#endregion

#region Process path curve
// Calculate positions points
for (int i = 0; i < pathSegments; i++)
{
float t = i / (float) pathSegments;
path[i] = CalculateBezierPoint(
t, handles[0], handles[1], handles[2], handles[3]);
}

// Draw line of the curve path
GL.PointSize(5f);
GL.Color3(Color.Green);
GL.Begin(BeginMode.LineStrip);
for (int i = 0; i < pathSegments; i++) GL.Vertex3(path[i]);
GL.End();

// Draw segment points (to see how the cuve path is divided)
GL.PointSize(2f);
GL.Color3(Color.White);
GL.Begin(BeginMode.Points);
for (int i = 0; i < pathSegments; i++) GL.Vertex3(path[i]);
GL.End();
#endregion

#region Process path geometry
// Calculate geometry
for (int i=0; i < pathSegments; i++)
{
Vector3 normal = new Vector3(0f);

// Note: Because we need to look ahead 1 array index, we make
// sure that we do not exceed limits of path[i] array.
if (i < pathSegments - 1)
{
// Normal calculation: nx = by - ay, ny = -(bx - ax)
normal = new Vector3(
-(path[i+1].Z - path[i].Z), 0f, path[i+1].X - path[i].X
);

normal.Normalize();
}

// Store left extrusion (calculated normal + point position)
geometry[i*2] = normal + path[i];
// Store right extrusion (flipped calculated normal + point position)
geometry[(i*2)+1] = (normal * -1f) + path[i];
}

// Draw geometry (only for test purposes)
GL.PointSize(2f);
GL.Color3(Color.Black);
GL.Begin(BeginMode.Lines);

// Don't ask: After of lots expirementation
for (int i = 0; i < geometry.Length-4; i+=2)
{
// Left
GL.Vertex3(geometry[i+0]);
GL.Vertex3(geometry[i+2]);

// Right
GL.Vertex3(geometry[i+1]);
GL.Vertex3(geometry[i+3]);

// Bottom
GL.Vertex3(geometry[i+2]);
GL.Vertex3(geometry[i+3]);

// Upper
GL.Vertex3(geometry[i]);
GL.Vertex3(geometry[i+1]);

}
GL.End();

// Draw points
GL.PointSize(3f);
GL.Color3(Color.Orange);
GL.Begin(BeginMode.Points);
for (int i = 0; i < geometry.Length; i++) GL.Vertex3(geometry[i]);
GL.End();
#endregion

#region Draw moving object
GL.PointSize(15f);
GL.Color3(Color.Pink);
GL.Begin(BeginMode.Points);
GL.Vertex3(CalculateBezierPoint(cycle, handles[0], handles[1], handles[2], handles[3]));
GL.End();
#endregion

SwapBuffers();
}

Vector3 CalculateBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3)
{
float u = 1 - t;
float tt = t * t;
float uu = u * u;
float uuu = uu * u;
float ttt = tt * t;

Vector3 p = uuu * p0;
p += 3 * uu * t * p1;
p += 3 * u * tt * p2;
p += ttt * p3;

return p;
}