I have been working on a toy Voxel Game Engine for some time now and wanted to write some posts about what the engine can do and the steps I’ve taken in building it. As voxel engines go it isn’t very impressive but it’s always improving and I have a lot of fun building it.
For the uninitiated, voxels are a way of representing physical space. The technique is used in everything from engineering simulation, to medical imaging, to video games. Space is broken down into a grid of voxels, with each voxel on the grid having certain properties associated with it. In the case of my engine and games like Minecraft, the voxels are given a type (wood, stone, empty air, etc.) which determine things like appearance and physical properties. Voxel systems are most often used to represent the environment around the player.
I think one of the most important aspects of voxel representation is its logical simplicity. The world is broken up into small cubes, each with a few simple properties associated with it. This simplicity lends itself very well to things like player modification, the player can easily predict how the environment will change when they break or place a block. It also works well with procedural generation, the developer can write algorithms to fill out the grid on the fly as the player moves through the environment. Many voxel games take advantage of both these properties, providing vast worlds for the player to explore and build in.
Voxels are usually organized into “chunks”, sub-grids of voxels within the larger grid. In Minecraft each chunk 16x16x256, 256 being the height of the map. I use 32x32x32 and don’t have a maximum height. The voxels in each chunk are stored close together in memory and many operations occur on the chunk as a whole. For example in my engine each chunk is rendered as a single object, each time a voxel is edited the entire chunk needs to be reprocessed for rendering.
Having to process an entire chunk’s worth of voxels (32,768 in my case) for a single change might sound expensive, but it provides massive savings overall. Even though modifying the environment is an important part of these games, in reality most voxels don’t change all that often. It is better to do more work up front and reduce the number of total discrete “objects” for rendering and physics simulation. Even though there could be millions of voxels loaded at any given time, my engine only needs to deal with a few hundred voxel chunks. Of course there is a balancing act here, larger chunks mean fewer objects at the cost of time spent processing each modification.
My engine is built in C#. Most major game engines use C++ but C# is the language that I understand best and am the most productive with. For a large project that I can only work on a few hours a week staying productive and avoiding unnecessary blockers is very important.
For rendering I am using the excellent Veldrid library along with a few of it’s companion libraries. Veldrid provides a low-level, high performance, cross-platform graphics API modeled after modern APIs like Vulkan and DX12. veldrid-spirv provides easy integration with SPIRV-Cross, which means I only need to write my shaders once and have them compiled across all supported platforms. I have used this setup in the past and it all works very well. Veldrid.StartupUtilities integrates SDL2 for desktop windowing and input. Although I am only targeting desktop for this project I have used Veldrid for mobile development in the past and I will be able to support Android and iOS if I choose.
I am using BepuPhysics for realistic physics. Bepu is a very impressive engine, the many demos the author has written show off large, complex scenes running buttery smooth. I expect it will handle the increasingly large scenes I will be building without much issue. This is particularly impressive knowing that the engine is written completely in C#. The downside is that the engine is very low level, the API is not particularly intuitive and is easy to use incorrectly. Just recently I spent an hour or two tracking down a memory leak related to disposing of physics shapes as voxels chunks are unloaded from the game. I have included Bepu as a git sub-module, making it easy for me to dig into source code when things aren’t going the way I expect. I still don’t really understand how a lot of it works, luckily the author has built a lot of well documented, complex example scenes to learn from.
My voxel engine now has a few basic features but is really just getting built. I have recently gone through a major refactor which I will write another post about soon. I am still re-adding a few features from the old version and starting new ones. Projects like this are excellent for practicing high performance programming techniques and managing complex code bases while giving the satisfaction of building your own little universe from the ground up. I would definitely recommend them to anyone hoping to learn more about graphics, game engines, and programming in general.