I've got a problem while rendering a 2D map which is drawn using vertex arrays; first let me explain my idea:
I want to draw objects on the map. They have irregular size and are stored in separate graphic files. I need to load them by ID (ID is loaded from a map file).
I also want to have them all in one texture, so I will call the draw function only once.
Until now, my game was creating a virtual image and putting them all in it. It was also storing an information about each object size and possition on the "big texture".
The problem is that when I added more objects there were too many of them and the texture was too big (it gives me SFML error).
My question is: is there any other way to manage graphics like that? I don't want to call draw() too often :S
The only idea that comes to my mind is to create just another texture and put the rest of the objects there. Here's a code I wrote a while ago. It doesn't work but I hope it will show you what I mean...
void SpriteSheet::mergeImages(GFXLoader::Directory &dir)
{
sf::Vector2f totalTextureSize;
sf::Vector2f currentPossition;
for(auto &img: dir.img)
{
unsigned int w = img.second.getSize().x;
unsigned int h = img.second.getSize().y;
if(currentPossition.x + w < sf::Texture::getMaximumSize())
{
currentPossition.x += w;
if(totalTextureSize.x < currentPossition.x) totalTextureSize.x = currentPossition.x;
}
else
{
currentPossition.x = 0;
currentPossition.y += h;
if(totalTextureSize.y < currentPossition.y) totalTextureSize.y = currentPossition.y;
}
if(totalTextureSize.y < h) totalTextureSize.y = h;
}
unsigned int img_w = totalTextureSize.x > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : totalTextureSize.x;
unsigned int img_h = totalTextureSize.y > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : totalTextureSize.y;
sf::Image bigImage;
bigImage.create(img_w, img_h, sf::Color(0, 0, 0));
unsigned int textureCount = 0;
unsigned int maxHeight = 0;
unsigned int sprId = 0;
sf::Vector2f remaining;
remaining.x = totalTextureSize.x;
remaining.y = totalTextureSize.y;
currentPossition.x = 0;
currentPossition.y = 0;
for(auto &img: dir.img)
{
unsigned int w = img.second.getSize().x;
unsigned int h = img.second.getSize().y;
SpriteInfo sprInfo;
sprInfo.textureId = textureCount;
if(h > maxHeight) maxHeight = h;
if(currentPossition.x + w < sf::Texture::getMaximumSize())
{
bigImage.copy(img.second, currentPossition.x, currentPossition.x, sf::IntRect(0, 0, w, h));
sprInfo.rect = sf::IntRect(currentPossition.x, currentPossition.x, w, h);
sprites[sprId] = sprInfo;
currentPossition.x += w;
sprId++;
}
else if(currentPossition.y + h < sf::Texture::getMaximumSize())
{
currentPossition.x = 0;
currentPossition.y += maxHeight;
remaining.y -= maxHeight;
bigImage.copy(img.second, currentPossition.x, currentPossition.y, sf::IntRect(0, 0, w, h));
sprInfo.rect = sf::IntRect(currentPossition.x, currentPossition.y, w, h);
sprites[sprId] = sprInfo;
sprId++;
}
else
{
images.push_back(bigImage);
sf::Texture tex;
tex.loadFromImage(images[images.size() - 1]);
textures.push_back(tex);
currentPossition.x = 0;
currentPossition.y = 0;
if(remaining.y > 0)
{
img_w = totalTextureSize.x > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : totalTextureSize.x;
img_h = remaining.y > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : remaining.y;
bigImage.create(img_w, img_h, sf::Color(0, 0, 0));
textureCount++;
}
}
}
}
That's good idea of course :) This is what works for me right now:
#include "spritesheet.hpp"
SpriteSheet::BigImage::BigImage(sf::Vector2f img_size)
{
create(img_size.x, img_size.y, sf::Color(0, 0, 0));
xRemaining = img_size.x;
yRemaining = img_size.y;
}
std::shared_ptr<sf::IntRect> SpriteSheet::BigImage::addImage(sf::Image &img)
{
unsigned int w = img.getSize().x;
unsigned int h = img.getSize().y;
unsigned int x = getSize().x - xRemaining;
unsigned int y = getSize().y - yRemaining;
if(x < getSize().x)
{
xRemaining -= w;
}
else if(y < getSize().y)
{
xRemaining = getSize().x;
yRemaining -= h;
}
else return std::shared_ptr<sf::IntRect>(0);
copy(img, x, y, sf::IntRect(0, 0, w, h));
return std::shared_ptr<sf::IntRect>(new sf::IntRect(x, y, w, h));
}
void SpriteSheet::load(std::shared_ptr<GFXLoader> gfxLoader, int id)
{
//texture = std::shared_ptr<sf::Texture>(&gfxLoader->dir["map/"].tex[id]);
}
sf::Vector2f SpriteSheet::getImageSize(GFXLoader::Directory &dir)
{
sf::Vector2f ret(0, 0);
unsigned int x = 0;
unsigned int y = 0;
unsigned int maxh = 0;
for(auto &img: dir.img)
{
unsigned int w = img.second.getSize().x;
unsigned int h = img.second.getSize().y;
if(h > maxh) maxh = h;
if(x + w < sf::Texture::getMaximumSize())
{
x += w;
if(x > ret.x) ret.x = x;
}
else
{
x = 0;
y += maxh;
maxh = 0;
}
}
ret.y = y;
return ret;
}
void SpriteSheet::mergeImages(GFXLoader::Directory &dir)
{
sf::Vector2f texSize = getImageSize(dir);
printf("total texture size: %fx%f\n", texSize.x, texSize.y);
unsigned int img_w = texSize.x > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : texSize.x;
unsigned int img_h = texSize.y > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : texSize.y;
BigImage bigImg(sf::Vector2f(img_w, img_h));
unsigned int textureId = 0;
unsigned int spriteId = 0;
unsigned int totalYSize = 0;
for(auto &img: dir.img)
{
std::shared_ptr<sf::IntRect> rect = bigImg.addImage(img.second);
if(rect.get())
{
SpriteInfo sprInfo(*rect.get());
sprInfo.textureId = textureId;
sprites[spriteId] = std::shared_ptr<SpriteInfo>(new SpriteInfo(sprInfo));
spriteId++;
//sprites.insert(std::make_pair(spriteId++, sprInfo));
}
else
{
images.push_back(bigImg);
totalYSize += bigImg.getSize().y;
textureId++;
sf::Texture tex;
tex.loadFromImage(images[images.size() - 1]);
textures.push_back(tex);
unsigned int img_w = texSize.x > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : texSize.x;
unsigned int img_h = texSize.y - totalYSize > sf::Texture::getMaximumSize()?
sf::Texture::getMaximumSize() : texSize.y - totalYSize;
bigImg = BigImage(sf::Vector2f(img_w, img_h));
rect = bigImg.addImage(img.second);
if(rect.get())
{
SpriteInfo sprInfo(*rect.get());
sprInfo.textureId = textureId;
sprites[spriteId] = std::shared_ptr<SpriteInfo>(new SpriteInfo(sprInfo));
spriteId++;
//sprites.insert(std::make_pair(spriteId++, sprInfo));
}
else puts("fatal error: could not create sprite sheet");
}
}
images.push_back(bigImg);
sf::Texture tex;
tex.loadFromImage(images[images.size() - 1]);
textures.push_back(tex);
}
I didn't rewrite the Map::draw() function but seems like it will be more complicated now.
Also,
If I were you I`d implement a streaming system on a separate thread, and implement the offscreen big texture essentially like a queue, so what`s needed would replace the one that`s not, if this is possible in your context then it would be a nice addition in terms of memory, and perhaps speed. :)
can you explain me little more what do you mean?