summaryrefslogtreecommitdiff
path: root/Client/Source/GUI/UITextMesh.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Client/Source/GUI/UITextMesh.cpp')
-rw-r--r--Client/Source/GUI/UITextMesh.cpp278
1 files changed, 278 insertions, 0 deletions
diff --git a/Client/Source/GUI/UITextMesh.cpp b/Client/Source/GUI/UITextMesh.cpp
new file mode 100644
index 0000000..13bd8dc
--- /dev/null
+++ b/Client/Source/GUI/UITextMesh.cpp
@@ -0,0 +1,278 @@
+#include "../Graphics/CustomVertexLayout.h"
+#include "../Utilities/StaticInitiator.h"
+#include "../Math/Math.h"
+#include "../Graphics/Color.h"
+#include "../Graphics/GfxDevice.h"
+#include "../Utilities/AutoInvoke.h"
+#include "../Graphics/DefaultVertexLayout.h"
+#include "../Debug/Log.h"
+
+#include "UITextMesh.h"
+
+#include <vector>
+#include <unordered_map>
+
+using namespace std;
+
+struct TextMeshVBOLayout
+{
+ Vector2f position;
+ Vector2f uv;
+ Color32 color;
+};
+
+static CustomVertexLayout s_TextMeshVBOLayout;
+
+static unsigned int s_VertexPerText;
+static unsigned int s_SizePerVertex;
+static unsigned int s_SizePerText;
+static unsigned int s_SizePerIndex;
+static unsigned int s_IndicesPerText;
+
+struct TextInfo {
+ const Character* ch;
+ float offset;
+ int line; // 从0开始
+};
+
+InitializeStaticVariables([]() {
+ VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout));
+ VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout));
+ VertexAttributeDescriptor COLOR = VertexAttributeDescriptor(sizeof(Vector2f)*2, 4, VertexAttrFormat_Unsigned_Byte, sizeof(TextMeshVBOLayout), true);
+ s_TextMeshVBOLayout.attributes.push_back(POSITION);
+ s_TextMeshVBOLayout.attributes.push_back(UV);
+ s_TextMeshVBOLayout.attributes.push_back(COLOR);
+
+ s_VertexPerText = 4;
+ s_SizePerVertex = sizeof(TextMeshVBOLayout);
+
+ s_IndicesPerText = 6;
+ s_SizePerIndex = VertexLayout::GetDefaultIndexSize();
+
+ s_SizePerText = sizeof(TextMeshVBOLayout) * 4;
+});
+
+UITextMesh::UITextMesh(
+ const UnicodeString& str // 文本
+ , Font* font // 字体
+ , int pixelSize // 大小
+ , int lineHeight // 行高
+ , Color32 color32 // 颜色
+ , ETextAnchor anchor // 锚点
+ , ETextAlignment alignment // 对齐方式
+ , bool wordwrap // 自动换行
+ , float preferred // 自动换行区域大小
+){
+ m_Font = font;
+ m_PixelSize = pixelSize;
+ m_LineHeight = lineHeight;
+ m_Color = color32;
+ m_Alignment = alignment;
+ m_Anchor = anchor;
+ m_Wordwrap = wordwrap;
+ m_Preferred = preferred;
+ m_Content.length = str.length;
+ m_Content.str = (character::Unicode*)malloc(str.length * sizeof(character::Unicode));
+ memcpy(m_Content.str, str.str, str.length * sizeof(character::Unicode));
+
+ // 记录文本按atlas分类
+ static unordered_map<unsigned int, vector<TextInfo>> s_TextInfos;
+ s_TextInfos.clear();
+
+ // 记录文本每行的长度
+ static unordered_map<int, int> s_LineWidths;
+ s_LineWidths.clear();
+
+ // 记录每行的偏移长度
+ static unordered_map<int, int> s_LineOffsets;
+ s_LineOffsets.clear();
+
+ InvokeWhenLeave([]() {
+ s_LineWidths.clear();
+ s_LineOffsets.clear();
+ s_TextInfos.clear();
+ });
+
+ const Vector2f atlasSize = font->GetAtlasSize();
+
+ //-----------------------------------------------------------------
+ // 处理了换行和自动换行之后的文本区域大小
+ Vector2f textRegion;
+ // 按照不同的atlas分类到s_TextInfos
+ float offset = 0;
+ int line = 0;
+ for (int i = 0; i < str.length; ++i)
+ {
+ character::Unicode c = str.str[i];
+ const Character* ch = font->GetCharacter(c, pixelSize);
+ if (ch == NULL)
+ continue;
+
+ if (wordwrap) // 自动换行
+ {
+ if (offset + ch->bearing.x + ch->position.width > preferred)
+ {
+
+ ++line;
+ offset = 0;
+ }
+ }
+
+ unsigned int atlasIndex = ch->atlas;
+ if (atlasIndex != FONT_NOT_IN_ATLAS_PLACEHOLDER) //非空格
+ {
+// 换行符Unix'\n', Windows'\r\n', MacOS '\r'
+#define CHECK_BREAKS() \
+ if (c == '\n' || c == '\r') \
+ { \
+ ++line; \
+ offset = 0; \
+ if (c == '\r' && ((i + 1) < str.length && str.str[i + 1] == '\n')) /*skip\n*/ \
+ ++i; \
+ continue; \
+ }
+
+ CHECK_BREAKS();
+
+ TextInfo info;
+ info.ch = ch;
+ info.offset = offset;
+ info.line = line;
+
+ auto list = s_TextInfos.find(atlasIndex);
+ if (list == s_TextInfos.end())
+ s_TextInfos.insert(std::pair<unsigned int, vector<TextInfo>>(atlasIndex, vector<TextInfo>()));
+
+ vector<TextInfo>& v = s_TextInfos[atlasIndex];
+ v.push_back(info);
+ }
+ else
+ {
+ // 有些字体换行没有字形,所以也需要在这里处理
+ CHECK_BREAKS();
+ }
+
+ offset += ch->advance;
+
+ textRegion.x = max(offset, textRegion.x);
+
+ if (s_LineWidths.count(line) == 0)
+ s_LineWidths.insert(std::pair<int, int>(line, 0));
+ s_LineWidths[line] = max(offset, s_LineWidths[line]);
+ }
+
+ textRegion.y = (line + 1) * lineHeight;
+
+ if (s_TextInfos.size() == 0)
+ {
+ return;
+ }
+
+ Vector2i textOffset;
+ Vector2f halfRegion = Vector2f(textRegion.x/ 2.f, textRegion.y / 2.f);
+ switch (anchor)
+ {
+ case TextAnchor_UpperLeft: textOffset.Set(0, 0); break;
+ case TextAnchor_UpperCenter: textOffset.Set(-halfRegion.x, 0); break;
+ case TextAnchor_UpperRight: textOffset.Set(-textRegion.x, 0); break;
+ case TextAnchor_MiddleLeft: textOffset.Set(0, -halfRegion.y); break;
+ case TextAnchor_MiddleCenter: textOffset.Set(-halfRegion.x, -halfRegion.y); break;
+ case TextAnchor_MiddleRight: textOffset.Set(-textRegion.x, -halfRegion.y); break;
+ case TextAnchor_LowerLeft: textOffset.Set(0, -textRegion.y); break;
+ case TextAnchor_LowerCenter: textOffset.Set(-halfRegion.x, -textRegion.y); break;
+ case TextAnchor_LowerRight: textOffset.Set(-textRegion.x, -textRegion.y); break;
+ }
+
+ for (int i = 0; i < line; ++i)
+ {
+ int lineLen = s_LineWidths.count(i) != 0 ? s_LineWidths[i] : 0;
+ int lineOffset = 0;
+ switch (alignment)
+ {
+ case TextAlignment_Left: lineOffset = 0; break;
+ case TextAlignment_Center: lineOffset = (textRegion.x - lineLen)/2.f; break;
+ case TextAlignment_Right: lineOffset = textRegion.x - lineLen; break;
+ }
+ s_LineOffsets.insert(std::pair<int, int>(i, lineOffset));
+ }
+
+ //-----------------------------------------------------------------
+
+ // 填充VBO和IBO
+ for (auto iter : s_TextInfos) {
+ unsigned int atlasIndex = iter.first; // atlas atlasIndex
+ vector<TextInfo>& texts = iter.second;
+ int textCount = texts.size();
+
+ VertexBuffer* vb = new VertexBuffer(textCount * s_SizePerText, textCount * s_IndicesPerText * s_SizePerIndex, VertexBuffer::VertexBufferType_Static);
+ void* pVB;
+ uint16* pIB;
+
+ vb->GetChunk(s_SizePerVertex, s_SizePerIndex, s_VertexPerText * textCount, s_IndicesPerText * textCount, EPrimitive::Primitive_Triangle, &pVB,(void**) &pIB);
+
+ TextMeshVBOLayout* dst = (TextMeshVBOLayout*)pVB;
+ for (int i = 0; i < textCount; ++i)
+ {
+ TextInfo& text = texts[i];
+
+ int vOff = i * s_VertexPerText;
+ int lineXOff = s_LineOffsets.count(text.line) ? s_LineOffsets[text.line] : 0;
+ int lineYOff = text.line * lineHeight;
+ // 左上角是原点
+ float pos[] = {
+ textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-left
+ textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-right
+ textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-right
+ textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-left
+ };
+ Vector4f uvQuad = Vector4f(text.ch->position.x / atlasSize.x, text.ch->position.y / atlasSize.y, text.ch->position.width / atlasSize.x, text.ch->position.height / atlasSize.y);
+ float uv[] = {
+ uvQuad.x, uvQuad.y + uvQuad.w,
+ uvQuad.x + uvQuad.z, uvQuad.y + uvQuad.w,
+ uvQuad.x + uvQuad.z, uvQuad.y,
+ uvQuad.x, uvQuad.y,
+ };
+ for (int j = 0; j < s_VertexPerText; ++j)
+ {
+ dst[vOff + j].position.Set(pos[2 * j], pos[2 * j + 1]);
+ dst[vOff + j].uv.Set(uv[2 * j], uv[2 * j + 1]);
+ dst[vOff + j].color = color32;
+ }
+
+ int iOff = i * s_IndicesPerText;
+ int indices[] = {
+ 0, 1, 3, // right-top
+ 1, 2, 3, // left-bottom
+ };
+ for (int j = 0; j < s_IndicesPerText; ++j)
+ pIB[iOff + j] = vOff + indices[j];
+ }
+
+ vb->FlushChunk(s_VertexPerText * textCount, s_IndicesPerText * textCount);
+
+ m_VBOs.insert(std::pair<int, VertexBuffer*>(atlasIndex, vb));
+ }
+
+ WipeGLError();
+}
+
+void UITextMesh::Draw() const
+{
+ for (auto subText : m_VBOs)
+ {
+ int atlasIndex = subText.first; // atlasIndex of atlas
+ VertexBuffer* vbo = subText.second;
+
+ const GlyphAtals* atlas = m_Font->GetGlyphAtlas(atlasIndex);
+ if (atlas == NULL)
+ {
+ log_error("Render text failed, no glyph atlas.");
+ continue;
+ }
+
+ g_GfxDevice.SetUniformTexture("gamelab_main_tex", atlas->altas);
+
+ WipeGLError();
+ vbo->Draw(s_TextMeshVBOLayout);
+ }
+}