|
@@ -0,0 +1,147 @@
|
|
|
|
|
+#include "texture_renderer.h"
|
|
|
|
|
+
|
|
|
|
|
+#include <spdlog/spdlog.h>
|
|
|
|
|
+
|
|
|
|
|
+#include <cassert>
|
|
|
|
|
+#include <cstddef>
|
|
|
|
|
+
|
|
|
|
|
+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<impl>()) {}
|
|
|
|
|
+
|
|
|
|
|
+texture_renderer::~texture_renderer() = default;
|
|
|
|
|
+
|
|
|
|
|
+void texture_renderer::render(const texture_renderer::render_config *config) {
|
|
|
|
|
+ pimpl->render(config);
|
|
|
|
|
+}
|