FontRenderer.cpp 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. #include "client/FontRenderer.h"
  2. #include "io/ImageReader.h"
  3. #include "rendering/Shader.h"
  4. #include "rendering/Texture.h"
  5. #include "rendering/VertexBuffer.h"
  6. #include "utils/Logger.h"
  7. static constexpr float FONT_WIDTH = 8.0f;
  8. static constexpr float FONT_HEIGHT = 8.0f;
  9. static constexpr float FONT_STEP = 1.0f / 128.0f;
  10. static Shader shader;
  11. static VertexBuffer buffer;
  12. static Texture texture;
  13. static Buffer data{50};
  14. static Color4 color;
  15. struct Symbol {
  16. int offsetX = 8;
  17. int width = 0;
  18. };
  19. static Array<Symbol, 256> symbols;
  20. bool FontRenderer::init() {
  21. Error e = shader.compile("resources/shader/fontTest.vs",
  22. "resources/shader/fontTest.fs");
  23. if(e.has()) {
  24. LOG_ERROR(e.message);
  25. return true;
  26. }
  27. ImageReader::Image image;
  28. const char* path = "resources/font8x8.png";
  29. e = ImageReader::load(image, path);
  30. if(e.has()) {
  31. LOG_ERROR(e.message);
  32. return true;
  33. }
  34. if(image.channels < 1 || image.channels > 4 || image.bitdepth != 8 ||
  35. image.width != 128 || image.height != 128) {
  36. LOG_ERROR("font must have 1-4 channels, 8 bit per channel and a size "
  37. "of 128x128");
  38. return true;
  39. }
  40. texture.init(Texture::Format::color8(image.channels), 0);
  41. int end = image.width * image.height;
  42. for(int i = 0; i < end; i++) {
  43. int c = image.data[i * image.channels];
  44. if(c != 255) {
  45. continue;
  46. }
  47. int px = i % image.width;
  48. int py = i / image.width;
  49. int index = (py / 8) * 16 + (px / 8);
  50. symbols[index].offsetX = std::min(symbols[index].offsetX, px % 8);
  51. symbols[index].width = std::max(symbols[index].width, px % 8);
  52. }
  53. texture.setData(image.width, image.height, image.data);
  54. for(Symbol& s : symbols) {
  55. s.width = std::max(s.width - s.offsetX + 2, 0);
  56. }
  57. symbols[' '].offsetX = 0;
  58. symbols[' '].width = 4;
  59. buffer.init(VertexBuffer::Attributes().addFloat(3).addFloat(2).addColor4());
  60. return false;
  61. }
  62. void FontRenderer::bind() {
  63. shader.use();
  64. texture.bindTo(0);
  65. }
  66. void FontRenderer::setProjection(const Matrix& m) {
  67. shader.setMatrix("proj", m);
  68. }
  69. void FontRenderer::setView(const Matrix& m) {
  70. shader.setMatrix("view", m);
  71. }
  72. void FontRenderer::setModel(const Matrix& m) {
  73. shader.setMatrix("model", m);
  74. }
  75. void FontRenderer::draw(const char* text, int alpha) {
  76. data.clear();
  77. color = Color4(255, 255, 255, alpha);
  78. int index = 0;
  79. Vector3 pos;
  80. int vertices = 0;
  81. while(text[index] != '\0') {
  82. int i = text[index];
  83. if(i < 0 && text[index + 1] != '\0') {
  84. index++;
  85. i = ((i & 0x1F) << 6) | (text[index] & 0x3F);
  86. }
  87. if(i < 0 || i >= 256) {
  88. continue;
  89. }
  90. Vector3 right = pos + Vector3(symbols[i].width, 0.0f, 0.0f);
  91. Vector3 down = pos + Vector3(0.0f, FONT_HEIGHT, 0.0f);
  92. Vector3 downRight = pos + Vector3(symbols[i].width, FONT_HEIGHT, 0.0f);
  93. Vector2 tPos((FONT_WIDTH * (i % 16) + symbols[i].offsetX) * FONT_STEP,
  94. FONT_HEIGHT * (i / 16) * FONT_STEP);
  95. Vector2 tRight = tPos + Vector2(symbols[i].width * FONT_STEP, 0.0f);
  96. Vector2 tDown = tPos + Vector2(0.0f, FONT_HEIGHT * FONT_STEP);
  97. Vector2 tDownRight = tPos + Vector2(symbols[i].width * FONT_STEP,
  98. FONT_HEIGHT * FONT_STEP);
  99. Color4 dark =
  100. Color4(color[0] / 2, color[1] / 2, color[2] / 2, color[3]);
  101. Vector3 shift(0.25f, 0.25f, 0.0f);
  102. data.add(shift + pos).add(tPos).add(dark);
  103. data.add(shift + down).add(tDown).add(dark);
  104. data.add(shift + right).add(tRight).add(dark);
  105. data.add(shift + down).add(tDown).add(dark);
  106. data.add(shift + right).add(tRight).add(dark);
  107. data.add(shift + downRight).add(tDownRight).add(dark);
  108. data.add(pos).add(tPos).add(color);
  109. data.add(down).add(tDown).add(color);
  110. data.add(right).add(tRight).add(color);
  111. data.add(down).add(tDown).add(color);
  112. data.add(right).add(tRight).add(color);
  113. data.add(downRight).add(tDownRight).add(color);
  114. pos += Vector3(symbols[i].width, 0.0f, 0.0f);
  115. vertices += 12;
  116. index++;
  117. }
  118. buffer.setData(data, GL::DYNAMIC_DRAW);
  119. buffer.draw(vertices);
  120. }
  121. float FontRenderer::getWidth(const char* text, int max) {
  122. int index = 0;
  123. float width = 0.0f;
  124. while(text[index] != '\0' && (max < 0 || index < max)) {
  125. int i = text[index];
  126. if(i < 0 && text[index + 1] != '\0') {
  127. index++;
  128. i = ((i & 0x1F) << 6) | (text[index] & 0x3F);
  129. }
  130. if(i < 0 || i >= 256) {
  131. continue;
  132. }
  133. width += symbols[i].width;
  134. index++;
  135. }
  136. return width;
  137. }
  138. void FontRenderer::draw(const Vector2& size, Color4 c) {
  139. data.clear();
  140. Vector3 pos;
  141. Vector3 right = pos + Vector3(size[0], 0.0f, 0.0f);
  142. Vector3 down = pos + Vector3(0.0f, size[1], 0.0f);
  143. Vector3 downRight = pos + Vector3(size[0], size[1], 0.0f);
  144. Vector2 tPos;
  145. Vector2 tRight(FONT_STEP, 0.0f);
  146. Vector2 tDown(0.0f, FONT_STEP);
  147. Vector2 tDownRight(FONT_STEP, FONT_STEP);
  148. data.add(pos).add(tPos).add(c);
  149. data.add(down).add(tDown).add(c);
  150. data.add(right).add(tRight).add(c);
  151. data.add(down).add(tDown).add(c);
  152. data.add(right).add(tRight).add(c);
  153. data.add(downRight).add(tDownRight).add(c);
  154. buffer.setData(data, GL::DYNAMIC_DRAW);
  155. buffer.draw(6);
  156. }