2

I am currently using Slick to render a custom chat in a game.

What I want to do, is I want to be able to create a custom "fading" effect for the chat, which smoothly fades from the right to the left. I'm not too great on explaining things in text, so instead I will show an example of what I want.

Example:

I want to render the text bernhardkiv: hello world!, running my regular font renderer, results in this:

enter image description here

And what I want to be able to do, is to cut off the text smoothly, as follows;

enter image description here

And I have no idea how to do that! I believe that this is possible using glScissoring, but I have been trying to use it and nothing seems to be working (nothing happens at all, everything stays the same way).

I would appreciate anyone helping, I essentially want to be able to, instead of rendering at x, y, I want to be able to render at x,y with width string_width - smooth_cut, to be able to make a little fading effect.

dbank
  • 1,173
  • 1
  • 17
  • 29
  • Can you show some code samples of what you have tried so far? – dbank Mar 26 '15 at 16:17
  • @dbank I have searched for hours upon hours upon hours, and I have not found any information *AT ALL* as to what I could even try. –  Mar 26 '15 at 18:00
  • When you say "my regular font renderer"... Are you using some implementation of `org.newdawn.slick.Font` such as `org.newdawn.slick.TrueTypeFont`? Or are you using something custom that you made? – dbank Mar 27 '15 at 04:40
  • Can you please elaborate by what you intend when you say "string_width - smooth_cut"? And what "portion of the last character glyph" do you want to render? In this case, it looks you're showing about half of the "r". – dbank Mar 27 '15 at 05:01
  • I am using UnicodeFont, and yes I mean I want to render 1/2 then 1/4 then 1/5 of it, but of course in pixels. –  Mar 27 '15 at 06:20
  • So you want something like [this](http://i.imgur.com/kSBhi2J.png)? – dbank Mar 27 '15 at 06:34
  • @dbank YES! EXACTLY THAT! How did you do that? ;o –  Mar 27 '15 at 07:13
  • I did something rather hack-ish, I think. I'll try to find a better approach and post it. – dbank Mar 27 '15 at 16:34
  • Out of curiosity, what `Effect`'s (and settings) are you applying to your `UnicodeFont`? – dbank Mar 27 '15 at 16:36
  • @dbank I don't care if it is hack-ish, I still want it! The only effect that I am adding is this: `ubuntuL28.getEffects().add(new ColorEffect(java.awt.Color.white));` –  Mar 27 '15 at 17:43
  • @dbank What I am guessing you did, is render the last character in a different font which has an effect of fading into a black color? –  Mar 27 '15 at 17:44
  • No, actually I extended `TrueTypeFont` and used OpenGL calls. I'm try to find a clean way to do it with `UnicodeFont` and/or an `Effect` , but I'm having trouble finding ways to extend these classes without copying large chunks of code as they don't seemed to be written with the intention of being extended (private variables and methods; no hooks). – dbank Mar 28 '15 at 22:28
  • Do you _need_ `UnicodeFont` or would any of the other `Font` implementations be an option (`AngelCodeFont`, `SpriteSheetFont`, `TrueTypeFont`)? I think it is possible to get the fade effect you want with any of the `Font` implementations, but in order to get the fade with `UnicodeFont`, I think it will require copying the whole `UnicodeFont` class along with several of its supporting classes just to make a few customizations (because the code doesn't appear to be extension friendly). I think the other `Font` implementations can simply be extended with minimal code copying. – dbank Mar 29 '15 at 06:20
  • @dbank I would like to continue using `UnicodeFont`, because all of my code expects it, and it would be too much to replace it all. An upside, is that I contain the source code of slick in my project, meaning that I can easily just exchange the `UnicodeFont` class for a modified one. –  Mar 29 '15 at 06:29
  • Ah, so basically you can use or already are using a custom slick build? I that case we could alter the Unicode class to make it's private members and methods protected or maybe add a hook. Then we could easily extend it. – dbank Mar 29 '15 at 06:32
  • @dbank it's not custom, I just have the source code added to my project's source code to avoid adding it to the build path (easier to compile later on etc. etc.) –  Mar 29 '15 at 07:00

1 Answers1

3

Disclaimer:

  1. This is new territory for me, but since no one else seems to be answering, I will make an attempt since this question seems kind of interesting to me. I haven't thoroughly tested these code modifications, so there may be bugs/side-effects I am unaware of. Use at your own risk. :-)

  2. The org.newdawn.slick.Font implementation classes (and supporting classes) in the Slick2D library don't appear to be extension-friendly (private members and methods). As far as I can tell, in order to achieve the desired effect, changes are required to be made to some of the these internal Slick2D classes. (The alternative being to copy entire classes and packages.) To my understanding from the question asker's comments, the Slick2D source code is contained within the question asker's project, so making changes to internal Slick2D classes should be workable in this case.

  3. It can be argued that the said classes were not made extension-friendly for a reason, and there may better ways to go about making the changes necessary to make them extensible. I might even go about it differently myself given an actual project and more time. However, the goal of this answer is to show the question asker the essence of how I achieved the desired effect with minimal code changes to the internal Slick2D classes. The question asker can make his/her own decisions how to go about the details of applying these changes to his/her project.

This code uses Slick build 237.


You can see the complete code changes at this github repo. Below I show only the code changes most relevant to achieve the desired fade effect.

Think of each character as being drawn in an GL_QUAD with the four corners each having an RGBA value (as described here where this picture is taken from):

 (x,x,x,1)       (x,x,x,0)
     -----------------
     |               |
     |               |
     |               |
     |               |
     |               |
     |               |
     -----------------
  (x,x,x,1)      (x,x,x,0)

What we are doing is leaving the top left and bottom left corner colors unchanged and changing the top right and bottom right corner colors to have an alpha of 0 (transparent). OpenGL will gradually fade the transparency for us. To do this we extend org.newdawn.slick.Image to create a FadableImage with the following method (which is very similar to Image's drawEmbedded method but does not override it):

public class FadableImage extends Image {

   //.....

    public void drawEmbeddedFaded(float x,float y,float width,float height, Color color, float fadePercentWidth) {
        init();

        //if percent width is set to an invalid amount, default it to 1.
        if(fadePercentWidth < 0.0f || fadePercentWidth > 1.0f)
            fadePercentWidth = 1.0f;

        if (corners == null) {
            GL.glTexCoord2f(textureOffsetX, textureOffsetY);
            GL.glVertex3f(x, y, 0);
            GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight);
            GL.glVertex3f(x, y + height, 0);

            //set the color of x2 and y2 of the quad to 0 alpha
            GL.glColor4f(color.r, color.g, color.b, 0.0f);
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY
                    + textureHeight);
            GL.glVertex3f(x + width*fadePercentWidth, y + height, 0);
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY);
            GL.glVertex3f(x + width*fadePercentWidth, y, 0);
            //reset the color
            GL.glColor4f(color.r, color.g, color.b, color.a);
        } else {
            corners[TOP_LEFT].bind();
            GL.glTexCoord2f(textureOffsetX, textureOffsetY);
            GL.glVertex3f(x, y, 0);
            corners[BOTTOM_LEFT].bind();
            GL.glTexCoord2f(textureOffsetX, textureOffsetY + textureHeight);
            GL.glVertex3f(x, y + height, 0);
            //set the color of x2 and y2 of the quad to 0 alpha
            GL.glColor4f(color.r, color.g, color.b, 0.0f);
            //corners[BOTTOM_RIGHT].bind();
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY
                    + textureHeight);
            GL.glVertex3f(x + width*fadePercentWidth, y + height, 0);
            //corners[TOP_RIGHT].bind();
            GL.glTexCoord2f(textureOffsetX + textureWidth*fadePercentWidth, textureOffsetY);
            GL.glVertex3f(x + width*fadePercentWidth, y, 0);
            //reset the color
            GL.glColor4f(color.r, color.g, color.b, color.a);
        }
    }

}

You can see the diff of what I am about to describe.

In order to use FadableImage, we also need to extend org.newdawn.slick.font.GlyphPage to create FadableGlyphPage. To support FadableGlyphPage we need to modify org.newdawn.slick.font.GlyphPage's constructor and add a new one.

Then we need to modify org.newdawn.slick.UnicodeFont to use our FadableGlyphPage when UnicodeFont loads the glyphs in loadGlyphs.

We change UnicodeFont's drawDisplayList(...) method to call our FadableImage's special drawEmbeddedFaded(...) method for the character at the specified end index of the given string. We also have to make modifications so that the DisplayList caching will be handled correctly.

We add a convenience method to UnicodeFont to draw a string with the character at endIndex-1 faded and drawn to the width percentage specified by fadePercentWidth:

public void drawString(float x, float y, String text, int endIndex, float fadePercentWidth) {
    drawDisplayList(x, y, text, Color.white, 0, endIndex, fadePercentWidth);
}

So the idea is that the last character rendered will be the character at endIndex-1. The fade width percentage is the percentage of the width that will be drawn of the quad containing the character at endIndex-1. So you can play with that parameter to fine tune the effect you want (0.0f to 1.0f). With the fade width percentage set to 0.5f, 50% of the total width of the quad will be drawn. And the fade will be to that 50% width:

 (x,x,x,1)       (x,x,x,0)

     ------------|------------
     |           |            |
     | drawn     | not        |
     |           |  drawn     |
     |           |            |
     |           |            |
     |           |            |
     ------------|------------

  (x,x,x,1)      (x,x,x,0)

