SFML community forums

Help => Graphics => Topic started by: AndrewB on August 11, 2012, 11:22:40 am

Title: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 11:22:40 am
Hey everyone,

I'm trying to copy a 25x25 pixel area from a master textures file and when I try and run my game this error appears in the output:
(http://i.imgur.com/e3MeK.png)

This is the code that I'm using:
  sf::Image tiles;
  tiles.loadFromFile("Resources/tiles.tga");

  sf::Image img;
  img.copy(tiles, 0, 0, sf::IntRect(0, 0, 25, 25)); // extracts the first 25x25 tile from "tiles"
 
  sf::Texture tex;
  tex.loadFromImage(img); // loads the texture from "img" which is just the first 25x25 tile

Is there any reason that I'm not seeing that is causing this?

Thanks,
Andrew
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: zsbzsb on August 11, 2012, 11:56:53 am
http://sfml-dev.org/features.php
Quote
Can load standard font file formats : ttf, cff, pcf, fnt, bdf, pfr, sfnt, type 1, type 42

Have you tried loading a *.png file?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 12:05:20 pm
Quote
The supported image formats are bmp, png, tga, jpg, gif, psd, hdr and pic. Some format options are not supported, like progressive jpeg. If this function fails, the image is left unchanged.

http://www.sfml-dev.org/documentation/2.0/classsf_1_1Image.php#a9e4f2aa8e36d0cabde5ed5a4ef80290b
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 12:36:58 pm
Problem solved for now - I've cut out the second img and just loaded the texture using sf::Texture.loadFromImage(src, area);
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 03:04:22 pm
You'll be having same pixels in memory few times if you do what you did in first post, sprites have subRects you can set to make them use just part of texture.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 03:11:42 pm
You'll be having same pixels in memory few times if you do what you did in first post, sprites have subRects you can set to make them use just part of texture.

See: http://en.sfml-dev.org/forums/index.php?topic=8844.msg59455#msg59455

Is that what you meant?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: Laurent on August 11, 2012, 05:05:57 pm
Before copying "tiles" to "img", "img" must be created (with a valid size). You can't copy an image to an empty one, it doesn't expand automatically.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 05:20:27 pm
Before copying "tiles" to "img", "img" must be created (with a valid size). You can't copy an image to an empty one, it doesn't expand automatically.

Thanks Laurent.

I have changed the way I load the textures, removing the need for the new sf::Image.
void loadTextures(std::vector<sf::Texture>& texture_list) {
  sf::Image master;
  master.loadFromFile("Resources/tiles.tga");

  for (int x = 0; x < ps::ktexture_grid_size; x++) {
    for (int y = 0; y < ps::ktexture_grid_size; y++) {
      sf::Texture temp_texture;
      temp_texture.loadFromImage(master, sf::IntRect(x * ps::ktexture_size, y * ps::ktexture_size, ps::ktexture_size, ps::ktexture_size));
      texture_list.push_back(temp_texture);
    }; // for y
  }; // for x
} // void loadtextures
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 05:24:18 pm
Instead of:
sf::Image tiles;
  tiles.loadFromFile("Resources/tiles.tga");
  sf::Image img;
  img.create(25,25);
  img.copy(tiles, 0, 0, sf::IntRect(0, 0, 25, 25));
  sf::Texture tex;
  tex.loadFromImage(img);
  sf::Sprite spr(tex);
You can do:
  sf::Texture tiles;
  tiles.loadFromFile("Resources/tiles.tga");
  sf::Sprite spr(tiles);
  spr.setTextureRect(sf::IntRect(0,0,25,25));

Why do you 'extract' tiles from tilesheet anyway?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 05:27:31 pm
Instead of:
sf::Image tiles;
  tiles.loadFromFile("Resources/tiles.tga");
  sf::Image img;
  img.create(25,25);
  img.copy(tiles, 0, 0, sf::IntRect(0, 0, 25, 25));
  sf::Texture tex;
  tex.loadFromImage(img);
  sf::Sprite spr(tex);
You can do:
  sf::Texture tiles;
  tiles.loadFromFile("Resources/tiles.tga");
  sf::Sprite spr(tiles);
  spr.setTextureRect(sf::IntRect(0,0,25,25));

Why do you 'extract' tiles from tilesheet anyway?

That's the method that I'm using now (see above post).

I'm want to keep the tiles in a single image so it's easier to update/change any of the tiles while developing and post-release. Plus it keeps my resources file clean.

Edit: Oh no, I just re-read what you posted. Would that method not keep the entire tiles.tga file loaded for every texture though?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 05:30:25 pm
That's great and that's why tilesheets are good but why do you 'extract' them into single texture each at runtime instead of using sf::VertexArray or sf::Sprite::setTextureRect ?
Load your texture once and then make every sprite/your one huge vertex array use this texture with proper rectangle/coords.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 05:42:14 pm
That's great and that's why tilesheets are good but why do you 'extract' them into single texture each at runtime instead of using sf::VertexArray or sf::Sprite::setTextureRect ?
Load your texture once and then make every sprite/your one huge vertex array use this texture with proper rectangle/coords.

void loadTextures(std::vector<sf::Texture>& texture_list) {
  sf::Image master;
  master.loadFromFile("Resources/tiles.tga");

  for (int x = 0; x < ps::ktexture_grid_size; x++) {
    for (int y = 0; y < ps::ktexture_grid_size; y++) {
      sf::Texture temp_texture;
      temp_texture.loadFromImage(master, sf::IntRect(x * ps::ktexture_size, y * ps::ktexture_size, ps::ktexture_size, ps::ktexture_size));
      texture_list.push_back(temp_texture);
    }; // for y
  }; // for x
} // void loadtextures

The tiles.tga file is loaded into memory, each texture is then created and stored on the gpu and then the image storing tiles.tga is freed.

From the definition of setTextureRect on the sfml documentation, EVERY texture would have to store the WHOLE tiles.tga image on the GPU (using 225 times more gpu memory than loading the textures individually). setTextureRect just modifies which part of the texture that will be drawn, it still keeps the whole texture in memory.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 05:47:31 pm
What? :o
Where is that stated?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 05:52:35 pm
What? :o
Where is that stated?

http://www.sfml-dev.org/documentation/2.0/classsf_1_1Sprite.php#a3fefec419a4e6a90c0fd54c793d82ec2
Quote
Set the sub-rectangle of the texture that the sprite will display.

The texture rect is useful when you don't want to display the whole texture, but rather a part of it. By default, the texture rect covers the entire texture.

That wording suggests that it's only changing what is going to be displayed.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 05:56:58 pm
You can have ONE and only ONE sf::Texture and assign it to each of 225 sf::Sprite s and then set each Sprite's rectangle.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 06:01:16 pm
You can have ONE and only ONE sf::Texture and assign it to each of 225 sf::Sprite s and then set each Sprite's rectangle.

By doing this:
  sf::Texture tiles;
  tiles.loadFromFile("Resources/tiles.tga");
  sf::Sprite spr(tiles);
  spr.setTextureRect(sf::IntRect(0,0,25,25));

Each sprite loaded in memory will be storing the entire tiles.tga image.
EDIT: Just to clarify, the spr sprite in the code above will still be storing the same amount of data on the GPU both BEFORE and AFTER setTextureRect is called.

Could @Laurent please clarify if setTextureRect() frees up the memory of the unused portions of the sprites.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 06:05:32 pm
This was an example.
Obviously no one does that.
sf::Texture tex;
tex.loadFromFile("whatever.tga");
std::vector<sf::Sprite> sprites;
for (int it=0;i<666;++it)
{
sprites.push_back(sf::Sprite(tex));
sprites[i].setTextureRect(insert rectangle here);
}
 
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 11, 2012, 06:08:33 pm
This was an example.
Obviously no one does that.
sf::Texture tex;
tex.loadFromFile("whatever.tga");
std::vector<sf::Sprite> sprites;
for (int it=0;i<666;++it)
{
sprites.push_back(sf::Sprite(tex));
sprites[i].setTextureRect(insert rectangle here);
}
 

This code would still store the entire texture file with every new sprite created (from what the description of setTextureRect says). See the function that I posted a few messages back, that's what I'm using. It's basically what you just wrote without using the setTextureRect.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: FRex on August 11, 2012, 06:12:08 pm
This code is storing texture once. Amount of sprites doesn't affect gpu memory. Sprites don't store textures, your code does. Sprites hold reference to texture and if texture gets out of scope they won't display properly anymore.
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: Laurent on August 11, 2012, 08:21:27 pm
If every sprite had to make a copy of its texture, it wouldn't make sense to have this sprite/texture separation. It allows to share a single texture among many sprites, which saves video memory and improves performances.

I think the sprite tutorial is pretty clear about that, isn't it?
Title: Re: Slicing/extracting tiles from a grid of tiles
Post by: AndrewB on August 12, 2012, 06:20:25 am
Yes it is haha that's my bad, I apologize.

For some reason it was stuck in my head that sprites and textures were the same thing even though I was arguing differently.