/*******************************************************************************
* Copyright (C) 2013 JMaNGOS <http://jmangos.org/>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package org.jmangos.tools.openGL;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
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.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.HashMap;
import java.util.Hashtable;
import javax.imageio.ImageIO;
import org.lwjgl.Sys;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
/**
* Modified from code originally written by:
*
* @author Kevin Glass
* @author Brian Matzon
*
*/
public class TextureLoader {
/** The table of textures that have been loaded in this loader */
private final HashMap<String, Texture> table = new HashMap<String, Texture>();
/** The colour model including alpha for the GL image */
public ColorModel glAlphaColorModel;
/** The colour model for the GL image */
private final ColorModel glColorModel;
private final int target = GL11.GL_TEXTURE_2D;
private final int dstPixelFormat = GL11.GL_RGBA;
// private int dstPixelFormat = GL13.GL_COMPRESSED_RGBA;
private final int minFilter = GL11.GL_LINEAR;
private final int magFilter = GL11.GL_LINEAR;
/**
* Create a new texture loader based on the game panel
*
* @param gl
* The GL content in which the textures should be loaded
*/
public TextureLoader() {
// dstPixelFormat = 4;
this.glAlphaColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {
8, 8, 8, 8 }, true, false, Transparency.TRANSLUCENT, DataBuffer.TYPE_BYTE);
this.glColorModel =
new ComponentColorModel(ColorSpace.getInstance(ColorSpace.CS_sRGB), new int[] {
8, 8, 8, 0 }, false, false, Transparency.OPAQUE, DataBuffer.TYPE_BYTE);
}
/**
* Create a new texture ID
*
* @return A new texture ID
*/
private int createTextureID() {
final IntBuffer tmp = createIntBuffer(1);
try {
GL11.glGenTextures(tmp);
} catch (final NullPointerException e) {
// e.printStackTrace();
Sys.alert(
"Error",
"Your system is not capable of running this game.\nPlease make sure your video drivers are current.");
System.exit(0);
}
return tmp.get(0);
}
/**
* Load a texture
*
* @param resourceName
* The location of the resource to load
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getTexture(final String resourceName, final boolean injar) throws IOException {
Texture tex = this.table.get(resourceName);
if (tex != null) {
return tex;
}
tex = getTexture(resourceName, injar, this.target, // target
this.dstPixelFormat, // dst pixel format
this.minFilter, // min filter (unused)
this.magFilter);
this.table.put(resourceName, tex);
return tex;
}
/**
* Load a texture into OpenGL from a image reference on disk.
*
* @param resourceName
* The location of the resource to load
* @param target
* The GL target to load the texture against
* @param dstPixelFormat
* The pixel format of the screen
* @param minFilter
* The minimising filter
* @param magFilter
* The magnification filter
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getTexture(final String resourceName, final boolean injar, final int target,
final int dstPixelFormat, final int minFilter, final int magFilter) throws IOException {
int srcPixelFormat = 0;
// create the texture ID for this texture
final int textureID = createTextureID();
final Texture texture = new Texture(target, textureID);
// bind this texture
GL11.glBindTexture(target, textureID);
final BufferedImage bufferedImage = loadImage(resourceName, injar);
texture.setWidth(bufferedImage.getWidth());
texture.setHeight(bufferedImage.getHeight());
if (bufferedImage.getColorModel().hasAlpha()) {
srcPixelFormat = GL11.GL_RGBA;
} else {
srcPixelFormat = GL11.GL_RGB;
}
// convert that image into a byte buffer of texture data
final ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
if (target == GL11.GL_TEXTURE_2D) {
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
// produce a texture from the byte buffer
/*
* GL11.glTexImage2D(target, 0, dstPixelFormat,
* get2Fold(bufferedImage.getWidth()),
* get2Fold(bufferedImage.getHeight()), 0, srcPixelFormat,
* GL11.GL_UNSIGNED_BYTE,
* textureBuffer );
*/
GLU.gluBuild2DMipmaps(target, dstPixelFormat, get2Fold(bufferedImage.getWidth()),
get2Fold(bufferedImage.getHeight()), srcPixelFormat, GL11.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
/**
* Get the closest greater power of 2 to the fold number
*
* @param fold
* The target number
* @return The power of 2
*/
private int get2Fold(final int fold) {
int ret = 2;
while (ret < fold) {
ret *= 2;
}
return ret;
}
/**
* Convert the buffered image to a texture
*
* @param bufferedImage
* The image to convert to a texture
* @param texture
* The texture to store the data into
* @return A buffer containing the data
*/
@SuppressWarnings("rawtypes")
private ByteBuffer convertImageData(final BufferedImage bufferedImage, final Texture texture) {
ByteBuffer imageBuffer = null;
WritableRaster raster;
BufferedImage texImage;
int texWidth = 2;
int texHeight = 2;
// find the closest power of 2 for the width and height
// of the produced texture
while (texWidth < bufferedImage.getWidth()) {
texWidth *= 2;
}
while (texHeight < bufferedImage.getHeight()) {
texHeight *= 2;
}
texture.setTextureHeight(texHeight);
texture.setTextureWidth(texWidth);
// create a raster that can be used by OpenGL as a source
// for a texture
if (bufferedImage.getColorModel().hasAlpha()) {
raster =
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 4,
null);
texImage = new BufferedImage(this.glAlphaColorModel, raster, false, new Hashtable());
} else {
raster =
Raster.createInterleavedRaster(DataBuffer.TYPE_BYTE, texWidth, texHeight, 3,
null);
texImage = new BufferedImage(this.glColorModel, raster, false, new Hashtable());
}
// copy the source image into the produced image
final Graphics g = texImage.getGraphics();
g.setColor(new Color(0f, 0f, 0f, 0f));
g.fillRect(0, 0, texWidth, texHeight);
g.drawImage(bufferedImage, 0, 0, null);
// build a byte buffer from the temporary image
// that be used by OpenGL to produce a texture.
final byte[] data = ((DataBufferByte) texImage.getRaster().getDataBuffer()).getData();
imageBuffer = ByteBuffer.allocateDirect(data.length);
imageBuffer.order(ByteOrder.nativeOrder());
imageBuffer.put(data, 0, data.length);
imageBuffer.flip();
return imageBuffer;
}
/**
* Load a given resource as a buffered image
*
* @param ref
* The location of the resource to load
* @return The loaded buffered image
* @throws IOException
* Indicates a failure to find a resource
*/
private BufferedImage loadImage(final String ref, final boolean injar) throws IOException {
// URL url = TextureLoader.class.getClassLoader().getResource(ref);
// if (url == null) {
// throw new IOException("Cannot find: "+ref);
// }
if (injar) {
final BufferedImage bufferedImage =
ImageIO.read(new BufferedInputStream(
getClass().getClassLoader().getResourceAsStream(ref)));
return bufferedImage;
} else {
final File file = new File(ref);
BufferedImage bufferedImage = null;
try {
bufferedImage = ImageIO.read(file);
} catch (final IOException e) {
System.out.println("Could not load texture: " + ref);
}
return bufferedImage;
}
}
/**
* Creates an integer buffer to hold specified ints - strictly a utility
* method
*
* @param size
* how many int to contain
* @return created IntBuffer
*/
protected IntBuffer createIntBuffer(final int size) {
final ByteBuffer temp = ByteBuffer.allocateDirect(4 * size);
temp.order(ByteOrder.nativeOrder());
return temp.asIntBuffer();
}
// ////////////////////////////////////////////////
// // Added for BufferedImage Support /////////////
// ////////////////////////////////////////////////
/**
* Load a texture
*
* @param resourceName
* The location of the resource to load
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getTexture(final String resourceName, final BufferedImage resourceImage)
throws IOException {
Texture tex = this.table.get(resourceName);
if (tex != null) {
return tex;
}
tex = getTexture(resourceImage, this.target, // target
this.dstPixelFormat, // dst pixel format
this.minFilter, // min filter (unused)
this.magFilter);
this.table.put(resourceName, tex);
return tex;
}
/**
* Load a texture into OpenGL from a BufferedImage
*
* @param resourceName
* The location of the resource to load
* @param target
* The GL target to load the texture against
* @param dstPixelFormat
* The pixel format of the screen
* @param minFilter
* The minimising filter
* @param magFilter
* The magnification filter
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getTexture(final BufferedImage resourceimage, final int target,
final int dstPixelFormat, final int minFilter, final int magFilter) throws IOException {
int srcPixelFormat = 0;
// create the texture ID for this texture
final int textureID = createTextureID();
final Texture texture = new Texture(target, textureID);
// bind this texture
GL11.glBindTexture(target, textureID);
final BufferedImage bufferedImage = resourceimage;
texture.setWidth(bufferedImage.getWidth());
texture.setHeight(bufferedImage.getHeight());
if (bufferedImage.getColorModel().hasAlpha()) {
srcPixelFormat = GL11.GL_RGBA;
} else {
srcPixelFormat = GL11.GL_RGB;
}
// convert that image into a byte buffer of texture data
final ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
if (target == GL11.GL_TEXTURE_2D) {
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
// produce a texture from the byte buffer
/*
* GL11.glTexImage2D(target, 0, dstPixelFormat,
* get2Fold(bufferedImage.getWidth()),
* get2Fold(bufferedImage.getHeight()), 0, srcPixelFormat,
* GL11.GL_UNSIGNED_BYTE,
* textureBuffer );
*/
GLU.gluBuild2DMipmaps(target, dstPixelFormat, get2Fold(bufferedImage.getWidth()),
get2Fold(bufferedImage.getHeight()), srcPixelFormat, GL11.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
// ////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////
// // Added for No MipMapping Support /////////////
// ////////////////////////////////////////////////
/**
* Load a texture
*
* @param resourceName
* The location of the resource to load
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getNMMTexture(final String resourceName, final BufferedImage resourceImage)
throws IOException {
Texture tex = this.table.get(resourceName);
if (tex != null) {
return tex;
}
tex = getNMMTexture(resourceImage, this.target, // target
this.dstPixelFormat, // dst pixel format
GL11.GL_NEAREST, // min filter (unused)
GL11.GL_LINEAR);
this.table.put(resourceName, tex);
return tex;
}
/**
* Load a texture into OpenGL from a BufferedImage
*
* @param resourceName
* The location of the resource to load
* @param target
* The GL target to load the texture against
* @param dstPixelFormat
* The pixel format of the screen
* @param minFilter
* The minimising filter
* @param magFilter
* The magnification filter
* @return The loaded texture
* @throws IOException
* Indicates a failure to access the resource
*/
public Texture getNMMTexture(final BufferedImage resourceimage, final int target,
final int dstPixelFormat, final int minFilter, final int magFilter) throws IOException {
int srcPixelFormat = 0;
// create the texture ID for this texture
final int textureID = createTextureID();
final Texture texture = new Texture(target, textureID);
// bind this texture
GL11.glBindTexture(target, textureID);
final BufferedImage bufferedImage = resourceimage;
texture.setWidth(bufferedImage.getWidth());
texture.setHeight(bufferedImage.getHeight());
if (bufferedImage.getColorModel().hasAlpha()) {
srcPixelFormat = GL11.GL_RGBA;
} else {
srcPixelFormat = GL11.GL_RGB;
}
// convert that image into a byte buffer of texture data
final ByteBuffer textureBuffer = convertImageData(bufferedImage, texture);
if (target == GL11.GL_TEXTURE_2D) {
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MIN_FILTER, minFilter);
GL11.glTexParameteri(target, GL11.GL_TEXTURE_MAG_FILTER, magFilter);
}
// produce a texture from the byte buffer
GL11.glTexImage2D(target, 0, dstPixelFormat, get2Fold(bufferedImage.getWidth()),
get2Fold(bufferedImage.getHeight()), 0, srcPixelFormat, GL11.GL_UNSIGNED_BYTE,
textureBuffer);
return texture;
}
// ////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////
// ////////////////////////////////////////////////////////////////
}