Archive

Tag Archives: Blender

Today I decided to roll up my sleeves and figure out how to run Blender headless. I’ve been telling people that it’s possible (based on documentation), but haven’t tried it out myself.

Until now!

It’s actually pretty simple. Here are some steps. I’m on a Mac, so the paths might be slightly different if you’re on another operating system (check out these instructions for for Windows).

1. Figure out the path of the Blender executable. If you’ve ran the Blender console window before, it’s the same executable.

To find it via GUI, open up where the downloaded Blender zip was extracted.

Blender package

On a Mac, show package contents and navigate to the blender executable.

Blender executable

Actual path:

/Users/Jenny/Desktop/Blender/blender.app/Contents/MacOS

(I currently have the extracted Blender 2.74 package on the Desktop for simplicity so my path reflects that.)

2. Run the executable with the -b argument to run Blender without the GUI. You can add the path to your PATH or run ./blender.

With path added:

blender -b

Run locally:

./blender -b

3. You can pass scripts into Blender while running Blender headless.

blender -b -P /Users/Jenny/Desktop/my_script.py

4. Passing arguments into your script is a little bizarre. The documentation specifies the -- (double dash) syntax to signal to Blender to stop reading those arguments and have them processed by your script. However, inside your script, if you’re trying to parse out the arguments, you get all the arguments, not just the ones after the --. You must parse them out yourself. Here’s an example below. I’m using argparse instead of sys.args because argparse is very powerful and handles a lot of edge cases (plus it’s just darn good practice).

blender -b -P /Users/Jenny/Desktop/my_script.py -- --number 5 --save '/Users/Jenny/Desktop/cube.obj'

The example script creates a number of cubes and exports them.

import argparse
import bpy

def get_args():
  parser = argparse.ArgumentParser()

  # get all script args
  _, all_arguments = parser.parse_known_args()
  double_dash_index = all_arguments.index('--')
  script_args = all_arguments[double_dash_index + 1: ]

  # add parser rules
  parser.add_argument('-n', '--number', help="number of cubes")
  parser.add_argument('-m', '--save', help="output file")
  parsed_script_args, _ = parser.parse_known_args(script_args)
  return parsed_script_args

args = get_args()
number_of_cubes = int(args.number)

# create cubes
if number_of_cubes > 1:
  for x in range(0, number_of_cubes):
    bpy.ops.mesh.primitive_cube_add(location=(x, 0, 0))

# export scene
bpy.ops.export_scene.obj(filepath=args.save)

Running the script will give you an exported OBJ with the cubes.

There you have it: how to run headless Blender. Most people use headless Blender to farm out rendering jobs and increase render speeds, but you can do all sorts of neat stuffs by providing a script. Have fun!

Since I got a few questions after my talk, I thought I’ll summarize them here. If I’ve missed a question, feel free to ping me.

Q: How to smooth a model in Blender?
A: Use subdivision surfaces modifier. Note: the more subdivisions you have, the more vertexes/edges/faces it creates, which might lead to slower performance.

Q: How to create SVGs to import into Blender?
A: You can use Inkscape, another open source software, to trace your picture into a vector image.

Q: What are some resources for 3D scanning objects?
A: 123D Catch is a good starter tool by Autodesk. There’s also lots of open source alternatives.

Q: What are some other resources to learn Blender?
A: CGCookie has really nice Blender tutorials.

Q: Can you talk more about what you did for mesh repair?
A: Here’s a blog post I wrote a while ago about mesh repair. I submitted my mesh repair script as a patched for Blender’s 3D Print Utility Add-On. Check it out here.

Q: Can you control the fill of the model, when 3D printed, through Blender?
A: If you’re printing the model yourself, you usually control the fill downstream of creating the model. For example, take a look at the different fills for Slic3r. If you’re sending off your model to be printed by a service, they’re usually filled solid.

Hopefully this is helpful to folks.

Today I saw a post about Emendo, a program for mesh repair, and became a little miffed. It costs roughly $50, which I think is pretty steep for something you could do in a few lines of code in Python with the Blender API. Even if you’re not a programmer, there are $0 solutions like netfabb Basic and free software like MeshLab and Blender. I can’t attest to the quality of Emendo, but it has to be amazing to be worth that price.

So, let’s talk mesh repair: what is it, why it’s hard, and why I would or wouldn’t spend $50 on it.

