#include "render/TextRenderer.h" #include #include FT_FREETYPE_H #include "Environment.h" #include "render/OpenGlExtensions.h" #include #include namespace ZL { TextRenderer::~TextRenderer() { for (auto& kv : glyphs) { if (kv.second.texID) glDeleteTextures(1, &kv.second.texID); } glyphs.clear(); if (vbo) glDeleteBuffers(1, &vbo); // if (vao) glDeleteVertexArrays(1, &vao); vao = 0; vbo = 0; } bool TextRenderer::init(Renderer& renderer, const std::string& ttfPath, int pixelSize) { r = &renderer; r->shaderManager.AddShaderFromFiles(shaderName, "resources/shaders/text2d.vertex", "resources/shaders/text2d.fragment", "" // ZIP пустой ); if (!loadGlyphs(ttfPath, pixelSize)) return false; // VAO/VBO для 6 вершин (2 треугольника) * (pos.xy + uv.xy) = 4 float // glGenVertexArrays(1, &vao); glGenBuffers(1, &vbo); //glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, nullptr, GL_DYNAMIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); return true; } bool TextRenderer::loadGlyphs(const std::string& ttfPath, int pixelSize) { FT_Library ft; if (FT_Init_FreeType(&ft)) { std::cerr << "FreeType: FT_Init_FreeType failed\n"; return false; } FT_Face face; if (FT_New_Face(ft, ttfPath.c_str(), 0, &face)) { std::cerr << "FreeType: failed to load font: " << ttfPath << "\n"; FT_Done_FreeType(ft); return false; } FT_Set_Pixel_Sizes(face, 0, pixelSize); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glyphs.clear(); for (unsigned char c = 32; c < 128; ++c) { if (FT_Load_Char(face, c, FT_LOAD_RENDER)) { continue; } unsigned int tex; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); 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 ); 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); GlyphInfo g; g.texID = tex; g.size = Eigen::Vector2f((float)face->glyph->bitmap.width, (float)face->glyph->bitmap.rows); g.bearing = Eigen::Vector2f((float)face->glyph->bitmap_left, (float)face->glyph->bitmap_top); g.advance = (unsigned int)face->glyph->advance.x; glyphs.emplace((char)c, g); } FT_Done_Face(face); FT_Done_FreeType(ft); glBindTexture(GL_TEXTURE_2D, 0); return true; } void TextRenderer::drawText(const std::string& text, float x, float y, float scale, bool centered, std::array color) { if (!r) return; // Считаем ширину строки для центрирования float totalW = 0.0f; if (centered) { for (char ch : text) { auto it = glyphs.find(ch); if (it == glyphs.end()) continue; totalW += (it->second.advance >> 6) * scale; } x -= totalW * 0.5f; } r->shaderManager.PushShader(shaderName); // Орто-проекция в пикселях: (0..W, 0..H) float W = (float)Environment::width; float H = (float)Environment::height; Eigen::Matrix4f proj = Eigen::Matrix4f::Identity(); proj(0,0) = 2.0f / W; proj(1,1) = 2.0f / H; proj(0,3) = -1.0f; proj(1,3) = -1.0f; // uProjection r->RenderUniformMatrix4fv("uProjection", false, proj.data()); r->RenderUniform1i("uText", 0); r->RenderUniform4fv("uColor", color.data()); glActiveTexture(GL_TEXTURE0); // glBindVertexArray(vao); glBindBuffer(GL_ARRAY_BUFFER, vbo); r->EnableVertexAttribArray("vPosition"); r->EnableVertexAttribArray("vTexCoord"); r->VertexAttribPointer2fv("vPosition", 4 * sizeof(float), (const char*)0); r->VertexAttribPointer2fv("vTexCoord", 4 * sizeof(float), (const char*)(2 * sizeof(float))); float penX = x; float penY = y; for (char ch : text) { auto it = glyphs.find(ch); if (it == glyphs.end()) continue; const GlyphInfo& g = it->second; float xpos = penX + g.bearing.x() * scale; float ypos = penY - (g.size.y() - g.bearing.y()) * scale; float w = g.size.x() * scale; float h = g.size.y() * scale; // 2 треугольника float verts[6][4] = { { xpos, ypos + h, 0.0f, 0.0f }, { xpos, ypos, 0.0f, 1.0f }, { xpos + w, ypos, 1.0f, 1.0f }, { xpos, ypos + h, 0.0f, 0.0f }, { xpos + w, ypos, 1.0f, 1.0f }, { xpos + w, ypos + h, 1.0f, 0.0f } }; glBindTexture(GL_TEXTURE_2D, g.texID); glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(verts), verts); glDrawArrays(GL_TRIANGLES, 0, 6); penX += (g.advance >> 6) * scale; } // cleanup glBindTexture(GL_TEXTURE_2D, 0); glBindBuffer(GL_ARRAY_BUFFER, 0); r->DisableVertexAttribArray("vPosition"); r->DisableVertexAttribArray("vTexCoord"); r->shaderManager.PopShader(); } } // namespace ZL