1
\$\begingroup\$

I'm going to be as specific about this question as I can. Ultimately, how is it that I apply a class that uses non-static fields in LWJGL?

More specifically, I know that OpenGL is quite nearly more a direct interface with the hardware than a traditional API. Multithreading it doesn't make any sense. I understand that it's in my best interest (perhaps as my only option) to keep all rendering commands in the same thread.

I also understand that given LWJGL's nature as a nearly one-on-one proxy to OpenGL, it cannot follow non-static class fields. Thus, every global variable in the main thread needs to be static, or it consistently just shows up as null.

I can deal with this, but I'm now creating a class that allows for generation of a texture around traditional BufferedImage graphics operations. It contains a BufferedImage in a field (image), provides access to that image, and produces a texture from image every time it changes. I thought this might be handy if I want to do anything flashy with text rendering through OpenGL (in spite of the buffering issues). The problem is, when called from the thread that renders OpenGL, the image field is consistently null.

I'm feeling a little boxed in by that, particularly as an old-school multithreading and OO programmer. LWJGL seems to be woefully short on documentation and even example code; so without digging through its source, could someone explain to me how it is that I maintain and access image in this class?

Here is my code. Hopefully it's something stupid, I've been cramming LWJGL for over a week. It's a three-layer code sample, including none of the imports, the interface, the abstract, and the (more-or-less) concrete. Pardon the lack of clean-up, I'm sort of in the middle of that with this.

package framework.texture;

import java.awt.Image;
import java.awt.image.BufferedImage;

public interface ImageTexture {
public void setImage(Image image);
public Image getImage();

/**
 * Activates the image as a texture.
 * 
 * @return the integer by which OpenGL identifies the data.
 */
public int activate();

}


public abstract class AbstractImageTexture implements ImageTexture {

protected BufferedImage image;

@Override
public void setImage(Image image) {
    System.out.println(image);
    this.image = (BufferedImage)image;
    deriveTexture();
}

@Override
public Image getImage() {
    return this.image;
}

protected abstract void deriveTexture(); 

public AbstractImageTexture(BufferedImage image) {
    this.image = image;
}

}


public class BufferedImageTexture extends AbstractImageTexture {

public BufferedImageTexture(BufferedImage image) {
    super(image);
}

private enum ImageType {
    RGB(3),
    RGBA(4);

    int bpp;

    private ImageType(int bpp) {
        this.bpp = bpp;
    }

    public int getBytesPerPixel() {
        return this.bpp;
    }
}


private ByteBuffer buffer = null;

@Override
protected void deriveTexture() {
    BufferedImage image = (BufferedImage)this.image;

    int[] pixels;
    try {
        pixels = new int[image.getWidth() * image.getHeight()];
    } catch(NegativeArraySizeException e) {
        throw new RuntimeException("Image cannot be converted into a texture due to unknown sizing.", e);
    }

    this.image.getRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());

    this.buffer = BufferUtils.createByteBuffer(image.getWidth() * image.getHeight() * getImageType().getBytesPerPixel());

    for(int y = 0; y < image.getHeight(); y++)
        for(int x = 0; x < image.getWidth(); x++) {
            int pixel = pixels[y * image.getWidth() + x];
            this.buffer.put((byte) ((pixel >> 16) & 0xFF));     //R
            this.buffer.put((byte) ((pixel >> 8) & 0xFF));      //G
            this.buffer.put((byte) (pixel  & 0xFF));                //B
            if(getImageType() == ImageType.RGBA)
                this.buffer.put((byte) ((pixel >> 24) & 0xFF)); //A
        }

    this.buffer.flip(); //Yes, you did it. Chill.
}

private ImageType getImageType() {
    if(this.image.getColorModel().hasAlpha())
        return ImageType.RGBA;
    else
        return ImageType.RGB;
}

@Override
public int activate() {
    glEnable(GL_TEXTURE_2D);

    int textureID = glGenTextures();

    {
        glBindTexture(GL_TEXTURE_2D, textureID);    //Bind texture ID

        //Setup wrap mode
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);

        //Setup texture scaling filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        //send texel data to OpenGL
        //TODO: Check these parameters. Seems like there are a few unhealthy and undefensive assumptions in them.
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, super.image.getWidth(), super.image.getHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
    }

    return textureID;
}


}

image, whether its set or not, consistently shows up to the main thread as null. If I were to make it static, that might change, but I want to have quite a few of these objects floating around so they can't share a field. Do I need to call it from a separate thread or something?