Mesh repair is essentially fixing up a 3D model so it can satisfy 3D printing constraints. I became interested in mesh repair when I was modeling a small figurine. When designing, I cared more about aesthetics of the model than the printability of the model. I figured there would be tools to magically and automatically correct my model to be printable.

There is such a tool. It’s called netfabb.

Netfabb is a software that’s pretty good at fixing all sorts of problems. It’s also popular among 3D printing enthusiasts and is leveraged by businesses and 3D printer manufacturers (like Shapeways, Formlabs, and Figulo).

Unfortunately, my model turned out to be disastrously hard to repair. Netfabb failed, and I ended up concocting a solution out of both netfabb Basic and MeshLab after days of struggle.

Rakdos figurine

Turning a model of any shape into a printable model is a hard problem. For example, suppose you have holes in your model that makes it unprintable. The mesh repair software will try to patch those holes. These holes are big gaps of missing information and the software has to guess what should go in them based on the topology of each hole’s surroundings. While some solutions can be easily guessed, other guesses can create more problems and can even conflict with one another. Here are wonderful visualizations of capping a hole in three different and valid ways, illustrating the underconstrained nature of the hole problem. Holes are just one of the many problems a 3D model could have.

There are many algorithms for mesh repair, but none of them are perfect. Here is an excellent introduction to various algorithms and the tradeoffs between them. Here is a more recent and more in-depth look at different algorithms and their features.

I use my figurine model to test the “robustness” of different mesh repair software. It may not be a fair test, since my model may be a very hard edge case, but I’m testing mostly to satisfy my curiosity. Here are some results:

netfabb Basic 4.9: failed

Netfabb flags something as unprintable with a big red caution sign. Even after repairs, the caution sign remained. Netfabb Cloud also failed. I believe the most recent version of netfabb Basic will successfully repair the model. Since netfabb is such a staple tool, I determine the success or failure of other mesh repair programs by importing the fixed models into netfabb and recording whether netfabb determines if they are printable or not.

Autodesk Meshmixer version 10.3.44: failed

Autodesk 3D Print Utility 1.1.1: succeeded

The 3D print utility actually succeeded in the mesh repair. However, it took about 3 hours for the repair to complete on my laptop, and the resulting mesh had a tremendous loss of resolution.

Photoshop CC: failed

I don’t know what version of Photoshop CC I used, but it was a 30-day trial from this past January. The repaired model still had issues, but could subsequently be repaired by netfabb. Photoshop actually emailed me asking for the figurine model, so I suspect they might’ve upgraded their algorithm.

GitHub: failed

 

Custom Blender Script: succeeded

Over the summer, when I was working on 3D printing glasses from 2D designs, I realized I need to repair the glasses models. I started to write a mesh repair script and decided to design it to repair the figurine. My script does indeed fix the model, but it takes about five minutes to run on the figurine model. The script produces better resolution in less time than Autodesk’s 3D Print Utility, but it’s not the fastest since I’m looping multiple times. I’ve decided to release it as a Blender Add-On, in the 3D Print Toolbox, available now in Blender 2.72b. Hope it’s helpful!

3D Print Add-On

The script’s algorithm works better on simple models or models with dense polygons. If patching holes result in non-manifold geometry, the script will try to delete vertices around the holes and then patch them again. If the polygons are dense, removing a few shouldn’t affect the overall aesthetics.

It probably won’t work for all models, though.

$50 for a mesh repair software that repairs all models is absolutely worth it, for all the time invested spent in manual repairs. However, given the diversity of models and the diversity of problems (some of which could be caused by printer-dependent constraints), I doubt there’s such a silver bullet right now.

Between the Blender Add-On and netfabb, you have a pretty good chance that your model will get repaired. However, should you need to, I advocate using Blender’s Python API to build your own repair tool. It will allow you to create something catered to your specific modeling needs. It might not be as fast as pressing a button, but you’ll have unparalleled customization ability. I hear the paid upgraded versions of netfabb give you a lot of customization choices in mesh repair, but I doubt it would be as thorough as interacting with the models directly. The Blender API already provides some nice helper functions like bpy.ops.mesh.fill to fill in faces and bpy.ops.mesh.select_non_manifold to select non-manifold vertices. You could even update the Add-On and release it back to the Blender community.

