Jump to content

[1.12.2] Drawing text with gradients


-TheLittleGuy

Recommended Posts

Is there a way to give text a gradient effect? Similar to the game's drawGradientRect() Method

Would I have to use openGL methods to achieve this and if so which ones?

 

Edit: Kinda related but is there a way to set the opacity of multiple objects being drawn on the screen? Example, I have 4 strings of text and I want to set all of their opacity to 50%

 

 

Edited by -TheLittleGuy
  • Like 1
Link to comment
Share on other sites

The drawGradientRect method uses the following trick to render a gradient: The top two vertices of the quad are in one color, and the bottom two vertices are in another. Then, the shadeModel GL11.GL_SMOOTH is used, so that these colors are interpolated and create a gradient effect. To render a String, the drawString method calls the renderString method, which calls the renderStringAtPos method which ultimately calls the renderGlyph method, which calls the render method of the TexturedGlyph class. In that last render method, the color is set via the function parameters. If you created a custom TexturedGlyph that hat a render method with two colors as input, you could enable the above mentioned shadeModel to render a text with a gradient. It's a bit of work but seems definitely possible.

Just have a look at

net.minecraft.client.gui.fonts.TexturedGlyph::render

and

net.minecraftforge.fml.client.config.GuiUtils::drawGradientRect

PS: Since I'm currently working with 1.14, the class names (any maybe the function names) differ, but apart from that, everything I said should be applicable.

  • Like 1
  • Thanks 1
Link to comment
Share on other sites

2 hours ago, deerangle said:

The drawGradientRect method uses the following trick to render a gradient: The top two vertices of the quad are in one color, and the bottom two vertices are in another. Then, the shadeModel GL11.GL_SMOOTH is used, so that these colors are interpolated and create a gradient effect. To render a String, the drawString method calls the renderString method, which calls the renderStringAtPos method which ultimately calls the renderGlyph method, which calls the render method of the TexturedGlyph class. In that last render method, the color is set via the function parameters. If you created a custom TexturedGlyph that hat a render method with two colors as input, you could enable the above mentioned shadeModel to render a text with a gradient. It's a bit of work but seems definitely possible.

Just have a look at


net.minecraft.client.gui.fonts.TexturedGlyph::render

and


net.minecraftforge.fml.client.config.GuiUtils::drawGradientRect

PS: Since I'm currently working with 1.14, the class names (any maybe the function names) differ, but apart from that, everything I said should be applicable.

It seems a bit different in 1.12.2. There doesnt seem to be a TexturedGlyph class.  After the renderStringAtPos method the method renderChar is called which then either calls renderDefaultChar or renderUnicodeChar

which is where the location of the actual glyphs are "cut out" from the font texture (default.png). The coloring of the text doesnt get set until the renderString method (§ codes excluded those seem to get set inside renderStringAtPos)

Link to comment
Share on other sites

So after a little testing I came up with this (for 1.12.2):

I created a Subclass of FontRenderer called GradientFontRenderer. In this class, I override the method renderDefaultChar (you should probably override the unicode method too.) so that It uses the above mentioned shadeModel and has the correct vertex order. The code for this is the following, and creates a gradient from red to blue:

@Override
protected float renderDefaultChar(int ch, boolean italic) {
    float charXPos = ch % 16 * 8f;
    float charYPos = (ch / 16) * 8f;
    float italicOffset = italic ? 1f : 0f;
    bindTexture(this.locationFontTexture);
    int charWidth = this.charWidth[ch];
    float width = (float)charWidth - 0.01F;

    GlStateManager.shadeModel(GL11.GL_SMOOTH);
    GlStateManager.glBegin(GL11.GL_QUADS);

    GlStateManager.color(1.0f, 0.0f, 0.0f);
    GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
    GlStateManager.glVertex3f(this.posX + italicOffset, this.posY, 0.0F);

    GlStateManager.color(0.0f, 0.0f, 1.0f);
    GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
    GlStateManager.glVertex3f(this.posX - italicOffset, this.posY + 7.99F, 0.0F);

    GlStateManager.color(0.0f, 0.0f, 1.0f);
    GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
    GlStateManager.glVertex3f(this.posX + width - 1.0F - italicOffset, this.posY + 7.99F, 0.0F);

    GlStateManager.color(1.0f, 0.0f, 0.0f);
    GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
    GlStateManager.glVertex3f(this.posX + width - 1.0F + italicOffset, this.posY, 0.0F);

    GlStateManager.glEnd();
    GlStateManager.shadeModel(GL11.GL_FLAT);
    return (float)charWidth;
}

