diff options
Diffstat (limited to 'Client/Source/GUI/UITextMesh.cpp')
-rw-r--r-- | Client/Source/GUI/UITextMesh.cpp | 278 |
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); + } +} |