GETerrainRenderer
Builds one cube per non-air block from a World, split into static and animated meshes.
Overview
Builds its meshes from a Worlds::World with cna-engine's
MeshBuilder and keeps them as
std::unique_ptr<CnaEngine::Graphics::GpuMesh> members: a static mesh for
non-animated non-air blocks (built once), and an animated-subset mesh rebuilt whenever
Update(device, animPhase) sees a new phase. Until 2026-10-20 these were
Easy3D::CubeMeshRenderer instances.
Sources: src/GalaxyEggbertCNA/Game/GETerrainRenderer.hpp and .cpp.
Construction
Triple-nested loop over the world's full blocksPerAxis() cube. For every non-air
block:
worldX = x - kWorldCenterX; worldZ = z - kWorldCenterZ; // Y unshifted
if (BlockTypes::tileAnimBase(block.type()) is animated)
m_animBlocks.push_back({worldX, y, worldZ, baseIcon});
else
AddCenteredBox(mesh, center=(worldX, y, worldZ), size=(1, 1, 1), faces); // MeshBuilder::AddBox
// faces: one BoxFaceStyle{visible, uv} per BoxFace, uv = tileAtlas.GetTileUv(block.type())
Also accumulates centroid sums (exposed as CentroidX/Y/Z()) — used as a camera
look-at target since the spawn tile may be open air.
The builder holds the vertex/index data directly (24 vertices + 36 indices per full cube — the same
data easy-3d's BuildCubeMesh produced); UploadTexturedMesh(device, mesh)
uploads it once to CNA VertexBuffer/IndexBuffer as a
GpuMesh. Special shapes use the same builder:
AddPlate, AddCrossedPlates (3 planes), and the game's own
AddPyramidTip() for the teleporter's tip.
Animated Tile Groups (ported from mobile-eggbert Tables.cpp)
| Tile | Frames |
|---|---|
| Lava | 8 |
| Spike | 16 |
| Crusher | 10 |
| Saw | 6 |
| Water1 / Water2 | 6 each |
| Temp | 20 (2 invisible frames) |
| Fan ×4 directions | 3 each |
| Marine | 11 |
AnimIcon(base, phase) looks up the current icon; IsAnimated(base) gates classification.
Update(device, animPhase)
No-op if the phase hasn't changed. Otherwise rebuilds the animated mesh from the current frame's icons. If the phase yields zero visible animated cubes (e.g. Temp's blank frames), the animated renderer is reset to null rather than uploading empty data.
Draw
staticRenderer->Draw(device, effect); // GpuMesh::Draw(device, effect)
if (animatedRenderer) animatedRenderer->Draw(device, effect);
Known Limitations
- No face culling or greedy meshing — every non-air block gets a full cube.
- No chunk-radius streaming — the entire world is uploaded up front.
Dependencies
cna-engine (MeshBuilder, GpuMesh, UvRect, via src/GalaxyEggbert/Game/TexturedMesh.hpp), GETileAtlas, Worlds::World, BlockTypes. Until 2026-10-20: easy-3d (CubeBatch, CubeMesh, CubeMeshRenderer).