Saturday, September 12, 2015

c++ - How do I turn an image into a Box2D physics object?


I'm using Box2D for a Game Programming course, and I was wondering if it is possible to choose the best shape and size of shape for the rigidbody/fixture for a sprite based on its transparency.


I've tried to build with the idea of new content being easy to add (all data for enemies, terrain, etc. is stored in XML documents). Because of this, there will not be a new class for every enemy. This makes creating the fixture shapes difficult, since it has to be a generic solution but still map realistically to the sprite when the game is being played. The best solution I've come up with so far is to somehow figure out where the majority of the non transparent pixels are in the sprite and build the shape from that.


Does something like this exist? If not, would it be hard to implement?



Answer



I tried this. It was hard, but I did it.



drawing a thing, getting an object


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.



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:




  1. Draw an input image.



    sword and magical girl wand




  2. Extract the alpha channel, because we only care about the shape.


    alpha channel


    convert infile.png -alpha extract outfile.png


  3. Threshold the alpha (push everything under 75% opacity to 0% and the rest to 100%)


    thresholded inverted alpha channel



    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.




  4. 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…


    path data




  5. Simplify and triangulate the path.


    Again a pretty simple Node.js call through simplify, then poly2tri.





  6. 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.


    Box2D bodies!


    Success!





Sometimes your images have holes, like the One Ring here—


THE RING. No, the other ring.


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:


  moveto
rlineto
rlineto
[ ... more rlineto ... ]
closepath
moveto
rlineto
rlineto

[ ... more rlineto ... ]
closepath
fill

Here's how it looks when parsed, simplified, and plotted:


image paths


The blue path is the enclosing one. The red is the hole.


An image might have arbitrarily many holes:


plussed circleplussed circle 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:


The One Ring in Box2D


No comments:

Post a Comment

Simple past, Present perfect Past perfect

Can you tell me which form of the following sentences is the correct one please? Imagine two friends discussing the gym... I was in a good s...