Welcome, Guest. Please login or register. Did you miss your activation email?

Author Topic: Too big textures...  (Read 3777 times)

0 Members and 1 Guest are viewing this topic.

Jimmyee

  • Newbie
  • *
  • Posts: 21
    • View Profile
Too big textures...
« on: August 06, 2013, 08:03:24 pm »
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++;
            }
        }
    }
}
 
« Last Edit: August 06, 2013, 08:05:52 pm by Jimmyee »

Gobbles

  • Full Member
  • ***
  • Posts: 132
    • View Profile
    • Email
Re: Too big textures...
« Reply #1 on: August 06, 2013, 08:12:56 pm »
How big is the map? Is it much larger then what your camera can see?

If this is the case try to only retrieve what your camera can see and just draw that, as there's no point in drawing images that are 2000 pixels off screen.

Jimmyee

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: Too big textures...
« Reply #2 on: August 06, 2013, 08:16:33 pm »
How big is the map? Is it much larger then what your camera can see?

If this is the case try to only retrieve what your camera can see and just draw that, as there's no point in drawing images that are 2000 pixels off screen.

Of course it is larger ;) So, I will have to draw each object (not a tile, object is my second map layer) separately? Won't it slow my game down much? I think there can be like 400 of them at once.

Also, I forgot to tell you that "objects" is just another map layer for me :P They are static objects.

KraHen

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Too big textures...
« Reply #3 on: August 06, 2013, 08:28:08 pm »
What about chopping that big texture that`s becoming a problem into smaller ones, like 1024*1024 or whatever works for you? And have some sort of container besides those containing what is where, then referencing that through it? Just my 2 cents tho.

Jimmyee

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: Too big textures...
« Reply #4 on: August 06, 2013, 08:31:57 pm »
What about chopping that big texture that`s becoming a problem into smaller ones, like 1024*1024 or whatever works for you? And have some sort of container besides those containing what is where, then referencing that through it? Just my 2 cents tho.

Yeah, this is the best solution I found so far. I was just not sure if it's a good way :P

KraHen

  • Newbie
  • *
  • Posts: 10
    • View Profile
Re: Too big textures...
« Reply #5 on: August 06, 2013, 10:50:42 pm »
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. :)

Jimmyee

  • Newbie
  • *
  • Posts: 21
    • View Profile
Re: Too big textures...
« Reply #6 on: August 07, 2013, 10:22:59 pm »
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,
Quote
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?
« Last Edit: August 07, 2013, 10:29:57 pm by Jimmyee »