#include "client/FontRenderer.h" #include "io/ImageReader.h" #include "rendering/Shader.h" #include "rendering/Texture.h" #include "rendering/VertexBuffer.h" #include "utils/Logger.h" static constexpr float FONT_WIDTH = 8.0f; static constexpr float FONT_HEIGHT = 8.0f; static constexpr float FONT_STEP = 1.0f / 128.0f; static Shader shader; static VertexBuffer buffer; static Texture texture; static Buffer data{50}; static Color4 color; struct Symbol { int offsetX = 8; int width = 0; }; static Array symbols; bool FontRenderer::init() { Error e = shader.compile("resources/shader/fontTest.vs", "resources/shader/fontTest.fs"); if(e.has()) { LOG_ERROR(e.message); return true; } ImageReader::Image image; const char* path = "resources/font8x8.png"; e = ImageReader::load(image, path); if(e.has()) { LOG_ERROR(e.message); return true; } if(image.channels < 1 || image.channels > 4 || image.bitdepth != 8 || image.width != 128 || image.height != 128) { LOG_ERROR("font must have 1-4 channels, 8 bit per channel and a size " "of 128x128"); return true; } texture.init(Texture::Format::color8(image.channels), 0); int end = image.width * image.height; for(int i = 0; i < end; i++) { int c = image.data[i * image.channels]; if(c != 255) { continue; } int px = i % image.width; int py = i / image.width; int index = (py / 8) * 16 + (px / 8); symbols[index].offsetX = std::min(symbols[index].offsetX, px % 8); symbols[index].width = std::max(symbols[index].width, px % 8); } texture.setData(image.width, image.height, image.data); for(Symbol& s : symbols) { s.width = std::max(s.width - s.offsetX + 2, 0); } symbols[' '].offsetX = 0; symbols[' '].width = 4; buffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(2).addColor4()); return false; } void FontRenderer::bind() { shader.use(); texture.bindTo(0); } void FontRenderer::setProjection(const Matrix& m) { shader.setMatrix("proj", m); } void FontRenderer::setView(const Matrix& m) { shader.setMatrix("view", m); } void FontRenderer::setModel(const Matrix& m) { shader.setMatrix("model", m); } void FontRenderer::draw(const char* text, int alpha) { data.clear(); color = Color4(255, 255, 255, alpha); int index = 0; Vector3 pos; int vertices = 0; while(text[index] != '\0') { int i = text[index]; if(i < 0 && text[index + 1] != '\0') { index++; i = ((i & 0x1F) << 6) | (text[index] & 0x3F); } if(i < 0 || i >= 256) { continue; } Vector3 right = pos + Vector3(symbols[i].width, 0.0f, 0.0f); Vector3 down = pos + Vector3(0.0f, FONT_HEIGHT, 0.0f); Vector3 downRight = pos + Vector3(symbols[i].width, FONT_HEIGHT, 0.0f); Vector2 tPos((FONT_WIDTH * (i % 16) + symbols[i].offsetX) * FONT_STEP, FONT_HEIGHT * (i / 16) * FONT_STEP); Vector2 tRight = tPos + Vector2(symbols[i].width * FONT_STEP, 0.0f); Vector2 tDown = tPos + Vector2(0.0f, FONT_HEIGHT * FONT_STEP); Vector2 tDownRight = tPos + Vector2(symbols[i].width * FONT_STEP, FONT_HEIGHT * FONT_STEP); Color4 dark = Color4(color[0] / 2, color[1] / 2, color[2] / 2, color[3]); Vector3 shift(0.25f, 0.25f, 0.0f); data.add(shift + pos).add(tPos).add(dark); data.add(shift + down).add(tDown).add(dark); data.add(shift + right).add(tRight).add(dark); data.add(shift + down).add(tDown).add(dark); data.add(shift + right).add(tRight).add(dark); data.add(shift + downRight).add(tDownRight).add(dark); data.add(pos).add(tPos).add(color); data.add(down).add(tDown).add(color); data.add(right).add(tRight).add(color); data.add(down).add(tDown).add(color); data.add(right).add(tRight).add(color); data.add(downRight).add(tDownRight).add(color); pos += Vector3(symbols[i].width, 0.0f, 0.0f); vertices += 12; index++; } buffer.setData(data, GL::DYNAMIC_DRAW); buffer.draw(vertices); } float FontRenderer::getWidth(const char* text, int max) { int index = 0; float width = 0.0f; while(text[index] != '\0' && (max < 0 || index < max)) { int i = text[index]; if(i < 0 && text[index + 1] != '\0') { index++; i = ((i & 0x1F) << 6) | (text[index] & 0x3F); } if(i < 0 || i >= 256) { continue; } width += symbols[i].width; index++; } return width; } void FontRenderer::draw(const Vector2& size, Color4 c) { data.clear(); Vector3 pos; Vector3 right = pos + Vector3(size[0], 0.0f, 0.0f); Vector3 down = pos + Vector3(0.0f, size[1], 0.0f); Vector3 downRight = pos + Vector3(size[0], size[1], 0.0f); Vector2 tPos; Vector2 tRight(FONT_STEP, 0.0f); Vector2 tDown(0.0f, FONT_STEP); Vector2 tDownRight(FONT_STEP, FONT_STEP); data.add(pos).add(tPos).add(c); data.add(down).add(tDown).add(c); data.add(right).add(tRight).add(c); data.add(down).add(tDown).add(c); data.add(right).add(tRight).add(c); data.add(downRight).add(tDownRight).add(c); buffer.setData(data, GL::DYNAMIC_DRAW); buffer.draw(6); }