BsTextSprite.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include "BsTextSprite.h"
  2. #include "BsGUIMaterialManager.h"
  3. #include "BsTextData.h"
  4. #include "BsFont.h"
  5. #include "BsVector2.h"
  6. #include "BsProfilerCPU.h" // PROFILING ONLY
  7. namespace BansheeEngine
  8. {
  9. TextSprite::TextSprite()
  10. {
  11. }
  12. TextSprite::~TextSprite()
  13. {
  14. clearMesh();
  15. }
  16. void TextSprite::update(const TEXT_SPRITE_DESC& desc, UINT64 groupId)
  17. {
  18. bs_frame_mark();
  19. {
  20. TextData<FrameAlloc> textData(desc.text, desc.font, desc.fontSize, desc.width, desc.height, desc.wordWrap, desc.wordBreak);
  21. UINT32 numLines = textData.getNumLines();
  22. UINT32 numPages = textData.getNumPages();
  23. // Free all previous memory
  24. for (auto& cachedElem : mCachedRenderElements)
  25. {
  26. if (cachedElem.vertices != nullptr) mAlloc.free(cachedElem.vertices);
  27. if (cachedElem.uvs != nullptr) mAlloc.free(cachedElem.uvs);
  28. if (cachedElem.indexes != nullptr) mAlloc.free(cachedElem.indexes);
  29. }
  30. mAlloc.clear();
  31. // Resize cached mesh array to needed size
  32. for (UINT32 i = numPages; i < (UINT32)mCachedRenderElements.size(); i++)
  33. {
  34. auto& renderElem = mCachedRenderElements[i];
  35. if (renderElem.matInfo.material != nullptr)
  36. GUIMaterialManager::instance().releaseMaterial(renderElem.matInfo);
  37. }
  38. if (mCachedRenderElements.size() != numPages)
  39. mCachedRenderElements.resize(numPages);
  40. // Actually generate a mesh
  41. UINT32 texPage = 0;
  42. for (auto& cachedElem : mCachedRenderElements)
  43. {
  44. UINT32 newNumQuads = textData.getNumQuadsForPage(texPage);
  45. cachedElem.vertices = (Vector2*)mAlloc.alloc(sizeof(Vector2) * newNumQuads * 4);
  46. cachedElem.uvs = (Vector2*)mAlloc.alloc(sizeof(Vector2) * newNumQuads * 4);
  47. cachedElem.indexes = (UINT32*)mAlloc.alloc(sizeof(UINT32) * newNumQuads * 6);
  48. cachedElem.numQuads = newNumQuads;
  49. const HTexture& tex = textData.getTextureForPage(texPage);
  50. bool getNewMaterial = false;
  51. if (cachedElem.matInfo.material == nullptr)
  52. getNewMaterial = true;
  53. else
  54. {
  55. const GUIMaterialInfo* matInfo = GUIMaterialManager::instance().findExistingTextMaterial(groupId, tex, desc.color);
  56. if (matInfo == nullptr)
  57. {
  58. getNewMaterial = true;
  59. }
  60. else
  61. {
  62. if (matInfo->material != cachedElem.matInfo.material)
  63. {
  64. GUIMaterialManager::instance().releaseMaterial(cachedElem.matInfo);
  65. getNewMaterial = true;
  66. }
  67. }
  68. }
  69. if (getNewMaterial)
  70. cachedElem.matInfo = GUIMaterialManager::instance().requestTextMaterial(groupId, tex, desc.color);
  71. texPage++;
  72. }
  73. // Calc alignment and anchor offsets and set final line positions
  74. for (UINT32 j = 0; j < numPages; j++)
  75. {
  76. SpriteRenderElement& renderElem = mCachedRenderElements[j];
  77. genTextQuads(j, textData, desc.width, desc.height, desc.horzAlign, desc.vertAlign, desc.anchor,
  78. renderElem.vertices, renderElem.uvs, renderElem.indexes, renderElem.numQuads);
  79. }
  80. }
  81. bs_frame_clear();
  82. updateBounds();
  83. }
  84. UINT32 TextSprite::genTextQuads(UINT32 page, const TextDataBase& textData, UINT32 width, UINT32 height,
  85. TextHorzAlign horzAlign, TextVertAlign vertAlign, SpriteAnchor anchor, Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 bufferSizeQuads)
  86. {
  87. UINT32 numLines = textData.getNumLines();
  88. UINT32 newNumQuads = textData.getNumQuadsForPage(page);
  89. Vector2I* alignmentOffsets = bs_stack_new<Vector2I>(numLines);
  90. getAlignmentOffsets(textData, width, height, horzAlign, vertAlign, alignmentOffsets);
  91. Vector2I offset = getAnchorOffset(anchor, width, height);
  92. UINT32 quadOffset = 0;
  93. for(UINT32 i = 0; i < numLines; i++)
  94. {
  95. const TextDataBase::TextLine& line = textData.getLine(i);
  96. UINT32 writtenQuads = line.fillBuffer(page, vertices, uv, indices, quadOffset, bufferSizeQuads);
  97. Vector2I position = offset + alignmentOffsets[i];
  98. UINT32 numVertices = writtenQuads * 4;
  99. for(UINT32 i = 0; i < numVertices; i++)
  100. {
  101. vertices[quadOffset * 4 + i].x += (float)position.x;
  102. vertices[quadOffset * 4 + i].y += (float)position.y;
  103. }
  104. quadOffset += writtenQuads;
  105. }
  106. bs_stack_delete(alignmentOffsets, numLines);
  107. return newNumQuads;
  108. }
  109. UINT32 TextSprite::genTextQuads(const TextDataBase& textData, UINT32 width, UINT32 height,
  110. TextHorzAlign horzAlign, TextVertAlign vertAlign, SpriteAnchor anchor, Vector2* vertices, Vector2* uv, UINT32* indices, UINT32 bufferSizeQuads)
  111. {
  112. UINT32 numLines = textData.getNumLines();
  113. UINT32 numPages = textData.getNumPages();
  114. Vector2I* alignmentOffsets = bs_stack_new<Vector2I>(numLines);
  115. getAlignmentOffsets(textData, width, height, horzAlign, vertAlign, alignmentOffsets);
  116. Vector2I offset = getAnchorOffset(anchor, width, height);
  117. UINT32 quadOffset = 0;
  118. for(UINT32 i = 0; i < numLines; i++)
  119. {
  120. const TextDataBase::TextLine& line = textData.getLine(i);
  121. for(UINT32 j = 0; j < numPages; j++)
  122. {
  123. UINT32 writtenQuads = line.fillBuffer(j, vertices, uv, indices, quadOffset, bufferSizeQuads);
  124. Vector2I position = offset + alignmentOffsets[i];
  125. UINT32 numVertices = writtenQuads * 4;
  126. for(UINT32 k = 0; k < numVertices; k++)
  127. {
  128. vertices[quadOffset * 4 + k].x += (float)position.x;
  129. vertices[quadOffset * 4 + k].y += (float)position.y;
  130. }
  131. quadOffset += writtenQuads;
  132. }
  133. }
  134. bs_stack_delete(alignmentOffsets, numLines);
  135. return quadOffset;
  136. }
  137. void TextSprite::getAlignmentOffsets(const TextDataBase& textData,
  138. UINT32 width, UINT32 height, TextHorzAlign horzAlign, TextVertAlign vertAlign, Vector2I* output)
  139. {
  140. UINT32 numLines = textData.getNumLines();
  141. UINT32 curHeight = 0;
  142. for(UINT32 i = 0; i < numLines; i++)
  143. {
  144. const TextDataBase::TextLine& line = textData.getLine(i);
  145. curHeight += line.getYOffset();
  146. }
  147. // Calc vertical alignment offset
  148. UINT32 vertDiff = std::max(0U, height - curHeight);
  149. UINT32 vertOffset = 0;
  150. switch(vertAlign)
  151. {
  152. case TVA_Top:
  153. vertOffset = 0;
  154. break;
  155. case TVA_Bottom:
  156. vertOffset = std::max(0, (INT32)vertDiff);
  157. break;
  158. case TVA_Center:
  159. vertOffset = std::max(0, (INT32)vertDiff) / 2;
  160. break;
  161. }
  162. // Calc horizontal alignment offset
  163. UINT32 curY = 0;
  164. for(UINT32 i = 0; i < numLines; i++)
  165. {
  166. const TextDataBase::TextLine& line = textData.getLine(i);
  167. UINT32 horzOffset = 0;
  168. switch(horzAlign)
  169. {
  170. case THA_Left:
  171. horzOffset = 0;
  172. break;
  173. case THA_Right:
  174. horzOffset = std::max(0, (INT32)(width - line.getWidth()));
  175. break;
  176. case THA_Center:
  177. horzOffset = std::max(0, (INT32)(width - line.getWidth())) / 2;
  178. break;
  179. }
  180. output[i] = Vector2I(horzOffset, vertOffset + curY);
  181. curY += line.getYOffset();
  182. }
  183. }
  184. void TextSprite::clearMesh()
  185. {
  186. for (auto& renderElem : mCachedRenderElements)
  187. {
  188. if (renderElem.vertices != nullptr)
  189. {
  190. mAlloc.free(renderElem.vertices);
  191. renderElem.vertices = nullptr;
  192. }
  193. if (renderElem.uvs != nullptr)
  194. {
  195. mAlloc.free(renderElem.uvs);
  196. renderElem.uvs = nullptr;
  197. }
  198. if (renderElem.indexes != nullptr)
  199. {
  200. mAlloc.free(renderElem.indexes);
  201. renderElem.indexes = nullptr;
  202. }
  203. if (renderElem.matInfo.material != nullptr)
  204. {
  205. GUIMaterialManager::instance().releaseMaterial(renderElem.matInfo);
  206. }
  207. }
  208. mCachedRenderElements.clear();
  209. mAlloc.clear();
  210. updateBounds();
  211. }
  212. }