Skip to content
Brass Orrery

Version 1.0, coming to Fab. Orrery Terrain 1.0 is not on sale yet; this guide can still change before it is.

GuideOrrery Terrain 1.0Tutorials

8. Tutorial: grassExperimental

The terrain can grow grass, flowers and stones on its paint layers. The grass follows edits: raise a hill and it grows on the new ground, paint a layer and the plants appear.

The grass system is Experimental: its C++ class (FTerrainGrassSystem) and the Is Grass Idle query can change in a minor version, and the changelog lists each change (versioning). The grass entries of a layer set and the terrain actor’s Layer Set property are stable API. It is a client feature: a dedicated server never loads it.

  • You describe grass with the engine’s own Landscape Grass Type assets. Each grass type holds one or more varieties (mesh, density, scale, cull distance).
  • You attach grass types to the entries of a Terrain Layer Set. A paint layer with a grass entry grows that grass.
  • The layer’s weight at a point is the painted weight plus the material’s automatic rules. So grass grows on the auto-grass ground of the default material without anyone painting it.
  • The grass is drawn as instanced meshes around each viewer. Distant sub-cells are released.
  1. In the Content Browser, Add > Landscape > Landscape Grass Type (or search “Grass Type”). Name it GT_Meadow.
  2. Open it. Under Grass Varieties add an element. Set Grass Mesh to a static mesh (a small blade or flower mesh), and a Grass Density and End Cull Distance. These are the engine’s own settings.
  3. The terrain honours density, scaling, rotation, alignment to slope, jitter, and the cull distances of each variety. It ignores the lightmap setting, because the terrain has no lightmap.

A grass type needs no Landscape actor. The asset factory and the variety struct are enough. This is the shape our sample maps use.

import unreal
tools = unreal.AssetToolsHelpers.get_asset_tools()
mesh = unreal.load_asset("/Game/Grass/SM_Tuft") # your static mesh
gt = tools.create_asset("GT_Meadow", "/Game/Grass", unreal.LandscapeGrassType, unreal.LandscapeGrassTypeFactory())
v = unreal.GrassVariety()
v.set_editor_property("grass_mesh", mesh)
v.set_editor_property("grass_density", unreal.PerPlatformFloat(default=90.0))
v.set_editor_property("start_cull_distance", unreal.PerPlatformInt(default=2000))
v.set_editor_property("end_cull_distance", unreal.PerPlatformInt(default=4000))
v.set_editor_property("scaling", unreal.GrassScaling.UNIFORM)
v.set_editor_property("scale_x", unreal.FloatInterval(min=0.7, max=1.4))
v.set_editor_property("random_rotation", True)
v.set_editor_property("align_to_surface", True)
gt.set_editor_property("grass_varieties", [v])
g = unreal.OrreryTerrainLayerGrass() # FOrreryTerrainLayerGrass
g.set_editor_property("grass_type", gt)
g.set_editor_property("slope_range_deg", unreal.Vector2D(0.0, 32.0))
entry = unreal.OrreryTerrainLayerEntry()
entry.set_editor_property("name", "Grass")
entry.set_editor_property("grass", [g])
f = unreal.DataAssetFactory()
f.set_editor_property("data_asset_class", unreal.OrreryTerrainLayerSet)
ls = tools.create_asset("LS_Terrain", "/Game/Terrain", unreal.OrreryTerrainLayerSet, f)
ls.set_editor_property("layers", [entry])
# terrain_actor.set_editor_property("layer_set", ls)

Save the assets (unreal.EditorAssetLibrary.save_loaded_asset). In an engine build where unreal.LandscapeGrassTypeFactory is not exposed to Python, the sample falls back to a DataAssetFactory with data_asset_class set to LandscapeGrassType.

  1. Open LS_Terrain (see Materials and layers).
  2. On the entry for the layer where the grass should grow, add one element under Grass.
  3. Set Grass Type to GT_Meadow.
  4. Optional rules per grass entry:
Setting Meaning
Density Scale Multiplies every variety’s density. 0 turns the entry off.
Slope Range (deg), Slope Fade Grows only between these slopes.
Altitude Range (m), Altitude Fade Grows only between these heights.
Ground Class Mask Grows only on these ground classes.
Keep Clear Margin (m) Stays clear of rail, road and canal footprints by this much.
On Plates Also grows on plates (pads). Off by default.

