Programmatically Removing Shells from 3D Models
Shells mean that two clusters of vertices within a model have no shared connection
If you’ve ever tried printing a 3d model, you may have come across a fairly common problem: the model having shells. Shells are the result of two clusters of vertices within the model having no shared connection. These clusters are called islands. While the STL file format doesn’t technically support multiple models in a single file, it doesn’t disallow having these clusters either, which causes problems for printers.
Design software and certain types of modeling can often yields models with many shells, especially if the parts are removable from the main model for demonstration purposes. Although the islands overlap, most 3D printers will not be able to handle the models, as the pieces are separated. Thus, one needs to develop a fast and robust process for “de-shelling” or “repairing” these models.
Existing Software
Many existing software applications can repair models for you. Common candidates are AutoDesk NetFabb (or Maya), MeshMixer, and even Blender 3D. Truthfully, most CAD software is built to handle this job if you can find the right place to look. In NetFabb, for example, you can achieve de-shelling by importing your models as normal, and selecting Extended Repair with Automatic Part Repair.
This works, but the process is slow: it is manually driven and the algorithm is memory intensive. Performance drags can quickly become impractical.
Automated Solutions
Python is a popular environment for building model/cleaning pipelines, so let’s begin there. There are a few popular libraries that can handle complex model manipulation:
Trimesh
Trimesh comes with a fairly simple to use, feature rich API. Best of all, it is by far the easiest to install out of all of the other packages tried — you can simply install it via pip install trimesh
.
Trimesh makes identifying shells in the model fairly easy:
The split
function in Trimesh returns all of the shells in the base model. If we print the result, we will see one or more Trimesh meshes, one for each of the shells in the model, or just one if there were no shells.
Now that we have the individual pieces, we can “glue” them back together. To do this, we want what is called a Boolean Union, or the result of merging the non-intersecting pieces of both shells. This is made easy via Trimesh’s trimesh.boolean.union
function. This function accepts two arguments: (1) an array of meshes to perform a boolean union on, and (2) which engine to use. Trimesh internally will use one of two engines — blender
or scad
(OpenSCAD), which use the python bindings mentioned above internally. In either case, it will generate the python code required for the selected engine, and will execute it behind the scenes.
And voilà, we’re done…. At least we could be, if our models ran faster in the Blender or OpenSCAD backends. Unfortunately, they do not, even after a variety of optimization experiments. We found that Blender as an engine yields significantly better results than OpenSCAD did, but we still needed to explore other options.
Blender & OpenSCAD
As mentioned above, Trimesh uses Blender and OpenSCAD as engines for their boolean union algorithm. We attempted to use their packages directly instead of using the auto-generated code from Trimesh. This was met with limited success — the code Trimesh generated was roughly just as fast as what we cooked up.
PyMesh
PyMesh is a fantastic library, but fairly difficult to get installed. You either will be spending a weekend scratching your head trying to build it from source, or find yourself lost in the boundaries of their docker image. Their existing image works fantastically if you’re okay with it as a base, but using it in a production system requires some tweaks and changes depending on the environment you target. Regardless, we decided to pursue it in the interest of speed.
Since we already had our models loaded from the existing manipulation pipeline in Trimesh, we decided to start there. First, we pick up from where we left off in the Trimesh code, but now begin to port the models to PyMesh:
PyMesh uses a tool called Constructive Solid Geometry (CSG) to build larger models. CSG is a very powerful way of express model manipulations, and is fairly easy to work with.
For our purposes, we will simply do a union of many models. This code is straightforward:
With some careful tweaks, we are able to get our entire model unification step down to a few seconds of processing time, all without a single human interaction or dedicated machine needed.