DecalRoad

DecalRoad is a strip-shaped decal object defined by a chain of road nodes. It is mainly used for roads, paths, painted surface strips, tire tracks, markings, and other long decal-like surfaces that follow terrain or static meshes.

A DecalRoad is not road geometry. It is projected/clipped onto existing surfaces, usually terrain, and rendered as a decal.

It is also one of the main sources of AI/navigation data. Roads with positive drivability can contribute to the navigation graph used by AI, traffic, and routing systems.


Basic example

A simple DecalRoad entry in items.level.json:

{
  "class": "DecalRoad",
  "name": "road_main_01",
  "material": "road_asphalt",
  "textureLength": 8,
  "drivability": 1,
  "autoLanes": true,
  "oneWay": false,
  "renderPriority": 10,
  "nodes": [
    [0, 0, 0, 8],
    [50, 0, 0, 8],
    [100, 20, 0, 8]
  ]
}

Each node is stored as:

[x, y, z, width]

Important fields

Field Type Description
class string Must be "DecalRoad".
name string Scene object name.
material string Material used to render the road decal.
nodes array Road node list. Each node is [x, y, z, width].
textureLength number Length in meters before the material texture repeats.
renderPriority integer Draw order. DecalRoads are rendered in descending priority order.
zBias number Additional depth/render bias.
decalBias number Height/bias above the clipped surface.
distanceFade array[2] Distance fading parameters.
startEndFade array[2] Fade length at start and end of the road.
overObjects bool Allows clipping over static mesh objects, not only terrain.
hiddenInNavi bool Hides this road from navigation display where supported.
persistentId string Editor/engine persistent ID. Usually generated automatically.

Node data

DecalRoad stores its shape as an ordered list of nodes.

Example:

"nodes": [
  [0, 0, 0, 8],
  [50, 0, 0, 8],
  [100, 20, 0, 10]
]

Each node contains:

Index Meaning
0 X position
1 Y position
2 Z position
3 Road width

The road is generated between consecutive nodes.

The node width controls road width at that point. Width is interpolated along the road.

When overObjects is disabled, node height is projected to terrain height during regeneration.

Terrain projection

By default, DecalRoad projects onto terrain.

It samples terrain height and adjusts generated edges to sit on the terrain surface.

This means the node Z value is often less important for terrain roads, because the road is snapped to terrain during regeneration.

If the level does not contain terrain and overObjects is not enabled, the road cannot generate correctly.


overObjects

"overObjects": true

When enabled, DecalRoad can project over static mesh objects as well as terrain.

This is useful for:

  • Bridges
  • Overpasses
  • Tunnels
  • Roads on mesh geometry
  • Parking structures
  • Custom road meshes

When disabled, the road expects terrain.

If a map has no terrain and the road does not use overObjects, the road may fail to generate.

Material

The material field references a Material datablock.

"material": "road_asphalt"

The material should be authored for decal road rendering and usually tile along the road length.

Important material expectations:

  • The texture should tile vertically/along the road.
  • Opacity can be used for soft edges or markings.
  • Normal/roughness can be used for road surface detail.
  • Clear coat is not used for decal road materials.

If the material cannot be found, the road uses a warning material.


Texture length

textureLength controls how often the road material repeats along the road.

"textureLength": 8

This means the texture repeats approximately every 8 meters.

Use lower values for dense repeating patterns, higher values for large markings or long texture spans.


Render priority

renderPriority controls ordering between overlapping decal roads.

"renderPriority": 10

Decal roads are rendered in descending priority order.

Use render priority when layering:

  • Lane markings over asphalt
  • Crosswalks over road surface
  • Tire marks over road surface
  • Patch decals over base roads

zBias and decalBias

zBias and decalBias affect how the decal road is offset/depth-biased against the target surface.

"zBias": 0,
"decalBias": 0

These help avoid z-fighting, where the road flickers against the surface underneath.

Use small values. Large values can cause visible hovering or sorting artifacts.


distanceFade

distanceFade controls distance-based fading.

