0
\$\begingroup\$

For a personal project, I'm trying to implement a tilemap. My goal is to obtain this

enter image description here

But all I got is this

enter image description here

I am calculating the UV coordinates inside my shader, with this formula. 8 and 13 is the width and height of my tileset, in tile.

vs_uv = (1.0 / vec2(8, 13)) * (vec2(tile / 8, tile % 13) + vertex);

Where tile is one element of this array

GLint map[] = {
     0,  1,  2,  3,  4,  5,  6,  7, 
     8,  9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23,
};

and where vertex is line of this array

GLfloat vertices[] = {
    0.0f, 1.0f,
    1.0f, 0.0f,
    0.0f, 0.0f,

    0.0f, 1.0f,
    1.0f, 0.0f,   
    1.0f, 1.0f                  
}; 

Here's the full code of the example :

#include <algorithm>
#include <memory>
#include <cstring>

#include <boost/filesystem.hpp>
#include <boost/multi_array.hpp>
#define GLEW_STATIC
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <SDL2/SDL.h>

#include <pck/global.hpp>
#include <pck/program.hpp>
#include <pck/utils/stb_image.h>
#include <pck/window.hpp>

namespace fs = boost::filesystem;

const int WIDTH = 800, HEIGHT = 600;

GLint map[] = {
     0,  1,  2,  3,  4,  5,  6,  7, 
     8,  9, 10, 11, 12, 13, 14, 15,
    16, 17, 18, 19, 20, 21, 22, 23,
};

GLuint map_w = 8;
GLuint map_h = 3;

int main()
{
    // Wrapper around SDL2 window, also initializing OpenGL
    pck::Global::window.reset(new pck::Window("Test_gl", WIDTH, HEIGHT));

    // Vertices buffer
    GLfloat vertices[] = {
        0.0f, 1.0f,
        1.0f, 0.0f,
        0.0f, 0.0f,

        0.0f, 1.0f,
        1.0f, 0.0f,   
        1.0f, 1.0f                  
    }; 

    // Positions buffer 
    glm::vec2 positions[24];

    int index = 0;
    for(size_t j = 0; j < map_h; ++j)
    {
        for(size_t i = 0; i < map_w; ++i)
        {
            positions[index++] = glm::vec2(i, j);
        }
    }


    // Texture declaration
    GLuint tex_ID;
    glGenTextures(1, &tex_ID);

    int w(0), h(0), c(0);
    unsigned char* data = stbi_load("tileset.png", &w, &h, &c, STBI_rgb_alpha);

    if(data == nullptr)
    {
        std::cout << "Failed to load texture\n";
        stbi_image_free(data);
        return 1;
    }

    glBindTexture(GL_TEXTURE_2D, tex_ID);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

        if(c == 3)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
        else if(c == 4)
            glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);

        glGenerateMipmap(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

    stbi_image_free(data);

    GLuint VAO;
    glGenVertexArrays(1, &VAO);

    glBindVertexArray(VAO);
        GLuint vertex_VBO;
        glGenBuffers(1, &vertex_VBO);

        glBindBuffer(GL_ARRAY_BUFFER, vertex_VBO);
            glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
            glEnableVertexAttribArray(0);
            glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // Also set instance data
        GLuint positions_VBO;
        glGenBuffers(1, &positions_VBO);

        glBindBuffer(GL_ARRAY_BUFFER, positions_VBO);
            glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * 24, &positions[0], GL_STATIC_DRAW);
            glEnableVertexAttribArray(1);
            glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (GLvoid*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);   
        glVertexAttribDivisor(1, 1);

        GLuint tiles_VBO;
        glGenBuffers(1, &tiles_VBO);

        glBindBuffer(GL_ARRAY_BUFFER, tiles_VBO);
            glBufferData(GL_ARRAY_BUFFER, sizeof(map), map, GL_STATIC_DRAW);
            glEnableVertexAttribArray(2);
            glVertexAttribPointer(2, 1, GL_INT, GL_FALSE, sizeof(GLint), (GLvoid*)0);
        glBindBuffer(GL_ARRAY_BUFFER, 0);   
        glVertexAttribDivisor(2, 1);
    glBindVertexArray(0); 

    // Shader for instanciated tiles
    pck::VertShader vs(std::string(R"(
        #version 330 core
        layout (location = 0) in vec2 vertex;
        layout (location = 1) in vec2 position;
        layout (location = 2) in int tile;

        uniform mat4 model;
        uniform mat4 projection;
        uniform ivec2 tileset_tile_size;

        out vec2 vs_uv;

        void main()
        {
            gl_Position = projection * model * vec4(vertex + position, 0.0f, 1.0f);

            vs_uv = (1.0 / vec2(8, 13)) * (vec2(tile / 8, tile % 13) + vertex);
        }  
    )"));

    pck::FragShader fs(std::string(R"(
        #version 330 core

        uniform sampler2D image;

        in vec2 vs_uv;

        out vec4 fs_color;

        void main()
        {
            //fs_color = vec4(vs_uv, 0.0f, 1.0f);
            fs_color = texture(image, vs_uv);
        }
    )"));

    std::shared_ptr<pck::Program> program(new pck::Program(vs, fs));   

    // Uniforms
    program->use();

    glm::mat4 model;
        model = glm::rotate(model, 0.0f, glm::vec3(0.0f, 0.0f, 1.0f)); 
        model = glm::scale(model, glm::vec3(16.0f, 16.0f, 1.0f)); 

    pck::Global::zoom = 2; 
    glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(pck::Global::width / pck::Global::zoom), 
        static_cast<GLfloat>(pck::Global::height / pck::Global::zoom), 0.0f, -1.0f, 1.0f);

    glm::ivec2 tileset_tile_size(8, 13);

    glUniform2iv(glGetUniformLocation(program->ID(), "tileset_tile_size"), 1, glm::value_ptr(tileset_tile_size));

    glUniformMatrix4fv(glGetUniformLocation(program->ID(), "projection"), 1, GL_FALSE, glm::value_ptr(projection));
    glUniformMatrix4fv(glGetUniformLocation(program->ID(), "model"), 1, GL_FALSE, glm::value_ptr(model)); 

    //glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

    while(pck::Global::window->is_closed())
    {
        while(pck::Global::window->poll_event() != 0)
        {
            if(pck::Global::event->type == SDL_QUIT)
            {
                pck::Global::window->close();
            }
            else if(pck::Global::event->type == SDL_KEYDOWN)
            {
                if(pck::Global::event->key.keysym.sym == SDLK_ESCAPE)
                    pck::Global::window->close();
            }
        }

        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        glBindTexture(GL_TEXTURE_2D, tex_ID);

        program->use();

        glBindVertexArray(VAO);
            glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 24);
        glBindVertexArray(0);

        pck::Global::window->update();
    }

    return 0;
}
\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

