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

Author Topic: Help understanding vertex array tilemap example  (Read 4093 times)

0 Members and 1 Guest are viewing this topic.

Anteara

  • Newbie
  • *
  • Posts: 40
    • View Profile
    • Email
Help understanding vertex array tilemap example
« on: July 03, 2013, 04:20:31 pm »
Hi all, I looked at the vertex array tutorial, and I got the example up and running in regards to the tilemap loader. However, I'm having a little bit of trouble understanding exactly what a few parts of the code do.

First of all, I'm not sure what tu or tv is; but in debugging, I noticed that tu is always equal to tileNumber, and tv is always equal to 0.

I'm also unsure about the parameter list of quad[2] and quad[3]. I'm not sure why it works, unless the tileSize.y refers the bottom of the tile, and not the top.

Anyway, here's my code with comments added in to help me understand.

If possible, could you please read it, and where i have comments tell me how those specific parts work?

Thanks :)!

//Create a class called tilemap, and inheret sf::Drawable and sf::Transformable
//i.e. TileMap class can use the methods from Drawable and Transformable directly.
class TileMap : public sf::Drawable, public sf::Transformable
{
public:
        //load the tilemap.
        //Param1 is the filename.
        //Param2 is the tile size (i.e. 40 by 40).
        //Param 3 is an int array with the level format. Explained later.
        //Param 4 and 5 are the width and height of the level. (Imagine it like a grid; 16 accross, 8 down, etc.)
        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.setPrimitiveType(sf::Quads); //a quad is simply two trianges put together
                m_vertices.resize(width * height * 4); //resize the vertex array so it can hold all the tiles
                //must be * 4 or higher... I assume its bcause there are 4 parts to a quad.

                // populate the vertex array, with one quad per tile
                // loop through the width and height of the level
                for (unsigned int i = 0; i < width; ++i)
                        for (unsigned int j = 0; j < height; ++j)
                        {
                                // get the current tile number
                                //Read TOP to BOTTOM, THEN LEFT TO RIGHT.
                                //Width = 16. Height = 8.
                                //1st element 1st row index = 0 + (0 * 16). 0.
                                //1st element 2nd row index = 1 + (0 * 16). 16.
                                //2nd element 1st row index = 1 + (0 * 16). 1.
                                //2nd element 2nd row index = (1 + (1 * 16). 17.
                                //and so on.
                                int tileNumber = tiles[i + j * width];

                                // find its position in the tileset texture
                                int tu = tileNumber % (m_tileset.getSize().x / tileSize.x); //gets the tile number from top to bottom.
                                //tu could simply be equal to tileNumber
                                tu = tileNumber;
                                int tv = tileNumber / (m_tileset.getSize().x / tileSize.x); //tv appears to be always 0?
                                //tv could simply be equal to 0.

                               
                                std::cout << tu << std::endl;
                                // get a pointer to the current tile's quad
                                //it's a reference to the element inside the m_vertices array, so it also changes m_vertices value.
                                sf::Vertex* quad = &m_vertices[(i + j * width) * 4];

                                // define its 4 corners
                                //Get the positions of each corner, in a clockwise direction.
                                //i and j represent each ENTIRE tile.
                                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);
                                quad[0].texCoords = sf::Vector2f(tu * tileSize.x, 0); //texture starts at top left
                                quad[1].texCoords = sf::Vector2f((tu + 1) * tileSize.x, 0); //texture ends at top right (i.e. top left of next tile, hence the +1)
                                quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tileSize.y); //I'm assuming tileSize.y is the BOTTOM of the tile.
                                quad[3].texCoords = sf::Vector2f(tu * tileSize.x, tileSize.y); //Otherwise, why would it work by simply having tileSize.y?
                        }

                        return true;
        }

private:

        //this virtual function is simply so we don't have to do window.draw(target, states), we can just do window.draw(instance)
        //this is called polymorphism?
        virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const
        {
                // apply the transform
                //this isn't our method, i assume it's something in draw() by default.
                //or this generates the finished quads in one image instead of multiple ones.
                states.transform *= getTransform();

                // apply the tileset texture
                //this puts the texture on to what we're going to draw (which is converted in to a single texture)
                states.texture = &m_tileset;

                // draw the vertex array
                target.draw(m_vertices, states);
        }

        sf::VertexArray m_vertices;
        sf::Texture m_tileset;
};

G.

  • Hero Member
  • *****
  • Posts: 1593
    • View Profile
Re: Help understanding vertex array tilemap example
« Reply #1 on: July 03, 2013, 05:54:43 pm »
Texture coordinates are (by implied convention) expressed as u and v. (like world coordinates are almost always x and y) In SFML u and v are expressed in pixels. (unlike raw OpenGL where they are expressed as % of the texture size)
tu and tv are the tile coordinates of the texture. tu is the column of the tile, and tv its line.
To get the u and v coordinates of the top left corner of the part of the texture you want to use, you multiply tu and tv respectively by the tile width and height. To get the other corners you add tileSize.x or / and tileSize.y.

In the example, tv is always 0 because the map only uses tile numbers from 0 to 3 and there are 16 tiles per line. tu is always tileNumber because you're on the first line. Try with tile numbers greater than 15. ;)

There is an error in your comments
//1st element 2nd row index = 1 + (0 * 16). 16. should be //1st element 2nd row index = 0 + (1 * 16). 16.

