CLI tool
Forceflow/cuda_voxelizer avatar
Forceflow/cuda_voxelizer

cuda_voxelizer turns polygon meshes into voxel grids, and says when the GPU is not worth it

CUDA Voxelizer to convert polygon meshes into annotated voxel grids

654 stars102 forksC++MIT

At a glance

What is it?
cuda_voxelizer is a command line tool that converts polygon meshes into voxel grids using a CUDA card, with a multi threaded CPU fallback for small models and machines without a compatible GPU. It is fast for what it does and narrow about what it accepts.
Who is it for?
Adopt cuda_voxelizer when you are converting polygon meshes into voxel grids offline or per frame in a pipeline where the input is a watertight or surface mesh you control, and you have an Nvidia card of compute capability 2.0 or higher. Do not adopt it as a general mesh processing tool, since the accepted inputs and the five output formats are fixed and the solid mode is marked experimental.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 128 days ago.
What is it written in?
Mainly C++, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A narrow converter: polygon mesh in, voxel grid out

cuda_voxelizer does one thing. It reads a polygon based 3D model and produces a voxel grid of it. The README calls it a command line tool to convert polygon meshes to annotated voxel grids, and the accepted input formats are listed explicitly: .ply, .off, .obj, .3DS, .SM and RAY.

That short list defines the audience. This is for someone who has a mesh and needs a discrete grid of it, typically for a graphics research pipeline, a volumetric dataset, or a real time application that voxelises geometry every frame. It is not a general purpose mesh library, it does not simplify or repair meshes, and it does not do anything else with the model than sample it into cubes.

The grid is cubical and cubic in count. The -s option takes the voxel grid length, defaults to 256, and the README says the process constructs the tightest possible cubical bounding box around the input model. So the resolution is expressed as a single number, 256 meaning a 256 by 256 by 256 grid, and the model is fitted into that box rather than the box being fitted around some padding convention.

Output naming is deterministic. Files are saved in the same folder as the input, named as the original file name, an underscore, the grid size, and the extension.

Five output formats, each aimed at a different consumer

The -o option selects the output format, and the README gives the default as binvox, while also marking vox as the default in the option description itself, which is a small inconsistency worth noticing when you read the usage text.

The formats differ in what they contain. A vox file is the native format of MagicaVoxel and can be viewed there. A binvox file follows the binvox format and can be viewed with viewvox. An obj file is a mesh containing actual cubes, made of triangle faces, one per voxel, which is the choice when you want to load the result into a modelling tool and see geometry. The obj_points variant produces a mesh containing a point cloud with a vertex for each voxel, viewable in anything that can display vertices, such as Blender or Meshlab. The morton format is a binary file containing a Morton ordered grid, and the README calls it an internal format used for other tools.

That last one is the most interesting from an engineering point of view and the least documented for an outsider. Morton ordering interleaves the grid so that nearby voxels stay near each other in memory, which is what you want for cache friendly traversal, but the README gives no layout specification and no reader for it.

Because the tool always writes a file rather than streaming to stdout or an API, integrating it into a live pipeline means shelling out and reading a file.

Surface voxelization by default, and an experimental solid mode

The default operation is to voxelize the faces of the mesh. The -solid flag switches to solid voxelization, which fills the interior of the model, and the README marks that mode as experimental. It also states the requirement: a watertight input mesh.

That single word carries a lot of weight. A watertight mesh has no holes, so the inside is unambiguous. Feed a non watertight mesh to solid mode and the fill result is not simply wrong in one place, it is wrong in a way that depends on how the holes are arranged, and the tool gives no diagnostic for it. The README credits conceptclear with implementing solid voxelization, which tells you the feature came from a separate contribution.

Surface mode has the opposite property, and it is a reasonable one. You get a shell of occupied voxels tracing the geometry, which is exactly what you want for surface reconstruction tasks, and it does not care whether the mesh is closed.

This is the clearest case in the project for asking what you actually need. If you want an occupancy field, use surface mode and accept the shell. If you want a filled volume, you have to guarantee the input is closed before you run it.

