Files
EVEngine/docs/dev/hex-terrain-capability-audit.md
T

6.6 KiB

Hex terrain capability audit

Result

EVEngine can generate and render a medium-size Civilization-style hex terrain prototype today. The procgen registry, deterministic parameters, CPU mesh upload, custom Mesh3D shaders, camera, lighting and shadow systems are sufficient for the included examples/hex-terrain proof. mesh.hexterrain adds the missing world generator and produces ocean, deep ocean, rivers, grassland, mountains, hills, coast, cliffs, forest, swamp, rainforest and ice without source art.

It is not yet a production-scale terrain renderer. The proof deliberately uses a documented UV packing bridge because the current graphics API has no terrain material-layer abstraction.

Requirement audit

  • Basic Civilization-style hex terrain: implemented and rendered by the mesh.hexterrain recipe and examples/hex-terrain.
  • Required terrain classes: deep ocean, ocean, coast, grassland, hills, mountains, forest, swamp, rainforest, ice, cliffs and rivers are all present in the acceptance seed and checked through generated metadata.
  • No supplied art: satisfied; geometry, vegetation, albedo, normals, roughness, metallic response, AO-like detail, snow, foam, caustics and weather motion are generated by C++ or GLSL.
  • Material transitions: implemented as world-space noisy two-biome blending, slope-aware cliff selection, dedicated shore bands and connected river edge geometry.
  • Engine API review and completion: this proof added boolean procgen parameters, a script-safe two-stage Mesh3D shader constructor, the missing cloud-shadow script binding, recipe schema/metadata and repeatable visual capture tooling.
  • Reference-based evaluation: three downloaded Civilization V screenshots and their source pages are stored under docs/dev/reference/civ5-terrain and are compared against the current acceptance frame below.

Rendered acceptance frame

examples/hex-terrain/hex-terrain-vulkan-latest.png is captured from the current binary on Mesa Lavapipe through an isolated 800x600 Xvfb display. Unlike earlier desktop screenshots, it is reproducible and corresponds to the checked source. The acceptance seed contains all requested terrain classes; exact per-class counts are exposed in mesh metadata and asserted by the procgen test.

Compared with the downloaded Civilization V references, the proof now matches the broad readability targets: layered deep/shallow sea, a coast ring, connected rivers, forest density changes, independent snow mountains, biome color zones, cliffs, atmospheric falloff and directional lighting. Remaining visual gaps are the still-visible (but material-smoothed) hex elevation steps, angular river centerlines, simplified peak silhouettes, lack of transparent/refraction water and the absence of production props such as resources, roads, farms and settlements.

Existing API coverage

  • TerrainSampler / Heightmap: continuous deterministic elevation and editable scalar fields.
  • Grid2D, semantic generation and hex tile systems: gameplay topology, pathfinding, FOV and cell costs.
  • MeshBuild and generateMesh: CPU procedural geometry and Vulkan upload. Squirrel callers consume the canonical Result projection (ok then value).
  • PBR recipes: aligned albedo, normal, roughness, metallic, height and AO image generation from a shared displacement field.
  • Mesh3D custom shaders: world position, normal, UV, camera and frame lighting.
  • Tree, bush and rock mesh recipes, plus the proof's deterministic broadleaf, conifer and peak geometry: enough to validate biome decoration and wind sway.
  • Per-frame cloud-shadow controls and two-stage custom Mesh3D shaders: enough for animated water normals, shoreline foam, vegetation wind and moving cloud shade.

Gaps exposed by the proof

  1. MeshBuild carries only position, normal and one UV channel. Terrain needs an integer cell/biome id and typically four to eight normalized layer weights.
  2. Material binds albedo, normal and height, but generated roughness, metallic and AO maps cannot be attached independently. Its scalar roughness/metallic values apply to the whole draw.
  3. Custom Mesh3D shaders inherit a fixed descriptor layout. There is no public texture-array or arbitrary named texture binding API, so a shader cannot sample a biome PBR array plus a cell-data texture.
  4. PbrTextureSet is CPU-only. There is no one-call upload/ownership object that converts the six generated images into a renderable material.
  5. There is no chunked terrain resource, dirty-region mesh update, terrain LOD, seam stitching, asynchronous generation or cell-to-world picking API tied to 3D hex geometry.
  6. Rivers are generated as drainage paths, but production hydrology still needs basin filling, flow accumulation, confluences, lake outlets and navigable river graph queries.
  7. Forest, rainforest and swamp decorations are currently baked into one mesh. Production terrain needs instance buffers, chunk culling, indirect drawing and independent vegetation regeneration instead of rebuilding the terrain mesh.

Introduce TerrainLayerSet (texture arrays for albedo/normal/ORM/height), TerrainCellBuffer (biome ids, weights, elevation, moisture, temperature and river edges), and HexTerrain (chunk generation, picking, neighbour/edge queries, LOD and dirty updates). Extend MeshBuild with optional typed custom streams and extend custom materials with reflected texture/buffer bindings. Preserve the existing compact vertex path for ordinary meshes.

The recommended shader path samples triplanar texture arrays, blends height-aware weights, normalizes reoriented normals, packs AO/roughness/metallic as ORM and uses the existing GGX lighting. Coast foam and river wetness should be edge masks in cell data rather than separate meshes; cliffs should select a world-space triplanar rock layer based on slope.

The proof now implements the visual approximation entirely from shader math: height-aware noisy biome blending, procedural normals, GGX lighting, animated water, coast foam, snow-line breakup, distance fog, moving cloud shade and vertex wind. It validates shader programmability, but is not a replacement for bindable PBR texture arrays or terrain data buffers.

Current proof encoding

UV.x = primaryBiome + transitionWeight and UV.y = secondaryBiome + riverCoverage. The fragment shader reconstructs two procedural PBR surfaces, perturbs the boundary with world-space FBM and performs a GGX metallic-roughness lighting evaluation. This gives stable, asset-free transitions and validates the end-to-end path, but it supports only two layers per vertex and should not become the permanent terrain data format.