org.fusfoundation.kranion.Main.java Source code

Java tutorial

Introduction

Here is the source code for org.fusfoundation.kranion.Main.java

Source

/* 
 * The MIT License
 *
 * Copyright 2016 Focused Ultrasound Foundation.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.fusfoundation.kranion;

import org.fusfoundation.kranion.controller.DefaultController;
import org.fusfoundation.kranion.controller.Controller;
import org.fusfoundation.kranion.plugin.PluginFinder;
import org.fusfoundation.kranion.plugin.Plugin;
import org.fusfoundation.kranion.view.View;
import org.fusfoundation.kranion.view.DefaultView;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.util.glu.GLU.*;

import org.lwjgl.BufferUtils;
import java.nio.*;
import java.io.IOException;
import java.util.logging.FileHandler;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.*;

import org.lwjgl.PointerBuffer;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.List;

import org.lwjgl.opencl.CL;

import org.lwjgl.opencl.CLDevice;
import org.lwjgl.opencl.CLPlatform;
import org.lwjgl.opencl.CLContext;

import static org.lwjgl.opencl.CL10.*;
import static org.lwjgl.opencl.CL10GL.*;

import java.lang.reflect.Constructor;

import java.io.File;
import java.awt.event.*;
import javax.swing.*;
import java.util.StringTokenizer;
import java.util.List;

import org.fusfoundation.kranion.model.*;
import org.fusfoundation.kranion.model.image.*;

import static org.lwjgl.opengl.GL20.GL_FRAGMENT_SHADER;
import static org.lwjgl.opengl.GL20.GL_VERTEX_SHADER;
import static org.lwjgl.opengl.GL20.glUniform1f;
import static org.lwjgl.opengl.GL20.glUniform3f;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;

import java.awt.SplashScreen;
import java.awt.geom.Rectangle2D;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Font;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.MissingResourceException;
import java.util.ResourceBundle;
import javax.imageio.ImageIO;
import org.lwjgl.opencl.CL10GL;
import org.lwjgl.opencl.CLCapabilities;
import org.lwjgl.opencl.CLCommandQueue;
import org.lwjgl.opencl.CLDeviceCapabilities;
import static org.lwjgl.opengl.GL20.glGetUniformLocation;

public class Main implements ProgressListener {

    private Model model;
    private Controller controller;
    private View view;

    private int currentBuffer = 0;
    private boolean bufferNeedsRendering[] = new boolean[3];

    public static final int DISPLAY_HEIGHT = 1024;
    public static final int DISPLAY_WIDTH = 1680;
    public static final Logger LOGGER = Logger.getLogger(Main.class.getName());

    public static float OpenGLVersion = -1f;

    public static CLContext CLcontext = null;
    public static CLCommandQueue CLqueue = null;

    public static final boolean GL_DEBUG = false;

    private float viewportAspect = 1f;
    private static List<Plugin> plugins;
    private static List<BackgroundWorker> workers = new ArrayList<>();

    public static void addBackgroundWorker(BackgroundWorker w) {
        workers.add(w);
    }

    public static void removeBackgroundWorker(BackgroundWorker w) {
        workers.remove(w);
    }

    public static void startBackgroundWorker(String name) {
        Iterator<BackgroundWorker> i = workers.iterator();
        while (i.hasNext()) {
            BackgroundWorker w = i.next();
            if (w.getName().compareTo(name) == 0) {
                w.start();
            }
        }
    }

    public static void stopBackgroundWorker(String name) {
        Iterator<BackgroundWorker> i = workers.iterator();
        while (i.hasNext()) {
            BackgroundWorker w = i.next();
            if (w.getName().compareTo(name) == 0) {
                w.stop();
            }
        }
    }

    private void doBackgroundWorkers() {
        Iterator<BackgroundWorker> i = workers.iterator();
        while (i.hasNext()) {
            BackgroundWorker w = i.next();
            w.doWorkStep();
        }
    }

    public static Model getModel() {
        return Main.main.model;
    }

    public static void setModel(Model model) {

        for (int i = 0; i < plugins.size(); i++) {
            plugins.get(i).release();
        }

        //disconnect old model if exists
        if (Main.main.model != null) {
            Main.main.model.deleteObservers();
            Main.main.model.clear();
        }

        Main.main.model = model;
        Main.main.controller.setModel(model);
        Main.main.view.setModel(model);

        // reconnect plugins to new model
        for (int i = 0; i < plugins.size(); i++) {
            plugins.get(i).init(Main.main.controller);
        }

        // reconnect observer controls to new model
        Iterator<Renderable> controls = Renderable.iterator();
        while (controls.hasNext()) {
            Renderable control = controls.next();
            if (control instanceof GUIControl) {
                model.addObserver((GUIControl) control);
            }
        }

        // update workers
        Iterator<BackgroundWorker> i = workers.iterator();
        while (i.hasNext()) {
            BackgroundWorker w = i.next();
            w.setModel(model);
        }
    }

    public static void updateAllControlBindings() {
        if (Main.main.model != null) {
            Iterator<Renderable> controls = Renderable.iterator();
            while (controls.hasNext()) {
                Renderable control = controls.next();
                if (control instanceof GUIControlModelBinding) {
                    ((GUIControlModelBinding) control).doBinding(Main.main.model);
                }
            }
        }
    }

    public static View getView() {
        return Main.main.view;
    }

    public static void setView(View view) {
        if (Main.main.view != null) {
            Main.main.view.setModel(null);
        }
        view.setModel(Main.main.model);
        Main.main.view = view;
    }

    static {
        try {
            LOGGER.addHandler(new FileHandler("errors.log", true));
        } catch (IOException ex) {
            LOGGER.log(Level.WARNING, ex.toString(), ex);
        }
    }

    private static Main main;
    private static long mainThreadId = Thread.currentThread().getId();

    public static void main(String[] args) {
        try {
            main = new Main();

            System.out.println("This is build " + getRbTok("app.version") + ":" + getRbTok("app.build"));

            main.create();

            SplashScreen mySplash = SplashScreen.getSplashScreen();

            if (mySplash != null) {
                mySplash.close();
            }

            main.run();
        } catch (Exception ex) {
            LOGGER.log(Level.SEVERE, ex.toString(), ex);
        } finally {
            if (main != null) {
                main.destroy();
            }
        }
    }

    public Main() {

    }

    public static void update() {
        // If the caller is in the same thread and thus blocking the main loop,
        // we do one cycle of the main loop without input checking.
        // Main idea is to allow a progress indicator to be updated while
        // some lengthy process is running in the main thread calling this update()
        // function periodically.
        if (Thread.currentThread().getId() == mainThreadId) {
            main.handleResize();
            main.nextFrame();

            Main.checkForGLError();
        }
    }

    public void create() throws LWJGLException {

        model = new Model();

        controller = new DefaultController();
        controller.setModel(model);

        view = new DefaultView();

        view.setModel(model);
        view.setPropertyPrefix("Model.Attribute");
        view.setController(controller);
        view.setTag("DefaultView");

        controller.setView(view);

        //Display
        DisplayMode[] modes = Display.getAvailableDisplayModes();

        DisplayMode chosenMode = null;
        int maxDisplayWidth = 0;
        for (int i = 0; i < modes.length; i++) {
            DisplayMode current = modes[i];
            System.out.println(current.getWidth() + "x" + current.getHeight() + "x" + current.getBitsPerPixel()
                    + " " + current.getFrequency() + "Hz");
            if (current.getBitsPerPixel() == 32 && current.getWidth() == 2560 && current.getHeight() == 1440
                    && current.getFrequency() == 60) {
                chosenMode = current;
            } else if (current.getBitsPerPixel() == 32 && current.getFrequency() >= 60) {
                if (current.getWidth() > maxDisplayWidth) {
                    maxDisplayWidth = current.getWidth();
                    chosenMode = current;
                    if (maxDisplayWidth >= DISPLAY_WIDTH) {
                        break;
                    }
                }
            }
        }
        DisplayMode mode = null;

        if (chosenMode == null) {
            mode = new DisplayMode(DISPLAY_WIDTH, DISPLAY_HEIGHT);
        } else {
            mode = chosenMode;
        }

        //        mode = chosenMode;
        System.out.println("Display: " + mode.getBitsPerPixel() + " bpp");
        Display.setDisplayMode(mode);
        //        Display.setFullscreen(true);
        System.out.println("Display: mode set");
        Display.setResizable(true);
        Display.setTitle("Kranion");

        System.out.println("Setting pixel format...");
        PixelFormat pixelFormat = new PixelFormat(24, 8, 24, 8, 1);
        org.lwjgl.opengl.ContextAttribs contextAtribs = new ContextAttribs(2, 1);
        contextAtribs.withForwardCompatible(true);

        try {
            ByteBuffer[] list = new ByteBuffer[3];
            InputStream rstm = this.getClass().getResourceAsStream("/org/fusfoundation/kranion/images/icon32.png");
            BufferedImage img = ImageIO.read(rstm);
            list[0] = this.convertToByteBuffer(img);
            rstm = this.getClass().getResourceAsStream("/org/fusfoundation/kranion/images/icon64.png");
            img = ImageIO.read(rstm);
            list[1] = this.convertToByteBuffer(img);
            rstm = this.getClass().getResourceAsStream("/org/fusfoundation/kranion/images/icon256.png");
            img = ImageIO.read(rstm);
            list[2] = this.convertToByteBuffer(img);
            Display.setIcon(list);
        } catch (Exception e) {
            System.out.println("Failed to set window icon.");
        }

        System.out.println("Creating display...");
        Display.create(pixelFormat, contextAtribs);
        //Display.create();

        System.out.println("GL Vendor: " + org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VENDOR));
        System.out.println("GL Version: " + org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VERSION));
        System.out.println("GLSL Language Version: "
                + org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL20.GL_SHADING_LANGUAGE_VERSION));
        System.out.println("GL Renderer: " + org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_RENDERER));

        checkGLSupport();

        checkCLSupport();

        //Keyboard
        Keyboard.create();

        //Mouse
        Mouse.setGrabbed(false);
        Mouse.create();

        //OpenGL
        initGL();
        resizeGL();

        view.create();

        // load plugins
        try {
            PluginFinder pluginFinder = new PluginFinder();
            pluginFinder.search("plugins");
            plugins = pluginFinder.getPluginCollection();
            for (int i = 0; i < plugins.size(); i++) {
                plugins.get(i).init(controller);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        resizeGL();
        //        resizeGL();

    }

    private ByteBuffer convertToByteBuffer(BufferedImage image) {
        byte[] buffer = new byte[image.getWidth() * image.getHeight() * 4];
        int counter = 0;
        for (int i = 0; i < image.getHeight(); i++) {
            for (int j = 0; j < image.getWidth(); j++) {
                int colorSpace = image.getRGB(j, i);
                buffer[counter + 0] = (byte) ((colorSpace << 8) >> 24);
                buffer[counter + 1] = (byte) ((colorSpace << 16) >> 24);
                buffer[counter + 2] = (byte) ((colorSpace << 24) >> 24);
                buffer[counter + 3] = (byte) (colorSpace >> 24);
                counter += 4;
            }
        }
        return ByteBuffer.wrap(buffer);
    }

    private void checkCLSupport() {
        try {
            // Initialize OpenCL and create a context and command queue
            CL.create();
            System.out.println("\n****************");
            System.out.println("CL created");

            System.out.println(CLPlatform.getPlatforms().size() + " platforms found.");
            CLPlatform platform = null;
            List<CLDevice> devices = null;
            CLDevice selectedDevice = null;
            IntBuffer errcode_ret = BufferUtils.createIntBuffer(1);

            boolean success = false;
            for (int p = 0; p < CLPlatform.getPlatforms().size(); p++) {
                platform = CLPlatform.getPlatforms().get(p);
                System.out.println("Platform : " + platform.getInfoString(CL_PLATFORM_NAME) + " version: "
                        + platform.getInfoString(CL_PLATFORM_VERSION));

                //                PointerBuffer ctxProps = BufferUtils.createPointerBuffer(3);
                //                ctxProps.put(CL_CONTEXT_PLATFORM).put(platform).put(0).flip();

                devices = platform.getDevices(CL_DEVICE_TYPE_GPU);

                for (int d = 0; d < devices.size(); d++) {

                    System.out.println(devices.size() + " GPU devices found.");

                    // long context = clCreateContext(platform, devices, null, null, null);
                    //CLContext context = org.lwjgl.opencl.CLContext.createFromType(platform, Thread.currentThread().getId(), null, Display.getDrawable(), errcode_ret);
                    try {
                        List<CLDevice> deviceCandidate = new ArrayList<CLDevice>();
                        deviceCandidate.add(devices.get(d));

                        CLcontext = org.lwjgl.opencl.CLContext.create(platform, deviceCandidate, null,
                                Display.getDrawable(), errcode_ret);
                        org.lwjgl.opencl.Util.checkCLError(errcode_ret.get(0));
                    } catch (Exception e) {
                        System.out.println("Couldn't create shareable OpenCL context.");
                    }
                    if (errcode_ret.get(0) == CL_SUCCESS) {
                        System.out.println("CL context created");
                        selectedDevice = devices.get(d);
                        success = true;
                    }

                    if (success && selectedDevice != null) {
                        System.out.println("Device : " + selectedDevice.getInfoString(CL_DEVICE_NAME));
                        System.out.println(
                                "Device CL extensions: " + selectedDevice.getInfoString(CL_DEVICE_EXTENSIONS));

                        try {
                            CLDeviceCapabilities abilities = CLCapabilities.getDeviceCapabilities(selectedDevice);
                            System.out.println("3d_image_writes: " + abilities.CL_KHR_3d_image_writes);
                            System.out.println("gl_sharing: " + abilities.CL_KHR_gl_sharing);

                            errcode_ret.clear();

                            org.lwjgl.opencl.CLCommandQueue queue = org.lwjgl.opencl.CL10.clCreateCommandQueue(
                                    CLcontext, selectedDevice,
                                    0 /*org.lwjgl.opencl.CL10.CL_QUEUE_PROFILING_ENABLE*/, errcode_ret);
                            // checkCLError throw a CLExcepetion if the error code does not equal CL_SUCCESS. This exception should be caught and all currently created resources released. See later. 
                            org.lwjgl.opencl.Util.checkCLError(errcode_ret.get(0));
                            CLqueue = queue;

                        } catch (Exception e) {
                            System.out.println("Failed to create a sharable CL ccommand queue with this device.");
                            success = false;
                        }
                    }
                }

                if (success) {
                    break;
                }
            }

            //            org.lwjgl.opencl.CL10.clReleaseContext(CLcontext);    
            //            org.lwjgl.opencl.CL.destroy();
            //            System.out.println("CL context released, CL shutdown");

            System.out.println("****************\n");

        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("*** Problem initializing OpenCL");
        }

    }

    private void checkGLSupport() {
        String vendor = org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VENDOR);
        String version = org.lwjgl.opengl.GL11.glGetString(org.lwjgl.opengl.GL11.GL_VERSION);
        int nMaxTexUnits = org.lwjgl.opengl.GL11.glGetInteger(org.lwjgl.opengl.GL20.GL_MAX_TEXTURE_IMAGE_UNITS);
        int nMaxCombinedTexUnits = org.lwjgl.opengl.GL11
                .glGetInteger(org.lwjgl.opengl.GL20.GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS);

        try {
            float versionVal = 0f;
            System.out.println(
                    "  Texture unit count = " + nMaxTexUnits + ", Max combined textures = " + nMaxCombinedTexUnits);
            StringTokenizer tok = new StringTokenizer(version, ". ");
            if (tok.hasMoreElements()) {
                versionVal = Float.parseFloat(tok.nextToken());
            }
            if (tok.hasMoreElements()) {
                versionVal += Float.parseFloat(tok.nextToken()) / 10f;
            }

            Main.OpenGLVersion = versionVal;

            if (versionVal < 4.5f) {
                JOptionPane.showMessageDialog(null,
                        "OpenGL 4.5 or later required.\n\nYou have:\n" + vendor + "\n" + version);
                //            System.exit(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public void destroy() {
        //Methods already check if created before destroying.
        Mouse.destroy();
        Keyboard.destroy();
        Display.destroy();
    }

    //----------- Variables added for Lighting Test -----------//
    private FloatBuffer matSpecular;
    private FloatBuffer lightPosition;
    private FloatBuffer whiteLight;
    private FloatBuffer lModelAmbient;

    //------- Added for Lighting Test----------//
    private void initLightArrays() {
        matSpecular = BufferUtils.createFloatBuffer(4);
        matSpecular.put(0.3f).put(0.3f).put(0.3f).put(1.0f).flip();

        lightPosition = BufferUtils.createFloatBuffer(4);
        lightPosition.put(150.0f).put(150.0f).put(400.01f).put(0.0f).flip();
        //lightPosition.put(0.0f).put(700.0f).put(700.01f).put(0.0f).flip();

        whiteLight = BufferUtils.createFloatBuffer(4);
        whiteLight.put(1.0f).put(1.0f).put(1.0f).put(1.0f).flip();

        lModelAmbient = BufferUtils.createFloatBuffer(4);
        lModelAmbient.put(0.1f).put(0.1f).put(0.1f).put(1.0f).flip();
    }

    public void initGL() {
        initLightArrays();

        //2D Initialization
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_LIGHTING);
        //        glEnable(GL_POLYGON_SMOOTH); // EVIL
        //        glHint(GL_POLYGON_SMOOTH_HINT, GL_NICEST);
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
        glShadeModel(GL_SMOOTH);
        glMaterial(GL_FRONT_AND_BACK, GL_SPECULAR, matSpecular); // sets specular material color
        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 50.0f); // sets shininess

        // lighting setup
        glLight(GL_LIGHT0, GL_POSITION, lightPosition); // sets light position
        glLight(GL_LIGHT0, GL_SPECULAR, whiteLight); // sets specular light to white
        glLight(GL_LIGHT0, GL_DIFFUSE, whiteLight); // sets diffuse light to white
        glLightModel(GL_LIGHT_MODEL_AMBIENT, lModelAmbient); // global ambient light 
        glEnable(GL_LIGHTING); // enables lighting
        // enables light0
        glEnable(GL_LIGHT0);

        glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
        glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);

        glEnable(GL_COLOR_MATERIAL); // enables opengl to use glColor3f to define material color
        glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); // tell opengl glColor3f effects the ambient and diffuse properties of material

    }

    public void resizeGL() throws org.lwjgl.LWJGLException {
        //2D Scene
        System.out.println("Viewport: " + Display.getWidth() + ", " + Display.getHeight());

        if (Display.getWidth() <= 0 || Display.getHeight() <= 0) {
            return;
        }

        glViewport(0, 0, Display.getWidth(), Display.getHeight());

        //        trackball.set(Display.getWidth() / 2, Display.getHeight() / 2, Display.getHeight() / 2f);
        //        registerBall.set(Display.getWidth() / 2, Display.getHeight() / 2, Display.getHeight() / 2f);
        //        registerBall2.set(Display.getWidth() / 2, Display.getHeight() / 2, Display.getHeight() / 2f);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        viewportAspect = (float) Display.getWidth() / (float) Display.getHeight();
        gluPerspective(40.0f, viewportAspect, 100.0f, 100000.0f);

        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();

        view.doLayout();
    }

    public void run() {
        while (true) {

            handleResize();

            view.processInput();

            nextFrame();

            Main.checkForGLError();

            if (Display.isCloseRequested()) {
                //                int result = JOptionPane.showConfirmDialog(null, "Are you sure you want to exit?", "Exit Kranion", JOptionPane.YES_NO_OPTION);
                //                if (result == JOptionPane.YES_OPTION) {
                //                    break;
                //                }
                if (view.okToExit()) {
                    break;
                }
            }

        }

        view.release();

        if (CLcontext != null) {
            org.lwjgl.opencl.CL10.clReleaseContext(CLcontext);
        }
        org.lwjgl.opencl.CL.destroy();

    }

    public void handleResize() {
        if (Display.wasResized()) {
            try {
                resizeGL();
            } catch (org.lwjgl.LWJGLException e) {
                System.out.println(e);
                System.exit(0);
            }
        }
    }

    public static void processNextFrame() {
        // Spin the event loop for one frame.
        // Modal dialog boxes and others need this
        if (Main.main != null) {
            Main.main.messageLoop();
        }
    }

    protected void messageLoop() {
        handleResize();

        view.processInput();

        nextFrame();

        Main.checkForGLError();
    }

    public void nextFrame() {
        boolean wasRendered = false;
        if (Display.isVisible()) {
            // manage rendering into double buffer
            if (view.getIsDirty()) {
                //                bufferNeedsRendering[0] = bufferNeedsRendering[1] = bufferNeedsRendering[2] = true;
                bufferNeedsRendering[currentBuffer] = true;
            }

            if (bufferNeedsRendering[currentBuffer]) {
                bufferNeedsRendering[currentBuffer] = false;

                // Render the scene
                view.render();
                wasRendered = true;
            } else {
                doBackgroundWorkers();
            }

        }

        Display.processMessages();
        Display.update();
        //        Display.sync(60);
        currentBuffer = (currentBuffer + 1) % 3; // keep track of front/back buffers

        try {
            if (!wasRendered) {
                Thread.sleep(10);
            } else {
                Display.sync(60);
            }
        } catch (Exception e) {
        }
    }

    public static void checkForGLErrorAndThrow() {
        int error = checkForGLError();
        if (error != GL_NO_ERROR) {
            throw new org.lwjgl.opengl.OpenGLException(error);
        }
    }

    public static void printStackTrace() {
        StackTraceElement[] traces;
        traces = Thread.currentThread().getStackTrace();
        for (int i = 2; i < traces.length; i++) {
            System.out.println("\t" + traces[i]);
        }
    }

    public static int checkForGLError() {
        int error = glGetError();
        if (error != GL_NO_ERROR) {
            System.out.println("GL ERROR DETECTED.");
            switch (error) {
            case GL_INVALID_ENUM:
                System.out.println("GL_INVALID_ENUM");
                break;
            case GL_INVALID_VALUE:
                System.out.println("GL_INVALID_VALUE");
                break;
            case GL_INVALID_OPERATION:
                System.out.println("GL_INVALID_OPERATION");
                break;
            case org.lwjgl.opengl.GL30.GL_INVALID_FRAMEBUFFER_OPERATION:
                System.out.println("GL_INVALID_FRAMEBUFFER_OPERATION");
                break;
            case GL_OUT_OF_MEMORY:
                System.out.println("GL_OUT_OF_MEMORY");
                break;
            case GL_STACK_OVERFLOW:
                System.out.println("GL_STACK_OVERFLOW");
                break;
            case GL_STACK_UNDERFLOW:
                System.out.println("GL_STACK_UNDERFLOW");
                break;
            default:
                System.out.println("UNKNOWN GL ERROR: " + error);
            }
            printStackTrace();
        }
        return error;
    }

    @Override
    public void percentDone(String msg, int percent) {
        System.out.println(msg + " - " + percent + "%");
    }

    public static void glPushMatrix() {
        org.lwjgl.opengl.GL11.glPushMatrix();

        if (GL_DEBUG && Main.checkForGLError() != GL_NO_ERROR) {
            System.out.println("MODELVIEW stack depth: " + glGetInteger(GL_MODELVIEW_STACK_DEPTH));
            System.out.println("MODELVIEW max stack depth: " + glGetInteger(GL_MAX_MODELVIEW_STACK_DEPTH));
            System.out.println("PROJECTIONVIEW stack depth: " + glGetInteger(GL_PROJECTION_STACK_DEPTH));
            System.out.println("PROJECTIONVIEW max stack depth: " + glGetInteger(GL_MAX_PROJECTION_STACK_DEPTH));
        }
    }

    public static void glPopMatrix() {
        org.lwjgl.opengl.GL11.glPopMatrix();

        if (GL_DEBUG && Main.checkForGLError() != GL_NO_ERROR) {
            System.out.println("MODELVIEW stack depth: " + glGetInteger(GL_MODELVIEW_STACK_DEPTH));
            System.out.println("MODELVIEW max stack depth: " + glGetInteger(GL_MAX_MODELVIEW_STACK_DEPTH));
            System.out.println("PROJECTIONVIEW stack depth: " + glGetInteger(GL_PROJECTION_STACK_DEPTH));
            System.out.println("PROJECTIONVIEW max stack depth: " + glGetInteger(GL_MAX_PROJECTION_STACK_DEPTH));
        }
    }

    public static void glPushAttrib(int bitmask) {
        org.lwjgl.opengl.GL11.glPushAttrib(bitmask);

        if (GL_DEBUG && Main.checkForGLError() != GL_NO_ERROR) {
            System.out.println("ATTRIB stack depth: " + glGetInteger(GL_ATTRIB_STACK_DEPTH));
            System.out.println("ATTRIB max stack depth: " + glGetInteger(GL_MAX_ATTRIB_STACK_DEPTH));
        }
    }

    public static void glPopAttrib() {
        org.lwjgl.opengl.GL11.glPopAttrib();

        if (GL_DEBUG && Main.checkForGLError() != GL_NO_ERROR) {
            System.out.println("ATTRIB stack depth: " + glGetInteger(GL_ATTRIB_STACK_DEPTH));
            System.out.println("ATTRIB max stack depth: " + glGetInteger(GL_MAX_ATTRIB_STACK_DEPTH));
        }
    }

    final static ResourceBundle appResourceBundle = ResourceBundle.getBundle("version");

    public static final String getRbTok(String propToken) {
        String msg = "";
        try {
            msg = appResourceBundle.getString(propToken);
        } catch (MissingResourceException e) {
            System.err.println("Token ".concat(propToken).concat(" not in Propertyfile!"));
        }
        return msg;
    }
}