Hello.
I've tested vertex array as explained in this article and works fine. But, when the array size is so big, FPS drops.
I'm not going to use such size of array, but I'm curious of how to do it correctly.
That's the code:
#include <SFML/Graphics.hpp>
#include <time.h>
#include <cstdio>
const int tilesX = 1000;
const int tilesY = 1000;
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
bool load(const std::string& tileset, sf::Vector2u tileSize, const int* tiles, unsigned int width, unsigned int height)
{
// load the tileset texture
if (!m_tileset.loadFromFile(tileset))
return false;
// resize the vertex array to fit the level size
m_vertices = new sf::Vertex [width * height * 4];
// populate the vertex array, with one quad per tile
for (unsigned int i = 0; i < width; ++i)
for (unsigned int j = 0; j < height; ++j)
{
// get the current tile number
int tileNumber = tiles[i + j * width];
// find its position in the tileset texture
int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);
int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
// get a pointer to the current tile's quad
sf::Vertex* quad = &m_vertices[(i + j * width) * 4];
// define its 4 corners
quad[0].position = sf::Vector2f(i * tileSize.x, j * tileSize.y);
quad[1].position = sf::Vector2f((i + 1) * tileSize.x, j * tileSize.y);
quad[2].position = sf::Vector2f((i + 1) * tileSize.x, (j + 1) * tileSize.y);
quad[3].position = sf::Vector2f(i * tileSize.x, (j + 1) * tileSize.y);
// define its 4 texture coordinates
quad[0].texCoords = sf::Vector2f(tu * tileSize.x, tv * tileSize.y);
quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tv * tileSize.y);
quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, (tv + 1) * tileSize.y);
quad[3].texCoords = sf::Vector2f(tu * tileSize.x, (tv + 1) * tileSize.y);
}
return true;
}
private:
virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
{
// apply the transform
states.transform *= getTransform();
// apply the tileset texture
states.texture = &m_tileset;
// draw the vertex array
target.draw(m_vertices, tilesX * tilesY * 4, sf::Quads, states);
}
sf::Vertex *m_vertices;
sf::Texture m_tileset;
};
int main()
{
// create the window
sf::RenderWindow window(sf::VideoMode(800, 600), "Tilemap");
// define the level with an array of tile indices
int *level = new int[tilesX*tilesY];
for( int i = tilesX*tilesY-1; i>=0; i--)
{
level[i] = rand()%12;
}
// create the tilemap from the level definition
TileMap map;
if (!map.load("tileset.png", sf::Vector2u(32, 32), level, tilesX, tilesY))
return -1;
sf::Text text;
sf::Font font;
font.loadFromFile("consola.ttf");
text.setFont(font); // font is a sf::Font
text.setString("FPS: wait...");
text.setCharacterSize(24); // in pixels, not points!
text.setColor(sf::Color::Red);
text.setStyle(sf::Text::Bold | sf::Text::Underlined);
sf::View vistaPj = window.getView();
sf::Vector2f centerPj = vistaPj.getCenter();
int frames = 0;
int frameTime = time(0);
// run the main loop
while (window.isOpen())
{
// handle events
sf::Event event;
while (window.pollEvent(event))
{
if(event.type == sf::Event::Closed)
window.close();
}
// draw the map
window.clear();
window.draw(map);
if( frameTime != time(0))
{
static char tmp[256];
snprintf(tmp, 256, "FPS: %d", frames);
text.setString(tmp);
frames = 0;
frameTime = time(0);
}
window.draw(text);
window.display();
frames++;
}
return 0;
}
For it to work, you'll need a Tileset, for example:
https://vxresource.files.wordpress.com/2011/05/tileeyl5.png (don't forget to rename it "tileset.png" and place it at correct folder) And "console.tff" font to see FPS count.
As you can see, I've changed VertexArray to a simple Vertex[]. I did it just for testing another draw() function that gives to me more control about the chunk of vertex to draw.
Low FPS are quite normal as draw() must iterate through all vertex, searching for the vertex ones to be drawn.
I think that the solution is to call one draw() per tile line, with the vertex pointer and size up to view possition.
What do you think?