Noob Question: Texturing how to?

Started by someone2, October 10, 2006, 01:29:41

Previous topic - Next topic

someone2

Hi,
I was wondering how to texture a quad using lwjgl?
I know the OpenGL side of the story (glGenTexture and glBindTexture), but I don't know how to load an image file to get the 2D array necessary for glGenTexture to work.

So, what should be the Java part of the story before jumping to OpenGL?

Sorry if this is Uber Noob  :oops: I am not a very good programmer.

Thanks a lot

Evil-Devil

You could use DevIL for loading texture. In the wiki there is a tutorial for this purpose. http://lwjgl.org/wiki/doku.php/lwjgl/tutorials/devil/loadingtextures

On the other side you can write your own textureloader with Java IO and the needed Ogl methods.

Like this TGA Loader:

/* =============================================================================
 * TGATextureLoader.java Bubble EngineOld 
 * Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
 * All rights reserved
 * -----------------------------------------------------------------------------
 * powered by LWJGL - Copyright (c) LWJGL Team (http://www.lwjgl.org)
 * powered by JAVA - Copyright (c) Sun Microsystems (http://java.sun.com)
 * -------------------------------------------------------------------------- */
package com.evildevil.bubble.core.texture;

//JAVA Imports
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;
import java.util.Arrays;

import com.evildevil.bubble.core.LogManager;
import com.evildevil.bubble.util.BufferUtils;

/**
 * @author Benjamin "Evil-Devil" Behrendt
 * @version 1.1, 25.03.2006 
 */
public final class TGATextureLoader extends TextureLoader {    
    private byte[] uncompressedTGAHeader = new byte[] {            
            0,0,2,0,0,0,0,0,0,0,0,0 };				// header of uncompressed tga image
    private byte[] compressedTGAHeader = new byte[] {            
            0,0,10,0,0,0,0,0,0,0,0,0 };				// header of compressed tga image
    private byte[] compareTGAHeader = new byte[12];	// to check if we really have a tga image
    private byte[] tgaHeader = new byte[6];			// only the first 6 bytes are usefull for us
    
    private byte[] colorBuffer;			// used for RLE compression
    private byte[] textureData;			// will store the TGA Image data    
    
    public TGATextureLoader() {             
    }
    
    public Texture loadTexture(String  textureName) throws TextureFormatException {
        // call TextureLoader::loadTexture to ensure everything went ok!
        super.loadTexture(textureName);
        
        // determine if it is an compressed or uncompressed tga image
        compareTGAHeader = getRawHeader(compareTGAHeader.length);        
        
        // load the image corresponding to be compressed or not        
        try {
            if (Arrays.equals(compareTGAHeader,uncompressedTGAHeader))	// load uncompressed TGA
                loadUncompressedTexture(textureName);
            else if (Arrays.equals(compareTGAHeader,compressedTGAHeader)) {	// load compressed TGA
                loadCompressedTexture(textureName);
            } else	// none TGA -> throw new TGA exception
                throw new TGAFormatException("texture \""+textureName+"\" is no tga image");
        } catch (TGAFormatException tgafe) {
            throw tgafe;		// rethrow the exception
        }                
        return texture;
    }
    
    /* 
     * @see com.evildevil.bubble.core.texture.ITextureLoad#loadUncompressedTexture(java.lang.String)
     * Fixed: 25.03.2006 got the RLE encoding working.
     */
    public int loadUncompressedTexture(String textureName) throws TGAFormatException {
    	LogManager.log("Attempting to load a uncompressed TGA Image.");
        texture = new Texture();
        textureFile = new File(textureName);
        try {
            // get the real header ^^
            tgaHeader = getHeader(tgaHeader.length);
            // get the texture format
            getTextureFormat();
        } catch (NullPointerException npe) {
            LogManager.logException(npe);
        } catch (TextureFormatException tfe) {
            LogManager.logException(tfe);
        }
        
        // load the binary texture data and close the stream
        textureData = loadTextureData(texture.width * texture.height * texture.bpp,true);
        
        // swap the r and g values
        /*for (int i=0; i<textureData.length; i+=texture.bpp) {
            textureData[i] ^= textureData[i+2] ^= textureData[i];
            textureData[i+2] ^= textureData[i];
        }*/
        // the image is mirrored vertical, but why?
        // one line
        //byte[] line = new byte[texture.width * texture.bpp];
        //for (int i=0; i<line.length)
        
        return createTexture(textureData);
    }
    
