Search code examples
javaopengllwjglslick2d

LWJGL/Slick-Util -- Draw Line displays only in some cases


I am attempting to learn more about displaying and interacting with graphics and GUI elements. As part of this, I have been following along with a Tower Defense Tutorial that uses LWJGL and Slick-Util: https://www.youtube.com/watch?v=rfR09erJu7U&list=PLFUqwj4q1Zr8GHs6bO4d6gxMGUh_2pcNg

Extending some things out a bit on my own, I am trying to draw some basic untextured shapes with textured shapes. I can get a 2d line to draw, however, it only appears when drawn after certain textured shapes are drawn, but doesn't appear after other textured shapes are drawn. I'm wondering what I don't understand that is causing this divergent behavior.

Here is my main class, LineTest.java:

package main;

import static helpers.Artist.BeginSession;
import static helpers.Artist.DrawQuadTex;
import static helpers.Artist.QuickLoad;
import static helpers.Artist.drawLine;

import org.lwjgl.opengl.Display;
import org.newdawn.slick.opengl.Texture;

import helpers.Artist;

public class LineTest {

    public LineTest() {
        BeginSession();
        Texture bg = QuickLoad("bg");
        //Texture bg = QuickLoad("exitbutton");

        while(!Display.isCloseRequested()) {
            DrawQuadTex(bg,0,0,Artist.WIDTH,Artist.HEIGHT);
            drawLine(0,0,800,800);

            Display.update();
            Display.sync(60);
        }
        Display.destroy();
    }

    public static void main(String[] args) {
        new LineTest();
    }
}

And my helper class, Artist.java:

package helpers;

import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_LINES;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_QUADS;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBegin;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glClearDepth;
import static org.lwjgl.opengl.GL11.glColor4f;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnd;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glTexCoord2f;
import static org.lwjgl.opengl.GL11.glTranslatef;
import static org.lwjgl.opengl.GL11.glVertex2f;

import java.io.IOException;
import java.io.InputStream;

import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

public class Artist {

    public static final int WIDTH = 640, HEIGHT = 480;

    public static void BeginSession() {
        Display.setTitle("Line Test");
        try {
            Display.setDisplayMode(new DisplayMode(WIDTH, HEIGHT));
            Display.create();
        } catch (LWJGLException e) {
            e.printStackTrace();
        }

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, WIDTH, HEIGHT, 0, 1, -1);
        glMatrixMode(GL_MODELVIEW);
        glEnable(GL_TEXTURE_2D);
        glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
        glClearDepth(1);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    }

    public static void drawLine(int x1, int y1, int x2, int y2) {
        glColor4f(0.0f, 1.0f, 0.2f, 1.0f);
        glBegin(GL_LINES);
        glVertex2f((float) x1, (float) y1);
        glVertex2f((float) x2, (float) y2);
        glEnd();
        glColor4f(1f,1f,1f,1f);
    }   
    public static void DrawQuadTex(Texture tex, float x, float y, float width, float height) {
        tex.bind();
        glTranslatef(x, y, 0);
        glBegin(GL_QUADS);
        glTexCoord2f(0, 0);
        glVertex2f(0, 0);
        glTexCoord2f(1, 0);
        glVertex2f(width, 0);
        glTexCoord2f(1, 1);
        glVertex2f(width, height);
        glTexCoord2f(0,1);
        glVertex2f(0, height);
        glEnd();
        glLoadIdentity();
    }
    public static Texture LoadTexture(String path, String fileType) {
        Texture tex = null;
        InputStream in = ResourceLoader.getResourceAsStream(path);
        try {
            tex = TextureLoader.getTexture(fileType, in);
            in.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tex;
    }

    public static Texture QuickLoad(String name) {
        Texture tex = null;
        tex = LoadTexture(name + ".png", "PNG");
        return tex;
    }
}

The important part of my problem is within the main class, right here:

Texture bg = QuickLoad("bg");
//Texture bg = QuickLoad("exitbutton");

while(!Display.isCloseRequested()) {
    DrawQuadTex(bg,0,0,Artist.WIDTH,Artist.HEIGHT);
    drawLine(0,0,800,800);

When my Texture bg is getting my 'bg' graphic (a solid black PNG file), the drawLine function doesn't seem to actually draw a line. However, if I change my Texture bg to get my 'exitbutton' graphic (a blue square with "Exit" written in it, still a PNG file) the drawLine function does create a visible line.

This imgur album contains the output for both: Texture bg = QuickLoad("bg"); & Texture bg = QuickLoad("exitbutton");

https://i.sstatic.net/xxDV7.jpg

If necessary, I can also upload both PNG files that I using, but I currently cannot include more than 2 links in my question. bg.png is a mono-black 64x64 PNG. exitbutton.png is 512x512.

Just looking to understand what is causing this. Thank you!


Solution

  • The problem is most likely that you enable texturing in the BeginSession() method, and then keep it enabled for the whole rendering:

    glEnable(GL_TEXTURE_2D);
    

    This means that your lines will be textured. Now, you may say: "But... I'm not specifying texture coordinates for the lines!" That does not matter. The immediate mode rendering API in OpenGL is state based, so whichever texture coordinates were specified last are the ones used. Since you call DrawQuadTex() before drawLine(), the last texture coordinates specified in DrawQuadTex() will be your current texture coordinates when drawing the line:

    glTexCoord2f(0,1);
    

    So the color of the line will be the texel at position (0, 1) of the currently bound texture, modulated with the color specified for the line:

    glColor4f(0.0f, 1.0f, 0.2f, 1.0f);
    

    If the two colors multiplied together result in black, e.g. because the given texel is black, the result will be a black line on black background. Which looks a lot like not rendering anything at all.

    The cleanest solution is to enable texturing only while drawing primitives that you want to texture, and disable it afterwards. For example, in the DrawQuadTex() method:

    public static void DrawQuadTex(Texture tex, float x, float y, float width, float height) {
        tex.bind();
        glEnable(GL_TEXTURE_2D);
        ...
        glDisable(GL_TEXTURE_2D);
    }
    

    A few hints on how to track down these types of problems, or avoid them altogether:

    1. Set the clear color to something other than black during development. Then you can easily tell if you're rendering black geometry, or nothing at all.
    2. Use shader based rendering. The fixed function pipeline may look easier at first, but it's really not. With shaders, the resulting color is exactly what you implement, not some magic combination of values based on a bunch of fixed state.
    3. While you're at it, you may also want to avoid using immediate mode rendering. While not directly causing this problem, it's just as obsolete as using the fixed function pipeline.