From d760d6857b10f5a40ce1e25978d3e8ad747025cd Mon Sep 17 00:00:00 2001 From: Dominic Masters Date: Thu, 15 Jun 2023 20:48:30 -0700 Subject: [PATCH] UI Label Wrap --- src/dawn/scene/components/ui/text/UILabel.cpp | 306 ++++++++++-------- src/dawn/scene/components/ui/text/UILabel.hpp | 25 +- src/dawnliminal/scenes/HelloWorldScene.hpp | 6 +- 3 files changed, 173 insertions(+), 164 deletions(-) diff --git a/src/dawn/scene/components/ui/text/UILabel.cpp b/src/dawn/scene/components/ui/text/UILabel.cpp index 7f97a535..5967b71e 100644 --- a/src/dawn/scene/components/ui/text/UILabel.cpp +++ b/src/dawn/scene/components/ui/text/UILabel.cpp @@ -15,7 +15,13 @@ UILabel::UILabel(SceneItem *item) : } void UILabel::onStart() { + UIComponentRenderable::onStart(); + this->shaderBuffer.init(); + + useEvent([&]{ + this->rebufferQuads(this->texts); + }, eventAlignmentUpdated); } std::vector UILabel::getUIRenderPasses() { @@ -68,163 +74,179 @@ std::vector UILabel::getUIRenderPasses() { } float_t UILabel::getContentWidth() { - return 0; + return 1; } float_t UILabel::getContentHeight() { - return 0; + return 1; } -void UILabel::rebufferQuads(std::vector newTexts) { - auto oldTexts = this->texts; - - textureMap.clear(); - glm::vec2 position(0, 0); - struct FontShaderBufferData fontData; - int32_t quadIndex = 0; - int32_t partIndex = 0; - quadCountTotal = 0; +void UILabel::rebufferQuads(const std::vector newTexts) { int32_t nextTexture = 0; - - // Determine how many quads there are, and the texture indexes. + glm::vec2 position(0, 0); + int32_t partIndex = 0; + std::vector> vertices; + struct FontShaderBufferData fontData; + quadCountTotal = 0; + std::vector realNewTexts; + + // Determine font dimensions. auto itText = newTexts.begin(); while(itText != newTexts.end()) { - quadCountTotal += itText->text.length(); - - // Determine font and lock it. - assertNotNull(itText->style.font); - itText->lockId = itText->style.font->lock(NewTrueTypeFaceTextureStyle{ - itText->style.size, - itText->style.style - }); - assertTrue(itText->lockId != -1); - itText->texture = itText->style.font->getTexture(itText->lockId); - - // Check for existing texture, if not, map it. - if(textureMap.find(itText->texture) == textureMap.end()) { - assertTrue(nextTexture < FONT_SHADER_TEXTURE_MAX); - textureMap[itText->texture] = nextTexture++; - } - + position.y = mathMax(position.y, itText->style.size); ++itText; } - // Cleanup old texst, we do this second so we don't unlock, cleanup, and then - // lock the same font, causing it to have to re-load. - itText = oldTexts.begin(); - while(itText != oldTexts.end()) { + // Now generate quads + itText = newTexts.begin(); + while(itText != newTexts.end()) { + auto text = *itText; + struct UILabelText realText; + + // Clone values + realText.style = text.style; + + // Lock the font + assertNotNull(text.style.font); + realText.lockId = text.style.font->lock(NewTrueTypeFaceTextureStyle{ + text.style.size, + text.style.style + }); + assertTrue(realText.lockId != -1); + realText.texture = text.style.font->getTexture(realText.lockId); + + // Map texture + if(textureMap.find(realText.texture) == textureMap.end()) { + assertTrue(nextTexture < FONT_SHADER_TEXTURE_MAX); + textureMap[realText.texture] = nextTexture++; + } + + // Buffer shader values + fontData.textures[partIndex] = textureMap[realText.texture]; + fontData.colors[partIndex] = realText.style.color; + + // Get some texture info + glm::vec2 wh = glm::vec2( + realText.texture->texture.getWidth(), + realText.texture->texture.getHeight() + ); + + // Now, iterate each character + auto len = text.text.length(); + int32_t lastSpaceCharacter = -1; + for(int32_t i = 0; i < len; i++) { + char ch = text.text[i]; + + // Handle newline + if(ch == '\n') { + position.x = 0; + position.y += realText.style.size; + + glm::vec4 uvs(0, 0, 1, 1); + glm::vec4 vert(0, 0, 0, 0); + vertices.push_back(std::make_pair(vert, uvs)); + fontData.fontQuadMappings[quadCountTotal] = partIndex; + quadCountTotal++; + realText.text += ch; + lastSpaceCharacter = i; + continue; + } else if(ch == ' ') { + lastSpaceCharacter = i; + } + + // Validate characters + assertTrue(ch >= NEW_TRUETYPE_CHAR_BEGIN && ch < NEW_TRUETYPE_CHAR_END); + assertTrue(ch != '\r'); + assertTrue(ch != '\t'); + assertTrue(ch != '\n'); + + FT_ULong c = ch; + auto charInfo = realText.texture->getCharacterData(c); + + // Word wrapping + if( + ch != ' ' && + lastSpaceCharacter != -1 && + this->width > charInfo.bitmapSize.x && + (position.x + charInfo.advanceX) > this->width + ) { + int32_t diff = i - lastSpaceCharacter; + for(int32_t k = 0; k < diff; k++) vertices.pop_back(); + text.text[lastSpaceCharacter] = '\n'; + i = lastSpaceCharacter - 1; + lastSpaceCharacter = -1; + quadCountTotal -= diff; + continue; + } + + // Buffer coordinates + glm::vec4 uvs; + uvs.x = 0.0f; + uvs.y = charInfo.textureY / wh.y; + uvs.w = charInfo.bitmapSize.x / wh.x; + uvs.z = uvs.y + (charInfo.bitmapSize.y / wh.y); + + glm::vec4 vert; + vert.x = position.x + charInfo.bitmapPosition.x; + vert.y = position.y + charInfo.bitmapPosition.y; + vert.w = vert.x + charInfo.bitmapSize.x; + vert.z = vert.y + charInfo.bitmapSize.y; + + // TODO:Check wordwrap here + vertices.push_back(std::make_pair(vert, uvs)); + + // Move the current position along. + position.x += charInfo.advanceX; + position.y += charInfo.advanceY; + + // Set the part index to the quad mappings + fontData.fontQuadMappings[quadCountTotal] = partIndex; + quadCountTotal++; + realText.text += ch; + } + + // Next + ++partIndex; + ++itText; + realNewTexts.push_back(realText); + } + + // Create mesh + this->mesh.createBuffers( + QUAD_VERTICE_COUNT * vertices.size(), + QUAD_INDICE_COUNT * vertices.size() + ); + + // Now buffer the quads. + int32_t j = 0; + auto itQuad = vertices.begin(); + while(itQuad != vertices.end()) { + auto vert = itQuad->first; + auto uvs = itQuad->second; + + QuadMesh::bufferQuadMeshWithZ(&this->mesh, + glm::vec2(vert.x, vert.y), glm::vec2(uvs.x, uvs.y), + glm::vec2(vert.w, vert.z), glm::vec2(uvs.w, uvs.z), + 0.0f, + j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT + ); + j++; + ++itQuad; + } + + // Buffer data + shaderBuffer.buffer(&fontData); + + // Finally, release the old locks + itText = textsBuffered.begin(); + while(itText != textsBuffered.end()) { assertTrue(itText->lockId != -1); assertNotNull(itText->style.font); itText->style.font->unlock(itText->lockId); ++itText; } - // Update texts. - this->texts = newTexts; - - // Create mesh - this->mesh.createBuffers( - QUAD_VERTICE_COUNT * quadCountTotal, - QUAD_INDICE_COUNT * quadCountTotal - ); - - // Buffer the text quads - itText = newTexts.begin(); - while(itText != newTexts.end()) { - position.y += itText->style.size; - quadIndex += this->bufferQuads( - *itText, - fontData, - textureMap, - position, - quadIndex, - partIndex - ); - // position.y -= itText->style.size; - ++partIndex; - ++itText; - } - - shaderBuffer.buffer(&fontData); -} - -int32_t UILabel::bufferQuads( - struct UILabelText text, - struct FontShaderBufferData &bufferData, - std::map &textureMap, - glm::vec2 &position, - int32_t quadStart, - int32_t partIndex -) { - // Get string length - int32_t len = text.text.length(); - - glm::vec2 wh = glm::vec2( - text.texture->texture.getWidth(), - text.texture->texture.getHeight() - ); - - // For each char - int32_t lastSpaceCharacter = -1; - for(int32_t i = 0; i < len; i++) { - char ch = text.text[i]; - - if(ch == '\n') { - position.x = 0; - position.y += text.style.size; - ch = ' '; - lastSpaceCharacter = i; - } else if(ch == ' ') { - lastSpaceCharacter = i; - } - - // Invalid/Unsupported chars - assertTrue(ch >= NEW_TRUETYPE_CHAR_BEGIN && ch < NEW_TRUETYPE_CHAR_END); - assertTrue(ch != '\r'); - assertTrue(ch != '\t'); - - int32_t j = quadStart + i; - FT_ULong c = ch; - auto charInfo = text.texture->getCharacterData(c); - - // Word wrapping - if( - lastSpaceCharacter != -1 && - this->width > 0 && - (position.x+charInfo.advanceX) > this->width - ) { - text.text[lastSpaceCharacter] = '\n'; - i = lastSpaceCharacter - 1; - lastSpaceCharacter = -1; - continue; - } - - // Determine texture coordinates. - glm::vec2 uv0 = glm::vec2(0.0f, charInfo.textureY) / wh; - glm::vec2 uv1 = uv0 + (charInfo.bitmapSize / wh); - - // Buffer the quad. - assertTrue(j < FONT_SHADER_QUADS_MAX); - QuadMesh::bufferQuadMeshWithZ(&this->mesh, - position + charInfo.bitmapPosition, uv0, - position + charInfo.bitmapPosition + charInfo.bitmapSize, uv1, - 0.0f, - j * QUAD_VERTICE_COUNT, j * QUAD_INDICE_COUNT - ); - - // Move the current position along. - position.x += charInfo.advanceX; - position.y += charInfo.advanceY; - - // Set the part index to the quad mappings - bufferData.fontQuadMappings[j] = partIndex; - } - - // Map texture level values - auto textureId = textureMap.find(text.texture); - assertTrue(textureId != textureMap.end()); - bufferData.textures[partIndex] = textureId->second; - bufferData.colors[partIndex] = text.style.color; - - return len; + // Update + textsBuffered = realNewTexts; + texts = newTexts; } \ No newline at end of file diff --git a/src/dawn/scene/components/ui/text/UILabel.hpp b/src/dawn/scene/components/ui/text/UILabel.hpp index d1edd548..9e015d02 100644 --- a/src/dawn/scene/components/ui/text/UILabel.hpp +++ b/src/dawn/scene/components/ui/text/UILabel.hpp @@ -22,6 +22,9 @@ namespace Dawn { struct UILabelText { std::string text; struct UILabelStyle style; + + glm::vec2 position; + glm::vec2 size; // Part index? // Quad start? @@ -39,29 +42,9 @@ namespace Dawn { Mesh mesh; FontShaderBuffer shaderBuffer; std::vector texts; + std::vector textsBuffered; std::map textureMap; - /** - * Buffers the quads for the given text and updates the progressing values - * as the buffer process continues. - * - * @param text Text information to buffer. - * @param bufferData The output quad mappings for the text. - * @param textureMap Texture map for the textures to map. - * @param position The 2D position to buffer the quads at. - * @param quadStart The starting quad index. - * @param partIndex The part index to store for each quad buffered. - * @return The number of quads buffered, not the string length. - */ - int32_t bufferQuads( - struct UILabelText text, - struct FontShaderBufferData &bufferData, - std::map &textureMap, - glm::vec2 &position, - int32_t quadStart, - int32_t partIndex - ); - public: int32_t quadStart = 0; int32_t quadCount = -1; diff --git a/src/dawnliminal/scenes/HelloWorldScene.hpp b/src/dawnliminal/scenes/HelloWorldScene.hpp index 7b1f46e1..24f6e209 100644 --- a/src/dawnliminal/scenes/HelloWorldScene.hpp +++ b/src/dawnliminal/scenes/HelloWorldScene.hpp @@ -23,10 +23,13 @@ namespace Dawn { auto canvasItem = this->createSceneItem(); auto canvas = canvasItem->addComponent(); + canvas->camera = camera; auto newLabelItem = this->createSceneItem(); - newLabelItem->transform.setParent(canvas->transform); auto newLabel = newLabelItem->addComponent(); + newLabel->alignment = glm::vec4(0, 0, 0, 0); + newLabel->alignX = UI_COMPONENT_ALIGN_STRETCH; + newLabel->alignY = UI_COMPONENT_ALIGN_STRETCH; // newLabel->maxWidth = 300.0f; newLabel->richText = std::string( "" @@ -44,6 +47,7 @@ namespace Dawn { "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " "" ); + newLabelItem->transform.setParent(canvas->transform); } std::vector getRequiredAssets() override {