MeshRoad

MeshRoad is a spline-based road object that generates real mesh geometry from nodes.

Use it when the road needs actual thickness, side faces, bridges, ramps, or geometry that is not just a decal projected onto terrain.

For painted road surfaces, markings, AI paths, and terrain-projected roads, DecalRoad is usually the better first choice.


Basic example

A simple MeshRoad entry in items.level.json:

{
  "class": "MeshRoad",
  "name": "bridge_meshroad_01",
  "__parent": "MissionGroup",
  "topMaterial": "road_asphalt",
  "bottomMaterial": "road_concrete",
  "sideMaterial": "road_concrete",
  "textureLength": 8,
  "breakAngle": 3,
  "widthSubdivisions": 0,
  "nodes": [
    [0, 0, 5, 8, 0.5, 0, 0, 1],
    [40, 0, 5, 8, 0.5, 0, 0, 1],
    [80, 10, 7, 8, 0.5, 0, 0, 1]
  ]
}

Each node is stored as:

[x, y, z, width, depth, normalX, normalY, normalZ]

Important fields

Field Type Description
class string Must be "MeshRoad".
name string Scene object name.
__parent string Parent group.
topMaterial string Material for the upper road surface.
bottomMaterial string Material for the underside.
sideMaterial string Material for side, front, and back faces.
textureLength number Length in meters before the material repeats along the road.
breakAngle number Angle threshold in degrees used when generating road slices from the spline.
widthSubdivisions integer Number of widthwise subdivisions for generated vertices.
nodes array Ordered node list. Each node is [x, y, z, width, depth, normalX, normalY, normalZ].

Node data

MeshRoad stores its shape as an ordered list of nodes.

Example:

"nodes": [
  [0, 0, 5, 8, 0.5, 0, 0, 1],
  [40, 0, 5, 8, 0.5, 0, 0, 1],
  [80, 10, 7, 8, 0.5, 0, 0, 1]
]

Each node contains:

Index Meaning
0 X position
1 Y position
2 Z position
3 Road width
4 Road depth/thickness
5 Normal X
6 Normal Y
7 Normal Z

The road mesh is generated between consecutive nodes.

Do not treat MeshRoad nodes like DecalRoad nodes. MeshRoad nodes include depth and normal data.

Creating MeshRoads

In the World Editor, create a MeshRoad when you need generated road geometry instead of a projected road decal.

Typical workflow:

  1. Create a MeshRoad object.
  2. Place nodes along the intended road centerline.
  3. Set node width and depth.
  4. Adjust node height and normal so the road deck follows the intended slope and banking.
  5. Assign topMaterial, bottomMaterial, and sideMaterial.
  6. Tune textureLength, breakAngle, and widthSubdivisions.
  7. Test visual smoothing, collision, and vehicle behavior in game.

Use fewer, well-placed nodes for smooth roads. Add nodes where the road must intentionally change direction, width, depth, height, or banking.

MeshRoad ignores normal object scale edits. Use nodes, width, depth, and generated geometry settings to shape the road.

Generation and smoothing

MeshRoad does not connect nodes with straight mesh spans directly.

When regenerated, the engine:

  1. Copies each node into a spline node containing position, width, depth, and normal.
  2. Builds a CatmullRom<MeshRoadSplineNode> spline from those nodes.
  3. Samples the spline between each pair of author nodes.
  4. Creates road slices along the sampled spline.
  5. Builds road segments between consecutive slices.
  6. Generates top, bottom, and side vertex/index buffers from those slices.

This means the centerline, width, depth, and normal are all interpolated along the spline.


Catmull-Rom spline

The smoothing algorithm is Catmull-Rom interpolation.

The engine builds a spline using the author nodes:

position + width + depth + normal

For each node span, it evaluates the spline and measures the approximate span length. The span is sampled into smaller steps before deciding where final road slices should be placed.

Catmull-Rom interpolation gives smooth curves through the control nodes. It is convenient for road authoring, but it also means tight or uneven node spacing can create unwanted bends or overshoot.

Good authoring practice:

  • Keep node spacing reasonably even.
  • Add support nodes before and after sharp transitions.
  • Avoid extreme width/depth changes over very short distances.
  • Check banking normals on curves and ramps.

breakAngle

breakAngle controls how many generated slices are kept from the sampled spline.

During generation, the engine compares the current sampled direction with the direction at the last kept slice. A new slice is kept when:

  • It is the start of a span.
  • It is the final sample of the final span.
  • The angle change is greater than breakAngle.

Example:

"breakAngle": 3

Lower values create more slices and smoother generated geometry, especially on curves.

Higher values create fewer slices, which can reduce geometry but may make curves more angular.

Very low breakAngle values can increase generated vertices, triangles, collision data, and editing cost. Use the lowest value that gives the required visual and driving smoothness.

Slice orientation

Each generated slice gets a local frame:

Vector Meaning
fvec Forward direction along the road.
rvec Right direction across the road width.
uvec Up direction from the node/spline normal.

The engine uses the slice normal and neighboring slice positions to calculate these vectors.

It then computes:

Point Meaning
p1 Center point of the road slice.
p0 Top-left edge point.
p2 Top-right edge point.
pb0 Bottom-left edge point.
pb2 Bottom-right edge point.

Width expands from the center along rvec. Depth extends downward along uvec.

If normals are inconsistent, the generated frame can twist. This is the most common cause of banking or side-face artifacts on generated mesh roads.


widthSubdivisions

widthSubdivisions adds extra vertices across the top surface width.

Example:

"widthSubdivisions": 2

The top surface uses:

2 + widthSubdivisions

vertices per slice.

This can help wide roads or banked sections shade and deform more smoothly across their width. It also increases vertex and triangle count.

Bottom and side surfaces are not subdivided widthwise in the same way as the top surface.


Texture mapping

textureLength controls the V coordinate along the road.

During vertex generation, the engine measures distance between consecutive slices and increments the texture coordinate by:

sliceDistance / textureLength

Example:

"textureLength": 8

Lower values make the texture repeat more often. Higher values stretch the texture over longer road distance.


MeshRoad vs DecalRoad

Use case Recommended class
Road marking or surface projected onto terrain DecalRoad
AI/navigation path data DecalRoad
Bridge deck with visible sides MeshRoad
Raised ramp or custom road geometry MeshRoad
Road on existing mesh with decal material DecalRoad with overObjects

MeshRoad creates geometry. DecalRoad renders a decal on existing geometry.


Materials

MeshRoad can use three separate materials:

"topMaterial": "road_asphalt",
"bottomMaterial": "road_concrete",
"sideMaterial": "road_concrete"

Materials should tile cleanly along the road length.

textureLength controls how often the material repeats:

"textureLength": 8

Tool notes

For tools that generate roads:

  • Preserve all eight node values.
  • Keep node order stable.
  • Normalize node normals where possible.
  • Generate reasonably even node spacing for smoother Catmull-Rom results.
  • Add support nodes around sharp corners, steep ramps, or sudden width/depth changes.
  • Tune breakAngle instead of blindly adding excessive author nodes.
  • Use DecalRoad if the output is only a projected visual road.
  • Use MeshRoad when generated geometry, thickness, or bridge-like behavior is required.
  • Validate collision and vehicle behavior in game after changing generated nodes.

Common issues

  • Road twists because node normals are wrong or inconsistent.
  • Materials stretch because textureLength is too large.
  • Road has missing side/bottom visuals because sideMaterial or bottomMaterial is missing.
  • Tool-generated nodes use the four-value DecalRoad format instead of the eight-value MeshRoad format.
  • Curves look angular because breakAngle is too high.
  • Mesh is too dense because breakAngle is too low or node spacing is excessive.

Validation criteria

  • All nodes have eight numeric values.
  • Top, side, and bottom materials exist.
  • Road geometry follows the intended path without twisting.
  • Curves are smooth enough at normal driving speed.
  • Generated geometry and collision remain reasonable for the road length.
  • Vehicles can drive over the generated surface as intended.
  • Decal roads and AI paths are added separately when navigation data is required.

See also: DecalRoad , Material file format , Navigation map (map.json) , Level object files (items.level.json) .

Last modified: June 24, 2026

Any further questions?

Join our discord
Our documentation is currently incomplete and undergoing active development. If you have any questions or feedback, please visit this forum thread.