How to color parts of the terrain
Introduction
How to use the CartesianTileLayerStyler and expressions to assign distinct fill colors to specific terrain feature types—e.g. footpaths, residential zones, and roads—based on their material index.
Using the Netherlands3D styling system ensures that any styling rules you define are saved directly into the project file and will be automatically reapplied whenever the project is reopened or the layer is reloaded.
Prerequisites
- A
BinaryMeshLayer
or similar layer already added to your scene
Step 1: Identify your feature-type → material-index mapping
Your tile data assigns each feature a material index via the data-materialindex
attribute. Decide which indices
correspond to each terrain part. For example:
// These values depend on how your CartesianTiles are authored!
const int footpathIndex = 0; // e.g. “footpath” material
const int residentialIndex = 1; // e.g. “residential area” material
const int roadIndex = 2; // e.g. “road” material
If you’re not sure, inspect your layer’s mesh materials in the editor or log layerFeature.Attributes["data-materialindex"]
at runtime.
Step 2: Choose your colors
Define the Color
instances you want to use:
var footpathColor = new Color(0.8f, 0.7f, 0.5f); // a light sandy tone
var residentialColor = new Color(0.9f, 0.9f, 0.6f); // a pale yellow
var roadColor = new Color(0.7f, 0.7f, 0.7f); // light gray
Step 3: Apply styling in code
In your layer-initialization (or UI callback), call CartesianTileLayerStyler.SetColor
for each type:
void StyleTerrainLayer(BinaryMeshLayer terrainLayer)
{
// Get the LayerGameObject wrapper
var layerGO = terrainLayer.GetComponent<LayerGameObject>();
// Iterate the existing features in this layer
foreach (var kvp in layerGO.LayerData.LayerFeatures)
{
var layerFeature = kvp.Value;
// Get the material index identifier from the attributes
if (!layerFeature.Attributes.TryGetValue("data-materialindex", out var idxStr)) continue;
if (!int.TryParse(idxStr, out var materialIndex)) continue;
// Choose color based on materialIndex
switch (materialIndex)
{
case footpathIndex: CartesianTileLayerStyler.SetColor(layerGO, layerFeature, footpathColor); break;
case residentialIndex: CartesianTileLayerStyler.SetColor(layerGO, layerFeature, residentialColor); break;
case roadIndex: CartesianTileLayerStyler.SetColor(layerGO, layerFeature, roadColor); break;
// add more cases as needed
}
}
}
What’s happening?
For eachLayerFeature
you find with a given index,SetColor
builds an expression likeExpression.EqualTo( Expression.Get("data-materialindex"), "<index>" )
and installs a
StylingRule
that fills any real feature with that same index in your layer.
Step 4: Verify in the Editor
- Enter Play Mode (or run in the Scene view).
- Inspect your terrain layer: footpaths should now use
footpathColor
, residential blocks useresidentialColor
, and roads useroadColor
. - If anything looks off, check the console for parsing errors or confirm the right material indices.
What’s next?
- Combine multiple indices: if “roads” span two materials, use a single
StylingRule
withExpression.Any
:It is recommended to add a convenience method, such asvar roadExpr = Expression.Any( Expression.EqualTo(Expression.Get("data-materialindex"), "2"), // first road material Expression.EqualTo(Expression.Get("data-materialindex"), "3") // second road material );
SetRoadColor()
to theCartesianTileLayerStyler
class -or through an extension method- to have a logical interface how to manipulate the styling of this type of layer. - Dynamic UI: build a color-picker panel that reads the current style via
GetColor
and lets users tweak colors at runtime.
Performance tips
- Regroup similar expressions into fewer rules; avoid one-rule-per-feature if you have hundreds.