"distanceFade": [300, 50]

The first value is the main fade distance, and the second value is the fade range.

If distanceFade is not set, the default is:

[300, 50]

The first value should generally be greater than the second.

Invalid fade values can make roads disappear too early or behave unexpectedly.

startEndFade

startEndFade fades the road at its beginning and end.

"startEndFade": [5, 5]

This is useful for blending road decals into other surfaces or avoiding abrupt ends.

Value Meaning
First value Fade distance at the start of the road.
Second value Fade distance at the end of the road.

Spline settings

DecalRoad can generate edges using either legacy or improved spline logic.

improvedSpline

"improvedSpline": true

Uses the newer Catmull-Rom-based spline generation.

smoothness

"smoothness": 0.5

Controls spline smoothness:

Value Description
0 More linear
0.5 Medium smoothness
1 Fully smooth

detail

"detail": 0.1

Controls spline sampling step.

Smaller values create more subdivisions. For example:

0.1 = every 10%
0.2 = every 20%
0.5 = midpoint only

looped

"looped": true

Connects the end of the road back to the start.

Useful for closed tracks or loops.

startTangent / endTangent

"startTangent": true,
"endTangent": true

Uses the first/last point as tangent markers for smoother endpoints.


Spline generation modes

DecalRoad supports two spline generation modes:

  • Legacy spline mode
  • Improved spline mode

Both modes convert the road’s control nodes into generated road edges. These generated edges are then used for terrain/static clipping, UV generation, rendering, and AI/navigation data.


Legacy spline mode

Legacy mode is used when:

"improvedSpline": false

or when the field is not enabled.

Legacy mode builds a Catmull-Rom spline through the road nodes and then decides where to place generated edges based on curve direction changes.

The process is roughly:

  1. Build a Catmull-Rom spline from the node positions.
  2. Build a second spline for road width.
  3. Walk along the spline between each pair of control nodes.
  4. Add a generated edge when:
    • it is the start of a segment,
    • it is the end of the road,
    • or the direction change exceeds breakAngle.

This means breakAngle controls how densely the road is subdivided on curves.

Example:

"improvedSpline": false,
"breakAngle": 3

Lower breakAngle values create more generated edges on curves, improving shape accuracy but increasing generated geometry.

Higher values create fewer edges, which can be cheaper but may look less smooth.


Improved spline mode

Improved mode is used when:

"improvedSpline": true

This mode also uses Catmull-Rom interpolation, but subdivision is controlled more directly by detail and smoothness.

The process is roughly:

  1. Read node positions and widths.
  2. Optionally treat the first/last nodes as tangent markers if startTangent or endTangent are enabled.
  3. If looped is enabled, wrap node indices so the road forms a closed loop.
  4. Evaluate the Catmull-Rom curve at regular intervals.
  5. Generate an edge at each interval.
  6. Interpolate road width along the same curve.
  7. Project generated points to terrain/static surface height.

In improved mode:

  • smoothness controls Catmull-Rom curve tension.
  • detail controls sampling density.
  • looped allows closed roads/tracks.
  • startTangent and endTangent improve endpoint direction control.

Example:

"improvedSpline": true,
"smoothness": 0.5,
"detail": 0.1

Here, detail: 0.1 means the curve is sampled roughly every 10% of each segment.


Choosing a mode

Use improved spline for new roads when possible. It gives more predictable control over smoothness, loop behavior, and subdivision.

Use legacy mode mainly for old content or when you need to preserve the exact shape of an existing road.

Mode Best use
Legacy Existing old roads, compatibility.
Improved New roads, smoother curves, loops, better control.
Switching an existing road from legacy to improved spline mode can slightly change its shape. Check road edges, UVs, decals, and AI/navigation behavior after changing the mode.

breakAngle

breakAngle is used by the legacy spline algorithm.

"breakAngle": 3

The road is subdivided when the curve angle exceeds this threshold.

Smaller values generate more edge slices on curves.


Generated edges

During regeneration, the road nodes are converted into road edges.