I'm sure you can expand on this code by adding parameters and auxiliary functions to control the gradient colors to your desire.

For instantiating the GradientFontRenderer, I just created an Instance in the constructor of my Gui class. You also need to call onResourceManagerReload to load the texture for the fontrenderer and all. This is the GuiScreen code:

public class TestGuiScreen extends GuiScreen {

    private GradientFontRenderer gradientFontRenderer;

    public TestGuiScreen() {
        Minecraft mc = Minecraft.getMinecraft();
        gradientFontRenderer = new GradientFontRenderer(mc.gameSettings, new ResourceLocation("textures/font/ascii.png"), mc.renderEngine, false);
        gradientFontRenderer.onResourceManagerReload(null);
    }

    public void render() {
        ScaledResolution res = new ScaledResolution(Minecraft.getMinecraft());
        this.width = res.getScaledWidth();
        this.height = res.getScaledHeight();
        int x = (this.width) / 2;
        int y = (this.height) / 2;
        drawHelloTextWithGradient(x, y-16, 0xFFFFFFFF, 0xFFFF0000);
    }

    private void drawHelloTextWithGradient(int x, int y, int topColor, int bottomColor) {
        drawRect(x-20, y-8, x+20, y+8, 0x7F000000);
        drawCenteredGradientString(gradientFontRenderer, "hello", x, y-4, topColor, bottomColor);
    }

    public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
    {
        fontRendererIn.drawString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color);
    }

}

Here is a screenshot of how it looks.

2019-11-23_23_32_17.png.48402de27839505a1f207babf16feb79.png

I hope this was helpful. If you have any further questions, just ask :D

  • Thanks 1
Link to comment
Share on other sites

So I decided to make all the functions to render with any gradient, and here's the code (it only supports normal text, no italics, no bold, no formatting or coloring, no unicode, but you can add that with some extra work if you need it):

public class GradientFontRenderer extends FontRenderer {

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        enableAlpha();
        int i;