The CPU path is not a fallback of last resort

Two facts sit side by side in the README. The tool requires a CUDA compatible video card with compute capability 2.0 or higher, meaning Nvidia Fermi or better. And since v0.4.4, the voxelizer reverts to a slower CPU voxelization method when no CUDA device is found.

The -cpu option makes the CPU path explicit rather than automatic, and the README gives two reasons to use it: when a CUDA device is not detected or compatible, and for very small models where GPU call overhead is not worth it. That second reason is the one that changes how you should think about the tool. On a tiny mesh, the launch overhead of the GPU path can exceed the actual work.

The CPU path is multi threaded through OpenMP, and the README notes that CPU voxelization time depends heavily on core count because OpenMP allocates one thread per core. So a machine with many cores gets a much different CPU number from a machine with few, and the timings quoted in the README are tied to a specific twelve thread laptop CPU.

If you are automating this, decide which path you want per model size rather than letting the detection decide, because the detection rule and the small model rule are different rules.

Building with CMake needs CUDA, Trimesh2 and OpenMP

The build dependencies are three: the Nvidia CUDA 8.0 Toolkit or higher, Trimesh2 for model importing, and OpenMP for multi threading. The README recommends the latest version of Trimesh2.

The CMake route applies to Windows and Linux, and it is the same configure step with two platform specific lines. On Linux the CUDA architecture is passed through the CUDAARCHS environment variable and an explicit -DCUDA_ARCH setting, along with the Trimesh2 include and link directories. On Windows with Visual Studio the equivalent line uses the -A x64 generator and sets the same two Trimesh2 paths.

bash
CUDAARCHS="your_cuda_compute_capability" cmake -DTrimesh2_INCLUDE_DIR:PATH="path_to_trimesh2_include" -DTrimesh2_LINK_DIR:PATH="path_to_trimesh2_library_dir" -DCUDA_ARCH:STRING="your_cuda_compute_capability" .. 

The placeholder is a string naming your CUDA architecture, and the README gives 50 and 61, or 60, as examples. Getting this wrong is the most common way to end up with a binary that refuses to start on your card, so it is the first value to check.

After configuring, the build step is a standard CMake build with a parallelism hint.

bash
cmake --build . --parallel number_of_cores

Since 2022 the project also builds through GitHub Actions, with the workflow configuration linked from the README, and CMakeLists.txt at the top level is the entry point for all of this.

The Visual Studio project hardcodes Windows library paths

There is a second build path for Windows users. A project solution for Visual Studio 2022 sits in the msvc folder, and the README is explicit that it is configured for CUDA 12.1 and that you can edit the project file to work with other CUDA versions.

The paths you are expected to edit live in a file called custom_includes.props, and they are absolute Windows paths pointing at a trimesh2 folder, a glm folder, and the output directory for the built binaries.

xml
    <TRIMESH_DIR>C:\libs\trimesh2\</TRIMESH_DIR>
    <GLM_DIR>C:\libs\glm\</GLM_DIR>
    <BINARY_OUTPUT_DIR>D:\dev\Binaries\</BINARY_OUTPUT_DIR>

Two observations. The GLM_DIR entry is a leftover from before v0.6, since that release replaced all GLM math types with builtin CUDA types and removed the external dependency, so a stale GLM path in the properties file can send you editing a setting that no longer affects the build. Second, the solution is aimed at 64 bit executables, and the README says 32 bit builds are possible but are not actively tested or supported.

On Linux the CMake path is the one to use, since the Visual Studio solution is Windows only.

The quoted timings exclude all I/O and come from one specific card

The README publishes voxelization timings for the Stanford Bunny Model, described as 70k triangles, in a table comparing a GTX 1050 TI on the GPU side against an Intel i7 8750H with 12 threads on the CPU side. The GPU column reads 0.2 ms at 64 cubed, 0.6 ms at 256 cubed, 1.8 ms at 512 cubed and 44.6 ms at 2048 cubed. The CPU column for the same rows is 39.8 ms, 118.2 ms, 308.8 ms and 4147.4 ms.

