/* @author Job Zwiers  
 * $Date: 2005/02/09 17:17:04 $      
 */

import java.awt.*;
import java.awt.image.*;
import java.awt.image.BufferedImage; 
import java.awt.event.*;
import java.io.*;
import java.nio.*;
import javax.imageio.*;
import javax.swing.JFrame;
import net.java.games.jogl.*;

/**
 * A program showing a problem with texture loading/mipmapping, for textures
 * with sizes that are not power-of-two.
 * The example texture, grass2.jpg, loaded in init(), has size 756 X 512, and causes the program to hang.
 * If it is replaced by grass3.jpg (same texture, but "cropped" to size 512 X 512), a textured
 * triangle shows up, as expected, and everything seems ok.
 * Error output for grass2.jpg: 
Texture: 756 X 512
Exception in thread "main" java.nio.BufferUnderflowException
	at java.nio.Buffer.nextGetIndex(Buffer.java:398)
	at java.nio.DirectByteBuffer.get(DirectByteBuffer.java:205)
	at net.java.games.jogl.impl.mipmap.ScaleInternal.scale_internal_ubyte(ScaleInternal.java:222)
	at net.java.games.jogl.impl.mipmap.BuildMipmap.gluBuild2DMipmapLevelsCore(BuildMipmap.java:549)
	at net.java.games.jogl.impl.mipmap.Mipmap.gluBuild2DMipmaps(Mipmap.java:710)
	at net.java.games.jogl.impl.GLUImpl.gluBuild2DMipmapsJava(GLUImpl.java:583)
	at net.java.games.jogl.impl.GLUImpl.gluBuild2DMipmaps(GLUImpl.java:819)
	at project.texturebug.TextureBug.loadGLTexture(TextureBug.java:139)
	at project.texturebug.TextureBug.init(TextureBug.java:53)
	....
 */
public class TextureBug extends JFrame implements GLEventListener {
   private GLCanvas glCanvas; 
   private int texId;
   private static final long serialVersionUID = 0L; // standard Java1.5 annoyance
 
   /**
    * called by Jogl upon OpenGL context creation
    */
   public void init(GLDrawable glDrawable) {    
      GL gl = glDrawable.getGL();
      gl.glClearColor(0.7f, 0.7f, 1.0f, 0.0f);  // background color for the OpenGL window
      gl.glClearDepth(1.0);            
      gl.glDepthFunc(GL.GL_LEQUAL);
      gl.glEnable(GL.GL_DEPTH_TEST);
      gl.glShadeModel(GL.GL_SMOOTH);
      gl.glHint(GL.GL_PERSPECTIVE_CORRECTION_HINT, GL.GL_NICEST);
      gl.glEnable(GL.GL_TEXTURE_2D); 
      //texId = loadGLTexture("grass3.jpg", glDrawable);   // This is ok (512 X 512 texture)
      texId = loadGLTexture("grass2.jpg", glDrawable); // This causes the program to hang (756 X 512)
     
     
   }
   
   /* required by GLEventListener interface, but Jogl support not yet implemented (2004) */
   public void displayChanged(GLDrawable glDrawable, boolean modeChanged, boolean deviceChanged){}

   
   /**
    * called when the drawable has been resized.
    */
   public void reshape(GLDrawable glDrawable, int x, int y, int width, int height) {
      final GL gl = glDrawable.getGL();
      final GLU glu = glDrawable.getGLU();

      if(height <= 0)  height = 1;
      gl.glViewport(0, 0, width, height);
      
      gl.glMatrixMode(GL.GL_PROJECTION);        
      gl.glLoadIdentity();    
      glu.gluPerspective(45.0f, (float)width/(float)height, 0.1f, 1000.0f);   
      
      gl.glMatrixMode(GL.GL_MODELVIEW);    
      gl.glLoadIdentity();         
   }


   public void display(GLDrawable glDrawable) {
      final GL gl = glDrawable.getGL();
      gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);      
      gl.glLoadIdentity();                              
      gl.glTranslatef(0.0f, 0.0f, -5.0f);
      gl.glBindTexture(GL.GL_TEXTURE_2D, texId);
      gl.glBegin(GL.GL_TRIANGLES);
         gl.glColor3f(1.0f,0.0f,0.0f); gl.glTexCoord2f(0.5f, 1.0f); gl.glVertex3f( 0.0f, 1.0f, 1.0f);    
         
         gl.glColor3f(0.0f,1.0f,0.0f); gl.glTexCoord2f(0.0f, 0.0f); gl.glVertex3f(-1.0f,-1.0f, 1.0f);    
         gl.glColor3f(0.0f,0.0f,1.0f); gl.glTexCoord2f(0.0f, 1.0f); gl.glVertex3f( 1.0f,-1.0f, 1.0f);    
      gl.glEnd();           
   }
   
   /**
    * terminated the OpenGL rendering thread, and exits
    */
   public boolean terminate() {
      //animator.stop(); 
      //System.out.println("terminating..");
      System.exit(0);
      return true;
   }
        

   public static int createTextureID(GL gl) { 
      int[] ids = new int[1]; 
      gl.glGenTextures(1, ids); 
      return ids[0]; 
   }

   public static int loadGLTexture(String resourceName, GLDrawable glDrawable) { 
      BufferedImage bufferedImage = null; 
      try {
         bufferedImage = ImageIO.read( new File( resourceName ));
      } catch (IOException e) {
          System.out.println("Could not load texture " + resourceName + ": "+ e);  
      }
      
      byte[] data = ((DataBufferByte) bufferedImage.getRaster().getDataBuffer()).getData(); 
      ByteBuffer textureBuffer = ByteBuffer.allocateDirect( data.length );                   
      textureBuffer.order(ByteOrder.nativeOrder()); 
      textureBuffer.put( data, 0, data.length ); 

      final GL gl = glDrawable.getGL();
      final GLU glu = glDrawable.getGLU();

      int textureId = createTextureID(gl); 
      gl.glBindTexture(GL.GL_TEXTURE_2D, textureId ); 
      gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_REPEAT); 
      gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_REPEAT); 
      gl.glTexEnvf (GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_REPLACE);
      gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR); // Magnification always GL_LINEAR
      gl.glTexParameteri(GL.GL_TEXTURE_2D,GL.GL_TEXTURE_MIN_FILTER,GL.GL_LINEAR_MIPMAP_LINEAR);
      
      int wd = bufferedImage.getWidth();
      int ht = bufferedImage.getHeight();
      System.out.println("Texture: " + wd + " X " + ht);
      glu.gluBuild2DMipmaps(GL.GL_TEXTURE_2D, GL.GL_RGB,bufferedImage.getWidth(), bufferedImage.getHeight(),GL.GL_BGR, GL.GL_UNSIGNED_BYTE, textureBuffer ); 
      return textureId;
   }



   public TextureBug() {
      super("Jogl Test");      
      this.addWindowListener(new WindowAdapter() {
         public void windowClosing(WindowEvent e) { terminate(); }
      });
      
      glCanvas = GLDrawableFactory.getFactory().createGLCanvas(new GLCapabilities());
      glCanvas.addGLEventListener(this);
                 
      setSize(800, 600);
      setLocationRelativeTo(null);
      getContentPane().setLayout(new BorderLayout());
      getContentPane().add(glCanvas, BorderLayout.CENTER);      
      setVisible(true);
   }
 
   public static void main(String[] args) {
      TextureBug buggy = new TextureBug();

   } 
   
}