#include #if __linux__ #include #include FT_FREETYPE_H #include FT_OUTLINE_H #endif #if _WIN32 #include #include FT_FREETYPE_H #include FT_OUTLINE_H #endif #include #include #include "JGL/logger/logger.h" namespace JGL { CachedFont* CacheFont(const Font& font, u32 size) { CachedFont* cachedFont; FT_Set_Pixel_Sizes(font.face, 0, size); jlog::Debug("Caching font data..."); GLuint texture_id; glGenTextures(1, &texture_id); glBindTexture(GL_TEXTURE_2D, texture_id); 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, GL_TEXTURE_BASE_LEVEL, 0); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0); GLsizei width = 0; GLsizei max_height = 0; FT_ULong charcode; FT_UInt gindex; //We have to loop over the available glyphs twice as we need the //final width and height of the texture_handle before we can construct it //and subsequently upload the glyph data. charcode = FT_Get_First_Char(font.face, &gindex); //Strings are char-based so we only handle charcodes within the extended ASCII range. while (gindex != 0 && charcode < 255) { if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER)) std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl; FT_GlyphSlot g = font.face->glyph; width += g->bitmap.width; max_height = std::max(max_height, (GLsizei) g->bitmap.rows); charcode = FT_Get_Next_Char(font.face, charcode, &gindex); } fontCache.newFont(texture_id, width, max_height, size, font.index); cachedFont = fontCache.getFont(size, font.index); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, width, max_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, nullptr); GLsizei xoffset = 0; charcode = FT_Get_First_Char(font.face, &gindex); while (gindex != 0 && charcode < 255) { if (FT_Load_Char(font.face, charcode, FT_LOAD_RENDER)) std::cout << "Error::FREETYPE: Failed to load charcode: " << charcode << std::endl; FT_GlyphSlot g = font.face->glyph; glTexSubImage2D(GL_TEXTURE_2D, 0, xoffset, 0, g->bitmap.width, g->bitmap.rows, GL_ALPHA, GL_UNSIGNED_BYTE, g->bitmap.buffer); GLfloat u0 = (GLfloat)xoffset / cachedFont->getTextureWidth(); GLfloat u1 = u0 + (GLfloat)g->bitmap.width / cachedFont->getTextureWidth(); GLfloat v0 = 0.0f; GLfloat v1 = (GLfloat)g->bitmap.rows / cachedFont->getTextureHeight(); std::array texcoords { u0, v0, u0, v1, u1, v1, u0, v0, u1, v1, u1, v0 }; cachedFont->appendGlyph(new CachedGlyph((char)charcode, texcoords, g->bitmap_left, g->bitmap_top, g->bitmap.width, g->bitmap.rows, (g->advance.x >> 6), (g->advance.y >> 6))); xoffset += g->bitmap.width; charcode = FT_Get_Next_Char(font.face, charcode, &gindex); } return cachedFont; } void J2D::DrawString(const Color4& color, const std::string& text, float x, float y, float scale, u32 size, const Font& font) { // Offset by height to render at "correct" location. y += size; CachedFont* cachedFont = fontCache.getFont(size, font.index); if (font.face == nullptr) jlog::Fatal("Drawing a string with an uninitialized font?"); //If the font doesn't exist in the cache yet. if (!cachedFont) cachedFont = CacheFont(font, size); glColor4ubv(color.ptr()); //Texture parameters are restored when the texture_handle is bound glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture()); std::vector> vertices(text.size()); std::vector> texcoords(text.size()); for (int i = 0; i < text.length(); i++) { float x2, y2, w, h; CachedGlyph* glyph = cachedFont->getGlyph(text.c_str()[i]); if (glyph == nullptr) continue; x2 = x + glyph->x2offset * scale; y2 = y - glyph->y2offset * scale; // Adjust y-coordinate w = glyph->w * scale; h = glyph->h * scale; x += glyph->advanceX * scale; y += glyph->advanceY * scale; std::array glyph_vertices { x2, y2, x2, y2 + h, x2 + w, y2 + h, x2, y2, x2 + w, y2 + h, x2 + w, y2 }; auto glyph_texcoords = glyph->getTexCoords(); vertices[i] = glyph_vertices; texcoords[i] = glyph_texcoords; } glVertexPointer(2, GL_FLOAT, sizeof(Vector2), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data()); glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); glBindTexture(GL_TEXTURE_2D, 0); glColor4f(1, 1, 1, 1); } void J3D::DrawString(const Color4& color, const std::string& text, const Vector3& pos, float scale, u32 size, const Font& font, const EulerAngle& angle, bool draw_back_face) { // TODO: Determine the proper scale factor mathematically scale = scale * 0.002f; scale = -scale; float x = pos.x; float y = pos.y; float z = pos.z; CachedFont* cachedFont = fontCache.getFont(size, font.index); if (font.face == nullptr) jlog::Fatal("Drawing a string with an uninitialized font?"); if (!cachedFont) cachedFont = CacheFont(font, size); glColor4ubv(color.ptr()); glBindTexture(GL_TEXTURE_2D, *cachedFont->getTexture()); std::vector> vertices(text.size()); std::vector> texcoords(text.size()); glPushMatrix(); glTranslatef(x, y, z); glRotatef(angle.pitch, 1.0f, 0.0f, 0.0f); glRotatef(angle.yaw, 0.0f, 1.0f, 0.0f); glRotatef(angle.roll, 0.0f, 0.0f, 1.0f); x = y = z = 0; for (int i = 0; i < text.length(); i++) { CachedGlyph* glyph = cachedFont->getGlyph(text[i]); float x2 = x + glyph->x2offset * scale; float y2 = y - glyph->y2offset * scale; float w = glyph->w * scale; float h = glyph->h * scale; std::array glyph_vertices { x2, y2, z, x2, y2 + h, z, x2 + w, y2 + h, z, x2, y2, z, x2 + w, y2 + h, z, x2 + w, y2, z }; vertices[i] = glyph_vertices; texcoords[i] = glyph->getTexCoords(); x += glyph->advanceX * scale; y += glyph->advanceY * scale; } glVertexPointer(3, GL_FLOAT, sizeof(Vector3), vertices.data()); glTexCoordPointer(2, GL_FLOAT, sizeof(Vector2), texcoords.data()); if (!draw_back_face) glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glDrawArrays(GL_TRIANGLES, 0, (int) vertices.size() * 6); if (!draw_back_face) glDisable(GL_CULL_FACE); glBindTexture(GL_TEXTURE_2D, 0); glColor4f(1, 1, 1, 1); glPopMatrix(); } }