Although it is not a “voxel game” in the contemporary sense—it has neither procedural nor deformable terrain—NEON STRUCT uses an advanced version of Eldritch‘s voxel system for quick and easy level building.
Voxels in this engine function like Minecraft‘s blocks: each one has a singular ID to define its properties, and is textured with up to three quads on it, for the top, sides, and bottom.
This system was perfectly sufficient for Eldritch‘s lo-fi, procedural worlds. But building more realistic spaces in NEON STRUCT, I kept running into a problem. In order to put different materials on each side of a wall, I had to use a two-voxel wide wall. At the 1m³ resolution of the voxel world, this requires 2m walls. It looks absurd and wastes space.
The worst case I encountered so far was trying to put an air duct (because of course this game has air ducts) between two rooms: 2m for the first wall, 1m for the duct, and 2m for the second wall. It was untenable.
Every couple of weeks, I would revisit the problem and try to find a solution. Fundamentally, I wanted to transform the world from an array of volumes into an array of faces so that they could be painted separately. For example, where a north-facing wall met an east-facing wall, each could have a separate texture, like so:
But many other systems—lighting, physics, pathfinding, and more—are dependent on the voxel array. Getting rid of that was not an option, and storing a parallel array of face data seemed wasteful and redundant.
I finally discovered a solution earlier this week. It seems obvious in retrospect, but it took me a few months to arrive at it.
The “eureka” moment was when I realized that I did not need to store a large parallel array of face data to accomplish my intention. Voxels already define their faces, and 99% of the time, I just want to leave those alone. In a few special cases, I want to override the default textures on a particular face, metaphorically applying a veneer over the voxel.
I implemented this as a sparse map of veneers, keyed off a bitwise combination of the voxel array index and an enumeration of the face normal (+/- X/Y/Z). When constructing the hull mesh around a voxel chunk, I check that map to see if there is a veneer for each face, and apply that texture instead of the default one if there is.
Now I can implement air ducts and other cases with much less wasted space:
I have also been using the veneer system to add decorative highlights which would otherwise have required an excessive number of custom voxel definitions.
Because the veneer is applied during mesh construction, there is no rendering overhead for using veneers. And because it uses a sparse array, it has a minimal impact on the file size of a level.
At the moment, surface properties (which are used to pick footstep sounds, for example) are still defined solely by the voxel instead of by veneers, but that could easily be overridden too if it becomes a problem.