Each generated edge contains:

  • Center point
  • Left/right edge points
  • Width
  • Forward vector
  • Right vector
  • Up vector
  • Parent node index

These edges are then used to capture/clamp underlying terrain/static mesh triangles.


Geometry capture

DecalRoad does not create a flat road mesh directly. Instead, it:

  1. Builds a strip from road edges.
  2. Creates clipping planes for each segment.
  3. Captures terrain/static mesh triangles inside the road strip.
  4. Generates decal road vertices and indices.
  5. Assigns UVs based on road width and textureLength.
  6. Splits the road into batches for rendering.

This allows the road to follow the underlying surface.


Batching and rendering

Decal roads are grouped by material and render priority.

The static decal road manager rebuilds and batches road geometry for efficient rendering.

Roads are split into render nodes/batches for:

  • Culling
  • Distance fade
  • Render priority ordering
  • Large road data management

This is important because levels can contain many decal road segments.


Packed vertex range

The decal road renderer packs vertex positions relative to block origins.

Large decal road batches are split into blocks so packed local positions remain within a supported range.

If a road produces a batch that is too large or spans too far, errors may be logged about packed position range or vertex count.

For best results:

  • Avoid extremely long single roads with huge uninterrupted spans.
  • Split very large roads into smaller sections.
  • Avoid very wide roads with complex clipping across many objects.

Terrain and static object updates

Decal roads can regenerate when the underlying surface changes.

Examples:

  • Terrain height changes
  • Static object with overObjects moves
  • Road nodes change
  • Material changes
  • Spline settings change
  • Bias/fade settings change

When terrain changes overlap a road, the road may be marked for regeneration.


Navigation / AI pathfinding fields

DecalRoad can contribute to AI/navigation data.

Important pathfinding fields:

Field Type Description
drivability number Determines whether/how AI uses this road.
autoLanes bool Automatically generates lane data.
lanesLeft integer Manual lane count on one side.
lanesRight integer Manual lane count on one side.
oneWay bool Marks the road as one-way.
flipDirection bool Reverses logical driving direction.
gatedRoad bool Marks the road as gated/private.
autoJunction bool Allows automatic junction connections.
useSubdivisions bool Generates higher-detail AI road data.
hiddenInNavi bool Hides road from navigation display where supported.

drivability

drivability controls if and how the road is used by AI/navigation.

"drivability": 1

Typical values:

Value Meaning
-1 Not used for AI/navigation.
1 Normal/optimal road.
0.5 Less preferred road, often dirt or rough road.
0 Very poor route, generally avoided.

If you want AI traffic to use the road, set drivability above 0.

If the road is visual only, use:

"drivability": -1

autoLanes

"autoLanes": true

When enabled, lanes are generated automatically from road width and road rules.

When disabled, use:

"lanesLeft": 1,
"lanesRight": 1

Manual lane settings are interpreted together with the level’s roadRules.rightHandDrive setting from info.json.


oneWay and flipDirection

oneWay makes the road one-way:

"oneWay": true

By default, direction follows node order.

flipDirection reverses the logical direction:

"flipDirection": true

Use flipDirection if AI drives the wrong way.


gatedRoad

"gatedRoad": true

Marks the road as gated/private.

This is used for:

  • Private roads
  • Restricted roads
  • Dirt tracks
  • Race tracks
  • Non-public routes

In navigation processing, private/gated roads are treated differently from normal public roads.


autoJunction

"autoJunction": true

When enabled, the navigation system may automatically connect paths when they intersect.

Disable it for roads where automatic merging would create bad connections:

"autoJunction": false

Useful for:

  • Overpasses
  • Tunnels
  • Parallel roads
  • Complex intersections
  • Close roads at different heights

useSubdivisions

"useSubdivisions": true

When enabled, the AI/navigation graph can use higher-detail road edge subdivision data instead of only road control nodes.

This can improve navigation path fidelity on curved roads.


hiddenInNavi

"hiddenInNavi": true

Hides the road in navigation display where supported while still allowing it to exist in the graph depending on system behavior.


