    package org.waltermilner;
    
    import java.awt.Frame;
    import java.awt.event.WindowAdapter;
    import java.awt.event.WindowEvent;
    
    import javax.media.opengl.*;
    import javax.media.opengl.glu.*;
    import com.sun.opengl.util.*;
    import java.nio.ByteBuffer;
    

    public class Texture2 implements GLEventListener {
    
        int texName;
        private GLU glu = new GLU();
        TGABuffer buffer;

        public static void main(String[] args) {
    
            Frame frame = new Frame("Texture 2");
            GLCanvas canvas = new GLCanvas();
    
            canvas.addGLEventListener(new Texture2());
            frame.add(canvas);
            frame.setSize(640, 480);
            final Animator animator = new Animator(canvas);
            frame.addWindowListener(new WindowAdapter() {
    
                @Override
                public void windowClosing(WindowEvent e) {
                    // Run this on another thread than the AWT event queue to
                    // make sure the call to Animator.stop() completes before
                    // exiting
                    new Thread(new Runnable() {
    
                        public void run() {
                            animator.stop();
                            System.exit(0);
                        }
                    }).start();
                }
            });
            // Center frame
            frame.setLocationRelativeTo(null);
            frame.setVisible(true);
            animator.start();
        }
    
        public void init(GLAutoDrawable drawable) {
            // like int InitGL(GLvoid)
            // but does not return an error code?
            // part of the GLEventListener interface
            // Use debug pipeline
            //drawable.setGL(new DebugGL(drawable.getGL()));
    
            GL gl = drawable.getGL();
    
            gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            gl.glShadeModel(GL.GL_SMOOTH);
            gl.glEnable(GL.GL_DEPTH_TEST);
            buffer = TGABufferMaker.make();
    
            // The following controls the alignment of the start of pixel rows in memory
            // 1 means align on bytes. This seems to be the default, since if you
            // comment this out, it still works
            gl.glPixelStorei(GL.GL_UNPACK_ALIGNMENT, 1);
            final int[] tmp = new int[1];
            // glGenTextures gives you a set of 'names' for textures
            // the names are identifying integers
            gl.glGenTextures(1, tmp, 0);
            texName = tmp[0];
            // make the named texture to be the type we want
            gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
            // say how it repeats if needed
            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);
            // and how texels and pixels map
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER,
                    GL.GL_LINEAR);
            gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER,
                    GL.GL_NEAREST);
            // connect to data
            gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGBA, buffer.getWidth(),
                    buffer.getHeight(), 0, GL.GL_RGBA, GL.GL_UNSIGNED_BYTE,
                    buffer.getBuffer());
        }
    
        public void reshape(GLAutoDrawable drawable, int x, int y, int width, int height) {
            // like GLvoid ReSizeGLScene(GLsizei width, GLsizei height)
            // reshape is part of the GLEventListener interface, and is called during first re-paint after a resize
            GL gl = drawable.getGL();
            
    
            if (height <= 0) { // avoid a divide by zero error!
    
                height = 1;
            }
            final float h = (float) width / (float) height;
            gl.glViewport(0, 0, width, height);
            gl.glMatrixMode(GL.GL_PROJECTION);
            gl.glLoadIdentity();
            glu.gluPerspective(45.0f, h, 0.1f, 100.0f);
            gl.glMatrixMode(GL.GL_MODELVIEW);
            gl.glLoadIdentity();
        }
    
        public void display(GLAutoDrawable drawable) {
            
    
            final GL gl = drawable.getGL();

            gl.glLoadIdentity();
    
            gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            gl.glEnable(GL.GL_TEXTURE_2D);
            gl.glTexEnvf(GL.GL_TEXTURE_ENV, GL.GL_TEXTURE_ENV_MODE, GL.GL_DECAL);
            gl.glBindTexture(GL.GL_TEXTURE_2D, texName);
            gl.glBegin(GL.GL_QUADS);
            gl.glColor3f(0.0f, 0.0f, 1.0f);
            gl.glTexCoord2f(0.0f, 0.0f);
            gl.glVertex3f(-1.0f, -1.0f, -3.0f);
            gl.glColor3f(1.0f, 1.0f, 0.0f);
            gl.glTexCoord2f(0.0f, 1.0f);
            gl.glVertex3f(-1.0f, 1.0f, -3.0f);
            gl.glTexCoord2f(1.0f, 1.0f);
            gl.glVertex3f(1.0f, 1.0f, -3.0f);
            gl.glTexCoord2f(1.0f, 0.0f);
            gl.glVertex3f(1.0f, -1.0f, -3.0f);
    
            gl.glEnd();
            gl.glFlush();
            gl.glDisable(GL.GL_TEXTURE_2D);
        }
    
        public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
        }
    }
    
