proof-of-concept graphics implementation
This commit is contained in:
parent
06e756dd2c
commit
06c7aa9eb5
@ -6,12 +6,47 @@ extern "C"
|
|||||||
{
|
{
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
GLERMINAL_CELL_SIZE = 8,
|
||||||
|
GLERMINAL_CELL_AREA = GLERMINAL_CELL_SIZE * GLERMINAL_CELL_SIZE
|
||||||
|
};
|
||||||
|
|
||||||
typedef void (*glerminal_main_cb)();
|
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);
|
void glerminal_run(glerminal_main_cb main);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Update the displayed screen contents to the current state of the library
|
||||||
|
*/
|
||||||
void glerminal_flush();
|
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
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -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++
|
Language/Generator: C/C++
|
||||||
Specification: gl
|
Specification: gl
|
||||||
|
@ -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++
|
Language/Generator: C/C++
|
||||||
Specification: gl
|
Specification: gl
|
||||||
|
@ -7,9 +7,21 @@
|
|||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
#ifdef _DEBUG
|
||||||
|
#include <fstream>
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace glerminal
|
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
|
class glerminal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -26,20 +38,43 @@ namespace glerminal
|
|||||||
|
|
||||||
void flush();
|
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:
|
private:
|
||||||
GLFWwindow* m_window;
|
GLFWwindow* m_window;
|
||||||
|
|
||||||
unsigned int m_vbo;
|
unsigned int m_vbo;
|
||||||
|
unsigned int m_instance_vbo;
|
||||||
unsigned int m_vao;
|
unsigned int m_vao;
|
||||||
unsigned int m_program;
|
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;
|
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_glfw();
|
||||||
void init_gl();
|
void init_gl();
|
||||||
|
|
||||||
void deinit_glfw();
|
void deinit_glfw();
|
||||||
void deinit_gl();
|
void deinit_gl();
|
||||||
|
|
||||||
|
void update_sprites();
|
||||||
|
void update_palette();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
#include "glerminal-private.h"
|
#include "glerminal-private.h"
|
||||||
|
|
||||||
#include <iostream>
|
#define SCREEN_SIZE_UNIFORM_NAME "screen_size"
|
||||||
|
#define PALETTE_UNIFORM_NAME "palette"
|
||||||
|
#define SPRITES_UNIFORM_NAME "sprites"
|
||||||
|
#define LAYERS_UNIFORM_NAME "layers"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
@ -9,38 +12,113 @@ namespace
|
|||||||
constexpr float VBO_VERTICES[] =
|
constexpr float VBO_VERTICES[] =
|
||||||
{
|
{
|
||||||
// first triangle
|
// first triangle
|
||||||
0.5f, 0.5f, // top right
|
0, 0, // top right
|
||||||
0.5f, -0.5f, // bottom right
|
0, -1, // bottom right
|
||||||
-0.5f, 0.5f, // top left
|
-1, 0, // top left
|
||||||
|
|
||||||
// second triangle
|
// second triangle
|
||||||
0.5f, -0.5f, // bottom right
|
0, -1, // bottom right
|
||||||
-0.5f, -0.5f, // bottom left
|
-1, -1, // bottom left
|
||||||
-0.5f, 0.5f // top left
|
-1, 0 // top left
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr char* VERTEX_SHADER_SOURCE =
|
constexpr char* VERTEX_SHADER_SOURCE =
|
||||||
"#version 330 core\n"
|
"#version 400 core\n"
|
||||||
"layout (location = 0) in vec2 pos;\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"
|
"void main()\n"
|
||||||
"{\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 =
|
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"
|
"out vec4 FragColor;\n"
|
||||||
"\n"
|
|
||||||
"void main()\n"
|
"void main()\n"
|
||||||
"{\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
|
namespace glerminal
|
||||||
{
|
{
|
||||||
glerminal::glerminal(glerminal_main_cb main) :
|
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)
|
if (GLERMINAL_G)
|
||||||
{
|
{
|
||||||
@ -79,30 +157,63 @@ namespace glerminal
|
|||||||
|
|
||||||
void glerminal::flush()
|
void glerminal::flush()
|
||||||
{
|
{
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glNamedBufferData(m_instance_vbo, sizeof(m_cells), m_cells, GL_STREAM_DRAW);
|
||||||
|
|
||||||
glUseProgram(m_program);
|
glUseProgram(m_program);
|
||||||
|
glBindTexture(GL_TEXTURE_2D_ARRAY, m_sprites_texture);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer);
|
||||||
glBindVertexArray(m_vao);
|
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);
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
|
|
||||||
glfwSwapBuffers(m_window);
|
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()
|
void glerminal::init_glfw()
|
||||||
{
|
{
|
||||||
glfwInit();
|
glfwInit();
|
||||||
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||||
// not resizable for now.
|
// not resizable for now.
|
||||||
//
|
//
|
||||||
// need to think about how to handle resizing to ensure
|
// need to think about how to handle resizing to ensure
|
||||||
// that the window stays an integer number of "tiles" large
|
// that the window stays an integer number of "tiles" large
|
||||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
|
||||||
|
#ifdef _DEBUG
|
||||||
|
glfwWindowHint(GLFW_CONTEXT_DEBUG, GLFW_TRUE);
|
||||||
|
#endif
|
||||||
|
|
||||||
// non-adjustable size for the same reason as above
|
// 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)
|
if (!m_window)
|
||||||
{
|
{
|
||||||
@ -110,6 +221,13 @@ namespace glerminal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#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()
|
void glerminal::init_gl()
|
||||||
{
|
{
|
||||||
glfwMakeContextCurrent(m_window);
|
glfwMakeContextCurrent(m_window);
|
||||||
@ -120,25 +238,96 @@ namespace glerminal
|
|||||||
throw std::runtime_error("Failed to initialize GLAD.");
|
throw std::runtime_error("Failed to initialize GLAD.");
|
||||||
}
|
}
|
||||||
|
|
||||||
glViewport(0, 0, 1280, 800);
|
#ifdef _DEBUG
|
||||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
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<const glerminal*>(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 --
|
// -- setup vertex data --
|
||||||
// create vertex buffer object
|
// create vertex buffer object
|
||||||
glGenBuffers(1, &m_vbo);
|
glGenBuffers(1, &m_vbo);
|
||||||
|
glGenBuffers(1, &m_instance_vbo);
|
||||||
|
|
||||||
// create vertex array object
|
// create vertex array object
|
||||||
glGenVertexArrays(1, &m_vao);
|
glGenVertexArrays(1, &m_vao);
|
||||||
glBindVertexArray(m_vao);
|
glBindVertexArray(m_vao);
|
||||||
|
|
||||||
// bind buffer and copy data
|
// set up static vertex attributes
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
|
||||||
glBufferData(GL_ARRAY_BUFFER, sizeof(VBO_VERTICES), VBO_VERTICES, GL_STATIC_DRAW);
|
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<void*>(0));
|
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(*VBO_VERTICES), reinterpret_cast<void*>(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<void*>(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<void*>(0));
|
||||||
|
|
||||||
// -- setup shader program --
|
// -- setup shader program --
|
||||||
// compile
|
// compile
|
||||||
@ -146,6 +335,10 @@ namespace glerminal
|
|||||||
glShaderSource(vertex_shader, 1, &VERTEX_SHADER_SOURCE, nullptr);
|
glShaderSource(vertex_shader, 1, &VERTEX_SHADER_SOURCE, nullptr);
|
||||||
glCompileShader(vertex_shader);
|
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);
|
const unsigned int fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
glShaderSource(fragment_shader, 1, &FRAGMENT_SHADER_SOURCE, nullptr);
|
glShaderSource(fragment_shader, 1, &FRAGMENT_SHADER_SOURCE, nullptr);
|
||||||
glCompileShader(fragment_shader);
|
glCompileShader(fragment_shader);
|
||||||
@ -153,23 +346,37 @@ namespace glerminal
|
|||||||
int success;
|
int success;
|
||||||
char info_log[512] = {};
|
char info_log[512] = {};
|
||||||
|
|
||||||
// verify
|
// verify compile
|
||||||
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
|
glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &success);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
glGetShaderInfoLog(vertex_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log);
|
glGetShaderInfoLog(vertex_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log);
|
||||||
glDeleteShader(vertex_shader);
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(geometry_shader);
|
||||||
glDeleteShader(fragment_shader);
|
glDeleteShader(fragment_shader);
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
throw std::runtime_error("Could not compile vertex shader: "s + info_log);
|
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);
|
glGetShaderiv(fragment_shader, GL_COMPILE_STATUS, &success);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
glGetShaderInfoLog(fragment_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log);
|
glGetShaderInfoLog(fragment_shader, sizeof(info_log) / sizeof(*info_log), nullptr, info_log);
|
||||||
glDeleteShader(vertex_shader);
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(geometry_shader);
|
||||||
glDeleteShader(fragment_shader);
|
glDeleteShader(fragment_shader);
|
||||||
|
|
||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
@ -179,12 +386,14 @@ namespace glerminal
|
|||||||
// link
|
// link
|
||||||
m_program = glCreateProgram();
|
m_program = glCreateProgram();
|
||||||
glAttachShader(m_program, vertex_shader);
|
glAttachShader(m_program, vertex_shader);
|
||||||
|
glAttachShader(m_program, geometry_shader);
|
||||||
glAttachShader(m_program, fragment_shader);
|
glAttachShader(m_program, fragment_shader);
|
||||||
glLinkProgram(m_program);
|
glLinkProgram(m_program);
|
||||||
glDeleteShader(vertex_shader);
|
glDeleteShader(vertex_shader);
|
||||||
|
glDeleteShader(geometry_shader);
|
||||||
glDeleteShader(fragment_shader);
|
glDeleteShader(fragment_shader);
|
||||||
|
|
||||||
// verify again
|
// verify link
|
||||||
glGetProgramiv(m_program, GL_LINK_STATUS, &success);
|
glGetProgramiv(m_program, GL_LINK_STATUS, &success);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
@ -194,12 +403,111 @@ namespace glerminal
|
|||||||
using namespace std::string_literals;
|
using namespace std::string_literals;
|
||||||
throw std::runtime_error("Could not link shader program: "s + info_log);
|
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()
|
void glerminal::deinit_glfw()
|
||||||
{
|
{
|
||||||
glDeleteProgram(m_program);
|
|
||||||
|
|
||||||
glfwDestroyWindow(m_window);
|
glfwDestroyWindow(m_window);
|
||||||
|
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
@ -207,8 +515,27 @@ namespace glerminal
|
|||||||
|
|
||||||
void glerminal::deinit_gl()
|
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);
|
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)
|
void glerminal_run(glerminal_main_cb main)
|
||||||
@ -219,7 +546,6 @@ void glerminal_run(glerminal_main_cb main)
|
|||||||
}
|
}
|
||||||
catch (const std::runtime_error& e)
|
catch (const std::runtime_error& e)
|
||||||
{
|
{
|
||||||
std::cout << "[glerminal] " << e.what() << std::endl;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,3 +555,17 @@ void glerminal_flush()
|
|||||||
|
|
||||||
GLERMINAL_G->flush();
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user