Advertisement
Guest User

Untitled

a guest
Mar 10th, 2012
40
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 145.42 KB | None | 0 0
  1. package glapp;
  2.  
  3. import java.util.*;
  4. import java.util.regex.Matcher;
  5. import java.util.regex.Pattern;
  6. import java.lang.reflect.Method;
  7. import java.nio.*;
  8. import java.io.*;
  9. import java.net.URL;
  10. import org.lwjgl.*;
  11. import org.lwjgl.opengl.*;
  12. import org.lwjgl.input.*;
  13. import org.lwjgl.util.glu.*;
  14.  
  15. /**
  16.  * Collection of functions to init and run an OpenGL app using LWJGL.
  17.  * <P>
  18.  * Includes functions to handle:  <BR>
  19.  *        Setup display mode, keyboard, mouse, handle events<BR>
  20.  *        Run main loop of application <BR>
  21.  *        Buffer allocation -- manage IntBuffer, ByteBuffer calls. <BR>
  22.  *        OpenGL functions  -- convert screen/world coords, set modes, lights, etc. <BR>
  23.  *        Utility functions -- load images, convert pixels, getTimeInMillis, etc. <BR>
  24.  * <P>
  25.  * Has a main() function to run as an application, though this class has only
  26.  * minimal placeholder functionality.  It is meant to be subclassed,
  27.  * and the subclass will define setup() draw() mouseMove() functions, etc.
  28.  * <P>
  29.  * GLApp initializes the LWJGL environment for OpenGL rendering,
  30.  * ie. creates a window, sets the display mode, inits mouse and keyboard,
  31.  * then runs a loop.
  32.  * <P>
  33.  * Uses GLImage to load and hold image pixels.
  34.  * <P>
  35.  * napier -at- potatoland -dot- org
  36.  *
  37.  * @see GLImage
  38.  */
  39. public class GLApp {
  40.     // Just for reference
  41.     public static final String GLAPP_VERSION = ".5";
  42.  
  43.     // Byte size of data types: Used when allocating native buffers
  44.     public static final int SIZE_DOUBLE = 8;
  45.     public static final int SIZE_FLOAT = 4;
  46.     public static final int SIZE_INT = 4;
  47.     public static final int SIZE_BYTE = 1;
  48.  
  49.     // Application settings
  50.     // These can be tweaked in main() before calling app.run()
  51.     // to customize app behavior.
  52.     public static int finishedKey = Keyboard.KEY_ESCAPE; // App will exit when this key is hit (set to 0 for no key exit)
  53.     public static String window_title = "OpenGL Window"; // window title, set in initDisplay()
  54.     public static String configFilename = "GLApp.cfg";   // init() calls loadSettings() to load initial settings from this file (OPTIONAL)
  55.     public static boolean hideNativeCursor = false;      // hide the operating system cursor, see hideNativeCursor()
  56.     public static boolean disableNativeCursor = false;   // turn completely off the operating system cursor, see disableNativeCursor()
  57.     public static boolean VSyncEnabled = true;           // if true, synchronize screen updates with video refresh rate
  58.     public static boolean useCurrentDisplay = false;     // when initing display, use the settings of the desktop (whatever the PC was using before app was started)
  59.     public static boolean fullScreen = false;            // full screen or floating window
  60.     public static boolean showMessages = true;           // if true, show debug messages, if false show only error messages (see msg() err())
  61.     public static float aspectRatio = 0;                 // aspect ratio of OpenGL context (if 0, default to displayWidth/displayHeight)
  62.  
  63.     // Display settings (settings in glapp.cfg will override these)
  64.     // initDisplay() will pick a Display that best matches displayWidth,
  65.     // displayHeight, displayColorBits, displayFrequency.  If these values
  66.     // are -1, initDisplay() will use the desktop screen settings.
  67.     public static int displayWidth = -1;
  68.     public static int displayHeight = -1;
  69.     public static int displayColorBits = -1;
  70.     public static int displayFrequency = -1;
  71.     public static int depthBufferBits = 24;             // bits per pixel in the depth buffer
  72.     public static int viewportX, viewportY;             // viewport position (will default to 0,0)
  73.     public static int viewportW, viewportH;             // viewport size (will default to screen width, height)
  74.     //private static int orthoWidth = 0;
  75.     //private static int orthoHeight = 0;
  76.  
  77.     // DisplayMode chosen by initDisplay()
  78.     // DM and displayMode are the same thing.
  79.     public static DisplayMode DM, origDM;               // hold display mode we set, and the display mode when app first executes
  80.     public static DisplayMode displayMode;              // hold display mode we set (same as DM)
  81.  
  82.     // Application variables
  83.     // These are set internally but can be read by the
  84.     // subclass application.
  85.     public static Properties settings=new Properties(); // holds settings from file GLApp.cfg (see loadSettings())
  86.     public static boolean finished;                     // App will exit when finished is true (when finishedKey is hit: see run())
  87.     public static int cursorX, cursorY;                 // mouse position (see handleEvents())
  88.     public static double lastFrameTime = 0;             // ticks since last frame was drawn (see run() and updateTimer())
  89.     public static double secondsSinceLastFrame = 0;     // seconds elapsed since last frame was drawn (see updateTimer())
  90.     public static long ticksPerSecond = 0;              // used to calc time in millis
  91.     public static int frameCount = 0;                   // count frames per sec (just to track performance)
  92.  
  93.     // For copying screen image to a texture
  94.     public static int screenTextureSize = 1024;         // how large should texture be to hold screen (see makeTextureForScreen())
  95.  
  96.     // NIO Buffers to retrieve OpenGL settings.
  97.     // For memory efficiency and performance, instantiate these once, and reuse.
  98.     // see getSetingInt(), getModelviewMatrix(), project(), unProject()
  99.     public static IntBuffer     bufferViewport = allocInts(16);
  100.     public static FloatBuffer   bufferModelviewMatrix = allocFloats(16);
  101.     public static FloatBuffer   bufferProjectionMatrix = allocFloats(16);
  102.     public static FloatBuffer   tmpResult = allocFloats(16);         // temp var to hold project/unproject results
  103.     public static FloatBuffer   tmpFloats = allocFloats(4);          // temp var used by setLightPos(), setFog()
  104.     public static ByteBuffer    tmpFloat = allocBytes(SIZE_FLOAT);   // temp var used by getZDepth()
  105.     public static IntBuffer     tmpInts = allocInts(16);             // temp var used by getSettingInt()
  106.     public static ByteBuffer    tmpByte = allocBytes(SIZE_BYTE);     // temp var used by getStencilValue()
  107.     public static ByteBuffer    tmpInt = allocBytes(GLApp.SIZE_INT); // temp var used by getPixelColor()
  108.  
  109.     // Material colors (see setMaterial())
  110.     public static FloatBuffer mtldiffuse = allocFloats(4);     // color of the lit surface
  111.     public static FloatBuffer mtlambient = allocFloats(4);     // color of the shadowed surface
  112.     public static FloatBuffer mtlspecular = allocFloats(4);    // reflection color (typically this is a shade of gray)
  113.     public static FloatBuffer mtlemissive = allocFloats(4);    // glow color
  114.     public static FloatBuffer mtlshininess = allocFloats(4);   // size of the reflection highlight
  115.  
  116.     // Misc.
  117.     public static float rotation = 0f;       // to rotate cubes (just to put something on screen)
  118.     public static final float PIOVER180 = 0.0174532925f;   // A constant used in navigation: PI/180
  119.     public static final float PIUNDER180 = 57.2957795130f;   // A constant used in navigation: 180/PI;
  120.     static Hashtable OpenGLextensions;       // will be populated by extensionExists()
  121.     static double avgSecsPerFrame=.01;       // to smooth out motion, keep a moving average of frame render times
  122.  
  123.     //========================================================================
  124.     // Run main loop of application.  Handle mouse and keyboard input.
  125.     //
  126.     // The functions main(), run() and init() start and run the application.
  127.     // The run() function starts a loop that handles mouse and keyboard events
  128.     // and calls draw() repeatedly.
  129.     //
  130.     //========================================================================
  131.  
  132.     public static void main(String args[]) {
  133.         GLApp demo = new GLApp();
  134.         demo.run();
  135.     }
  136.  
  137.     /**
  138.      * Runs the application.  Calls init() function to setup OpenGL,
  139.      * input and display.  Runs the main loop of the application, which handles
  140.      * mouse and keyboard input.
  141.      * <P>
  142.      * Calls init(), handleEvents(), update() and draw(). <BR>
  143.      * handleEvents() calls:  mouseMove(), mouseDown(), mouseUp(), keyDown(), keyUp()
  144.      */
  145.     public void run() {
  146.         // hold onto application class in case we need to load images from jar (see getInputStream())
  147.         setRootClass();
  148.         try {
  149.             // Init Display, Keyboard, Mouse, OpenGL, load config file
  150.             init();
  151.             // Main loop
  152.             while (!finished) {
  153.                 if (!Display.isVisible()) {  // window is minimized
  154.                     Thread.sleep(200L);
  155.                 }
  156.                 else if (Display.isCloseRequested()) {  // window X button clicked
  157.                     finished = true;
  158.                 }
  159.                 else {   // yield a little so other threads can work
  160.                     Thread.sleep(1);
  161.                 }
  162.                 updateTimer();      // track when frame was drawn (see secondsSinceLastFrame)
  163.                 handleEvents();     // call key...() and mouse...() functions based on input events
  164.                 update();           // do program logic here (subclass may override this)
  165.                 draw();             // redraw the screen (subclass overrides this)
  166.                 Display.update();
  167.             }
  168.         }
  169.         catch (Exception e) {
  170.             err("GLApp.run(): " + e);
  171.             e.printStackTrace(System.out);
  172.         }
  173.         // prepare to exit
  174.         cleanup();
  175.         System.exit(0);
  176.     }
  177.  
  178.     /**
  179.      * Called only once when app is first started, init() prepares the display,
  180.      * mouse and OpenGL context for use. Override init() only if you want to
  181.      * substantially alter the app startup behavior.  Otherwise just override
  182.      * initGL() to tweak the OpenGL context and setup() to load textures,
  183.      * models, etc..
  184.      */
  185.     public void init()
  186.     {
  187.         // load settings from config file (display size, resolution, etc.)
  188.         loadSettings(configFilename);
  189.         initDisplay();
  190.         initInput();
  191.         initGL();
  192.         setup();        // subclass usually overrides this
  193.         updateTimer();  // Do this once to init time values to something sane, otherwise the first game loop will report a huge secondsElapsedPerFrame
  194.     }
  195.  
  196.     /**
  197.      * Called by the run() loop.  Handles animation and input for each frame.
  198.      */
  199.     public void handleEvents() {
  200.         int mouseDX = Mouse.getDX();
  201.         int mouseDY = Mouse.getDY();
  202.         int mouseDW = Mouse.getDWheel();
  203.         // handle mouse motion
  204.         if (mouseDX != 0 || mouseDY != 0 || mouseDW != 0) {
  205.             cursorX += mouseDX;
  206.             cursorY += mouseDY;
  207.             if (cursorX < 0) {
  208.                 cursorX = 0;
  209.             }
  210.             else if (cursorX > displayMode.getWidth()) {
  211.                 cursorX = displayMode.getWidth();
  212.             }
  213.             if (cursorY < 0) {
  214.                 cursorY = 0;
  215.             }
  216.             else if (cursorY > displayMode.getHeight()) {
  217.                 cursorY = displayMode.getHeight();
  218.             }
  219.             mouseMove(cursorX,cursorY);
  220.             //msg("DX=" + mouseDX + " DY=" + mouseDY + " cursorX=" + cursorX);
  221.         }
  222.         // handle mouse wheel event
  223.         if (mouseDW != 0) {
  224.             mouseWheel(mouseDW);
  225.         }
  226.         // handle mouse clicks
  227.         while ( Mouse.next() ) {
  228.             if(Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == true) {
  229.                 mouseDown(cursorX, cursorY);
  230.             }
  231.             if(Mouse.getEventButton() == 0 && Mouse.getEventButtonState() == false) {
  232.                 mouseUp(cursorX, cursorY);
  233.             }
  234.             if(Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == true) {
  235.                 mouseDown(cursorX, cursorY);
  236.             }
  237.             if(Mouse.getEventButton() == 1 && Mouse.getEventButtonState() == false) {
  238.                 mouseUp(cursorX, cursorY);
  239.             }
  240.         }
  241.         // Handle key hits
  242.         while ( Keyboard.next() )  {
  243.             // check for exit key
  244.             if (Keyboard.getEventKey() == finishedKey) {
  245.                 finished = true;
  246.             }
  247.             // pass key event to handler
  248.             if (Keyboard.getEventKeyState()) {    // key was just pressed, trigger keyDown()
  249.                 keyDown(Keyboard.getEventKey());
  250.             }
  251.             else {
  252.                 keyUp(Keyboard.getEventKey());    // key was released
  253.             }
  254.         }
  255.  
  256.         // Count frames
  257.         frameCount++;
  258.         if ((Sys.getTime()-lastFrameTime) > ticksPerSecond) {
  259.             //msg("==============> FramesPerSec=" + (frameCount/1) + " timeinsecs=" + getTimeInSeconds() + " timeinmillis=" + getTimeInMillis());
  260.             frameCount = 0;
  261.         }
  262.     }
  263.  
  264.     /**
  265.      * Load configuration settings from optional properties file.
  266.      * File format is:<BR>
  267.      * <PRE>
  268.      * # Comment
  269.      * displayWidth=1024
  270.      * displayHeight=768
  271.      * </PRE>
  272.      *
  273.      * @param configFilename
  274.      */
  275.     public void loadSettings(String configFilename)
  276.     {
  277.         if (configFilename == null || configFilename.equals("")) {
  278.             return;
  279.         }
  280.         InputStream configFileIn = getInputStream(configFilename);
  281.         settings = new Properties();
  282.         if (configFileIn == null) {
  283.             msg("GLApp.loadSettings() warning: config file " + configFilename + " not found, will use default settings.");
  284.             return;
  285.         }
  286.         else {
  287.             try { settings.load(configFileIn); }
  288.             catch (Exception e) {
  289.                 msg("GLApp.loadSettings() warning: " + e);
  290.                 return;
  291.             }
  292.         }
  293.         // Debug: show the settings
  294.         settings.list(System.out);
  295.         // Check for available settings
  296.         if (settings.getProperty("displayWidth") != null) {
  297.             displayWidth = Integer.parseInt(settings.getProperty("displayWidth"));
  298.         }
  299.         if (settings.getProperty("displayHeight") != null) {
  300.             displayHeight = Integer.parseInt(settings.getProperty("displayHeight"));
  301.         }
  302.         if (settings.getProperty("displayColorBits") != null) {
  303.             displayColorBits = Integer.parseInt(settings.getProperty("displayColorBits"));
  304.         }
  305.         if (settings.getProperty("displayFrequency") != null) {
  306.             displayFrequency = Integer.parseInt(settings.getProperty("displayFrequency"));
  307.         }
  308.         if (settings.getProperty("depthBufferBits") != null) {
  309.             depthBufferBits = Integer.parseInt(settings.getProperty("depthBufferBits"));
  310.         }
  311.         if (settings.getProperty("aspectRatio") != null) {
  312.             aspectRatio = Float.parseFloat(settings.getProperty("aspectRatio"));
  313.         }
  314.         if (settings.getProperty("fullScreen") != null) {
  315.             fullScreen = settings.getProperty("fullScreen").toUpperCase().equals("YES");
  316.         }
  317.         if (settings.getProperty("useCurrentDisplay") != null) {
  318.             useCurrentDisplay = settings.getProperty("useCurrentDisplay").toUpperCase().equals("YES");
  319.         }
  320.         if (settings.getProperty("finishedKey") != null) {  // key codes are defined in the lwjgl Keyboard class
  321.             finishedKey = Integer.parseInt(settings.getProperty("finishedKey"));
  322.         }
  323.         if (settings.getProperty("window_title") != null) {
  324.             window_title = settings.getProperty("window_title");
  325.         }
  326.         if (settings.getProperty("VSyncEnabled") != null) {
  327.             VSyncEnabled = settings.getProperty("VSyncEnabled").toUpperCase().equals("YES");
  328.         }
  329.     }
  330.  
  331.     //========================================================================
  332.     // Setup display mode
  333.     //
  334.     // Initialize Display, Mouse, Keyboard.
  335.     //
  336.     //========================================================================
  337.  
  338.     /**
  339.      * Initialize the Display mode, viewport size, and open a Window.
  340.      * By default the window is fullscreen, the viewport is the same dimensions
  341.      * as the window.
  342.      */
  343.     public boolean initDisplay() {
  344.         origDM = Display.getDisplayMode();  // current display settings
  345.         msg("GLApp.initDisplay(): Current display mode is " + origDM);
  346.         // for display properties that have not been specified, default to current display value
  347.         if (displayHeight == -1) displayHeight = origDM.getHeight();
  348.         if (displayWidth == -1) displayWidth = origDM.getWidth();
  349.         if (displayColorBits == -1) displayColorBits = origDM.getBitsPerPixel();
  350.         if (displayFrequency == -1) displayFrequency = origDM.getFrequency();
  351.         // Set display mode
  352.         try {
  353.             if (useCurrentDisplay) {  
  354.                 // use current display settings (ignore properties file)
  355.                 DM = origDM;
  356.             }
  357.             else {
  358.                 // find a display mode that matches the specified settings (or use a sane alternative)
  359.                 if ( (DM = getDisplayMode(displayWidth, displayHeight, displayColorBits, displayFrequency)) == null) {
  360.                     if ( (DM = getDisplayMode(1024, 768, 32, 60)) == null) {
  361.                         if ( (DM = getDisplayMode(1024, 768, 16, 60)) == null) {
  362.                             if ( (DM = getDisplayMode(origDM.getWidth(), origDM.getHeight(), origDM.getBitsPerPixel(), origDM.getFrequency())) == null) {
  363.                                 err("GLApp.initDisplay(): Can't find a compatible Display Mode!!!");
  364.                             }
  365.                         }
  366.                     }
  367.                 }
  368.             }
  369.             msg("GLApp.initDisplay(): Setting display mode to " + DM + " with pixel depth = " + depthBufferBits);
  370.             Display.setDisplayMode(DM);
  371.             displayMode = DM;
  372.             displayWidth = DM.getWidth();
  373.             displayHeight = DM.getHeight();
  374.             displayColorBits = DM.getBitsPerPixel();
  375.             displayFrequency = DM.getFrequency();
  376.         }
  377.         catch (Exception exception) {
  378.             System.err.println("GLApp.initDisplay(): Failed to create display: " + exception);
  379.             System.exit(1);  //!!!!new
  380.         }
  381.         // Initialize the Window
  382.         try {
  383.             Display.create(new PixelFormat(0, depthBufferBits, 8));  // set bits per buffer: alpha, depth, stencil
  384.             Display.setTitle(window_title);
  385.             Display.setFullscreen(fullScreen);
  386.             Display.setVSyncEnabled(VSyncEnabled);
  387.             //msg("GLApp.initDisplay(): Created OpenGL window.");
  388.         }
  389.         catch (Exception exception1) {
  390.             System.err.println("GLApp.initDisplay(): Failed to create OpenGL window: " + exception1);
  391.             System.exit(1);
  392.         }
  393.         // Set viewport width/height and Aspect ratio.
  394.         if (aspectRatio == 0f) {
  395.             // no aspect ratio was set in GLApp.cfg: default to full screen.
  396.             aspectRatio = (float)DM.getWidth() / (float)DM.getHeight(); // calc aspect ratio of display
  397.         }
  398.         // viewport may not match shape of screen.  Adjust to fit.
  399.         viewportH = DM.getHeight();                        // viewport Height
  400.         viewportW = (int) (DM.getHeight() * aspectRatio);  // Width
  401.         if (viewportW > DM.getWidth()) {
  402.             viewportW = DM.getWidth();
  403.             viewportH = (int) (DM.getWidth() * (1 / aspectRatio));
  404.         }
  405.         // center viewport in screen
  406.         viewportX = (int) ((DM.getWidth()-viewportW) / 2);
  407.         viewportY = (int) ((DM.getHeight()-viewportH) / 2);
  408.         return true;
  409.     }
  410.  
  411.     /**
  412.      * Retrieve a DisplayMode object with the given params
  413.      */
  414.     public static DisplayMode getDisplayMode(int w, int h, int colorBits, int freq) {
  415.          try {
  416.              DisplayMode allDisplayModes[] = Display.getAvailableDisplayModes();
  417.              DisplayMode dm = null;
  418.              for (int j = 0; j < allDisplayModes.length; j++) {
  419.                  dm = allDisplayModes[j];
  420.                  if (dm.getWidth() == w && dm.getHeight() == h && dm.getBitsPerPixel() == colorBits &&
  421.                      dm.getFrequency() == freq) {
  422.                      return dm;
  423.                  }
  424.              }
  425.          }
  426.          catch (LWJGLException lwjgle) {
  427.              err("GLApp.getDisplayMode() error:" + lwjgle);
  428.          }
  429.         return null;
  430.     }
  431.  
  432.     /**
  433.      * Initialize the Keyboard and Mouse.
  434.      * <P>
  435.      * Disable or hide the native cursor.  Set the initial cursor position.  Get
  436.      * the timer resolution (ticks per second).
  437.      *
  438.      * @see handleEvents()
  439.      */
  440.     public void initInput() {
  441.         try {
  442.             // init keyboard
  443.             Keyboard.create();
  444.  
  445.             // Turn off native cursor?
  446.             if (disableNativeCursor) {
  447.                 // Mouse.setGrabbed(true) will turn off the native cursor
  448.                 disableNativeCursor(true);
  449.                 // set initial cursor pos to center screen
  450.                 cursorX = (int) DM.getWidth() / 2;
  451.                 cursorY = (int) DM.getHeight() / 2;
  452.             }
  453.  
  454.             // Hide native cursor when inside application window?
  455.             if (hideNativeCursor) {
  456.                 hideNativeCursor(true);
  457.             }
  458.  
  459.             // Init hi-res timer (see time functions)
  460.             ticksPerSecond = Sys.getTimerResolution();
  461.         }
  462.         catch (Exception e) {
  463.             err("GLApp.initInput(): " + e);
  464.         }
  465.     }
  466.  
  467.  
  468.     //========================================================================
  469.     // Custom Application functionality: can be overriden by subclass.
  470.     //
  471.     // Functions to initialize OpenGL, set the viewing mode, render the scene,
  472.     // respond to mouse actions, and initialize the app.  These functions
  473.     // are overridden in the subclass to create custom behavior.
  474.     //
  475.     //========================================================================
  476.  
  477.     /**
  478.      * Initialize the OpenGL context.  The subclass can override this function
  479.      * to customize the OpenGL settings.  This function is called by init()
  480.      * once when app starts, but may be called again to restore settings to a
  481.      * default state, or to initialize a PBuffer object to the exact same
  482.      * state as the display.
  483.      */
  484.     public void initGL() {
  485.         try {
  486.             // Depth test setup
  487.             GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
  488.             GL11.glDepthFunc(GL11.GL_LEQUAL);  // The Type Of Depth Testing To Do
  489.  
  490.             // Some basic settings
  491.             GL11.glClearColor(0f, 0f, 0f, 1f); // Black Background
  492.             GL11.glEnable(GL11.GL_NORMALIZE);  // force normal lengths to 1
  493.             GL11.glEnable(GL11.GL_CULL_FACE);  // don't render hidden faces
  494.             GL11.glEnable(GL11.GL_TEXTURE_2D); // use textures
  495.             GL11.glEnable(GL11.GL_BLEND);      // enable transparency
  496.  
  497.             // How to handle transparency: average colors together
  498.             GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
  499.  
  500.             // Enable alpha test so the transparent backgrounds in texture images don't draw.
  501.             // This prevents transparent areas from affecting the depth or stencil buffer.
  502.             // alpha func will accept only fragments with alpha greater than 0
  503.             GL11.glEnable(GL11.GL_ALPHA_TEST);
  504.             GL11.glAlphaFunc(GL11.GL_GREATER, 0f);
  505.  
  506.             // Draw specular highlghts on top of textures
  507.             GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SEPARATE_SPECULAR_COLOR );
  508.  
  509.             // Perspective quality
  510.             GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
  511.  
  512.             // Set the size and shape of the screen area
  513.             GL11.glViewport(viewportX, viewportY, viewportW, viewportH);
  514.  
  515.             // setup perspective (see setOrtho() for 2D)
  516.             setPerspective();
  517.  
  518.             // select model view for subsequent transformations
  519.             GL11.glMatrixMode(GL11.GL_MODELVIEW);
  520.             GL11.glLoadIdentity();
  521.         }
  522.         catch (Exception e) {
  523.             err("GLApp.initOpenGL(): " + e);
  524.         }
  525.     }
  526.  
  527.     /**
  528.      *  Setup can be overridden by the subclass to initialize the application
  529.      *  ie load textures, models, and create data structures used by the app.
  530.      *
  531.      *  This function is called only once, when application starts.  It is
  532.      *  called after initDisplay and initOpenGL(), so the OpenGL context is
  533.      *  already created.
  534.      *
  535.      *  @see init()
  536.      */
  537.     public void setup() {
  538.     }
  539.  
  540.     /**
  541.      *  Update can be overridden by the subclass
  542.      *
  543.      *  @see run()
  544.      */
  545.     public void update() {
  546.     }
  547.  
  548.     /**
  549.      * Called by run() to draw one frame.  Subclass will override this.
  550.      * This is an alias function just to be follow Processing and OpenFrameworks
  551.      * function naming conventions.
  552.      */
  553.     public void draw() {
  554.         render();
  555.     }
  556.  
  557.     /**
  558.      * Same as draw().  Subclass can override render() instead of draw().
  559.      * Same thing, just a matter of taste.
  560.      */
  561.     public void render() {
  562.         // rotate 90 degrees per second
  563.         rotation += secondsSinceLastFrame * 90f;
  564.  
  565.         // clear depth buffer and color
  566.         GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);
  567.  
  568.         // select model view for subsequent transforms
  569.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  570.         GL11.glLoadIdentity();
  571.  
  572.         // set viewpoint 10 units from origin, looking at origin
  573.         GLU.gluLookAt(0,0,10, 0,0,0, 0,1,0);
  574.  
  575.         // rotate, scale and draw cube
  576.         GL11.glPushMatrix();
  577.         {
  578.             GL11.glRotatef(rotation, 0, 1, 0);
  579.             GL11.glColor4f(0f, .5f, 1f, 1f);
  580.             renderCube();
  581.         }
  582.         GL11.glPopMatrix();
  583.  
  584.         // draw another overlapping cube
  585.         GL11.glPushMatrix();
  586.         {
  587.             GL11.glRotatef(rotation, 1, 1, 1);
  588.             GL11.glColor4f(.7f, .5f, 0f, 1f);
  589.             renderCube();
  590.         }
  591.         GL11.glPopMatrix();
  592.     }
  593.  
  594.     /**
  595.      * Run() calls this right before exit. Free up allocated resources (display lists)
  596.      * and gracefully shut down OpenGL context.
  597.      */
  598.     public void cleanup() {
  599.         destroyFont();
  600.         destroyDisplayLists();
  601.         Keyboard.destroy();
  602.         Display.destroy();  // will call Mouse.destroy()
  603.     }
  604.  
  605.     /**
  606.      * Shutdown the application.  This will call cleanup() before exiting from the
  607.      * application.
  608.      * @see cleanup()
  609.      */
  610.     public void exit() {
  611.         finished = true;
  612.     }
  613.  
  614.     //========================================================================
  615.     // Mouse events, called by handleEvents()
  616.     //========================================================================
  617.  
  618.     /**
  619.      * Called by handleEvents() when mouse moves
  620.      */
  621.     public void mouseMove(int x, int y) {
  622.     }
  623.  
  624.     public void mouseDown(int x, int y) {
  625.     }
  626.  
  627.     public void mouseUp(int x, int y) {
  628.     }
  629.  
  630.     public void mouseWheel(int wheelMoved) {
  631.     }
  632.  
  633.     /**
  634.      * Return true if the given mousebutton is down.  Typically mouse buttons
  635.      * are 0=left, 1=right.  This function can be called inside mouse events such
  636.      * as mouseDown() and mouseMove() to see which button is activated.
  637.      * @param whichButton  number of mouse button (0=left button)
  638.      */
  639.     public boolean mouseButtonDown(int whichButton) {
  640.         return Mouse.isButtonDown(whichButton);
  641.     }
  642.  
  643.     /**
  644.      * Called when key is pressed.  Keycode will be the key ID value as
  645.      * defined in the LWJGL Keyboard class.
  646.      *
  647.      * @see Keyboard class in the LWJGL documentation
  648.      * @param keycode
  649.      */
  650.     public void keyDown(int keycode) {
  651.     }
  652.  
  653.     /**
  654.      * Called when key is released.  Keycode will be the key ID value as
  655.      * defined in the LWJGL Keyboard class.
  656.      *
  657.      * @see Keyboard class in the LWJGL documentation
  658.      * @param keycode
  659.      */
  660.     public void keyUp(int keycode) {
  661.     }
  662.  
  663.     /**
  664.      * Return the character associatated with the last key event.  When
  665.      * called inside keyDown() or keyUp() this function will return the
  666.      * character equivalent of the keycode that was passed to keyDown()
  667.      * or keyUp().
  668.      */
  669.     public char keyChar() {
  670.         return Keyboard.getEventCharacter();
  671.     }
  672.  
  673.     //========================================================================
  674.     // functions to get values from a Properties object.  Properties can be
  675.     // loaded from a text file containing name=value pairs.
  676.     //========================================================================
  677.  
  678.     /**
  679.      * Load configuration settings from a properties file.
  680.      * File format is:<BR>
  681.      * <PRE>
  682.      * # Comment
  683.      * displayWidth=1024
  684.      * displayHeight=768
  685.      * </PRE>
  686.      *
  687.      * @param configFilename
  688.      */
  689.     public static Properties loadPropertiesFile(String configFilename)
  690.     {
  691.         Properties props = new Properties();
  692.         try { settings.load(getInputStream(configFilename)); }
  693.         catch (Exception e) {
  694.             msg("GLApp.loadPropertiesFile() warning: " + e);
  695.             return null;
  696.         }
  697.         return props;
  698.     }
  699.  
  700.     public static String getProperty(Properties props, String propName) {
  701.         String s = null;
  702.         if (propName != null && propName.length() > 0) {
  703.             s = props.getProperty(propName);
  704.         }
  705.         return s;
  706.     }
  707.  
  708.     public static int getPropertyInt(Properties props, String propName) {
  709.         String s = getProperty(props,propName);
  710.         int v = -1;
  711.         if (s != null) {
  712.             v = Integer.parseInt(s);
  713.         }
  714.         return v;
  715.     }
  716.  
  717.     public static float getPropertyFloat(Properties props, String propName) {
  718.         String s = getProperty(props,propName);
  719.         float v = -1f;
  720.         if (s != null) {
  721.             v = Float.parseFloat(s);
  722.         }
  723.         return v;
  724.     }
  725.  
  726.     public static boolean getPropertyBool(Properties props, String propName) {
  727.         String s = getProperty(props,propName);
  728.         boolean v = false;
  729.         if (s != null) {
  730.             v = (s.toUpperCase().equals("YES") || s.toUpperCase().equals("TRUE") || s.equals("1"));
  731.         }
  732.         return v;
  733.     }
  734.  
  735.     /**
  736.      * Return a property from the application configuration file (the filename given
  737.      * in the configFilename variable).  This file is optional, so properties may be empty.
  738.      * @see loadSettings()
  739.      */
  740.     public static String getProperty(String propertyName) {
  741.         return settings.getProperty(propertyName);
  742.     }
  743.  
  744.     /**
  745.      * return the width of the Viewport (the screen area that OpenGL will draw into).
  746.      * By default the viewport is the same size as the Display (see getWidthWindow()),
  747.      * however the setViewport() function can set the viewport to a sub-region of the screen.
  748.      * <P>
  749.      * This function is only valid after app is running and Display has been initialized.
  750.      *
  751.      * @see setViewport(int,int,int,int)
  752.      */
  753.     public static int getWidth() {
  754.         return viewportW;
  755.     }
  756.  
  757.     /**
  758.      * return the height of the Viewport (the screen area that OpenGL will draw into).
  759.      * By default the viewport is the same size as the Display (see getHeightWindow()),
  760.      * however the setViewport() function can set the viewport to a sub-region of the screen.
  761.      * <P>
  762.      * This function is only valid after app is running and Display has been initialized.
  763.      *
  764.      * @see setViewport(int,int,int,int)
  765.      */
  766.     public static int getHeight() {
  767.         return viewportH;
  768.     }
  769.  
  770.     /**
  771.      * return the Display width (the width of the full window).  Only valid after app
  772.      * is running and Display has been initialized.
  773.      */
  774.     public static int getWidthWindow() {
  775.         return displayWidth;
  776.     }
  777.  
  778.     /**
  779.      * return the Display height (the height of the full window).  Only valid after
  780.      * app is running and Display has been initialized.
  781.      */
  782.     public static int getHeightWindow() {
  783.         return displayHeight;
  784.     }
  785.  
  786.     //========================================================================
  787.     // functions to set basic application behavior
  788.     //========================================================================
  789.  
  790.     /**
  791.      * Set the background color of the screen.  The red,green,blue
  792.      * color components are floats in the range 0-1.  Black is 0,0,0
  793.      * and white is 1,1,1.  Color will take effect the next time the
  794.      * screen is cleared.
  795.      */
  796.     public static void setBackgroundColor(float R, float G, float B) {
  797.         GL11.glClearColor(R,G,B,1);
  798.     }
  799.  
  800.     /**
  801.      * If the param is true, turn off the hardware cursor.  The application can
  802.      * decide how to respond to mouse events and whether to draw a position indicator on
  803.      * screen or not.  If running in a window (not fullscreen), there will be no hardware
  804.      * cursor visible and the user cannot move or click outside the window area.
  805.      * <P>
  806.      * If the param is false, the hardware cursor will behave normally.  Use
  807.      * hideHardwareCursor() to show or hide the hardware cursor.
  808.      *
  809.      * @see hideHardwareCursor()
  810.      */
  811.     public static void disableNativeCursor(boolean off) {
  812.         disableNativeCursor = off;
  813.         Mouse.setGrabbed(off);
  814.     }
  815.  
  816.     /**
  817.      *  If param is true, make the native cursor transparent.  Cursor will be hidden
  818.      *  in the window area, but will be visible outside the window (assuming you're not in
  819.      *  fullscreen mode).  I also used this approach with touch screens because the touch
  820.      *  screen drivers needed to read the hardware mouse position, so I
  821.      *  couldn't disable the hardware cursor, but I wanted to hide it.
  822.      *  <P>
  823.      *  If param is false, reset the cursor to the default.
  824.      *
  825.      *  @see disableHardwareCursor()
  826.      */
  827.     public static void hideNativeCursor(boolean hide) {
  828.         hideNativeCursor = hide;
  829.         if ( (Cursor.getCapabilities() & Cursor.CURSOR_ONE_BIT_TRANSPARENCY) == 0) {
  830.             err("GLApp.hideHardwareCursor(): No hardwared cursor support!");
  831.             return;
  832.         }
  833.         try {
  834.             if (hide) {
  835.                 Cursor cursor = null;
  836.                 int cursorImageCount = 1;
  837.                 int cursorWidth = Cursor.getMaxCursorSize();
  838.                 int cursorHeight = cursorWidth;
  839.                 IntBuffer cursorImages;
  840.                 IntBuffer cursorDelays = null;
  841.                 // Create a single cursor, completely transparent
  842.                 cursorImages = ByteBuffer.allocateDirect(cursorWidth * cursorHeight * cursorImageCount * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
  843.                 for (int j = 0; j < cursorWidth; j++) {
  844.                     for (int l = 0; l < cursorHeight; l++) {
  845.                         cursorImages.put(0x00000000);
  846.                     }
  847.                 }
  848.                 cursorImages.flip();
  849.                 cursor = new Cursor(Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize(), Cursor.getMaxCursorSize() / 2, Cursor.getMaxCursorSize() / 2, cursorImageCount, cursorImages, cursorDelays);
  850.                 // turn it on
  851.                 Mouse.setNativeCursor(cursor);
  852.             }
  853.             else {
  854.                 Mouse.setNativeCursor(null);
  855.             }
  856.         }
  857.         catch (Exception e) {
  858.             err("GLApp.hideHardwareCursor(): error " + e);
  859.         }
  860.     }
  861.  
  862.     /**
  863.      * Set the cursorX,cursorY position.  This will set the screen position of the native
  864.      * cursor also, unless hideCursor() was called.
  865.      *
  866.      * @param screenX
  867.      * @param screenY
  868.      */
  869.     public static void setCursorPosition(int screenX, int screenY) {
  870.         Mouse.setCursorPosition(screenX,screenY);
  871.         cursorX = screenX;
  872.         cursorY = screenY;
  873.     }
  874.  
  875.     //========================================================================
  876.     // Matrix functions: get settings, get matrices, convert
  877.     // screen to world coords.
  878.     //========================================================================
  879.  
  880.     /**
  881.      * retrieve a setting from OpenGL (calls glGetInteger())
  882.      * @param whichSetting  the id number of the value to be returned (same constants as for glGetInteger())
  883.      */
  884.     public static int getSettingInt(int whichSetting)
  885.     {
  886.         tmpInts.clear();
  887.         GL11.glGetInteger(whichSetting, tmpInts);
  888.         return tmpInt.get(0);
  889.     }
  890.  
  891.     public static FloatBuffer getModelviewMatrix()
  892.     {
  893.         bufferModelviewMatrix.clear();
  894.         GL11.glGetFloat(GL11.GL_MODELVIEW_MATRIX, bufferModelviewMatrix);
  895.         return bufferModelviewMatrix;
  896.     }
  897.  
  898.     public static FloatBuffer getProjectionMatrix()
  899.     {
  900.         bufferProjectionMatrix.clear();
  901.         GL11.glGetFloat(GL11.GL_PROJECTION_MATRIX, bufferProjectionMatrix);
  902.         return bufferProjectionMatrix;
  903.     }
  904.  
  905.     public static IntBuffer getViewport()
  906.     {
  907.         bufferViewport.clear();
  908.         GL11.glGetInteger(GL11.GL_VIEWPORT, bufferViewport);
  909.         return bufferViewport;
  910.     }
  911.  
  912.     /**
  913.      * Convert a FloatBuffer matrix to a 4x4 float array.
  914.      * @param fb   FloatBuffer containing 16 values of 4x4 matrix
  915.      * @return     2 dimensional float array
  916.      */
  917.     public static float[][] getMatrixAsArray(FloatBuffer fb) {
  918.         float[][] fa = new float[4][4];
  919.         fa[0][0] = fb.get();
  920.         fa[0][1] = fb.get();
  921.         fa[0][2] = fb.get();
  922.         fa[0][3] = fb.get();
  923.         fa[1][0] = fb.get();
  924.         fa[1][1] = fb.get();
  925.         fa[1][2] = fb.get();
  926.         fa[1][3] = fb.get();
  927.         fa[2][0] = fb.get();
  928.         fa[2][1] = fb.get();
  929.         fa[2][2] = fb.get();
  930.         fa[2][3] = fb.get();
  931.         fa[3][0] = fb.get();
  932.         fa[3][1] = fb.get();
  933.         fa[3][2] = fb.get();
  934.         fa[3][3] = fb.get();
  935.         return fa;
  936.     }
  937.  
  938.     /**
  939.      * Return the Z depth of the single pixel at the given screen position.
  940.      */
  941.     public static float getZDepth(int x, int y)
  942.     {
  943.         tmpFloat.clear();
  944.         GL11.glReadPixels(x, y, 1, 1, GL11.GL_DEPTH_COMPONENT, GL11.GL_FLOAT, tmpFloat);
  945.         return ( (float) (tmpFloat.getFloat(0)));
  946.     }
  947.  
  948.     /**
  949.      * Find the Z depth of the origin in the projected world view. Used by getWorldCoordsAtScreen()
  950.      * Projection matrix  must be active for this to return correct results (GL.glMatrixMode(GL.GL_PROJECTION)).
  951.      * For some reason I have to chop this to four decimals or I get bizarre
  952.      * results when I use the value in project().
  953.      */
  954.     public static float getZDepthAtOrigin()
  955.     {
  956.         float[] resultf = new float[3];
  957.         project( 0, 0, 0, resultf);
  958.         return ((int)(resultf[2] * 10000F)) / 10000f;  // truncate to 4 decimals
  959.     }
  960.  
  961.     /**
  962.      * Return screen coordinates for a given point in world space.  The world
  963.      * point xyz is 'projected' into screen coordinates using the current model
  964.      * and projection matrices, and the current viewport settings.
  965.      *
  966.      * @param x         world coordinates
  967.      * @param y
  968.      * @param z
  969.      * @param resultf    the screen coordinate as an array of 3 floats
  970.      */
  971.     public static void project(float x, float y, float z, float[] resultf)
  972.     {
  973.         // lwjgl 2.0 altered params for GLU funcs
  974.         GLU.gluProject( x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
  975.         resultf[0] = tmpResult.get(0);
  976.         resultf[1] = tmpResult.get(1);
  977.         resultf[2] = tmpResult.get(2);
  978.     }
  979.  
  980.     /**
  981.      * Return world coordinates for a given point on the screen.  The screen
  982.      * point xyz is 'un-projected' back into world coordinates using the
  983.      * current model and projection matrices, and the current viewport settings.
  984.      *
  985.      * @param x         screen x position
  986.      * @param y         screen y position
  987.      * @param z         z-buffer depth position
  988.      * @param resultf   the world coordinate as an array of 3 floats
  989.      * @see             getWorldCoordsAtScreen()
  990.      */
  991.     public static void unProject(float x, float y, float z, float[] resultf)
  992.     {
  993.         GLU.gluUnProject( x, y, z, getModelviewMatrix(), getProjectionMatrix(), getViewport(), tmpResult);
  994.         resultf[0] = tmpResult.get(0);
  995.         resultf[1] = tmpResult.get(1);
  996.         resultf[2] = tmpResult.get(2);
  997.     }
  998.  
  999.     /**
  1000.      * For given screen xy, return the world xyz coords in a float array.  Assume
  1001.      * Z position is 0.
  1002.      */
  1003.     public static float[] getWorldCoordsAtScreen(int x, int y) {
  1004.         float z = getZDepthAtOrigin();
  1005.         float[] resultf = new float[3];
  1006.         unProject( (float)x, (float)y, (float)z, resultf);
  1007.         return resultf;
  1008.     }
  1009.  
  1010.     /**
  1011.      * For given screen xy and z depth, return the world xyz coords in a float array.
  1012.      */
  1013.     public static float[] getWorldCoordsAtScreen(int x, int y, float z) {
  1014.         float[] resultf = new float[3];
  1015.         unProject( (float)x, (float)y, (float)z, resultf);
  1016.         return resultf;
  1017.     }
  1018.  
  1019.     //========================================================================
  1020.     // Texture functions
  1021.     //========================================================================
  1022.  
  1023.     /**
  1024.      * Allocate a texture (glGenTextures) and return the handle to it.
  1025.      */
  1026.     public static int allocateTexture()
  1027.     {
  1028.         IntBuffer textureHandle = allocInts(1);
  1029.         GL11.glGenTextures(textureHandle);
  1030.         return textureHandle.get(0);
  1031.     }
  1032.  
  1033.     /**
  1034.      * "Select" the given texture for further texture operations.
  1035.      */
  1036.     public static void activateTexture(int textureHandle)
  1037.     {
  1038.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  1039.     }
  1040.  
  1041.     /**
  1042.      * Create a texture and mipmap from the given image file.
  1043.      */
  1044.     public static int makeTexture(String textureImagePath)
  1045.     {
  1046.         int textureHandle = 0;
  1047.         GLImage textureImg = loadImage(textureImagePath);
  1048.         if (textureImg != null) {
  1049.             textureHandle = makeTexture(textureImg);
  1050.             makeTextureMipMap(textureHandle,textureImg);
  1051.         }
  1052.         return textureHandle;
  1053.     }
  1054.  
  1055.     /**
  1056.      * Create a texture and optional mipmap with the given parameters.
  1057.      *
  1058.      * @param mipmap: if true, create mipmaps for the texture
  1059.      * @param anisotropic: if true, enable anisotropic filtering
  1060.      */
  1061.     public static int makeTexture(String textureImagePath, boolean mipmap, boolean anisotropic)
  1062.     {
  1063.         int textureHandle = 0;
  1064.         GLImage textureImg = loadImage(textureImagePath);
  1065.         if (textureImg != null) {
  1066.             textureHandle = makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, anisotropic);
  1067.             if (mipmap) {
  1068.                 makeTextureMipMap(textureHandle,textureImg);
  1069.             }
  1070.         }
  1071.         return textureHandle;
  1072.     }
  1073.  
  1074.     /**
  1075.      * Create a texture from the given image.
  1076.      */
  1077.     public static int makeTexture(GLImage textureImg)
  1078.     {
  1079.         if ( textureImg != null ) {
  1080.             if (isPowerOf2(textureImg.w) && isPowerOf2(textureImg.h)) {
  1081.                 return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
  1082.             }
  1083.             else {
  1084.                 msg("GLApp.makeTexture(GLImage) Warning: not a power of two: " + textureImg.w + "," + textureImg.h);
  1085.                 textureImg.convertToPowerOf2();
  1086.                 return makeTexture(textureImg.pixelBuffer, textureImg.w, textureImg.h, false);
  1087.             }
  1088.         }
  1089.         return 0;
  1090.     }
  1091.  
  1092.     /**
  1093.      * De-allocate the given texture (glDeleteTextures()).
  1094.      */
  1095.     public static void deleteTexture(int textureHandle)
  1096.     {
  1097.         IntBuffer bufferTxtr = allocInts(1).put(textureHandle);;
  1098.         GL11.glDeleteTextures(bufferTxtr);
  1099.     }
  1100.  
  1101.     /**
  1102.      *  Returns true if n is a power of 2.  If n is 0 return zero.
  1103.      */
  1104.     public static boolean isPowerOf2(int n) {
  1105.         if (n == 0) { return false; }
  1106.         return (n & (n - 1)) == 0;
  1107.     }
  1108.  
  1109.     /**
  1110.      * Create a blank square texture with the given size.
  1111.      * @return  the texture handle
  1112.      */
  1113.     public static int makeTexture(int w)
  1114.     {
  1115.         ByteBuffer pixels = allocBytes(w*w*SIZE_INT);  // allocate 4 bytes per pixel
  1116.         return makeTexture(pixels, w, w, false);
  1117.     }
  1118.  
  1119.     /**
  1120.      * Create a texture from the given pixels in the default Java ARGB int format.<BR>
  1121.      * Configure the texture to repeat in both directions and use LINEAR for magnification.
  1122.      * <P>
  1123.      * @return  the texture handle
  1124.      */
  1125.     public static int makeTexture(int[] pixelsARGB, int w, int h, boolean anisotropic)
  1126.     {
  1127.         if (pixelsARGB != null) {
  1128.             ByteBuffer pixelsRGBA = GLImage.convertImagePixelsRGBA(pixelsARGB,w,h,true);
  1129.             return makeTexture(pixelsRGBA, w, h, anisotropic);
  1130.         }
  1131.         return 0;
  1132.     }
  1133.  
  1134.     /**
  1135.      * Create a texture from the given pixels in the default OpenGL RGBA pixel format.
  1136.      * Configure the texture to repeat in both directions and use LINEAR for magnification.
  1137.      * <P>
  1138.      * @return  the texture handle
  1139.      */
  1140.     public static int makeTexture(ByteBuffer pixels, int w, int h, boolean anisotropic)
  1141.     {
  1142.         // get a new empty texture
  1143.         int textureHandle = allocateTexture();
  1144.         // preserve currently bound texture, so glBindTexture() below won't affect anything)
  1145.         GL11.glPushAttrib(GL11.GL_TEXTURE_BIT);
  1146.         // 'select' the new texture by it's handle
  1147.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  1148.         // set texture parameters
  1149.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
  1150.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
  1151.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
  1152.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
  1153.  
  1154.         // make texture "anisotropic" so it will minify more gracefully
  1155.         if (anisotropic && extensionExists("GL_EXT_texture_filter_anisotropic")) {
  1156.             // Due to LWJGL buffer check, you can't use smaller sized buffers (min_size = 16 for glGetFloat()).
  1157.             FloatBuffer max_a = allocFloats(16);
  1158.             // Grab the maximum anisotropic filter.
  1159.             GL11.glGetFloat(EXTTextureFilterAnisotropic.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, max_a);
  1160.             // Set up the anisotropic filter.
  1161.             GL11.glTexParameterf(GL11.GL_TEXTURE_2D, EXTTextureFilterAnisotropic.GL_TEXTURE_MAX_ANISOTROPY_EXT, max_a.get(0));
  1162.         }
  1163.  
  1164.         // Create the texture from pixels
  1165.         GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
  1166.                 0,                      // level of detail
  1167.                 GL11.GL_RGBA8,          // internal format for texture is RGB with Alpha
  1168.                 w, h,                   // size of texture image
  1169.                 0,                      // no border
  1170.                 GL11.GL_RGBA,           // incoming pixel format: 4 bytes in RGBA order
  1171.                 GL11.GL_UNSIGNED_BYTE// incoming pixel data type: unsigned bytes
  1172.                 pixels);                // incoming pixels
  1173.  
  1174.         // restore previous texture settings
  1175.         GL11.glPopAttrib();
  1176.  
  1177.         return textureHandle;
  1178.     }
  1179.  
  1180.     /**
  1181.      * Create a texture from the given pixels in ARGB format.  The pixels buffer contains
  1182.      * 4 bytes per pixel, in ARGB order.  ByteBuffers are created with native hardware byte
  1183.      * orders, so the pixels can be in big-endian (ARGB) order, or little-endian (BGRA) order.
  1184.      * Set the pixel_byte_order accordingly when creating the texture.
  1185.      * <P>
  1186.      * Configure the texture to repeat in both directions and use LINEAR for magnification.
  1187.      * <P>
  1188.      * NOTE: I'm having problems creating mipmaps when image pixel data is in GL_BGRA format.
  1189.      * Looks like GLU type param doesn't recognize GL_UNSIGNED_INT_8_8_8_8 and
  1190.      * GL_UNSIGNED_INT_8_8_8_8_REV so I can't specify endian byte order.  Mipmaps look
  1191.      * right on PC but colors are reversed on Mac.  Have to stick with GL_RGBA
  1192.      * byte order for now.
  1193.      * <P>
  1194.      * @return  the texture handle
  1195.      */
  1196.     public static int makeTextureARGB(ByteBuffer pixels, int w, int h)
  1197.     {
  1198.         // byte buffer has ARGB ints in little endian or big endian byte order
  1199.         int pixel_byte_order = (pixels.order() == ByteOrder.BIG_ENDIAN)?
  1200.                 GL12.GL_UNSIGNED_INT_8_8_8_8 :
  1201.                 GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
  1202.         // get a new empty texture
  1203.         int textureHandle = allocateTexture();
  1204.         // 'select' the new texture by it's handle
  1205.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  1206.         // set texture parameters
  1207.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
  1208.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
  1209.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
  1210.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); //GL11.GL_NEAREST);
  1211.         // Create the texture from pixels
  1212.         GL11.glTexImage2D(GL11.GL_TEXTURE_2D,
  1213.                 0,                      // level of detail
  1214.                 GL11.GL_RGBA8,          // internal format for texture is RGB with Alpha
  1215.                 w, h,                   // size of texture image
  1216.                 0,                      // no border
  1217.                 GL12.GL_BGRA,           // incoming pixel format: 4 bytes in ARGB order
  1218.                 pixel_byte_order,       // incoming pixel data type: little or big endian ints
  1219.                 pixels);                // incoming pixels
  1220.         return textureHandle;
  1221.     }
  1222.  
  1223.     /**
  1224.      * Build Mipmaps for currently bound texture (builds a set of textures at various
  1225.      * levels of detail so that texture will scale up and down gracefully)
  1226.      *
  1227.      * @param textureImg  the texture image
  1228.      * @return   error code of buildMipMap call
  1229.      */
  1230.     public static int makeTextureMipMap(int textureHandle, GLImage textureImg)
  1231.     {
  1232.         int ret = 0;
  1233.         if (textureImg != null && textureImg.isLoaded()) {
  1234.             GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
  1235.             ret = GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, GL11.GL_RGBA8,
  1236.                     textureImg.w, textureImg.h,
  1237.                     GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, textureImg.getPixelBytes());
  1238.             if (ret != 0) {
  1239.                 err("GLApp.makeTextureMipMap(): Error occured while building mip map, ret=" + ret + " error=" + GLU.gluErrorString(ret) );
  1240.             }
  1241.             // Assign the mip map levels and texture info
  1242.             GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR_MIPMAP_NEAREST);
  1243.             GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
  1244.         }
  1245.         return ret;
  1246.     }
  1247.  
  1248.     /**
  1249.      * Create a texture large enough to hold the screen image.  Use RGBA8 format
  1250.      * to insure colors are copied exactly.  Use GL_NEAREST for magnification
  1251.      * to prevent slight blurring of image when screen is drawn back.
  1252.      *
  1253.      * @see frameCopy()
  1254.      * @see frameDraw()
  1255.      */
  1256.     public static int makeTextureForScreen(int screenSize)
  1257.     {
  1258.         // get a texture size big enough to hold screen (512, 1024, 2048 etc.)
  1259.         screenTextureSize = getPowerOfTwoBiggerThan(screenSize);
  1260.         msg("GLApp.makeTextureForScreen(): made texture for screen with size " + screenTextureSize);
  1261.         // get a new empty texture
  1262.         int textureHandle = allocateTexture();
  1263.         ByteBuffer pixels = allocBytes(screenTextureSize*screenTextureSize*SIZE_INT);
  1264.         // 'select' the new texture by it's handle
  1265.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  1266.         // set texture parameters
  1267.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_REPEAT);
  1268.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_REPEAT);
  1269.         // use GL_NEAREST to prevent blurring during frequent screen restores
  1270.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST);
  1271.         GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST);
  1272.         // Create the texture from pixels: use GL_RGBA8 to insure exact color copy
  1273.         GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, screenTextureSize, screenTextureSize, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixels);
  1274.         return textureHandle;
  1275.     }
  1276.  
  1277.     /**
  1278.      * Find a power of two equal to or greater than the given value.
  1279.      * Ie. getPowerOfTwoBiggerThan(800) will return 1024.
  1280.      * <P>
  1281.      * @see makeTextureForScreen()
  1282.      * @param dimension
  1283.      * @return a power of two equal to or bigger than the given dimension
  1284.      */
  1285.     public static int getPowerOfTwoBiggerThan(int n) {
  1286.         if (n < 0)
  1287.             return 0;
  1288.         --n;
  1289.         n |= n >> 1;
  1290.         n |= n >> 2;
  1291.         n |= n >> 4;
  1292.         n |= n >> 8;
  1293.         n |= n >> 16;
  1294.         return n+1;
  1295.     }
  1296.  
  1297.     /**
  1298.      * Copy pixels from a ByteBuffer to a texture.  The buffer pixels are integers in ARGB format
  1299.      * (this is the Java default format you get from a BufferedImage) or BGRA format (this is the
  1300.      * native order of Intel systems.
  1301.      *
  1302.      * The glTexSubImage2D() call treats the incoming pixels as integers
  1303.      * in either big-endian (ARGB) or little-endian (BGRA) formats based on the setting
  1304.      * of the bytebuffer (pixel_byte_order).
  1305.      *
  1306.      * @param bb  ByteBuffer of pixels stored as ARGB or BGRA integers
  1307.      * @param w   width of source image
  1308.      * @param h   height of source image
  1309.      * @param textureHandle  texture to copy pixels into
  1310.      */
  1311.     public static void copyPixelsToTexture(ByteBuffer bb, int w, int h, int textureHandle) {
  1312.         int pixel_byte_order = (bb.order() == ByteOrder.BIG_ENDIAN)?
  1313.                                         GL12.GL_UNSIGNED_INT_8_8_8_8 :
  1314.                                         GL12.GL_UNSIGNED_INT_8_8_8_8_REV;
  1315.  
  1316.         // "select" the texture that we'll write into
  1317.         GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureHandle);
  1318.  
  1319.         // Copy pixels to texture
  1320.         GL11.glTexSubImage2D(
  1321.                 GL11.GL_TEXTURE_2D,         // always GL_TEXTURE_2D
  1322.                 0,                          // texture detail level: always 0
  1323.                 0, 0,                       // x,y offset into texture
  1324.                 w, h,                       // dimensions of image pixel data
  1325.                 GL12.GL_BGRA,               // format of pixels in texture (little_endian - native for PC)
  1326.                 pixel_byte_order,           // format of pixels in bytebuffer (big or little endian ARGB integers)
  1327.                 bb                          // image pixel data
  1328.         );
  1329.     }
  1330.  
  1331.     /**
  1332.      * Calls glTexSubImage2D() to copy pixels from an image into a texture.
  1333.      */
  1334.     public static void copyImageToTexture(GLImage img, int textureHandle) {
  1335.         copyPixelsToTexture(img.pixelBuffer, img.w, img.h, textureHandle);
  1336.     }
  1337.  
  1338.     //========================================================================
  1339.     // functions to set projection
  1340.     //========================================================================
  1341.  
  1342.     /**
  1343.      * Set OpenGL to render in 3D perspective.  Set the projection matrix using
  1344.      * GLU.gluPerspective().  The projection matrix controls how the scene is
  1345.      * "projected" onto the screen.  Think of it as the lens on a camera, which
  1346.      * defines how wide the field of vision is, how deep the scene is, and how
  1347.      * what the aspect ratio will be.
  1348.      */
  1349.     public static void setPerspective()
  1350.     {
  1351.         // select projection matrix (controls perspective)
  1352.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  1353.         GL11.glLoadIdentity();
  1354.         GLU.gluPerspective(40f, aspectRatio, 1f, 1000f);
  1355.         // return to modelview matrix
  1356.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  1357.     }
  1358.  
  1359.     /**
  1360.      * Set OpenGL to render in flat 2D (no perspective) with a one-to-one
  1361.      * relation between screen pixels and world coordinates, ie. if
  1362.      * you draw a 10x10 quad at 100,100 it will appear as a 10x10 pixel
  1363.      * square on screen at pixel position 100,100.
  1364.      * <P>
  1365.      * <B>ABOUT Ortho and Viewport:</B><br>
  1366.      * Let's say we're drawing in 2D and want to have a cinema proportioned
  1367.      * viewport (16x9), and want to bound our 2D rendering into that area ie.
  1368.      * <PRE>
  1369.           ___________1024,576
  1370.          |           |
  1371.          |  Scene    |      Set the bounds on the scene geometry
  1372.          |___________|      to the viewport size and shape
  1373.       0,0
  1374.  
  1375.           ___________1024,576
  1376.          |           |
  1377.          |  Ortho    |      Set the projection to cover the same
  1378.          |___________|      area as the scene
  1379.       0,0
  1380.  
  1381.           ___________ 1024,768
  1382.          |___________|
  1383.          |           |1024,672
  1384.          |  Viewport |      Set the viewport to the same shape
  1385.      0,96|___________|      as scene and ortho, but centered on
  1386.          |___________|      screen.
  1387.       0,0
  1388.      *</PRE>
  1389.      */
  1390.     public static void setOrtho()
  1391.     {
  1392.         // select projection matrix (controls view on screen)
  1393.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  1394.         GL11.glLoadIdentity();
  1395.         // set ortho to same size as viewport, positioned at 0,0
  1396.         GL11.glOrtho(
  1397.                 0,viewportW,  // left,right
  1398.                 0,viewportH,  // bottom,top
  1399.                 -500,500);    // Zfar, Znear
  1400.         // return to modelview matrix
  1401.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  1402.     }
  1403.  
  1404.     public static void setOrtho(int width, int height)
  1405.     {
  1406.         // select projection matrix (controls view on screen)
  1407.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  1408.         GL11.glLoadIdentity();
  1409.         // set ortho to same size as viewport, positioned at 0,0
  1410.         GL11.glOrtho(
  1411.                 0,width,  // left,right
  1412.                 0,height,  // bottom,top
  1413.                 -500,500);    // Zfar, Znear
  1414.         // return to modelview matrix
  1415.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  1416.     }
  1417.  
  1418.     /**
  1419.      * Set OpenGL to render in flat 2D (no perspective) on top of current scene.
  1420.      * Preserve current projection and model views, and disable depth testing.
  1421.      * Ortho world size will be same as viewport size, so any ortho drawing
  1422.      * (drawQuad(), drawImageFullscreen(), etc.) will be scaled to fit viewport.
  1423.      * <P>
  1424.      * NOTE: if the viewport is the same size as the window (by default it is),
  1425.      * then setOrtho() will make the world coordinates exactly match the screen
  1426.      * pixel positions.  This is convenient for mouse interaction, but be warned:
  1427.      * if you setViewport() to something other than fullscreen, then you need
  1428.      * to use getWorldCoordsAtScreen() to convert screen xy to world xy.
  1429.      * <P>
  1430.      * Once Ortho is on, glTranslate() will take pixel coords as arguments,
  1431.      * with the lower left corner 0,0 and the upper right corner 1024,768 (or
  1432.      * whatever your screen size is).  !!!
  1433.      *
  1434.      * @see setOrthoOff()
  1435.      * @see setViewport(int,int,int,int)
  1436.      */
  1437.     public static void setOrthoOn()
  1438.     {
  1439.         // prepare projection matrix to render in 2D
  1440.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  1441.         GL11.glPushMatrix();                   // preserve perspective view
  1442.         GL11.glLoadIdentity();                 // clear the perspective matrix
  1443.         GL11.glOrtho(                          // turn on 2D mode
  1444.                 ////viewportX,viewportX+viewportW,    // left, right
  1445.                 ////viewportY,viewportY+viewportH,    // bottom, top    !!!
  1446.                 0,viewportW,    // left, right
  1447.                 0,viewportH,    // bottom, top
  1448.                 -500,500);                        // Zfar, Znear
  1449.         // clear the modelview matrix
  1450.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  1451.         GL11.glPushMatrix();                   // Preserve the Modelview Matrix
  1452.         GL11.glLoadIdentity();                 // clear the Modelview Matrix
  1453.         // disable depth test so further drawing will go over the current scene
  1454.         GL11.glDisable(GL11.GL_DEPTH_TEST);
  1455.     }
  1456.  
  1457.     /**
  1458.      * Set the width and height of the Ortho scene.  By default GLApp will use
  1459.      * viewportWidth and viewportHeight for the width and height of ortho mode.
  1460.      * You can override this behavior by setting a specific size with setOrthoDimensions().
  1461.      * <P>
  1462.      * By default ortho mode (setOrtho(), setOrthoOn()) will set the world width
  1463.      * and height to exactly match the screen pixel width and height (it uses
  1464.      * the viewport size, which is typically the exact same size as the opengl window).
  1465.      * This makes it easy to map screen positions to the world space.  Text positions,
  1466.      * images and mouse coordinates map pixel-for-pixel to the screen.
  1467.      * <P>
  1468.      * The drawback is that the ortho world space will change size on different
  1469.      * resolution screens. In low-res screens the ortho world may be 800x600 while
  1470.      * in a hi-res screen it could be 1920x1200, which means that anything drawn
  1471.      * in ortho mode may shift on different screens.
  1472.      * <P>
  1473.      * setOrthoDimensions() assigns a fixed size for ortho scenes, independent of
  1474.      * the viewport size.  OpenGL will project the ortho world into the
  1475.      * viewport just as it does with a perspective projection.  Things drawn in
  1476.      * ortho will stay in the right place on any resolution screen.
  1477.      * <P>
  1478.      * The drawback with this method is that screen coordinates don't necessarily
  1479.      * map exactly to the ortho world.  To translate a screen xy to the ortho
  1480.      * world coordinates, use getWorldCoordsAtScreen(x,y) just as with perspective
  1481.      * worlds.
  1482.      *
  1483.      * @param width    width of ortho scene
  1484.      * @param height   height of ortho scene
  1485.      *
  1486.      * @see setOrtho()
  1487.      * @see setOrthoOn()
  1488.      * @see setPerspective()
  1489.      *//*
  1490.     public static void setOrthoDimensions(int width, int height)
  1491.     {
  1492.         orthoWidth = width;
  1493.         orthoHeight = height;
  1494.     }
  1495.     */
  1496.  
  1497.     /**
  1498.      * Turn 2D mode off.  Return the projection and model views to their
  1499.      * preserved state that was saved when setOrthoOn() was called, and
  1500.      * re-enable depth testing.
  1501.      *
  1502.      * @see setOrthoOn()
  1503.      */
  1504.     public static void setOrthoOff()
  1505.     {
  1506.         // restore the original positions and views
  1507.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  1508.         GL11.glPopMatrix();
  1509.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  1510.         GL11.glPopMatrix();
  1511.         // turn Depth Testing back on
  1512.         GL11.glEnable(GL11.GL_DEPTH_TEST);
  1513.     }
  1514.  
  1515.     /**
  1516.      * Define the position and size of the screen area in which the
  1517.      * OpenGL context will draw. Position and size of the area are given
  1518.      * in exact pixel sizes.  By default the viewport is the same size as
  1519.      * the window (displayWidth,displayHeight).
  1520.      * <P>
  1521.      * NOTE: by default the window size, viewport size and setOrtho() size are all
  1522.      * the same, so in ortho mode screen pixel positions exactly match to world
  1523.      * coordinates.  THIS IS NO LONGER TRUE if you setViewport() to some other
  1524.      * size.  With a custom viewport you need to use getWorldCoordsAtScreen()
  1525.      * to convert screen xy to world xy.
  1526.      * <P>
  1527.      * @param x       position of the lower left of viewport area, in pixels
  1528.      * @param y
  1529.      * @param width   size of the viewport area, in pixels
  1530.      * @param height
  1531.      *
  1532.      * @see setPerspective()
  1533.      * @see setOrtho()
  1534.      * @see setOrthoDimensions(int,int)
  1535.      */
  1536.     public static void setViewport(int x, int y, int width, int height)
  1537.     {
  1538.         viewportX = x;
  1539.         viewportY = y;
  1540.         viewportW = width;
  1541.         viewportH = height;
  1542.         aspectRatio = (float)width / (float)height;
  1543.         GL11.glViewport(x,y,width,height);
  1544.    }
  1545.  
  1546.     /**
  1547.      * Reset the viewport to full screen (displayWidth x displayHeight).
  1548.      *
  1549.      * @see setViewport(int,int,int,int)
  1550.      */
  1551.     public static void resetViewport()
  1552.     {
  1553.         setViewport(0,0,displayWidth,displayHeight);
  1554.     }
  1555.  
  1556.     /**
  1557.      * A simple way to set eye position.  Calls gluLookat() to place
  1558.      * the viewpoint <distance> units up the Z axis from the given target position,
  1559.      * looking at the target position. The camera is oriented vertically (Y axis is up).
  1560.      * away.
  1561.      */
  1562.     public static void lookAt(float lookatX, float lookatY, float lookatZ, float distance)
  1563.     {
  1564.         // set viewpoint
  1565.         GLU.gluLookAt(
  1566.                 lookatX,lookatY,lookatZ+distance,  // eye is at the same XY as the target, <distance> units up the Z axis
  1567.                 lookatX,lookatY,lookatZ,           // look at the target position
  1568.                 0,1,0);                            // the Y axis is up
  1569.     }
  1570.  
  1571.     //========================================================================
  1572.     // Functions to push/pop OpenGL settings
  1573.     //========================================================================
  1574.  
  1575.     /**
  1576.      * preserve all OpenGL settings that can be preserved.  Use this
  1577.      * function to isolate settings changes.  Call pushAttrib() before
  1578.      * calling glEnable(), glDisable(), glMatrixMode() etc. After
  1579.      * your code executes, call popAttrib() to return to the
  1580.      * previous settings.
  1581.      *
  1582.      * For better performance, call pushAttrib() with specific settings
  1583.      * flags to preserve only specific settings.
  1584.      *
  1585.      * @see popAttrib()
  1586.      */
  1587.     public static void pushAttrib()
  1588.     {
  1589.         GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS);
  1590.     }
  1591.  
  1592.     /**
  1593.      * preserve the specified OpenGL setting.  Call popAttrib() to return to
  1594.      * the preserved state.
  1595.      *
  1596.      * @see popAttrib()
  1597.      */
  1598.     public static void pushAttrib(int attribute_bits)
  1599.     {
  1600.         GL11.glPushAttrib(attribute_bits);
  1601.     }
  1602.  
  1603.     /**
  1604.      * preserve the OpenGL settings that will be affected when we draw in ortho
  1605.      * mode over the scene.  For example if we're drawing an interface layer,
  1606.      * buttons, popup menus, cursor, text, etc. we need to turn off lighting,
  1607.      * turn on blending, set color to white and turn off depth test.
  1608.      * <P>
  1609.      * call pushAttribOverlay(), enable settings that you need, when done call popAttrib()
  1610.      *
  1611.      * @see popAttrib()
  1612.      */
  1613.     public static void pushAttribOrtho()
  1614.     {
  1615.         GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_DEPTH_BUFFER_BIT);
  1616.     }
  1617.  
  1618.     /**
  1619.      * preserve the OpenGL viewport settings.
  1620.      * <pre>
  1621.      *       pushAttribViewport();
  1622.      *           setViewport(0,0,displaymode.getWidth(),displaymode.getHeight());
  1623.      *           ... do some drawing outside of previous viewport area
  1624.      *       popAttrib();
  1625.      * </pre>
  1626.      *
  1627.      * @see popAttrib()
  1628.      */
  1629.     public static void pushAttribViewport()
  1630.     {
  1631.         GL11.glPushAttrib(GL11.GL_VIEWPORT_BIT);
  1632.     }
  1633.  
  1634.     /**
  1635.      * return to the OpenGL settings that were preserved by the previous pushAttrib() call.
  1636.      *
  1637.      * @see pushAttrib()
  1638.      */
  1639.     public static void popAttrib()
  1640.     {
  1641.         GL11.glPopAttrib();
  1642.     }
  1643.  
  1644.     //========================================================================
  1645.     // Lighting functions
  1646.     //========================================================================
  1647.  
  1648.     /**
  1649.      * Set the color of a 'positional' light (a light that has a specific
  1650.      * position within the scene).  <BR>
  1651.      *
  1652.      * Pass in an OpenGL light number (GL11.GL_LIGHT1),
  1653.      * the 'Diffuse' and 'Ambient' colors (direct light and reflected light),
  1654.      * and the position.<BR>
  1655.      *
  1656.      * @param GLLightHandle
  1657.      * @param diffuseLightColor
  1658.      * @param ambientLightColor
  1659.      * @param position
  1660.      */
  1661.     public static void setLight( int GLLightHandle,
  1662.         float[] diffuseLightColor, float[] ambientLightColor, float[] specularLightColor, float[] position )
  1663.     {
  1664.         FloatBuffer ltDiffuse = allocFloats(diffuseLightColor);
  1665.         FloatBuffer ltAmbient = allocFloats(ambientLightColor);
  1666.         FloatBuffer ltSpecular = allocFloats(specularLightColor);
  1667.         FloatBuffer ltPosition = allocFloats(position);
  1668.         GL11.glLight(GLLightHandle, GL11.GL_DIFFUSE, ltDiffuse);   // color of the direct illumination
  1669.         GL11.glLight(GLLightHandle, GL11.GL_SPECULAR, ltSpecular); // color of the highlight
  1670.         GL11.glLight(GLLightHandle, GL11.GL_AMBIENT, ltAmbient);   // color of the reflected light
  1671.         GL11.glLight(GLLightHandle, GL11.GL_POSITION, ltPosition);
  1672.         GL11.glEnable(GLLightHandle);   // Enable the light (GL_LIGHT1 - 7)
  1673.         //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .005F);    // how light beam drops off
  1674.     }
  1675.  
  1676.  
  1677.     public static void setSpotLight( int GLLightHandle,
  1678.         float[] diffuseLightColor, float[] ambientLightColor,
  1679.         float[] position, float[] direction, float cutoffAngle )
  1680.     {
  1681.         FloatBuffer ltDirection = allocFloats(direction);
  1682.         setLight(GLLightHandle, diffuseLightColor, ambientLightColor, diffuseLightColor, position);
  1683.         GL11.glLightf(GLLightHandle, GL11.GL_SPOT_CUTOFF, cutoffAngle);   // width of the beam
  1684.         GL11.glLight(GLLightHandle, GL11.GL_SPOT_DIRECTION, ltDirection);    // which way it points
  1685.         GL11.glLightf(GLLightHandle, GL11.GL_CONSTANT_ATTENUATION, 2F);    // how light beam drops off
  1686.         //GL11.glLightf(GLLightHandle, GL11.GL_LINEAR_ATTENUATION, .5F);    // how light beam drops off
  1687.         //GL11.glLightf(GLLightHandle, GL11.GL_QUADRATIC_ATTENUATION, .5F);    // how light beam drops off
  1688.     }
  1689.  
  1690.     /**
  1691.      * Set the color of the Global Ambient Light.  Affects all objects in
  1692.      * scene regardless of their placement.
  1693.      */
  1694.     public static void setAmbientLight(float[] ambientLightColor)
  1695.     {
  1696.         put(tmpFloats,ambientLightColor);
  1697.         GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, tmpFloats);
  1698.     }
  1699.  
  1700.     /**
  1701.      * Set the position of a light to the given xyz. NOTE: Positional light only,
  1702.      * not directional.
  1703.      */
  1704.     public static void setLightPosition(int GLLightHandle, float x, float y, float z)
  1705.     {
  1706.         put(tmpFloats, new float[] {x,y,z,1});
  1707.         GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
  1708.     }
  1709.  
  1710.     /**
  1711.      * Set the position (or direction) of a light to the given xyz.
  1712.      */
  1713.     public static void setLightPosition(int GLLightHandle, float[] position)
  1714.     {
  1715.         put(tmpFloats,position);
  1716.         GL11.glLight(GLLightHandle, GL11.GL_POSITION, tmpFloats);
  1717.     }
  1718.  
  1719.     /**
  1720.      * enable/disable the given light.  The light handle parameter is one of
  1721.      * the predefined OpenGL light handle numbers (GL_LIGHT1, GL_LIGHT2 ... GL_LIGHT7).
  1722.      */
  1723.     public static void setLight(int GLLightHandle, boolean on)
  1724.     {
  1725.         if (on) {
  1726.             GL11.glEnable(GLLightHandle);
  1727.         }
  1728.         else {
  1729.             GL11.glDisable(GLLightHandle);
  1730.         }
  1731.     }
  1732.  
  1733.     /**
  1734.      * Enable/disable lighting.  If parameter value is false, this will turn off all
  1735.      * lights and ambient lighting.
  1736.      *
  1737.      * NOTE: When lighting is disabled, material colors are disabled as well.  Use
  1738.      *       glColor() to set color properties when ligthing is off.
  1739.      */
  1740.     public static void setLighting(boolean on) {
  1741.         if (on) {
  1742.             GL11.glEnable(GL11.GL_LIGHTING);
  1743.         }
  1744.         else {
  1745.             GL11.glDisable(GL11.GL_LIGHTING);
  1746.         }
  1747.     }
  1748.  
  1749.     //========================================================================
  1750.     // Material functions
  1751.     //========================================================================
  1752.     public static final float[] colorClear   = {  0f,  0f,  0f,  0f}; // alpha is 0
  1753.     public static final float[] colorBlack   = {  0f,  0f,  0f,  1f};
  1754.     public static final float[] colorWhite   = {  1f,  1f,  1f,  1f};
  1755.     public static final float[] colorGray    = { .5f, .5f, .5f,  1f};
  1756.     public static final float[] colorRed     = {  1f,  0f,  0f,  1f};
  1757.     public static final float[] colorGreen   = {  0f,  1f,  0f,  1f};
  1758.     public static final float[] colorBlue    = {  0f,  0f,  1f,  1f};
  1759.  
  1760.     /**
  1761.     *  A simple way to set the current material properties to approximate a
  1762.     *  "real" surface.  Provide the surface color (float[4]]) and shininess
  1763.     *  value (range 0-1).
  1764.     *  <P>
  1765.     *  Sets diffuse material color to the surfaceColor and ambient material color
  1766.     *  to surfaceColor/2.  Based on the shiny value (0-1), sets the specular
  1767.     *  property to a color between black (0) and white (1), and sets the
  1768.     *  shininess property to a value between 0 and 127.
  1769.     *  <P>
  1770.     *  Lighting must be enabled for material colors to take effect.
  1771.     *  <P>
  1772.     *  @param surfaceColor - must be float[4] {R,G,B,A}
  1773.     *  @param reflection - a float from 0-1 (0=very matte, 1=very shiny)
  1774.     */
  1775.     public static void setMaterial(float[] surfaceColor, float shiny) {
  1776.         float[] reflect = {shiny,shiny,shiny,1}; // make a shade of gray
  1777.         float[] ambient = {surfaceColor[0]*.5f,surfaceColor[1]*.5f,surfaceColor[2]*.5f,1};  // darker surface color
  1778.         mtldiffuse.put(surfaceColor).flip();     // surface directly lit
  1779.         mtlambient.put(ambient).flip();          // surface in shadow
  1780.         mtlspecular.put(reflect).flip();         // reflected light
  1781.         mtlemissive.put(colorBlack).flip();      // no emissive light
  1782.         // size of reflection
  1783.         int openglShininess = ((int)(shiny*127f));   // convert 0-1 to 0-127
  1784.         if (openglShininess >= 0 && openglShininess <= 127) {
  1785.             mtlshininess.put(new float[] {openglShininess,0,0,0}).flip();
  1786.         }
  1787.         applyMaterial();
  1788.     }
  1789.  
  1790.     /**
  1791.     *  Set the four material colors and calls glMaterial() to change the current
  1792.     *  material color in OpenGL.  Lighting must be enabled for material colors to take effect.
  1793.     *
  1794.     *  @param shininess: size of reflection (0 is matte, 127 is pinpoint reflection)
  1795.     */
  1796.     public static void setMaterial(float[] diffuseColor, float[] ambientColor, float[] specularColor, float[] emissiveColor, float shininess) {
  1797.         mtldiffuse.put(diffuseColor).flip();     // surface directly lit
  1798.         mtlambient.put(ambientColor).flip();     // surface in shadow
  1799.         mtlspecular.put(specularColor).flip();   // reflection color
  1800.         mtlemissive.put(emissiveColor).flip();   // glow color
  1801.         if (shininess >= 0 && shininess <= 127) {
  1802.             mtlshininess.put(new float[] {shininess,0,0,0}).flip();  // size of reflection 0=broad 127=pinpoint
  1803.         }
  1804.         applyMaterial();
  1805.     }
  1806.  
  1807.     /**
  1808.      * Alter the material opacity by setting the diffuse material color
  1809.      * alpha value to the given value
  1810.      * @para alpha 0=transparent 1=opaque
  1811.      */
  1812.     public static void setMaterialAlpha(float alpha) {
  1813.         if (alpha < 0) alpha = 0;
  1814.         if (alpha > 1) alpha = 1;
  1815.         mtldiffuse.put(3,alpha).flip();     // alpha value of diffuse color
  1816.         applyMaterial();
  1817.     }
  1818.  
  1819.     /**
  1820.      *  Call glMaterial() to activate these material properties in the OpenGL environment.
  1821.      *  These properties will stay in effect until you change them or disable lighting.
  1822.      */
  1823.     public static void applyMaterial() {
  1824.         GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, mtldiffuse);
  1825.         GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, mtlambient);
  1826.         GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, mtlspecular);
  1827.         GL11.glMaterial(GL11.GL_FRONT, GL11.GL_EMISSION, mtlemissive);
  1828.         GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SHININESS, mtlshininess);
  1829.     }
  1830.  
  1831.  
  1832.     //========================================================================
  1833.     // Fog
  1834.     //========================================================================
  1835.  
  1836.     /**
  1837.      * Enable atmospheric fog effect, with the given color and density.
  1838.      * <PRE>
  1839.      *      setFog(new float[] {.5f,.5f,.5f,1f}, .3f);
  1840.      * </PRE>
  1841.      *
  1842.      * @param fogColor   float[4] specifies the RGB fog color value
  1843.      * @param fogDensity  float in range 0-1 specifies how opaque the fog will be
  1844.      */
  1845.     public static void setFog(float[] fogColor, float fogdensity) {
  1846.         put(tmpFloats,fogColor);
  1847.         // turn fog on
  1848.         GL11.glEnable(GL11.GL_FOG);
  1849.         // mode: GL_EXP2 is dense fog, GL_EXP is thinner, GL_LINEAR is very thin
  1850.         GL11.glFogi(GL11.GL_FOG_MODE, GL11.GL_EXP2);
  1851.         // start and end (only apply when fog mode=GL_LINEAR
  1852.         //GL11.glFogf(GL11.GL_FOG_START, 100f);
  1853.         //GL11.glFogf(GL11.GL_FOG_END, 1000f);
  1854.         // color
  1855.         GL11.glFog(GL11.GL_FOG_COLOR, tmpFloats);
  1856.         // density
  1857.         GL11.glFogf(GL11.GL_FOG_DENSITY, fogdensity);
  1858.         // quality
  1859.         GL11.glHint(GL11.GL_FOG_HINT, GL11.GL_NICEST);
  1860.     }
  1861.  
  1862.  
  1863.     /**
  1864.      * Enable/disable fog effect.  Does not change the fog settings.
  1865.      */
  1866.     public static void setFog(boolean on) {
  1867.         if (on) {
  1868.             GL11.glEnable(GL11.GL_FOG);
  1869.         }
  1870.         else {
  1871.             GL11.glDisable(GL11.GL_FOG);
  1872.         }
  1873.     }
  1874.  
  1875.     //========================================================================
  1876.     // Time functions
  1877.     //========================================================================
  1878.  
  1879.     public static double getTimeInSeconds()
  1880.     {
  1881.         if (ticksPerSecond == 0) {
  1882.             ticksPerSecond = Sys.getTimerResolution();
  1883.         }
  1884.         return (((double)Sys.getTime())/(double)ticksPerSecond);
  1885.     }
  1886.  
  1887.     public static double getTimeInMillis()
  1888.     {
  1889.         if (ticksPerSecond == 0) {
  1890.             ticksPerSecond = Sys.getTimerResolution();
  1891.         }
  1892.         return (double) ((((double)Sys.getTime())/(double)ticksPerSecond) * 1000.0);
  1893.     }
  1894.  
  1895.     /**
  1896.      *  Calculate time since we last called updateTimer().  Updates secondsSinceLastFrame and
  1897.      *  sets lastFrameTime to the current Sys.getTime().
  1898.      *  <P>
  1899.      *  Called by run() at the beginning of each loop.
  1900.      *
  1901.      *  @see run()
  1902.      *  @see getSecondsPerFrame()
  1903.      */
  1904.     public static void updateTimer()
  1905.     {
  1906.         // number of frames to average (about one second)
  1907.         double numToAvg = 50;
  1908.         // calc time elapsed since we last rendered
  1909.         secondsSinceLastFrame = (double)(Sys.getTime() - lastFrameTime) / (double)ticksPerSecond;
  1910.         lastFrameTime = Sys.getTime();
  1911.         // keep a moving average of frame elapsed times
  1912.         if (secondsSinceLastFrame < 1) {
  1913.             avgSecsPerFrame = (double) ((avgSecsPerFrame*numToAvg)+secondsSinceLastFrame) / (numToAvg+1D);
  1914.         }
  1915.     }
  1916.  
  1917.     /**
  1918.      * Return the moving average of the seconds per frame for the last 50 frames.
  1919.      * Useful when animating in real time.  Will provide smoother time deltas
  1920.      * than the secondsSinceLastFrame variable, which holds the exact time elapsed
  1921.      * during the last frame (but may jump or lag as processor load varies).
  1922.      *
  1923.      * @see updateTimer()
  1924.      */
  1925.     public static double getSecondsPerFrame() {
  1926.         return avgSecsPerFrame;
  1927.     }
  1928.  
  1929.     /**
  1930.      * Return the moving average of the frames per second for the last 50 frames.
  1931.      *
  1932.      * @see updateTimer()
  1933.      */
  1934.     public static double getFramesPerSecond() {
  1935.         return 1d/avgSecsPerFrame;
  1936.     }
  1937.  
  1938.     //========================================================================
  1939.     // Load images
  1940.     //========================================================================
  1941.  
  1942.     /**
  1943.      * Make a blank image of the given size.
  1944.      * @return  the new GLImage
  1945.      */
  1946.     public static GLImage makeImage(int w, int h) {
  1947.         ByteBuffer pixels = allocBytes(w*h*SIZE_INT);
  1948.         return new GLImage(pixels,w,h);
  1949.     }
  1950.  
  1951.     /**
  1952.      * Load an image from the given file and return a GLImage object.
  1953.      * @param image filename
  1954.      * @return the loaded GLImage
  1955.      */
  1956.     public static GLImage loadImage(String imgFilename) {
  1957.         GLImage img = new GLImage(imgFilename);
  1958.         if (img.isLoaded()) {
  1959.             return img;
  1960.         }
  1961.         return null;
  1962.     }
  1963.  
  1964.     /**
  1965.      * Load an image from the given file and return a ByteBuffer containing ARGB pixels.<BR>
  1966.      * Can be used to create textures. <BR>
  1967.      * @param imgFilename
  1968.      * @return
  1969.      */
  1970.     public static ByteBuffer loadImagePixels(String imgFilename) {
  1971.         GLImage img = new GLImage(imgFilename);
  1972.         return img.pixelBuffer;
  1973.     }
  1974.  
  1975.     /**
  1976.      * Draw a cursor image textured onto a quad at cursorX,cursorY.  The cursor
  1977.      * image must be loaded into a 32x32 texture. This function can be called
  1978.      * after scene is drawn to place a cursor on top of scene.
  1979.      * <P>
  1980.      * NOTE: the cursor is drawn in screen space, at an absolute screen pixel location
  1981.      * without regard for viewport (temporarily zets viewport to entire screen).
  1982.      * <P>
  1983.      * See handleEvents() for cursorX cursorY and mouse motion handling.
  1984.      * <P>
  1985.      * Example:
  1986.      * <PRE>
  1987.      *    int cursorTxtr;
  1988.      *
  1989.      *    public void setup() {
  1990.      *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
  1991.      *    }
  1992.      *
  1993.      *    public void draw() {
  1994.      *        // render scene
  1995.      *        ...
  1996.      *        drawCursor(cursorTxtr);
  1997.      *    }
  1998.      * </PRE>
  1999.      *
  2000.      * @param cursorTextureHandle  handle to texture containing 32x32 cursor image
  2001.      */
  2002.     public static void drawCursor(int cursorTextureHandle) {
  2003.         // set projection matrix to 2D fullscreen
  2004.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  2005.         GL11.glPushMatrix();                   // preserve perspective view
  2006.         GL11.glLoadIdentity();                 // clear the perspective matrix
  2007.         GL11.glOrtho(                          // set ortho to exactly match screen size
  2008.                 0,displayWidth,     // left, right
  2009.                 0,displayHeight,    // bottom, top
  2010.                 -1,1);              // Zfar, Znear
  2011.         // clear the modelview matrix
  2012.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  2013.         GL11.glPushMatrix();                   // preserve current modelview matrix
  2014.         GL11.glLoadIdentity();                 // clear the modelview matrix
  2015.  
  2016.         // preserve current settings then draw cursor
  2017.         GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT | GL11.GL_VIEWPORT_BIT);
  2018.         {
  2019.             // set viewport to full screen
  2020.             GL11.glViewport(0,0,displayWidth,displayHeight);
  2021.             // tweak settings
  2022.             GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
  2023.             GL11.glColor4f(1,1,1,1);             // no color
  2024.             GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
  2025.             GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
  2026.             GL11.glEnable(GL11.GL_BLEND);        // enable transparency
  2027.             GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
  2028.             // draw 32x32 cursor image
  2029.             drawQuadZ(cursorTextureHandle, cursorX-15, cursorY-15, 0, 32, 32);
  2030.         }
  2031.         GL11.glPopAttrib();
  2032.  
  2033.         // restore the previous matrix settings
  2034.         GL11.glMatrixMode(GL11.GL_PROJECTION);
  2035.         GL11.glPopMatrix();
  2036.         GL11.glMatrixMode(GL11.GL_MODELVIEW);
  2037.         GL11.glPopMatrix();
  2038.     }
  2039.  
  2040.     /**
  2041.      * OLD function: this drew the cursor only into the current viewport, and
  2042.      * could not handle difffent ortho coordinate systems (ortho had to be
  2043.      * exactly mapped to screen size).
  2044.      *
  2045.      * Draw a cursor image textured onto a quad at cursor position.  The cursor
  2046.      * image must be loaded into a texture, then this function can be called
  2047.      * after scene is drawn.  Uses glPushAttrib() to preserve the current
  2048.      * drawing state.  glPushAttrib() may slow performance down, so in your
  2049.      * app you may want to set the states yourself before calling drawCursor()
  2050.      * and take the push/pop out of here.
  2051.      * <P>
  2052.      * See handleEvents() for cursorX cursorY and mouse motion handling.
  2053.      * <P>
  2054.      * Example:
  2055.      * <PRE>
  2056.      *    int cursorTxtr;
  2057.      *
  2058.      *    public void setup() {
  2059.      *        cursorTxtr = makeTexture("images/cursorCrosshair32.gif"); // image must be 32x32
  2060.      *    }
  2061.      *
  2062.      *    public void draw() {
  2063.      *        // render scene
  2064.      *        ...
  2065.      *        drawCursor(cursorTxtr);
  2066.      *    }
  2067.      * </PRE>
  2068.      *
  2069.      * @param cursorTextureHandle  handle to texture containing 32x32 cursor image
  2070.      */
  2071.     public static void drawCursorOLD(int cursorTextureHandle) {
  2072.         setOrthoOn();
  2073.         GL11.glPushAttrib(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
  2074.         {
  2075.             // tweak settings
  2076.             GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
  2077.             GL11.glColor4f(1,1,1,1);             // no color
  2078.             GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
  2079.             GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
  2080.             GL11.glEnable(GL11.GL_BLEND);        // enable transparency
  2081.             GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
  2082.             drawQuad(cursorTextureHandle, cursorX-15, cursorY-15, 32, 32);  // assumes 32x32 pixels
  2083.         }
  2084.         GL11.glPopAttrib();
  2085.         setOrthoOff();
  2086.     }
  2087.  
  2088.     /**
  2089.      * Draw an image in ortho mode (2D) over the entire viewport area.
  2090.      * Converts the image to a texture and maps onto a viewport-sized quad.
  2091.      * Depth test is turned off, lighting is off, color is set to white.
  2092.      * Alpha blending is on, so transparent areas will be respected.
  2093.      * <P>
  2094.      * NOTE: By default the viewport is the same size as the window so this function
  2095.      * will draw the image over the entire window.  If you setViewport() to a
  2096.      * custom size the image will be drawn into the custom viewport area. To
  2097.      * insure that the image is drawn truly full screen, call resetViewport()
  2098.      * before drawImageFullScreen().
  2099.      * <P>
  2100.      * @see loadImage(String)
  2101.      * @see setViewport(int,int,int,int)
  2102.      * @see resetViewport()
  2103.      */
  2104.     public static void drawImageFullScreen(GLImage img) {
  2105.         if (img == null || img.isLoaded() == false) {
  2106.             return;
  2107.         }
  2108.         // if image has no texture, convert the image to a texture
  2109.         if (img.textureHandle <= 0) {
  2110.             img.textureHandle = makeTexture(img);
  2111.         }
  2112.         // Calculate the UV dimensions of the image in the texture
  2113.         float maxU = (float)img.w / (float)img.textureW;
  2114.         float maxV = (float)img.h / (float)img.textureH;
  2115.         // preserve settings
  2116.         pushAttribOrtho();
  2117.         // switch to 2D projection
  2118.         setOrthoOn();
  2119.         // tweak settings
  2120.         GL11.glEnable(GL11.GL_TEXTURE_2D);   // be sure textures are on
  2121.         GL11.glColor4f(1,1,1,1);             // no color
  2122.         GL11.glDisable(GL11.GL_LIGHTING);    // no lighting
  2123.         GL11.glDisable(GL11.GL_DEPTH_TEST);  // no depth test
  2124.         GL11.glEnable(GL11.GL_BLEND);        // enable transparency
  2125.         GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
  2126.         // activate the image texture
  2127.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,img.textureHandle);
  2128.         // draw a textured quad
  2129.         GL11.glBegin(GL11.GL_QUADS);
  2130.         {
  2131.             GL11.glTexCoord2f(0, 0);
  2132.             GL11.glVertex3f(0f, 0f, 0f);         // Bottom Left
  2133.  
  2134.             GL11.glTexCoord2f(maxU, 0);
  2135.             GL11.glVertex3f(getWidth(), 0f, 0f);         // Bottom Right
  2136.  
  2137.             GL11.glTexCoord2f(maxU, maxV);
  2138.             GL11.glVertex3f(getWidth(), getHeight(), 0f);   // Top Right
  2139.  
  2140.             GL11.glTexCoord2f(0, maxV);
  2141.             GL11.glVertex3f(0f, getHeight(), 0f);       // Top left
  2142.         }
  2143.         GL11.glEnd();
  2144.         // return to previous projection mode
  2145.         setOrthoOff();
  2146.         // return to previous settings
  2147.         popAttrib();
  2148.     }
  2149.  
  2150.     /**
  2151.      * Draw an image in whichever projection mode is current (does not switch to ortho mode).
  2152.      * Convert the image to a texture and draw onto quad.  Will draw with current settings
  2153.      * (light, material, depth, blend, etc.)
  2154.      * <BR>
  2155.      * @see loadImage()
  2156.      * @see drawQuad()
  2157.      * @see drawImageFullScreen()
  2158.      */
  2159.     public static void drawImage(GLImage img, int x, int y, float w, float h) {
  2160.         // if image has no texture, convert the image to a texture
  2161.         if (img.textureHandle <= 0) {
  2162.             img.textureHandle = makeTexture(img);
  2163.         }
  2164.         // preserve settings
  2165.         pushAttribOrtho();
  2166.         // set color to white
  2167.         //GL11.glColor4f(1,1,1,1);   // don't force color to white (may want to tint image)
  2168.         // activate the image texture
  2169.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,img.textureHandle);
  2170.         // draw a textured quad
  2171.         GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
  2172.         GL11.glBegin(GL11.GL_QUADS);
  2173.         {
  2174.             GL11.glTexCoord2f(0f, 0f);
  2175.             GL11.glVertex3f( (float)x, (float)y, (float)0);
  2176.             GL11.glTexCoord2f(1f, 0f);
  2177.             GL11.glVertex3f( (float)x+w, (float)y, (float)0);
  2178.             GL11.glTexCoord2f(1f, 1f);
  2179.             GL11.glVertex3f( (float)x+w, (float)y+h, (float)0);
  2180.             GL11.glTexCoord2f(0f, 1f);
  2181.             GL11.glVertex3f( (float)x, (float)y+h, (float)0);
  2182.         }
  2183.         GL11.glEnd();
  2184.         // return to previous settings
  2185.         popAttrib();
  2186.     }
  2187.  
  2188.     /**
  2189.      * Draw a textured quad in Ortho mode (2D) at the given xy, scaled to
  2190.      * the given width and height.  Depth test is turned off so quad will
  2191.      * be drawn on top of the current scene.  Quad will be drawn
  2192.      * with current light and material if any are active.
  2193.      * <BR>
  2194.      * @ee loadImage()
  2195.      */
  2196.     public static void drawQuad(int textureHandle, int x, int y, float w, float h) {
  2197.         // activate the specified texture
  2198.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  2199.         // prepare to render in 2D
  2200.         setOrthoOn();
  2201.         // draw the textured quad
  2202.         GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
  2203.         GL11.glBegin(GL11.GL_QUADS);
  2204.         {
  2205.             GL11.glTexCoord2f(0f, 0f);
  2206.             GL11.glVertex3f( (float)x, (float)y, (float)0);
  2207.             GL11.glTexCoord2f(1f, 0f);
  2208.             GL11.glVertex3f( (float)x+w, (float)y, (float)0);
  2209.             GL11.glTexCoord2f(1f, 1f);
  2210.             GL11.glVertex3f( (float)x+w, (float)y+h, (float)0);
  2211.             GL11.glTexCoord2f(0f, 1f);
  2212.             GL11.glVertex3f( (float)x, (float)y+h, (float)0);
  2213.         }
  2214.         GL11.glEnd();
  2215.         // restore the previous perspective and model views
  2216.         setOrthoOff();
  2217.     }
  2218.  
  2219.     /**
  2220.      * Draw a textured quad at the given xyz position in 3D space.  Quad will be drawn
  2221.      * with current settings (ie. light, material, depth test, projection, etc.)
  2222.      */
  2223.     public static void drawQuadZ(int textureHandle, float x, float y, float z, float w, float h) {
  2224.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  2225.         // draw  textured quad
  2226.         GL11.glNormal3f(0.0f, 0.0f, 1.0f); // normal faces positive Z
  2227.         GL11.glBegin(GL11.GL_QUADS);
  2228.         {
  2229.             GL11.glTexCoord2f(0f, 0f);
  2230.             GL11.glVertex3f( x, y, z);
  2231.             GL11.glTexCoord2f(1f, 0f);
  2232.             GL11.glVertex3f( x+w, y, z);
  2233.             GL11.glTexCoord2f(1f, 1f);
  2234.             GL11.glVertex3f( x+w, y+h, z);
  2235.             GL11.glTexCoord2f(0f, 1f);
  2236.             GL11.glVertex3f( x, y+h, z);
  2237.         }
  2238.         GL11.glEnd();
  2239.     }
  2240.  
  2241.     //========================================================================
  2242.     // Functions to get and set framebuffer pixels
  2243.     //========================================================================
  2244.  
  2245.     /**
  2246.      * Return a ByteBuffer containing ARGB pixels of the entire screen area.
  2247.      */
  2248.     public static ByteBuffer framePixels() {
  2249.         return framePixels(0,0,displayMode.getWidth(),displayMode.getHeight());
  2250.     }
  2251.  
  2252.     /**
  2253.      * Return a ByteBuffer containing ARGB pixels from the given screen area.
  2254.      */
  2255.     public static ByteBuffer framePixels(int x, int y, int w, int h) {
  2256.         // allocate 4 bytes per pixel
  2257.         ByteBuffer pixels = allocBytes(w*h*4);
  2258.         // Get pixels from frame buffer in ARGB format.
  2259.         GL11.glReadPixels(x,y,w,h, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, pixels);
  2260.         return pixels;
  2261.     }
  2262.  
  2263.     /**
  2264.      * Return an int array containing ARGB pixels from the given screen area.
  2265.      */
  2266.     public static int[] framePixelsInt(int x, int y, int w, int h) {
  2267.         int[] pixels = new int[w * h];
  2268.         ByteBuffer pixelsBB = framePixels(x, y, w, h);
  2269.         get(pixelsBB,pixels);
  2270.         return pixels;
  2271.     }
  2272.  
  2273.     /**
  2274.      * Return the color buffer RGB value at the given screen position as byte[3].
  2275.      *
  2276.      * @param x    screen position
  2277.      * @param y
  2278.      * @return rgb byte array
  2279.      */
  2280.     public static byte[] getPixelColor(int x, int y)
  2281.     {
  2282.         // color value will be stored in an integer
  2283.         tmpInt.clear();
  2284.         // read the framebuffer color value at the given position, as bytes
  2285.         GL11.glReadPixels(x, y, 1, 1, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, tmpInt);
  2286.         byte[] rgb = new byte[] {tmpInt.get(0), tmpInt.get(1), tmpInt.get(2)};
  2287.         return rgb;
  2288.     }
  2289.  
  2290.     /**
  2291.      * Return the depth buffer value at the given screen position.
  2292.      *
  2293.      * @param x    screen position
  2294.      * @param y
  2295.      * @return float Z depth value
  2296.      */
  2297.     public static float getPixelDepth(int x, int y)
  2298.     {
  2299.         return getZDepth(x,y);
  2300.     }
  2301.  
  2302.     /**
  2303.      * Return the stencil buffer value at the given screen position. Stencil values are
  2304.      * typically bytes (0-255).  The value will be returned as an integer.
  2305.      *
  2306.      * @param x    screen position
  2307.      * @param y
  2308.      * @return int stencil value
  2309.      */
  2310.     public static int getPixelStencil(int x, int y)
  2311.     {
  2312.         return getMaskValue(x,y);
  2313.     }
  2314.  
  2315.     /**
  2316.      * Save entire screen image to a texture.  Will copy entire screen even
  2317.      * if a viewport is in use.  Texture param must be large enough to hold
  2318.      * screen image (see makeTextureForScreen()).
  2319.      *
  2320.      * @param txtrHandle   texture where screen image will be stored
  2321.      * @see frameDraw()
  2322.      * @see makeTextureForScreen()
  2323.      */
  2324.     public static void frameCopy(int txtrHandle)
  2325.     {
  2326.         frameCopy(txtrHandle, 0,0, DM.getWidth(),DM.getHeight());   // entire screen
  2327.     }
  2328.  
  2329.     /**
  2330.      * Save a region of the screen to a texture.  Texture must be large enough to hold
  2331.      * screen image.
  2332.      *
  2333.      * @param txtrHandle   texture where screen region will be stored
  2334.      * @see frameDraw()
  2335.      * @see makeTextureForScreen()
  2336.      */
  2337.     public static void frameCopy(int txtrHandle, int x, int y, int w, int h)
  2338.     {
  2339.         GL11.glColor4f(1,1,1,1);                // turn off alpha and color tints
  2340.         GL11.glReadBuffer(GL11.GL_BACK);
  2341.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,txtrHandle);
  2342.         // Copy screen to texture
  2343.         GL11.glCopyTexSubImage2D(GL11.GL_TEXTURE_2D, 0, 0,0, x,y,w,h);
  2344.     }
  2345.  
  2346.     /**
  2347.      * Draw the screen-sized image over entire screen area.  The screen image
  2348.      * is stored in the given texture at 0,0 (see frameCopy()) and has the
  2349.      * same dimensions as the current display mode (DM.getWidth(), DM.getHeight()).
  2350.      * <P>
  2351.      * Reset the viewport and ortho mode to full screen (viewport may be
  2352.      * different proportion than screen if custom aspectRatio is set).  Draw the
  2353.      * quad the same size as texture so no stretching or compression of image.
  2354.      *
  2355.      * @param txtrHandle
  2356.      */
  2357.     public static void frameDraw(int txtrHandle)
  2358.     {
  2359.         // keep it opaque
  2360.         GL11.glDisable(GL11.GL_BLEND);
  2361.         // set viewport to full screen
  2362.         GL11.glViewport(0, 0, DM.getWidth(), DM.getHeight());
  2363.         // draw square quad that covers entire screen
  2364.         drawQuad(txtrHandle, 0, 0, screenTextureSize, screenTextureSize); // draw the full screen image
  2365.         // restore viewport to custom aspect ratio
  2366.         GL11.glViewport(viewportX, viewportY, viewportW, viewportH);
  2367.     }
  2368.  
  2369.     /**
  2370.      * Save the current frame buffer to a PNG image. Exactly the same as screenShot().
  2371.      * @see screenShot()
  2372.      */
  2373.     public static void frameSave() {
  2374.         screenShot();
  2375.     }
  2376.  
  2377.     //========================================================================
  2378.     // Functions to render shapes.
  2379.     //========================================================================
  2380.  
  2381.     /**
  2382.      * Draw a rectangle outline in ortho mode (draws in 2D over the scene).
  2383.      * <BR>
  2384.      * @see setLineWidth()
  2385.      * @see drawRectZ()
  2386.      */
  2387.     public static void drawRect(int x, int y, float w, float h) {
  2388.         // switch projection to 2D mode
  2389.         setOrthoOn();
  2390.         // draw rectangle at Z=0
  2391.         drawRectZ(x,y,0,w,h);
  2392.         // restore the previous perspective and model views
  2393.         setOrthoOff();
  2394.     }
  2395.  
  2396.     /**
  2397.      * Draw a rectangle outline in world space.  Uses opengl line_strip to make
  2398.      * the rectangle.
  2399.      * <BR>
  2400.      * @see setLineWidth()
  2401.      * @see drawRect()
  2402.      */
  2403.     public static void drawRectZ(int x, int y, int z, float w, float h) {
  2404.         // preserve current settings
  2405.         GL11.glPushAttrib(GL11.GL_TEXTURE_BIT | GL11.GL_LIGHTING_BIT);
  2406.         // de-activate texture and light
  2407.         GL11.glDisable(GL11.GL_TEXTURE_2D);
  2408.         GL11.glDisable(GL11.GL_LIGHTING);
  2409.         // draw the rectangle
  2410.         GL11.glBegin(GL11.GL_LINE_STRIP);
  2411.         {
  2412.             GL11.glVertex3f( (float)x, (float)y, (float)z);
  2413.             GL11.glVertex3f( (float)x+w, (float)y, (float)z);
  2414.             GL11.glVertex3f( (float)x+w, (float)y+h, (float)z);
  2415.             GL11.glVertex3f( (float)x, (float)y+h, (float)z);
  2416.             GL11.glVertex3f( (float)x, (float)y, (float)z);
  2417.         }
  2418.         GL11.glEnd();
  2419.         // draw points at the corners
  2420.         GL11.glBegin(GL11.GL_POINTS);
  2421.         {
  2422.             GL11.glVertex3f( (float)x, (float)y, (float)z);
  2423.             GL11.glVertex3f( (float)x+w, (float)y, (float)z);
  2424.             GL11.glVertex3f( (float)x+w, (float)y+h, (float)z);
  2425.             GL11.glVertex3f( (float)x, (float)y+h, (float)z);
  2426.         }
  2427.         GL11.glEnd();
  2428.         // re-enable settings
  2429.         popAttrib();
  2430.     }
  2431.  
  2432.     /**
  2433.      * Draws a circle with the given radius centered at the given world position.
  2434.      */
  2435.     public static void drawCircle(int x, int y, int radius, int linewidth) {
  2436.         // switch projection to 2D mode
  2437.         setOrthoOn();
  2438.         // draw circle at x,y with z=0
  2439.         GL11.glPushMatrix();
  2440.         {
  2441.             GL11.glTranslatef(x,y,0);
  2442.             drawCircle(radius-linewidth, radius, 180);
  2443.         }
  2444.         GL11.glPopMatrix();
  2445.         // restore the previous perspective and model views
  2446.         setOrthoOff();
  2447.     }
  2448.  
  2449.     /**
  2450.      * Draws a circle with the given radius centered at the given world position.
  2451.      */
  2452.     public static void drawCircleZ(int x, int y, int z, int radius, int linewidth) {
  2453.         GL11.glPushMatrix();
  2454.         {
  2455.             GL11.glTranslatef(x,y,z);
  2456.             drawCircle(radius-linewidth, radius, 180);
  2457.         }
  2458.         GL11.glPopMatrix();
  2459.     }
  2460.  
  2461.     /**
  2462.      * Draws a circle centered at 0,0,0.  Use translate() to place circle at desired coords.
  2463.      * Inner and outer radius specify width, stepsize is number of degrees for each segment.
  2464.      */
  2465.     public static void drawCircle(float innerRadius, float outerRadius, int numSegments) {
  2466.         int s = 0;     // start
  2467.         int e = 360;   // end
  2468.         int stepSize = 360/numSegments;   // degrees per segment
  2469.         GL11.glBegin(GL11.GL_QUAD_STRIP);
  2470.         {
  2471.             // add first 2 vertices
  2472.             float ts = (float) Math.sin(Math.toRadians(s));
  2473.             float tc = (float) Math.cos(Math.toRadians(s));
  2474.             GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
  2475.             GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
  2476.             // add intermediate vertices, snap to {step} degrees
  2477.             while ( (s = ( (s + stepSize) / stepSize) * stepSize) < e) {
  2478.                 ts = (float) Math.sin(Math.toRadians(s));
  2479.                 tc = (float) Math.cos(Math.toRadians(s));
  2480.                 GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
  2481.                 GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
  2482.             }
  2483.             // add last 2 vertices at end angle
  2484.             ts = (float) Math.sin(Math.toRadians(e));
  2485.             tc = (float) Math.cos(Math.toRadians(e));
  2486.             GL11.glVertex2f(tc * innerRadius, ts * innerRadius);
  2487.             GL11.glVertex2f(tc * outerRadius, ts * outerRadius);
  2488.         }
  2489.         GL11.glEnd();
  2490.     }
  2491.  
  2492.     /**
  2493.      * Render a 2 unit cube centered at origin.  Includes texture coordinates
  2494.      * and normals.
  2495.      */
  2496.     public static void renderCube()
  2497.     {
  2498.         GL11.glBegin(GL11.GL_QUADS);
  2499.         // Front Face
  2500.         GL11.glNormal3f( 0.0f, 0.0f, 1.0f);
  2501.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);    // Bottom Left
  2502.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);    // Bottom Right
  2503.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);    // Top Right
  2504.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);    // Top Left
  2505.         // Back Face
  2506.         GL11.glNormal3f( 0.0f, 0.0f, -1.0f);
  2507.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);    // Bottom Right
  2508.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);    // Top Right
  2509.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);    // Top Left
  2510.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);    // Bottom Left
  2511.         // Top Face
  2512.         GL11.glNormal3f( 0.0f, 1.0f, 0.0f);
  2513.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);    // Top Left
  2514.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);    // Bottom Left
  2515.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);    // Bottom Right
  2516.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);    // Top Right
  2517.         // Bottom Face
  2518.         GL11.glNormal3f( 0.0f, -1.0f, 0.0f);
  2519.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);    // Top Right
  2520.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);    // Top Left
  2521.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);    // Bottom Left
  2522.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);    // Bottom Right
  2523.         // Right face
  2524.         GL11.glNormal3f( 1.0f, 0.0f, 0.0f);
  2525.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f, -1.0f);    // Bottom Right
  2526.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f, -1.0f);    // Top Right
  2527.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f( 1.0f,  1.0f,  1.0f);    // Top Left
  2528.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f( 1.0f, -1.0f,  1.0f);    // Bottom Left
  2529.         // Left Face
  2530.         GL11.glNormal3f( -1.0f, 0.0f, 0.0f);
  2531.         GL11.glTexCoord2f(0.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f, -1.0f);    // Bottom Left
  2532.         GL11.glTexCoord2f(1.0f, 0.0f); GL11.glVertex3f(-1.0f, -1.0f,  1.0f);    // Bottom Right
  2533.         GL11.glTexCoord2f(1.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f,  1.0f);    // Top Right
  2534.         GL11.glTexCoord2f(0.0f, 1.0f); GL11.glVertex3f(-1.0f,  1.0f, -1.0f);    // Top Left
  2535.         GL11.glEnd();
  2536.     }
  2537.  
  2538.     /**
  2539.      * draw a cube with the given size, centered at origin.  Include texture coordinates.
  2540.      * @param size       length of each side
  2541.      * @param segments   # segments to divide each side into
  2542.      */
  2543.     public static void renderCube(float size, int segments) {
  2544.         float halfsize = size/2f;
  2545.         GL11.glPushMatrix();
  2546.         {
  2547.             GL11.glPushMatrix();
  2548.             {
  2549.                 GL11.glTranslatef(0,0,halfsize);
  2550.                 renderPlane(size,segments);// front
  2551.             }
  2552.             GL11.glPopMatrix();
  2553.             GL11.glPushMatrix();
  2554.             {
  2555.                 GL11.glRotatef(90,0,1,0);
  2556.                 GL11.glTranslatef(0,0,halfsize);
  2557.                 renderPlane(size,segments);// right
  2558.             }
  2559.             GL11.glPopMatrix();
  2560.             GL11.glPushMatrix();
  2561.             {
  2562.                 GL11.glRotatef(180,0,1,0);
  2563.                 GL11.glTranslatef(0,0,halfsize);
  2564.                 renderPlane(size,segments);// back
  2565.             }
  2566.             GL11.glPopMatrix();
  2567.             GL11.glPushMatrix();
  2568.             {
  2569.                 GL11.glRotatef(270,0,1,0);
  2570.                 GL11.glTranslatef(0,0,halfsize);
  2571.                 renderPlane(size,segments);// left
  2572.             }
  2573.             GL11.glPopMatrix();
  2574.             GL11.glPushMatrix();
  2575.             {
  2576.                 GL11.glRotatef(90,1,0,0);
  2577.                 GL11.glTranslatef(0,0,halfsize);
  2578.                 renderPlane(size,segments);// bottom
  2579.             }
  2580.             GL11.glPopMatrix();
  2581.             GL11.glPushMatrix();
  2582.             {
  2583.                 GL11.glRotatef(-90,1,0,0);
  2584.                 GL11.glTranslatef(0,0,halfsize);
  2585.                 renderPlane(size,segments);// top
  2586.             }
  2587.             GL11.glPopMatrix();
  2588.         }
  2589.         GL11.glPopMatrix();
  2590.     }
  2591.  
  2592.     /**
  2593.      * draw a square plane in the X,Y axis, centered at origin.  Include texture coordinates.
  2594.      * @param size       length of each side
  2595.      * @param segments   number of segments to divide each side into
  2596.      */
  2597.     public static void renderPlane(float size, int segments) {
  2598.         renderPlane(size,size,segments,segments);
  2599.     }
  2600.  
  2601.     /**
  2602.      * draw a rectangular plane in the X,Y axis, centered at origin, with the specified size and
  2603.      * number of divisions.  Texture will cover entire rectangle without repeating.
  2604.      * @param length     length of X axis side
  2605.      * @param height     length of Y axis side
  2606.      * @param segments   number of segments to divide each side into
  2607.      */
  2608.     public static void renderPlane(float length, float height, int length_segments, int height_segments) {
  2609.         renderPlane(length, height, length_segments, height_segments, 1, 1);
  2610.     }
  2611.  
  2612.     /**
  2613.      * draw a rectangular plane in the X,Y axis, centered at origin.  Include texture coordinates.
  2614.      * Scale the UV coordinates to same proportion as plane dimensions. Texture will repeat as
  2615.      * specified by the tilefactorU and tilefactorV values.  If tilefactor values are 1, the texture
  2616.      * will cover the rectangle without tiling.
  2617.      * @param length    length on X axis
  2618.      * @param depth     length on Y axis
  2619.      * @param segments  number of segments to divide each side into
  2620.      */
  2621.     public static void renderPlane(float length, float height, int length_segments, int height_segments, float tilefactorU, float tilefactorV) {
  2622.         float xpos = - length/2f;
  2623.         float ypos = - height/2f;
  2624.         float segsizeL = length/(float)length_segments;
  2625.         float segsizeH = height/(float)height_segments;
  2626.         float maxDimension = (length > height)? length : height;
  2627.         float uvsegsizeL = (length/maxDimension) / (float)length_segments;
  2628.         float uvsegsizeH = (height/maxDimension) / (float)height_segments;
  2629.         GL11.glBegin(GL11.GL_QUADS); {
  2630.             GL11.glNormal3f(0f, 0f, 1f);   // plane is facing up the Z axis
  2631.             for (int x=0; x < length_segments; x++, xpos+=segsizeL) {
  2632.                 for (int y=0; y < height_segments; y++, ypos+=segsizeH) {
  2633.                     // bottom left
  2634.                     GL11.glTexCoord2f((x*uvsegsizeL)*tilefactorU, (y*uvsegsizeH)*tilefactorV);
  2635.                     GL11.glVertex3f( xpos, ypos, 0f);
  2636.                     // bottom rite
  2637.                     GL11.glTexCoord2f(((x*uvsegsizeL)+uvsegsizeL)*tilefactorU, (y*uvsegsizeH)*tilefactorV);
  2638.                     GL11.glVertex3f( xpos+segsizeL, ypos,  0f);
  2639.                     // top rite
  2640.                     GL11.glTexCoord2f(((x*uvsegsizeL)+uvsegsizeL)*tilefactorU, ((y*uvsegsizeH)+uvsegsizeH)*tilefactorV);
  2641.                     GL11.glVertex3f( xpos+segsizeL,  ypos+segsizeH, 0f);
  2642.                     // top left
  2643.                     GL11.glTexCoord2f((x*uvsegsizeL)*tilefactorU, ((y*uvsegsizeH)+uvsegsizeH)*tilefactorV);
  2644.                     GL11.glVertex3f( xpos,  ypos+segsizeH, 0f);
  2645.                 }
  2646.                 ypos = - height/2f; // reset column position
  2647.             }
  2648.         }
  2649.         GL11.glEnd();
  2650.     }
  2651.  
  2652.     /**
  2653.      * draw a rectangular plane in the X,Y axis, centered at origin.  Include texture coordinates.
  2654.      * Scale the UV coordinates to same proportion as plane dimensions.
  2655.      * @param length    length on X axis
  2656.      * @param depth     length on Y axis
  2657.      * @param segments  number of segments to divide each side into
  2658.      */
  2659.     public static void renderPlaneORIG(float length, float height, int length_segments, int height_segments) {
  2660.         float xpos = - length/2f;
  2661.         float ypos = - height/2f;
  2662.         float segsizeL = length/(float)length_segments;
  2663.         float segsizeH = height/(float)height_segments;
  2664.         float maxDimension = (length > height)? length : height;
  2665.         float uvsegsizeL = (length/maxDimension) / (float)length_segments;
  2666.         float uvsegsizeH = (height/maxDimension) / (float)height_segments;
  2667.         GL11.glBegin(GL11.GL_QUADS); {
  2668.             GL11.glNormal3f(0f, 0f, 1f);   // plane is facing up the Z axis
  2669.             for (int x=0; x < length_segments; x++, xpos+=segsizeL) {
  2670.                 for (int y=0; y < height_segments; y++, ypos+=segsizeH) {
  2671.                     // bottom left
  2672.                     GL11.glTexCoord2f(x*uvsegsizeL, y*uvsegsizeH);
  2673.                     GL11.glVertex3f( xpos, ypos, 0f);
  2674.                     // bottom rite
  2675.                     GL11.glTexCoord2f((x*uvsegsizeL)+uvsegsizeL, y*uvsegsizeH);
  2676.                     GL11.glVertex3f( xpos+segsizeL, ypos,  0f);
  2677.                     // top rite
  2678.                     GL11.glTexCoord2f((x*uvsegsizeL)+uvsegsizeL, (y*uvsegsizeH)+uvsegsizeH);
  2679.                     GL11.glVertex3f( xpos+segsizeL,  ypos+segsizeH, 0f);
  2680.                     // top left
  2681.                     GL11.glTexCoord2f(x*uvsegsizeL, (y*uvsegsizeH)+uvsegsizeH);
  2682.                     GL11.glVertex3f( xpos,  ypos+segsizeH, 0f);
  2683.                 }
  2684.                 ypos = - height/2f; // reset column position
  2685.             }
  2686.         }
  2687.         GL11.glEnd();
  2688.     }
  2689.  
  2690.     /**
  2691.      * call the LWJGL Sphere class to draw sphere geometry
  2692.      * with texture coordinates and normals
  2693.      * @param facets  number of divisions around longitude and latitude
  2694.      */
  2695.     public static void renderSphere(int facets) {
  2696.         Sphere s = new Sphere();            // an LWJGL class
  2697.         s.setOrientation(GLU.GLU_OUTSIDE);  // normals point outwards
  2698.         s.setTextureFlag(true);             // generate texture coords
  2699.         GL11.glPushMatrix();
  2700.         {
  2701.             GL11.glRotatef(-90f, 1,0,0);    // rotate the sphere to align the axis vertically
  2702.             s.draw(1, facets, facets);              // run GL commands to draw sphere
  2703.         }
  2704.         GL11.glPopMatrix();
  2705.     }
  2706.  
  2707.     /**
  2708.      * draw a sphere with 48 facets (pretty smooth) with normals and texture coords
  2709.      */
  2710.     public static void renderSphere() {
  2711.         renderSphere(48);
  2712.     }
  2713.  
  2714.  
  2715.     /**
  2716.      * Sets glLineWidth() and glPointSize() to the given width.  This will
  2717.      * affect geometry drawn using glBegin(GL_LINES), GL_LINE_STRIP, and GL_POINTS.
  2718.      * May only work with widths up to 10 (depends on hardware).
  2719.      */
  2720.     public static void setLineWidth(int width)
  2721.     {
  2722.         GL11.glLineWidth(width);
  2723.         GL11.glPointSize(width);
  2724.         //GL11.glEnable(GL11.GL_POINT_SMOOTH);
  2725.         //GL11.glEnable(GL11.GL_LINE_SMOOTH);
  2726.     }
  2727.  
  2728.     /**
  2729.      * Set the current color with RGBA floats in range 0-1.  The current color
  2730.      * is disabled when lighting is enabled.  When lighting is enabled (glEnable(GL_LIGHTING))
  2731.      * then material colors are in effect and the current color is ignored.
  2732.      */
  2733.     public static void setColor(float R, float G, float B, float A)
  2734.     {
  2735.         GL11.glColor4f(R,G,B,A);
  2736.     }
  2737.  
  2738.     /**
  2739.      * Set the current color with RGBA bytes in range 0-255. The current color
  2740.      * is disabled when lighting is enabled.  When lighting is enabled (glEnable(GL_LIGHTING))
  2741.      * then material colors are in effect and the current color is ignored.
  2742.      */
  2743.     public static void setColorB(int R, int G, int B, int A)
  2744.     {
  2745.         GL11.glColor4ub((byte)R,(byte)G,(byte)B,(byte)A);
  2746.     }
  2747.  
  2748.     /**
  2749.      * Set the current color to the given RGB or RGBA float array.  Floats are
  2750.      * in range 0-1. The current color is disabled when lighting is enabled.
  2751.      * When lighting is enabled (glEnable(GL_LIGHTING)) then
  2752.      * material colors are in effect and the current color is ignored.
  2753.      */
  2754.     public static void setColor(float[] rgba)
  2755.     {
  2756.         if (rgba != null) {
  2757.             if (rgba.length == 4) {
  2758.                 GL11.glColor4f(rgba[0],rgba[1],rgba[2],rgba[3]);
  2759.             }
  2760.             else if (rgba.length == 3) {
  2761.                 GL11.glColor4f(rgba[0],rgba[1],rgba[2],1);
  2762.             }
  2763.         }
  2764.     }
  2765.  
  2766.     /**
  2767.      * Enable/disable the color-material setting.  When enabled, the glColor() command
  2768.      * will change the current material color.  This provides a convenient and
  2769.      * efficient way to change material colors without having to call glMaterial().
  2770.      * When disabled, the glColor() command functions normally (has no affect on
  2771.      * material colors).
  2772.      *
  2773.      * @param on   when true, glColor() will set the current material color
  2774.      */
  2775.     public static void setColorMaterial(boolean on)
  2776.     {
  2777.         if (on) {
  2778.             // glColor() will change the diffuse and ambient material colors
  2779.             GL11.glColorMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT_AND_DIFFUSE);
  2780.             GL11.glEnable(GL11.GL_COLOR_MATERIAL);
  2781.         }
  2782.         else {
  2783.             // glColor() behaves normally
  2784.             GL11.glDisable(GL11.GL_COLOR_MATERIAL);
  2785.         }
  2786.     }
  2787.  
  2788.     //========================================================================
  2789.     // Functions to build a character set and draw text strings.
  2790.     //
  2791.     // Example:
  2792.     //           buildFont("Font_tahoma.png");
  2793.     //           ...
  2794.     //           glPrint(100, 100, 0, "Here's some text");
  2795.     //           ...
  2796.     //           destroyFont();   // cleanup
  2797.     //========================================================================
  2798.  
  2799.     static int fontListBase = -1;           // Base Display List For The character set
  2800.     static int fontTextureHandle = -1;      // Texture handle for character set image
  2801.  
  2802.     /**
  2803.      * Build a character set from the given texture image.
  2804.      *
  2805.      * @param charSetImage   texture image containing 256 characters in a 16x16 grid
  2806.      * @param fontWidth      how many pixels to allow per character on screen
  2807.      *
  2808.      * @see       destroyFont()
  2809.      */
  2810.     public static boolean buildFont(String charSetImage, int fontWidth)
  2811.     {
  2812.         // make texture from image
  2813.         GLImage textureImg = loadImage(charSetImage);
  2814.         if (textureImg == null) {
  2815.             return false;  // image not found
  2816.         }
  2817.         //pushAttrib();
  2818.         fontTextureHandle = makeTexture(textureImg);
  2819.         // build character set as call list of 256 textured quads
  2820.         buildFont(fontTextureHandle, fontWidth);
  2821.         //popAttrib();
  2822.         return true;
  2823.     }
  2824.  
  2825.     /**
  2826.       * Build the character set display list from the given texture.  Creates
  2827.       * one quad for each character, with one letter textured onto each quad.
  2828.       * Assumes the texture is a 256x256 image containing every
  2829.       * character of the charset arranged in a 16x16 grid.  Each character
  2830.       * is 16x16 pixels.  Call destroyFont() to release the display list memory.
  2831.       *
  2832.       * Should be in ORTHO (2D) mode to render text (see setOrtho()).
  2833.       *
  2834.       * Special thanks to NeHe and Giuseppe D'Agata for the "2D Texture Font"
  2835.       * tutorial (http://nehe.gamedev.net).
  2836.       *
  2837.       * @param charSetImage   texture image containing 256 characters in a 16x16 grid
  2838.       * @param fontWidth      how many pixels to allow per character on screen
  2839.       *
  2840.       * @see       destroyFont()
  2841.       */
  2842.     public static void buildFont(int fontTxtrHandle, int fontWidth)
  2843.     {
  2844.         float factor = 1f/16f;
  2845.         float cx, cy;
  2846.         fontListBase = GL11.glGenLists(256); // Creating 256 Display Lists
  2847.         for (int i = 0; i < 256; i++) {
  2848.             cx = (float) (i % 16) / 16f;              // X Texture Coord Of Character (0 - 1.0)
  2849.             cy = (float) (i / 16) / 16f;              // Y Texture Coord Of Character (0 - 1.0)
  2850.             GL11.glNewList(fontListBase + i, GL11.GL_COMPILE); // Start Building A List
  2851.             GL11.glBegin(GL11.GL_QUADS);              // Use A 16x16 pixel Quad For Each Character
  2852.             GL11.glTexCoord2f(cx, 1 - cy - factor);  // Texture Coord (Bottom Left)
  2853.             GL11.glVertex2i(0, 0);
  2854.             GL11.glTexCoord2f(cx + factor, 1 - cy - factor); // Texture Coord (Bottom Right)
  2855.             GL11.glVertex2i(16, 0);
  2856.             GL11.glTexCoord2f(cx + factor, 1 - cy);   // Texture Coord (Top Right)
  2857.             GL11.glVertex2i(16, 16);
  2858.             GL11.glTexCoord2f(cx, 1 - cy);             // Texture Coord (Top Left)
  2859.             GL11.glVertex2i(0, 16);
  2860.             GL11.glEnd();                              // Done Building Our Quad (Character)
  2861.             GL11.glTranslatef(fontWidth, 0, 0);        // Move To The Right Of The Character
  2862.             GL11.glEndList();                          // Done Building The Display List
  2863.         } // Loop Until All 256 Are Built
  2864.     }
  2865.  
  2866.     /**
  2867.      * Clean up the allocated display lists for the character set.
  2868.      */
  2869.     public static void destroyFont()
  2870.     {
  2871.         if (fontListBase != -1) {
  2872.             GL11.glDeleteLists(fontListBase,256);
  2873.             fontListBase = -1;
  2874.         }
  2875.     }
  2876.  
  2877.     /**
  2878.      * Render a text string in 2D over the scene, using the character set created
  2879.      * by buildFont().
  2880.      *
  2881.      * @param x  screen pixel position of the string
  2882.      * @param y
  2883.      * @param msg  text string to draw
  2884.      */
  2885.     public static void print(int x, int y, String msg)
  2886.     {
  2887.         print(x,y,msg,0);
  2888.     }
  2889.  
  2890.     /**
  2891.      * Render a text string in 2D over the scene, using the character set created
  2892.      * by buildFont().
  2893.      *
  2894.      * @param x  screen pixel position of the string
  2895.      * @param y
  2896.      * @param msg  text string to draw
  2897.      * @param set  which of the two character sets: 0 or 1
  2898.      */
  2899.     public static void print(int x, int y, String msg, int set)
  2900.     {
  2901.         // if font is not initiallized, try loading default font
  2902.         if (fontListBase == -1 || fontTextureHandle == -1) {
  2903.             if (!buildFont("images/font_tahoma.png", 12)) {
  2904.                 err("GLApp.print(): character set has not been created -- see buildFont()");
  2905.                 return;
  2906.             }
  2907.         }
  2908.         if (msg != null) {
  2909.             int offset = fontListBase - 32 + (128 * set);
  2910.             // preserve current GL settings
  2911.             pushAttribOrtho();
  2912.             // turn off lighting
  2913.             GL11.glDisable(GL11.GL_LIGHTING);
  2914.             // enable alpha blending, so character background is transparent
  2915.             GL11.glEnable(GL11.GL_BLEND);
  2916.             GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
  2917.             // enable the charset texture
  2918.             GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
  2919.             // prepare to render in 2D
  2920.             setOrthoOn();
  2921.             // draw the text
  2922.             GL11.glTranslatef(x, y, 0);        // Position The Text (in pixel coords)
  2923.             for(int i=0; i<msg.length(); i++) {
  2924.                 GL11.glCallList(offset + msg.charAt(i));
  2925.             }
  2926.             // restore the original positions and views
  2927.             setOrthoOff();
  2928.             // restore previous settings
  2929.             popAttrib();
  2930.         }
  2931.     }
  2932.  
  2933.     /**
  2934.      * Render a text string in model space, using the character set created
  2935.      * by buildFont().
  2936.      */
  2937.     public static void printZ(float x, float y, float z, int set, float scale, String msg)
  2938.     {
  2939.         int offset;
  2940.         if (fontListBase == -1 || fontTextureHandle == -1) {
  2941.             // font is not initiallized, try this default
  2942.             if (!buildFont("images/font_tahoma.png", 12)) {
  2943.                 err("GLApp.printZ(): character set has not been created -- see buildFont()");
  2944.                 return;
  2945.             }
  2946.         }
  2947.         offset = fontListBase - 32 + (128 * set);
  2948.         if (msg != null) {
  2949.             // enable the charset texture
  2950.             GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureHandle);
  2951.             // draw the text
  2952.             GL11.glPushMatrix();
  2953.             {
  2954.                 GL11.glTranslatef(x, y, z); // Position The Text (in pixels coords)
  2955.                 GL11.glScalef(scale,scale,scale);  // make it smaller (arbitrary kludge!!!!)
  2956.                 for (int i = 0; i < msg.length(); i++) {
  2957.                     GL11.glCallList(offset + msg.charAt(i));
  2958.                 }
  2959.             }
  2960.             GL11.glPopMatrix();
  2961.         }
  2962.     }
  2963.  
  2964.  
  2965.     //========================================================================
  2966.     // PBuffer functions
  2967.     //
  2968.     // Pbuffers are offscreen buffers that can be rendered into just like
  2969.     // the regular framebuffer.  A pbuffer can be larger than the screen,
  2970.     // which allows for the creation of higher resolution images.
  2971.     //
  2972.     //========================================================================
  2973.  
  2974.     /**
  2975.      * Create a Pbuffer for use as an offscreen buffer, with the given
  2976.      * width and height.  Use selectPbuffer() to make the pbuffer the
  2977.      * context for all subsequent opengl commands.  Use selectDisplay() to
  2978.      * make the Display the context for opengl commands.
  2979.      * <P>
  2980.      * @param width
  2981.      * @param height
  2982.      * @return Pbuffer
  2983.      * @see selectPbuffer(), selectDisplay()
  2984.      */
  2985.     public static Pbuffer makePbuffer(final int width, final int height) {
  2986.         Pbuffer pbuffer = null;
  2987.         try {
  2988.             pbuffer = new Pbuffer(width, height,
  2989.                     new PixelFormat(24, //bitsperpixel
  2990.                             8,  //alpha
  2991.                             24, //depth
  2992.                             8,  //stencil
  2993.                             0), //samples
  2994.                             null,
  2995.                             null);
  2996.         } catch (LWJGLException e) {
  2997.             err("GLApp.makePbuffer(): exception " + e);
  2998.         }
  2999.         return pbuffer;
  3000.     }
  3001.  
  3002.     /**
  3003.      * Make the pbuffer the current context for opengl commands.  All following
  3004.      * gl functions will operate on this buffer instead of the display.
  3005.      * <P>
  3006.      * NOTE: the Pbuffer may be recreated if it was lost since last used.  It's
  3007.      * a good idea to use:
  3008.      * <PRE>
  3009.      *         pbuff = selectPbuffer(pbuff);
  3010.      * </PRE>
  3011.      * to hold onto the new Pbuffer reference if Pbuffer was recreated.
  3012.      *
  3013.      * @param pb  pbuffer to make current
  3014.      * @return    Pbuffer
  3015.      * @see       selectDisplay(), makePbuffer()
  3016.      */
  3017.     public static Pbuffer selectPbuffer(Pbuffer pb) {
  3018.         if (pb != null) {
  3019.             try {
  3020.                 // re-create the buffer if necessary
  3021.                 if (pb.isBufferLost()) {
  3022.                     int w = pb.getWidth();
  3023.                     int h = pb.getHeight();
  3024.                     msg("GLApp.selectPbuffer(): Buffer contents lost - recreating the pbuffer");
  3025.                     pb.destroy();
  3026.                     pb = makePbuffer(w, h);
  3027.                 }
  3028.                 // select the pbuffer for rendering
  3029.                 pb.makeCurrent();
  3030.             }
  3031.             catch (LWJGLException e) {
  3032.                 err("GLApp.selectPbuffer(): exception " + e);
  3033.             }
  3034.         }
  3035.         return pb;
  3036.     }
  3037.  
  3038.     /**
  3039.      * Make the Display the current context for OpenGL commands.  Subsequent
  3040.      * gl functions will operate on the Display.
  3041.      *
  3042.      * @see selectPbuffer()
  3043.      */
  3044.     public static void selectDisplay()
  3045.     {
  3046.         try {
  3047.             Display.makeCurrent();
  3048.         } catch (LWJGLException e) {
  3049.             err("GLApp.selectDisplay(): exception " + e);
  3050.         }
  3051.     }
  3052.  
  3053.     /**
  3054.      * Copy the pbuffer contents to a texture.  (Should this use glCopyTexSubImage2D()?
  3055.      * Is RGB the fastest format?)
  3056.      */
  3057.     public static void frameCopy(Pbuffer pbuff, int textureHandle) {
  3058.         GL11.glBindTexture(GL11.GL_TEXTURE_2D,textureHandle);
  3059.         GL11.glCopyTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGB, 0, 0, pbuff.getWidth(), pbuff.getHeight(), 0);
  3060.     }
  3061.  
  3062.     /**
  3063.      * Save the current frame buffer to a PNG image. Same as
  3064.      * screenShot(filename) but the screenshot filename will be automatically
  3065.      * set to <applicationClassName>-<timestamp>.png
  3066.      */
  3067.     public static void screenShot() {
  3068.         screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), rootClass.getName() + "-" + makeTimestamp() + ".png");
  3069.     }
  3070.  
  3071.     /**
  3072.      * Save the current frame buffer to a PNG image. Can also
  3073.      * be used with the PBuffer class to copy large images or textures that
  3074.      * have been rendered into the offscreen pbuffer.
  3075.      */
  3076.     public static void screenShot(String imageFilename) {
  3077.         screenShot(0, 0, displayMode.getWidth(), displayMode.getHeight(), imageFilename);
  3078.     }
  3079.  
  3080.     /**
  3081.      * Save the current Pbuffer to a PNG image. Same as screenShot(filename)
  3082.      * but the Pbuffer will be saved instead of the framebuffer, and the
  3083.      * screenshot filename will be set to <applicationClassName>-<timestamp>.png
  3084.      * NOTE: Have to call selectPbuffer() before calling this function.
  3085.      */
  3086.     public static void screenShot(Pbuffer pb) {
  3087.         screenShot(0, 0, pb.getWidth(), pb.getHeight(), rootClass.getName() + "_" + makeTimestamp() + ".png");
  3088.     }
  3089.  
  3090.     /**
  3091.      * Save a region of the current render buffer to a PNG image.  If the current
  3092.      * buffer is the framebuffer then this will work as a screen capture.  Can
  3093.      * also be used with the PBuffer class to copy large images or textures that
  3094.      * have been rendered into the offscreen pbuffer.
  3095.      * <P>
  3096.      * WARNING: this function hogs memory!  Call java with more memory
  3097.      * (java -Xms128m -Xmx128m)
  3098.      * <P>
  3099.      * @see   selectPbuffer(Pbuffer)
  3100.      * @see   selectDisplay()
  3101.      * @see   savePixelsToPNG()
  3102.      */
  3103.     public static void screenShot(int x, int y, int width, int height, String imageFilename) {
  3104.         // allocate space for ARBG pixels
  3105.         ByteBuffer framebytes = allocBytes(width * height * SIZE_INT);
  3106.         int[] pixels = new int[width * height];
  3107.         // grab the current frame contents as ARGB ints (BGRA ints reversed)
  3108.         GL11.glReadPixels(x, y, width, height, GL12.GL_BGRA, GL12.GL_UNSIGNED_INT_8_8_8_8_REV, framebytes);
  3109.         // copy ARGB data from ByteBuffer to integer array
  3110.         framebytes.asIntBuffer().get(pixels, 0, pixels.length);
  3111.         // free up this memory
  3112.         framebytes = null;
  3113.         // flip the pixels vertically and save to file
  3114.         GLImage.savePixelsToPNG(pixels, width, height, imageFilename, true);
  3115.     }
  3116.  
  3117.     /**
  3118.      * Save a ByteBuffer of ARGB pixels to a PNG file.
  3119.      * If flipY is true, flip the pixels on the Y axis before saving.
  3120.      */
  3121.     public static void savePixelsToPNG(ByteBuffer framebytes, int width, int height, String imageFilename, boolean flipY) {
  3122.         if (framebytes != null && imageFilename != null) {
  3123.             // copy ARGB data from ByteBuffer to integer array
  3124.             int[] pixels = new int[width * height];
  3125.             framebytes.asIntBuffer().get(pixels, 0, pixels.length);
  3126.             // save pixels to file
  3127.             GLImage.savePixelsToPNG(pixels, width, height, imageFilename, flipY);
  3128.         }
  3129.     }
  3130.  
  3131.     /**
  3132.      * Save the contents of the current render buffer to a PNG image. This is
  3133.      * an older version of screenShot() that used the default OpenGL GL_RGBA
  3134.      * pixel format which had to be swizzled into an ARGB format. I'm
  3135.      * keeping the function here for reference.
  3136.      * <P>
  3137.      * If the current buffer is the framebuffer then this will work as a screen capture.
  3138.      * Can also be used with the PBuffer class to copy large images or textures that
  3139.      * have been rendered into the offscreen pbuffer.
  3140.      * <P>
  3141.      * WARNING: this function hogs memory!  Call java with more memory
  3142.      * (java -Xms128m -Xmx128)
  3143.      * <P>
  3144.      * @see   selectPbuffer(), selectDisplay()
  3145.      */
  3146.     public static void screenShotRGB(int width, int height, String saveFilename) {
  3147.         // allocate space for RBG pixels
  3148.         ByteBuffer framebytes = GLApp.allocBytes(width * height * 3);
  3149.         int[] pixels = new int[width * height];
  3150.         int bindex;
  3151.         // grab a copy of the current frame contents as RGB (has to be UNSIGNED_BYTE or colors come out too dark)
  3152.         GL11.glReadPixels(0, 0, width, height, GL11.GL_RGB, GL11.GL_UNSIGNED_BYTE, framebytes);
  3153.         // copy RGB data from ByteBuffer to integer array
  3154.         for (int i = 0; i < pixels.length; i++) {
  3155.             bindex = i * 3;
  3156.             pixels[i] =
  3157.                 0xFF000000                                          // A
  3158.                 | ((framebytes.get(bindex) & 0x000000FF) << 16)     // R
  3159.                 | ((framebytes.get(bindex+1) & 0x000000FF) << 8)    // G
  3160.                 | ((framebytes.get(bindex+2) & 0x000000FF) << 0);   // B
  3161.         }
  3162.         // free up some memory
  3163.         framebytes = null;
  3164.         // save to file (flip Y axis before saving)
  3165.         GLImage.savePixelsToPNG(pixels, width, height, saveFilename, true);
  3166.     }
  3167.  
  3168.     //========================================================================
  3169.     // Stencil functions
  3170.     //========================================================================
  3171.  
  3172.     /**
  3173.      * clear the stencil buffer
  3174.      */
  3175.     public static void clearMask() {
  3176.         GL11.glClear(GL11.GL_STENCIL_BUFFER_BIT);
  3177.     }
  3178.  
  3179.  
  3180.     /**
  3181.      *  Begin creating a mask.  This function turns off the color and depth buffers
  3182.      *  so all subsequent drawing will go only into the stencil buffer.
  3183.      *  To use:
  3184.      *          beginMask(1);
  3185.      *          renderModel();  // draw some geometry
  3186.      *          endMask();
  3187.      */
  3188.     public static void beginMask(int maskvalue) {
  3189.         // turn off writing to the color buffer and depth buffer
  3190.         GL11.glColorMask(false, false, false, false);
  3191.         GL11.glDepthMask(false);
  3192.  
  3193.         // enable stencil buffer
  3194.         GL11.glEnable(GL11.GL_STENCIL_TEST);
  3195.  
  3196.         // set the stencil test to ALWAYS pass
  3197.         GL11.glStencilFunc(GL11.GL_ALWAYS, maskvalue, 0xFFFFFFFF);
  3198.         // REPLACE the stencil buffer value with maskvalue whereever we draw
  3199.         GL11.glStencilOp(GL11.GL_REPLACE, GL11.GL_REPLACE, GL11.GL_REPLACE);
  3200.     }
  3201.  
  3202.     /**
  3203.      *  End the mask.  Freeze the stencil buffer and activate the color and depth buffers.
  3204.      */
  3205.     public static void endMask() {
  3206.         // don't let future drawing modify the contents of the stencil buffer
  3207.         GL11.glStencilOp(GL11.GL_KEEP, GL11.GL_KEEP, GL11.GL_KEEP);
  3208.  
  3209.         // turn the color and depth buffers back on
  3210.         GL11.glColorMask(true, true, true, true);
  3211.         GL11.glDepthMask(true);
  3212.     }
  3213.  
  3214.     /**
  3215.      *  Restrict rendering to the masked area.
  3216.      *  To use:
  3217.      *          GLStencil.beginMask(1);
  3218.      *          renderModel();
  3219.      *          GLStencil.endMask();
  3220.      */
  3221.     public static void activateMask(int maskvalue) {
  3222.         // enable stencil buffer
  3223.         GL11.glEnable(GL11.GL_STENCIL_TEST);
  3224.  
  3225.         // until stencil test is disabled, only write to areas where the
  3226.         // stencil buffer equals the mask value
  3227.         GL11.glStencilFunc(GL11.GL_EQUAL, maskvalue, 0xFFFFFFFF);
  3228.     }
  3229.  
  3230.     /**
  3231.      *  turn off the stencil test so stencil has no further affect on rendering.
  3232.      */
  3233.     public static void disableMask() {
  3234.         GL11.glDisable(GL11.GL_STENCIL_TEST);
  3235.     }
  3236.  
  3237.     /**
  3238.      * Return the stencil buffer value at the given screen position.
  3239.      */
  3240.     public static int getMaskValue(int x, int y)
  3241.     {
  3242.         tmpByte.clear();
  3243.         // read the stencil value at the given position, as an unsigned byte, store it in tmpByte
  3244.         GL11.glReadPixels(x, y, 1, 1, GL11.GL_STENCIL_INDEX, GL11.GL_UNSIGNED_BYTE, tmpByte);
  3245.         return (int) tmpByte.get(0);
  3246.     }
  3247.  
  3248.     //========================================================================
  3249.     // Display list functions
  3250.     //
  3251.     // Display lists are OpenGL commands that have been optimized and stored
  3252.     // into memory on the graphics card.  They greatly improve rendering
  3253.     // performance but also "freeze" the geometry, so are not suitable in cases
  3254.     // where the geometry has to change dynamically.
  3255.     //
  3256.     // Display lists have to be deleted from the graphics card when
  3257.     // the program exits, or they can accumulate and consume memory.  The
  3258.     // function destroyDisplayLists() is called by cleanup() to de-allocate
  3259.     // any display lists that were created by these functions.
  3260.     //
  3261.     //========================================================================
  3262.     public static ArrayList displayLists = new ArrayList();  // will hold display list IDs created by beginDisplayList()
  3263.  
  3264.     /**
  3265.      * Begin a display list. All following OpenGL geometry commands (up to endDisplayList())
  3266.      * will be stored in a display list, not drawn to screen.
  3267.      * <P>
  3268.      * To use, create a display list in setup():
  3269.      * <PRE>
  3270.      *      int teapotID = beginDisplayList();
  3271.      *      ... // run teapot render code here
  3272.      *      endDisplayList();
  3273.      * </PRE>
  3274.      *
  3275.      * Then call the display list later in render():
  3276.      * <PRE>
  3277.      *      callDisplayList(teapotID);
  3278.      * </PRE>
  3279.      *
  3280.      * @return integer display list id
  3281.      * @see endDisplayList(), callDisplayList(), destroyDisplayList()
  3282.      */
  3283.     public static int beginDisplayList() {
  3284.         int DL_ID = GL11.glGenLists(1);         // Allocate 1 new Display List
  3285.         GL11.glNewList(DL_ID, GL11.GL_COMPILE); // Start Building A List
  3286.         displayLists.add( new Integer(DL_ID) ); // save the list ID so we can delete it later (see destroyDisplayLists())
  3287.         return DL_ID;
  3288.     }
  3289.  
  3290.     /**
  3291.      * Finish display list creation.  Use this function only after calling
  3292.      * beginDisplayList()
  3293.      *
  3294.      * @see beginDisplayList()
  3295.      */
  3296.     public static void endDisplayList() {
  3297.         GL11.glEndList();
  3298.     }
  3299.  
  3300.     /**
  3301.      * Render the geometry stored in a display list.  Use this function after
  3302.      * calling beginDisplayList() and endDisplayList() to create a display list.
  3303.      *
  3304.      * @see beginDisplayList()
  3305.      * @see endDisplayList()
  3306.      */
  3307.     public static void callDisplayList(int displayListID) {
  3308.         GL11.glCallList(displayListID);
  3309.     }
  3310.  
  3311.     /**
  3312.      * Delete the given display list ID.  Frees up resources on the graphics card.
  3313.      */
  3314.     public static void destroyDisplayList(int DL_ID)
  3315.     {
  3316.         GL11.glDeleteLists(DL_ID,1);
  3317.     }
  3318.  
  3319.     /**
  3320.      * Clean up the allocated display lists.  Called by cleanUp() when app exits.
  3321.      *
  3322.      * @see cleanUp();
  3323.      */
  3324.     public static void destroyDisplayLists()
  3325.     {
  3326.         while (displayLists.size() > 0) {
  3327.             int displaylistID = ((Integer)displayLists.get(0)).intValue();
  3328.             GL11.glDeleteLists(displaylistID,1);
  3329.             displayLists.remove(0);
  3330.         }
  3331.     }
  3332.  
  3333.     //========================================================================
  3334.     // Native IO Buffer allocation functions
  3335.     //
  3336.     // These functions create and populate the native buffers used by LWJGL.
  3337.     //========================================================================
  3338.  
  3339.     public static ByteBuffer allocBytes(int howmany) {
  3340.         return ByteBuffer.allocateDirect(howmany * SIZE_BYTE).order(ByteOrder.nativeOrder());
  3341.     }
  3342.  
  3343.     public static IntBuffer allocInts(int howmany) {
  3344.         return ByteBuffer.allocateDirect(howmany * SIZE_INT).order(ByteOrder.nativeOrder()).asIntBuffer();
  3345.     }
  3346.  
  3347.     public static FloatBuffer allocFloats(int howmany) {
  3348.         return ByteBuffer.allocateDirect(howmany * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
  3349.     }
  3350.  
  3351.     public static DoubleBuffer allocDoubles(int howmany) {
  3352.         return ByteBuffer.allocateDirect(howmany * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
  3353.     }
  3354.  
  3355.     public static ByteBuffer allocBytes(byte[] bytearray) {
  3356.         ByteBuffer bb = ByteBuffer.allocateDirect(bytearray.length * SIZE_BYTE).order(ByteOrder.nativeOrder());
  3357.         bb.put(bytearray).flip();
  3358.         return bb;
  3359.     }
  3360.  
  3361.     public static IntBuffer allocInts(int[] intarray) {
  3362.         IntBuffer ib = ByteBuffer.allocateDirect(intarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asIntBuffer();
  3363.         ib.put(intarray).flip();
  3364.         return ib;
  3365.     }
  3366.  
  3367.     public static FloatBuffer allocFloats(float[] floatarray) {
  3368.         FloatBuffer fb = ByteBuffer.allocateDirect(floatarray.length * SIZE_FLOAT).order(ByteOrder.nativeOrder()).asFloatBuffer();
  3369.         fb.put(floatarray).flip();
  3370.         return fb;
  3371.     }
  3372.  
  3373.     public static DoubleBuffer allocDoubles(double[] darray) {
  3374.         DoubleBuffer fb = ByteBuffer.allocateDirect(darray.length * SIZE_DOUBLE).order(ByteOrder.nativeOrder()).asDoubleBuffer();
  3375.         fb.put(darray).flip();
  3376.         return fb;
  3377.     }
  3378.  
  3379.     public static void put(ByteBuffer b, byte[] values) {
  3380.         b.clear();
  3381.         b.put(values).flip();
  3382.     }
  3383.  
  3384.     public static void put(IntBuffer b, int[] values) {
  3385.         b.clear();
  3386.         b.put(values).flip();
  3387.     }
  3388.  
  3389.     public static void put(FloatBuffer b, float[] values) {
  3390.         b.clear();
  3391.         b.put(values).flip();
  3392.     }
  3393.  
  3394.     public static void put(DoubleBuffer b, double[] values) {
  3395.         b.clear();
  3396.         b.put(values).flip();
  3397.     }
  3398.  
  3399.     /**
  3400.      *  copy ints from the given byteBuffer into the given int array.
  3401.      *  @param b       source ByteBuffer
  3402.      *  @param values  target integer array, must be same length as ByteBuffer capacity/4
  3403.      */
  3404.     public static void get(ByteBuffer b, int[] values) {
  3405.         b.asIntBuffer().get(values, 0, values.length);
  3406.     }
  3407.  
  3408.     /**
  3409.      *  copy ints from the given IntBuffer into the given int array.
  3410.      *  @param b       source IntBuffer
  3411.      *  @param values  target integer array, must be same length as IntBuffer
  3412.      */
  3413.     public static void get(IntBuffer b, int[] values) {
  3414.         b.get(values, 0, values.length);
  3415.     }
  3416.  
  3417.     /**
  3418.      *  return the contents of the byteBuffer as an array of ints.
  3419.      *  @param b  source ByteBuffer
  3420.      */
  3421.     public static int[] getInts(ByteBuffer b) {
  3422.         int[] values = new int[b.capacity()/SIZE_INT];
  3423.         b.asIntBuffer().get(values, 0, values.length);
  3424.         return values;
  3425.     }
  3426.  
  3427.     //========================================================================
  3428.     // Misc functions
  3429.     //========================================================================
  3430.     public static URL appletBaseURL = null;
  3431.     public static Class rootClass = GLApp.class;
  3432.  
  3433.     /**
  3434.      * Open the given file and return the InputStream.  This function assumes
  3435.      * 1) that we're running an application and the file is in the local filesystem.  If not found, then assume
  3436.      * 2) we're in a jar file and look for the file in the current jar.  If not found, then assume
  3437.      * 3) we're running an applet and look for the file relative to the applet code base.
  3438.      * @param filename to open
  3439.      */
  3440.     public static InputStream getInputStream(String filename) {
  3441.         InputStream in = null;
  3442.        
  3443.         // 1) look for file in local filesystem
  3444.         try {
  3445.             in = new FileInputStream(filename);
  3446.         }
  3447.         catch (IOException ioe) {
  3448.             msg("GLApp.getInputStream (" + filename + "): " + ioe);
  3449.             if (in != null) {
  3450.                 try {
  3451.                     in.close();
  3452.                 }
  3453.                 catch (Exception e) {}
  3454.                 in = null;
  3455.             }
  3456.         }
  3457.         catch (Exception e) {
  3458.             msg("GLApp.getInputStream (" + filename + "): " + e);
  3459.         }
  3460.        
  3461.         // 2) if couldn't open file, look in jar
  3462.         if (in == null && rootClass != null) {
  3463.             // NOTE: class.getResource() looks for files relative to the folder that the class is in.
  3464.             // ideally the class will be an application in the root of the installation, see setRootClass().
  3465.             URL u = null;
  3466.             if (filename.startsWith(".")) {   // remove leading . ie. "./file"
  3467.                 filename = filename.substring(1);
  3468.             }
  3469.             try {u = rootClass.getResource(filename);}
  3470.             catch (Exception ue) {msg("GLApp.getInputStream(): Can't find resource: " + ue);}
  3471.             //msg("GLApp.getInputStream (" +filename+ "): try jar resource url=" + u);
  3472.             if (u != null) {
  3473.                 try {
  3474.                     in = u.openStream();
  3475.                 }
  3476.                 catch (Exception e) {
  3477.                     msg("GLApp.getInputStream (" +filename+ "): Can't load from jar: " + e);
  3478.                 }
  3479.             }
  3480.            
  3481.             // 3) try loading file from applet base url
  3482.             if (in == null && appletBaseURL != null) {
  3483.                 try {u = new URL(appletBaseURL,filename);}
  3484.                 catch (Exception ue) {msg("GLApp.getInputStream(): Can't make applet base url: " + ue);}
  3485.                 //msg("GLApp.getInputStream (" +filename+ "): try applet base url=" + u);
  3486.                 try {
  3487.                     in = u.openStream();
  3488.                 }
  3489.                 catch (Exception e) {
  3490.                     msg("GLApp.getInputStream (" +filename+ "): Can't load from applet base URL: " + e);
  3491.                 }
  3492.             }
  3493.         }
  3494.         return in;
  3495.     }
  3496.  
  3497.     /**
  3498.      * Return an array of bytes read from an InputStream.  Reads all bytes
  3499.      * until the end of stream.  Can read an arbitrary number of bytes.
  3500.      * NOTE: Does not close the inputStream!
  3501.      */
  3502.     public static byte[] getBytesFromStream(InputStream is) {
  3503.         int chunkSize = 1024;
  3504.         int totalRead = 0;
  3505.         int num = 0;
  3506.         byte[] bytes = new byte[chunkSize];
  3507.         ArrayList byteChunks = new ArrayList();
  3508.  
  3509.         // Read the bytes in chunks of 1024
  3510.         try {
  3511.             while ( (num=is.read(bytes)) >= 0) {
  3512.                 byteChunks.add(bytes);
  3513.                 bytes = new byte[chunkSize];
  3514.                 totalRead += num;
  3515.             }
  3516.         }
  3517.         catch (IOException ioe) {
  3518.             err("GLApp.getBytesFromStream(): IOException " + ioe);
  3519.         }
  3520.  
  3521.         int numCopied = 0;
  3522.         bytes = new byte[totalRead];
  3523.  
  3524.         // copy byte chunks to byte array (last chunk may be partial)
  3525.         while (byteChunks.size() > 0) {
  3526.             byte[] byteChunk = (byte[]) byteChunks.get(0);
  3527.             int copylen = (totalRead - numCopied > chunkSize)? chunkSize : (totalRead - numCopied);
  3528.             System.arraycopy(byteChunk, 0, bytes, numCopied, copylen);
  3529.             byteChunks.remove(0);
  3530.             numCopied += copylen;
  3531.         }
  3532.  
  3533.         msg("getBytesFromStream() read " + numCopied + " bytes.");
  3534.  
  3535.         return bytes;
  3536.     }
  3537.  
  3538.     /**
  3539.      *  Return an array of bytes read from a file.
  3540.      */
  3541.     public static byte[] getBytesFromFile(String filename) {
  3542.         InputStream is = getInputStream(filename);
  3543.         byte[] bytes = getBytesFromStream(is);
  3544.         try {
  3545.             is.close();
  3546.         }
  3547.         catch (IOException ioe) {
  3548.             err("GLApp.getBytesFromFile(): IOException " + ioe);
  3549.         }
  3550.         return bytes;
  3551.     }
  3552.  
  3553.     /**
  3554.      *  Return a String array containing the path portion of a filename (result[0]),
  3555.      *  and the fileame (result[1]).  If there is no path, then result[0] will be ""
  3556.      *  and result[1] will be the full filename.
  3557.      */
  3558.     public static String[] getPathAndFile(String filename) {
  3559.         String[] pathAndFile = new String[2];
  3560.         Matcher matcher = Pattern.compile("^.*/").matcher(filename);
  3561.         if (matcher.find()) {
  3562.             pathAndFile[0] = matcher.group();
  3563.             pathAndFile[1] = filename.substring(matcher.end());
  3564.         }
  3565.         else {
  3566.             pathAndFile[0] = "";
  3567.             pathAndFile[1] = filename;
  3568.         }
  3569.         return pathAndFile;
  3570.     }
  3571.  
  3572.     /**
  3573.      * Hold onto this Class for later class.getResource() calls (to load
  3574.      * resources from JAR files, see getInputStream()) and also to get class
  3575.      * name for use in screenshot filenames (see screenShot()).
  3576.      * <P>
  3577.      * To load files from a jar we need to access a class in the root folder
  3578.      * of the installation.  It's not good to use GLApp.class because that
  3579.      * class is in the glapp package folder, and the getResource() function will not
  3580.      * find model, image and sound files because they're a level higher in
  3581.      * the folder tree.  Below we call this.getClass() to record the class of the
  3582.      * application that subclasses GLApp, ie. assume we create an app MyGame that
  3583.      * extends GLApp, and MyGame.class is in the root folder of the installation:
  3584.      * <PRE>
  3585.      *      MyGame.class
  3586.      *      models (folder)
  3587.      *      images (folder)
  3588.      *      sounds (folder)
  3589.      *  </PRE>
  3590.      *  In this case setRootClass() will set the rootClass to MyGame.  If MyGame
  3591.      *  and subfolders are packaged in a jar file, then getInputStream() should
  3592.      *  be able to do a rootClass.getResource("models/some_model.obj") and correctly
  3593.      *  retrieve the file from the JAR.
  3594.      *  <P>
  3595.      *  @see getInputStream()
  3596.      */
  3597.     public void setRootClass() {
  3598.         rootClass = this.getClass();
  3599.     }
  3600.  
  3601.     /**
  3602.      * make a time stamp for filename
  3603.      * @return a string with format "YYYYMMDD-hhmmss"
  3604.      */
  3605.     public static String makeTimestamp()
  3606.     {
  3607.         Calendar now = Calendar.getInstance();
  3608.         int year = now.get(Calendar.YEAR);
  3609.         int month = now.get(Calendar.MONTH) + 1;
  3610.         int day = now.get(Calendar.DAY_OF_MONTH);
  3611.         int hours = now.get(Calendar.HOUR_OF_DAY);
  3612.         int minutes = now.get(Calendar.MINUTE);
  3613.         int seconds = now.get(Calendar.SECOND);
  3614.         String datetime =  ""
  3615.             + year
  3616.             + (month < 10 ? "0" : "") + month
  3617.             + (day < 10 ? "0" : "") + day
  3618.             + "-"
  3619.             + (hours < 10 ? "0" : "") + hours
  3620.             + (minutes < 10 ? "0" : "") + minutes
  3621.             + (seconds < 10 ? "0" : "")  + seconds;
  3622.         return datetime;
  3623.     }
  3624.  
  3625.     /**
  3626.      * Return a random floating point value between 0 and 1
  3627.      */
  3628.     public static float random() {
  3629.         return (float)Math.random();
  3630.     }
  3631.  
  3632.     /**
  3633.      * Return a random floating point value between 0 and upperbound (not including upperbound)
  3634.      */
  3635.     public static float random(float upperbound) {
  3636.         return (float)(Math.random()*(double)upperbound);
  3637.     }
  3638.  
  3639.     /**
  3640.      * Return a random integer value between 0 and upperbound (not including upperbound)
  3641.      */
  3642.     public static int random(int upperbound) {
  3643.         return (int)(Math.random()*(double)upperbound);
  3644.     }
  3645.  
  3646.     /**
  3647.      * Round a float value to the nearest int.
  3648.      */
  3649.     public static int round(float f) {
  3650.         return Math.round(f);
  3651.     }
  3652.  
  3653.     /**
  3654.      * Return true if the OpenGL context supports the given OpenGL extension.
  3655.      */
  3656.     public static boolean extensionExists(String extensionName) {
  3657.         if (OpenGLextensions == null) {
  3658.             String[] GLExtensions = GL11.glGetString(GL11.GL_EXTENSIONS).split(" ");
  3659.             OpenGLextensions = new Hashtable();
  3660.             for (int i=0; i < GLExtensions.length; i++) {
  3661.                 OpenGLextensions.put(GLExtensions[i].toUpperCase(),"");
  3662.             }
  3663.         }
  3664.         return (OpenGLextensions.get(extensionName.toUpperCase()) != null);
  3665.     }
  3666.  
  3667.     /**
  3668.      * Show a debug message on the system console (calls System.out.println()).  If
  3669.      * showMessages flag is false, does nothing.
  3670.      * @param text
  3671.      */
  3672.     public static void msg(String text) {
  3673.         if (showMessages) {
  3674.             System.out.println(text);
  3675.         }
  3676.     }
  3677.  
  3678.     /**
  3679.      * Show an error message on the system console (calls System.out.println()).
  3680.      * Does not check showMessages flag.
  3681.      * @param text
  3682.      */
  3683.     public static void err(String text) {
  3684.         System.out.println(text);
  3685.     }
  3686.  
  3687.     /**
  3688.      * Find a method in the given class with the given method name.  Assumes the method
  3689.      * takes no parameters.  The returned Method can be executed later using invoke()
  3690.      * (similar to a callback function in C/C++).
  3691.      * <P>
  3692.      * NOTE: method invocation is very fast for methods that take no parameters.  If
  3693.      * the method takes parameters then invoking is much slower than calling the function
  3694.      * directly through code.  For this reason and for simplicity I assume there are
  3695.      * no parameters on the function.
  3696.      *
  3697.      * @param object   object that has the method we want to invoke
  3698.      * @param methodName   name of function that we want to invoke
  3699.      * @return the Method object
  3700.      * @see invoke()
  3701.      */
  3702.     public static Method method(Object object, String methodName) {
  3703.         Method M = null;
  3704.         try {
  3705.             // Look for a method with the given name and no parameters
  3706.             M = object.getClass().getMethod(methodName, null);
  3707.         } catch (Exception e) {
  3708.             err("GLApp.method(): Can't find method (" +methodName+ ").  " + e);
  3709.         }
  3710.         return M;
  3711.     }
  3712.  
  3713.     /**
  3714.      * Similar to the static method() function, this looks for the method in the
  3715.      * GLApp class (or it's subclass).
  3716.      *
  3717.      * @param methodName   name of function that we want to invoke
  3718.      * @return the Method object
  3719.      * @see invoke()
  3720.      */
  3721.     public Method method(String methodName) {
  3722.         return method(this,methodName);
  3723.     }
  3724.  
  3725.     /**
  3726.      * Execute a method on the given object.  Assumes the method
  3727.      * takes no parameters. Useful as a callback function.
  3728.      *
  3729.      * @param object (the object to call the method on)
  3730.      * @param method  (the method that will be executed)
  3731.      * @see method()
  3732.      */
  3733.     public static void invoke(Object object, Method method) {
  3734.         if (object != null && method != null){
  3735.             try {
  3736.                 // Call the method with this object as the argument!
  3737.                 method.invoke(object, null);
  3738.             } catch (Exception e) {
  3739.                 // Error handling
  3740.                 System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + object.getClass().getName());
  3741.             }
  3742.         }
  3743.     }
  3744.  
  3745.     /**
  3746.      * Similar to the static invoke() function, this execute a method on the
  3747.      * GLApp class or subclass.  Assumes the method takes no parameters.
  3748.      * Useful as a callback function.
  3749.      *
  3750.      * @param method  (the method that will be executed)
  3751.      * @see method()
  3752.      */
  3753.     public void invoke(Method method) {
  3754.         if (method != null){
  3755.             try {
  3756.                 // Call the method with this object as the argument!
  3757.                 method.invoke(this, null);
  3758.             } catch (Exception e) {
  3759.                 // Error handling
  3760.                 System.err.println("GLApp.invoke(): couldn't invoke method " + method.getName() + " on object " + this.getClass().getName());
  3761.             }
  3762.         }
  3763.     }
  3764. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement