An OpenGL-based tool, and a library, that converts a 3D model to a 3D volume, represented as a Sparse Voxel Octree.
This code was originally part of mdmc, another project of mine meant to convert a 3D model to a Minecraft schematic. I've decided to modularize this part
as it could be useful for other tasks (and hopefully be useful to other people).
You can generate the octree out of the 3d model using the following command:
./voxelizer <model-file> <volume-height> <output-file>
You can visualize the output octree by running the following command:
./viewer <octree-file> [model-file]
Where model-file is optional and can be used to compare the original model with the result.
The output file consists of an array of little endian uint32_t, in binary format, representing the following data:
- The 
versionof the format (should be 0x01) - The 
volume_size.x - The 
volume_size.y - The 
volume_size.z - The 
octree_resolution(could be derived fromvolume_size) - The 
octree_bytesize: the number of bytes reserved for the octree structure - The 
octree: the actual octree structure 
The octree structure consists of a set of levels one allocated after the other.
The first 8 uint32_t correspond to the first octree level. Every value can be either:
- A parent node: can be identified because the MSB is set. The remaining bits are the index of the first child node
 - A leaf node: can be identified because the MSB is not set. The remaining bits are the voxel color. Excluding the MSB: 7 bits for alpha, 8 bits for blue, 8 bits for green, 8 bits for red
 - An empty node: is 0
 
Once you have integrated the code in your project (e.g. either by using git submodules, CMake's FetchContent...), you can add it as a dependency to your project:
add_subdirectory(voxelizer)Then you have to include its directories and link the library to your target:
target_include_directories(<your-target> PUBLIC "voxelizer/voxelizer")
target_link_libraries(<your-target> PUBLIC voxelizer)As a first step, you have to create a representation of the 3d model that the voxelizer pipeline is compatible with (i.e. you have to initialize the voxelizer::scene object). You can either load it from a file or initialize it from already loaded data (this is the hard part, I'll cover it, probably).
To load it from a model file:
#include <voxelizer/ai_scene_loader.hpp>
#include <voxelizer/scene.hpp>
voxelizer::assimp_scene_loader scene_loader{};
voxelizer::scene scene{};
char const* my_model_file = /* ... */;
scene_loader.load(scene, my_model_file);Then you can run the voxelization process:
#include <voxelizer/voxelize.hpp>
voxelizer::voxelize voxelize{};
voxelizer::voxel_list voxel_list{};
uint32_t volume_height = /* ... */;
voxelize(voxel_list, scene, volume_height, scene.m_transformed_min, scene.get_transformed_size());Finally build the octree:
#include <voxelizer/voxelize.hpp>
#include <voxelizer/octree_builder.hpp>
voxelizer::octree_builder octree_builder{};
glm::uvec3 volume_size = voxelizer::voxelize::calc_proportional_grid(scene.get_transformed_size(), volume_height);
uint32_t max_volume_side = glm::max(glm::max(volume_size.x, volume_size.y), volume_size.z);
uint32_t octree_resolution = (uint32_t) glm::ceil(glm::log2((float) max_volume_side));
size_t octree_bytesize = voxelizer::octree::get_octree_bytesize(octree_resolution);
GLuint octree_buffer{};
glGenBuffers(1, &octree_buffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, octree_buffer);
glBufferStorage(GL_SHADER_STORAGE_BUFFER, octree_bytesize, nullptr, NULL);
voxelizer::octree octree{};
octree_builder.build(voxel_list, octree_resolution, octree_buffer, 0, octree); The built data structure is encapsulated in the octree object.
