Figuring out how to render the roads correctly in OpenSAGE turned out to be a little more challenging than expected. This is the first post in a series describing the journey.

Roads in OpenSAGE

Here's a list of all posts:

We'll start by creating a test map in the World Builder for Command & Conquer Generals Zero Hour to explore the different road types and their features.

Creating a test map

We can create roads using the Road tool. First we have to select one of the predefined road types. There are quite a lot of different roads available in Zero Hour - we'll look into their definitions later on, but for now, let's just create a simple road network.

Predefined road types

Individual road segments of the selected type can be created by dragging the mouse from a start to an end point.

Disconnected road segments

When two endpoints are moved close enough together, World Builder automatically connects the two segments and merges the endpoints into a single one. This allows us to create a connected graph with nodes and edges.

Connected road segments

For each node, we can set the corner type that determines how corners are rendered, but it will only affect nodes with exactly two adjacent edges.

Broad curve: Broad curve Tight curve: Tight curve Angled: Angled

If more than two edges are joined together, the node becomes a crossing and is rendered using a special texture:

Crossing texture

A different texture is used depending on the number of edges and their angles. But where are they all defined?

It's time to look at the road definitions in the game's Roads.ini file. This is where all the road types we can select are listed. The definition of one type looks rather simple, it's just one reference to a texture and two widths:

Road TwoLaneDarkDotted
  Texture = TRTwoLane6.tga
  RoadWidth = 35.0
  RoadWidthInTexture = 0.9

Let's take a look at that texture: A sample road texture

Ah, so this is where the magic happens! Apparently there's one single image file for every road type containing the textures for all the different road types:

  • Straight
  • Broad curve
  • Tight curve
  • End cap
  • Symmetric Y crossing
  • Asymmetric Y crossing
  • T crossing
  • X crossing

There are three different versions of crossings for three edges and one for four. If more than four edges are connected to the same node, they are simply rendered as separate segments as there is no crossing texture.

Last but not least there is the checkbox labeled "Add end cap and/or Join to different road". It can be used to "blend" roads of different types together by rendering the small piece of texture between the tight curve and the Y crossing:

End cap

It should only be used for end points of a network (nodes with only one adjacent edge). When there is a different road near the end point, SAGE also tries to rotate the end cap so that it matches the other road. That works well for some cases, but when the angle becomes too small, it causes weird artefacts:

End cap (skewed)

In the next post we'll try to find out how this road network is stored in the map file.