    public int loadCompressedTexture(String textureName) throws TGAFormatException {
        LogManager.log("Attempting to load a compressed TGA Image.");
        texture = new Texture();
        textureFile = new File(textureName);
        
        try {            
            // get the real header ^^
            tgaHeader = getHeader(tgaHeader.length);
            // get the texture format
            getTextureFormat();
        } catch (NullPointerException npe) {
            LogManager.logException(npe);
        } catch (TextureFormatException tfe) {
            LogManager.logException(tfe);
        }
        
        textureData = new byte[texture.width * texture.height * texture.bpp];
        
        final int pixelCount = texture.width * texture.height;	// pixels in this image
        int currPixel = 0;	// current pixel
        int currByte = 0;	// current byte                
        int chunkheader;
        
        do {
        	byte chunkHeader = 0;	// have to be unsigned -.-
        	// read
        	chunkheader = loadTextureData(1)[0] & 0xFF;
        	
        	// RAW Chunk
        	if (chunkheader < 128) {
        		chunkheader++;		// number of raw pixels
        		
        		// pixel reading
        		for (int counter = 0; counter < chunkheader; counter++) {
  			
        			writeTextureData(loadTextureData(texture.bpp),currByte);        			
        			
        			// increase the currByte counter
        			currByte+=texture.bpp;        			
        			currPixel++;
    			}
        	} else {	// RLE Header
        		chunkheader -= 127;
        		// read the data
        		colorBuffer = loadTextureData(texture.bpp);
        		
        		// pixel loop
        		for (int counter = 0; counter < chunkheader; counter++) {
        			
        			writeTextureData(colorBuffer,currByte);
        			
    				// increase the currByte counter
        			currByte+=texture.bpp;
        			currPixel++;
        		}
        	}
        } while (currPixel < pixelCount);
        
        return createTexture(textureData);
    }
    
    private int createTexture(byte[] data) {
    	// put the data in a bytebuffer
    	final ByteBuffer textureBuffer = BufferUtils.createByteBuffer(data);
        // create the glname for the new texture
    	final IntBuffer glName = generateGLName();
    	
    	generateTexture(glName);
    	bindTexture(glName.get(0));
    	
    	setLinearFilter();
    	generateTextureImage(textureBuffer);
    	texture.setGLName(glName.get(0));
    	
    	return glName.get(0);
    }
    
    private void writeTextureData(byte[] colorBuffer,int currentByte) {
    	textureData[currentByte] = colorBuffer[2];		// R Byte
		textureData[currentByte+1] = colorBuffer[1];	// G Byte
		textureData[currentByte+2] = colorBuffer[0];	// B Byte
		
		// 32bit (alpha) check
		if (texture.bpp == 4)
			textureData[currentByte+3] = colorBuffer[3];	// A Byte
    }
    
    public int getWidth(byte[] header) throws TextureFormatException {
        return Math.abs(header[1] * 256 + header[0]);
    }
    
    public int getHeight(byte[] header) throws TextureFormatException {        
        return Math.abs(header[3] * 256 + header[2]);        
    }
    
    public int getBitdepth(byte[] header) throws TextureFormatException {
        // we have to prove the correct bitdepth ourself, because the texture only checks
        // for zero values on width and height        
        if (header[4] != 24 && header[4] != 32)
            throw new TGAFormatException("bitdepth of the tga texture is not 24 or 32");
        return header[4];
    }
    
    public void getTextureFormat() throws TextureFormatException {        
        texture.setWidth(getWidth(tgaHeader));
        texture.setHeight(getHeight(tgaHeader));
        texture.setBitdepth(getBitdepth(tgaHeader));        
    }
}

/* =============================================================================
 * TextureLoader.java Bubble EngineOld 
 * Copyright (c) 2004 - 2005 Benjamin "Evil-Devil" Behrendt
 * All rights reserved
 * -----------------------------------------------------------------------------
 * powered by LWJGL - Copyright (c) LWJGL Team (http://www.lwjgl.org)
 * powered by JAVA - Copyright (c) Sun Microsystems (http://java.sun.com)
 * -------------------------------------------------------------------------- */
package com.evildevil.bubble.core.texture;

// JAVA Imports
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.IntBuffer;

import org.lwjgl.opengl.EXTBgra;
import org.lwjgl.opengl.GL12;

// LWJGL Imports
import static org.lwjgl.opengl.GL11.*;

// Bubble Engine Imports
import com.evildevil.bubble.core.EngineOld;
import com.evildevil.bubble.core.LogManager;
import com.evildevil.bubble.util.BufferUtils;

/**
 * Textureloading implementation will be done in subclasses or within custom classes
 * which implements ITextureLoad interface.
 * 
 * @author Evil-Devil
 * @version 1.0, 08.02.2005 
 */
public abstract class TextureLoader implements ITextureLoad {
    protected Texture texture = null;				// the texture that will holds the relevant data
    protected File textureFile = null;				// the file that represents the binary texture
    private FileInputStream fis = null;				// used for reading the texturedata
    private BufferedInputStream br = null;       // for webstart sucking 
    protected static glFilter defaultFilter = glFilter.LINEAR;		// the default filter that will used on filtering the texture
    
    public static enum glFilter {
        LINEAR, NEAREST, MIPMAPPED 
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getRawHeader(byte[])
     */
    public byte[] getRawHeader(int headerSize) throws NullPointerException  {
        if (textureFile == null)
            throw new NullPointerException("texture can't be null");
        // load the raw header
        return loadTextureData(headerSize);
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getHeader(byte[])
     * @exception a NullPointerException will be thrown if the textureFile is null
     */
    public byte[] getHeader(int headerSize) {        
        if (textureFile == null)
            throw new NullPointerException("texture can't be null");
        // load the header
        return loadTextureData(headerSize);
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getWidth(byte[])
     */
    public int getWidth(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getHeight(byte[])
     */
    public int getHeight(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#getBitDepth(byte[])
     */
    public int getBitdepth(byte[] header) throws TextureFormatException {
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadTexture(java.lang.String)
     */
    public Texture loadTexture(String textureName) throws TextureFormatException {
        texture = new Texture();
        textureFile = new File(textureName);
        return null;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadCompressedTexture(java.lang.String)
     */
    public int loadCompressedTexture(String textureName) throws TextureFormatException {        
        return 0;
    }

    /* 
     * @see com.evildevil.bubble.texture.ITextureLoad#loadUncompressedTexture(java.lang.String)
     */
    public int loadUncompressedTexture(String textureName) throws TextureFormatException {
        return 0;
    }    
    
    /*
     * use this to handle all obtainable informations with one function :)
     * But you can instead calling themselve and do the error handling :p
     */
    public abstract void getTextureFormat() throws TextureFormatException;
    
    /**
     * obtain the binary data from the texture file and return it to you
     * @param size
     * @return the loaded texturedata
     */
    public byte[] loadTextureData(int size) {        
        final byte[] textureData = new byte[size];
        try {
            if (fis == null) {
                fis = new FileInputStream(textureFile);
                if (EngineOld.isDebugMode())
                    LogManager.logDebug("FIS textureFile is \""+textureFile.getName()+"\"");
            }            
            fis.read(textureData);
            /*if (br == null) {
                br = new BufferedInputStream(
                        getClass().getResourceAsStream(textureFile.toString()));
            }
            br.read(textureData);*/
        } catch (FileNotFoundException fnfe) {
           // LogManager.logException(fnfe);
        } catch (IOException ioe) {
            LogManager.logException(ioe);
        }
        return textureData;
    }
    
    public byte[] loadTextureData(int size, boolean closeStream) {
        final byte[] textureData = loadTextureData(size);
        // close the stream if needed
        if (closeStream) {
            try {
                fis.close();
                //br.close();
            } catch (IOException ioe) {
                LogManager.logException(ioe);
            } finally {
                fis = null;		// make sure with the next load there will be a new FIS created
                //br = null;
            }
        }
        return textureData;
    }
    
    protected IntBuffer generateGLName() {
        return BufferUtils.createIntBuffer(1);
    }
    
    protected void setLinearFilter() {
        // Linear Filtering
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    }
    
    protected void setNearestFilter() {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);        
    }
    
    protected void setMipMappedFilter() {
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
    }
    
    protected void setTextureFilter(glFilter filter) {
        if (filter == glFilter.LINEAR)
            setLinearFilter();
        else if (filter == glFilter.NEAREST)
            setNearestFilter();
        else if (filter == glFilter.MIPMAPPED)
            setMipMappedFilter();
    }
    
    protected void bindTexture(int glName) {
        glBindTexture(GL_TEXTURE_2D,glName);
    }
    
    protected void generateTexture(IntBuffer address) {
        glGenTextures(address);
    }
    
    protected void generateAndBindTexture(IntBuffer address) {
        generateTexture(address);
        bindTexture(address.get(0));
    }
    
    protected void generateTextureImage(ByteBuffer textureBuffer) {
        //int glColor = texture.bitdepth == 24 ? GL_RGB : GL_RGBA;
    	int glColor = texture.bitdepth == 24 ? GL12.GL_BGR : GL12.GL_BGRA;
        glTexImage2D(GL_TEXTURE_2D,0,glColor,texture.width,texture.height,0,glColor,GL_UNSIGNED_BYTE,textureBuffer);
        // i guess we can free the buffer now, it isn't needed anymore
        //textureBuffer.clear();
    }
    
    protected ByteBuffer generateTextureBuffer(byte[] textureData) {
        return BufferUtils.createByteBuffer(textureData);

    }
    
    /**
     * performs all nessessary steps to create a functional gl texture, excepting texturefiltering, by calling
     * generateTexture(IntBuffer address) 
     * @param address the address that is used to create a valid gl name
     * @param textureBuffer the bytebuffer that holds the binary informations about the texture file
     */
    protected void buildTexture(IntBuffer address, ByteBuffer textureBuffer) {
        generateTexture(address);
        bindTexture(address.get(0));
        generateTextureImage(textureBuffer);
    }
    
    protected void buildTexture(IntBuffer address, ByteBuffer textureBuffer, glFilter filter) {
        buildTexture(address,textureBuffer);
        setTextureFilter(filter);
    }
    
    protected void buildTexture(byte[] textureData) {
        final IntBuffer address = generateGLName();
        // assign the glName to the texture
        texture.setGLName(address.get(0));
        System.out.println(address.get());
        // call an overloaded version of buildTexture() to do the rest work
        buildTexture(address,generateTextureBuffer(textureData));
    }
    
    protected void setDefaultFilter(glFilter filter) {
        defaultFilter = filter;
    }
}



In the end you just need an additional class that manage the loading and storing of the textures.
Its not the cleanest code, and comments were removed due recompile effect (at the moment i have no src of my engine @work)


//edit: Ok, updated with valid code. The TextureLoader class isn't at is best. But still be used by me ;)

Evil