LWJGL Forum

Programming => OpenGL => Topic started by: someone2 on October 10, 2006, 01:29:41

Title: Noob Question: Texturing how to?
Post by: someone2 on October 10, 2006, 01:29:41
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
Title: Noob Question: Texturing how to?
Post by: Evil-Devil on October 10, 2006, 07:58:51
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