I think the line you've highlighted is the problem:

vs_uv = (1.0 / vec2(8, 13)) * (vec2(tile / 8, tile % 13) + vertex);

Your vertices have coordinates that are between 0 and 1. Because your using GL_TEXTURE_2D, your texture coordinates need to be between 0 and 1. So the above calculation will always give values greater than 1, except for the first tile.

I would think that you'd need the width and height of your texture atlas to calculate this properly. Without seeing your texture atlas, it's hard to tell you the right thing to do, but if your tiles are laid out in a single row in the texture atlas, it should be something like this:

vs_uv = tile * tileSize;

where tileSize is the width and height in normalized coordinates of 1 tile of the texture atlas. So if you have 5 tiles that are 8 x 13 pixels laid out in a single row, your texture atlas will be 40 x 13. Normalizing that, 1 tile is 1/5 width and 1 height, so tileSize would be (0.2, 1.0).

\$\endgroup\$
4
  • \$\begingroup\$ I don´t know if the formula you gave me is totally right, but the fact that the vertex value will give a value greater than 1 make sense. I´ll give you an update in a few hours. Thanks \$\endgroup\$
    – bl4ckb0ne
    Commented Apr 5, 2017 at 16:37
  • \$\begingroup\$ I changed the formula to vs_uv = (1.0 / vec2(8, 13)) * (vec2(tile / 8, tile % 13) + vec2(vertex.x / 8, vertex.y / 13));, and here's the result. I don't know what's wrong this time. \$\endgroup\$
    – bl4ckb0ne
    Commented Apr 8, 2017 at 0:33
  • \$\begingroup\$ If you'd post a picture of your texture atlas, that would help. \$\endgroup\$ Commented Apr 8, 2017 at 1:59
  • \$\begingroup\$ First picture of the post is what I am trying to get on my screen. The full tileset is here \$\endgroup\$
    – bl4ckb0ne
    Commented Apr 8, 2017 at 2:05
0
\$\begingroup\$

I finally found the right formula, where (8.0, 13.0) is the number of tiles in my tileset, in width and height.

vs_uv = (1.0 / vec2(8.0, 13.0)) * vec2(mod(tile, 8.0), floor(tile / 8.0)) + (vertex / vec2(8.0, 13.0));

Giving me this

enter code here

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .