diff --git a/examples/basic.cpp b/examples/basic.cpp index 345439b..2b0bf62 100644 --- a/examples/basic.cpp +++ b/examples/basic.cpp @@ -1,12 +1,14 @@ #include +#include + namespace { void init() { glerminal_update_palette(0, 0x00000000); - glerminal_update_palette(1, 0xFF00001F); - glerminal_update_palette(2, 0x00FF001F); + glerminal_update_palette(1, 0xFF00007F); + glerminal_update_palette(2, 0x00FF007F); glerminal_update_sprite(1, { 2, 2, 2, 2, 2, 2, 2, 2, @@ -18,17 +20,35 @@ namespace 2, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2 }); - - glerminal_set(0, 0, 0, 1); - glerminal_set(1, 0, 0, 1); - glerminal_set(1, 0, 1, 1); - glerminal_set(2, 0, 0, 1); - glerminal_set(2, 0, 1, 1); - glerminal_set(2, 0, 2, 1); } void mainloop(float dt) { + static float time = 1; + + time += dt; + + if (time < 0.2f) + { + return; + } + else + { + time = 0; + } + + for (int i = 0; i < 32; i++) + { + for (int j = 0; j < 20; j++) + { + for (int k = 0; k < 16; k++) + { + glerminal_set(i, j, k, 1); + glerminal_offset(i, j, k, (rand() * rand()) % 256 - 128, (rand() * rand()) % 256 - 128); + } + } + } + glerminal_flush(); } } diff --git a/include/glerminal.h b/include/glerminal.h index e104719..caef052 100644 --- a/include/glerminal.h +++ b/include/glerminal.h @@ -35,7 +35,7 @@ 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 layer layer of the cell in the range [0, 8) * @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); @@ -43,10 +43,19 @@ void glerminal_set(unsigned char x, unsigned char y, unsigned char layer, unsign * @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) + * @param layer layer of the cell in the range [0, 8) * @return sprite index currently assigned to the cell */ unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char layer); +/** + * @brief Set a cell's offset + * @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, 8) + * @param x_offset offset of the cell on the x axis in the range [-128, 127], where 0 is no offset + * @param y_offset offset of the cell on the y axis in the range [-128, 127], where 0 is no offset + */ +void glerminal_offset(unsigned char x, unsigned char y, unsigned char layer, char x_offset, char y_offset); /** * @brief Upload sprite to the given sprite ID diff --git a/source/glerminal-private.h b/source/glerminal-private.h index 56cd8ee..3883b0a 100644 --- a/source/glerminal-private.h +++ b/source/glerminal-private.h @@ -20,7 +20,7 @@ namespace glerminal 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; + constexpr unsigned int LAYER_COUNT = 8; class glerminal { @@ -40,6 +40,7 @@ namespace glerminal 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; + void offset(unsigned char x, unsigned char y, unsigned char layer, char x_offset, char y_offset); void update_sprite(unsigned char id, glerminal_sprite sprite); void update_palette_color(unsigned char id, unsigned int color); @@ -48,7 +49,8 @@ namespace glerminal GLFWwindow* m_window; unsigned int m_vbo; - unsigned int m_instance_vbo; + unsigned int m_sprites_instance_vbo; + unsigned int m_offsets_instance_vbo; unsigned int m_vao; unsigned int m_program; unsigned int m_screen_vao; @@ -60,6 +62,7 @@ namespace glerminal unsigned int m_palette_uniform_location; unsigned char m_cells[GRID_AREA * LAYER_COUNT]; + char m_offsets[GRID_AREA * LAYER_COUNT * 2]; unsigned char m_sprites[CELL_SIZE * CELL_SIZE * 256]; float m_palette[64]; diff --git a/source/glerminal.cpp b/source/glerminal.cpp index 2faa9d2..866bc42 100644 --- a/source/glerminal.cpp +++ b/source/glerminal.cpp @@ -25,15 +25,21 @@ namespace constexpr char* VERTEX_SHADER_SOURCE = "#version 400 core\n" "layout (location = 0) in vec2 position;\n" - "layout (location = 1) in ivec4 sprite[4];\n" + "layout (location = 1) in vec2 offset[8];\n" + "layout (location = 9) in ivec4 sprite[2];\n" "uniform vec4 " SCREEN_SIZE_UNIFORM_NAME ";\n" "out VS_OUT {\n" - " flat ivec4 sprite[4];\n" + " flat vec2 offset[8];\n" + " flat ivec4 sprite[2];\n" " vec2 texcoord;\n" "} vs_out;\n" "void main()\n" "{\n" " vs_out.sprite = sprite;\n" + " for (int i = 0; i < 8; i++)\n" + " {\n" + " vs_out.offset[i] = offset[i] * " SCREEN_SIZE_UNIFORM_NAME ".zw;\n" + " }\n" " vs_out.texcoord = vec2(position.x + 1, -position.y);\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" " vec2 temp = vec2((position + cell_position) * " SCREEN_SIZE_UNIFORM_NAME ".zw * 2 + vec2(-1, 1));\n" @@ -43,10 +49,11 @@ namespace 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" + "layout (triangle_strip, max_vertices = 24) out;\n" + "layout (invocations = 8) in;\n" "in VS_OUT {\n" - " flat ivec4 sprite[4];\n" + " flat vec2 offset[8];\n" + " flat ivec4 sprite[2];\n" " vec2 texcoord;\n" "} gs_in[];\n" "flat out int sprite;\n" @@ -54,18 +61,18 @@ namespace "void main()\n" "{\n" " gl_Layer = gl_InvocationID;\n" - " gl_Position = gl_in[0].gl_Position;\n" - " sprite = gs_in[0].sprite[gl_InvocationID / 16][gl_InvocationID % 16];\n" + " gl_Position = vec4(gl_in[0].gl_Position.xy + gs_in[0].offset[gl_InvocationID], 0, 1);\n" + " sprite = gs_in[0].sprite[gl_InvocationID / 8][gl_InvocationID % 8];\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[gl_InvocationID / 16][gl_InvocationID % 16];\n" + " gl_Position = vec4(gl_in[1].gl_Position.xy + gs_in[0].offset[gl_InvocationID], 0, 1);\n" + " sprite = gs_in[1].sprite[gl_InvocationID / 8][gl_InvocationID % 8];\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[gl_InvocationID / 16][gl_InvocationID % 16];\n" + " gl_Position = vec4(gl_in[2].gl_Position.xy + gs_in[0].offset[gl_InvocationID], 0, 1);\n" + " sprite = gs_in[2].sprite[gl_InvocationID / 8][gl_InvocationID % 8];\n" " texcoord = gs_in[2].texcoord;\n" " EmitVertex();\n" " EndPrimitive();\n" @@ -101,7 +108,7 @@ namespace "void main()\n" "{\n" " vec3 current_color = vec3(0);\n" - " for (int i = 0; i < 16; i++)\n" + " for (int i = 0; i < 8; i++)\n" " {\n" " vec4 texsample = texture(" LAYERS_UNIFORM_NAME ", vec3(texcoord, i));\n" " current_color = mix(current_color, texsample.rgb, texsample.a);\n" @@ -115,6 +122,7 @@ namespace glerminal glerminal::glerminal(glerminal_init_cb init, glerminal_main_cb main) : m_main(main), m_cells{ }, + m_offsets{ }, m_sprites{ }, m_palette{ } #ifdef _DEBUG @@ -171,7 +179,8 @@ namespace glerminal void glerminal::flush() { // use dirty flag later - glNamedBufferData(m_instance_vbo, sizeof(m_cells), m_cells, GL_STREAM_DRAW); + glNamedBufferData(m_sprites_instance_vbo, sizeof(m_cells), m_cells, GL_STREAM_DRAW); + glNamedBufferData(m_offsets_instance_vbo, sizeof(m_offsets), m_offsets, GL_STREAM_DRAW); update_sprites(); update_palette(); @@ -212,6 +221,15 @@ namespace glerminal } } + void glerminal::offset(unsigned char x, unsigned char y, unsigned char layer, char x_offset, char y_offset) + { + if (x < GRID_WIDTH && y < GRID_HEIGHT && layer < LAYER_COUNT) + { + m_offsets[2 * (layer + x * LAYER_COUNT + y * LAYER_COUNT * GRID_WIDTH) + 0] = x_offset; + m_offsets[2 * (layer + x * LAYER_COUNT + y * LAYER_COUNT * GRID_WIDTH) + 1] = y_offset; + } + } + void glerminal::update_sprite(unsigned char id, glerminal_sprite sprite) { // does this work? @@ -331,11 +349,14 @@ namespace glerminal glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT); glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // -- setup vertex data -- // create vertex buffer object glGenBuffers(1, &m_vbo); - glGenBuffers(1, &m_instance_vbo); + glGenBuffers(1, &m_sprites_instance_vbo); + glGenBuffers(1, &m_offsets_instance_vbo); // create vertex array object glGenVertexArrays(1, &m_vao); @@ -348,20 +369,42 @@ namespace glerminal 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); + glBindBuffer(GL_ARRAY_BUFFER, m_offsets_instance_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(m_offsets), m_offsets, GL_STREAM_DRAW); glEnableVertexAttribArray(1); - glVertexAttribIPointer(1, 4, GL_UNSIGNED_BYTE, 16 * sizeof(*m_cells), reinterpret_cast(0)); + glVertexAttribPointer(1, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(0 * sizeof(*m_offsets))); glVertexAttribDivisor(1, 1); glEnableVertexAttribArray(2); - glVertexAttribIPointer(2, 4, GL_UNSIGNED_BYTE, 16 * sizeof(*m_cells), reinterpret_cast(4)); + glVertexAttribPointer(2, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(2 * sizeof(*m_offsets))); glVertexAttribDivisor(2, 1); glEnableVertexAttribArray(3); - glVertexAttribIPointer(3, 4, GL_UNSIGNED_BYTE, 16 * sizeof(*m_cells), reinterpret_cast(8)); + glVertexAttribPointer(3, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(4 * sizeof(*m_offsets))); glVertexAttribDivisor(3, 1); glEnableVertexAttribArray(4); - glVertexAttribIPointer(4, 4, GL_UNSIGNED_BYTE, 16 * sizeof(*m_cells), reinterpret_cast(12)); + glVertexAttribPointer(4, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(6 * sizeof(*m_offsets))); glVertexAttribDivisor(4, 1); + glEnableVertexAttribArray(5); + glVertexAttribPointer(5, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(8 * sizeof(*m_offsets))); + glVertexAttribDivisor(5, 1); + glEnableVertexAttribArray(6); + glVertexAttribPointer(6, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(10 * sizeof(*m_offsets))); + glVertexAttribDivisor(6, 1); + glEnableVertexAttribArray(7); + glVertexAttribPointer(7, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(12 * sizeof(*m_offsets))); + glVertexAttribDivisor(7, 1); + glEnableVertexAttribArray(8); + glVertexAttribPointer(8, 2, GL_BYTE, GL_TRUE, 16 * sizeof(*m_offsets), reinterpret_cast(14 * sizeof(*m_offsets))); + glVertexAttribDivisor(8, 1); + + glBindBuffer(GL_ARRAY_BUFFER, m_sprites_instance_vbo); + glBufferData(GL_ARRAY_BUFFER, sizeof(m_cells), m_cells, GL_STREAM_DRAW); + glEnableVertexAttribArray(9); + glVertexAttribIPointer(9, 4, GL_UNSIGNED_BYTE, 8 * sizeof(*m_cells), reinterpret_cast(0 * sizeof(*m_cells))); + glVertexAttribDivisor(9, 1); + glEnableVertexAttribArray(10); + glVertexAttribIPointer(10, 4, GL_UNSIGNED_BYTE, 8 * sizeof(*m_cells), reinterpret_cast(4 * sizeof(*m_cells))); + glVertexAttribDivisor(10, 1); + // set up static vertex attributes glGenVertexArrays(1, &m_screen_vao); @@ -548,7 +591,8 @@ namespace glerminal glDeleteVertexArrays(1, &m_vao); glDeleteVertexArrays(1, &m_screen_vao); glDeleteBuffers(1, &m_vbo); - glDeleteBuffers(1, &m_instance_vbo); + glDeleteBuffers(1, &m_sprites_instance_vbo); + glDeleteBuffers(1, &m_offsets_instance_vbo); glDeleteProgram(m_program); } @@ -597,6 +641,13 @@ unsigned char glerminal_get(unsigned char x, unsigned char y, unsigned char laye return GLERMINAL_G->get(x, y, layer); } +void glerminal_offset(unsigned char x, unsigned char y, unsigned char layer, char x_offset, char y_offset) +{ + if (!GLERMINAL_G) { return; } + + GLERMINAL_G->offset(x, y, layer, x_offset, y_offset); +} + void glerminal_update_sprite(unsigned char id, glerminal_sprite sprite) { if (!GLERMINAL_G) { return; }