Grund3D
A C++20 Vulkan/SDL3 engine built around gameplay hot reload, and the cozy Finnish-forest survival game it runs. Playable on itch.io.
Highlights
- Runtime host with a session-stable engine module and a disposable, hot-reloaded game module over a versioned C ABI
- Vulkan 1.3 renderer: physically based sky, ray-traced shadows/reflections/GI when supported, FFT ocean, GTAO, bloom
- Seeded terrain streamed in LOD'd chunks across a job pool, with rivers, lakes, and scattered instanced props
- Reproducible Linux + Windows release builds from one Docker image, published to itch.io with butler
Overview #
Grund is a runtime-oriented C++/Vulkan/SDL3 game engine, and grund3d is the game it is being built for: a cozy survival game set in a late-autumn Finnish forest. You fell trees, buck them into blocks, split those into klapi (firewood), and build a nuotio (campfire). The long-term goal is a sauna you heated yourself.
The game is currently a playable pre-alpha (v0.12.0) for Linux and Windows on itch.io.

Grund is the successor to my earlier 2D C++ engine. It keeps the idea that worked there, a runtime host that hot-reloads the gameplay code, and rebuilds everything else from scratch around a 3D renderer and a streamed world.
Architecture: runtime, engine, game #
The engine is deliberately not an editor project. The workflow is hot reload: change gameplay code, run make game, and keep playing. Everything is split into three modules plus shared code:
flowchart TD Runtime["<b>runtime</b> (executable)<br/>owns the process and main loop<br/>loads both modules<br/>crash handling"] Engine["<b>engine.so</b><br/>stable for the whole session<br/>window, Vulkan, input, audio<br/>assets, physics, jobs, memory"] Game["<b>game.so</b><br/>disposable, reloaded on rebuild<br/>fauna, fishing, firewood<br/>weather, saves, HUD"] Shared["<b>shared</b><br/>math, collision, mesh<br/>worldgen, water, pak, save"] Runtime -->|"load once"| Engine Runtime -->|"load from a copy,<br/>reload on rebuild"| Game Game -->|"versioned C ABI"| Engine Engine --- Shared Game --- Shared
The important difference from my previous engine is who owns the state. There, the game module had to serialize itself before a reload and restore itself afterwards. In Grund, the engine owns the persistent game state in arena-allocated memory domains, so a reload just swaps the code. The camera, the captured mouse, and the whole world carry across without a save/restore step.
A few details make that robust in practice:
- The runtime loads
game.sofrom a copy, so a rebuild in progress can never corrupt the loaded module. - The modules talk through a versioned C ABI. A mismatched module is refused instead of crashing.
- Crashes are caught on both POSIX and Win32 and written out with symbols, so pre-alpha bug reports are actually useful.
Renderer #
The renderer is Vulkan 1.3 (through volk and VMA) with reverse-Z depth. Most of the frame is compute:
- Sky and atmosphere: a physically based sky with transmittance, multiple-scattering and sky-view passes, plus aerial perspective over distant terrain.
- Ray tracing: shadows, reflections and bounce lighting are ray traced and turned on automatically when the GPU supports it.
- Water: an FFT ocean, caustics, and Snell’s window when you are underwater.
- Post and lighting: GTAO ambient occlusion, bloom, and histogram-based auto-exposure.
- Vegetation and weather: GPU-culled grass, foliage with impostors in the distance, particles, rain and snow.
Terrain is drawn with indirect draws, and quality presets scale the render resolution. On Wayland the game runs in native 4K fullscreen.

A streamed, seeded world #
The world is generated from a seed. Terrain is streamed in LOD’d chunks around the camera and built across a fixed pool of eight job workers, and the chunk meshes are sub-allocated through VMA. The same jobs scatter trees, bushes and stones from the world seed and draw them instanced. Rivers run from lakes and springs down to the sea through carved channels, and nothing grows below the waterline.
Every scattered prop has an identity. When you fell a tree, the stump stays through streaming, in the ray-traced shadows, and in collision. The player is a Jolt Physics character behind the engine’s own PhysicsAPI, so you can walk, run, jump, crouch and swim.

All of the art, audio, models and terrain inputs are produced by scripts under tools/. The asset registry is path-keyed and reference-counted, and textures, meshes and shaders all reload live, the same way the gameplay code does.

Nights, villages and the map #


Shipping #
The releases are reproducible. One Docker image builds both platforms: Linux (glibc 2.36+, X11 and Wayland) natively and Windows through llvm-mingw, and the Windows tests run under Wine in the same container. make release builds, tests and packages the game together with its crash symbols, and make publish-itch uploads it with butler. The changelog is generated from conventional commits, and pre-commit (gitleaks, cppcheck, shellcheck, hadolint) also runs in Docker.
Each feature starts as a written plan and ends with a result doc describing what actually happened, including divergences from the plan and the bugs worth remembering. There are about a hundred of these in the repo by now, and they have turned out to be the most useful documentation the project has.







