package glapp;

import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.lang.reflect.Method;
import java.nio.*;
import java.io.*;
import java.net.URL;
import org.lwjgl.*;
import org.lwjgl.opengl.*;
import org.lwjgl.input.*;
import org.lwjgl.util.glu.*;

/**
 * Collection of functions to init and run an OpenGL app using LWJGL.
 * <P>
 * Includes functions to handle:  <BR>
 *        Setup display mode, keyboard, mouse, handle events<BR>
 *        Run main loop of application <BR>
 *        Buffer allocation -- manage IntBuffer, ByteBuffer calls. <BR>
 *        OpenGL functions  -- convert screen/world coords, set modes, lights, etc. <BR>
 *        Utility functions -- load images, convert pixels, getTimeInMillis, etc. <BR>
 * <P>
 * Has a main() function to run as an application, though this class has only
 * minimal placeholder functionality.  It is meant to be subclassed,
 * and the subclass will define setup() draw() mouseMove() functions, etc.
 * <P>
 * GLApp initializes the LWJGL environment for OpenGL rendering,
 * ie. creates a window, sets the display mode, inits mouse and keyboard,
 * then runs a loop.
 * <P>
 * Uses GLImage to load and hold image pixels.
 * <P>
 * napier -at- potatoland -dot- org
 *
 * @see GLImage
 */
public class GLApp {
    // Just for reference
    public static final String GLAPP_VERSION = ".5";

    // Byte size of data types: Used when allocating native buffers
    public static final int SIZE_DOUBLE = 8;
    public static final int SIZE_FLOAT = 4;
    public static final int SIZE_INT = 4;
    public static final int SIZE_BYTE = 1;

    // Application settings
    // These can be tweaked in main() before calling app.run()
    // to customize app behavior.
    public static int finishedKey = Keyboard.KEY_ESCAPE; // App will exit when this key is hit (set to 0 for no key exit)
    public static String window_title = "OpenGL Window"; // window title, set in initDisplay()
    public static String configFilename = "GLApp.cfg";   // init() calls loadSettings() to load initial settings from this file (OPTIONAL)
    public static boolean hideNativeCursor = false;      // hide the operating system cursor, see hideNativeCursor()
    public static boolean disableNativeCursor = false;   // turn completely off the operating system cursor, see disableNativeCursor()
    public static boolean VSyncEnabled = true;           // if true, synchronize screen updates with video refresh rate
    public static boolean useCurrentDisplay = false;     // when initing display, use the settings of the desktop (whatever the PC was using before app was started)
    public static boolean fullScreen = false;            // full screen or floating window
	public static boolean showMessages = true;           // if true, show debug messages, if false show only error messages (see msg() err())
    public static float aspectRatio = 0;                 // aspect ratio of OpenGL context (if 0, default to displayWidth/displayHeight)

    // Display settings (settings in glapp.cfg will override these)
    // initDisplay() will pick a Display that best matches displayWidth,
    // displayHeight, displayColorBits, displayFrequency.  If these values
    // are -1, initDisplay() will use the desktop screen settings.
    public static int displayWidth = -1;
    public static int displayHeight = -1;
    public static int displayColorBits = -1;
    public static int displayFrequency = -1;
    public static int depthBufferBits = 24;             // bits per pixel in the depth buffer
    public static int viewportX, viewportY;             // viewport position (will default to 0,0)
    public static int viewportW, viewportH;             // viewport size (will default to screen width, height)
    //private static int orthoWidth = 0;
    //private static int orthoHeight = 0;

    // DisplayMode chosen by initDisplay()
    // DM and displayMode are the same thing.
    public static DisplayMode DM, origDM;               // hold display mode we set, and the display mode when app first executes
    public static DisplayMode displayMode;              // hold display mode we set (same as DM)

    // Application variables
    // These are set internally but can be read by the
    // subclass application.
    public static Properties settings=new Properties(); // holds settings from file GLApp.cfg (see loadSettings())
    public static boolean finished;                     // App will exit when finished is true (when finishedKey is hit: see run())
    public static int cursorX, cursorY;                 // mouse position (see handleEvents())
    public static double lastFrameTime = 0;             // ticks since last frame was drawn (see run() and updateTimer())
    public static double secondsSinceLastFrame = 0;     // seconds elapsed since last frame was drawn (see updateTimer())
    public static long ticksPerSecond = 0;              // used to calc time in millis
    public static int frameCount = 0;                   // count frames per sec (just to track performance)

    // For copying screen image to a texture
    public static int screenTextureSize = 1024;         // how large should texture be to hold screen (see makeTextureForScreen())

    // NIO Buffers to retrieve OpenGL settings.
    // For memory efficiency and performance, instantiate these once, and reuse.
    // see getSetingInt(), getModelviewMatrix(), project(), unProject()
    public static IntBuffer     bufferViewport = allocInts(16);
    public static FloatBuffer   bufferModelviewMatrix = allocFloats(16);
    public static FloatBuffer   bufferProjectionMatrix = allocFloats(16);
    public static FloatBuffer   tmpResult = allocFloats(16);         // temp var to hold project/unproject results
    public static FloatBuffer   tmpFloats = allocFloats(4);          // temp var used by setLightPos(), setFog()
    public static ByteBuffer    tmpFloat = allocBytes(SIZE_FLOAT);   // temp var used by getZDepth()
    public static IntBuffer     tmpInts = allocInts(16);             // temp var used by getSettingInt()
    public static ByteBuffer 	tmpByte = allocBytes(SIZE_BYTE);     // temp var used by getStencilValue()
    public static ByteBuffer    tmpInt = allocBytes(GLApp.SIZE_INT); // temp var used by getPixelColor()

    // Material colors (see setMaterial())
    public static FloatBuffer mtldiffuse = allocFloats(4);     // color of the lit surface
    public static FloatBuffer mtlambient = allocFloats(4);     // color of the shadowed surface
    public static FloatBuffer mtlspecular = allocFloats(4);    // reflection color (typically this is a shade of gray)
    public static FloatBuffer mtlemissive = allocFloats(4);    // glow color
    public static FloatBuffer mtlshininess = allocFloats(4);   // size of the reflection highlight

    // Misc.
    public static float rotation = 0f;       // to rotate cubes (just to put something on screen)
    public static final float PIOVER180 = 0.0174532925f;   // A constant used in navigation: PI/180
	public static final float PIUNDER180 = 57.2957795130f;   // A constant used in navigation: 180/PI;
	static Hashtable OpenGLextensions;       // will be populated by extensionExists()
	static double avgSecsPerFrame=.01;       // to smooth out motion, keep a moving average of frame render times

    //========================================================================
    // Run main loop of application.  Handle mouse and keyboard input.
    //
    // The functions main(), run() and init() start and run the application.
    // The run() function starts a loop that handles mouse and keyboard events
    // and calls draw() repeatedly.
    //
    //========================================================================

    public static void main(String args[]) {
        GLApp demo = new GLApp();
        demo.run();
    }

    /**
     * Runs the application.  Calls init() function to setup OpenGL,
     * input and display.  Runs the main loop of the application, which handles
     * mouse and keyboard input.
     * <P>
     * Calls init(), handleEvents(), update() and draw(). <BR>
     * handleEvents() calls:  mouseMove(), mouseDown(), mouseUp(), keyDown(), keyUp()
     */
    public void run() {
    	// hold onto application class in case we need to load images from jar (see getInputStream())
    	setRootClass();
        try {
            // Init Display, Keyboard, Mouse, OpenGL, load config file
            init();
            // Main loop
            while (!finished) {
                if (!Display.isVisible()) {  // window is minimized
                    Thread.sleep(200L);
                }
                else if (Display.isCloseRequested()) {  // window X button clicked
                    finished = true;
                }
                else {   // yield a little so other threads can work
                    Thread.sleep(1);
                }
                updateTimer();      // track when frame was drawn (see secondsSinceLastFrame)
                handleEvents();     // call key...() and mouse...() functions based on input events
                update();           // do program logic here (subclass may override this)
                draw();             // redraw the screen (subclass overrides this)
                Display.update();
            }
        }
        catch (Exception e) {
            err("GLApp.run(): " + e);
            e.printStackTrace(System.out);
        }
        // prepare to exit
        cleanup();
        System.exit(0);
    }

    /**
     * Called only once when app is first started, init() prepares the display,
     * mouse and OpenGL context for use. Override init() only if you want to
     * substantially alter the app startup behavior.  Otherwise just override
     * initGL() to tweak the OpenGL context and setup() to load textures,
     * models, etc..
     */
    public void init()
    {
        // load settings from config file (display size, resolution, etc.)
        loadSettings(configFilename);
        initDisplay();
        initInput();
        initGL();
        setup();        // subclass usually overrides this
    	updateTimer();  // Do this once to init time values to something sane, otherwise the first game loop will report a huge secondsElapsedPerFrame
    }

    /**
     * Called by the run() loop.  Handles animation and input for each frame.
     */
    public void handleEvents() {
        int mouseDX = Mouse.getDX();
        int mouseDY = Mouse.getDY();
        int mouseDW = Mouse.getDWheel();
        // handle mouse motion
        if (mouseDX != 0 || mouseDY != 0 || mouseDW != 0) {
            cursorX += mouseDX;
            cursorY += mouseDY;
            if (cursorX < 0) {
                cursorX = 0;
            }
            else if (cursorX > displayMode.getWidth()) {
                cursorX = displayMode.getWidth();
            }
            if (cursorY < 0) {
                cursorY = 0;
            }
            else if (cursorY > displayMode.getHeight()) {
                cursorY = displayMode.getHeight();
            }
            mouseMove(cursorX,cursorY);
            //msg("DX=" + mouseDX + " DY=" + mouseDY + " cursorX=" + cursorX);
        }
        // handle mouse wheel event
        if (mouseDW != 0) {
        	mouseWheel(mouseDW);
        }
        // handle mouse clicks
        while ( Mouse.next() ) {
        	if(Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == true) {
        		mouseDown(cursorX, cursorY);
        	}
        	if(Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == false) {
        		mouseUp(cursorX, cursorY);
        	}
        	if(Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == true) {
        		mouseDown(cursorX, cursorY);
        	}
        	if(Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == false) {
        		mouseUp(cursorX, cursorY);
        	}
        }
        // Handle key hits
        while ( Keyboard.next() )  {
        	// check for exit key
            if (Keyboard.getEventKey() == finishedKey) {
                finished = true;
            }
            // pass key event to handler
            if (Keyboard.getEventKeyState()) {    // key was just pressed, trigger keyDown()
                keyDown(Keyboard.getEventKey());
            }
            else {
                keyUp(Keyboard.getEventKey());    // key was released
            }
        }

        // Count frames
        frameCount++;
        if ((Sys.getTime()-lastFrameTime) > ticksPerSecond) {
            //msg("==============> FramesPerSec=" + (frameCount/1) + " timeinsecs=" + getTimeInSeconds() + " timeinmillis=" + getTimeInMillis());
            frameCount = 0;
        }
    }

    /**
     * Load configuration settings from optional properties file.
     * File format is:<BR>
     * <PRE>
     * # Comment
     * displayWidth=1024
     * displayHeight=768
     * </PRE>
     *
     * @param configFilename
     */
    public void loadSettings(String configFilename)
    {
    	if (configFilename == null || configFilename.equals("")) {
    		return;
    	}
    	InputStream configFileIn = getInputStream(configFilename);
        settings = new Properties();
        if (configFileIn == null) {
            msg("GLApp.loadSettings() warning: config file " + configFilename + " not found, will use default settings.");
            return;
        }
        else {
        	try { settings.load(configFileIn); }
        	catch (Exception e) {
        		msg("GLApp.loadSettings() warning: " + e);
        		return;
        	}
        }
        // Debug: show the settings
        settings.list(System.out);
        // Check for available settings
        if (settings.getProperty("displayWidth") != null) {
            displayWidth = Integer.parseInt(settings.getProperty("displayWidth"));
        }
        if (settings.getProperty("displayHeight") != null) {
            displayHeight = Integer.parseInt(settings.getProperty("displayHeight"));
        }
        if (settings.getProperty("displayColorBits") != null) {
            displayColorBits = Integer.parseInt(settings.getProperty("displayColorBits"));
        }
        if (settings.getProperty("displayFrequency") != null) {
            displayFrequency = Integer.parseInt(settings.getProperty("displayFrequency"));
        }
        if (settings.getProperty("depthBufferBits") != null) {
            depthBufferBits = Integer.parseInt(settings.getProperty("depthBufferBits"));
        }
        if (settings.getProperty("aspectRatio") != null) {
            aspectRatio = Float.parseFloat(settings.getProperty("aspectRatio"));
        }
        if (settings.getProperty("fullScreen") != null) {
            fullScreen = settings.getProperty("fullScreen").toUpperCase().equals("YES");
        }
        if (settings.getProperty("useCurrentDisplay") != null) {
            useCurrentDisplay = settings.getProperty("useCurrentDisplay").toUpperCase().equals("YES");
        }
        if (settings.getProperty("finishedKey") != null) {  // key codes are defined in the lwjgl Keyboard class
            finishedKey = Integer.parseInt(settings.getProperty("finishedKey"));
        }
        if (settings.getProperty("window_title") != null) {
            window_title = settings.getProperty("window_title");
        }
        if (settings.getProperty("VSyncEnabled") != null) {
            VSyncEnabled = settings.getProperty("VSyncEnabled").toUpperCase().equals("YES");
        }
    }

    //========================================================================
    // Setup display mode
    //
    // Initialize Display, Mouse, Keyboard.
    //
    //========================================================================

    /**
     * Initialize the Display mode, viewport size, and open a Window.
     * By default the window is fullscreen, the viewport is the same dimensions
     * as the window.
     */
    public boolean initDisplay() {
        origDM = Display.getDisplayMode();  // current display settings
        msg("GLApp.initDisplay(): Current display mode is " + origDM);
    	// for display properties that have not been specified, default to current display value
    	if (displayHeight == -1) displayHeight = origDM.getHeight();
    	if (displayWidth == -1) displayWidth = origDM.getWidth();
    	if (displayColorBits == -1) displayColorBits = origDM.getBitsPerPixel();
    	if (displayFrequency == -1) displayFrequency = origDM.getFrequency();
        // Set display mode
        try {
            if (useCurrentDisplay) {  
            	// use current display settings (ignore properties file)
                DM = origDM;
            }
            else {
            	// find a display mode that matches the specified settings (or use a sane alternative)
                if ( (DM = getDisplayMode(displayWidth, displayHeight, displayColorBits, displayFrequency)) == null) {
                    if ( (DM = getDisplayMode(1024, 768, 32, 60)) == null) {
                        if ( (DM = getDisplayMode(1024, 768, 16, 60)) == null) {
                            if ( (DM = getDisplayMode(origDM.getWidth(), origDM.getHeight(), origDM.getBitsPerPixel(), origDM.getFrequency())) == null) {
                                err("GLApp.initDisplay(): Can't find a compatible Display Mode!!!");
                            }
                        }
                    }
                }
            }
            msg("GLApp.initDisplay(): Setting display mode to " + DM + " with pixel depth = " + depthBufferBits);
            Display.setDisplayMode(DM);
            displayMode = DM;
            displayWidth = DM.getWidth();
            displayHeight = DM.getHeight();
            displayColorBits = DM.getBitsPerPixel();
            displayFrequency = DM.getFrequency();
        }
        catch (Exception exception) {
            System.err.println("GLApp.initDisplay(): Failed to create display: " + exception);
            System.exit(1);  //!!!!new
        }
        // Initialize the Window
        try {
            Display.create(new PixelFormat(0, depthBufferBits, 8));  // set bits per buffer: alpha, depth, stencil
            Display.setTitle(window_title);
            Display.setFullscreen(fullScreen);
            Display.setVSyncEnabled(VSyncEnabled);
            //msg("GLApp.initDisplay(): Created OpenGL window.");
        }
        catch (Exception exception1) {
            System.err.println("GLApp.initDisplay(): Failed to create OpenGL window: " + exception1);
            System.exit(1);
        }
        // Set viewport width/height and Aspect ratio.
        if (aspectRatio == 0f) {
            // no aspect ratio was set in GLApp.cfg: default to full screen.
            aspectRatio = (float)DM.getWidth() / (float)DM.getHeight(); // calc aspect ratio of display
        }
        // viewport may not match shape of screen.  Adjust to fit.
        viewportH = DM.getHeight();                        // viewport Height
        viewportW = (int) (DM.getHeight() * aspectRatio);  // Width
        if (viewportW > DM.getWidth()) {
            viewportW = DM.getWidth();
            viewportH = (int) (DM.getWidth() * (1 / aspectRatio));
        }
        // center viewport in screen
        viewportX = (int) ((DM.getWidth()-viewportW) / 2);
        viewportY = (int) ((DM.getHeight()-viewportH) / 2);
        return true;
    }

    /**
     * Retrieve a DisplayMode object with the given params
     */
    public static DisplayMode getDisplayMode(int w, int h, int colorBits, int freq) {
         try {
             DisplayMode allDisplayModes[] = Display.getAvailableDisplayModes();
             DisplayMode dm = null;
             for (int j = 0; j < allDisplayModes.length; j++) {
                 dm = allDisplayModes[j];
                 if (dm.getWidth() == w && dm.getHeight() == h && dm.getBitsPerPixel() == colorBits &&
                     dm.getFrequency() == freq) {
                     return dm;
                 }
             }
         }
         catch (LWJGLException lwjgle) {
             err("GLApp.getDisplayMode() error:" + lwjgle);
         }
        return null;
    }

    /**
     * Initialize the Keyboard and Mouse.
     * <P>
     * Disable or hide the native cursor.  Set the initial cursor position.  Get
     * the timer resolution (ticks per second).
     *
     * @see handleEvents()
     */
    public void initInput() {
        try {
            // init keyboard
            Keyboard.create();

            // Turn off native cursor?
            if (disableNativeCursor) {
                // Mouse.setGrabbed(true) will turn off the native cursor
	            disableNativeCursor(true);
	            // set initial cursor pos to center screen
	            cursorX = (int) DM.getWidth() / 2;
	            cursorY = (int) DM.getHeight() / 2;
            }

            // Hide native cursor when inside application window?
            if (hideNativeCursor) {
            	hideNativeCursor(true);
            }

            // Init hi-res timer (see time functions)
            ticksPerSecond = Sys.getTimerResolution();
        }
        catch (Exception e) {
            err("GLApp.initInput(): " + e);
        }
    }


    //========================================================================
    // Custom Application functionality: can be overriden by subclass.
    //
    // Functions to initialize OpenGL, set the viewing mode, render the scene,
    // respond to mouse actions, and initialize the app.  These functions
    // are overridden in the subclass to create custom behavior.
    //
    //========================================================================

    /**
     * Initialize the OpenGL context.  The subclass can override this function
     * to customize the OpenGL settings.  This function is called by init()
     * once when app starts, but may be called again to restore settings to a
     * default state, or to initialize a PBuffer object to the exact same
     * state as the display.
     */
    public void initGL() {
        try {
            // Depth test setup
            GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
            GL11.glDepthFunc(GL11.GL_LEQUAL);  // The Type Of Depth Testing To Do

            // Some basic settings
            GL11.glClearColor(0f, 0f, 0f, 1f); // Black Background
            GL11.glEnable(GL11.GL_NORMALIZE);  // force normal lengths to 1
            GL11.glEnable(GL11.GL_CULL_FACE);  // don't render hidden faces
        	GL11.glEnable(GL11.GL_TEXTURE_2D); // use textures
            GL11.glEnable(GL11.GL_BLEND);      // enable transparency

            // How to handle transparency: average colors together
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);

            // Enable alpha test so the transparent backgrounds in texture images don't draw.
            // This prevents transparent areas from affecting the depth or stencil buffer.
            // alpha func will accept only fragments with alpha greater than 0
            GL11.glEnable(GL11.GL_ALPHA_TEST);
            GL11.glAlphaFunc(GL11.GL_GREATER, 0f);

            // Draw specular highlghts on top of textures
            GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR );

            // Perspective quality
            GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);

            // Set the size and shape of the screen area
            GL11.glViewport(viewportX, viewportY, viewportW, viewportH);

            // setup perspective (see setOrtho() for 2D)
            setPerspective();

            // select model view for subsequent transformations
            GL11.glMatrixMode(GL11.GL_MODELVIEW);
            GL11.glLoadIdentity();
        }
        catch (Exception e) {
            err("GLApp.initOpenGL(): " + e);
        }
    }

    /**
     *  Setup can be overridden by the subclass to initialize the application
     *  ie load textures, models, and create data structures used by the app.
     *
     *  This function is called only once, when application starts.  It is
     *  called after initDisplay and initOpenGL(), so the OpenGL context is
     *  already created.
     *
     *  @see init()
     */
    public void setup() {
    }

    /**
     *  Update can be overridden by the subclass
     *
     *  @see run()
     */
    public void update() {
    }

    /**
     * Called by run() to draw one frame.  Subclass will override this.
     * This is an alias function just to be follow Processing and OpenFrameworks
     * function naming conventions.
     */
    public void draw() {
        render();
    }

    /**
     * Same as draw().  Subclass can override render() instead of draw().
     * Same thing, just a matter of taste.
     */
    public void render() {
        // rotate 90 degrees per second
        rotation += secondsSinceLastFrame * 90f;

        // clear depth buffer and color
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

        // select model view for subsequent transforms
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glLoadIdentity();

        // set viewpoint 10 units from origin, looking at origin
        GLU.gluLookAt(0,0,10, 0,0,0, 0,1,0);

        // rotate, scale and draw cube
        GL11.glPushMatrix();
        {
        	GL11.glRotatef(rotation, 0, 1, 0);
        	GL11.glColor4f(0f, .5f, 1f, 1f);
        	renderCube();
        }
        GL11.glPopMatrix();

        // draw another overlapping cube
        GL11.glPushMatrix();
        {
        	GL11.glRotatef(rotation, 1, 1, 1);
        	GL11.glColor4f(.7f, .5f, 0f, 1f);
        	renderCube();
        }
        GL11.glPopMatrix();
    }

    /**
     * Run() calls this right before exit. Free up allocated resources (display lists)
     * and gracefully shut down OpenGL context.
     */
    public void cleanup() {
        destroyFont();
        destroyDisplayLists();
        Keyboard.destroy();
        Display.destroy();  // will call Mouse.destroy()
    }

    /**
     * Shutdown the application.  This will call cleanup() before exiting from the 
     * application. 
     * @see cleanup()
     */
    public void exit() {
        finished = true;
    }

    //========================================================================
    // Mouse events, called by handleEvents()
    //========================================================================

    /**
     * Called by handleEvents() when mouse moves
     */
    public void mouseMove(int x, int y) {
    }

    public void mouseDown(int x, int y) {
    }

    public void mouseUp(int x, int y) {
    }

    public void mouseWheel(int wheelMoved) {
    }

    /**
     * Return true if the given mousebutton is down.  Typically mouse buttons
     * are 0=left, 1=right.  This function can be called inside mouse events such
     * as mouseDown() and mouseMove() to see which button is activated.
     * @param whichButton  number of mouse button (0=left button)
     */
    public boolean mouseButtonDown(int whichButton) {
    	return Mouse.isButtonDown(whichButton);
    }

    /**
     * Called when key is pressed.  Keycode will be the key ID value as
     * defined in the LWJGL Keyboard class.
     *
     * @see Keyboard class in the LWJGL documentation
     * @param keycode
     */
    public void keyDown(int keycode) {
    }

    /**
     * Called when key is released.  Keycode will be the key ID value as
     * defined in the LWJGL Keyboard class.
     *
     * @see Keyboard class in the LWJGL documentation
     * @param keycode
     */
    public void keyUp(int keycode) {
    }

    /**
     * Return the character associatated with the last key event.  When
     * called inside keyDown() or keyUp() this function will return the
     * character equivalent of the keycode that was passed to keyDown()
     * or keyUp().
     */
    public char keyChar() {
        return Keyboard.getEventCharacter();
    }

    //========================================================================
    // functions to get values from a Properties object.  Properties can be
    // loaded from a text file containing name=value pairs.
    //========================================================================

    /**
     * Load configuration settings from a properties file.
     * File format is:<BR>
     * <PRE>
     * # Comment
     * displayWidth=1024
     * displayHeight=768
     * </PRE>
     *
     * @param configFilename
     */
    public static Properties loadPropertiesFile(String configFilename)
    {
        Properties props = new Properties();
        try { settings.load(getInputStream(configFilename)); }
        catch (Exception e) {
            msg("GLApp.loadPropertiesFile() warning: " + e);
            return null;
        }
        return props;
    }

    public static String getProperty(Properties props, String propName) {
        String s = null;
        if (propName != null && propName.length() > 0) {
            s = props.getProperty(propName);
        }
        return s;
    }

    public static int getPropertyInt(Properties props, String propName) {
        String s = getProperty(props,propName);
        int v = -1;
        if (s != null) {
            v = Integer.parseInt(s);
        }
        return v;
    }

    public static float getPropertyFloat(Properties props, String propName) {
        String s = getProperty(props,propName);
        float v = -1f;
        if (s != null) {
            v = Float.parseFloat(s);
        }
        return v;
    }

    public static boolean getPropertyBool(Properties props, String propName) {
        String s = getProperty(props,propName);
        boolean v = false;
        if (s != null) {
            v = (s.toUpperCase().equals("YES") || s.toUpperCase().equals("TRUE") || s.equals("1"));
        }
        return v;
    }

    /**
     * Return a property from the application configuration file (the filename given
     * in the configFilename variable).  This file is optional, so properties may be empty.
     * @see loadSettings()
     */
    public static String getProperty(String propertyName) {
        return settings.getProperty(propertyName);
    }

    /**
     * return the width of the Viewport (the screen area that OpenGL will draw into).
     * By default the viewport is the same size as the Display (see getWidthWindow()),
     * however the setViewport() function can set the viewport to a sub-region of the screen.
     * <P>
     * This function is only valid after app is running and Display has been initialized.
     *
     * @see setViewport(int,int,int,int)
     */
    public static int getWidth() {
    	return viewportW;
    }

    /**
     * return the height of the Viewport (the screen area that OpenGL will draw into).
     * By default the viewport is the same size as the Display (see getHeightWindow()),
     * however the setViewport() function can set the viewport to a sub-region of the screen.
     * <P>
     * This function is only valid after app is running and Display has been initialized.
     *
     * @see setViewport(int,int,int,int)
     */
    public static int getHeight() {
    	return viewportH;
    }

    /**
     * return the Display width (the width of the full window).  Only valid after app
     * is running and Display has been initialized.
     */
    public static int getWidthWindow() {
    	return displayWidth;
    }

    /**
     * return the Display height (the height of the full window).  Only valid after
     * app is running and Display has been initialized.
     */
    public static int getHeightWindow() {
    	return displayHeight;
    }

    //========================================================================
    // functions to set basic application behavior
    //========================================================================

    /**
     * Set the background color of the screen.  The red,green,blue
     * color components are floats in the range 0-1.  Black is 0,0,0
     * and white is 1,1,1.  Color will take effect the next time the
     * screen is cleared.
     */
    public static void setBackgroundColor(float R, float G, float B) {
        GL11.glClearColor(R,G,B,1);
    }

    /**
     * If the param is true, turn off the hardware cursor.  The application can
     * decide how to respond to mouse events and whether to draw a position indicator on
     * screen or not.  If running in a window (not fullscreen), there will be no hardware
     * cursor visible and the user cannot move or click outside the window area.
     * <P>
     * If the param is false, the hardware cursor will behave normally.  Use
     * hideHardwareCursor() to show or hide the hardware cursor.
     *
     * @see hideHardwareCursor()
     */
    public static void disableNativeCursor(boolean off) {
    	disableNativeCursor = off;
        Mouse.setGrabbed(off);
    }

	/**
	 *  If param is true, make the native cursor transparent.  Cursor will be hidden
	 *  in the window area, but will be visible outside the window (assuming you're not in
	 *  fullscreen mode).  I also used this approach with touch screens because the touch
	 *  screen drivers needed to read the hardware mouse position, so I
	 *  couldn't disable the hardware cursor, but I wanted to hide it.
	 *  <P>
	 *  If param is false, reset the cursor to the default.
	 *
	 *  @see disableHardwareCursor()
	 */
    public static void hideNativeCursor(boolean hide) {
    	hideNativeCursor = hide;
    	if ( (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) == 0) {
    		err("GLApp.hideHardwareCursor(): No hardwared cursor support!");
    		return;
    	}
    	try {
    		if (hide) {
    			Cursor cursor = null;
    			int cursorImageCount = 1;
    			int cursorWidth = Cursor.getMaxCursorSize();
    			int cursorHeight = cursorWidth;
    			IntBuffer cursorImages;
    			IntBuffer cursorDelays = null;
    			// Create a single cursor, completely transparent
    			cursorImages = ByteBuffer.allocateDirect(cursorWidth * cursorHeight * cursorImageCount * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
    			for (int j = 0; j < cursorWidth; j++) {
    				for (int l = 0; l < cursorHeight; l++) {
    					cursorImages.put(0x00000000);
    				}
    			}
    			cursorImages.flip();
    			cursor = new Cursor(Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize() / 2, Cursor.getMaxCursorSize() / 2, cursorImageCount, cursorImages, cursorDelays);
    			// turn it on
    			Mouse.setNativeCursor(cursor);
    		}
    		else {
    			Mouse.setNativeCursor(null);
    		}
    	}
    	catch (Exception e) {
    		err("GLApp.hideHardwareCursor(): error " + e);
    	}
    }

    /**
     * Set the cursorX,cursorY position.  This will set the screen position of the native
     * cursor also, unless hideCursor() was called.
     *
     * @param screenX
     * @param screenY
     */
    public static void setCursorPosition(int screenX, int screenY) {
       	Mouse.setCursorPosition(screenX,screenY);
       	cursorX = screenX;
       	cursorY = screenY;
    }

    //========================================================================
    // Matrix functions: get settings, get matrices, convert
    // screen to world coords.
    //========================================================================

    /**
     * retrieve a setting from OpenGL (calls glGetInteger())
     * @param whichSetting  the id number of the value to be returned (same constants as for glGetInteger())
     */
    public static int getSettingInt(int whichSetting)
    {
        tmpInts.clear();
        GL11.glGetInteger(whichSetting, tmpInts);
        return tmpInt.get(0);
    }

    public static FloatBuffer getModelviewMatrix()
    {
        bufferModelviewMatrix.clear();
        GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, bufferModelviewMatrix);
        return bufferModelviewMatrix;
    }

    public static FloatBuffer getProjectionMatrix()
    {
        bufferProjectionMatrix.clear();
        GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, bufferProjectionMatrix);
        return bufferProjectionMatrix;
    }

    public static IntBuffer getViewport()
    {
        bufferViewport.clear();
        GL11.glGetInteger(GL11.GL_VIEWPORT, bufferViewport);
        return bufferViewport;
    }

    /**
     * Convert a FloatBuffer matrix to a 4x4 float array.
     * @param fb   FloatBuffer containing 16 values of 4x4 matrix
     * @return     2 dimensional float array
     */
    public static float[][] getMatrixAsArray(FloatBuffer fb) {
        float[][] fa = new float[4][4];
        fa[0][0] = fb.get();
        fa[0][1] = fb.get();
        fa[0][2] = fb.get();
        fa[0][3] = fb.get();
        fa[1][0] = fb.get();
        fa[1][1] = fb.get();
        fa[1][2] = fb.get();
        fa[1][3] = fb.get();
        fa[2][0] = fb.get();
        fa[2][1] = fb.get();
        fa[2][2] = fb.get();
        fa[2][3] = fb.get();
        fa[3][0] = fb.get();
        fa[3][1] = fb.get();
        fa[3][2] = fb.get();
        fa[3][3] = fb.get();
        return fa;
    }

    /**
     * Return the Z depth of the single pixel at the given screen position.
     */
    public static float getZDepth(int x, int y)
    {
        tmpFloat.clear();
        GL11.glReadPixels(x, y, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, tmpFloat);
        return ( (float) (tmpFloat.getFloat(0)));
    }

    /**
     * Find the Z depth of the origin in the projected world view. Used by getWorldCoordsAtScreen()
     * Projection matrix  must be active for this to return correct results (GL.glMatrixMode(GL.GL_PROJECTION)).
     * For some reason I have to chop this to four decimals or I get bizarre
     * results when I use the value in project().
     */
    public static float getZDepthAtOrigin()
    {
        float[] resultf = new float[3];
        project( 0, 0, 0, resultf);
        return ((int)(resultf[2] * 10000F)) / 10000f;  // truncate to 4 decimals
    }

    /**
     * Return screen coordinates for a given point in world space.  The world
     * point xyz is 'projected' into screen coordinates using the current model
     * and projection matrices, and the current viewport settings.
     *
     * @param x         world coordinates
     * @param y
     * @param z
     * @param resultf    the screen coordinate as an array of 3 floats
     */
    public static void project(float x, float y, float z, float[] resultf)
    {
        // lwjgl 2.0 altered params for GLU funcs
        GLU.gluProject( x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
        resultf[0] = tmpResult.get(0);
        resultf[1] = tmpResult.get(1);
        resultf[2] = tmpResult.get(2);
    }

    /**
     * Return world coordinates for a given point on the screen.  The screen
     * point xyz is 'un-projected' back into world coordinates using the
     * current model and projection matrices, and the current viewport settings.
     *
     * @param x         screen x position
     * @param y         screen y position
     * @param z         z-buffer depth position
     * @param resultf   the world coordinate as an array of 3 floats
     * @see             getWorldCoordsAtScreen()
     */
    public static void unProject(float x, float y, float z, float[] resultf)
    {
        GLU.gluUnProject( x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
        resultf[0] = tmpResult.get(0);
        resultf[1] = tmpResult.get(1);
        resultf[2] = tmpResult.get(2);
    }

    /**
     * For given screen xy, return the world xyz coords in a float array.  Assume
     * Z position is 0.
     */
    public static float[] getWorldCoordsAtScreen(int x, int y) {
        float z = getZDepthAtOrigin();
        float[] resultf = new float[3];
        unProject( (float)x, (float)y, (float)z, resultf);
        return resultf;
    }

    /**
     * For given screen xy and z depth, return the world xyz coords in a float array.
     */
    public static float[] getWorldCoordsAtScreen(int x, int y, float z) {
        float[] resultf = new float[3];
        unProject( (float)x, (float)y, (float)z, resultf);
        return resultf;
    }

    //========================================================================
    // Texture functions
    //========================================================================

    /**
     * Allocate a texture (glGenTextures) and return the handle to it.
     */
    public static int allocateTexture()
    {
        IntBuffer textureHandle = allocInts(1);
        GL11.glGenTextures(textureHandle);
        return textureHandle.get(0);
    }

    /**
     * "Select" the given texture for further texture operations.
     */
    public static void activateTexture(int textureHandle)
    {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
    }

    /**
     * Create a texture and mipmap from the given image file.
     */
    public static int makeTexture(String textureImagePath)
    {
		int textureHandle = 0;
		GLImage textureImg = loadImage(textureImagePath);
		if (textureImg != null) {
			textureHandle = makeTexture(textureImg);
			makeTextureMipMap(textureHandle,textureImg);
		}
		return textureHandle;
    }

    /**
     * Create a texture and optional mipmap with the given parameters.
     *
     * @param mipmap: if true, create mipmaps for the texture
     * @param anisotropic: if true, enable anisotropic filtering
     */
    public static int makeTexture(String textureImagePath, boolean mipmap, boolean anisotropic)
    {
        int textureHandle = 0;
        GLImage textureImg = loadImage(textureImagePath);
        if (textureImg != null) {
            textureHandle = makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, anisotropic);
            if (mipmap) {
                makeTextureMipMap(textureHandle,textureImg);
            }
        }
        return textureHandle;
    }

    /**
     * Create a texture from the given image.
     */
    public static int makeTexture(GLImage textureImg)
    {
        if ( textureImg != null ) {
            if (isPowerOf2(textureImg.w) && isPowerOf2(textureImg.h)) {
                return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
            }
            else {
                msg("GLApp.makeTexture(GLImage) Warning: not a power of two: " + textureImg.w + "," + textureImg.h);
                textureImg.convertToPowerOf2();
                return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
            }
        }
        return 0;
    }

    /**
     * De-allocate the given texture (glDeleteTextures()).
     */
    public static void deleteTexture(int textureHandle)
    {
        IntBuffer bufferTxtr = allocInts(1).put(textureHandle);;
        GL11.glDeleteTextures(bufferTxtr);
    }

    /**
     *  Returns true if n is a power of 2.  If n is 0 return zero.
     */
    public static boolean isPowerOf2(int n) {
    	if (n == 0) { return false; }
        return (n & (n - 1)) == 0;
    }

    /**
     * Create a blank square texture with the given size.
     * @return  the texture handle
     */
    public static int makeTexture(int w)
    {
        ByteBuffer pixels = allocBytes(w*w*SIZE_INT);  // allocate 4 bytes per pixel
        return makeTexture(pixels, w, w, false);
	}

    /**
     * Create a texture from the given pixels in the default Java ARGB int format.<BR>
     * Configure the texture to repeat in both directions and use LINEAR for magnification.
     * <P>
     * @return  the texture handle
     */
    public static int makeTexture(int[] pixelsARGB, int w, int h, boolean anisotropic)
    {
    	if (pixelsARGB != null) {
    		ByteBuffer pixelsRGBA = GLImage.convertImagePixelsRGBA(pixelsARGB,w,h,true);
    		return makeTexture(pixelsRGBA, w, h, anisotropic);
    	}
    	return 0;
    }

    /**
     * Create a texture from the given pixels in the default OpenGL RGBA pixel format.
     * Configure the texture to repeat in both directions and use LINEAR for magnification.
     * <P>
     * @return  the texture handle
     */
    public static int makeTexture(ByteBuffer pixels, int w, int h, boolean anisotropic)
    {
        // get a new empty texture
        int textureHandle = allocateTexture();
        // preserve currently bound texture, so glBindTexture() below won't affect anything)
        GL11.glPushAttrib(GL11.GL_TEXTURE_BIT);
        // 'select' the new texture by it's handle
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // set texture parameters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);

        // make texture "anisotropic" so it will minify more gracefully
    	if (anisotropic && extensionExists("GL_EXT_texture_filter_anisotropic")) {
    		// Due to LWJGL buffer check, you can't use smaller sized buffers (min_size = 16 for glGetFloat()).
    		FloatBuffer max_a = allocFloats(16);
    		// Grab the maximum anisotropic filter.
    		GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_a);
    		// Set up the anisotropic filter.
    		GL11.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, max_a.get(0));
    	}

        // Create the texture from pixels
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
        		0, 						// level of detail
        		GL11.GL_RGBA8,			// internal format for texture is RGB with Alpha
        		w, h, 					// size of texture image
        		0,						// no border
        		GL11.GL_RGBA, 			// incoming pixel format: 4 bytes in RGBA order
        		GL11.GL_UNSIGNED_BYTE,	// incoming pixel data type: unsigned bytes
        		pixels);				// incoming pixels

        // restore previous texture settings
        GL11.glPopAttrib();

        return textureHandle;
    }

    /**
     * Create a texture from the given pixels in ARGB format.  The pixels buffer contains
     * 4 bytes per pixel, in ARGB order.  ByteBuffers are created with native hardware byte
     * orders, so the pixels can be in big-endian (ARGB) order, or little-endian (BGRA) order.
     * Set the pixel_byte_order accordingly when creating the texture.
     * <P>
     * Configure the texture to repeat in both directions and use LINEAR for magnification.
     * <P>
     * NOTE: I'm having problems creating mipmaps when image pixel data is in GL_BGRA format.
     * Looks like GLU type param doesn't recognize GL_UNSIGNED_INT_8_8_8_8 and
     * GL_UNSIGNED_INT_8_8_8_8_REV so I can't specify endian byte order.  Mipmaps look
     * right on PC but colors are reversed on Mac.  Have to stick with GL_RGBA
     * byte order for now.
     * <P>
     * @return  the texture handle
     */
    public static int makeTextureARGB(ByteBuffer pixels, int w, int h)
    {
    	// byte buffer has ARGB ints in little endian or big endian byte order
		int pixel_byte_order = (pixels.order() == ByteOrder.BIG_ENDIAN)?
				GL12.GL_UNSIGNED_INT_8_8_8_8 :
				GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
        // get a new empty texture
        int textureHandle = allocateTexture();
        // 'select' the new texture by it's handle
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // set texture parameters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
        // Create the texture from pixels
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
        		0, 						// level of detail
        		GL11.GL_RGBA8,			// internal format for texture is RGB with Alpha
        		w, h, 					// size of texture image
        		0,						// no border
        		GL12.GL_BGRA, 			// incoming pixel format: 4 bytes in ARGB order
        		pixel_byte_order,		// incoming pixel data type: little or big endian ints
        		pixels);				// incoming pixels
        return textureHandle;
    }

    /**
     * Build Mipmaps for currently bound texture (builds a set of textures at various
     * levels of detail so that texture will scale up and down gracefully)
     *
     * @param textureImg  the texture image
     * @return   error code of buildMipMap call
     */
    public static int makeTextureMipMap(int textureHandle, GLImage textureImg)
    {
    	int ret = 0;
    	if (textureImg != null && textureImg.isLoaded()) {
    		GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
    		ret = GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA8,
    				textureImg.w, textureImg.h,
    				GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, textureImg.getPixelBytes());
    		if (ret != 0) {
    			err("GLApp.makeTextureMipMap(): Error occured while building mip map, ret=" + ret + " error=" + GLU.gluErrorString(ret) );
    		}
    		// Assign the mip map levels and texture info
    		GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
    		GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
    	}
        return ret;
    }

    /**
     * Create a texture large enough to hold the screen image.  Use RGBA8 format
     * to insure colors are copied exactly.  Use GL_NEAREST for magnification
     * to prevent slight blurring of image when screen is drawn back.
     *
     * @see frameCopy()
     * @see frameDraw()
     */
    public static int makeTextureForScreen(int screenSize)
    {
        // get a texture size big enough to hold screen (512, 1024, 2048 etc.)
        screenTextureSize = getPowerOfTwoBiggerThan(screenSize);
        msg("GLApp.makeTextureForScreen(): made texture for screen with size " + screenTextureSize);
        // get a new empty texture
        int textureHandle = allocateTexture();
        ByteBuffer pixels = allocBytes(screenTextureSize*screenTextureSize*SIZE_INT);
        // 'select' the new texture by it's handle
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // set texture parameters
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
        // use GL_NEAREST to prevent blurring during frequent screen restores
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
        GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
        // Create the texture from pixels: use GL_RGBA8 to insure exact color copy
        GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, screenTextureSize, screenTextureSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
        return textureHandle;
    }

    /**
     * Find a power of two equal to or greater than the given value.
     * Ie. getPowerOfTwoBiggerThan(800) will return 1024.
     * <P>
     * @see makeTextureForScreen()
     * @param dimension
     * @return a power of two equal to or bigger than the given dimension
     */
    public static int getPowerOfTwoBiggerThan(int n) {
        if (n < 0)
            return 0;
        --n;
        n |= n >> 1;
        n |= n >> 2;
        n |= n >> 4;
        n |= n >> 8;
        n |= n >> 16;
        return n+1;
    }

	/**
	 * Copy pixels from a ByteBuffer to a texture.  The buffer pixels are integers in ARGB format
	 * (this is the Java default format you get from a BufferedImage) or BGRA format (this is the
	 * native order of Intel systems.
	 *
	 * The glTexSubImage2D() call treats the incoming pixels as integers
	 * in either big-endian (ARGB) or little-endian (BGRA) formats based on the setting
	 * of the bytebuffer (pixel_byte_order).
	 *
	 * @param bb  ByteBuffer of pixels stored as ARGB or BGRA integers
	 * @param w   width of source image
	 * @param h   height of source image
	 * @param textureHandle  texture to copy pixels into
	 */
	public static void copyPixelsToTexture(ByteBuffer bb, int w, int h, int textureHandle) {
		int pixel_byte_order = (bb.order() == ByteOrder.BIG_ENDIAN)?
										GL12.GL_UNSIGNED_INT_8_8_8_8 :
										GL12.GL_UNSIGNED_INT_8_8_8_8_REV;

		// "select" the texture that we'll write into
    	GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);

		// Copy pixels to texture
    	GL11.glTexSubImage2D(
    			GL11.GL_TEXTURE_2D,   		// always GL_TEXTURE_2D
    			0,                  		// texture detail level: always 0
    			0, 0,                		// x,y offset into texture
    			w, h,             			// dimensions of image pixel data
    			GL12.GL_BGRA,   			// format of pixels in texture (little_endian - native for PC)
    			pixel_byte_order,    		// format of pixels in bytebuffer (big or little endian ARGB integers)
    			bb           				// image pixel data
    	);
	}

	/**
	 * Calls glTexSubImage2D() to copy pixels from an image into a texture.
	 */
	public static void copyImageToTexture(GLImage img, int textureHandle) {
		copyPixelsToTexture(img.pixelBuffer, img.w, img.h, textureHandle);
	}

    //========================================================================
    // functions to set projection
    //========================================================================

    /**
     * Set OpenGL to render in 3D perspective.  Set the projection matrix using
     * GLU.gluPerspective().  The projection matrix controls how the scene is
     * "projected" onto the screen.  Think of it as the lens on a camera, which
     * defines how wide the field of vision is, how deep the scene is, and how
     * what the aspect ratio will be.
     */
    public static void setPerspective()
    {
        // select projection matrix (controls perspective)
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        GLU.gluPerspective(40f, aspectRatio, 1f, 1000f);
        // return to modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }

    /**
     * Set OpenGL to render in flat 2D (no perspective) with a one-to-one
     * relation between screen pixels and world coordinates, ie. if
     * you draw a 10x10 quad at 100,100 it will appear as a 10x10 pixel
     * square on screen at pixel position 100,100.
     * <P>
     * <B>ABOUT Ortho and Viewport:</B><br>
     * Let's say we're drawing in 2D and want to have a cinema proportioned
     * viewport (16x9), and want to bound our 2D rendering into that area ie.
     * <PRE>
          ___________1024,576
         |           |
         |  Scene    |      Set the bounds on the scene geometry
         |___________|      to the viewport size and shape
      0,0

          ___________1024,576
         |           |
         |  Ortho    |      Set the projection to cover the same
         |___________|      area as the scene
      0,0

          ___________ 1024,768
         |___________|
         |           |1024,672
         |  Viewport |      Set the viewport to the same shape
     0,96|___________|      as scene and ortho, but centered on
         |___________|      screen.
      0,0
     *</PRE>
     */
    public static void setOrtho()
    {
        // select projection matrix (controls view on screen)
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        // set ortho to same size as viewport, positioned at 0,0
        GL11.glOrtho(
        		0,viewportW,  // left,right
        		0,viewportH,  // bottom,top
        		-500,500);    // Zfar, Znear
        // return to modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }

    public static void setOrtho(int width, int height)
    {
        // select projection matrix (controls view on screen)
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glLoadIdentity();
        // set ortho to same size as viewport, positioned at 0,0
        GL11.glOrtho(
        		0,width,  // left,right
        		0,height,  // bottom,top
        		-500,500);    // Zfar, Znear
        // return to modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
    }

    /**
     * Set OpenGL to render in flat 2D (no perspective) on top of current scene.
     * Preserve current projection and model views, and disable depth testing.
     * Ortho world size will be same as viewport size, so any ortho drawing
     * (drawQuad(), drawImageFullscreen(), etc.) will be scaled to fit viewport.
     * <P>
     * NOTE: if the viewport is the same size as the window (by default it is),
     * then setOrtho() will make the world coordinates exactly match the screen
     * pixel positions.  This is convenient for mouse interaction, but be warned:
     * if you setViewport() to something other than fullscreen, then you need
     * to use getWorldCoordsAtScreen() to convert screen xy to world xy.
     * <P>
     * Once Ortho is on, glTranslate() will take pixel coords as arguments,
     * with the lower left corner 0,0 and the upper right corner 1024,768 (or
     * whatever your screen size is).  !!!
     *
     * @see setOrthoOff()
     * @see setViewport(int,int,int,int)
     */
    public static void setOrthoOn()
    {
		// prepare projection matrix to render in 2D
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPushMatrix();                   // preserve perspective view
        GL11.glLoadIdentity();                 // clear the perspective matrix
        GL11.glOrtho(                          // turn on 2D mode
        		////viewportX,viewportX+viewportW,    // left, right
        		////viewportY,viewportY+viewportH,    // bottom, top    !!!
        		0,viewportW,    // left, right
        		0,viewportH,    // bottom, top
        		-500,500);                        // Zfar, Znear
        // clear the modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();				   // Preserve the Modelview Matrix
        GL11.glLoadIdentity();				   // clear the Modelview Matrix
		// disable depth test so further drawing will go over the current scene
		GL11.glDisable(GL11.GL_DEPTH_TEST);
    }

    /**
     * Set the width and height of the Ortho scene.  By default GLApp will use
     * viewportWidth and viewportHeight for the width and height of ortho mode.
     * You can override this behavior by setting a specific size with setOrthoDimensions().
     * <P>
     * By default ortho mode (setOrtho(), setOrthoOn()) will set the world width
     * and height to exactly match the screen pixel width and height (it uses
     * the viewport size, which is typically the exact same size as the opengl window).
     * This makes it easy to map screen positions to the world space.  Text positions,
     * images and mouse coordinates map pixel-for-pixel to the screen.
     * <P>
     * The drawback is that the ortho world space will change size on different
     * resolution screens. In low-res screens the ortho world may be 800x600 while
     * in a hi-res screen it could be 1920x1200, which means that anything drawn
     * in ortho mode may shift on different screens.
     * <P>
     * setOrthoDimensions() assigns a fixed size for ortho scenes, independent of
     * the viewport size.  OpenGL will project the ortho world into the
     * viewport just as it does with a perspective projection.  Things drawn in
     * ortho will stay in the right place on any resolution screen.
     * <P>
     * The drawback with this method is that screen coordinates don't necessarily
     * map exactly to the ortho world.  To translate a screen xy to the ortho
     * world coordinates, use getWorldCoordsAtScreen(x,y) just as with perspective
     * worlds.
     *
     * @param width    width of ortho scene
     * @param height   height of ortho scene
     *
     * @see setOrtho()
     * @see setOrthoOn()
     * @see setPerspective()
     *//*
    public static void setOrthoDimensions(int width, int height)
    {
    	orthoWidth = width;
    	orthoHeight = height;
    }
    */

    /**
     * Turn 2D mode off.  Return the projection and model views to their
     * preserved state that was saved when setOrthoOn() was called, and
     * re-enable depth testing.
     *
     * @see setOrthoOn()
     */
    public static void setOrthoOff()
    {
        // restore the original positions and views
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPopMatrix();
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPopMatrix();
        // turn Depth Testing back on
        GL11.glEnable(GL11.GL_DEPTH_TEST);
    }

    /**
     * Define the position and size of the screen area in which the
     * OpenGL context will draw. Position and size of the area are given
     * in exact pixel sizes.  By default the viewport is the same size as
     * the window (displayWidth,displayHeight).
     * <P>
     * NOTE: by default the window size, viewport size and setOrtho() size are all
     * the same, so in ortho mode screen pixel positions exactly match to world
     * coordinates.  THIS IS NO LONGER TRUE if you setViewport() to some other
     * size.  With a custom viewport you need to use getWorldCoordsAtScreen()
     * to convert screen xy to world xy.
     * <P>
     * @param x       position of the lower left of viewport area, in pixels
     * @param y
     * @param width   size of the viewport area, in pixels
     * @param height
     *
     * @see setPerspective()
     * @see setOrtho()
     * @see setOrthoDimensions(int,int)
     */
    public static void setViewport(int x, int y, int width, int height)
    {
    	viewportX = x;
    	viewportY = y;
    	viewportW = width;
    	viewportH = height;
     	aspectRatio = (float)width / (float)height;
       	GL11.glViewport(x,y,width,height);
   }

    /**
     * Reset the viewport to full screen (displayWidth x displayHeight).
     *
     * @see setViewport(int,int,int,int)
     */
    public static void resetViewport()
    {
    	setViewport(0,0,displayWidth,displayHeight);
    }

    /**
     * A simple way to set eye position.  Calls gluLookat() to place
     * the viewpoint <distance> units up the Z axis from the given target position,
     * looking at the target position. The camera is oriented vertically (Y axis is up).
     * away.
     */
    public static void lookAt(float lookatX, float lookatY, float lookatZ, float distance)
    {
        // set viewpoint
        GLU.gluLookAt(
        		lookatX,lookatY,lookatZ+distance,  // eye is at the same XY as the target, <distance> units up the Z axis
        		lookatX,lookatY,lookatZ,           // look at the target position
        		0,1,0);                            // the Y axis is up
    }

    //========================================================================
    // Functions to push/pop OpenGL settings
    //========================================================================

	/**
	 * preserve all OpenGL settings that can be preserved.  Use this
	 * function to isolate settings changes.  Call pushAttrib() before
	 * calling glEnable(), glDisable(), glMatrixMode() etc. After
	 * your code executes, call popAttrib() to return to the
	 * previous settings.
	 *
	 * For better performance, call pushAttrib() with specific settings
	 * flags to preserve only specific settings.
	 *
	 * @see popAttrib()
	 */
	public static void pushAttrib()
	{
        GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
	}

	/**
	 * preserve the specified OpenGL setting.  Call popAttrib() to return to
	 * the preserved state.
	 *
	 * @see popAttrib()
	 */
	public static void pushAttrib(int attribute_bits)
	{
        GL11.glPushAttrib(attribute_bits);
	}

	/**
	 * preserve the OpenGL settings that will be affected when we draw in ortho
	 * mode over the scene.  For example if we're drawing an interface layer,
	 * buttons, popup menus, cursor, text, etc. we need to turn off lighting,
	 * turn on blending, set color to white and turn off depth test.
	 * <P>
	 * call pushAttribOverlay(), enable settings that you need, when done call popAttrib()
	 *
	 * @see popAttrib()
	 */
	public static void pushAttribOrtho()
	{
        GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_DEPTH_BUFFER_BIT);
	}

	/**
	 * preserve the OpenGL viewport settings.
	 * <pre>
	 *       pushAttribViewport();
	 *           setViewport(0,0,displaymode.getWidth(),displaymode.getHeight());
	 *           ... do some drawing outside of previous viewport area
	 *       popAttrib();
	 * </pre>
	 *
	 * @see popAttrib()
	 */
	public static void pushAttribViewport()
	{
        GL11.glPushAttrib(GL11.GL_VIEWPORT_BIT);
	}

	/**
	 * return to the OpenGL settings that were preserved by the previous pushAttrib() call.
	 *
	 * @see pushAttrib()
	 */
	public static void popAttrib()
	{
        GL11.glPopAttrib();
	}

    //========================================================================
    // Lighting functions
    //========================================================================

    /**
     * Set the color of a 'positional' light (a light that has a specific
     * position within the scene).  <BR>
     *
     * Pass in an OpenGL light number (GL11.GL_LIGHT1),
     * the 'Diffuse' and 'Ambient' colors (direct light and reflected light),
     * and the position.<BR>
     *
     * @param GLLightHandle
     * @param diffuseLightColor
     * @param ambientLightColor
     * @param position
     */
    public static void setLight( int GLLightHandle,
        float[] diffuseLightColor, float[] ambientLightColor, float[] specularLightColor, float[] position )
    {
        FloatBuffer ltDiffuse = allocFloats(diffuseLightColor);
        FloatBuffer ltAmbient = allocFloats(ambientLightColor);
        FloatBuffer ltSpecular = allocFloats(specularLightColor);
        FloatBuffer ltPosition = allocFloats(position);
        GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse);   // color of the direct illumination
        GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight
        GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient);   // color of the reflected light
        GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition);
        GL11.glEnable(GLLightHandle);	// Enable the light (GL_LIGHT1 - 7)
        //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F);    // how light beam drops off
    }


    public static void setSpotLight( int GLLightHandle,
        float[] diffuseLightColor, float[] ambientLightColor,
        float[] position, float[] direction, float cutoffAngle )
    {
        FloatBuffer ltDirection = allocFloats(direction);
        setLight(GLLightHandle, diffuseLightColor, ambientLightColor, diffuseLightColor, position);
        GL11.glLightf(GLLightHandle, GL11.GL_SPOT_CUTOFF, cutoffAngle);   // width of the beam
        GL11.glLight(GLLightHandle, GL11.GL_SPOT_DIRECTION, ltDirection);    // which way it points
        GL11.glLightf(GLLightHandle, GL11.GL_CONSTANT_ATTENUATION, 2F);    // how light beam drops off
        //GL11.glLightf(GLLightHandle, GL11.GL_LINEAR_ATTENUATION, .5F);    // how light beam drops off
        //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .5F);    // how light beam drops off
    }

    /**
     * Set the color of the Global Ambient Light.  Affects all objects in
     * scene regardless of their placement.
     */
    public static void setAmbientLight(float[] ambientLightColor)
    {
        put(tmpFloats,ambientLightColor);
        GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, tmpFloats);
    }

    /**
     * Set the position of a light to the given xyz. NOTE: Positional light only,
     * not directional.
     */
    public static void setLightPosition(int GLLightHandle, float x, float y, float z)
    {
    	put(tmpFloats, new float[] {x,y,z,1});
        GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
    }

    /**
     * Set the position (or direction) of a light to the given xyz.
     */
    public static void setLightPosition(int GLLightHandle, float[] position)
    {
        put(tmpFloats,position);
        GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
    }

    /**
     * enable/disable the given light.  The light handle parameter is one of
     * the predefined OpenGL light handle numbers (GL_LIGHT1, GL_LIGHT2 ... GL_LIGHT7).
     */
    public static void setLight(int GLLightHandle, boolean on)
    {
		if (on) {
			GL11.glEnable(GLLightHandle);
		}
		else {
			GL11.glDisable(GLLightHandle);
		}
    }

	/**
	 * Enable/disable lighting.  If parameter value is false, this will turn off all
	 * lights and ambient lighting.
	 *
	 * NOTE: When lighting is disabled, material colors are disabled as well.  Use
	 *       glColor() to set color properties when ligthing is off.
	 */
	public static void setLighting(boolean on) {
		if (on) {
			GL11.glEnable(GL11.GL_LIGHTING);
		}
		else {
			GL11.glDisable(GL11.GL_LIGHTING);
		}
	}

    //========================================================================
    // Material functions
    //========================================================================
    public static final float[] colorClear   = {  0f,  0f,  0f,  0f}; // alpha is 0
    public static final float[] colorBlack   = {  0f,  0f,  0f,  1f};
    public static final float[] colorWhite   = {  1f,  1f,  1f,  1f};
    public static final float[] colorGray    = { .5f, .5f, .5f,  1f};
    public static final float[] colorRed     = {  1f,  0f,  0f,  1f};
    public static final float[] colorGreen   = {  0f,  1f,  0f,  1f};
    public static final float[] colorBlue    = {  0f,  0f,  1f,  1f};

	/**
	*  A simple way to set the current material properties to approximate a
	*  "real" surface.  Provide the surface color (float[4]]) and shininess
	*  value (range 0-1).
	*  <P>
	*  Sets diffuse material color to the surfaceColor and ambient material color
	*  to surfaceColor/2.  Based on the shiny value (0-1), sets the specular
	*  property to a color between black (0) and white (1), and sets the
	*  shininess property to a value between 0 and 127.
	*  <P>
	*  Lighting must be enabled for material colors to take effect.
	*  <P>
	*  @param surfaceColor - must be float[4] {R,G,B,A}
	*  @param reflection - a float from 0-1 (0=very matte, 1=very shiny)
	*/
    public static void setMaterial(float[] surfaceColor, float shiny) {
        float[] reflect = {shiny,shiny,shiny,1}; // make a shade of gray
        float[] ambient = {surfaceColor[0]*.5f,surfaceColor[1]*.5f,surfaceColor[2]*.5f,1};  // darker surface color
        mtldiffuse.put(surfaceColor).flip();     // surface directly lit
        mtlambient.put(ambient).flip();          // surface in shadow
        mtlspecular.put(reflect).flip();         // reflected light
        mtlemissive.put(colorBlack).flip();      // no emissive light
        // size of reflection
        int openglShininess = ((int)(shiny*127f));   // convert 0-1 to 0-127
        if (openglShininess >= 0 && openglShininess <= 127) {
        	mtlshininess.put(new float[] {openglShininess,0,0,0}).flip();
        }
        applyMaterial();
    }

	/**
	*  Set the four material colors and calls glMaterial() to change the current
	*  material color in OpenGL.  Lighting must be enabled for material colors to take effect.
	*
	*  @param shininess: size of reflection (0 is matte, 127 is pinpoint reflection)
	*/
    public static void setMaterial(float[] diffuseColor, float[] ambientColor, float[] specularColor, float[] emissiveColor, float shininess) {
    	mtldiffuse.put(diffuseColor).flip();     // surface directly lit
    	mtlambient.put(ambientColor).flip();     // surface in shadow
    	mtlspecular.put(specularColor).flip();   // reflection color
    	mtlemissive.put(emissiveColor).flip();   // glow color
        if (shininess >= 0 && shininess <= 127) {
        	mtlshininess.put(new float[] {shininess,0,0,0}).flip();  // size of reflection 0=broad 127=pinpoint
        }
        applyMaterial();
    }

    /**
     * Alter the material opacity by setting the diffuse material color
     * alpha value to the given value
     * @para alpha 0=transparent 1=opaque
     */
    public static void setMaterialAlpha(float alpha) {
        if (alpha < 0) alpha = 0;
        if (alpha > 1) alpha = 1;
    	mtldiffuse.put(3,alpha).flip();     // alpha value of diffuse color
    	applyMaterial();
    }

    /**
     *  Call glMaterial() to activate these material properties in the OpenGL environment.
     *  These properties will stay in effect until you change them or disable lighting.
     */
    public static void applyMaterial() {
        GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, mtldiffuse);
        GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, mtlambient);
        GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, mtlspecular);
        GL11.glMaterial(GL11.GL_FRONT, GL11.GL_EMISSION, mtlemissive);
        GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, mtlshininess);
    }


    //========================================================================
    // Fog
    //========================================================================

	/**
	 * Enable atmospheric fog effect, with the given color and density.
	 * <PRE>
	 *      setFog(new float[] {.5f,.5f,.5f,1f}, .3f);
	 * </PRE>
	 *
	 * @param fogColor   float[4] specifies the RGB fog color value
	 * @param fogDensity  float in range 0-1 specifies how opaque the fog will be
	 */
	public static void setFog(float[] fogColor, float fogdensity) {
		put(tmpFloats,fogColor);
		// turn fog on
		GL11.glEnable(GL11.GL_FOG);
		// mode: GL_EXP2 is dense fog, GL_EXP is thinner, GL_LINEAR is very thin
		GL11.glFogi(GL11.GL_FOG_MODE, GL11.GL_EXP2);
		// start and end (only apply when fog mode=GL_LINEAR
		//GL11.glFogf(GL11.GL_FOG_START, 100f);
		//GL11.glFogf(GL11.GL_FOG_END, 1000f);
		// color
		GL11.glFog(GL11.GL_FOG_COLOR, tmpFloats);
		// density
		GL11.glFogf(GL11.GL_FOG_DENSITY, fogdensity);
		// quality
		GL11.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST);
	}


	/**
	 * Enable/disable fog effect.  Does not change the fog settings.
	 */
	public static void setFog(boolean on) {
		if (on) {
			GL11.glEnable(GL11.GL_FOG);
		}
		else {
			GL11.glDisable(GL11.GL_FOG);
		}
	}

    //========================================================================
    // Time functions
    //========================================================================

    public static double getTimeInSeconds()
    {
        if (ticksPerSecond == 0) {
            ticksPerSecond = Sys.getTimerResolution();
        }
        return (((double)Sys.getTime())/(double)ticksPerSecond);
    }

    public static double getTimeInMillis()
    {
        if (ticksPerSecond == 0) {
            ticksPerSecond = Sys.getTimerResolution();
        }
        return (double) ((((double)Sys.getTime())/(double)ticksPerSecond) * 1000.0);
    }

    /**
     *  Calculate time since we last called updateTimer().  Updates secondsSinceLastFrame and
     *  sets lastFrameTime to the current Sys.getTime().
     *  <P>
     *  Called by run() at the beginning of each loop.
     *
     *  @see run()
     *  @see getSecondsPerFrame()
     */
    public static void updateTimer()
    {
    	// number of frames to average (about one second)
    	double numToAvg = 50;
    	// calc time elapsed since we last rendered
    	secondsSinceLastFrame = (double)(Sys.getTime() - lastFrameTime) / (double)ticksPerSecond;
    	lastFrameTime = Sys.getTime();
    	// keep a moving average of frame elapsed times
    	if (secondsSinceLastFrame < 1) {
    		avgSecsPerFrame = (double) ((avgSecsPerFrame*numToAvg)+secondsSinceLastFrame) / (numToAvg+1D);
    	}
    }

	/**
     * Return the moving average of the seconds per frame for the last 50 frames.
     * Useful when animating in real time.  Will provide smoother time deltas
     * than the secondsSinceLastFrame variable, which holds the exact time elapsed
     * during the last frame (but may jump or lag as processor load varies).
     *
     * @see updateTimer()
     */
	public static double getSecondsPerFrame() {
    	return avgSecsPerFrame;
    }

	/**
     * Return the moving average of the frames per second for the last 50 frames.
     *
     * @see updateTimer()
     */
	public static double getFramesPerSecond() {
    	return 1d/avgSecsPerFrame;
    }

    //========================================================================
    // Load images
    //========================================================================

    /**
     * Make a blank image of the given size.
     * @return  the new GLImage
     */
    public static GLImage makeImage(int w, int h) {
        ByteBuffer pixels = allocBytes(w*h*SIZE_INT);
        return new GLImage(pixels,w,h);
    }

    /**
     * Load an image from the given file and return a GLImage object.
     * @param image filename
     * @return the loaded GLImage
     */
    public static GLImage loadImage(String imgFilename) {
        GLImage img = new GLImage(imgFilename);
        if (img.isLoaded()) {
            return img;
        }
        return null;
    }

    /**
     * Load an image from the given file and return a ByteBuffer containing ARGB pixels.<BR>
     * Can be used to create textures. <BR>
     * @param imgFilename
     * @return
     */
    public static ByteBuffer loadImagePixels(String imgFilename) {
        GLImage img = new GLImage(imgFilename);
        return img.pixelBuffer;
    }

    /**
     * Draw a cursor image textured onto a quad at cursorX,cursorY.  The cursor
     * image must be loaded into a 32x32 texture. This function can be called
     * after scene is drawn to place a cursor on top of scene.
     * <P>
     * NOTE: the cursor is drawn in screen space, at an absolute screen pixel location
     * without regard for viewport (temporarily zets viewport to entire screen).
     * <P>
     * See handleEvents() for cursorX cursorY and mouse motion handling.
     * <P>
     * Example:
     * <PRE>
     *    int cursorTxtr;
     *
     *    public void setup() {
     *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
     *    }
     *
     *    public void draw() {
     *        // render scene
     *        ...
     *        drawCursor(cursorTxtr);
     *    }
     * </PRE>
     *
     * @param cursorTextureHandle  handle to texture containing 32x32 cursor image
     */
    public static void drawCursor(int cursorTextureHandle) {
		// set projection matrix to 2D fullscreen
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPushMatrix();                   // preserve perspective view
        GL11.glLoadIdentity();                 // clear the perspective matrix
        GL11.glOrtho(                          // set ortho to exactly match screen size
        		0,displayWidth,     // left, right
        		0,displayHeight,    // bottom, top
        		-1,1);              // Zfar, Znear
        // clear the modelview matrix
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPushMatrix();				   // preserve current modelview matrix
        GL11.glLoadIdentity();				   // clear the modelview matrix

        // preserve current settings then draw cursor
        GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_VIEWPORT_BIT);
        {
        	// set viewport to full screen
        	GL11.glViewport(0,0,displayWidth,displayHeight);
    	    // tweak settings
            GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
            GL11.glColor4f(1,1,1,1);             // no color
            GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
            GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
            GL11.glEnable(GL11.GL_BLEND);        // enable transparency
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
            // draw 32x32 cursor image
            drawQuadZ(cursorTextureHandle, cursorX-15, cursorY-15, 0, 32, 32);
        }
        GL11.glPopAttrib();

        // restore the previous matrix settings
        GL11.glMatrixMode(GL11.GL_PROJECTION);
        GL11.glPopMatrix();
        GL11.glMatrixMode(GL11.GL_MODELVIEW);
        GL11.glPopMatrix();
    }

    /**
     * OLD function: this drew the cursor only into the current viewport, and
     * could not handle difffent ortho coordinate systems (ortho had to be
     * exactly mapped to screen size).
     *
     * Draw a cursor image textured onto a quad at cursor position.  The cursor
     * image must be loaded into a texture, then this function can be called
     * after scene is drawn.  Uses glPushAttrib() to preserve the current
     * drawing state.  glPushAttrib() may slow performance down, so in your
     * app you may want to set the states yourself before calling drawCursor()
     * and take the push/pop out of here.
     * <P>
     * See handleEvents() for cursorX cursorY and mouse motion handling.
     * <P>
     * Example:
     * <PRE>
     *    int cursorTxtr;
     *
     *    public void setup() {
     *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
     *    }
     *
     *    public void draw() {
     *        // render scene
     *        ...
     *        drawCursor(cursorTxtr);
     *    }
     * </PRE>
     *
     * @param cursorTextureHandle  handle to texture containing 32x32 cursor image
     */
    public static void drawCursorOLD(int cursorTextureHandle) {
        setOrthoOn();
        GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
        {
    	    // tweak settings
            GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
            GL11.glColor4f(1,1,1,1);             // no color
            GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
            GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
            GL11.glEnable(GL11.GL_BLEND);        // enable transparency
            GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
            drawQuad(cursorTextureHandle, cursorX-15, cursorY-15, 32, 32);  // assumes 32x32 pixels
        }
        GL11.glPopAttrib();
        setOrthoOff();
    }

    /**
     * Draw an image in ortho mode (2D) over the entire viewport area.
     * Converts the image to a texture and maps onto a viewport-sized quad.
     * Depth test is turned off, lighting is off, color is set to white.
     * Alpha blending is on, so transparent areas will be respected.
     * <P>
     * NOTE: By default the viewport is the same size as the window so this function
     * will draw the image over the entire window.  If you setViewport() to a
     * custom size the image will be drawn into the custom viewport area. To
     * insure that the image is drawn truly full screen, call resetViewport()
     * before drawImageFullScreen().
     * <P>
     * @see loadImage(String)
     * @see setViewport(int,int,int,int)
     * @see resetViewport()
     */
    public static void drawImageFullScreen(GLImage img) {
    	if (img == null || img.isLoaded() == false) {
    		return;
    	}
    	// if image has no texture, convert the image to a texture
    	if (img.textureHandle <= 0) {
    		img.textureHandle = makeTexture(img);
    	}
    	// Calculate the UV dimensions of the image in the texture
    	float maxU = (float)img.w / (float)img.textureW;
    	float maxV = (float)img.h / (float)img.textureH;
    	// preserve settings
    	pushAttribOrtho();
    	// switch to 2D projection
    	setOrthoOn();
    	// tweak settings
    	GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
    	GL11.glColor4f(1,1,1,1);             // no color
    	GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
    	GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
    	GL11.glEnable(GL11.GL_BLEND);        // enable transparency
    	GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
    	// activate the image texture
    	GL11.glBindTexture(GL11.GL_TEXTURE_2D,img.textureHandle);
    	// draw a textured quad
    	GL11.glBegin(GL11.GL_QUADS);
    	{
    		GL11.glTexCoord2f(0, 0);
    		GL11.glVertex3f(0f, 0f, 0f);         // Bottom Left

    		GL11.glTexCoord2f(maxU, 0);
    		GL11.glVertex3f(getWidth(), 0f, 0f);         // Bottom Right

    		GL11.glTexCoord2f(maxU, maxV);
    		GL11.glVertex3f(getWidth(), getHeight(), 0f);   // Top Right

    		GL11.glTexCoord2f(0, maxV);
    		GL11.glVertex3f(0f, getHeight(), 0f);       // Top left
    	}
    	GL11.glEnd();
    	// return to previous projection mode
    	setOrthoOff();
    	// return to previous settings
    	popAttrib();
    }

    /**
     * Draw an image in whichever projection mode is current (does not switch to ortho mode).
     * Convert the image to a texture and draw onto quad.  Will draw with current settings
     * (light, material, depth, blend, etc.)
     * <BR>
     * @see loadImage()
     * @see drawQuad()
     * @see drawImageFullScreen()
     */
    public static void drawImage(GLImage img, int x, int y, float w, float h) {
    	// if image has no texture, convert the image to a texture
    	if (img.textureHandle <= 0) {
    		img.textureHandle = makeTexture(img);
    	}
    	// preserve settings
    	pushAttribOrtho();
    	// set color to white
    	//GL11.glColor4f(1,1,1,1);   // don't force color to white (may want to tint image)
        // activate the image texture
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,img.textureHandle);
        // draw a textured quad
        GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
        GL11.glBegin(GL11.GL_QUADS);
        {
	        GL11.glTexCoord2f(0f, 0f);
	        GL11.glVertex3f( (float)x, (float)y, (float)0);
	        GL11.glTexCoord2f(1f, 0f);
	        GL11.glVertex3f( (float)x+w, (float)y, (float)0);
	        GL11.glTexCoord2f(1f, 1f);
	        GL11.glVertex3f( (float)x+w, (float)y+h, (float)0);
	        GL11.glTexCoord2f(0f, 1f);
	        GL11.glVertex3f( (float)x, (float)y+h, (float)0);
        }
        GL11.glEnd();
        // return to previous settings
        popAttrib();
    }

    /**
     * Draw a textured quad in Ortho mode (2D) at the given xy, scaled to
     * the given width and height.  Depth test is turned off so quad will
     * be drawn on top of the current scene.  Quad will be drawn
     * with current light and material if any are active.
     * <BR>
     * @ee loadImage()
     */
    public static void drawQuad(int textureHandle, int x, int y, float w, float h) {
        // activate the specified texture
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // prepare to render in 2D
        setOrthoOn();
        // draw the textured quad
        GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
        GL11.glBegin(GL11.GL_QUADS);
        {
            GL11.glTexCoord2f(0f, 0f);
            GL11.glVertex3f( (float)x, (float)y, (float)0);
            GL11.glTexCoord2f(1f, 0f);
            GL11.glVertex3f( (float)x+w, (float)y, (float)0);
            GL11.glTexCoord2f(1f, 1f);
            GL11.glVertex3f( (float)x+w, (float)y+h, (float)0);
            GL11.glTexCoord2f(0f, 1f);
            GL11.glVertex3f( (float)x, (float)y+h, (float)0);
        }
        GL11.glEnd();
        // restore the previous perspective and model views
        setOrthoOff();
    }

    /**
     * Draw a textured quad at the given xyz position in 3D space.  Quad will be drawn
     * with current settings (ie. light, material, depth test, projection, etc.)
     */
    public static void drawQuadZ(int textureHandle, float x, float y, float z, float w, float h) {
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
        // draw  textured quad
        GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
        GL11.glBegin(GL11.GL_QUADS);
        {
            GL11.glTexCoord2f(0f, 0f);
            GL11.glVertex3f( x, y, z);
            GL11.glTexCoord2f(1f, 0f);
            GL11.glVertex3f( x+w, y, z);
            GL11.glTexCoord2f(1f, 1f);
            GL11.glVertex3f( x+w, y+h, z);
            GL11.glTexCoord2f(0f, 1f);
            GL11.glVertex3f( x, y+h, z);
        }
        GL11.glEnd();
    }

    //========================================================================
    // Functions to get and set framebuffer pixels
    //========================================================================

    /**
     * Return a ByteBuffer containing ARGB pixels of the entire screen area.
     */
    public static ByteBuffer framePixels() {
		return framePixels(0,0,displayMode.getWidth(),displayMode.getHeight());
    }

    /**
     * Return a ByteBuffer containing ARGB pixels from the given screen area.
     */
    public static ByteBuffer framePixels(int x, int y, int w, int h) {
		// allocate 4 bytes per pixel
        ByteBuffer pixels = allocBytes(w*h*4);
        // Get pixels from frame buffer in ARGB format.
        GL11.glReadPixels(x,y,w,h, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
        return pixels;
    }

    /**
     * Return an int array containing ARGB pixels from the given screen area.
     */
    public static int[] framePixelsInt(int x, int y, int w, int h) {
        int[] pixels = new int[w * h];
        ByteBuffer pixelsBB = framePixels(x, y, w, h);
        get(pixelsBB,pixels);
        return pixels;
    }

    /**
     * Return the color buffer RGB value at the given screen position as byte[3].
     *
     * @param x    screen position
     * @param y
     * @return rgb byte array
     */
    public static byte[] getPixelColor(int x, int y)
    {
    	// color value will be stored in an integer
    	tmpInt.clear();
    	// read the framebuffer color value at the given position, as bytes
    	GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, tmpInt);
    	byte[] rgb = new byte[] {tmpInt.get(0), tmpInt.get(1), tmpInt.get(2)};
    	return rgb;
    }

    /**
     * Return the depth buffer value at the given screen position.
     *
     * @param x    screen position
     * @param y
     * @return float Z depth value
     */
    public static float getPixelDepth(int x, int y)
    {
    	return getZDepth(x,y);
    }

    /**
     * Return the stencil buffer value at the given screen position. Stencil values are
     * typically bytes (0-255).  The value will be returned as an integer.
     *
     * @param x    screen position
     * @param y
     * @return int stencil value
     */
    public static int getPixelStencil(int x, int y)
    {
    	return getMaskValue(x,y);
    }

    /**
     * Save entire screen image to a texture.  Will copy entire screen even
     * if a viewport is in use.  Texture param must be large enough to hold
     * screen image (see makeTextureForScreen()).
     *
     * @param txtrHandle   texture where screen image will be stored
     * @see frameDraw()
     * @see makeTextureForScreen()
     */
    public static void frameCopy(int txtrHandle)
    {
        frameCopy(txtrHandle, 0,0, DM.getWidth(),DM.getHeight());   // entire screen
    }

    /**
     * Save a region of the screen to a texture.  Texture must be large enough to hold
     * screen image.
     *
     * @param txtrHandle   texture where screen region will be stored
     * @see frameDraw()
     * @see makeTextureForScreen()
     */
    public static void frameCopy(int txtrHandle, int x, int y, int w, int h)
    {
        GL11.glColor4f(1,1,1,1);                // turn off alpha and color tints
        GL11.glReadBuffer(GL11.GL_BACK);
        GL11.glBindTexture(GL11.GL_TEXTURE_2D,txtrHandle);
        // Copy screen to texture
        GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0,0, x,y,w,h);
    }

    /**
     * Draw the screen-sized image over entire screen area.  The screen image
     * is stored in the given texture at 0,0 (see frameCopy()) and has the
     * same dimensions as the current display mode (DM.getWidth(), DM.getHeight()).
     * <P>
     * Reset the viewport and ortho mode to full screen (viewport may be
     * different proportion than screen if custom aspectRatio is set).  Draw the
     * quad the same size as texture so no stretching or compression of image.
     *
     * @param txtrHandle
     */
    public static void frameDraw(int txtrHandle)
    {
        // keep it opaque
        GL11.glDisable(GL11.GL_BLEND);
        // set viewport to full screen
        GL11.glViewport(0, 0, DM.getWidth(), DM.getHeight());
        // draw square quad that covers entire screen
        drawQuad(txtrHandle, 0, 0, screenTextureSize, screenTextureSize); // draw the full screen image
        // restore viewport to custom aspect ratio
        GL11.glViewport(viewportX, viewportY, viewportW, viewportH);
    }

    /**
     * Save the current frame buffer to a PNG image. Exactly the same as screenShot().
     * @see screenShot()
     */
    public static void frameSave() {
    	screenShot();
    }

    //========================================================================
    // Functions to render shapes.
    //========================================================================

    /**
     * Draw a rectangle outline in ortho mode (draws in 2D over the scene).
     * <BR>
     * @see setLineWidth()
     * @see drawRectZ()
     */
    public static void drawRect(int x, int y, float w, float h) {
        // switch projection to 2D mode
        setOrthoOn();
        // draw rectangle at Z=0
        drawRectZ(x,y,0,w,h);
        // restore the previous perspective and model views
        setOrthoOff();
    }

    /**
     * Draw a rectangle outline in world space.  Uses opengl line_strip to make
     * the rectangle.
     * <BR>
     * @see setLineWidth()
     * @see drawRect()
     */
    public static void drawRectZ(int x, int y, int z, float w, float h) {
    	// preserve current settings
    	GL11.glPushAttrib(GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
        // de-activate texture and light
        GL11.glDisable(GL11.GL_TEXTURE_2D);
        GL11.glDisable(GL11.GL_LIGHTING);
        // draw the rectangle
        GL11.glBegin(GL11.GL_LINE_STRIP);
        {
            GL11.glVertex3f( (float)x, (float)y, (float)z);
            GL11.glVertex3f( (float)x+w, (float)y, (float)z);
            GL11.glVertex3f( (float)x+w, (float)y+h, (float)z);
            GL11.glVertex3f( (float)x, (float)y+h, (float)z);
            GL11.glVertex3f( (float)x, (float)y, (float)z);
        }
        GL11.glEnd();
        // draw points at the corners
        GL11.glBegin(GL11.GL_POINTS);
        {
            GL11.glVertex3f( (float)x, (float)y, (float)z);
            GL11.glVertex3f( (float)x+w, (float)y, (float)z);
            GL11.glVertex3f( (float)x+w, (float)y+h, (float)z);
            GL11.glVertex3f( (float)x, (float)y+h, (float)z);
        }
        GL11.glEnd();
        // re-enable settings
        popAttrib();
    }

    /**
     * Draws a circle with the given radius centered at the given world position.
     */
    public static void drawCircle(int x, int y, int radius, int linewidth) {
        // switch projection to 2D mode
        setOrthoOn();
        // draw circle at x,y with z=0
        GL11.glPushMatrix();
        {
            GL11.glTranslatef(x,y,0);
            drawCircle(radius-linewidth, radius, 180);
        }
        GL11.glPopMatrix();
        // restore the previous perspective and model views
        setOrthoOff();
    }

    /**
     * Draws a circle with the given radius centered at the given world position.
     */
    public static void drawCircleZ(int x, int y, int z, int radius, int linewidth) {
        GL11.glPushMatrix();
        {
            GL11.glTranslatef(x,y,z);
            drawCircle(radius-linewidth, radius, 180);
        }
        GL11.glPopMatrix();
    }

    /**
     * Draws a circle centered at 0,0,0.  Use translate() to place circle at desired coords.
     * Inner and outer radius specify width, stepsize is number of degrees for each segment.
     */
    public static void drawCircle(float innerRadius, float outerRadius, int numSegments) {
        int s = 0;     // start
        int e = 360;   // end
        int stepSize = 360/numSegments;   // degrees per segment
        GL11.glBegin(GL11.GL_QUAD_STRIP);
        {
            // add first 2 vertices
            float ts = (float) Math.sin(Math.toRadians(s));
            float tc = (float) Math.cos(Math.toRadians(s));
            GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
            GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
            // add intermediate vertices, snap to {step} degrees
            while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e) {
                ts = (float) Math.sin(Math.toRadians(s));
                tc = (float) Math.cos(Math.toRadians(s));
                GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
                GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
            }
            // add last 2 vertices at end angle
            ts = (float) Math.sin(Math.toRadians(e));
            tc = (float) Math.cos(Math.toRadians(e));
            GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
            GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
        }
        GL11.glEnd();
    }

    /**
     * Render a 2 unit cube centered at origin.  Includes texture coordinates
     * and normals.
     */
    public static void renderCube()
    {
        GL11.glBegin(GL11.GL_QUADS);
        // Front Face
        GL11.glNormal3f( 0.0f, 0.0f, 1.0f);
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Left
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Right
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Right
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Left
        // Back Face
        GL11.glNormal3f( 0.0f, 0.0f, -1.0f);
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Right
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Right
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Left
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Left
        // Top Face
        GL11.glNormal3f( 0.0f, 1.0f, 0.0f);
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);	// Bottom Left
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);	// Bottom Right
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right
        // Bottom Face
        GL11.glNormal3f( 0.0f, -1.0f, 0.0f);
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);	// Top Right
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);	// Top Left
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right
        // Right face
        GL11.glNormal3f( 1.0f, 0.0f, 0.0f);
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);	// Bottom Right
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);	// Top Right
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);	// Top Left
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);	// Bottom Left
        // Left Face
        GL11.glNormal3f( -1.0f, 0.0f, 0.0f);
        GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);	// Bottom Left
        GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);	// Bottom Right
        GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);	// Top Right
        GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);	// Top Left
        GL11.glEnd();
    }

    /**
     * draw a cube with the given size, centered at origin.  Include texture coordinates.
     * @param size       length of each side
     * @param segments   # segments to divide each side into
     */
    public static void renderCube(float size, int segments) {
    	float halfsize = size/2f;
    	GL11.glPushMatrix();
    	{
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// front
    		}
    		GL11.glPopMatrix();
    		GL11.glPushMatrix();
    		{
    			GL11.glRotatef(90,0,1,0);
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// right
    		}
    		GL11.glPopMatrix();
    		GL11.glPushMatrix();
    		{
    			GL11.glRotatef(180,0,1,0);
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// back
    		}
    		GL11.glPopMatrix();
    		GL11.glPushMatrix();
    		{
    			GL11.glRotatef(270,0,1,0);
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// left
    		}
    		GL11.glPopMatrix();
    		GL11.glPushMatrix();
    		{
    			GL11.glRotatef(90,1,0,0);
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// bottom
    		}
    		GL11.glPopMatrix();
    		GL11.glPushMatrix();
    		{
    			GL11.glRotatef(-90,1,0,0);
    			GL11.glTranslatef(0,0,halfsize);
    			renderPlane(size,segments);// top
    		}
    		GL11.glPopMatrix();
    	}
    	GL11.glPopMatrix();
    }

    /**
     * draw a square plane in the X,Y axis, centered at origin.  Include texture coordinates.
     * @param size       length of each side
     * @param segments   number of segments to divide each side into
     */
    public static void renderPlane(float size, int segments) {
        renderPlane(size,size,segments,segments);
    }

    /**
     * draw a rectangular plane in the X,Y axis, centered at origin, with the specified size and
     * number of divisions.  Texture will cover entire rectangle without repeating.
     * @param length     length of X axis side
     * @param height     length of Y axis side
     * @param segments   number of segments to divide each side into
     */
    public static void renderPlane(float length, float height, int length_segments, int height_segments) {
        renderPlane(length, height, length_segments, height_segments, 1, 1);
    }

    /**
     * draw a rectangular plane in the X,Y axis, centered at origin.  Include texture coordinates.
     * Scale the UV coordinates to same proportion as plane dimensions. Texture will repeat as
     * specified by the tilefactorU and tilefactorV values.  If tilefactor values are 1, the texture
     * will cover the rectangle without tiling.
     * @param length    length on X axis
     * @param depth     length on Y axis
     * @param segments  number of segments to divide each side into
     */
    public static void renderPlane(float length, float height, int length_segments, int height_segments, float tilefactorU, float tilefactorV) {
    	float xpos = - length/2f;
    	float ypos = - height/2f;
    	float segsizeL = length/(float)length_segments;
    	float segsizeH = height/(float)height_segments;
        float maxDimension = (length > height)? length : height;
    	float uvsegsizeL = (length/maxDimension) / (float)length_segments;
    	float uvsegsizeH = (height/maxDimension) / (float)height_segments;
    	GL11.glBegin(GL11.GL_QUADS); {
    		GL11.glNormal3f(0f, 0f, 1f);   // plane is facing up the Z axis
    		for (int x=0; x < length_segments; x++, xpos+=segsizeL) {
    			for (int y=0; y < height_segments; y++, ypos+=segsizeH) {
    				// bottom left
    				GL11.glTexCoord2f((x*uvsegsizeL)*tilefactorU, (y*uvsegsizeH)*tilefactorV);
    				GL11.glVertex3f( xpos, ypos, 0f);
    				// bottom rite
    				GL11.glTexCoord2f(((x*uvsegsizeL)+uvsegsizeL)*tilefactorU, (y*uvsegsizeH)*tilefactorV);
    				GL11.glVertex3f( xpos+segsizeL, ypos,  0f);
    				// top rite
    				GL11.glTexCoord2f(((x*uvsegsizeL)+uvsegsizeL)*tilefactorU, ((y*uvsegsizeH)+uvsegsizeH)*tilefactorV);
    				GL11.glVertex3f( xpos+segsizeL,  ypos+segsizeH, 0f);
    				// top left
    				GL11.glTexCoord2f((x*uvsegsizeL)*tilefactorU, ((y*uvsegsizeH)+uvsegsizeH)*tilefactorV);
    				GL11.glVertex3f( xpos,  ypos+segsizeH, 0f);
    			}
    			ypos = - height/2f; // reset column position
    		}
    	}
    	GL11.glEnd();
    }

    /**
     * draw a rectangular plane in the X,Y axis, centered at origin.  Include texture coordinates.
     * Scale the UV coordinates to same proportion as plane dimensions.
     * @param length    length on X axis
     * @param depth     length on Y axis
     * @param segments  number of segments to divide each side into
     */
    public static void renderPlaneORIG(float length, float height, int length_segments, int height_segments) {
    	float xpos = - length/2f;
    	float ypos = - height/2f;
    	float segsizeL = length/(float)length_segments;
    	float segsizeH = height/(float)height_segments;
        float maxDimension = (length > height)? length : height;
    	float uvsegsizeL = (length/maxDimension) / (float)length_segments;
    	float uvsegsizeH = (height/maxDimension) / (float)height_segments;
    	GL11.glBegin(GL11.GL_QUADS); {
    		GL11.glNormal3f(0f, 0f, 1f);   // plane is facing up the Z axis
    		for (int x=0; x < length_segments; x++, xpos+=segsizeL) {
    			for (int y=0; y < height_segments; y++, ypos+=segsizeH) {
    				// bottom left
    				GL11.glTexCoord2f(x*uvsegsizeL, y*uvsegsizeH);
    				GL11.glVertex3f( xpos, ypos, 0f);
    				// bottom rite
    				GL11.glTexCoord2f((x*uvsegsizeL)+uvsegsizeL, y*uvsegsizeH);
    				GL11.glVertex3f( xpos+segsizeL, ypos,  0f);
    				// top rite
    				GL11.glTexCoord2f((x*uvsegsizeL)+uvsegsizeL, (y*uvsegsizeH)+uvsegsizeH);
    				GL11.glVertex3f( xpos+segsizeL,  ypos+segsizeH, 0f);
    				// top left
    				GL11.glTexCoord2f(x*uvsegsizeL, (y*uvsegsizeH)+uvsegsizeH);
    				GL11.glVertex3f( xpos,  ypos+segsizeH, 0f);
    			}
    			ypos = - height/2f; // reset column position
    		}
    	}
    	GL11.glEnd();
    }

    /**
     * call the LWJGL Sphere class to draw sphere geometry
     * with texture coordinates and normals
     * @param facets  number of divisions around longitude and latitude
     */
    public static void renderSphere(int facets) {
        Sphere s = new Sphere();            // an LWJGL class
        s.setOrientation(GLU.GLU_OUTSIDE);  // normals point outwards
        s.setTextureFlag(true);             // generate texture coords
        GL11.glPushMatrix();
        {
	        GL11.glRotatef(-90f, 1,0,0);    // rotate the sphere to align the axis vertically
	        s.draw(1, facets, facets);              // run GL commands to draw sphere
        }
        GL11.glPopMatrix();
    }

    /**
     * draw a sphere with 48 facets (pretty smooth) with normals and texture coords
     */
    public static void renderSphere() {
        renderSphere(48);
    }


    /**
     * Sets glLineWidth() and glPointSize() to the given width.  This will
     * affect geometry drawn using glBegin(GL_LINES), GL_LINE_STRIP, and GL_POINTS.
     * May only work with widths up to 10 (depends on hardware).
     */
    public static void setLineWidth(int width)
    {
    	GL11.glLineWidth(width);
    	GL11.glPointSize(width);
    	//GL11.glEnable(GL11.GL_POINT_SMOOTH);
    	//GL11.glEnable(GL11.GL_LINE_SMOOTH);
    }

    /**
     * Set the current color with RGBA floats in range 0-1.  The current color
     * is disabled when lighting is enabled.  When lighting is enabled (glEnable(GL_LIGHTING))
     * then material colors are in effect and the current color is ignored.
     */
    public static void setColor(float R, float G, float B, float A)
    {
    	GL11.glColor4f(R,G,B,A);
    }

    /**
     * Set the current color with RGBA bytes in range 0-255. The current color
     * is disabled when lighting is enabled.  When lighting is enabled (glEnable(GL_LIGHTING))
     * then material colors are in effect and the current color is ignored.
     */
    public static void setColorB(int R, int G, int B, int A)
    {
    	GL11.glColor4ub((byte)R,(byte)G,(byte)B,(byte)A);
    }

    /**
     * Set the current color to the given RGB or RGBA float array.  Floats are
     * in range 0-1. The current color is disabled when lighting is enabled.
     * When lighting is enabled (glEnable(GL_LIGHTING)) then
     * material colors are in effect and the current color is ignored.
     */
    public static void setColor(float[] rgba)
    {
    	if (rgba != null) {
    		if (rgba.length == 4) {
    			GL11.glColor4f(rgba[0],rgba[1],rgba[2],rgba[3]);
    		}
    		else if (rgba.length == 3) {
    			GL11.glColor4f(rgba[0],rgba[1],rgba[2],1);
    		}
    	}
    }

    /**
     * Enable/disable the color-material setting.  When enabled, the glColor() command
     * will change the current material color.  This provides a convenient and
     * efficient way to change material colors without having to call glMaterial().
     * When disabled, the glColor() command functions normally (has no affect on
     * material colors).
     *
     * @param on   when true, glColor() will set the current material color
     */
    public static void setColorMaterial(boolean on)
    {
    	if (on) {
    		// glColor() will change the diffuse and ambient material colors
    		GL11.glColorMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE);
    		GL11.glEnable(GL11.GL_COLOR_MATERIAL);
    	}
    	else {
    		// glColor() behaves normally
    		GL11.glDisable(GL11.GL_COLOR_MATERIAL);
    	}
    }

    //========================================================================
    // Functions to build a character set and draw text strings.
    //
    // Example:
    //           buildFont("Font_tahoma.png");
    //           ...
    //           glPrint(100, 100, 0, "Here's some text");
    //           ...
    //           destroyFont();   // cleanup
    //========================================================================

    static int fontListBase = -1;           // Base Display List For The character set
    static int fontTextureHandle = -1;      // Texture handle for character set image

    /**
     * Build a character set from the given texture image.
     *
     * @param charSetImage   texture image containing 256 characters in a 16x16 grid
     * @param fontWidth      how many pixels to allow per character on screen
     *
     * @see       destroyFont()
     */
    public static boolean buildFont(String charSetImage, int fontWidth)
    {
    	// make texture from image
    	GLImage textureImg = loadImage(charSetImage);
    	if (textureImg == null) {
    		return false;  // image not found
    	}
    	//pushAttrib();
    	fontTextureHandle = makeTexture(textureImg);
    	// build character set as call list of 256 textured quads
    	buildFont(fontTextureHandle, fontWidth);
    	//popAttrib();
    	return true;
    }

    /**
      * Build the character set display list from the given texture.  Creates
      * one quad for each character, with one letter textured onto each quad.
      * Assumes the texture is a 256x256 image containing every
      * character of the charset arranged in a 16x16 grid.  Each character
      * is 16x16 pixels.  Call destroyFont() to release the display list memory.
      *
      * Should be in ORTHO (2D) mode to render text (see setOrtho()).
      *
      * Special thanks to NeHe and Giuseppe D'Agata for the "2D Texture Font"
      * tutorial (http://nehe.gamedev.net).
      *
      * @param charSetImage   texture image containing 256 characters in a 16x16 grid
      * @param fontWidth      how many pixels to allow per character on screen
      *
      * @see       destroyFont()
      */
    public static void buildFont(int fontTxtrHandle, int fontWidth)
    {
        float factor = 1f/16f;
        float cx, cy;
        fontListBase = GL11.glGenLists(256); // Creating 256 Display Lists
        for (int i = 0; i < 256; i++) {
            cx = (float) (i % 16) / 16f;              // X Texture Coord Of Character (0 - 1.0)
            cy = (float) (i / 16) / 16f;              // Y Texture Coord Of Character (0 - 1.0)
            GL11.glNewList(fontListBase + i, GL11.GL_COMPILE); // Start Building A List
            GL11.glBegin(GL11.GL_QUADS);              // Use A 16x16 pixel Quad For Each Character
            GL11.glTexCoord2f(cx, 1 - cy - factor);  // Texture Coord (Bottom Left)
            GL11.glVertex2i(0, 0);
            GL11.glTexCoord2f(cx + factor, 1 - cy - factor); // Texture Coord (Bottom Right)
            GL11.glVertex2i(16, 0);
            GL11.glTexCoord2f(cx + factor, 1 - cy);   // Texture Coord (Top Right)
            GL11.glVertex2i(16, 16);
            GL11.glTexCoord2f(cx, 1 - cy);             // Texture Coord (Top Left)
            GL11.glVertex2i(0, 16);
            GL11.glEnd();                              // Done Building Our Quad (Character)
            GL11.glTranslatef(fontWidth, 0, 0);        // Move To The Right Of The Character
            GL11.glEndList();                          // Done Building The Display List
        } // Loop Until All 256 Are Built
    }

    /**
     * Clean up the allocated display lists for the character set.
     */
    public static void destroyFont()
    {
        if (fontListBase != -1) {
            GL11.glDeleteLists(fontListBase,256);
            fontListBase = -1;
        }
    }

    /**
     * Render a text string in 2D over the scene, using the character set created
     * by buildFont().
     *
     * @param x  screen pixel position of the string
     * @param y
     * @param msg  text string to draw
     */
    public static void print(int x, int y, String msg)
    {
    	print(x,y,msg,0);
    }

    /**
     * Render a text string in 2D over the scene, using the character set created
     * by buildFont().
     *
     * @param x  screen pixel position of the string
     * @param y
     * @param msg  text string to draw
     * @param set  which of the two character sets: 0 or 1
     */
    public static void print(int x, int y, String msg, int set)
    {
    	// if font is not initiallized, try loading default font
    	if (fontListBase == -1 || fontTextureHandle == -1) {
    		if (!buildFont("images/font_tahoma.png", 12)) {
    			err("GLApp.print(): character set has not been created -- see buildFont()");
    			return;
    		}
    	}
    	if (msg != null) {
    		int offset = fontListBase - 32 + (128 * set);
    		// preserve current GL settings
    		pushAttribOrtho();
    		// turn off lighting
    		GL11.glDisable(GL11.GL_LIGHTING);
    		// enable alpha blending, so character background is transparent
    		GL11.glEnable(GL11.GL_BLEND);
    		GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
    		// enable the charset texture
    		GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
    		// prepare to render in 2D
    		setOrthoOn();
    		// draw the text
    		GL11.glTranslatef(x, y, 0);        // Position The Text (in pixel coords)
    		for(int i=0; i<msg.length(); i++) {
    			GL11.glCallList(offset + msg.charAt(i));
    		}
    		// restore the original positions and views
    		setOrthoOff();
    		// restore previous settings
    		popAttrib();
    	}
    }

    /**
     * Render a text string in model space, using the character set created
     * by buildFont().
     */
    public static void printZ(float x, float y, float z, int set, float scale, String msg)
    {
    	int offset;
    	if (fontListBase == -1 || fontTextureHandle == -1) {
    		// font is not initiallized, try this default
    		if (!buildFont("images/font_tahoma.png", 12)) {
    			err("GLApp.printZ(): character set has not been created -- see buildFont()");
    			return;
    		}
    	}
    	offset = fontListBase - 32 + (128 * set);
    	if (msg != null) {
    		// enable the charset texture
    		GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
    		// draw the text
    		GL11.glPushMatrix();
    		{
    			GL11.glTranslatef(x, y, z); // Position The Text (in pixels coords)
    			GL11.glScalef(scale,scale,scale);  // make it smaller (arbitrary kludge!!!!)
    			for (int i = 0; i < msg.length(); i++) {
    				GL11.glCallList(offset + msg.charAt(i));
    			}
    		}
    		GL11.glPopMatrix();
    	}
    }


    //========================================================================
    // PBuffer functions
    //
    // Pbuffers are offscreen buffers that can be rendered into just like
    // the regular framebuffer.  A pbuffer can be larger than the screen,
    // which allows for the creation of higher resolution images.
    //
    //========================================================================

    /**
     * Create a Pbuffer for use as an offscreen buffer, with the given
     * width and height.  Use selectPbuffer() to make the pbuffer the
     * context for all subsequent opengl commands.  Use selectDisplay() to
     * make the Display the context for opengl commands.
     * <P>
     * @param width
     * @param height
     * @return Pbuffer
     * @see selectPbuffer(), selectDisplay()
     */
    public static Pbuffer makePbuffer(final int width, final int height) {
    	Pbuffer pbuffer = null;
    	try {
    		pbuffer = new Pbuffer(width, height,
    				new PixelFormat(24, //bitsperpixel
    						8,  //alpha
    						24, //depth
    						8,  //stencil
    						0), //samples
    						null,
    						null);
    	} catch (LWJGLException e) {
    		err("GLApp.makePbuffer(): exception " + e);
    	}
    	return pbuffer;
    }

    /**
     * Make the pbuffer the current context for opengl commands.  All following
     * gl functions will operate on this buffer instead of the display.
     * <P>
     * NOTE: the Pbuffer may be recreated if it was lost since last used.  It's
     * a good idea to use:
     * <PRE>
     *         pbuff = selectPbuffer(pbuff);
     * </PRE>
     * to hold onto the new Pbuffer reference if Pbuffer was recreated.
     *
     * @param pb  pbuffer to make current
     * @return    Pbuffer
     * @see       selectDisplay(), makePbuffer()
     */
    public static Pbuffer selectPbuffer(Pbuffer pb) {
    	if (pb != null) {
    		try {
    			// re-create the buffer if necessary
    			if (pb.isBufferLost()) {
    				int w = pb.getWidth();
    				int h = pb.getHeight();
    				msg("GLApp.selectPbuffer(): Buffer contents lost - recreating the pbuffer");
    				pb.destroy();
    				pb = makePbuffer(w, h);
    			}
    			// select the pbuffer for rendering
    			pb.makeCurrent();
    		}
    		catch (LWJGLException e) {
    			err("GLApp.selectPbuffer(): exception " + e);
    		}
    	}
    	return pb;
    }

    /**
     * Make the Display the current context for OpenGL commands.  Subsequent
     * gl functions will operate on the Display.
     *
     * @see selectPbuffer()
     */
    public static void selectDisplay()
    {
    	try {
    		Display.makeCurrent();
    	} catch (LWJGLException e) {
    		err("GLApp.selectDisplay(): exception " + e);
    	}
    }

    /**
     * Copy the pbuffer contents to a texture.  (Should this use glCopyTexSubImage2D()?
     * Is RGB the fastest format?)
     */
    public static void frameCopy(Pbuffer pbuff, int textureHandle) {
    	GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
    	GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, 0, 0, pbuff.getWidth(), pbuff.getHeight(), 0);
    }

    /**
     * Save the current frame buffer to a PNG image. Same as
     * screenShot(filename) but the screenshot filename will be automatically
     * set to <applicationClassName>-<timestamp>.png
     */
    public static void screenShot() {
    	screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), rootClass.getName() + "-" + makeTimestamp() + ".png");
    }

    /**
     * Save the current frame buffer to a PNG image. Can also
     * be used with the PBuffer class to copy large images or textures that
     * have been rendered into the offscreen pbuffer.
     */
    public static void screenShot(String imageFilename) {
    	screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), imageFilename);
    }

    /**
     * Save the current Pbuffer to a PNG image. Same as screenShot(filename)
     * but the Pbuffer will be saved instead of the framebuffer, and the
     * screenshot filename will be set to <applicationClassName>-<timestamp>.png
     * NOTE: Have to call selectPbuffer() before calling this function.
     */
    public static void screenShot(Pbuffer pb) {
    	screenShot(0, 0, pb.getWidth(), pb.getHeight(), rootClass.getName() + "_" + makeTimestamp() + ".png");
    }

    /**
     * Save a region of the current render buffer to a PNG image.  If the current
     * buffer is the framebuffer then this will work as a screen capture.  Can
     * also be used with the PBuffer class to copy large images or textures that
     * have been rendered into the offscreen pbuffer.
     * <P>
     * WARNING: this function hogs memory!  Call java with more memory
     * (java -Xms128m -Xmx128m)
     * <P>
     * @see   selectPbuffer(Pbuffer)
     * @see   selectDisplay()
     * @see   savePixelsToPNG()
     */
    public static void screenShot(int x, int y, int width, int height, String imageFilename) {
    	// allocate space for ARBG pixels
    	ByteBuffer framebytes = allocBytes(width * height * SIZE_INT);
    	int[] pixels = new int[width * height];
    	// grab the current frame contents as ARGB ints (BGRA ints reversed)
    	GL11.glReadPixels(x, y, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, framebytes);
    	// copy ARGB data from ByteBuffer to integer array
    	framebytes.asIntBuffer().get(pixels, 0, pixels.length);
    	// free up this memory
    	framebytes = null;
    	// flip the pixels vertically and save to file
    	GLImage.savePixelsToPNG(pixels, width, height, imageFilename, true);
    }

	/**
	 * Save a ByteBuffer of ARGB pixels to a PNG file.
	 * If flipY is true, flip the pixels on the Y axis before saving.
	 */
	public static void savePixelsToPNG(ByteBuffer framebytes, int width, int height, String imageFilename, boolean flipY) {
		if (framebytes != null && imageFilename != null) {
			// copy ARGB data from ByteBuffer to integer array
			int[] pixels = new int[width * height];
			framebytes.asIntBuffer().get(pixels, 0, pixels.length);
			// save pixels to file
			GLImage.savePixelsToPNG(pixels, width, height, imageFilename, flipY);
		}
	}

	/**
	 * Save the contents of the current render buffer to a PNG image. This is
	 * an older version of screenShot() that used the default OpenGL GL_RGBA
	 * pixel format which had to be swizzled into an ARGB format. I'm
	 * keeping the function here for reference.
	 * <P>
	 * If the current buffer is the framebuffer then this will work as a screen capture.
	 * Can also be used with the PBuffer class to copy large images or textures that
	 * have been rendered into the offscreen pbuffer.
	 * <P>
	 * WARNING: this function hogs memory!  Call java with more memory
	 * (java -Xms128m -Xmx128)
	 * <P>
	 * @see   selectPbuffer(), selectDisplay()
	 */
	public static void screenShotRGB(int width, int height, String saveFilename) {
		// allocate space for RBG pixels
		ByteBuffer framebytes = GLApp.allocBytes(width * height * 3);
		int[] pixels = new int[width * height];
		int bindex;
		// grab a copy of the current frame contents as RGB (has to be UNSIGNED_BYTE or colors come out too dark)
		GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, framebytes);
		// copy RGB data from ByteBuffer to integer array
		for (int i = 0; i < pixels.length; i++) {
			bindex = i * 3;
			pixels[i] =
				0xFF000000                                          // A
				| ((framebytes.get(bindex) & 0x000000FF) << 16)     // R
				| ((framebytes.get(bindex+1) & 0x000000FF) << 8)    // G
				| ((framebytes.get(bindex+2) & 0x000000FF) << 0);   // B
		}
		// free up some memory
		framebytes = null;
		// save to file (flip Y axis before saving)
		GLImage.savePixelsToPNG(pixels, width, height, saveFilename, true);
	}

	//========================================================================
	// Stencil functions
	//========================================================================

	/**
	 * clear the stencil buffer
	 */
	public static void clearMask() {
		GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
	}


	/**
	 *  Begin creating a mask.  This function turns off the color and depth buffers
	 *  so all subsequent drawing will go only into the stencil buffer.
	 *  To use:
	 *          beginMask(1);
	 *          renderModel();  // draw some geometry
	 *          endMask();
	 */
	public static void beginMask(int maskvalue) {
		// turn off writing to the color buffer and depth buffer
		GL11.glColorMask(false, false, false, false);
		GL11.glDepthMask(false);

		// enable stencil buffer
		GL11.glEnable(GL11.GL_STENCIL_TEST);

		// set the stencil test to ALWAYS pass
		GL11.glStencilFunc(GL11.GL_ALWAYS, maskvalue, 0xFFFFFFFF);
		// REPLACE the stencil buffer value with maskvalue whereever we draw
		GL11.glStencilOp(GL11.GL_REPLACE, GL11.GL_REPLACE, GL11.GL_REPLACE);
	}

	/**
	 *  End the mask.  Freeze the stencil buffer and activate the color and depth buffers.
	 */
	public static void endMask() {
		// don't let future drawing modify the contents of the stencil buffer
		GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);

		// turn the color and depth buffers back on
		GL11.glColorMask(true, true, true, true);
		GL11.glDepthMask(true);
	}

	/**
	 *  Restrict rendering to the masked area.
	 *  To use:
	 *          GLStencil.beginMask(1);
	 *          renderModel();
	 *          GLStencil.endMask();
	 */
	public static void activateMask(int maskvalue) {
		// enable stencil buffer
		GL11.glEnable(GL11.GL_STENCIL_TEST);

		// until stencil test is disabled, only write to areas where the
		// stencil buffer equals the mask value
		GL11.glStencilFunc(GL11.GL_EQUAL, maskvalue, 0xFFFFFFFF);
	}

	/**
	 *  turn off the stencil test so stencil has no further affect on rendering.
	 */
	public static void disableMask() {
		GL11.glDisable(GL11.GL_STENCIL_TEST);
	}

	/**
	 * Return the stencil buffer value at the given screen position.
	 */
	public static int getMaskValue(int x, int y)
	{
		tmpByte.clear();
		// read the stencil value at the given position, as an unsigned byte, store it in tmpByte
		GL11.glReadPixels(x, y, 1, 1, GL11.GL_STENCIL_INDEX, GL11.GL_UNSIGNED_BYTE, tmpByte);
		return (int) tmpByte.get(0);
	}

	//========================================================================
	// Display list functions
	//
	// Display lists are OpenGL commands that have been optimized and stored
	// into memory on the graphics card.  They greatly improve rendering
	// performance but also "freeze" the geometry, so are not suitable in cases
	// where the geometry has to change dynamically.
	//
	// Display lists have to be deleted from the graphics card when
	// the program exits, or they can accumulate and consume memory.  The
	// function destroyDisplayLists() is called by cleanup() to de-allocate
	// any display lists that were created by these functions.
	//
	//========================================================================
	public static ArrayList displayLists = new ArrayList();  // will hold display list IDs created by beginDisplayList()

	/**
	 * Begin a display list. All following OpenGL geometry commands (up to endDisplayList())
	 * will be stored in a display list, not drawn to screen.
	 * <P>
	 * To use, create a display list in setup():
	 * <PRE>
	 *      int teapotID = beginDisplayList();
	 *      ... // run teapot render code here
	 *      endDisplayList();
	 * </PRE>
	 *
	 * Then call the display list later in render():
	 * <PRE>
	 *      callDisplayList(teapotID);
	 * </PRE>
	 *
	 * @return integer display list id
	 * @see endDisplayList(), callDisplayList(), destroyDisplayList()
	 */
	public static int beginDisplayList() {
		int DL_ID = GL11.glGenLists(1);         // Allocate 1 new Display List
		GL11.glNewList(DL_ID, GL11.GL_COMPILE); // Start Building A List
		displayLists.add( new Integer(DL_ID) ); // save the list ID so we can delete it later (see destroyDisplayLists())
		return DL_ID;
	}

	/**
	 * Finish display list creation.  Use this function only after calling
	 * beginDisplayList()
	 *
	 * @see beginDisplayList()
	 */
	public static void endDisplayList() {
		GL11.glEndList();
	}

	/**
	 * Render the geometry stored in a display list.  Use this function after
	 * calling beginDisplayList() and endDisplayList() to create a display list.
	 *
	 * @see beginDisplayList()
	 * @see endDisplayList()
	 */
	public static void callDisplayList(int displayListID) {
		GL11.glCallList(displayListID);
	}

    /**
     * Delete the given display list ID.  Frees up resources on the graphics card.
     */
    public static void destroyDisplayList(int DL_ID)
    {
        GL11.glDeleteLists(DL_ID,1);
    }

    /**
     * Clean up the allocated display lists.  Called by cleanUp() when app exits.
     *
     * @see cleanUp();
     */
    public static void destroyDisplayLists()
    {
        while (displayLists.size() > 0) {
            int displaylistID = ((Integer)displayLists.get(0)).intValue();
            GL11.glDeleteLists(displaylistID,1);
            displayLists.remove(0);
        }
    }

    //========================================================================
    // Native IO Buffer allocation functions
    //
    // These functions create and populate the native buffers used by LWJGL.
    //========================================================================

    public static ByteBuffer allocBytes(int howmany) {
    	return ByteBuffer.allocateDirect(howmany * SIZE_BYTE).order(ByteOrder.nativeOrder());
    }

    public static IntBuffer allocInts(int howmany) {
    	return ByteBuffer.allocateDirect(howmany * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
    }

    public static FloatBuffer allocFloats(int howmany) {
    	return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
    }

    public static DoubleBuffer allocDoubles(int howmany) {
    	return ByteBuffer.allocateDirect(howmany * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
    }

    public static ByteBuffer allocBytes(byte[] bytearray) {
    	ByteBuffer bb = ByteBuffer.allocateDirect(bytearray.length * SIZE_BYTE).order(ByteOrder.nativeOrder());
    	bb.put(bytearray).flip();
    	return bb;
    }

    public static IntBuffer allocInts(int[] intarray) {
    	IntBuffer ib = ByteBuffer.allocateDirect(intarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asIntBuffer();
    	ib.put(intarray).flip();
    	return ib;
    }

    public static FloatBuffer allocFloats(float[] floatarray) {
    	FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
    	fb.put(floatarray).flip();
    	return fb;
    }

    public static DoubleBuffer allocDoubles(double[] darray) {
    	DoubleBuffer fb = ByteBuffer.allocateDirect(darray.length * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
    	fb.put(darray).flip();
    	return fb;
    }

    public static void put(ByteBuffer b, byte[] values) {
    	b.clear();
    	b.put(values).flip();
    }

    public static void put(IntBuffer b, int[] values) {
    	b.clear();
    	b.put(values).flip();
    }

    public static void put(FloatBuffer b, float[] values) {
    	b.clear();
    	b.put(values).flip();
    }

    public static void put(DoubleBuffer b, double[] values) {
    	b.clear();
    	b.put(values).flip();
    }

    /**
     *  copy ints from the given byteBuffer into the given int array.
     *  @param b       source ByteBuffer
     *  @param values  target integer array, must be same length as ByteBuffer capacity/4
     */
    public static void get(ByteBuffer b, int[] values) {
    	b.asIntBuffer().get(values, 0, values.length);
    }

    /**
     *  copy ints from the given IntBuffer into the given int array.
     *  @param b       source IntBuffer
     *  @param values  target integer array, must be same length as IntBuffer
     */
    public static void get(IntBuffer b, int[] values) {
    	b.get(values, 0, values.length);
    }

    /**
     *  return the contents of the byteBuffer as an array of ints.
     *  @param b  source ByteBuffer
     */
    public static int[] getInts(ByteBuffer b) {
    	int[] values = new int[b.capacity()/SIZE_INT];
    	b.asIntBuffer().get(values, 0, values.length);
    	return values;
    }

    //========================================================================
    // Misc functions
    //========================================================================
    public static URL appletBaseURL = null;
    public static Class rootClass = GLApp.class;

    /**
     * Open the given file and return the InputStream.  This function assumes
     * 1) that we're running an application and the file is in the local filesystem.  If not found, then assume
     * 2) we're in a jar file and look for the file in the current jar.  If not found, then assume
     * 3) we're running an applet and look for the file relative to the applet code base.
     * @param filename to open
     */
    public static InputStream getInputStream(String filename) {
    	InputStream in = null;
    	
    	// 1) look for file in local filesystem
    	try {
    		in = new FileInputStream(filename);
    	}
    	catch (IOException ioe) {
    		msg("GLApp.getInputStream (" + filename + "): " + ioe);
    		if (in != null) {
    			try {
    				in.close();
    			}
    			catch (Exception e) {}
    			in = null;
    		}
    	}
    	catch (Exception e) {
    		msg("GLApp.getInputStream (" + filename + "): " + e);
    	}
    	
		// 2) if couldn't open file, look in jar
    	if (in == null && rootClass != null) {
    		// NOTE: class.getResource() looks for files relative to the folder that the class is in.
    		// ideally the class will be an application in the root of the installation, see setRootClass().
    		URL u = null;
    		if (filename.startsWith(".")) {   // remove leading . ie. "./file"
    			filename = filename.substring(1);
    		}
    		try {u = rootClass.getResource(filename);}
    		catch (Exception ue) {msg("GLApp.getInputStream(): Can't find resource: " + ue);}
    		//msg("GLApp.getInputStream (" +filename+ "): try jar resource url=" + u);
    		if (u != null) {
    			try {
    				in = u.openStream();
    			}
    			catch (Exception e) {
    				msg("GLApp.getInputStream (" +filename+ "): Can't load from jar: " + e);
    			}
    		}
    		
    		// 3) try loading file from applet base url
    		if (in == null && appletBaseURL != null) {
    			try {u = new URL(appletBaseURL,filename);}
    			catch (Exception ue) {msg("GLApp.getInputStream(): Can't make applet base url: " + ue);}
    			//msg("GLApp.getInputStream (" +filename+ "): try applet base url=" + u);
    			try {
    				in = u.openStream();
    			}
    			catch (Exception e) {
    				msg("GLApp.getInputStream (" +filename+ "): Can't load from applet base URL: " + e);
    			}
    		}
    	}
    	return in;
    }

    /**
     * Return an array of bytes read from an InputStream.  Reads all bytes
     * until the end of stream.  Can read an arbitrary number of bytes.
     * NOTE: Does not close the inputStream!
     */
    public static byte[] getBytesFromStream(InputStream is) {
    	int chunkSize = 1024;
    	int totalRead = 0;
    	int num = 0;
    	byte[] bytes = new byte[chunkSize];
    	ArrayList byteChunks = new ArrayList();

    	// Read the bytes in chunks of 1024
    	try {
    		while ( (num=is.read(bytes)) >= 0) {
    			byteChunks.add(bytes);
    			bytes = new byte[chunkSize];
    			totalRead += num;
    		}
    	}
    	catch (IOException ioe) {
    		err("GLApp.getBytesFromStream(): IOException " + ioe);
    	}

    	int numCopied = 0;
    	bytes = new byte[totalRead];

    	// copy byte chunks to byte array (last chunk may be partial)
    	while (byteChunks.size() > 0) {
    		byte[] byteChunk = (byte[]) byteChunks.get(0);
    		int copylen = (totalRead - numCopied > chunkSize)? chunkSize : (totalRead - numCopied);
    		System.arraycopy(byteChunk, 0, bytes, numCopied, copylen);
    		byteChunks.remove(0);
    		numCopied += copylen;
    	}

    	msg("getBytesFromStream() read " + numCopied + " bytes.");

    	return bytes;
    }

    /**
     *  Return an array of bytes read from a file.
     */
    public static byte[] getBytesFromFile(String filename) {
    	InputStream is = getInputStream(filename);
    	byte[] bytes = getBytesFromStream(is);
    	try {
    		is.close();
    	}
    	catch (IOException ioe) {
    		err("GLApp.getBytesFromFile(): IOException " + ioe);
    	}
    	return bytes;
    }

    /**
     *  Return a String array containing the path portion of a filename (result[0]),
     *  and the fileame (result[1]).  If there is no path, then result[0] will be ""
     *  and result[1] will be the full filename.
     */
    public static String[] getPathAndFile(String filename) {
    	String[] pathAndFile = new String[2];
    	Matcher matcher = Pattern.compile("^.*/").matcher(filename);
    	if (matcher.find()) {
    		pathAndFile[0] = matcher.group();
    		pathAndFile[1] = filename.substring(matcher.end());
    	}
    	else {
    		pathAndFile[0] = "";
    		pathAndFile[1] = filename;
    	}
    	return pathAndFile;
    }

    /**
     * Hold onto this Class for later class.getResource() calls (to load
     * resources from JAR files, see getInputStream()) and also to get class
     * name for use in screenshot filenames (see screenShot()).
     * <P>
     * To load files from a jar we need to access a class in the root folder
     * of the installation.  It's not good to use GLApp.class because that
     * class is in the glapp package folder, and the getResource() function will not
     * find model, image and sound files because they're a level higher in
     * the folder tree.  Below we call this.getClass() to record the class of the
     * application that subclasses GLApp, ie. assume we create an app MyGame that
     * extends GLApp, and MyGame.class is in the root folder of the installation:
     * <PRE>
     *      MyGame.class
     *      models (folder)
     *      images (folder)
     *      sounds (folder)
     *  </PRE>
     *  In this case setRootClass() will set the rootClass to MyGame.  If MyGame
     *  and subfolders are packaged in a jar file, then getInputStream() should
     *  be able to do a rootClass.getResource("models/some_model.obj") and correctly
     *  retrieve the file from the JAR.
     *  <P>
     *  @see getInputStream()
     */
    public void setRootClass() {
    	rootClass = this.getClass();
    }

    /**
     * make a time stamp for filename
     * @return a string with format "YYYYMMDD-hhmmss"
     */
    public static String makeTimestamp()
    {
    	Calendar now = Calendar.getInstance();
    	int year = now.get(Calendar.YEAR);
    	int month = now.get(Calendar.MONTH) + 1;
    	int day = now.get(Calendar.DAY_OF_MONTH);
    	int hours = now.get(Calendar.HOUR_OF_DAY);
    	int minutes = now.get(Calendar.MINUTE);
    	int seconds = now.get(Calendar.SECOND);
    	String datetime =  ""
    		+ year
    		+ (month < 10 ? "0" : "") + month
    		+ (day < 10 ? "0" : "") + day
    		+ "-"
    		+ (hours < 10 ? "0" : "") + hours
    		+ (minutes < 10 ? "0" : "") + minutes
    		+ (seconds < 10 ? "0" : "")  + seconds;
    	return datetime;
    }

    /**
     * Return a random floating point value between 0 and 1
     */
    public static float random() {
    	return (float)Math.random();
    }

    /**
     * Return a random floating point value between 0 and upperbound (not including upperbound)
     */
    public static float random(float upperbound) {
    	return (float)(Math.random()*(double)upperbound);
    }

    /**
     * Return a random integer value between 0 and upperbound (not including upperbound)
     */
    public static int random(int upperbound) {
    	return (int)(Math.random()*(double)upperbound);
    }

    /**
     * Round a float value to the nearest int.
     */
    public static int round(float f) {
    	return Math.round(f);
    }

    /**
     * Return true if the OpenGL context supports the given OpenGL extension.
     */
    public static boolean extensionExists(String extensionName) {
    	if (OpenGLextensions == null) {
    		String[] GLExtensions = GL11.glGetString(GL11.GL_EXTENSIONS).split(" ");
    		OpenGLextensions = new Hashtable();
    		for (int i=0; i < GLExtensions.length; i++) {
    			OpenGLextensions.put(GLExtensions[i].toUpperCase(),"");
    		}
    	}
    	return (OpenGLextensions.get(extensionName.toUpperCase()) != null);
    }

    /**
     * Show a debug message on the system console (calls System.out.println()).  If
     * showMessages flag is false, does nothing.
     * @param text
     */
    public static void msg(String text) {
    	if (showMessages) {
    		System.out.println(text);
    	}
    }

    /**
     * Show an error message on the system console (calls System.out.println()).
     * Does not check showMessages flag.
     * @param text
     */
    public static void err(String text) {
    	System.out.println(text);
    }

    /**
     * Find a method in the given class with the given method name.  Assumes the method
     * takes no parameters.  The returned Method can be executed later using invoke()
     * (similar to a callback function in C/C++).
     * <P>
     * NOTE: method invocation is very fast for methods that take no parameters.  If
     * the method takes parameters then invoking is much slower than calling the function
     * directly through code.  For this reason and for simplicity I assume there are
     * no parameters on the function.
     *
     * @param object   object that has the method we want to invoke
     * @param methodName   name of function that we want to invoke
     * @return the Method object
     * @see invoke()
     */
    public static Method method(Object object, String methodName) {
    	Method M = null;
    	try {
    		// Look for a method with the given name and no parameters
    		M = object.getClass().getMethod(methodName, null);
    	} catch (Exception e) {
    		err("GLApp.method(): Can't find method (" +methodName+ ").  " + e);
    	}
    	return M;
    }

    /**
     * Similar to the static method() function, this looks for the method in the
     * GLApp class (or it's subclass).
     *
     * @param methodName   name of function that we want to invoke
     * @return the Method object
     * @see invoke()
     */
    public Method method(String methodName) {
    	return method(this,methodName);
    }

    /**
     * Execute a method on the given object.  Assumes the method
     * takes no parameters. Useful as a callback function.
     *
     * @param object (the object to call the method on)
     * @param method  (the method that will be executed)
     * @see method()
     */
    public static void invoke(Object object, Method method) {
    	if (object != null && method != null){
    		try {
    			// Call the method with this object as the argument!
    			method.invoke(object, null);
    		} catch (Exception e) {
    			// Error handling
    			System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + object.getClass().getName());
    		}
    	}
    }

    /**
     * Similar to the static invoke() function, this execute a method on the
     * GLApp class or subclass.  Assumes the method takes no parameters.
     * Useful as a callback function.
     *
     * @param method  (the method that will be executed)
     * @see method()
     */
    public void invoke(Method method) {
    	if (method != null){
    		try {
    			// Call the method with this object as the argument!
    			method.invoke(this, null);
    		} catch (Exception e) {
    			// Error handling
    			System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + this.getClass().getName());
    		}
    	}
    }
}