I have a problem with texture loading. I have a code example which works on my notebook very fine, but on my desktop computer an OpenGLException will be thrown:
org.lwjgl.opengl.OpenGLException: Invalid value (1281)
at org.lwjgl.opengl.Util.checkGLError(Util.java:56)
at org.lwjgl.opengl.Display.swapBuffers(Display.java:555)
at org.lwjgl.opengl.Display.update(Display.java:571)
at TexturedHouse.run(TexturedHouse.java:44)
at TexturedHouse.main(TexturedHouse.java:23)
After some code changes the example is working now, but the textured model (http://www.2send.us/en/picture.php?file=148ce99a61.png) is looking very bad:
import java.awt.Graphics;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ComponentColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.glu.GLU;
public class TexturedHouse
{
private static boolean exit;
public static void main(String[] args)
{
new TexturedHouse().run();
}
public void run()
{
if (Display.isCreated() == false)
{
try
{
Display.setTitle("Textured House");
Display.setDisplayMode(new DisplayMode(640, 480));
Display.setVSyncEnabled(true);
Display.create();
init();
synchronized (this)
{
exit = false;
}
while (exit == false)
{
// call update
Display.update();
// check for exit
if (Display.isCloseRequested())
{
synchronized (this)
{
exit = true;
}
}
// the display is active
else if (Display.isActive())
{
update();
Display.sync(60);
}
// the display is inactive
else
{
try
{
Thread.sleep(100);
}
catch (InterruptedException ie)
{
ie.printStackTrace();
}
update();
}
}
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
Display.destroy();
}
}
}
public void init()
{
try
{
// load the image
File file = new File("woodtexture.png");
BufferedImage loadedImage = ImageIO.read(file);
// obtain the texture size
int textureWidth = 2;
while (textureWidth < loadedImage.getWidth())
{
textureWidth *= 2;
}
int textureHeight = 2;
while (textureHeight < loadedImage.getHeight())
{
textureHeight *= 2;
}
// create the texture image
ColorSpace colorSpace = ColorSpace.getInstance(ColorSpace.CS_sRGB);
ComponentColorModel colorModel;
WritableRaster raster;
Hashtable<Object, Object> properties = new Hashtable<Object, Object>();
BufferedImage textureImage;
if (loadedImage.getColorModel().hasAlpha())
{
colorModel = new ComponentColorModel(colorSpace, new int[] { 8, 8, 8, 8 }, true, false,
Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, textureWidth,
textureHeight, 4, null);
textureImage = new BufferedImage(colorModel, raster, false, properties);
}
else
{
colorModel = new ComponentColorModel(colorSpace, new int[] { 8, 8, 8, 0 }, false, false,
Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
raster = Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, textureWidth,
textureHeight, 3, null);
textureImage = new BufferedImage(colorModel, raster, false, properties);
}
Graphics graphics = textureImage.getGraphics();
graphics.fillRect(0, 0, textureWidth, textureHeight);
graphics.drawImage(loadedImage, 0, 0, null);
// create the texture buffer
byte[] textureData = ((DataBufferByte) textureImage.getRaster().getDataBuffer()).getData();
ByteBuffer textureBuffer = ByteBuffer.allocateDirect(textureData.length);
textureBuffer.order(ByteOrder.nativeOrder());
textureBuffer.put(textureData, 0, textureData.length);
textureBuffer.flip();
// init OpenGL
GL11.glClearColor(0.3f, 0.5f, 0.8f, 1.0f);
GL11.glClearDepth(1f);
GL11.glDepthFunc(GL11.GL_LEQUAL);
GL11.glEnable(GL11.GL_DEPTH_TEST);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 1);
GL11.glPixelStorei(GL11.GL_UNPACK_ALIGNMENT, 1);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameterf(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_DECAL);
GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, textureWidth, textureHeight, 0,
GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, textureBuffer);
}
catch (IOException e)
{
e.printStackTrace();
}
}
public void update()
{
// clear the screen
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_STENCIL_BUFFER_BIT
| GL11.GL_DEPTH_BUFFER_BIT);
// the projection
GL11.glMatrixMode(GL11.GL_PROJECTION);
GL11.glLoadIdentity();
GLU.gluPerspective(15, 640f / 480f, 1, 1500);
// the model
GL11.glMatrixMode(GL11.GL_MODELVIEW);
GL11.glLoadIdentity();
GL11.glTranslatef(0, 0, -1000f);
GL11.glRotatef(75, 1f, 1f, 1f);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 1);
// // house wall back
GL11.glBegin(GL11.GL_POLYGON);
GL11.glVertex3f(0, 50, -70);
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(-50, 10, -70);
GL11.glTexCoord2f(2f, 0f);
GL11.glVertex3f(-50, -60, -70);
GL11.glTexCoord2f(2f, 1f);
GL11.glVertex3f(50, -60, -70);
GL11.glTexCoord2f(0f, 1f);
GL11.glVertex3f(50, 10, -70);
GL11.glEnd();
GL11.glBegin(GL11.GL_QUADS);
// // house wall left
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(-50, 10, -70);
GL11.glTexCoord2f(2f, 0f);
GL11.glVertex3f(-50, 10, 70);
GL11.glTexCoord2f(2f, 1f);
GL11.glVertex3f(-50, -60, 70);
GL11.glTexCoord2f(0f, 1f);
GL11.glVertex3f(-50, -60, -70);
// // house wall right
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(50, 10, -70);
GL11.glTexCoord2f(2f, 0f);
GL11.glVertex3f(50, 10, 70);
GL11.glTexCoord2f(2f, 1f);
GL11.glVertex3f(50, -60, 70);
GL11.glTexCoord2f(0f, 1f);
GL11.glVertex3f(50, -60, -70);
// // grounding
GL11.glVertex3f(-50, -60, -70);
GL11.glVertex3f(50, -60, -70);
GL11.glVertex3f(50, -60, 70);
GL11.glVertex3f(-50, -60, 70);
// // house top left
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(-50, 10, -70);
GL11.glTexCoord2f(1f, 0f);
GL11.glVertex3f(0, 50, -70);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex3f(0, 50, 70);
GL11.glTexCoord2f(0f, 1f);
GL11.glVertex3f(-50, 10, 70);
// // house top right
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(50, 10, -70);
GL11.glTexCoord2f(1f, 0f);
GL11.glVertex3f(0, 50, -70);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex3f(0, 50, 70);
GL11.glTexCoord2f(0f, 1f);
GL11.glVertex3f(50, 10, 70);
GL11.glEnd();
// // house top front
GL11.glBegin(GL11.GL_TRIANGLES);
GL11.glTexCoord2f(0f, 0f);
GL11.glVertex3f(-50, 10, 70);
GL11.glTexCoord2f(1f, 0f);
GL11.glVertex3f(0, 50, 70);
GL11.glTexCoord2f(1f, 1f);
GL11.glVertex3f(50, 10, 70);
GL11.glEnd();
}
}
I changed the following lines from
// obtain the texture size
int textureWidth = loadedImage.getWidth();
int textureHeight = loadedImage.getHeight();
to
// obtain the texture size
int width = 2;
while (width < loadedImage.getWidth())
{
width *= 2;
}
int height = 2;
while (height < loadedImage.getHeight())
{
height *= 2;
}
I think the dimensions of the texture are oversized now, but if I use the exact dimensions for the texture, an OpenGLException will be thrown.
Is anybody here who can help me?
Ok, solved, just resize the image to the power of 2:
// obtain the texture size
int textureWidth = 2;
while (textureWidth < loadedImage.getWidth())
{
textureWidth *= 2;
}
int textureHeight = 2;
while (textureHeight < loadedImage.getHeight())
{
textureHeight *= 2;
}
// scale the image if the dimensions does not match
if (textureWidth != loadedImage.getWidth() || textureHeight != loadedImage.getHeight())
{
Image scaledImage = loadedImage.getScaledInstance(textureWidth, textureHeight, Image.SCALE_DEFAULT);
loadedImage = new BufferedImage(scaledImage.getWidth(null), scaledImage.getHeight(null), BufferedImage.TYPE_3BYTE_BGR);
Graphics2D graphics = loadedImage.createGraphics();
graphics.drawImage(scaledImage, 0, 0, null);
graphics.dispose();
}
The image resizing should be applied to the TextureLoader of SpaceInvaders, because this class has the same problem.
This is a general working of OpenGL (it only accepts textures with a power-of-two size). Probably why it wasn't explicitly done.
If you need non-power-of-two textures, then there is an extension to allow it.