I am trying to make a perfectly optimized Sprite atlas. That means, given any collection of small sprites, I want to find (very quickly) a good way to cram them all into a big image with minimal waste. This essentially the same problem as the 2D cutting stock problem, for which I found this paper: http://www.inf.uos.de/papers_html/or_94/cutpaper.html . However, I don't want to be able to rotate my sprites 90º, so this algorithm doesn't fit my desires exactly.

So, I was wondering 2 things:1) Has anyone implemented this Java before? If so, would you be willing to share source so I can save myself time?or2) Does anyone have ideas on a good way to do this, given that rotations are not allowed? Genetic and other learning algorithms won't really do the trick because they take a long time to get up to speed. At most, I want a wait of about 10 seconds for a 1024x1024 Atlas.

In OpenGL you will have exactly the same performance.In Java2D you can just 'extract' your atlas sprites once.

But heck, you might have a very good reason

It's all about time. These are images that exist already in an engine that exists already in a project that is supposed to be done in 10 days. Although it's obviously easy to rotate in OpenGL and there's no performance hit, it means another couple of hours of adjusting the SpriteAtlas code to allow for rotated sprites. I'm just trying to avoid the extra work at this juncture. If that's not an option, then by all means I can go for it.

I am trying to make a perfectly optimized Sprite atlas. That means, given any collection of small sprites, I want to find (very quickly) a good way to cram them all into a big image with minimal waste.

This is basically 2d bin packing which means it's np-hard so you're not going to find a 'perfect' solution other than the brute-force method of trying every possible combination and taking the best one.

I've got a 2d atlas within Rebirth, which is implemented like a container so you could use that if you wanted (check out the Atlas class ).

If you wanted to do it yourself then there's a few different approaches. The one I settled on is to use a 2d bsp to divide the space, if you combine this with inserting rectangles from largest to smallest you get very little wasted space (since the smaller rectangles tend to fill up the awkward holes left by the bigger sprites).

This is basically 2d bin packing which means it's np-hard so you're not going to find a 'perfect' solution other than the brute-force method of trying every possible combination and taking the best one.

I've got a 2d atlas within Rebirth, which is implemented like a container so you could use that if you wanted (check out the Atlas class ).

If you wanted to do it yourself then there's a few different approaches. The one I settled on is to use a 2d bsp to divide the space, if you combine this with inserting rectangles from largest to smallest you get very little wasted space (since the smaller rectangles tend to fill up the awkward holes left by the bigger sprites).

Example atlas:

That looks great, although I couldn't find any source for Atlas to take a look at.

So basically all you do is start with the biggest sprite, make your way to the right with progressively smaller sprites, and simply stick each sprite wherever it will fit at first? And is "2D BSP" a 2D binary-space partitioning tree? I guess the tree is the best way to determine what space is open and what isn't.

That looks great, although I couldn't find any source for Atlas to take a look at.

Rebirth is a binary-only library at the moment until I get some distribution and licensing gubbins sorted out. Something I've been meaning to do for a while...

Quote

So basically all you do is start with the biggest sprite, make your way to the right with progressively smaller sprites, and simply stick each sprite wherever it will fit at first?

It's not quite that simple - basically the tree is a bunch of nodes with a 'first' and 'second' child nodes, which split the parent rect into two (where splitting alternates between horizontal and vertical at each level). To insert you find the first leaf that it can fit within, then split that into two and mark one half as allocated.

Alternative heristics are sorting by largest-edge or sorting by width then height, but I found by area gave the best results for sprite-like rectangles.

Rebirth is a binary-only library at the moment until I get some distribution and licensing gubbins sorted out. Something I've been meaning to do for a while...

It's not quite that simple - basically the tree is a bunch of nodes with a 'first' and 'second' child nodes, which split the parent rect into two (where splitting alternates between horizontal and vertical at each level). To insert you find the first leaf that it can fit within, then split that into two and mark one half as allocated.

Alternative heristics are sorting by largest-edge or sorting by width then height, but I found by area gave the best results for sprite-like rectangles.

Right. I'm trying a sort of quad-tree implementation, like this:

1) If the added rect has a bigger size than this one, return.2) If this one already has something in it, return.3) If this one has no children, ether add directly to this node if the size is an exact match, or split this into 4 nodes, using both the horizontal and the vertical to do so, then add to the top-left node (which will be an exact match in size).4) If this one has children, try to recursively add to each one in turn.

