#include "texture_renderer.h" #include #include #include static constexpr auto vertex_shader_source = R"( #version 460 layout (location = 0) in vec2 pos_in; layout (location = 1) in vec2 tex_coord_in; out vec2 tex_coord; void main() { gl_Position = vec4(pos_in, 0, 1); tex_coord = tex_coord_in; } )"; static constexpr auto fragment_shader_source = R"( #version 460 layout (location = 0) out vec4 color_out; in vec2 tex_coord; uniform sampler2D tex_sampler; void main() { color_out = texture(tex_sampler, tex_coord); } )"; static constexpr GLuint indices[] = { 0, 1, 3, // first triangle 1, 2, 3 // second triangle }; struct texture_renderer::impl { GLuint vertex_array = 0; GLuint vertex_buffer = 0, element_buffer = 0; GLuint program = 0; impl() { // build program auto vertex_shader = glCreateShader(GL_VERTEX_SHADER); auto fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); compile_shader(vertex_shader, vertex_shader_source, "vertex"); compile_shader(fragment_shader, fragment_shader_source, "fragment"); program = glCreateProgram(); glAttachShader(program, vertex_shader); glAttachShader(program, fragment_shader); glLinkProgram(program); check_program(); glDeleteShader(vertex_shader); glDeleteShader(fragment_shader); // create buffers static_assert(offsetof(impl, element_buffer) - offsetof(impl, vertex_buffer) == sizeof(GLuint)); glGenBuffers(2, &vertex_buffer); // config vertex buffer glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glBufferStorage(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), nullptr, GL_DYNAMIC_STORAGE_BIT); // fill element buffer glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // config vertex array glGenVertexArrays(1, &vertex_array); glBindVertexArray(vertex_array); glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glVertexAttribPointer(0, 2, GL_FLOAT, false, 4 * sizeof(GLfloat), (void *) 0); glVertexAttribPointer(1, 2, GL_FLOAT, false, 4 * sizeof(GLfloat), (void *) (2 * sizeof(GLfloat))); } ~impl() { glDeleteBuffers(2, &vertex_buffer); glDeleteProgram(program); } static void compile_shader(GLuint shader, const char *source, const char *name) { glShaderSource(shader, 1, &source, nullptr); glCompileShader(shader); GLint status, log_length; glGetShaderiv(shader, GL_COMPILE_STATUS, &status); glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length); auto info_log = (GLchar *) malloc(log_length); glGetShaderInfoLog(shader, log_length, nullptr, info_log); if (status == GL_TRUE) { SPDLOG_INFO("Compile {} shader succeeded: {}", name, info_log); } else { SPDLOG_ERROR("Compile {} shader failed: {}", name, info_log); assert(false); } free(info_log); } void check_program() { GLint status, log_length; glGetProgramiv(program, GL_LINK_STATUS, &status); glGetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length); auto info_log = (GLchar *) malloc(log_length); glGetProgramInfoLog(program, log_length, nullptr, info_log); if (status == GL_TRUE) { SPDLOG_INFO("Link program succeeded: {}", info_log); } else { SPDLOG_ERROR("Link program failed: {}", info_log); assert(false); } free(info_log); } void render(const texture_renderer::render_config *config) { auto x = config->x, y = config->y; auto width = config->width, height = config->height; // bindings glUseProgram(program); glBindVertexArray(vertex_array); glBindBuffer(GL_ARRAY_BUFFER, vertex_buffer); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer); glBindTexture(GL_TEXTURE_2D, config->tex); // fill vertex buffer GLfloat vertices[] = { // 2 for position; 2 for texture x + width, y + height, 1, 1, // top right x + width, y, 1, 0, // bottom right x, y, 0, 0, // bottom left x, y + height, 0, 1 // top left }; static_assert(sizeof(vertices) == 16 * sizeof(GLfloat)); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); // draw texture glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr); } }; texture_renderer::texture_renderer() : pimpl(std::make_unique()) {} texture_renderer::~texture_renderer() = default; void texture_renderer::render(const texture_renderer::render_config *config) { pimpl->render(config); }