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