        if (dropShadow)
        {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false));
        }
        else
        {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        if (text == null)
        {
            return 0;
        }
        else
        {

            if ((topColor & -67108864) == 0)
            {
                topColor |= -16777216;
            }

            if ((bottomColor & -67108864) == 0)
            {
                bottomColor |= -16777216;
            }

            if (dropShadow)
            {
                topColor = (topColor & 16579836) >> 2 | topColor & -16777216;
                bottomColor = (bottomColor & 16579836) >> 2 | bottomColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, topColor, bottomColor);
            return (int)this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int topColor, int bottomColor) {
        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f = this.renderGradientChar(c0, topColor, bottomColor);

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private float renderGradientChar(char ch, int topColor, int bottomColor) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, topColor, bottomColor);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int topColor, int bottomColor) {
        float topAlpha = ((topColor >> 24) & 0xFF) / 255f;
        float topRed = ((topColor >> 16) & 0xFF) / 255f;
        float topGreen = ((topColor >> 8) & 0xFF) / 255f;
        float topBlue = (topColor & 0xFF) / 255f;

        float bottomAlpha = ((bottomColor >> 24) & 0xFF) / 255f;
        float bottomRed = ((bottomColor >> 16) & 0xFF) / 255f;
        float bottomGreen = ((bottomColor >> 8) & 0xFF) / 255f;
        float bottomBlue = (bottomColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

Then change this code from above to the following:

public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
{
    fontRendererIn.drawGradientString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color, colorBottom, true);
}

And it should work.

Link to comment
Share on other sites

2 hours ago, deerangle said:

So I decided to make all the functions to render with any gradient, and here's the code (it only supports normal text, no italics, no bold, no formatting or coloring, no unicode, but you can add that with some extra work if you need it):


public class GradientFontRenderer extends FontRenderer {

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        enableAlpha();
        int i;

        if (dropShadow)
        {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false));
        }
        else
        {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow)
    {
        if (text == null)
        {
            return 0;
        }
        else
        {

            if ((topColor & -67108864) == 0)
            {
                topColor |= -16777216;
            }

            if ((bottomColor & -67108864) == 0)
            {
                bottomColor |= -16777216;
            }

            if (dropShadow)
            {
                topColor = (topColor & 16579836) >> 2 | topColor & -16777216;
                bottomColor = (bottomColor & 16579836) >> 2 | bottomColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, topColor, bottomColor);
            return (int)this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int topColor, int bottomColor) {
        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f = this.renderGradientChar(c0, topColor, bottomColor);

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private float renderGradientChar(char ch, int topColor, int bottomColor) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000".indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, topColor, bottomColor);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int topColor, int bottomColor) {
        float topAlpha = ((topColor >> 24) & 0xFF) / 255f;
        float topRed = ((topColor >> 16) & 0xFF) / 255f;
        float topGreen = ((topColor >> 8) & 0xFF) / 255f;
        float topBlue = (topColor & 0xFF) / 255f;

        float bottomAlpha = ((bottomColor >> 24) & 0xFF) / 255f;
        float bottomRed = ((bottomColor >> 16) & 0xFF) / 255f;
        float bottomGreen = ((bottomColor >> 8) & 0xFF) / 255f;
        float bottomBlue = (bottomColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(bottomRed, bottomGreen, bottomBlue, bottomAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        GlStateManager.color(topRed, topGreen, topBlue, topAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

Then change this code from above to the following:


public void drawCenteredGradientString(GradientFontRenderer fontRendererIn, String text, int x, int y, int color, int colorBottom)
{
    fontRendererIn.drawGradientString(text, x - fontRendererIn.getStringWidth(text) / 2, y, color, colorBottom, true);
}

And it should work.

I actually do have a question: I changed the gradient to start from the left instead of the top however, the current method applies the gradient effect to the individual character. What would have to be changed in order to have the gradient be applied to the entire string? Where would the GL Functions go in that case? Where they are currently or would it have to go after the chars are "assembled" into the string? Ex: i have the string "Test String"  the start color is Green and the end color is Red, the first letter will start green and will fade to red by the time it gets to the last letter.

Link to comment
Share on other sites

You would have to color different vertices differently based on the length they are through the string to have that type of gradient. You would need extra code to store the length of the string that you’re drawing and to use that data in the render char method

  • Like 1

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

I've added a horizontal parameter to every gradient text function so that you can make horizontal and vertical gradients. This is the updated code:

public class GradientFontRenderer extends FontRenderer {

    private static final String charmap = "\u00c0\u00c1\u00c2\u00c8\u00ca\u00cb\u00cd\u00d3\u00d4\u00d5\u00da\u00df\u00e3\u00f5\u011f\u0130\u0131\u0152\u0153\u015e\u015f\u0174\u0175\u017e\u0207\u0000\u0000\u0000\u0000\u0000\u0000\u0000 !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\u0000\u00c7\u00fc\u00e9\u00e2\u00e4\u00e0\u00e5\u00e7\u00ea\u00eb\u00e8\u00ef\u00ee\u00ec\u00c4\u00c5\u00c9\u00e6\u00c6\u00f4\u00f6\u00f2\u00fb\u00f9\u00ff\u00d6\u00dc\u00f8\u00a3\u00d8\u00d7\u0192\u00e1\u00ed\u00f3\u00fa\u00f1\u00d1\u00aa\u00ba\u00bf\u00ae\u00ac\u00bd\u00bc\u00a1\u00ab\u00bb\u2591\u2592\u2593\u2502\u2524\u2561\u2562\u2556\u2555\u2563\u2551\u2557\u255d\u255c\u255b\u2510\u2514\u2534\u252c\u251c\u2500\u253c\u255e\u255f\u255a\u2554\u2569\u2566\u2560\u2550\u256c\u2567\u2568\u2564\u2565\u2559\u2558\u2552\u2553\u256b\u256a\u2518\u250c\u2588\u2584\u258c\u2590\u2580\u03b1\u03b2\u0393\u03c0\u03a3\u03c3\u03bc\u03c4\u03a6\u0398\u03a9\u03b4\u221e\u2205\u2208\u2229\u2261\u00b1\u2265\u2264\u2320\u2321\u00f7\u2248\u00b0\u2219\u00b7\u221a\u207f\u00b2\u25a0\u0000";

    public GradientFontRenderer(GameSettings gameSettingsIn, ResourceLocation location, TextureManager textureManagerIn, boolean unicode) {
        super(gameSettingsIn, location, textureManagerIn, unicode);
    }

    public int drawGradientString(String text, float x, float y, int topColor, int bottomColor, boolean dropShadow, boolean horizontal) {
        enableAlpha();
        int i;

        if (dropShadow) {
            i = this.renderGradientString(text, x + 1.0F, y + 1.0F, topColor, bottomColor, true, horizontal);
            i = Math.max(i, this.renderGradientString(text, x, y, topColor, bottomColor, false, horizontal));
        } else {
            i = this.renderGradientString(text, x, y, topColor, bottomColor, false, horizontal);
        }

        return i;
    }

    private int renderGradientString(String text, float x, float y, int startColor, int endColor, boolean dropShadow, boolean horizontal) {
        if (text == null) {
            return 0;
        } else {

            if ((startColor & -67108864) == 0) {
                startColor |= -16777216;
            }

            if ((endColor & -67108864) == 0) {
                endColor |= -16777216;
            }

            if (dropShadow) {
                startColor = (startColor & 16579836) >> 2 | startColor & -16777216;
                endColor = (endColor & 16579836) >> 2 | endColor & -16777216;
            }

            this.posX = x;
            this.posY = y;
            this.renderGradientStringAtPos(text, dropShadow, startColor, endColor, horizontal);
            return (int) this.posX;
        }
    }

    private void renderGradientStringAtPos(String text, boolean shadow, int startColor, int endColor, boolean horizontal) {
        float totalWidth = this.getStringWidth(text);
        float currentCountWidth = 0;

        for (int i = 0; i < text.length(); ++i) {
            char c0 = text.charAt(i);
            int j = charmap.indexOf(c0);

            float f1 = j == -1 ? 0.5f : 1f;
            boolean flag = (c0 == 0 || j == -1) && shadow;

            if (flag) {
                this.posX -= f1;
                this.posY -= f1;
            }

            float f;
            if (horizontal) {
                float nextCharWidth = this.getCharWidth(c0);
                float firstMix = currentCountWidth / totalWidth;
                float lastMix = (currentCountWidth + nextCharWidth) / totalWidth;
                int firstColor = colorMix(startColor, endColor, firstMix);
                int lastColor = colorMix(startColor, endColor, lastMix);
                f = this.renderGradientChar(c0, firstColor, lastColor, true);
                currentCountWidth += f;
            } else {
                f = this.renderGradientChar(c0, startColor, endColor, false);
            }

            if (flag) {
                this.posX += f1;
                this.posY += f1;
            }

            doDraw(f);
        }
    }

    private int colorMix(int startColor, int endColor, float mix) {
        float startAlpha = ((startColor >> 24) & 0xFF) / 255f;
        float startRed = ((startColor >> 16) & 0xFF) / 255f;
        float startGreen = ((startColor >> 8) & 0xFF) / 255f;
        float startBlue = (startColor & 0xFF) / 255f;

        float endAlpha = ((endColor >> 24) & 0xFF) / 255f;
        float endRed = ((endColor >> 16) & 0xFF) / 255f;
        float endGreen = ((endColor >> 8) & 0xFF) / 255f;
        float endBlue = (endColor & 0xFF) / 255f;

        int mixAlpha = (int) (((1 - mix) * startAlpha + mix * endAlpha) * 255);
        int mixRed = (int) (((1 - mix) * startRed + mix * endRed) * 255);
        int mixGreen = (int) (((1 - mix) * startGreen + mix * endGreen) * 255);
        int mixBlue = (int) (((1 - mix) * startBlue + mix * endBlue) * 255);

        return (mixAlpha << 24) | (mixRed << 16) | (mixGreen << 8) | mixBlue;
    }

    private float renderGradientChar(char ch, int startColor, int endColor, boolean horizontal) {
        if (ch == 160) return 4.0F; // forge: display nbsp as space. MC-2595
        if (ch == ' ') {
            return 4.0F;
        } else {
            int i = charmap.indexOf(ch);
            if (i != -1) {
                return this.renderGradientDefaultChar(i, startColor, endColor, horizontal);
            } else {
                throw new RuntimeException("Unrecognized char: " + ch);
            }
        }
    }

    protected float renderGradientDefaultChar(int ch, int startColor, int endColor, boolean horizontal) {
        float startAlpha = ((startColor >> 24) & 0xFF) / 255f;
        float startRed = ((startColor >> 16) & 0xFF) / 255f;
        float startGreen = ((startColor >> 8) & 0xFF) / 255f;
        float startBlue = (startColor & 0xFF) / 255f;

        float endAlpha = ((endColor >> 24) & 0xFF) / 255f;
        float endRed = ((endColor >> 16) & 0xFF) / 255f;
        float endGreen = ((endColor >> 8) & 0xFF) / 255f;
        float endBlue = (endColor & 0xFF) / 255f;

        float charXPos = ch % 16 * 8f;
        float charYPos = (ch / 16) * 8f;
        bindTexture(this.locationFontTexture);
        int charWidth = this.charWidth[ch];
        float width = (float) charWidth - 0.01F;

        GlStateManager.shadeModel(GL11.GL_SMOOTH);
        GlStateManager.glBegin(GL11.GL_QUADS);

        GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        GlStateManager.glTexCoord2f(charXPos / 128.0F, charYPos / 128.0F); // 0 0
        GlStateManager.glVertex3f(this.posX, this.posY, 0.0F);

        if (horizontal) {
            GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        } else {
            GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        }
        GlStateManager.glTexCoord2f(charXPos / 128.0F, (charYPos + 7.99F) / 128.0F); // 0 1
        GlStateManager.glVertex3f(this.posX, this.posY + 7.99F, 0.0F);

        GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, (charYPos + 7.99F) / 128.0F); // 1 1
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY + 7.99F, 0.0F);

        if (horizontal) {
            GlStateManager.color(endRed, endGreen, endBlue, endAlpha);
        } else {
            GlStateManager.color(startRed, startGreen, startBlue, startAlpha);
        }
        GlStateManager.glTexCoord2f((charXPos + width - 1.0F) / 128.0F, charYPos / 128.0F); // 1 0
        GlStateManager.glVertex3f(this.posX + width - 1.0F, this.posY, 0.0F);

        GlStateManager.glEnd();
        GlStateManager.shadeModel(GL11.GL_FLAT);
        return (float) charWidth;
    }
}

2019-11-24_10_23_33.png.431785ef4179a820f30edd88bbea722c.png

Pretty basic. It just linearly interpolates the colors for each char so that the gradient colors of each char match up.

Link to comment
Share on other sites

You probably want to use hex instead of decimal for your color codes and masks.

-67108864 -> 0xFC000000

-16777216 -> 0xFF000000

16579836 -> 0xFCFCFC

It makes it a lot easier to understand as colours are packed 0x(AA)RRGGBBx

About Me

Spoiler

My Discord - Cadiboo#8887

My WebsiteCadiboo.github.io

My ModsCadiboo.github.io/projects

My TutorialsCadiboo.github.io/tutorials

Versions below 1.14.4 are no longer supported on this forum. Use the latest version to receive support.

When asking support remember to include all relevant log files (logs are found in .minecraft/logs/), code if applicable and screenshots if possible.

Only download mods from trusted sites like CurseForge (minecraft.curseforge.com). A list of bad sites can be found here, with more information available at stopmodreposts.org

Edit your own signature at www.minecraftforge.net/forum/settings/signature/ (Make sure to check its compatibility with the Dark Theme)

Link to comment
Share on other sites

1 minute ago, Cadiboo said:

You probably want to use hex instead of decimal for your color codes and masks.

-67108864 -> 0xFC000000

-16777216 -> 0xFF000000

16579836 -> 0xFCFCFC

It makes it a lot easier to understand as colours are packed 0x(AA)RRGGBBx

Since I copied most of the code for the GradientFontRenderer from the original FontRenderer, which is decompiled and therefore doesn't use hex literals, the color values are in decimal.

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.



×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.