Recently I read an inspirational post on how to create stereographic lampshades. I noticed the author created the model in MATLAB and then rendered the lampshades in Blender. I feel like Blender on its own provides all the features necessary to create stereographic lampshades, so I set out to write a script to do just that.

rendered lampshade

My script is [here] on Github.

Check it out!

I created a SVG pattern modified from a public domain snowflake on Pixabay.

snow pattern

Let it snow!

Then I imported the SVG into Blender and ran my script. My script converted the SVG into a mesh and transformed each vertex according to the post’s instructions.

I admit I cheated a little. In the original post, the input to the lampshade creation was a raster image. I instead jumped straight into vector images with SVGs. Since Blender can manipulate SVGs easily and there’re good tools on converting raster images to SVGs, I felt no need to reinvent the wheel.

There are two things that were non-obvious to me initially for computing the new vertices’ coordinates. First, all angles were supposed to be in radians. Luckily the default math functions like sin() and cos()  operate in radians, so that wasn’t a big trip-up. Second was that the default arctangent function atan() only returned a compressed range which resulted in a super collapsed/deformed hemispherical blob. I needed to use atan2() to get the full range. A fuller explanation can be found on Wikipedia.

After transforming each vertex the resulting mesh is a non-manifold surface. It’s basically a sheet with infinite thinness, which isn’t 3D printable.

My first instinct was to use Blender’s solidify modifier to turn the surface into a solid, but it didn’t behave like I expected. I ended up choosing extrude and scaling the extruded faces. Since I scaled proportionally to the thickness of the extrusion, I hope that the image projected by my lampshade won’t be distorted.

Since I don’t own a 3D printer, I sent my model to a service for printing. I’ll update with my results (hopefully it comes before holiday time!)

To print your own, you need an SVG (you can convert raster images to SVGs using Inkscape). I’ve provided the snowflake pattern as well as the final STL I used in the Github repository.

imported SVG

After you import the SVG into Blender, open up my script in the the Blender Text Editor.

hit Run Script!

Select the SVG and hit Run Script.

final object

You might need to rescale to the desired print size.

Have fun! Feedback welcome!

Click for Update

Success!

The Blender API is a little quirky. You have to either be in OBJECT mode or use bmesh to select or deselect vertices and edges.

Let’s start with a cube, in EDIT mode, with everything deselected.

start box

To select a vertex, one way is to change to OBJECT mode, select the vertex, and change back to EDIT mode.

#change to OBJECT mode
bpy.ops.object.mode_set(mode="OBJECT")

#deselect the 0th vertex
bpy.data.meshes['Cube'].vertices[0].select = True

#change to back to EDIT mode
bpy.ops.object.mode_set(mode="EDIT")

selected vertex

Deselect works very similarly:

bpy.ops.object.mode_set(mode="OBJECT")

#deselect the 0th vertex
bpy.data.meshes['Cube'].vertices[0].select = False

bpy.ops.object.mode_set(mode="EDIT")

And we’re back to the start.

start box

Selecting an edge uses the same principle:

bpy.ops.object.mode_set(mode="OBJECT")

#select the 0th edge
bpy.data.meshes['Cube'].edge[0].select = True

bpy.ops.object.mode_set(mode="EDIT")

selected edge

Deselecting that edge is quite a bit different. You can’t just use:

bpy.data.meshes['Cube'].edge[0].select =False

You have to deselect the vertices of an edge first, and then deselect the edge.

bpy.ops.object.mode_set(mode="OBJECT")

bpy.data.meshes['Cube'].vertices[0].select = False
bpy.data.meshes['Cube'].vertices[1].select = False
bpy.data.meshes['Cube'].edges[0].select = False

bpy.ops.object.mode_set(mode="EDIT")

Occasionally, when switching the mode to deselect doesn’t work (like if you have non-manifold edges selected), or if you want to stay in edit mode, you can use bmesh.

Let’s start with a box with once face removed and non-manifold edges selected
( bpy.ops.mesh.select_non_manifold())

non manifold edges

bpy.ops.object.mode_set(mode="EDIT")
mesh = bmesh.from_edit_mesh(bpy.context.object.data)
mesh.verts[2].select = False #the vertices are indexed differently
mesh.verts[3].select = False
mesh.edges[0].select = False

deselected edge for non manifold

Ta da! The bottom edge is deselected.

Blender 2.71