I tried this. It was hard, but I did it.
Left is GIMP, top-right is a Box2D debug renderer, bottom-right is a build shell
Code repository for reference
The full code is on github here. It's scattered in a whole lot of files, so it's a bit big to put here. See below for an explanation of the technique.
How?
I used ImageMagick, Potrace, Node.js (with poly2tri.js and simplify-js), and two evenings.
This is possible also with other tools, so I'll go over the steps in a general way:
Draw an input image.
Extract the alpha channel, because we only care about the shape.
convert infile.png -alpha extract outfile.png
Threshold the alpha (push everything under 75% opacity to 0% and the rest to 100%)
convert infile.png -threshold 75% -negate outfile.png
This gets rid of the barely visible parts.
I also negated the image because the next step's Potrace understands black pixels as stuff it should be tracing.
Trace to a vector format.
With the right options, Potrace outputs an easily readable PostScript file.
potrace --longcoding --postscript --tight --alphamax=0 \
--cleartext --output=outfile.ps infile.bmp
I wrote a fairly uninteresting Node.js script to parse the path data out of that and into a JSON data structure.
I threw the JSON at D3.js to see what the path data looks like. Sure enough…
Simplify and triangulate the path.
Again a pretty simple Node.js call through simplify, then poly2tri.
Put it in Box2D!
Create a body for each set of triangles, and add the triangles as fixtures. Remember to handle each triangle's vertices in counter-clockwise order.
Success!
Shapes with holes in them
Sometimes your images have holes, like the One Ring here—
Thanks user Xander at Wikimedia!
The PostScript path data represents such situations as a sequence of paths (rlineto
/moveto
-commands followed by a closepath
), such that the first path is the enclosing one, and all the rest are holes. That group of paths is then terminated by a fill
command.
It looks somewhat like this:
<x> <y> moveto
<x> <y> rlineto
<x> <y> rlineto
[ ... more rlineto ... ]
closepath
<x> <y> moveto
<x> <y> rlineto
<x> <y> rlineto
[ ... more rlineto ... ]
closepath
fill
Here's how it looks when parsed, simplified, and plotted:
The blue path is the enclosing one. The red is the hole.
An image might have arbitrarily many holes:
→
poly2tri has methods that allow you to add holes to the generated path, as any other Delaunay triangulation library worth its salt should.
And finally, the ring in Box2D for good measure: