Some checks failed
Run ReCI Build Test / Explore-Gitea-Actions (push) Failing after 1m45s
217 lines
7.8 KiB
C++
217 lines
7.8 KiB
C++
#include <JGL/JGL.h>
|
|
|
|
|
|
#if __linux__
|
|
#include <freetype2/ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include FT_OUTLINE_H
|
|
#endif
|
|
|
|
#if _WIN32
|
|
#include <ft2build.h>
|
|
#include FT_FREETYPE_H
|
|
#include FT_OUTLINE_H
|
|
#endif
|
|
|
|
#include <JGL/types/Font.h>
|
|
#include <JGL/types/FontCache.h>
|
|
#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<GLfloat, 12> 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<std::array<GLfloat, 12>> vertices(text.size());
|
|
std::vector<std::array<GLfloat, 12>> 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<GLfloat, 12> 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<std::array<GLfloat, 18>> vertices(text.size());
|
|
std::vector<std::array<GLfloat, 12>> 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<GLfloat, 18> 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();
|
|
}
|
|
}
|