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.

Demonstration of unified and deshelled models is visible in MeshMixer and Blender
On the left, we see an object that appears to be solid (though colored). On the right, we see that the individual pieces of the car are actually shells, and could potentially be removed from the model. Most 3D printers will not be able to print this file while it contains these shells.

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.

Using Extended Repair in AutoDesk NetFabb

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.

PyMesh CSG Tree
Each step is represented as either an union, difference, or intersection between one model and another. As we combined multiple operations, we obtain a final model.

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.

--

--

Joseph A. Boyle
Dandy Engineering, Product & Data Blog

Senior Software Engineer at Dandy. Rutgers University Computer Science Alumni. Lover of low-level systems, vintage computers, compilers, and 3d printing.