From 48b1493410f6393cf03ce08b1cfb04f1e1cf2849 Mon Sep 17 00:00:00 2001
From: HifiExperiments <thingsandstuffblog@gmail.com>
Date: Thu, 4 Apr 2024 19:01:41 -0700
Subject: [PATCH] text clips to edges instead of disappearing

---
 libraries/render-utils/src/sdf_text3D.slf |  6 +++
 libraries/render-utils/src/sdf_text3D.slv |  4 ++
 libraries/render-utils/src/text/Font.cpp  | 62 ++++++++++++++---------
 3 files changed, 49 insertions(+), 23 deletions(-)

diff --git a/libraries/render-utils/src/sdf_text3D.slf b/libraries/render-utils/src/sdf_text3D.slf
index c5bed1ecab..ccc9091100 100644
--- a/libraries/render-utils/src/sdf_text3D.slf
+++ b/libraries/render-utils/src/sdf_text3D.slf
@@ -32,6 +32,7 @@
 <@include sdf_text3D.slh@>
 <$declareEvalSDFSuperSampled()$>
 
+layout(location=RENDER_UTILS_ATTR_POSITION_MS) in vec2 _positionMS;
 <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
     layout(location=RENDER_UTILS_ATTR_POSITION_ES) in vec4 _positionES;
 <@endif@>
@@ -44,6 +45,11 @@ layout(location=RENDER_UTILS_ATTR_FADE1) flat in vec4 _glyphBounds; // we're reu
 void main() {
     vec4 color = evalSDFSuperSampled(_texCoord0, _glyphBounds);
 
+    vec2 absPos = abs(_positionMS);
+    if (max(absPos.x, absPos.y) >= 1.0 || _positionMS.x <= 0.0) {
+        color.a = 0.0;
+    }
+
 <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
     color.a *= params.color.a;
     if (color.a <= 0.0) {
diff --git a/libraries/render-utils/src/sdf_text3D.slv b/libraries/render-utils/src/sdf_text3D.slv
index 9ac3b871f9..d88ee78f89 100644
--- a/libraries/render-utils/src/sdf_text3D.slv
+++ b/libraries/render-utils/src/sdf_text3D.slv
@@ -19,6 +19,7 @@
 
 <@include sdf_text3D.slh@>
 
+layout(location=RENDER_UTILS_ATTR_POSITION_MS) out vec2 _positionMS;
 <@if HIFI_USE_TRANSLUCENT or HIFI_USE_FORWARD@>
     layout(location=RENDER_UTILS_ATTR_POSITION_ES) out vec4 _positionES;
 <@endif@>
@@ -47,6 +48,9 @@ void main() {
     <$transformModelToClipPos(cam, obj, position, gl_Position)$>
 <@endif@>
 
+    // Our position has scale baked in, but we need the normalized values in the fragment shader
+    _positionMS = inPosition.xy * vec2(length(obj._model[0]), length(obj._model[1]));
+
     const vec3 normal = vec3(0, 0, 1);
     <$transformModelToWorldDir(cam, obj, normal, _normalWS)$>
 }
\ No newline at end of file
diff --git a/libraries/render-utils/src/text/Font.cpp b/libraries/render-utils/src/text/Font.cpp
index 81cdaa51c9..2d24fa9a91 100644
--- a/libraries/render-utils/src/text/Font.cpp
+++ b/libraries/render-utils/src/text/Font.cpp
@@ -338,9 +338,18 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
     float rightEdge = origin.x + enlargedBoundsX;
 
     // Top left of text
+    bool firstTokenOfLine = true;
     glm::vec2 advance = origin;
     std::vector<std::pair<Glyph, vec2>> glyphsAndCorners;
-    foreach(const QString& token, tokenizeForWrapping(str)) {
+    const QStringList tokens = tokenizeForWrapping(str);
+    for (size_t i = 0; i < tokens.length(); i++) {
+        const QString& token = tokens[i];
+
+        if ((bounds.y != -1) && (advance.y < origin.y - bounds.y)) {
+            // We are out of the y bound, stop drawing
+            break;
+        }
+
         bool isNewLine = (token == QString('\n'));
         bool forceNewLine = false;
 
@@ -349,36 +358,43 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
             // We are out of the x bound, force new line
             forceNewLine = true;
         }
-        if (isNewLine || forceNewLine) {
+
+        if (isNewLine || (forceNewLine && !firstTokenOfLine)) {
+            if (forceNewLine && !firstTokenOfLine) {
+                // We want to try this token again on the new line
+                i--;
+            }
+
             // Character return, move the advance to a new line
             advance = glm::vec2(origin.x, advance.y - _leading);
-
-            if (isNewLine) {
-                // No need to draw anything, go directly to next token
-                continue;
-            } else if (computeExtent(token).x > enlargedBoundsX) {
-                // token will never fit, stop drawing
-                break;
-            }
-        }
-        if ((bounds.y != -1) && (advance.y - _fontSize < origin.y - bounds.y)) {
-            // We are out of the y bound, stop drawing
-            break;
+            firstTokenOfLine = true;
+            // No need to draw anything, go directly to next token
+            continue;
         }
 
         // Draw the token
-        if (!isNewLine) {
-            for (auto c : token) {
-                auto glyph = _glyphs[c];
-
-                glyphsAndCorners.emplace_back(glyph, advance - glm::vec2(0.0f, _ascent));
-
-                // Advance by glyph size
-                advance.x += glyph.d;
+        for (const QChar& c : token) {
+            if (advance.x > rightEdge) {
+                break;
             }
+            const Glyph& glyph = _glyphs[c];
 
+            glyphsAndCorners.emplace_back(glyph, advance - glm::vec2(0.0f, _ascent));
+
+            // Advance by glyph size
+            advance.x += glyph.d;
+        }
+
+        if (forceNewLine && firstTokenOfLine) {
+            // If the first word of a line didn't fit, we draw as many characters as we could, now go to the next line
+            // Character return, move the advance to a new line
+            advance = glm::vec2(origin.x, advance.y - _leading);
+            firstTokenOfLine = true;
+        } else {
             // Add space after all non return tokens
             advance.x += _spaceWidth;
+            // Our token fits in the x direction!  Any subsequent tokens won't be the first for this line.
+            firstTokenOfLine = false;
         }
     }
 
@@ -393,7 +409,7 @@ void Font::buildVertices(Font::DrawInfo& drawInfo, const QString& str, const glm
                                                                        alignment, rightSpacing));
             i--;
             while (i >= 0) {
-                auto prevGlyphAndCorner = glyphsAndCorners[i];
+                const auto& prevGlyphAndCorner = glyphsAndCorners[i];
                 // We're to the right of the last character we checked, which means we're on a previous line, so we need to
                 // recalculate the spacing, so we exit this loop
                 if (prevGlyphAndCorner.second.x >= nextGlyphAndCorner.second.x) {