This thread is out-dated. Please use this one.Hi there
Introduction:we already know that using few, large textures with multiple sprite animation frames is more efficient than using many small textures with single frames. I was looking - I guess as most other hobby developers, too - for suitable sprite graphics. Some assets are using the frameset-approach (multiple frames within a larger texture), some are using single files per frame. But if you want to avoid using a mixture of both approaches, you are forced to convert all graphics to one format either used by the first or used by the second approach.
But simple merging (potentially diffently sized!) frames to a set of uniform sized frames isn't great. It implies some wasted memory (because of many empty/transparent pixels inside the image). On the other hand, merging them memory-efficient by hand isn't great either. To solve this, I wrote some piece of code
How it's working:The idea is straight-forward: try to find an empty spot for your images. But this implies some more work! The basic idea is to add lots of images to some kind of container. On each insertion the container (with a fixed size) returns whether it had free space. If yes, the image was added and it's offset (topleft position within the container) will be returned call-by-reference to the second argument given to add.
Here's a small (runnable) example code.. It's creating some random single-colored images with randomized size, sorting it by the images' size (desceding) and adding largest images first. After that, the final image is written to disk as example usage.
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <utils/image_atlas.hpp> // <-- see below
int main() {
std::srand(std::time(0));
// create images --- ugly - feel free to skip while reading^^
std::size_t num = 40;
std::vector<sf::Image> images{num};
sf::Uint8 k = 0;
int i = 0;
for (auto& img: images) {
sf::Color c;
if (i < num/3) {
c = sf::Color{k, 0, 0};
} else if (i < 2*num/3) {
c = sf::Color{0, k, 0};
} else {
c = sf::Color{0, 0, k};
}
img.create(40 * (std::rand() % 10 + 1), 40 * (std::rand() % 10 + 1), c);
k += (255/(num/3));
++i;
if (k > 200) {
k = 40;
}
}
// sort by size (descending)
// --> helps to achieve a (at least nearly) well-constructed atlas
std::sort(images.begin(), images.end(), [&](sf::Image const & a, sf::Image const & b) {
auto size_a = a.getSize();
auto size_b = b.getSize();
return size_a.x * size_a.y > size_b.x * size_b.y;
});
// add to ImageAtlas -- here the actual work starts
ImageAtlas target{1024u}; // or use default ctor for max size
for (auto const & img: images) {
sf::Vector2u pos;
if (target.add(img, pos)) {
auto size = img.getSize();
std::cout << "Added image sized " << size.x << "x" << size.y
<< " added at (" << pos.x << "|" << pos.y << ")\n";
}
}
// generate final image
auto result = target.generate();
result.saveToFile("out.png");
}
By the way: The included header for the image_atlas can be found here:
https://github.com/cgloeckner/SfmlExtWhat to do with?The image atlas can be create once for the current machine (atlas' size might depend on the hardware's limitations), write it to disk and load it when starting the game. Of course you'll also need to store those initial-image-and-its-offset-relations to keep atlas and mapping consistent.
Possibly, the (resulting) texture and rectangles (computed offset with initial frame's size) could be used by Thor's animation handling system
About the code:It poorly documented, yet. Also some approaches (e.g. searching a free spot) are not that great... But it's working in the first place
Please feel free to feedback!
Kind regards
Glocke
/EDIT: Some productive example: These offsets should be stored (mapping!) for the example frameset (graphics taken from "Battle for Wesnoth")
Added image sized 72x102 added at (0|0)
Added image sized 72x102 added at (72|0)
Added image sized 72x102 added at (144|0)
Added image sized 72x102 added at (216|0)
Added image sized 72x102 added at (288|0)
Added image sized 72x102 added at (360|0)
Added image sized 72x102 added at (432|0)
Added image sized 72x100 added at (0|102)
Added image sized 72x100 added at (72|102)
Added image sized 72x100 added at (144|102)
Added image sized 72x100 added at (216|102)
Added image sized 94x76 added at (288|102)
Added image sized 94x76 added at (382|102)
Added image sized 82x72 added at (288|178)
Added image sized 78x72 added at (370|178)
Added image sized 72x72 added at (0|202)
Added image sized 72x72 added at (72|202)
Added image sized 72x72 added at (144|202)
Added image sized 72x72 added at (216|202)
Added image sized 72x72 added at (288|250)
Added image sized 72x72 added at (360|250)
Added image sized 72x72 added at (432|250)
Added image sized 72x72 added at (0|274)
Added image sized 72x72 added at (72|274)
Added image sized 72x72 added at (144|274)
Added image sized 72x72 added at (216|274)
Added image sized 72x72 added at (288|322)
Added image sized 72x72 added at (360|322)
Added image sized 72x72 added at (432|322)
Added image sized 72x72 added at (0|346)