Gessa Docs
Recipes

Recipe

Terrain and heightmaps procedural generation biomes and foliage

You want a natural outdoor world: rolling hills, mountains, rivers, biomes, and scattered vegetation. Engine terrain is a HEIGHTFIELD (a height per x,z sample), not a voxel volume. Every natural landscape verb is a terrain semantic patch op. For binding an existing terrain resource and basic sculpting, start at terrain-basics; this playbook covers the procedural and ecological ops.
engine v1.0.234since component-field-contract ecs.component-field-contract.v1, action-catalog.v1.0.232, terrain, rendering.v1Copy for LLM

Use this for

procedural outdoor landscapes; hills valleys and coastlines; biome regions; rivers and paths; whole-terrain erosion smoothing; scattering instanced grass and rocks (tree foliageKeys render as placeholder cones today, see pitfalls); large natural worlds the player explores

Not for

blocky mineable voxel worlds (not authorable, see capability-ceiling); flat indoor floors (use a box ColliderComponent plus RenderableComponent); importing a raw greyscale heightmap image (no AI patch op binds heightmapAssetId)

Pairs with: Terrain basics ground heightmap and sculpting, Atmosphere sky lighting fog and time of day, NPC and enemy navigation chase and patrol behavior

The recipe

  1. Create a terrain resource asset: project_create_asset (project.asset.create) with assetKind: "terrain". This backs the binding; the dedicated terrain.resource.create action is planned with no AI tool yet.
  2. Shape the land with project_apply_terrain_semantic_patch (terrain.patch.apply). Emit a batch of ops (up to 1024) drawn from the nine:
    • create seeds base extent: width, depth, maxHeight, optional seed, optional biome.
    • generate reshapes the heights of a rectangle with a registered recipe: regionKind one of heightfield_tile/mesh_chunk/procedural_recipe, min/max (Vec2), recipe, optional seed. The schema lists recipe as optional, but the write seam refuses a generate without one and refuses a recipe name outside the terrain vocabulary registry (TERRAIN_RECIPE_IDS in packages/content-schema/src/terrainVocabulary.ts); the refusal lists the accepted ids and the landscape-and-terrain skill names them. You choose the recipe and seed; you cannot supply raw octave/frequency numbers.
    • sculpt (mode raise/lower/flatten/smooth at x,z with radius, strength) for hand shaping.
    • biome repaints the texture layers of a rectangle with a registered biomeKey from TERRAIN_BIOME_IDS (an unregistered name is refused with the accepted ids); paint (layerIndex 0..15) paints a texture layer.
    • road lays a road/path/river polyline (points, width, river depth).
    • erosion (min/max, iterations, strength) runs ONE deterministic thermal-style smoothing pass over the WHOLE terrain: it softens peaks and fills pits everywhere. The compiler reads only iterations and strength; mode (hydraulic/thermal), the min/max region and seed are accepted and not read, so it neither stays inside its rectangle nor carves water valleys.
    • scatter paints INSTANCED foliage (foliageKey, density per 100 sq units, min/max, scale jitter) over its whole rectangle. The schema accepts a layerIndex gate, but neither foliage producer reads it, so scatter is not confined to a painted layer. This is the one real instanced-content path in terrain: it bakes a foliage_instance_buffer artifact the renderer instances, so thousands of grass blades cost one batch, not one entity each. Real geometry exists only for the grass/tall_grass and rock/boulder keys; any other foliageKey, tree and flower keys included, renders as a small placeholder cone today.
    • cutHole removes a disc of surface (for a cave mouth or pit). It is a SURFACE hole, not a volumetric carve.
  3. Build artifacts: terrain_build_artifacts (terrain.artifacts.build) turns the ops into the runtime meshes and collision (height_tile, mesh_chunk, collision_proxy, nav_source_mesh, foliage_instance_buffer).
  4. Bind: attach TerrainComponent { "type":"TerrainComponent", "enabled": true, "terrainAssetId": <asset id> }.
  5. Sample from scripts with rendering.v1: ctx.terrain.heightAt, ctx.terrain.normalAt, ctx.terrain.slopeAt, ctx.terrain.raycastDown, ctx.terrain.sampleLayerWeights (place props on the surface, gate steep slopes).

The voxel nuance (read before promising minecraft)

A blocky, mineable, chunk-rendered voxel world is NOT authorable through terrain capabilities. The one path to a voxel world is instantiating the whole template at PROJECT CREATION: project_create_game with request.creationIntent { "mode":"template", "templateId":"voxel_frontier" } (voxel_frontier is in CREATION_TEMPLATE_IDS). That template is scripts plus runtime-spawned block entities, not terrain; you cannot edit its voxels as typed capabilities afterward, and per-block entities breach the 1000-collider runtime cap at real scale. If the user asks for minecraft, offer procedural HEIGHTFIELD terrain plus mobs, inventory, and day-night (all expressible) and name the voxel limit honestly rather than attempting per-block authoring.

Pitfalls

  • Terrain is a heightfield: there is no per-voxel set/place op and no volumetric cave carve. cutHole is a surface disc only.
  • generate noise is recipe-plus-seed, not raw parameters. Name a registered recipe; a generate without one, or with an unregistered name, is refused.
  • erosion ignores its mode and its region: hydraulic and thermal both run the same smoothing pass over the whole terrain. Expect softened, settled terrain everywhere, not carved drainage; carve valleys with sculpt or a river road op instead.
  • scatter is a grass-and-rocks tool today: only grass/tall_grass and rock/boulder render real geometry, and a scattered tree foliageKey renders as a placeholder cone. Place trees as model entities (mind the runtime entity cap) and reserve scatter for ground cover.
  • Importing a greyscale heightmap image is not an AI patch op; heightmapAssetId is a facet field with no AI verb that sets it. Author the field with generate/sculpt/erosion instead.
  • Artifacts are stale until rebuilt: after any patch batch, run terrain_build_artifacts before expecting the change to render or collide.
  • Nav agents need collision_proxy/nav_source_mesh built to path over terrain.

Verify

  • project_get_graph_snapshot to confirm the terrain asset carries the ops and the entity carries the TerrainComponent binding.
  • qa_capture_renderer_viewport to confirm the landscape, biomes, and foliage read with elevation.
Was this helpful?Report an issueContact support

On this page