Hi, I'm starting to learn OpenGL and it's been really fun because of the amount of things I can do. I would like to implement a tilemap in my game engine, but the way I'm trying to do this is consuming a lot of CPU and GPU, just to draw parts of the texture, without entities or animations. The approach I'm using is to load a texture with all the tiles (tileset) and traverse the image by drawing only parts of it in specific (side-by-side) positions in the visible region of the screen.
The OpenGL version is 3.3. And the code can be seen below.
I think there must be a right way to do it, because old games like Diablo 2 draw a big map with several things happening during the game and consume practically nothing, even playing for hours.
void GameMap::draw()
{
// Use shader
m_shader->use();
// Use texture
m_texture->bind();
glBindVertexArray(m_quadVAO);
// Draw map
for (size_t r = 0; r < 10; r++)
{
for (size_t c = 0; c < 10; c++)
{
// Tile position
m_tileCoord->setX(c*m_tileHeight);
m_tileCoord->setY(r*m_tileHeight);
// Isometric perspective
m_tileCoord->convert2DToIso();
// Draw tile by index
drawTile(0);
}
}
glBindVertexArray(0);
}
void GameMap::drawTile(GLint index)
{
// Identity matrix
glm::mat4 position_coord = glm::mat4(1.0f);
glm::mat4 texture_coord = glm::mat4(1.0f);
// Part of the texture
m_srcX = index * m_tileWidth;
GLfloat clipX = m_srcX / m_texture->m_width;
GLfloat clipY = m_srcY / m_texture->m_height;
// Coordinates of the texture
texture_coord = glm::translate(texture_coord, glm::vec3(clipX, clipY, 0.0f));
// Coordinates of the vertices
position_coord = glm::translate(position_coord, glm::vec3(m_tileCoord->getX(), m_tileCoord->getY(), 0.0f));
position_coord = glm::scale(position_coord, glm::vec3(m_tileWidth, m_tileHeight, 1.0f));
// Change the shader
m_shader->setMatrix4("texture_coord", texture_coord);
m_shader->setMatrix4("position_coord", position_coord);
// Draws part of the texture
glDrawArrays(GL_TRIANGLES, 0, 6);
}
void GameMap::initRenderData()
{
// Coordinates
GLfloat posX = (GLfloat)m_tileWidth / m_texture->m_width;
GLfloat posY = (GLfloat)m_tileHeight / m_texture->m_height;
// Configure VAO/VBO
GLuint VBO;
GLfloat vertices[] = {
// Left triangle
// Pos // Tex
0.0f, 1.0f, 0.0f, posY, // Bottom left corner
1.0f, 0.0f, posX, 0.0f, // Top right corner
0.0f, 0.0f, 0.0f, 0.0f, // Upper left corner
// Right triangle
0.0f, 1.0f, 0.0f, posY, // Lower left corner
1.0f, 1.0f, posX, posY, // Bottom right corner
1.0f, 0.0f, posX, 0.0f // Top right corner
};
glGenVertexArrays(1, &m_quadVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(m_quadVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
// ------------------------------------------------------------------------------------------
// Vertex Shader
#version 330 core
layout (location = 0) in vec4 vertex; // <vec2 position, vec2 texCoords>
out vec4 TexCoords;
uniform mat4 texture_coord;
uniform mat4 position_coord;
uniform mat4 projection;
void main()
{
TexCoords = texture_coord * vec4(vertex.z, vertex.w, 1.0, 1.0);
gl_Position = projection * position_coord * vec4(vertex.xy, 0.0, 1.0);
}
// Fragment Shader
#version 330 core
out vec4 FragColor;
in vec4 TexCoords;
uniform sampler2D image;
void main()
{
FragColor = texture(image, vec2(TexCoords.x, TexCoords.y));
}