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?