diff --git a/main.cpp b/main.cpp index 1b183ed..a049880 100644 --- a/main.cpp +++ b/main.cpp @@ -76,7 +76,7 @@ struct debug_clock_t #define TIME_BLOCK(name) debug_clock_t _name_##timer(#name, __COUNTER__); struct charglyph_t { - uint32_t tex; // handle to glyph texture + int32_t tex; // index of character in tex array glm::ivec2 size; // 2d dimensions glm::ivec2 bearing; // offset from baseline to left/top of glyph uint32_t advance; // offset to next glyph @@ -109,10 +109,16 @@ struct selection_t { int32_t nselected; }; +const int32_t ARRAY_LIMIT = 400; + charglyph_t glyphmap[128]; uint32_t text_program; uint32_t textvao, textvbo; uint32_t hex_program; +uint32_t texture_array; +glm::mat4 transforms[ARRAY_LIMIT]; +int32_t letter_map[ARRAY_LIMIT]; + // error processing enum err_type { @@ -238,16 +244,24 @@ void render_text(const char *text, float x, float y, float scale, glm::vec3 colo { // TODO: Group draw calls together instead of doing 1 draw call/character. Use an atlas with GL_TEXTURE_ARRAY // Take tips from https://www.youtube.com/watch?v=S0PyZKX4lyI + scale *= 48.0f/256; // activate corresponding render state glUseProgram(text_program); glUniform3f(glGetUniformLocation(text_program, "text_color"), color.x, color.y, color.z); glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array); glBindVertexArray(textvao); + glBindBuffer(GL_ARRAY_BUFFER, textvbo); + int idx = 0; // iterate through 128 ASCI characters for (const char *p = text; *p != '\0'; ++p) { + // Avoid drawing more than 400 chars at a time + if (idx == ARRAY_LIMIT - 1) + break; + char c = *p; charglyph_t charglyph = glyphmap[(size_t)c]; @@ -258,31 +272,19 @@ void render_text(const char *text, float x, float y, float scale, glm::vec3 colo } float xpos = x + charglyph.bearing.x * scale; - float ypos = y - (charglyph.size.y - charglyph.bearing.y) * scale; + float ypos = y - (256 - charglyph.bearing.y) * scale; - glm::mat4 transform = glm::translate(glm::mat4(1.0f), glm::vec3(xpos, ypos, 0)) * glm::scale(glm::mat4(1.0f), glm::vec3(charglyph.size.x * scale, charglyph.size.y * scale, 0)); - glUniformMatrix4fv(glGetUniformLocation(text_program, "transform"), 1, GL_FALSE, &transform[0][0]); + transforms[idx] = glm::translate(glm::mat4(1.0f), glm::vec3(xpos, ypos, 0)) * glm::scale(glm::mat4(1.0f), glm::vec3(256 * scale, 256 * scale, 0)); + letter_map[idx] = charglyph.tex; - // update VBO for each character - // float vertices[4][4] = { - // { xpos, ypos + h, 0.0f, 0.0f }, - // { xpos, ypos, 0.0f, 1.0f }, - // { xpos + w, ypos, 1.0f, 1.0f }, - // { xpos + w, ypos + h, 1.0f, 0.0f } - // }; - // render glyph texture over quad - glBindTexture(GL_TEXTURE_2D, charglyph.tex); - // update content of VBO memory - glBindBuffer(GL_ARRAY_BUFFER, textvbo); - //glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices), vertices); - - // render quad - glEnable(GL_CULL_FACE); - glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, 1); - glDisable(GL_CULL_FACE); // now advance cursors for next glyph (note that advance is number of 1/64 pixels) x += (charglyph.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64) + ++idx; } + glUniformMatrix4fv(glGetUniformLocation(text_program, "transforms"), idx, GL_FALSE, &transforms[0][0][0]); + glUniform1iv(glGetUniformLocation(text_program, "letter_map"), idx, &letter_map[0]); + glDrawArraysInstanced(GL_TRIANGLE_FAN, 0, 4, idx); + glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); glBindTexture(GL_TEXTURE_2D, 0); @@ -413,10 +415,15 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv) MAIN_ERROR(ERR_FONT_INIT); } - FT_Set_Pixel_Sizes(face, 0, 48); + FT_Set_Pixel_Sizes(face, 256, 256); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction + glGenTextures(1, &texture_array); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_R8, 256, 256, 128, 0, GL_RED, GL_UNSIGNED_BYTE, 0); + for (unsigned char c = 0; c < 128; c++) { // load character glyph @@ -424,32 +431,24 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv) { MAIN_ERROR(ERR_CHAR_LOAD); } - // generate texture - uint32_t texture; - glGenTextures(1, &texture); - glBindTexture(GL_TEXTURE_2D, texture); - glTexImage2D( - GL_TEXTURE_2D, - 0, - GL_RED, - face->glyph->bitmap.width, - face->glyph->bitmap.rows, - 0, - GL_RED, - GL_UNSIGNED_BYTE, - face->glyph->bitmap.buffer - ); + + glTexSubImage3D(GL_TEXTURE_2D_ARRAY,0, 0, 0, int(c), + face->glyph->bitmap.width, + face->glyph->bitmap.rows, + 1, GL_RED, GL_UNSIGNED_BYTE, + face->glyph->bitmap.buffer); + // set texture options - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + 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_LINEAR); + glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // now store character for later use charglyph_t charglyph = { - texture, - glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), - glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), - (uint32_t)face->glyph->advance.x + int(c), + glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows), + glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top), + (uint32_t)face->glyph->advance.x }; glyphmap[c] = charglyph; } @@ -460,6 +459,12 @@ int main([[maybe_unused]]int argc, [[maybe_unused]]char **argv) // text initialization text_program = make_gl_program("shaders/text.vert", "shaders/text.frag"); + for (int i = 0; i < ARRAY_LIMIT; ++i) + { + letter_map[i] = 0; + transforms[i] = glm::mat4(1.0f); + } + static GLfloat vertex_data[] = { 0.0f, 1.0f, 0.0f, 0.0f, diff --git a/shaders/text.frag b/shaders/text.frag index 66a51e1..5be854f 100644 --- a/shaders/text.frag +++ b/shaders/text.frag @@ -1,12 +1,18 @@ #version 330 core -in vec2 texuv; + out vec4 color; -uniform sampler2D text; +in VS_OUT { + vec2 texuv; + flat int index; +} fs_in; + +uniform sampler2DArray text; +uniform int letter_map[400]; uniform vec3 text_color; void main() -{ - vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, texuv).r); +{ + vec4 sampled = vec4(1.0, 1.0, 1.0, texture(text, vec3(fs_in.texuv.xy, letter_map[fs_in.index])).r); color = vec4(text_color, 1.0) * sampled; } diff --git a/shaders/text.vert b/shaders/text.vert index 49c10a1..d33c597 100644 --- a/shaders/text.vert +++ b/shaders/text.vert @@ -1,13 +1,17 @@ #version 330 core layout (location = 0) in vec2 vertex; // -out vec2 texuv; -uniform mat4 transform; +out VS_OUT { + vec2 texuv; + flat int index; +} vs_out; + +uniform mat4 transforms[400]; uniform mat4 projection; void main() { - gl_Position = projection * transform * vec4(vertex.xy, 0.0, 1.0); - texuv = vertex.xy; - texuv.y=1.0f-texuv.y; + gl_Position = projection * transforms[gl_InstanceID] * vec4(vertex.xy, 0.0, 1.0); + vs_out.index = gl_InstanceID; + vs_out.texuv = vec2(vertex.x, 1.0f - vertex.y); }