TerrainRenderer
Turns a loaded world into terrain meshes, keeps the animated ones current, builds reliefs near the camera and draws them in the right passes.
| Header | src/GalaxyEggbert/Game/TerrainRenderer.hpp |
|---|---|
| Source | src/GalaxyEggbert/Game/TerrainRenderer.cpp |
| Namespace | GalaxyEggbert::Game |
| Former name | GETerrainRenderer (the GE prefixes were dropped from game components) |
Purpose
One TerrainRenderer is built per loaded world (and rebuilt when the world changes in a way it cannot patch).
The constructor walks every cell of the World and sorts it into a mesh by what its
block definition and its tile's texels call for: the static opaque mesh,
the cut-out mesh (alpha-tested sprites), the static blended mesh (faint tiles), the grass-top overlay, the animated
and water cells (rebuilt when the animation phase changes), and the lava, lightning and wind cells (each frame of their
cycle built once and cached). Cells drawn as 3D models are left out and listed by ModelCells().
The meshes are uploaded as GpuMeshParts, split when one buffer would be too big (MeshParts.hpp).
The class holds GPU resources and needs a GraphicsDevice, so it is not unit-tested itself; the pure parts it
relies on are (see Tests).
Collaborators
- World (input) and BlockDefinitionRegistry (render mode, texture icon, animation, water).
- TileAtlas (UVs on the padded terrain sheet) and
TileCoverage(opaque, cut-out or translucent per icon). - Geometry helpers:
DirectionalCubeTiles,InnerPillarBoxTiles,ThinBarTiles,InnerFlatPlateTiles,BrickEdges,WaterSurface,LavaSurface,BoltMesh,TerrainModelCells. TerrainReliefTable(TerrainRelief.hpp) for the reliefs;TerrainGiSettings(TerrainGi.hpp) for Ultra's baked light.- WorldRenderer draws it each frame and passes the effects' shaders as a
TerrainShading. - GalaxyEggbertGame creates it, calls
Update()each tick and patches or rebuilds it inRefreshTerrainMeshIfChanged().
Public API
| Member | What it does |
|---|---|
TerrainRenderer(device, world, tileAtlas, tileCoverage = {}, drawSecret = false, modelDrawnIcons = {}, reliefs = nullptr, ambientOcclusion = false, globalIllumination = nullptr) | Builds every mesh. drawSecret draws the flight barrier 214 (ShowSecret); modelDrawnIcons are still icons the caller draws as models; reliefs must outlive the renderer; the last two bake ambient occlusion or Ultra's GI into the vertex colour. |
AmbientOcclusion(), GlobalIllumination() | Whether the meshes were built with those bakes. |
Update(device, animPhase, blupiFocused = true) | Rebuilds the animated and water meshes and picks the lava, lightning and wind frame for the phase; a no-op when the phase is unchanged. |
UpdateRelief(device, focus, radius, maxBuilds = 2) | Builds reliefs for the chunks within radius of focus (nearest first, on worker threads) and releases far ones. |
AdoptReliefs(previous) | Takes over a previous renderer's reliefs for chunks whose cells did not change. |
SetReliefOverhangs(keep) | Keep reliefs' overhangs into the air (for comparison). |
ReliefChunkCount(), ReliefChunksShown(), ReliefPrimitiveCount() | Relief statistics. |
kReliefChunkCells = 8, kReliefRelease = 4.0, kReliefBuildsInFlight = 2 | Chunk edge in cells, release distance beyond the radius, worker builds at a time. |
DrawOpaque(device, effect, shading = nullptr) | Lava, the static mesh, reliefs or far cubes, the cut-outs; writes depth. |
DrawTransparent(device, effect, shading = nullptr) | Blended static tiles, animated tiles, lightning, wind and water; depth read only. Must come after everything opaque. |
Draw(device, effect) | Both halves in a row, for a caller with nothing to draw in between. |
DrawGrass(device, grassEffect) | The icon-107 grass tops with their own texture and effect. |
DrawShadowCasters(device, solid, cutout), DrawGeometryPass(device, colored, plain, cutout), DrawWater(device, effect) | The effects' shadow map, Ultra's geometry pass and its water mirror. |
LavaCells(), BlitzCells(), BlitzShown() | Where lava and lightning are, and whether the lightning strikes now (point lights, the zap sound). |
SetModelDrawnBases(bases), SetSawModelBases(bases) | Animated tiles the caller draws as models (fans, saws); the next Update() leaves them out. |
ModelCells(), PatchModelCells(world, changes, icons, drawSecret, coverage) | The cells drawn as models; apply journal changes that only touch them (false: rebuild instead). |
BlockCount(), RaggedBrickCount(), AnimatedBlockCount(), WaterBlockCount(), VertexCount(), PrimitiveCount() | Statistics printed at load. |
CentroidX(), CentroidY(), CentroidZ() | Centre of all non-air blocks, a fallback camera target. |
TerrainShading (same header) carries the effects' replacement shaders: occluded, lit, cutout, lava, water and Ultra's reliefGi callback; a null member keeps the stock effect.
How it is used
Built at load (src/GalaxyEggbertCNA/GalaxyEggbertGameLoad.cpp):
terrainRenderer_ = std::make_unique<TerrainRenderer>(device, worldRuntime_.GetWorld(), terrainTileAtlas_, tileCoverage_,
terrainDrawSecret_, StillModelIcons(), TerrainReliefs(),
TerrainOcclusionWanted(), TerrainGiWanted());
...
terrainRevision_ = worldRuntime_.GetWorld().blockRevision();
After a tick changed blocks, the cheap path is tried first:
const auto changes = world.blockChangesSince(terrainRevision_);
if (TerrainDrawSecret() == terrainDrawSecret_ &&
terrainRenderer_->PatchModelCells(world, changes, StillModelIcons(), terrainDrawSecret_, tileCoverage_))
{
terrainRevision_ = world.blockRevision();
return;
}
... // otherwise build a new TerrainRenderer and call rebuilt->AdoptReliefs(*terrainRenderer_)
- Draw order matters:
DrawOpaque()before the models,DrawTransparent()after them, or water does not cover what lies in it (OR-0052). - Reliefs are off unless the Setup's "3D terrain" is on (or
GALAXY_TERRAIN_RELIEF=1); withoutUpdateRelief()every chunk stays textured cubes. - Brick edge pieces 262, 263 and 283 are drawn ragged only at a wall's open end (
AppendRaggedBrick); the count isRaggedBrickCount().
Tests
VerifyTileFaces(tools/VerifyTileFaces.cpp): directional-cube faces, water exterior faces, the padded atlas, and that incremental model-cell patching (PatchTerrainModelCells, behindPatchModelCells) equals a full scan.VerifyTileCoverage: tile classification ofobject-m.pngand when a brick edge piece is ragged (BrickEdges::ShapeFor).VerifyTerrainLighting,VerifyTerrainAnimDivisor: face brightness and the per-type animation pace.VerifySceneEffects,VerifyUltra: the occlusion and GI bakes' rules.- The drawn result: golden frames (
tools/verify_golden_frames.sh) andtools/verify_lava_depth.py, manual, with a display.
Related pages
- Rendering — where the terrain sits in the frame.
- WorldRenderer, TileAtlas, World, BlockDefinitionRegistry.