Two qualifiers make these numbers usable. The timing is for non solid voxelization, and no I/O is included, neither reading the model from disk nor writing the result to the GPU. So the gap between the columns is about the voxelization kernel itself, not about the whole program.

The ratio is the part that matters for planning. At 64 cubed the GPU figure is already a fraction of a millisecond, which is precisely the regime the -cpu option warns about, since fixed overhead can exceed the work at that size. The project states that using the routine as a per frame voxelization step for real time applications is viable, and these figures are the basis for that claim.

Be careful with the CPU column as a planning number. It is tied to twelve threads on a specific laptop processor, and OpenMP scales with core count, so a workstation with more cores will show different numbers.

MIT licensed, last pushed in June 2026, with v0.6 dating from 2023

The licence is MIT, with the text in the LICENSE file at the top level. For a research and graphics tool that you compile yourself, that is about as permissive as licensing gets, and it means embedding the tool in a commercial pipeline carries few obligations beyond keeping the licence text with the source.

Maintenance needs a careful reading. The repository is not archived and the last push was on 2026-06-16, which is inside the six month window and means work is still landing on the main branch. The release history, though, is older: v0.6 was released on 2023-09-17, preceded by v0.5.2 on 2022-11-10 and v0.5.1 on 2022-10-03. So the tree is more recent than the newest tag, and the README header still says v0.6.

That gap has a practical consequence. The README flags the v0.6 change itself: all GLM math types were replaced with builtin CUDA types to remove an external dependency, and the author says he tested the release as well as he could and advises checking whether a problem reproduces with an older version if you hit weirdness. That is an invitation to bisect between tags when something breaks.

The repository also carries CONTRIBUTING.md, a test_models directory with sample inputs, and a src directory holding the implementation.

Editorial conclusion

Adopt cuda_voxelizer when you are converting polygon meshes into voxel grids offline or per frame in a pipeline where the input is a watertight or surface mesh you control, and you have an Nvidia card of compute capability 2.0 or higher. Do not adopt it as a general mesh processing tool, since the accepted inputs and the five output formats are fixed and the solid mode is marked experimental. Before you build, check the CUDAARCHS value against your device, confirm Trimesh2 paths in CMakeLists.txt or custom_includes.props, and measure the CPU path with the -cpu flag before you assume the GPU path is the one you will use.

Frequently asked questions

Which input formats does cuda_voxelizer accept?

The README lists .ply, .off, .obj, .3DS, .SM and RAY as the supported input formats. Model importing is handled by the Trimesh2 library, which is a build dependency.

What output formats can cuda_voxelizer write?

The -o option selects vox for MagicaVoxel, binvox for viewvox, obj for a mesh of cubes, obj_points for a point cloud with one vertex per voxel, and morton for a binary Morton ordered grid that the README describes as an internal format.

Does cuda_voxelizer work without a GPU?

Since v0.4.4 the tool reverts to a slower CPU voxelization method when no CUDA device is found, and the -cpu option forces the multi threaded CPU path. The README notes that CPU voxelization time depends on core count because OpenMP allocates one thread per core.

What does the -solid option require?

Solid voxelization is marked as experimental and needs a watertight input mesh. Without the flag the tool voxelizes the faces of the mesh rather than filling its interior.

How do I build cuda_voxelizer on Linux?

Install the Nvidia CUDA 8.0 Toolkit or higher, Trimesh2 and OpenMP, then configure with CMake passing the Trimesh2 include and link directories and a CUDA architecture value through CUDAARCHS. The README points to CMakeLists.txt at the top level and to the GitHub Actions workflow for a build reference.

Official sources

  1. Forceflow/cuda_voxelizer on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/forceflow-cuda-voxelizer.svg)](https://hysenlabs.com/projects/forceflow-cuda-voxelizer)

Was this useful?