1
\$\begingroup\$

I'm following this tutorial series on making a simple 2D clone of the classic Breakout game, but its author doesn't mention how to render a whole word or sentence in the middle of the screen. He simply uses hard-coded X and Y coordinates that obviously work for his screen, but not for mine (hence why the mere suggestion of using hard-coded values for something like this makes my skin crawl).

For more details on how he accomplishes the task, see this page of the aforementioned tutorial.

My version of his game has two major differences:

  1. I am rendering the GLFW window in fullscreen mode instead of a 800x600 window.
  2. I suspect that even if I were to use a 800x600 window, things still wouldn't look consistent across every device. As such I have made many changes to hard-coded values across the entire code base.

I'm a perfectionist so even though no-one else will probably ever run my game, I'm trying to eliminate the possibility of different results on different resolutions. But to do that has turned out to be a massive undertaking!

Here is the code that I'm having particular trouble with:

void TextRenderer::RenderText(string text, GLfloat x, GLfloat y, GLfloat scale, vec3 color) {
    // Activate corresponding render state  
    shader.Use();
    shader.SetVector3f("textColor", color);

    glActiveTexture(GL_TEXTURE0);
    glBindVertexArray(vao);

    // Iterate through all characters
    for (auto c = text.begin(); c != text.end(); c++) {
        auto ch = characters[*c];

        auto xPos = x + ch.bearing.x * scale;
        auto yPos = y + (characters['H'].bearing.
            y - ch.bearing.y) * scale;
        auto w = ch.size.x * scale;
        auto h = ch.size.y * scale;

        // Update VBO for each character
        GLfloat vertices[6][4] = {
            { xPos,     yPos + h, 0.0, 1.0 },
            { xPos + w, yPos,     1.0, 0.0 },
            { xPos,     yPos,     0.0, 0.0 },

            { xPos,     yPos + h, 0.0, 1.0 },
            { xPos + w, yPos + h, 1.0, 1.0 },
            { xPos + w, yPos,     1.0, 0.0 }
        };

        // Render glyph texture over quad
        glBindTexture(GL_TEXTURE_2D, ch.textureID);

        // Update content of VBO memory
        glBindBuffer(GL_ARRAY_BUFFER, vbo);
        glBufferSubData(GL_ARRAY_BUFFER, 0,
            sizeof(vertices), vertices);
        glBindBuffer(GL_ARRAY_BUFFER, 0);

        // Render quad and advance cursors for next glyph
        glDrawArrays(GL_TRIANGLES, 0, 6);
        // Bitshift by 6 to get value in pixels (1/64th times 2^6 = 64)
        x += (ch.advance >> 6) * scale;
    }

    glBindVertexArray(0);
    glBindTexture(GL_TEXTURE_2D, 0);
}

I have tried adding a bool centreX and bool centreY param to the function, as well as another for loop before the main one like this:

if (centreX || centreY) {
    auto textWidth = 0.0f;
    auto textHeight = 0.0f;

    for (auto c = text.begin(); c != text.end(); c++) {
        auto ch = characters[*c];

        if (centreX) {
            textWidth += (ch.bearing.x * scale) + (ch.size.
                x * scale) + ((ch.advance >> 6) * scale);
        }

        if (centreY) {
            textHeight += (y + (characters['H'].bearing.y -
                ch.bearing.y) * scale) + (ch.size.y * scale);
        }
    }

    if (centreX) {
        auto lastCh = text.end() - 1;
        textWidth -= (characters[*lastCh].advance >> 6) * scale;
    }
}

But for the sentence, font and font size used I'm getting a textWidth value of 1952 while a screenshot of the rendered text is 1143 px wide :S

Where has my math or understanding of the original code gone wrong? My display is a high DPI one and I have my Windows scaling settings at 200%; could either of those things be the culprit?

MTIA!

\$\endgroup\$
2
\$\begingroup\$

The picture below (from the website you linked) is quite good to understand what is going on. Advance is the distance from one character's origin to the next. To get the width of the whole text you should add the advances together. Right now you are adding the bearing, the width and the advance.

enter image description here

Once you have the size of your text and the size of your window you can add an offset to each character that is (window_width - text_width)/2.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ OMG I feel like such a fool! I knew the last part, and that all I needed was the width...but for some stupid reason I was thinking 'advance' is the distance from the right side (i.e. perceived advance = actual advance - width - bearing). Moral of the story: when it's 4 am and you're yawning your head off, GO TO BED lol. Anyway thank you for your help :-) \$\endgroup\$ – Kenny83 Dec 28 '19 at 23:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.