Rendering
One cube per non-air block, drawn via Easy3D's CubeMeshRenderer through CNA.
Overview
GalaxyEggbertCNA renders terrain only — there is no Blupi sprite, no object
rendering, no HUD, no sky dome. Terrain rendering works end-to-end and is verified: a real run
on worlds3d/world001.vwr uploaded 2749 blocks (pre-bugfix count) as 65976 vertices
/ 32988 triangles, and a 5×5 on-screen pixel sample found 21 of 25 sampled points showing real
terrain color across 10 distinct colors — confirming genuine texture sampling, not a placeholder.
Pipeline
GETerrainRenderer(world, tileAtlas):
for each (x, y, z) in world.blocksPerAxis()³:
block = world.getBlock(x, y, z)
if block.isAir(): continue
worldX = x - kWorldCenterX; worldZ = z - kWorldCenterZ // Y unshifted
if BlockTypes::tileAnimBase(block.type()) is animated:
store in m_animBlocks (rebuilt on anim-phase change)
else:
add a 1×1×1 cube at (worldX, y, worldZ) to a CubeBatch,
UV from tileAtlas.GetTileUv(block.type())
staticMesh = Easy3D::BuildCubeMesh(staticBatch) // 24 verts + 36 indices per cube
staticRenderer = Easy3D::CubeMeshRenderer(device, staticMesh)
animatedRenderer = Easy3D::CubeMeshRenderer(device, animatedMeshForCurrentPhase)
No face culling or greedy meshing is performed — every non-air block gets a full cube, even where faces are fully occluded by neighbors. This is a known, flagged limitation (see Known Issues) that will matter once denser hand-authored worlds exist.
Texture Mapping — GETileAtlas
Wraps an Easy3D::TextureAtlas sized to object-m.png (1301×1431 px),
registered as one grid: 64 px tiles, 20 columns, 65 px pitch (64 px icon + 1 px inter-tile gap).
GetTileUv(blockType) returns an all-zero rect for Air (type ≤ 0), otherwise looks
up "tile_<blockType>" in the atlas.
Animated Tiles
Ported 1:1 from mobile-eggbert's Tables.cpp (via GalaxyEggbertSimple3D):
| Tile | Frames |
|---|---|
| Lava | 8 |
| Spike | 16 |
| Crusher | 10 |
| Saw | 6 |
| Water1 / Water2 | 6 each |
| Temp | 20 (including 2 invisible/blank frames) |
| Fan ×4 directions | 3 each |
| Marine | 11 |
Animated blocks are split into a second mesh, rebuilt only when the anim phase actually changes (not every frame). If the current 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 Call
terrainEffect_->View = camera.GetViewMatrix();
terrainEffect_->Projection = camera.GetProjectionMatrix();
terrainEffect_->World = Matrix::Identity;
staticRenderer->Draw(device, *terrainEffect_);
if (animatedRenderer) animatedRenderer->Draw(device, *terrainEffect_);
Known Rendering Limitations
- No face culling between adjacent solid blocks.
- No chunk-radius streaming — the entire world is uploaded up front.
- No Blupi sprite, no object/pickup rendering, no sky dome, no HUD.
Easy3D::BillboardBatchhas no vertex builder or renderer adapter — this blocks all sprite rendering.