Now let's test it with this code:

import java.awt.Font;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.newdawn.slick.AppGameContainer;
import org.newdawn.slick.BasicGame;
import org.newdawn.slick.Color;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;

public class TextFadeTest extends BasicGame {

    private final int Y_OFFSET = 50;
    private final org.newdawn.slick.Color bgColor =  new org.newdawn.slick.Color(126, 166, 240);

    private UnicodeFont unicodeFont;

    public TextFadeTest(String gamename) {
        super(gamename);
    }

    @Override
    public void init(GameContainer gc) throws SlickException {
        gc.setShowFPS(false);
        gc.getGraphics().setBackground(bgColor);

        Font awtFont = new Font(Font.SERIF, Font.PLAIN, 36);
        unicodeFont = new UnicodeFont(awtFont);
        unicodeFont.getEffects().add(new ColorEffect(java.awt.Color.WHITE));
        unicodeFont.addAsciiGlyphs();
        unicodeFont.loadGlyphs();
    }

    @Override
    public void update(GameContainer gc, int i) throws SlickException {}

    @Override
    public void render(GameContainer gc, Graphics g) throws SlickException {

        String str = "bernhardkiv: hello world!";
        int y = 0;
        unicodeFont.drawString(10, y, str);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, Color.white, 0, 22);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.10f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.25f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.50f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.75f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 0.90f);
        unicodeFont.drawString(10, y+=Y_OFFSET, str, 22, 1.0f);

    }

    public static void main(String[] args) {
        try {
            AppGameContainer appgc;
            appgc = new AppGameContainer(new TextFadeTest("TextFadeTest"));
            appgc.setDisplayMode(640, 480, false);
            appgc.start();
        }
        catch(SlickException ex) {
            Logger.getLogger(TextFadeTest.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

}

Which gives this result:

result

Community
  • 1
  • 1
dbank
  • 1,173
  • 1
  • 17
  • 29
  • Hey! I am currently out of town, so I do not have access to test this, but judging by the result, this is exactly what I need! Thank you very much, sir dbank! –  Mar 30 '15 at 12:19
  • 1
    No, problem! I hope it helps! – dbank Mar 30 '15 at 12:32