Inn the car controller post, you can see I’ve been living the Kenny asset life. I’m a huge fan of being able to focus on prototyping the gameplay.

Moving forward I’d like to be able to make content (race tracks) quickly, so today I’m going to talk about the system I made to transform an arbitrary curve in space into drivable geometry.

Process

I started a curve made with Houdini’s builtin curve SOP, which uses given points as anchors for a Bezier curve (or other curve types). Unfortunately, the curve tool is pretty clunky compared to others, and it might be easier to define elsewhere and bring in as a series of points.

houdini procedural road input curve

Input curve

Next I sample points along the curve. Corners with tight curvature need a much higher resolution to get smooth geometry, whereas straights can be a rectangle, so I used the facet SOP to consolidate colinear points. Also I only want the point data, so I chopped out the line geometry.

houdini procedural road resampling

Resampled curve

To transform the curve into a surface, I originally had tried sampling lines an using the sweep SOP, but this twisted the surface uncontrollably when working in all 3 axes. Instead, I now (for each point:

  1. Get a vector diffV along the curve. For a given point i, this can be done by averaging the differences between i-1,i and i,i+1.
  2. Generate a direction vector crossDir, orthogonal to the road. I can get this with diffV crossed with the road surface’s normal (world up since the road shouldn’t twist)
  3. Place new points at pos(point_i) +/- crossDif * width
  4. With the points in place, I can create triangles between every 3 points. I originally did this manually, but the poly patch SOP does this for me.

houdini procedural road generation

Points and lines orthogonal to the road, which form the road geometry when connected

And it becomes a drivable surface! But I’d like to make this surface form ramps with walls, not just a paper thin surface. So I follow up by extruding downwards, creating a bottom face. I do another pass, projecting this bottom face onto the XZ ground plane.

houdini procedural road extrude

Extruding and projecting onto ground

From here, it’s just some optimizations before exporting. I remove the faces on the ground, and weld close points (mainly to clean up the extruded walls where the road is on the ground already), and create smoothing groups.

Banking

Unfortunately when I tried driving it in game, I had all sorts of problems. Collision detection really struggled with the triangulation, but adding a suspension instead of a box collider fixed most of the jank.

Still, curves that move in 3 axes at once were problems. The car handles poorly on them, usually flying off. And, once triangulated, the road would form “walls” because the inner side would rise more steeply than the outer side (the Y/XZ ratio is greater).

houdini procedural road problems

Wall artifacts

One solution to both is to try to bank the turns based on the tightness of the turn. I tried banking by a function of the Y/XZ ratio (an estimate of curvature), which I smoothed over several points. Unfortunately, I didn’t have a great way to twist the surface, so it ended up just lowering/smoothing out the ramps instead of banking. It cleaned the walls but made for a dull road.

My second approach was limiting the amount of climb, e.g. for every unit of XZ, the road can at most climb some amount on the Y axis. Critically, I measure this value separately on the inner and outer edges, so if the outer edge travels further, it can climb more.

This means each point is a function of the previous point. In Houdini each attribute wrangle can only access the attributes of others points before execution, so an iteration like this requires a single pass over the whole object, tracking points in an array manually iterating.

houdini procedural road banking

Banking. Top image shows the difference between the original points and the banked result

There are still a few artifacts that can be cleaned up, but you can see the surface banks a lot for the tight turn and the walls have be substantially smoothed.

houdini procedural road in game

In Game