diff --git a/include/glerminal.h b/include/glerminal.h index 073f3f2..94bb212 100644 --- a/include/glerminal.h +++ b/include/glerminal.h @@ -6,12 +6,47 @@ extern "C" { #endif +enum +{ + GLERMINAL_CELL_SIZE = 8, + GLERMINAL_CELL_AREA = GLERMINAL_CELL_SIZE * GLERMINAL_CELL_SIZE +}; + typedef void (*glerminal_main_cb)(); +typedef struct glerminal_sprite +{ + unsigned char data[GLERMINAL_CELL_AREA]; +} glerminal_sprite; + +/** + * @brief Run the application's mainloop + * @param main main calllback + */ void glerminal_run(glerminal_main_cb main); +/** + * @brief Update the displayed screen contents to the current state of the library + */ void glerminal_flush(); +/** + * @brief Set a cell's sprite + * @param x position of the cell in the range [0, 32) + * @param y position of the cell in the range [0, 20) + * @param layer layer of the cell in the range [0, 16) + * @param sprite sprite's index in the range [0, 256) + */ +void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite); +/** + * @brief Get a cell's sprite + * @param x position of the cell in the range [0, 32) + * @param y position of the cell in the range [0, 20) + * @param layer layer of the cell in the range [0, 16) + * @return sprite index currently assigned to the cell + */ +unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char layer); + #ifdef __cplusplus } #endif diff --git a/source/glad.c b/source/glad.c index 1fbb2fb..bb86194 100644 --- a/source/glad.c +++ b/source/glad.c @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.36 on Mon May 6 22:10:23 2024. + OpenGL loader generated by glad 0.1.36 on Tue May 14 15:22:23 2024. Language/Generator: C/C++ Specification: gl diff --git a/source/glad/glad.h b/source/glad/glad.h index 3a007dd..1cd6a4f 100644 --- a/source/glad/glad.h +++ b/source/glad/glad.h @@ -1,6 +1,6 @@ /* - OpenGL loader generated by glad 0.1.36 on Mon May 6 22:10:23 2024. + OpenGL loader generated by glad 0.1.36 on Tue May 14 15:22:23 2024. Language/Generator: C/C++ Specification: gl diff --git a/source/glerminal-private.h b/source/glerminal-private.h index 50f09c1..184bbe1 100644 --- a/source/glerminal-private.h +++ b/source/glerminal-private.h @@ -7,9 +7,21 @@ #include #include +#ifdef _DEBUG +#include +#endif namespace glerminal { + constexpr unsigned int SCREEN_WIDTH = 1280; + constexpr unsigned int SCREEN_HEIGHT = 800; + constexpr unsigned int CELL_SIZE = GLERMINAL_CELL_SIZE; + constexpr unsigned int CELL_SCALE = 5; + constexpr unsigned int GRID_WIDTH = SCREEN_WIDTH / (CELL_SIZE * CELL_SCALE); + constexpr unsigned int GRID_HEIGHT = SCREEN_HEIGHT / (CELL_SIZE * CELL_SCALE); + constexpr unsigned int GRID_AREA = GRID_WIDTH * GRID_HEIGHT; + constexpr unsigned int LAYER_COUNT = 16; + class glerminal { public: @@ -26,20 +38,43 @@ namespace glerminal void flush(); + void set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite); + unsigned char get(unsigned char x, unsigned char y, unsigned char layer) const; + private: GLFWwindow* m_window; unsigned int m_vbo; + unsigned int m_instance_vbo; unsigned int m_vao; unsigned int m_program; + unsigned int m_screen_vao; + unsigned int m_screen_program; + unsigned int m_sprites_texture; + unsigned int m_framebuffer; + unsigned int m_framebuffer_backing_texture; + unsigned int m_screen_size_uniform_location; + unsigned int m_palette_uniform_location; + + unsigned char m_cells[GRID_AREA * LAYER_COUNT]; + unsigned char m_sprites[CELL_SIZE * CELL_SIZE * 256]; + float m_palette[64]; glerminal_main_cb m_main; +#ifdef _DEBUG + mutable std::ofstream m_log; + void log(GLenum type, GLuint id, GLenum severity, const char* message) const; +#endif + void init_glfw(); void init_gl(); void deinit_glfw(); void deinit_gl(); + + void update_sprites(); + void update_palette(); }; } diff --git a/source/glerminal.cpp b/source/glerminal.cpp index be263a7..7cc56bb 100644 --- a/source/glerminal.cpp +++ b/source/glerminal.cpp @@ -1,6 +1,9 @@ #include "glerminal-private.h" -#include +#define SCREEN_SIZE_UNIFORM_NAME "screen_size" +#define PALETTE_UNIFORM_NAME "palette" +#define SPRITES_UNIFORM_NAME "sprites" +#define LAYERS_UNIFORM_NAME "layers" namespace { @@ -9,38 +12,113 @@ namespace constexpr float VBO_VERTICES[] = { // first triangle - 0.5f, 0.5f, // top right - 0.5f, -0.5f, // bottom right - -0.5f, 0.5f, // top left + 0, 0, // top right + 0, -1, // bottom right + -1, 0, // top left // second triangle - 0.5f, -0.5f, // bottom right - -0.5f, -0.5f, // bottom left - -0.5f, 0.5f // top left + 0, -1, // bottom right + -1, -1, // bottom left + -1, 0 // top left }; constexpr char* VERTEX_SHADER_SOURCE = - "#version 330 core\n" - "layout (location = 0) in vec2 pos;\n" + "#version 400 core\n" + "layout (location = 0) in vec2 position;\n" + "layout (location = 1) in int sprite;\n" + "uniform vec4 " SCREEN_SIZE_UNIFORM_NAME ";\n" + "out VS_OUT {\n" + " flat int sprite;\n" + " vec2 texcoord;\n" + "} vs_out;\n" "void main()\n" "{\n" - " gl_Position = vec4(pos.x, pos.y, 0, 1);\n" + " vs_out.sprite = sprite;\n" + " vs_out.texcoord = position + 1;\n" + " vec2 cell_position = vec2(1 + gl_InstanceID - " SCREEN_SIZE_UNIFORM_NAME ".x * floor(gl_InstanceID * " SCREEN_SIZE_UNIFORM_NAME ".z), -floor((gl_InstanceID) * " SCREEN_SIZE_UNIFORM_NAME ".z));\n" + " gl_Position = vec4((position + cell_position) * " SCREEN_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1), 0, 1);\n" + "}"; + + constexpr char* GEOMETRY_SHADER_SOURCE = + "#version 400 core\n" + "layout (triangles) in;\n" + "layout (triangle_strip, max_vertices = 48) out;\n" + "layout (invocations = 16) in;\n" + "in VS_OUT {\n" + " flat int sprite;\n" + " vec2 texcoord;\n" + "} gs_in[];\n" + "flat out int sprite;\n" + "out vec2 texcoord;\n" + "void main()\n" + "{\n" + " gl_Layer = gl_InvocationID;\n" + " gl_Position = gl_in[0].gl_Position;\n" + " sprite = gs_in[0].sprite;\n" + " texcoord = gs_in[0].texcoord;\n" + " EmitVertex();\n" + " gl_Layer = gl_InvocationID;\n" + " gl_Position = gl_in[1].gl_Position;\n" + " sprite = gs_in[1].sprite;\n" + " texcoord = gs_in[1].texcoord;\n" + " EmitVertex();\n" + " gl_Layer = gl_InvocationID;\n" + " gl_Position = gl_in[2].gl_Position;\n" + " sprite = gs_in[2].sprite;\n" + " texcoord = gs_in[2].texcoord;\n" + " EmitVertex();\n" + " EndPrimitive();\n" "}"; constexpr char* FRAGMENT_SHADER_SOURCE = - "#version 330 core\n" + "#version 400 core\n" + "in vec2 texcoord;\n" + "flat in int sprite;\n" + "uniform usampler2DArray " SPRITES_UNIFORM_NAME ";\n" + "uniform vec4 " PALETTE_UNIFORM_NAME "[16];\n" "out vec4 FragColor;\n" - "\n" "void main()\n" "{\n" - " FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);\n" + " FragColor = " PALETTE_UNIFORM_NAME "[int(texture(" SPRITES_UNIFORM_NAME ", vec3(texcoord, sprite)))];\n" + "}"; + + constexpr char* SCREEN_VERTEX_SHADER_SOURCE = + "#version 400 core\n" + "layout (location = 0) in vec2 position;\n" + "out vec2 texcoord;\n" + "void main()\n" + "{\n" + " gl_Position = vec4(position * 2 + 1, 0, 1);\n" + " texcoord = -position;\n" + "}"; + + constexpr char* SCREEN_FRAGMENT_SHADER_SOURCE = + "#version 400 core\n" + "in vec2 texcoord;\n" + "uniform sampler2DArray " LAYERS_UNIFORM_NAME ";\n" + "out vec4 FragColor;\n" + "void main()\n" + "{\n" + " vec3 current_color = vec3(0);\n" + " for (int i = 15; i >= 0; i--)\n" + " {\n" + " vec4 texsample = texture(" LAYERS_UNIFORM_NAME ", vec3(texcoord, i));\n" + " current_color = mix(current_color, texsample.xyz, texsample.w);\n" + " }\n" + " FragColor = vec4(current_color, 1);\n" "}"; } namespace glerminal { glerminal::glerminal(glerminal_main_cb main) : - m_main(main) + m_main(main), + m_cells{ }, + m_sprites{ }, + m_palette{ } +#ifdef _DEBUG + , m_log("log.txt") +#endif { if (GLERMINAL_G) { @@ -79,36 +157,76 @@ namespace glerminal void glerminal::flush() { - glClear(GL_COLOR_BUFFER_BIT); + glNamedBufferData(m_instance_vbo, sizeof(m_cells), m_cells, GL_STREAM_DRAW); glUseProgram(m_program); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture); + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); glBindVertexArray(m_vao); + glClear(GL_COLOR_BUFFER_BIT); + glDrawArraysInstanced(GL_TRIANGLES, 0, 6, GRID_AREA); + + glUseProgram(m_screen_program); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_framebuffer_backing_texture); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBindVertexArray(m_screen_vao); + glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_TRIANGLES, 0, 6); glfwSwapBuffers(m_window); } + void glerminal::set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite) + { + if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) + { + m_cells[x + y * GRID_WIDTH + layer * GRID_AREA] = sprite; + } + } + + unsigned char glerminal::get(unsigned char x, unsigned char y, unsigned char layer) const + { + if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) + { + return m_cells[x + y * GRID_WIDTH + layer * GRID_AREA]; + } + else + { + return 0; + } + } + void glerminal::init_glfw() { glfwInit(); - glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); - glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // not resizable for now. // // need to think about how to handle resizing to ensure // that the window stays an integer number of "tiles" large glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE); +#ifdef _DEBUG + glfwWindowHint(GLFW_CONTEXT_DEBUG, GLFW_TRUE); +#endif // non-adjustable size for the same reason as above - m_window = glfwCreateWindow(1280, 800, "glerminal", nullptr, nullptr); + m_window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "glerminal", nullptr, nullptr); if (!m_window) { throw std::runtime_error("Failed to create glerminal window."); } } + +#ifdef _DEBUG + void glerminal::log(GLenum type, GLuint id, GLenum severity, const char* message) const + { + glDebugMessageInsert(GL_DEBUG_SOURCE_THIRD_PARTY, type, id, severity, -1, message); + } +#endif void glerminal::init_gl() { @@ -120,25 +238,96 @@ namespace glerminal throw std::runtime_error("Failed to initialize GLAD."); } - glViewport(0, 0, 1280, 800); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); +#ifdef _DEBUG + glEnable(GL_DEBUG_OUTPUT); + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback([](GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) + { + const char* source_str = "[UNKNOWN]"; + switch (source) + { + case GL_DEBUG_SOURCE_API: + source_str = "[API]"; + break; + + case GL_DEBUG_SOURCE_WINDOW_SYSTEM: + source_str = "[WINDOW]"; + break; + + case GL_DEBUG_SOURCE_SHADER_COMPILER: + source_str = "[SHADER]"; + break; + + case GL_DEBUG_SOURCE_THIRD_PARTY: + source_str = "[GLERMINAL]"; + break; + + case GL_DEBUG_SOURCE_APPLICATION: + source_str = "[APPLICATION]"; + break; + } + + const char* type_str = "[UNKNOWN]"; + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + type_str = "[ERROR]"; + break; + + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + type_str = "[DEPRECATED]"; + break; + + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + type_str = "[UNDEFINED]"; + break; + + case GL_DEBUG_TYPE_PORTABILITY: + type_str = "[PORTABILITY]"; + break; + + case GL_DEBUG_TYPE_PERFORMANCE: + type_str = "[PERFORMANCE]"; + break; + } + + static_cast(userParam)->m_log << source_str << type_str << ' ' << std::string(message, length) << std::endl; + + }, this); +#endif + + glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); + glDisable(GL_DEPTH_TEST); // -- setup vertex data -- // create vertex buffer object glGenBuffers(1, &m_vbo); + glGenBuffers(1, &m_instance_vbo); // create vertex array object glGenVertexArrays(1, &m_vao); glBindVertexArray(m_vao); - // bind buffer and copy data + // set up static vertex attributes glBindBuffer(GL_ARRAY_BUFFER, m_vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(VBO_VERTICES), VBO_VERTICES, GL_STATIC_DRAW); - - // set up vertex attributes - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), reinterpret_cast(0)); glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*VBO_VERTICES), reinterpret_cast(0)); + // set up instanced vertex attributes + glBindBuffer(GL_ARRAY_BUFFER, m_instance_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(m_cells), m_cells, GL_STREAM_DRAW); + glEnableVertexAttribArray(1); + glVertexAttribIPointer(1, 1, GL_UNSIGNED_BYTE, sizeof(*m_cells), reinterpret_cast(0)); + glVertexAttribDivisor(1, 1); + + // set up static vertex attributes + glGenVertexArrays(1, &m_screen_vao); + glBindVertexArray(m_screen_vao); + + glBindBuffer(GL_ARRAY_BUFFER, m_vbo); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*VBO_VERTICES), reinterpret_cast(0)); // -- setup shader program -- // compile @@ -146,6 +335,10 @@ namespace glerminal glShaderSource(vertex_shader, 1, &VERTEX_SHADER_SOURCE, nullptr); glCompileShader(vertex_shader); + const unsigned int geometry_shader = glCreateShader(GL_GEOMETRY_SHADER); + glShaderSource(geometry_shader, 1, &GEOMETRY_SHADER_SOURCE, nullptr); + glCompileShader(geometry_shader); + const unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragment_shader, 1, &FRAGMENT_SHADER_SOURCE, nullptr); glCompileShader(fragment_shader); @@ -153,23 +346,37 @@ namespace glerminal int success; char info_log[512] = {}; - // verify + // verify compile glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(vertex_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); glDeleteShader(vertex_shader); + glDeleteShader(geometry_shader); glDeleteShader(fragment_shader); using namespace std::string_literals; throw std::runtime_error("Could not compile vertex shader: "s + info_log); } + glGetShaderiv(geometry_shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(geometry_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); + glDeleteShader(vertex_shader); + glDeleteShader(geometry_shader); + glDeleteShader(fragment_shader); + + using namespace std::string_literals; + throw std::runtime_error("Could not compile geometry shader: "s + info_log); + } + glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success); if (!success) { glGetShaderInfoLog(fragment_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); glDeleteShader(vertex_shader); + glDeleteShader(geometry_shader); glDeleteShader(fragment_shader); using namespace std::string_literals; @@ -179,12 +386,14 @@ namespace glerminal // link m_program = glCreateProgram(); glAttachShader(m_program, vertex_shader); + glAttachShader(m_program, geometry_shader); glAttachShader(m_program, fragment_shader); glLinkProgram(m_program); glDeleteShader(vertex_shader); + glDeleteShader(geometry_shader); glDeleteShader(fragment_shader); - // verify again + // verify link glGetProgramiv(m_program, GL_LINK_STATUS, &success); if (!success) { @@ -194,12 +403,111 @@ namespace glerminal using namespace std::string_literals; throw std::runtime_error("Could not link shader program: "s + info_log); } + + // setup uniforms + m_screen_size_uniform_location = glGetUniformLocation(m_program, SCREEN_SIZE_UNIFORM_NAME); + m_palette_uniform_location = glGetUniformLocation(m_program, PALETTE_UNIFORM_NAME); + + glUseProgram(m_program); + glUniform4f(m_screen_size_uniform_location, GRID_WIDTH, GRID_HEIGHT, 1.0f / GRID_WIDTH, 1.0f / GRID_HEIGHT); + + glUniform1i(glGetUniformLocation(m_program, SPRITES_UNIFORM_NAME), 0); + + update_palette(); + + // compile + const unsigned int screen_vertex_shader = glCreateShader(GL_VERTEX_SHADER); + glShaderSource(screen_vertex_shader, 1, &SCREEN_VERTEX_SHADER_SOURCE, nullptr); + glCompileShader(screen_vertex_shader); + + const unsigned int screen_fragment_shader = glCreateShader(GL_FRAGMENT_SHADER); + glShaderSource(screen_fragment_shader, 1, &SCREEN_FRAGMENT_SHADER_SOURCE, nullptr); + glCompileShader(screen_fragment_shader); + + // verify compile + glGetShaderiv(screen_vertex_shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(screen_vertex_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); + glDeleteShader(screen_vertex_shader); + glDeleteShader(screen_fragment_shader); + + using namespace std::string_literals; + throw std::runtime_error("Could not compile screen vertex shader: "s + info_log); + } + + glGetShaderiv(screen_fragment_shader, GL_COMPILE_STATUS, &success); + if (!success) + { + glGetShaderInfoLog(screen_fragment_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); + glDeleteShader(screen_vertex_shader); + glDeleteShader(screen_fragment_shader); + + using namespace std::string_literals; + throw std::runtime_error("Could not compile screen fragment shader: "s + info_log); + } + + // link + m_screen_program = glCreateProgram(); + glAttachShader(m_screen_program, screen_vertex_shader); + glAttachShader(m_screen_program, screen_fragment_shader); + glLinkProgram(m_screen_program); + glDeleteShader(screen_vertex_shader); + glDeleteShader(screen_fragment_shader); + + // verify link + glGetProgramiv(m_screen_program, GL_LINK_STATUS, &success); + if (!success) + { + glGetProgramInfoLog(m_screen_program, sizeof(info_log) / sizeof(*info_log), nullptr, info_log); + glDeleteProgram(m_screen_program); + + using namespace std::string_literals; + throw std::runtime_error("Could not link screen shader program: "s + info_log); + } + + // setup uniforms later + + // -- setup textures -- + glGenTextures(1, &m_sprites_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); + + update_sprites(); + + // -- setup framebuffer -- + glGenFramebuffers(1, &m_framebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer); + + glGenTextures(1, &m_framebuffer_backing_texture); + glBindTexture(GL_TEXTURE_2D_ARRAY, m_framebuffer_backing_texture); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_BASE_LEVEL, 0); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAX_LEVEL, 0); + + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, SCREEN_WIDTH, SCREEN_HEIGHT, LAYER_COUNT, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_framebuffer_backing_texture, 0); + + // setup uniforms for screen shader + glUseProgram(m_screen_program); } void glerminal::deinit_glfw() { - glDeleteProgram(m_program); - glfwDestroyWindow(m_window); glfwTerminate(); @@ -207,8 +515,27 @@ namespace glerminal void glerminal::deinit_gl() { + glDeleteFramebuffers(1, &m_framebuffer); + glDeleteTextures(1, &m_framebuffer_backing_texture); + glDeleteTextures(1, &m_sprites_texture); + glDeleteVertexArrays(1, &m_vao); + glDeleteVertexArrays(1, &m_screen_vao); + glDeleteBuffers(1, &m_vbo); + glDeleteBuffers(1, &m_instance_vbo); glDeleteProgram(m_program); } + + void glerminal::update_sprites() + { + glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8UI, CELL_SIZE, CELL_SIZE, 256, 0, GL_RED_INTEGER, GL_UNSIGNED_BYTE, m_sprites); + } + + void glerminal::update_palette() + { + glUseProgram(m_program); + glUniform4fv(m_palette_uniform_location, 16, m_palette); + } } void glerminal_run(glerminal_main_cb main) @@ -219,7 +546,6 @@ void glerminal_run(glerminal_main_cb main) } catch (const std::runtime_error& e) { - std::cout << "[glerminal] " << e.what() << std::endl; } } @@ -228,4 +554,18 @@ void glerminal_flush() if (!GLERMINAL_G) { return; } GLERMINAL_G->flush(); +} + +void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsigned char sprite) +{ + if (!GLERMINAL_G) { return; } + + GLERMINAL_G->set(x, y, layer, sprite); +} + +unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char layer) +{ + if (!GLERMINAL_G) { return 0; } + + return GLERMINAL_G->get(x, y, layer); } \ No newline at end of file