Select the terrain actor and set its Layer Set (Terrain category) to your layer set. The terrain is re-created with it, in the editor and when play starts, and the grass grows. Clearing the property clears the grass. Undo and redo work like any other property.

Quick test: a console command (development builds)

Section titled “Quick test: a console command (development builds)”
  1. Press Play.
  2. Open the console (`) and type:
Orrery.Terrain.Grass.LayerSet /Game/Path/LS_Terrain.LS_Terrain
  1. The log (LogOrreryTerrain) prints Orrery.Terrain.Grass.LayerSet: <path>. Typing the command with no argument clears the grass.

The command does not exist in Shipping builds.

From C++: switch the layer set at run time

Section titled “From C++: switch the layer set at run time”

A game that changes the grass while it runs (a season, a biome) can set it on the grass system directly. Add OrreryTerrainRenderer and OrreryTerrainCore to your module’s Build.cs. Then, once the terrain exists:

#include "OrreryTerrainRenderSubsystem.h"
#include "Grass/TerrainGrassSystem.h"
void AMyGameMode::SetGrass(UOrreryTerrainLayerSet* LayerSet)
{
if (UOrreryTerrainRenderSubsystem* Render = GetWorld()->GetSubsystem<UOrreryTerrainRenderSubsystem>())
{
if (FTerrainGrassSystem* Grass = Render->GetGrassSystem())
{
Grass->SetLayerSet(LayerSet);
}
}
}

A layer set given this way stays until the terrain is re-created with one of its own: an actor that names no layer set leaves it alone. GetGrassSystem() is null where grass never runs: a dedicated server, a commandlet, or -nullrhi. Check it. The header marks it internal, not part of the stable API, so it can change between versions.

Console variable Effect
r.OrreryTerrain.Grass 1 grows grass, 0 releases all grass.
r.OrreryTerrain.Grass.DensityScale 0 to 1 multiplier on all density.
r.OrreryTerrain.Grass.CullDistanceScale Scales every cull distance. A change rebuilds the grass.
r.OrreryTerrain.Grass.MaxTasks How many build tasks run at once (1 to 64).
r.OrreryTerrain.Grass.AcceptBudgetMs Game-thread time per tick for showing finished grass, in milliseconds.
r.OrreryTerrain.Grass.DuringStroke 1 refreshes grass as the stroke’s ground lands: a brush stroke reaches the grass four times a second, a road or other spline edit every tick. 0 waits until the stroke ends.
r.OrreryTerrain.Grass.Debug 1 shows grass counts on screen (not in Shipping).

When an edit changes the ground under grass that is already built (a brush stroke, a road laid live, painting a layer), the grass of each edited chunk is rebuilt on its own and shown whole: every grass variety and every changed patch of that chunk appears in the same tick, no earlier than the tick after the ground changed. The grass edge therefore follows the painted edge, not a patch grid.

Two project settings, under Project Settings, Plugins, Orrery Terrain, Grass, limit this rebuild:

Setting Default Effect
Grass Edit Budget Ms 4 Game-thread time per tick for showing edited chunks’ grass. One chunk always lands per tick, so 0 means one chunk a tick.
Grass Edit Chunks 8 Edited chunks whose grass may be rebuilding at once (1 to 64).

The cost: a long edit that re-commits every tick, such as a road built live, rebuilds all varieties of the chunks it crosses each time. In one measured road-building clip that raised the grass share of the game thread to about 1.3 ms in the busiest 1 % of ticks (up to 2.8 ms), and the worker threads’ grass time over the clip from 0.8 s to 17.5 s. A game that builds roads live and needs the tick shorter can lower the budget or the chunk cap: the edit then lands over more ticks, the nearest chunks first, and each chunk’s grass still lands whole. Brush strokes cost far less, because their ground reaches the grass only four times a second.

The engine’s grass.densityScale and sg.FoliageQuality also apply. Changing sg.FoliageQuality rebuilds the grass.

  1. Play with a layer set whose layer 0 has a grass entry. Grass appears on flat ground near you and not on steep rock, if you also set a slope range.
  2. Sculpt a hill with the brush tool. Grass grows on the new ground a moment after the stroke (or about four times a second while you hold, with DuringStroke at 1).
  3. Run r.OrreryTerrain.Grass 0. The grass vanishes. Run r.OrreryTerrain.Grass 1. It comes back.

Placed foliage that follows terrain edits is a separate system. See Foliage that follows edits, and PCG.

Plugin 1.0 · Unreal Engine 5.8Found a mistake or something unclear? E-mail us