Serialization

In modern items.level.json, road nodes are stored in the nodes array:

"nodes": [
  [0, 0, 0, 8],
  [50, 0, 0, 8]
]

Older TorqueScript serialization used repeated Node = "x y z width"; fields. Modern JSON should use the nodes array.


Minimal road example

{
  "class": "DecalRoad",
  "name": "road_asphalt_01",
  "material": "road_asphalt",
  "textureLength": 8,
  "renderPriority": 10,
  "drivability": 1,
  "autoLanes": true,
  "autoJunction": true,
  "nodes": [
    [0, 0, 0, 8],
    [50, 0, 0, 8],
    [100, 20, 0, 8]
  ]
}

One-way road example

{
  "class": "DecalRoad",
  "name": "highway_ramp_01",
  "material": "road_asphalt",
  "textureLength": 12,
  "drivability": 1,
  "oneWay": true,
  "flipDirection": false,
  "autoLanes": false,
  "lanesLeft": 0,
  "lanesRight": 2,
  "nodes": [
    [0, 0, 0, 8],
    [80, 10, 0, 8],
    [160, 40, 0, 8]
  ]
}

Bridge / overObjects example

{
  "class": "DecalRoad",
  "name": "bridge_decal_01",
  "material": "road_asphalt",
  "overObjects": true,
  "textureLength": 10,
  "drivability": 1,
  "autoJunction": false,
  "nodes": [
    [0, 0, 20, 8],
    [50, 0, 20, 8],
    [100, 0, 20, 8]
  ]
}

Visual-only marking example

{
  "class": "DecalRoad",
  "name": "painted_line_01",
  "material": "road_line_white",
  "textureLength": 4,
  "renderPriority": 20,
  "drivability": -1,
  "decalBias": 0.001,
  "nodes": [
    [0, 0, 0, 0.3],
    [30, 0, 0, 0.3]
  ]
}

Best practices

  • Use DecalRoad for road decals and pathing surfaces, not raised road geometry.
  • Use TSStatic or terrain for actual physical geometry.
  • Use overObjects for bridges, overpasses, tunnels, or mesh roads.
  • Keep textureLength matched to the material’s intended scale.
  • Use renderPriority for layered markings.
  • Set drivability correctly:
    • 1 for normal roads
    • 0.5 for rough/dirt roads
    • -1 for visual-only decals
  • Disable autoJunction for stacked roads, overpasses, or close parallel paths.
  • Use proper lane settings for AI/traffic.
  • Split very long or complex roads into manageable sections.
  • Regenerate roads after terrain or node changes.

Common issues

Road does not render

Possible causes:

  • Fewer than two nodes
  • Invalid or missing material
  • No terrain and overObjects is false
  • Road captured no underlying geometry
  • Distance fade hides it
  • Material is road_invisible

Road flickers or z-fights

Adjust:

"decalBias": 0.001

or check material/render priority.

Road floats above terrain

decalBias may be too high, or the road is projected over an unexpected object.

Road does not follow bridge/mesh

Enable:

"overObjects": true

AI does not use the road

Check:

"drivability": 1

Also check lane settings and whether the road contributes to the navgraph.

AI drives the wrong direction

Toggle:

"flipDirection": true

or check node order.

Roads connect incorrectly

Set:

"autoJunction": false

on problematic roads.

Texture repeats too often or too slowly

Adjust:

"textureLength": 8

Lower values repeat more often. Higher values stretch/repeat less often.

Road disappears in distance

Check:

"distanceFade": [300, 50]

or increase the fade distance.


Summary

DecalRoad is a terrain/static-surface projected road decal object.

It is used both visually and for AI/navigation. Visually, it renders a textured strip clipped onto terrain or static meshes. For gameplay, it can contribute road/lane/drivability data to the navigation graph.

Use it for roads, paths, markings, and navigation-connected surfaces. For actual raised geometry, use TSStatic or terrain, with DecalRoad projected on top when needed.

See also: Material file format , Decals format , Navigation map (map.json) , MeshRoad .

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.