quad[0] is the top left corner of the tile
quad[1] is the top right corner
quad[2] is the bottom right corner
quad[3] is the bottom left corner

And yes, the size of your array is * 4 because one quad is made of 4 vertices.
« Last Edit: July 03, 2013, 05:59:24 pm by G. »

Anteara

  • Newbie
  • *
  • Posts: 40
    • View Profile
    • Email
Re: Help understanding vertex array tilemap example
« Reply #2 on: July 03, 2013, 11:16:47 pm »
Texture coordinates are (by implied convention) expressed as u and v. (like world coordinates are almost always x and y) In SFML u and v are expressed in pixels. (unlike raw OpenGL where they are expressed as % of the texture size)
tu and tv are the tile coordinates of the texture. tu is the column of the tile, and tv its line.
To get the u and v coordinates of the top left corner of the part of the texture you want to use, you multiply tu and tv respectively by the tile width and height. To get the other corners you add tileSize.x or / and tileSize.y.

In the example, tv is always 0 because the map only uses tile numbers from 0 to 3 and there are 16 tiles per line. tu is always tileNumber because you're on the first line. Try with tile numbers greater than 15. ;)

There is an error in your comments
//1st element 2nd row index = 1 + (0 * 16). 16. should be //1st element 2nd row index = 0 + (1 * 16). 16.

quad[0] is the top left corner of the tile
quad[1] is the top right corner
quad[2] is the bottom right corner
quad[3] is the bottom left corner

And yes, the size of your array is * 4 because one quad is made of 4 vertices.

Ah, thanks.

So wikipedia says "This process projects a texture map onto a 3D object. The letters "U" and "V" denote the axes of the 2D texture[note 1] because "X", "Y" and "Z" are already used to denote the axes of the 3D object in model space."

so i guess (for my help) it would be okay to change tu and tv to "TexX, TexY) where TexX represents tu and TexY represents TV? If so, i'll put comments to the right of it so I remember the naming convention.

And yep, accidentally reversed those two numbers in that math problem.

In this part:

                                quad[2].texCoords = sf::Vector2f((tu + 1) * tileSize.x, tileSize.y); //I'm assuming tileSize.y is the BOTTOM of the tile.
                                quad[3].texCoords = sf::Vector2f(tu * tileSize.x, tileSize.y); //Otherwise, why would it work by simply having tileSize.y?

It appears to work by simply having tileSize.y in the second parameter. By default; it's
(tv + 1) * tileSize.y)

I don't see why both work. However, as you said... I may have an idea. Perhaps tv (the y axis on the texture) starts at 0, and hence we + 1, so on the first line is ONLY when it's simply equal to tileSize.y
On the SECOND line (of the tileset, not what we're rendering), it will be double, and so on.

Is that right?

Laurent

  • Administrator
  • Hero Member
  • *****
  • Posts: 32498
    • View Profile
    • SFML's website
    • Email
Re: Help understanding vertex array tilemap example
« Reply #3 on: July 03, 2013, 11:23:08 pm »
Quote
so i guess (for my help) it would be okay to change tu and tv to "TexX, TexY) where TexX represents tu and TexY represents TV?
Yes.

Quote
I don't see why both work
Quoting yourself: "tv is always equal to 0".
Laurent Gomila - SFML developer

Anteara

  • Newbie
  • *
  • Posts: 40
    • View Profile
    • Email
Re: Help understanding vertex array tilemap example
« Reply #4 on: July 04, 2013, 12:27:56 am »
I think I get it now, I added comments to make sure I'm correct; could you please look over them and let me know? Thanks!

// find its position in the tileset texture
                                //tu finds its horizontal axes (tu = x axes)
                                //m_tileset.getSize(x) is the TILESET size. Will be a high number. Should be a multiple of the tile tilesize.
                                //Like 512 is the with of the set, then it can fit exactly 16 tiles per line
                                //This checks what tile it is, irrespective of the vertical axes
                                //tileSize.x is the INDIVIDUAL tile size.
                                //We have tile 3. 3 % (512 / 32). 3 % 16. 16 Goes in to 3 zero times with a remainder of 3.
                                //If the tile is LESS THAN 16 it will ALWAYS be equal to 0.
                                //Remember: This doesn't consider the vertical axes, only the horizontal.
                                //Thus, if we have tile 19; it will still be 3. As:
                                //19 % (512 / 32). 19 % 16 = 3. It goes in to 16 ONCE with a remainder of 3.
                                int tu = tileNumber % (m_tileset.getSize().x / tileSize.x);

                                //tv finds its vertical axes (tv = y axes)
                                //m_tileset.getSize(x) is the TILESET size.
                                //tileSize.x is the INDIVIDUAL tile size.
                                //We have tile 3. 3 / (512 / 32). 3 / 16. This will equal 0. That's correct, as tile 3 is on the first line.
                                //We have tile 19. 19 / (512 / 32). 19 / 16. This will equal 1 (casting to integer). That's correct, as tile 19 is on the first line.
                                int tv = tileNumber / (m_tileset.getSize().x / tileSize.x);
                                std::cout << tv << std::endl;
                                //thus, by calculating tu, and tv, we can calculate any tiles position in the tileset!

                                //Remember, that the code doesn't allow us to simply say tu is 19 and tv is 1.
                                //This is because when we place the tile, we place it in pixels, thus we need to multiply it by the number of pixels in the tile.
 
« Last Edit: July 04, 2013, 12:30:21 am by Anteara »