org.jtrfp.trcl.core.TR.java Source code

Java tutorial

Introduction

Here is the source code for org.jtrfp.trcl.core.TR.java

Source

/*******************************************************************************
 * This file is part of TERMINAL RECALL
 * Copyright (c) 2012-2015 Chuck Ritola
 * Part of the jTRFP.org project
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Public License v3.0
 * which accompanies this distribution, and is available at
 * http://www.gnu.org/licenses/gpl.html
 * 
 * Contributors:
 *     chuck - initial API and implementation
 ******************************************************************************/
package org.jtrfp.trcl.core;

import java.awt.Color;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Map.Entry;
import java.util.concurrent.Callable;

import javax.media.opengl.GL3;
import javax.swing.JOptionPane;

import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.jtrfp.trcl.KeyStatus;
import org.jtrfp.trcl.MatrixWindow;
import org.jtrfp.trcl.ObjectDefinitionWindow;
import org.jtrfp.trcl.ObjectListWindow;
import org.jtrfp.trcl.OutputDump;
import org.jtrfp.trcl.RenderableSpacePartitioningGrid;
import org.jtrfp.trcl.RootGrid;
import org.jtrfp.trcl.World;
import org.jtrfp.trcl.file.VOXFile;
import org.jtrfp.trcl.flow.Game;
import org.jtrfp.trcl.flow.GameShell;
import org.jtrfp.trcl.gpu.GPU;
import org.jtrfp.trcl.gui.MenuSystem;
import org.jtrfp.trcl.gui.Reporter;
import org.jtrfp.trcl.img.vq.ColorPaletteVectorList;
import org.jtrfp.trcl.math.Vect3D;
import org.jtrfp.trcl.obj.CollisionManager;
import org.jtrfp.trcl.obj.Player;
import org.jtrfp.trcl.snd.SoundSystem;
import org.jtrfp.trcl.tools.Util;

public final class TR {
    //// BEAN PROPERTIES
    public static final String GAME = "game";

    public static final double unitCircle = 65535;
    public static final double crossPlatformScalar = 16;//Shrinks everything so that we can use floats instead of ints
    public static final double mapSquareSize = Math.pow(2, 20) / crossPlatformScalar;
    public static final double mapWidth = mapSquareSize * 256;
    public static final double mapCartOffset = mapWidth / 2.;//This is the scaled-down version, not the full version
    public static final double visibilityDiameterInMapSquares = 35;
    public static final int terrainChunkSideLengthInSquares = 4;//Keep at power of two for now. 4x4 = 16. 16x6 = 96. 96 vertices per GLSL block means 1 chunk per block.
    public static final double antiGamma = 1.6;
    public static final boolean ANIMATED_TERRAIN = false;

    public final TRFutureTask<GPU> gpu;
    public final TRFutureTask<SoundSystem> soundSystem;
    private Player player;
    public final RootWindow rootWindow;
    private final MenuSystem menuSystem;
    private Color[] globalPalette, darkIsClearPalette;
    private ColorPaletteVectorList globalPaletteVL, darkIsClearPaletteVL;
    private final KeyStatus keyStatus;
    private ResourceManager resourceManager;
    public final ThreadManager threadManager;
    public final TRFutureTask<Renderer> mainRenderer;
    private final CollisionManager collisionManager = new CollisionManager(this);
    private final Reporter reporter = new Reporter();
    private Game game;
    private final PropertyChangeSupport pcSupport;

    public final TRFutureTask<MatrixWindow> matrixWindow;
    public final TRFutureTask<ObjectListWindow> objectListWindow;
    public final TRFutureTask<ObjectDefinitionWindow> objectDefinitionWindow;
    private World world;
    private final GameShell gameShell;
    private RenderableSpacePartitioningGrid defaultGrid;

    public final TRConfiguration config = TRConfiguration.getConfig();

    /**
     * Converts legacy coordinate to modern coordinate
     * @param v
     * @return
     * @since Oct 17, 2012
     */
    public static double legacy2Modern(double v) {
        return v < 0 ? (v + 268435456) / crossPlatformScalar : v / crossPlatformScalar;
    }

    public static double modern2Legacy(double v) {
        v *= crossPlatformScalar;
        return v > 134217727 ? v - 268435456 : v;
    }

