// Copyright (c) 2023 Dominic Masters // // This software is released under the MIT License. // https://opensource.org/licenses/MIT #include "UILabelNew.hpp" #include "game/DawnGame.hpp" using namespace Dawn; UILabelNew::UILabelNew(SceneItem *item) : UIComponentRenderable(item) { } void UILabelNew::onStart() { this->shaderBuffer.init(); std::vector styleStack; struct UILabelStyle current; styleStack.push_back(current); std::vector texts; std::function parseChildren = [&](Xml *node) { if(node->children.empty()) { struct UILabelText text; text.style = current; text.text = node->value; texts.push_back(text); } else { auto itNode = node->children.begin(); while(itNode != node->children.end()) { auto child = *itNode; assertTrue(child->node == "font"); struct UILabelStyle style; if(child->attributes.contains("font")) { style.font = this->getGame()->assetManager.get(child->attributes["font"]); } else { style.font = current.font; } if(child->attributes.contains("size")) { style.size = std::stoi(child->attributes["size"]); } else { style.size = current.size; } if(child->attributes.contains("style")) { std::string s = child->attributes["style"]; style.style = 0; if(s.find("bold") != std::string::npos) style.style |= NEW_TRUETYPE_VARIANT_BOLD; if(s.find("italic") != std::string::npos) style.style |= NEW_TRUETYPE_VARIANT_ITALICS; } else { style.style = current.style; } if(child->attributes.contains("color")) { style.color = Color::fromString(child->attributes["color"]); } else { style.color = current.color; } styleStack.push_back(style); current = style; parseChildren(child); styleStack.pop_back(); current = styleStack.back(); ++itNode; } } }; auto root = Xml::load("" + this->test + ""); parseChildren(&root); this->rebufferQuads(texts); } std::vector UILabelNew::getUIRenderPasses() { // if(this->texts.size() == 0) return {}; auto canvas = this->getCanvas(); auto shader = getGame()->renderManager.fontShader; struct ShaderPassItem item; item.shader = shader; item.mesh = &this->mesh; item.matrixValues[shader->paramModel] = transform->getWorldTransform(); item.parameterBuffers[shader->bufferUiCanvas] = &canvas->shaderBuffer; item.parameterBuffers[shader->bufferFont] = &this->shaderBuffer; item.renderFlags = RENDER_MANAGER_RENDER_FLAG_BLEND; // Map texture slots auto it = textureMap.begin(); while(it != textureMap.end()) { shaderparameter_t param; switch(it->second) { case 0: param = shader->paramTexture0; break; case 1: param = shader->paramTexture1; break; case 2: param = shader->paramTexture2; break; case 3: param = shader->paramTexture3; break; default: assertUnreachable(); } item.textureSlots[it->second] = &it->first->texture; item.textureValues[param] = it->second; ++it; } return { item }; } float_t UILabelNew::getWidth() { return 0; } float_t UILabelNew::getHeight() { return 0; } float_t UILabelNew::getContentWidth() { return 0; } float_t UILabelNew::getContentHeight() { return 0; } void UILabelNew::rebufferQuads(std::vector texts) { auto oldTexts = this->texts; textureMap.clear(); glm::vec2 position(0, 0); struct FontShaderBufferData fontData; int32_t quadIndex = 0; int32_t partIndex = 0; int32_t quadCount = 0; int32_t nextTexture = 0; // Determine how many quads there are, and the texture indexes. auto itText = texts.begin(); while(itText != texts.end()) { quadCount += 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++; } // Set initial line height position.y = mathMax(itText->style.size, position.y); ++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()) { assertTrue(itText->lockId != -1); assertNotNull(itText->style.font); itText->style.font->unlock(itText->lockId); ++itText; } // Update texts. this->texts = texts; // Create mesh this->mesh.createBuffers( QUAD_VERTICE_COUNT * quadCount, QUAD_INDICE_COUNT * quadCount ); // Buffer the text quads itText = texts.begin(); while(itText != texts.end()) { quadIndex += this->bufferQuads( *itText, fontData, textureMap, position, quadIndex, partIndex ); ++partIndex; ++itText; } shaderBuffer.buffer(&fontData); } int32_t UILabelNew::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 for(int32_t i = 0; i < len; i++) { char ch = text.text[i]; int32_t j = quadStart + i; FT_ULong c = ch; auto charInfo = text.texture->getCharacterData(c); // Determine texture coordinates. glm::vec2 uv0 = glm::vec2(0.0f, charInfo.textureY) / wh; glm::vec2 uv1 = uv0 + (charInfo.bitmapSize / wh); // Buffer the quad. 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; }