Terrain Material
Apply the M_AT_LayerStack layer-stack material and its three instances to a Landscape or MeshTerrain, bind paint layers to biomes, and see what Runtime Virtual Texture and Nanite displacement need from your project.
Terrain material
M_AT_LayerStack is the plugin's terrain master. It is built on Unreal's Material Layers: a stack of six layers, with five blend assets that decide where each layer above the first shows. Each layer is one of two layer assets (ML_AT_Surface for ground, ML_AT_Cliff for rock), and each blend is a rule: a painted Landscape layer, a Mesh Partition channel, a slope, a noise band or an altitude band. Three ready instances ship, one for Landscape, one for MeshTerrain and one for painted showcase biomes.
Applying the material is manual. No code in the plugin assigns the material to a Landscape or a MeshTerrain, creates a Runtime Virtual Texture volume, enables Nanite on a Landscape, or checks your RVT or Nanite settings. The shipped L_Showcase_Landscape_Texture and L_Showcase_Landscape_Volume levels do not use this material either: they use M_Showcase_BiomeLandscape, a plain Landscape Layer Blend material. Of the shipped levels, only the two MeshTerrain showcase levels use the layer stack, through MPD_AT_Showcase.
What ships
All paths are under /AgenticBiomes/LandscapeMaster/ unless stated.
| Asset | What it is |
|---|---|
M_AT_LayerStack | The master. |
MICs/MI_AT_LayerStack_Landscape | Instance for Landscape: a noise-band layer, painted Dirt, Gravel and Sand, and a slope-driven Cliff. |
MICs/MI_AT_LayerStack_MeshTerrain | Instance for MeshTerrain: the four lower layers read Mesh Partition channels; Cliff is slope-driven. |
MICs/MI_AT_LayerStack_Showcase | Instance for Landscape that paints Desert, BroadleafForest, ConiferousForest and Snow, with a slope-driven Cliff. No shipped level uses it. |
Stack/Layers/ML_AT_Surface, ML_AT_Cliff | The two layer assets. |
Stack/Blends/ | MLB_AT_Slope, MLB_AT_Altitude, MLB_AT_NoiseBand, MLB_AT_Channel, MLB_AT_Paint_Template, and 24 paint blends named MLB_AT_Paint_<Name>. |
Stack/Palette_AT_Default, Stack/Palette_AT_Showcase | AgenticTerrainPalette assets that list paint layer names. |
Layers/LayerInfo_* | 18 Landscape layer-info assets. |
RVT/ | RVT_AT_Material, RVT_AT_Displacement, RVT_AT_Height, RVT_AT_Mask4. |
Textures/Megascans/ | Eight Quixel Megascans surface sets, 4096 by 4096. |
/AgenticBiomes/Landscape/LLI_* | 13 layer-info assets (LLI_Desert, LLI_Snow, and so on) that PaintBiomeLayers reuses or creates. |
/AgenticBiomes/PCG/MeshTerrain/MPD_AT_Showcase | A Mesh Partition definition that already uses MI_AT_LayerStack_MeshTerrain. |
The 24 paint blends are Beach, BroadleafForest, ConiferousForest, Desert, Dirt, Grass, Gravel, Jungle, Land, Mountain, Mud, Puddle, Road, Sand, Shore, Snow, Soil, StoneFloor, and MS_BroadleafForest, MS_ConiferousForest, MS_Desert, MS_Landslide, MS_MountainForest, MS_Snow. Each one reads the Landscape layer of the same name.
Apply it to a Landscape
- Select the Landscape actor. In Details, under Landscape, set Landscape Material (
LandscapeMaterial) toMI_AT_LayerStack_Landscape. - Add the layers the instance paints as target layers on the Landscape, each with a layer-info asset whose layer name matches:
Dirt,GravelandSandforMI_AT_LayerStack_Landscape. Use Landscape mode, or theAddLandscapeTargetLayertool. - Paint them.
The Dirt (auto) layer and the Cliff layer need no painting: the first is a noise band and the second follows slope.
Check the Layer Name on a layer-info asset before you use it. LayerInfo_Auto, LayerInfo_Gravel, LayerInfo_Jungle, LayerInfo_Mud, LayerInfo_Puddle and LayerInfo_Sand have no layer name stored in the asset. Layer-info assets made by a palette Build (below) or by Landscape mode carry the name.
Apply it to a MeshTerrain
A MeshTerrain takes its material from its Mesh Partition definition asset. Set the definition's Material to MI_AT_LayerStack_MeshTerrain, and make its Channel Map match what the instance reads. MPD_AT_Showcase does both. The definition's channels, in order, are:
| Index | Channel |
|---|---|
| 0 | Grass |
| 1 | ForestFloor |
| 2 | Dirt |
| 3 | Mud |
| 4 | Gravel |
| 5 | Rock |
MI_AT_LayerStack_MeshTerrain reads those channels with four MLB_AT_Channel blends, one per layer from layer 1 up. Each blend has a ChannelIndex parameter, the index into the definition's channel list:
| Layer | Blend | ChannelIndex | Channel in MPD_AT_Showcase |
|---|---|---|---|
1 Dirt (auto) | MLB_AT_Channel | 2 | Dirt |
2 Dirt | MLB_AT_Channel | 2 | Dirt |
3 Gravel | MLB_AT_Channel | 4 | Gravel |
4 Sand | MLB_AT_Channel | 3 | Mud |
5 Cliff | MLB_AT_Slope | none | none |
If your own definition orders its channels differently, change ChannelIndex on the instance. Paint-layer blends (MLB_AT_Paint_*) read Landscape layers, so use MLB_AT_Channel blends on a MeshTerrain.
The layer stack
Layer 0 is the base and has no blend. Each later layer has one blend. The three instances share one stack of layer assets: layers 0 to 4 are ML_AT_Surface and layer 5 is ML_AT_Cliff. They differ in blends and textures.
| Layer | Name | Landscape instance blend | MeshTerrain instance blend | Showcase instance blend |
|---|---|---|---|---|
| 0 | Grass | none (base) | none (base) | none (base) |
| 1 | Dirt (auto) | MLB_AT_NoiseBand | MLB_AT_Channel | MLB_AT_Paint_Desert |
| 2 | Dirt | MLB_AT_Paint_Dirt | MLB_AT_Channel | MLB_AT_Paint_BroadleafForest |
| 3 | Gravel | MLB_AT_Paint_Gravel | MLB_AT_Channel | MLB_AT_Paint_ConiferousForest |
| 4 | Sand | MLB_AT_Paint_Sand | MLB_AT_Channel | MLB_AT_Paint_Snow |
| 5 | Cliff | MLB_AT_Slope | MLB_AT_Slope | MLB_AT_Slope |
The layer names are the same in all three instances. In the Showcase instance they are only labels: the blends paint the biome names above.
Surface textures per layer:
| Layer | Landscape and MeshTerrain instances | Showcase instance |
|---|---|---|
| 0 | Gravel_And_Grass_On_Soil_rmrk1gp0 | Gravel_And_Grass_On_Soil_rmrk1gp0 |
| 1 | Dry_Soil_Ground_vl4jehag | Rippled_Sand_vlziefgfw |
| 2 | Dry_Soil_Ground_vl4jehag | Rocky_Forest_Floor_vl0gbbwo |
| 3 | Gravel_Ground_vl3iffhg | Forest_Floor_peioA0 |
| 4 | Rippled_Sand_vlziefgfw | Snow_Covered_Ground_siikchka |
| 5 | Layered_Rock_Cliff_tkieffufw | Layered_Rock_Cliff_tkieffufw |
Each set is a folder under Textures/Megascans/ with _B (base colour), _N (normal), _ORM and _H (height) textures at 4096 by 4096, named T_<id>_4K_B and so on. All three instances also set the global Grass_*, Dirt_*, Sand_* and Snow_* texture parameters, to Gravel_And_Grass_On_Soil_rmrk1gp0, Dry_Soil_Ground_vl4jehag, Rippled_Sand_vlziefgfw and Snow_Covered_Ground_siikchka. Those parameters feed the far overlay (see RVT and Nanite).
The instances override TileSize on a few layers: 400 on layers 0 and 3 and 800 on layer 5 in the Landscape and MeshTerrain instances, and 400 on layer 0 and 800 on layer 5 in the Showcase instance.
Blend rules
Every blend shares one height-based blend function, MF_AT_HeightLerp, so a layer's height map shapes the transition. These parameters sit on every blend:
| Parameter | Default | Effect |
|---|---|---|
HeightBlendIntensity | 1 | How strongly the height difference between the two layers shapes the edge. |
HeightContrast | 1 | Contrast applied to the height values. |
HeightBias | 0 | Shifts the height values. |
Smoothness | 1 | Divides the blend term: a larger value gives a softer edge. |
NormalZGuardIntensity, bUseNormalZGuard | 1 (intensity); the switch is off | With the switch on, reduces the blend weight on surfaces whose normal points away from up. |
The effects above are read from the blend function's own arithmetic. Each rule then adds its own parameters:
| Blend | Where its layer shows | Own parameters (defaults) |
|---|---|---|
MLB_AT_Paint_<Name> | Where the Landscape layer named <Name> is painted. | None beyond the shared set. |
MLB_AT_Channel | Where the Mesh Partition channel at ChannelIndex has weight. | ChannelIndex (0). |
MLB_AT_Slope | On slopes. Slope is one minus the Z of the vertex normal, with optional noise on the edge. | The MF_AT_SlopeMask parameters below. |
MLB_AT_NoiseBand | Where a world-space noise value falls in a band. It needs no painting. | NoiseTexture (T_AT_MacroNoise_RGBA_2k, red channel), MacroScale (50000), BandMin (0), BandMax (0.7), BandFalloff (0.1). |
MLB_AT_Altitude | Between two world heights. | MinHeight (9000), MaxHeight (100000), Falloff (500), all world Z. |
MLB_AT_NoiseBand samples the noise at world XY divided by MacroScale. MLB_AT_Altitude ships but no shipped instance uses it.
MF_AT_SlopeMask parameters: SlopeMul (1.5), SlopeContrast (5), Skirt_SlopeCenter (0.3), Skirt_BandWidth (0.15), Auto_bUseSlopeEdgeNoise (on), Auto_Slope_EdgeNoise_Scale (200), Auto_Slope_EdgeNoise_Intensity (0.15), Auto_Slope_EdgeNoise_OverallMult (0.95) and the noise texture Auto_Slope_EdgeNoise_Tex (T_AT_MacroNoise_2k).
Layer parameters
Per-layer parameters show up in the instance's Layer Parameters tab, one set per layer. ML_AT_Surface has these, in groups numbered 01 to 07:
| Group | Parameters (defaults) |
|---|---|
| 01 Textures | BaseColor, Normal, ORM, Height, VariantNoise (engine default diffuse and normal, T_AT_Neutral_Masks, T_AT_Neutral_Height, T_AT_MacroNoise_2k), and the texture-array parameters BaseColor_Array, Normal_Array, ORM_Array and Height_Array (no default texture) |
| 02 Look | TileSize (200, slider 10 to 2000), AOIntensity (1), Specular (0.5), RoughnessMin (0), RoughnessMax (1), ColorTint (white), bUseAntiTile (on), bUseAltitudeTint (on) |
| 03 AntiTile | CellSize (150), EdgeSharpness (1), HexCellSize (3), UVScrambleStrength (0.02), UVScrambleFrequency (8), MinScale (0.85), MaxScale (1.15), OffsetStrength (1), RotationStrength (1), bUseCellBomb (off), bUseUVScramble (on), bUseHexTile (off) |
| 04 Distance | FarTileMultiplier (5.7), FarNormalFlatten (0.85), bUseDistanceBlend (on) |
| 05 Macro | MacroStrength (0.5), MacroMinTint (grey 0.85), MacroMaxTint (grey 1.15), bUseMacroNormal (on) |
| 06 Displacement | Displacement (vector 1, 0, 1, 1), bUseDisplacement (on) |
| 07 Variant | Variant0_Min/Variant0_Max (0/0.5), Variant1_Min/Variant1_Max (0.5/1), Variant2_Min/Variant2_Max (1/1), VariantCellSize (2000), VariantBandFalloff (0.05), VariantAltitudeMin/VariantAltitudeMax (0/1000), VariantCurvatureScale (1), DriverIsNoise (on), DriverIsSlope, DriverIsAltitude, DriverIsCurvature (off), bUseTextureArray (off) |
TileSize is the world distance of one texture repeat: the layer samples its textures at world XY divided by TileSize.
ML_AT_Cliff has the same parameters and samples triplanar. In group 02 Look it adds Slope_TriSize (566.8), Slope_TriSharpness (8), Slope_NormalStrength (1), Slope_TriSize_Far (2400), Slope_DistFadeNear (800), Slope_DistFadeFar (6000) and bUseSlopeTriDistFade, and it has a DisplacementOffset (0).
The master carries shared signals with these defaults: Sample_FadeRange (4096), Sample_FadeFalloff (4), Macro_FadeRange (1500), Macro_FadeFalloff (4), Disp_FadeRange (4096), Disp_FadeFalloff (4), Macro_Normal_Intensity (0.333), Macro_Normal_Size (80), Macro_Scale (5000), Altitude_RangeMin (0) and Altitude_RangeMax (50000).
Bind a biome to a painted layer
A biome follows a Landscape paint layer through Landscape Layer Name (LandscapeLayerName). It is a name match: the layer's name must equal this value. See Biome assets for what reads it. Three links must line up:
- The biome's Landscape Layer Name equals the painted layer's name.
- The Landscape has that layer as a target layer, with a layer-info asset of that name.
- The material shows it: the instance's stack needs a blend that reads that name, such as
MLB_AT_Paint_<Name>. A layer can be painted, and visible to placement, without the material drawing it.
The shipped showcase biomes and the paint assets they find:
Biome Landscape Layer Name | Shipped MLB_AT_Paint_<Name> | Used by MI_AT_LayerStack_Showcase |
|---|---|---|
ConiferousForest | yes | yes |
BroadleafForest | yes | yes |
Desert | yes | yes |
Snow | yes | yes |
Beach | yes | no |
Landslide | none (MLB_AT_Paint_MS_Landslide is a different name) | no |
MountainForest | none (MLB_AT_Paint_MS_MountainForest and MLB_AT_Paint_Mountain are different names) | no |
MI_AT_LayerStack_Landscape paints only Dirt, Gravel and Sand, which none of the showcase biomes use. To show a biome's layer in the material you need an instance whose stack paints that name.
Tools
These tools work on a Landscape only, and are described with their refusals in Toolsets reference:
| Tool | Use |
|---|---|
AddLandscapeTargetLayer | Add a named target layer, with a layer-info asset, to every Landscape in the level. |
PaintBiomeLayers | From the resolved biome map, or from region bounds when there is none, create a layer per biome and write 0 and 255 weight masks. It names each layer after the biome, so a region for Biome_Desert gives layer Desert. It does not read Landscape Layer Name, so a biome whose value differs from that derived name is not found by the bake or by BuildBiomesFromPaint. |
PaintLayerRect | Paint one existing layer to a constant weight over a world rectangle. |
BuildBiomesFromPaint | The reverse of PaintBiomeLayers: compose a biome map from painted layers, using biomes that declare a Landscape Layer Name. |
PaintBiomeLayers makes layers real for placement. It does not make them visible in the material: that is the stack's job, described above.
On a MeshTerrain, Landscape weight layers cannot be read by this path. Bake Runtime Textures bakes no mask texture there, and each biome it tries to bake logs a no terrain under any of its texels warning. See Placement for the MeshTerrain routes.
Add your own paint layer
An AgenticTerrainPalette (UAgenticTerrainPalette) turns a list of names into the assets a paint layer needs. Duplicate a shipped palette (Palette_AT_Default or Palette_AT_Showcase) and set:
| Property | Default | Meaning |
|---|---|---|
Paint Layers (PaintLayers) | empty | Layer names. Names that are None are skipped. |
Layer Info Folder (LayerInfoFolder) | A project content folder | Where LayerInfo_<Name> is created. |
Blend Folder (BlendFolder) | A project content folder | Where MLB_AT_Paint_<Name> is created. |
Paint Blend Template (PaintBlendTemplate) | MLB_AT_Paint_Template | The blend that is duplicated. |
Press Build. For each name it creates LayerInfo_<Name> with the layer name set and the blend method set to Premultiplied Alpha Blending, and MLB_AT_Paint_<Name>, a copy of the template with its Landscape layer node renamed. It never overwrites: an existing blend asset is left as it was, and an existing layer-info asset is kept, with its blend method set to Premultiplied Alpha Blending if it differed. The report goes to the Output Log under LogAgenticBiomes, one line per name:
Dirt: LayerInfo created, Blend created
Gravel: LayerInfo exists (blend fixed), Blend exists
Moss: FAILED <reason>A failure on one name does not stop the others. The folders are in your project, not in the plugin.
Then:
- Add the layer to the Landscape as a target layer with the new layer-info asset.
- In the instance's Layer Parameters tab, set one layer's blend to
MLB_AT_Paint_<Name>. - Set the biome's Landscape Layer Name to
<Name>.
Do not replace a whole instance stack from script to change blends. An instance whose layers are all unlinked from the parent is merged with the parent's layers rather than replacing them. Keep the instance linked to its parent and override only the blends.
Scripting helpers
The editor module exposes UAgenticTerrainStackLibrary, an editor-only Blueprint function library (category AgenticTerrain) for editing an instance's layer stack, because the stack is not otherwise reachable from scripts: SetInstanceBlends, SetInstanceStack, GetInstanceStack, SetInstanceLayerScalar, SetInstanceLayerVector, SetInstanceLayerTexture, SetInstanceLayerStaticSwitch, DuplicateFunctionAs and ClearLandscapePaintLayer. SetInstanceBlends keeps the parent's layers and replaces only the blends: its Blends array must match the resolved stack's blend count, and a null entry keeps the parent's blend.
Runtime Virtual Texture and Nanite displacement
The master supports both. Neither is on in the shipped instances: all three set bUseNanite and bEnableRVT to off. The plugin does not turn either on for you.
| Switch (on the master) | Group | Master default | Shipped instances |
|---|---|---|---|
bUseNanite | Nanite Tessellation | off | off |
bEnableRVT | RVT | off | off |
bUseLiveCliffOverlay | RVT | on | on in the Landscape instance; not overridden in the others |
bUseLiveDistanceOverlay | RVT | on | off |
bUseLiveCliffOverlay is not connected to anything in the master graph, so changing it has no effect.
The master is flagged Used with Nanite, with tessellation and displacement fade enabled and a displacement magnitude of 0.5. From the graph wiring: bEnableRVT is the last switch before the material output. With it off, the output does not pass through MF_AT_RVTReader or the four far overlays, and bUseNanite chooses whether the displacement output is set. With it on, the output passes through the RVT reader and, when bUseLiveDistanceOverlay is on, the far overlays for Grass, Dirt, Sand and Snow, and the Nanite displacement branch is not in that path. This is read from the graph; the plugin does not ship a level that runs it.
The master always has a Runtime Virtual Texture Output node, so a terrain using it can write into runtime virtual textures whatever the switches say. MF_AT_RVTReader reads RVT_AT_Material (type BaseColor_Normal_Specular) and RVT_AT_Displacement (type Displacement). The shipped RVT assets:
| Asset | Content type | Tile Count | Tile Size |
|---|---|---|---|
RVT_AT_Material | Base colour, normal, specular | 12 | 3 |
RVT_AT_Displacement | Displacement | 12 | 3 |
RVT_AT_Height | World height | 9 | 3 |
RVT_AT_Mask4 | Mask4 | 9 | 1 (border 1) |
Tile Count and Tile Size are the stored TileCount and TileSize properties, which the Details panel labels Size of the virtual texture in tiles and Size of each virtual texture tile. Both are powers of two: TileCount 12 is 4096 tiles, and TileSize 3 is 512 texels per tile.
What you must set up yourself, because nothing in the plugin does:
- Unreal's virtual texture support (
r.VirtualTextures, on by default in UE 5.8) and the RVT material types you use. - A Runtime Virtual Texture Volume in the level for each virtual texture, with its Virtual Texture property set. A volume holds one virtual texture.
- For a Landscape, the virtual textures in the Landscape's Draw in Virtual Textures list.
- For Nanite displacement: Nanite on the Landscape (Enable Nanite),
r.Nanite.Tessellationleft at its default of1, andbUseNaniteon the instance. Whether it works end to end on your terrain was not tested.
Limits
- The plugin never assigns the material, creates RVT volumes, enables Nanite or checks those settings.
- Shipped instances leave RVT and Nanite displacement off.
MI_AT_LayerStack_Landscapepaints onlyDirt,GravelandSand. No shipped paint blend exists forLandslideorMountainForest.- Paint-layer tools and Landscape Layer Name work on a Landscape only. A MeshTerrain uses Mesh Partition channels.
- The layer-stack material is not editable by the agent toolsets; use the Details panel or the scripting helpers above.
- The Megascans textures are 4096 by 4096. No performance figures are claimed for the stack.
For problems, see Troubleshooting. To ask a question, join the Discord.
Next steps
- Biome assets, for Landscape Layer Name and where it is read.
- Placement, for how regions place biomes on each backend.
- Wind bridge, for foliage wind.
Runtime ground cover
How a biome attaches GPU-scattered grass and small plants that generate around the camera at runtime, which generators and templates ship, and what each parameter does.
Agent toolsets
Drive Agentic Biomes from an MCP client with 28 editor tools, following a safe inspect, edit, validate, place, build and verify loop