\$\endgroup\$
1
  • \$\begingroup\$ OpenGL is far from a direct interface to modern graphics hardware; in fact it is a rather high-level API compared to what the actual hardware does, typically involving 3 asynchronous systems (client/driver front-end, driver back-end, and GPU). The reason OpenGL commands are typically issued from a single thread is because OpenGL is a huge state machine. \$\endgroup\$
    – bcrist
    Sep 25, 2014 at 9:31

2 Answers 2

0
\$\begingroup\$

It is completely possible to use all OO paradigms when using LWJGL.

All that matters is that the data that you're sending to OpenGL with the gl... methods is correct. How you structure your program or code is not important and an Object-oriented approach is probably the best way to create a complex LWJGL game.

I'm developing my small LWJGL game using Spring and plenty of @Autowired annotations so practically all my classes (camera, engine, renderers) are being instantiated by Spring, so it's definitely possible, so there's no need to use static all over the place (unless for the things that static IS used for - constants, enums, ...)

For example, I can have a Square class looking like this.

public class Square {
    private float size;
    private Vector3f position;
    private Color color;

    public Cube(float size, Vector3f position, Color color) {
        this.size = size;
        this.position = position;
        this.color = color;
    }

    public Vector3f getPosition() {
        return this.position;
    }

    public float getSize() {
        return this.size;
    }

    public Color getColor() {
        return this.color;
    }

    public void render() {
        float halfsize = size / 2;
        // bottom left
        glVertex3f(position.x - halfsize, position.y - halfsize, 0); // z-axis = 0
        // top left
        glVertex3f(position.x - halfsize, position.y + halfsize, 0);
        // top right
        glVertex3f(position.x + halfsize, position.y + halfsize, 0);
        // bottom right
        glVertex3f(position.x + halfsize, position.y - halfsize, 0);
    }
}

You would probably make some kind of Interface for this type of objects with a render() method which then should be implemented for each type of object you want to draw (squares, triangles, cubes, octagons, whatever) or offload it to a Renderer class of some kind. The only thing that is important is that the OpenGL calls happen in the correct order each frame. For each frame that your game is rendering, you need to repeat the same steps, for example:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

// Do a single loop (logic/render)
loopCycle();

// Let the CPU synchronize with the GPU if GPU is tagging behind
Display.update();

So you're quite free to structure your program the way you want :-)

\$\endgroup\$
4
  • \$\begingroup\$ Thank you, but the issue I'm running into is that, when running LWJGL (exclusively), any class I instantiate that has a non-static private field seems to fail to set that field. I will attempt to run the code you have provided, but as an example, unless I declare it to be static, position would always be equal to null. Are you familiar with this problem? \$\endgroup\$ Nov 25, 2013 at 1:08
  • \$\begingroup\$ If you could provide me with the startup code of your application (or an example sample) or provide it here (which I prefer), I would be able to help you more easily. \$\endgroup\$ Nov 25, 2013 at 15:29
  • 1
    \$\begingroup\$ I've implemented your solution, and it works nicely. I must be making a mistake somewhere else in the code. (This is an enormous relief to me.) Let me see if I can dig the rest of it up. \$\endgroup\$ Nov 25, 2013 at 23:40
  • \$\begingroup\$ While digging, I've noticed a new problem that might involve the way that I'm applying textures. Let me take care of that, and if the original bug persists (and is not just a facet of this one,) I'll post it; otherwise, thank you for your response, it's been very helpful. \$\endgroup\$ Nov 26, 2013 at 0:28
0
\$\begingroup\$

So, in conclusion to my original issues, it seems that a lot of this has to do with the application of a state machine in my more typical style of programming.

As to whether most GPUs are particularly multicore, I don't know. It's ultimately irrelevant given the necessity to buffer data to them to begin with. However, it seems that it is critical that all calls to GL**, or even many of the calls to Display, come from the same thread. This makes sense, as calls from multiple threads or programs is often undefined and I want exclusive-mode graphics anyway.

At the same time, things like OpenAL and JInput have nothing to do with graphics, and more than likely are state machines in themselves. So, ideally, they would have a thread of their own. So, it behoves me to create a HardwareLibrary interface, which extends Runnable, which each of these libraries can be encapsulated in. They will run on independent threads, managed by an additional thread called ThreadManager. ThreadManager will keep a Stack of HardwareLibraries that are implemented, and shut each one down after a cueShutdown() call, before ending the program. This can be implemented via a low-risk static method.