    public static Vector3D twosComplimentSubtract(Vector3D l, Vector3D r) {
        return new Vector3D(deltaRollover(l.getX() - r.getX()), deltaRollover(l.getY() - r.getY()),
                deltaRollover(l.getZ() - r.getZ()));
    }

    public static double twosComplimentDistance(Vector3D l, Vector3D r) {
        return twosComplimentSubtract(l, r).getNorm();
    }

    public static double deltaRollover(double v) {
        if (v > mapCartOffset)
            return v - mapWidth;
        else if (v < -mapCartOffset)
            return v + mapWidth;
        return v;
    }

    public TR() {
        try {
            new OutputDump();
        } catch (Exception e) {
            e.printStackTrace();
        }
        AutoInitializable.Initializer.initialize(this);
        pcSupport = new PropertyChangeSupport(this);
        rootWindow = new RootWindow();
        if (config.isWaitForProfiler()) {
            waitForProfiler();
        } //end if(waitForProfiler)
        keyStatus = new KeyStatus(rootWindow);
        gpu = new TRFutureTask<GPU>(this, new Callable<GPU>() {
            @Override
            public GPU call() throws Exception {
                return new GPU(TR.this);
            }
        });
        soundSystem = new TRFutureTask<SoundSystem>(this, new Callable<SoundSystem>() {
            @Override
            public SoundSystem call() throws Exception {
                return new SoundSystem(TR.this);
            }
        });
        threadManager = new ThreadManager(this);
        threadManager.threadPool.submit(gpu);
        threadManager.threadPool.submit(soundSystem);//TODO: Use new methods
        System.out.println("Initializing graphics engine...");
        mainRenderer = new TRFutureTask<Renderer>(this, new Callable<Renderer>() {
            @Override
            public Renderer call() throws Exception {
                Thread.currentThread().setName("Renderer constructor.");
                return gpu.get().rendererFactory.get().newRenderer();
            }//end call()
        });
        threadManager.threadPool.submit(mainRenderer);
        matrixWindow = new TRFutureTask<MatrixWindow>(this, new Callable<MatrixWindow>() {
            @Override
            public MatrixWindow call() throws Exception {
                return new MatrixWindow(TR.this);
            }//end call()
        });
        threadManager.threadPool.submit(matrixWindow);
        objectListWindow = new TRFutureTask<ObjectListWindow>(this, new Callable<ObjectListWindow>() {
            @Override
            public ObjectListWindow call() throws Exception {
                return new ObjectListWindow(TR.this);
            }//end call()
        });
        threadManager.threadPool.submit(objectListWindow);
        objectDefinitionWindow = new TRFutureTask<ObjectDefinitionWindow>(this,
                new Callable<ObjectDefinitionWindow>() {
                    @Override
                    public ObjectDefinitionWindow call() throws Exception {
                        return new ObjectDefinitionWindow(TR.this);
                    }//end call()
                });
        threadManager.threadPool.submit(objectDefinitionWindow);
        System.out.println("...Done");
        setResourceManager(new ResourceManager(this));

        final Renderer renderer = mainRenderer.get();
        renderer.setRootGrid(getDefaultGrid());//TODO: replace with Camera objects?
        renderer.setCollisionManager(getCollisionManager());
        getThreadManager().addRepeatingGLTask(renderer.render);

        gameShell = new GameShell(this);
        menuSystem = new MenuSystem(this);
        gameShell.startShell();

        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                try {
                    config.saveConfig();
                } catch (Exception e) {
                    System.err.println("Failed to write the config file.\n" + e.getLocalizedMessage() + "\n");
                } //end catch(Exception)
                System.err.println("Great work, Guys!");
            }//end run()
        });
    }//end constructor

    private void waitForProfiler() {
        JOptionPane.showMessageDialog(rootWindow, "Connect profiler and click OK to continue.", "Connect profiler",
                JOptionPane.OK_OPTION);
    }

    public void showStopper(final Exception e) {
        System.err.println("==== SHOWSTOPPER ====");
        e.printStackTrace();
        System.err.println("======================");
        System.err.println("\nIrrecoverable. Exiting...\n\n");
        System.exit(-1);
    }

    public static int bidiMod(int v, int mod) {
        while (v < 0)
            v += mod;
        v %= mod;
        return v;
    }

    /**
     * A non-diplomatic way of requesting a GC event.
     * In theory, this call will force (probability=100%) a full sweep including Weak and Soft references.
     * Unlike System.gc() which serves as a suggestion to execute a GC sweep, nuclearGC creates an
     * OutOfMemoryError scenario, forcing a sweep in order for the JVM to keep functioning.
     * <br><br>
     * This was created mainly in response to GPU resources depending on being freed by finalization but the 
     * GC ignoring this need as it focuses on CPU resources only. By ensuring the clearance of unused cache 
     * resources after a level-loading, it can be assured to some degree that unneeded GPU resources will also be
     * released.
     * <br><br>
     * WARNING: This is not a substitute for good programming. If live references are left hanging they will
     * not be freed by the GC, ever. Also note that this will clear any well-designed caching system so it is
     * advised to defer calling this method until the program is at its fullest (post-loading) state to avoid 
     * unnecessary cache misses.
     * 
     * Idea adapted from:
     * http://stackoverflow.com/questions/3785713/how-to-make-the-java-system-release-soft-references>
     * 
     * @since Sep 15, 2014
     */
    public static void nuclearGC() {
        try {
            final ArrayList<byte[]> spaceHog = new ArrayList<byte[]>();
            while (true) {
                spaceHog.add(new byte[1024 * 1024 * 16]);//16MB
            }
        } catch (OutOfMemoryError e) {
        }
        //Still alive? Great!
        System.gc();
    }//end nuclearGC()

    /**
     * @return the resourceManager
     */
    public ResourceManager getResourceManager() {
        return resourceManager;
    }

    /**
     * @param resourceManager
     *            the resourceManager to set
     */
    public void setResourceManager(ResourceManager resourceManager) {
        this.resourceManager = resourceManager;
    }

    public static double[] floats2Doubles(float[] toConvert) {
        double[] result = new double[toConvert.length];
        for (int i = 0; i < toConvert.length; i++) {
            result[i] = toConvert[i];
        }
        return result;
    }// end floats2Doubles

    public static float[] doubles2Floats(double[] toConvert) {
        float[] result = new float[toConvert.length];
        for (int i = 0; i < toConvert.length; i++) {
            result[i] = (float) toConvert[i];
        }
        return result;
    }// end floats2Floats

    public boolean isStampingTextures() {
        return false;
    }

    public Game newGame(VOXFile mission) {
        final Game newGame = new Game(this, mission);
        setGame(newGame);
        return newGame;
    }// end newGame(...)

    private void setGame(Game newGame) {
        if (newGame == game)
            return;
        final Game oldGame = game;
        game = newGame;
        if (newGame == null)
            getThreadManager().setPaused(true);
        pcSupport.firePropertyChange("game", oldGame, newGame);
    }

    /**
     * @return the keyStatus
     */
    public KeyStatus getKeyStatus() {
        return keyStatus;
    }

    public Color[] getGlobalPalette() {
        if (globalPalette == null)
            globalPalette = Util.DEFAULT_PALETTE;
        return globalPalette;
    }

    public Color[] getDarkIsClearPalette() {
        if (darkIsClearPalette == null) {
            darkIsClearPalette = new Color[256];
            for (int i = 0; i < 256; i++) {
                float newAlpha = (float) Math
                        .pow(((globalPalette[i].getRed() + globalPalette[i].getGreen() + globalPalette[i].getBlue())
                                / (3f * 255f)), .5);
                darkIsClearPalette[i] = new Color(globalPalette[i].getRed() / 255f,
                        globalPalette[i].getGreen() / 255f, globalPalette[i].getBlue() / 255f, newAlpha);
            } // end for(colors)
        } // end if(null)
        return darkIsClearPalette;
    }// end getDarkIsClearPalette

    /**
     * @return the world
     */
    public World getWorld() {
        if (world == null)
            world = new World(256 * mapSquareSize, 24. * mapSquareSize, 256 * mapSquareSize,
                    mapSquareSize * visibilityDiameterInMapSquares / 2., this);
        return world;
    }

    public ThreadManager getThreadManager() {
        return threadManager;
    }

    public CollisionManager getCollisionManager() {
        return collisionManager;
    }

    /**
     * @return the reporter
     */
    public Reporter getReporter() {
        return reporter;
    }

    public Game getGame() {
        return game;
    }

    public static double legacy2MapSquare(double z) {
        return legacy2Modern(z) / TR.mapSquareSize;
    }

    public void gatherSysInfo() {
        final GPU _gpu = gpu.get();
        final Reporter r = getReporter();
        getThreadManager().submitToGL(new Callable<Void>() {
            @Override
            public Void call() throws Exception {
                r.report("org.jtrfp.trcl.flow.RunMe.glVendor", _gpu.glGetString(GL3.GL_VENDOR));
                r.report("org.jtrfp.trcl.flow.RunMe.glRenderer", _gpu.glGetString(GL3.GL_RENDERER));
                r.report("org.jtrfp.trcl.flow.RunMe.glVersion", _gpu.glGetString(GL3.GL_VERSION));
                r.report("org.jtrfp.trcl.flow.RunMe.availableProcs", Runtime.getRuntime().availableProcessors());
                for (Entry<Object, Object> prop : System.getProperties().entrySet()) {
                    r.report((String) prop.getKey(), prop.getValue());
                }
                return null;
            }// end call()
        }).get();
    }// end gatherSysInfo()

    public void setGlobalPalette(Color[] palette) {
        globalPalette = palette;
    }

    public static double sloppyTwosComplimentTaxicabDistanceXZ(double[] l, double[] r) {
        return deltaRollover(Math.abs(l[0] - r[0]) + Math.abs(l[2] - r[2]));
    }

    public static double twosComplimentDistance(double[] l, double[] r) {
        final double dx = deltaRollover(l[0] - r[0]);
        final double dy = deltaRollover(l[1] - r[1]);
        final double dz = deltaRollover(l[2] - r[2]);
        assert !Vect3D.isAnyNaN(l);
        assert !Vect3D.isAnyNaN(r);
        return Math.sqrt((dx) * (dx) + (dy) * (dy) + (dz) * (dz));
    }

    public static double[] twosComplimentSubtract(double[] l, double[] r, double[] dest) {
        assert !Vect3D.isAnyNaN(l);
        assert !Vect3D.isAnyNaN(r);
        dest[0] = deltaRollover(l[0] - r[0]);
        dest[1] = deltaRollover(l[1] - r[1]);
        dest[2] = deltaRollover(l[2] - r[2]);
        assert !Vect3D.isAnyNaN(dest) : "l=" + l[0] + " " + l[1] + " " + l[2] + " r=" + r[0] + " " + r[1] + " "
                + r[2];
        return dest;
    }

    public RootWindow getRootWindow() {
        return rootWindow;
    }

    public ColorPaletteVectorList getGlobalPaletteVL() {
        if (globalPaletteVL == null)
            globalPaletteVL = new ColorPaletteVectorList(getGlobalPalette());
        return globalPaletteVL;
    }

    public ColorPaletteVectorList getDarkIsClearPaletteVL() {
        if (darkIsClearPaletteVL == null)
            darkIsClearPaletteVL = new ColorPaletteVectorList(getDarkIsClearPalette());
        return darkIsClearPaletteVL;
    }

    public GameShell getGameShell() {
        return gameShell;
    }

    public TR addPropertyChangeListener(String propertyName, PropertyChangeListener l) {
        pcSupport.addPropertyChangeListener(propertyName, l);
        return this;
    }

    public TR removePropertyChangeListener(PropertyChangeListener l) {
        pcSupport.removePropertyChangeListener(l);
        return this;
    }

    public void abortCurrentGame() {
        final Game game = getGame();
        if (game != null)
            game.abort();
        setGame(null);
    }// end abortCurrentGame()

    /**
     * @return the menuSystem
     */
    public MenuSystem getMenuSystem() {
        return menuSystem;
    }

    /**
     * @return the defaultGrid
     */
    public RenderableSpacePartitioningGrid getDefaultGrid() {
        if (defaultGrid == null)
            defaultGrid = world.newRootGrid();
        return defaultGrid;
    }
}//end TR