269 lines
6.8 KiB
C++
269 lines
6.8 KiB
C++
// 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<struct UILabelStyle> styleStack;
|
|
struct UILabelStyle current;
|
|
styleStack.push_back(current);
|
|
std::vector<struct UILabelText> texts;
|
|
|
|
std::function<void(Xml*)> 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<NewTrueTypeAsset>(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("<root>" + this->test + "</root>");
|
|
parseChildren(&root);
|
|
this->rebufferQuads(texts);
|
|
}
|
|
|
|
std::vector<struct ShaderPassItem> 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<struct UILabelText> 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<float_t>(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<NewTrueTypeFaceTexture*, int32_t> &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;
|
|
} |