UI Label Wrap
This commit is contained in:
		@@ -15,7 +15,13 @@ UILabel::UILabel(SceneItem *item) :
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UILabel::onStart() {
 | 
			
		||||
  UIComponentRenderable::onStart();
 | 
			
		||||
 | 
			
		||||
  this->shaderBuffer.init();
 | 
			
		||||
 | 
			
		||||
  useEvent([&]{
 | 
			
		||||
    this->rebufferQuads(this->texts);
 | 
			
		||||
  }, eventAlignmentUpdated);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
std::vector<struct ShaderPassItem> UILabel::getUIRenderPasses() {
 | 
			
		||||
@@ -68,163 +74,179 @@ std::vector<struct ShaderPassItem> UILabel::getUIRenderPasses() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float_t UILabel::getContentWidth() {
 | 
			
		||||
  return 0;
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
float_t UILabel::getContentHeight() {
 | 
			
		||||
  return 0;
 | 
			
		||||
  return 1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void UILabel::rebufferQuads(std::vector<struct UILabelText> 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<struct UILabelText> 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<std::pair<glm::vec4, glm::vec4>> vertices;
 | 
			
		||||
  struct FontShaderBufferData fontData;
 | 
			
		||||
  quadCountTotal = 0;
 | 
			
		||||
  std::vector<struct UILabelText> 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<float_t>(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<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
 | 
			
		||||
  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;
 | 
			
		||||
}
 | 
			
		||||
@@ -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<struct UILabelText> texts;
 | 
			
		||||
      std::vector<struct UILabelText> textsBuffered;
 | 
			
		||||
      std::map<NewTrueTypeFaceTexture*, int32_t> 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<NewTrueTypeFaceTexture*, int32_t> &textureMap,
 | 
			
		||||
        glm::vec2 &position,
 | 
			
		||||
        int32_t quadStart,
 | 
			
		||||
        int32_t partIndex
 | 
			
		||||
      );
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
      int32_t quadStart = 0;
 | 
			
		||||
      int32_t quadCount = -1;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user