This seems like it should work fairly well and was quick to implement, but I haven't tested it thoroughly yet. It will definitely appears like it will be less effective than alternating between vertical and horizontal, but good enough for my purposes. I'll try the other if this works shittily.

The image packer is useful for anyone, the PackedImages and other classes are specific to the Skorpios Android/desktop OpenGL project, but are easily modified for use elsewhere. Eg, it was very easy to port the PackedImages class to the iPhone. It would be even easier to port to a different OpenGL binding in Java.

It's worth noting that if you've got so many sprites you need multiple sprite sheets, that it's not necessarily the most efficient thing to do to find the best fit in any sheet for any one sprite. Often sprites are used in sequence or in clusters, and if you have each sprite in a sequence spread out over 16 4MB sprite sheets, you run the risk of texture thrashing on lower end cards. Just a thought.

It's worth noting that if you've got so many sprites you need multiple sprite sheets, that it's not necessarily the most efficient thing to do to find the best fit in any sheet for any one sprite. Often sprites are used in sequence or in clusters, and if you have each sprite in a sequence spread out over 16 4MB sprite sheets, you run the risk of texture thrashing on lower end cards. Just a thought.

Cas

Yes indeed. This texture packing is for the iPhone, too, so it is doubly important for me to be efficient here. I am only packing like-images in each sprite sheet, and I have optimized the rendering pipeline so that pretty much everything will be drawn with just two or three texture binds.

Use a Random object and hard-code the seed to get the same random sprites each time.

I meant that I didn't save a seed of those specific objects in the screenshot above, so I can't get those objects back out no matter how hard I try. If I made a new one, then I could store the seed, but then I wouldn't be giving Nate the same images.

Of course, you need a nice Image class. This doesn't preserve the order that the Java PackedImages class does. The nice part is the upper left of the image before transparency is stripped is preserved.

I was mostly just curious as to how well the algorithm I used packed something similar to your screenshots.

Most animations have transparency that can be stripped off to save space. This is nice to get more sprites per texture and especially important if you are using PVRTC on the iPhone.

Parsing my tools output on the iPhone goes like this:

Of course, you need a nice Image class. This doesn't preserve the order that the Java PackedImages class does. The nice part is the upper left of the image before transparency is stripped is preserved.

My tool already strips transparency. It also gives you the option to force every sprite to be a square, if you so desire, and also saves an animation and atlas file detailing the data in the sheet. Perfect for my exact purpose. I've already got several sprite loaders, atlas loaders, and animation loaders written in ObjC.

This stuff's pretty cool, I heard that texture binds are expensive so these tools are very useful.

By the way Nate, I think Demonpants/Eli must have had a bad day, he's not an egotist. Like you, he's helped me a lot with code and pointers plenty of times

You got it. I did indeed have quite a bad day. More a bad week. Or month. Let me put it this way... this game was originally supposed to be released in June. Due to a large variety of factors, including feature creep, having to work on other projects to raise money, employees getting fired and others getting hired, and more, we are still working on this game (although this time I think the Christmas release date is sound). I am supposed to get a pay bump, health insurance, etc. when the game gets released (depending on how well it does), but most importantly I have been busting my ass for months now (when you have a new release date every month, then every month becomes "crunch time" when you are working too many hours a week and tiring yourself and pissing off your fiancée ) trying to hit a release date that inevitably gets put off. So it feels a bit like I've been wasting time and spinning my wheels for this whole time, not getting anything to actually add to my resume. This week was particularly bad because my computer crashed on Monday so I spent the entire day fixing it, then on Wednesday and Thursday I had to go to a bunch of meetings about yet another project that isn't this one.

The stress is pouring out of my ear-holes.

Now as for this texture packing tool, I was indeed thinking I would share my source at some point when I had time to fix it up, given that the examples I've seen have had many more features than appear to be necessary. Mine is very short, and also uses generics so you can use it to pack anything that has a width and a height. The whitespace trimmer I have in there is external to the actual packing - I only put it in because the images I was given for this particular scenario are 128x128 but only use about 64x64. So I just did what I needed to for my scenario - I'm not trying to make something that is publicly useful yet.

java-gaming.org is not responsible for the content posted by its members, including references to external websites,
and other references that may or may not have a relation with our primarily
gaming and game production oriented community.
inquiries and complaints can be sent via email to the info‑account of the
company managing the website of java‑gaming.org