By keeping everything non-OpenGL in ordinary classes that interact with, but do not depend on, the encapsulating GL class, I can program as I typically do. The run-down is as follows.

public interface HardwareLibrary extends Runnable {
public void setExclusive(boolean mode);

    /** Rasterize/display any buffered data. **/
public void render();

    /** End loop, close thread **/
public void cease();
}

And the only section that really needs to worry about static fields:

public abstract class GL extends AbstractHardwareLibrary {

private int FPS = 60;
private boolean exclusive = false;

private volatile boolean isRunning = false;

@Override
public void run() {
    isRunning = true;

    if (!Display.isCreated())
        try {
            createDisplay();
        } catch (LWJGLException ex) {
            ex.printStackTrace();
            System.exit(0);
        }

    while (isRunning) {
        render();

        //check status of Display. Add any other shutdown cues here.
        if(Display.isCloseRequested())
            DebugMain.cueShutdown();

        Display.sync(FPS);
        Display.update();
    }
}

public void createDisplay() throws LWJGLException {
    Display.create();
    this.setDisplayMode(Display.getDesktopDisplayMode().getWidth(), Display
            .getDesktopDisplayMode().getHeight(), true);
}

/**
 * Renders all graphics relevant to the thread.
 */
public abstract void render();

/**
 * Set the display mode to be used; defaults to non-exclusive mode.
 * 
 * @param width
 *            The width of the display required
 * @param height

 *            The height of the display required
 */
public void setDisplayMode(int width, int height) {
    this.setDisplayMode(width, height, false);
}

/**
 * Set the display mode to be used
 * 
 * @param width
 *            The width of the display required
 * @param height
 *            The height of the display required
 * @param fullscreen
 *            True if we want fullscreen mode
 */
public void setDisplayMode(int width, int height, boolean fullscreen) {

            // ...
            // Fairly well-understood material omitted, check Ninja Cave's tutorials
            // for details
            // ...
}

@Override
public void setExclusive(boolean mode) {
    this.exclusive = mode;

    this.setDisplayMode(Display.getDisplayMode().getWidth(), Display
            .getDisplayMode().getHeight(), true);
}

@Override
public void cease() {
    isRunning = false;
}

}

AL is more-or-less the same idea. Rename pending, as it may be confused for the actual LWJGL class called AL; I'm not working with audio yet, so I'm putting it off. Now, ThreadManager:

class ThreadManager implements Runnable {

Stack<HardwareLibrary> threadsManaged = new Stack<>();

private volatile boolean shutdownTime = false;

@Override
public void run() {
    try {
        while (!shutdownTime) {
            // Do nothing
            Thread.yield();  //Avoid busy-waiting.
        }
    } catch(IllegalStateException ex) {
        //Window too torn down to count as created; good enough for me. 
    } finally {

        while (!threadsManaged.isEmpty()) {
            threadsManaged.pop().cease();
        }
    }
}

public void addLibrary(HardwareLibrary lib) {
    this.threadsManaged.add(lib);
}

    /** Cue the stopping and dismantling of all HardwareLibrary threads. */
public void shutdown() {
    this.shutdownTime = true;
}

}

While this is technically a debugging (and thus disposable) class, the main class would go something like so:

public class DebugMain {
GL gl = new GL() {

    @Override
    public void render() {
        // TODO Auto-generated method stub
        System.out.println("Rendering " + Math.random());
    }

};
Thread glThread = new Thread(gl);

AL al = new AL() {

    @Override
    public void render() {
        System.out.println("Playing " + Math.random());
    }
};
Thread alThread = new Thread(al);

static ThreadManager manager = new ThreadManager();
Thread managerThread = new Thread(manager);

public static void main(String args[]) throws LWJGLException {
    new DebugMain();
}

public DebugMain() throws LWJGLException {
    manager.addLibrary(al);
    manager.addLibrary(gl);

    managerThread.start();

    glThread.start();
    alThread.start();

}

    /* Static so it can be called by anyone, even without a reference; two programs
     * are not, by plan, going to be running at once so it shouldn't be a concern.
     * Asks ThreadManager to shut down all HardwareLibraries through their cease
     * methods. */
public static void cueShutdown() {
    manager.shutdown();
}
}

By doing this, I can keep my program pliable, take advantage of multicore systems while using libraries like OpenAL and OpenGL, and program with some realistic business-sense. It works fine; any remaining problems with my BufferedImage-to-Texture program are just sloppy coding by me and will be worked out in the debugging process.

Peace.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .