espresso3d.engine.E3DEngine.java Source code

Java tutorial

Introduction

Here is the source code for espresso3d.engine.E3DEngine.java

Source

/*
 * Created on Jul 17, 2004
 *
     
  Copyright 2004 Curtis Moxley
  This file is part of Espresso3D.
     
  Espresso3D is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
    
Espresso3D is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU Lesser General Public License for more details.
    
You should have received a copy of the GNU Lesser General Public License
along with Espresso3D.  If not, see <http://www.gnu.org/licenses/>.
 */
package espresso3d.engine;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.GL11;

import espresso3d.engine.collision.E3DCollisionHandler;
import espresso3d.engine.common.E3DFeatureChecker;
import espresso3d.engine.exceptions.E3DMissingInputHandlerException;
import espresso3d.engine.input.E3DInputHandler;
import espresso3d.engine.logger.E3DEngineLogger;
import espresso3d.engine.logo.E3DLogoRunner;
import espresso3d.engine.renderer.E3DExternalRenderable;
import espresso3d.engine.renderer.E3DGeometryRenderer;
import espresso3d.engine.renderer.base.E3DRenderable;
import espresso3d.engine.renderer.texture.E3DTexture;
import espresso3d.engine.sound.E3DSoundHandler;
import espresso3d.engine.timer.E3DFPSTimer;
import espresso3d.engine.window.E3DWindow;
import espresso3d.engine.window.viewport.E3DViewport;
import espresso3d.engine.world.E3DWorld;

/**
 * @author espresso3d
 *
 * This is the main class for espresso3d.  The engine does all of the work.
 */
public final class E3DEngine extends E3DRenderable {
    public static final String ENGINE_VERSION = "0.5";
    /**
     * This is always true except for debugging purposes during development.
     */
    public static final boolean IN_JAR = true;

    private E3DWindow window;

    private E3DViewport currentViewport; //holds the viewport that is currently being rendered to
    private E3DViewport primarySoundViewport; //holds the viewport used to get the actor for the listener position

    private E3DDebugFlags debugFlags = new E3DDebugFlags();

    //Worlds
    private HashMap worldMap = new HashMap(); //a hashmap of all the E3DWorld objects in the engine keyed on worldID string

    //External renderable list for non display-tree type rendering
    private ArrayList externalRenderables = new ArrayList(); //This is a list that allows external apps to add renderable items to be rendered after rendering the scene (not in the normal rendering loop)

    private boolean running = false;

    //Handlers///////
    //Collisions - inited in constructor
    private E3DCollisionHandler collisionHandler = null;
    //Input - inited in Init method
    private E3DInputHandler inputHandler = null;
    //Sound - inited in the Init method.
    private E3DSoundHandler soundHandler = null;
    //logger
    private E3DEngineLogger logger = null;
    //Feature checker
    private E3DFeatureChecker featureChecker = null;

    //Geometry renderer
    private E3DGeometryRenderer geometryRenderer = null;

    //FPS
    private E3DFPSTimer fpsTimer;

    private boolean backfaceCullingEnabled = false;

    //    private E3DThreadPool threadPool;

    public E3DEngine() {
        super(null); //this is the root

        setCollisionHandler(new E3DCollisionHandler(this));

        fpsTimer = new E3DFPSTimer(this);

        //Initialize logger at start
        logger = new E3DEngineLogger(this);

        //Initialize a threadPool that the engine can use
        //        threadPool = new E3DThreadPool(this, 10);
    }

    /**
     * Builds a textureSet out of all the texture sets in all the worlds
     *   If two worlds have the same entry name, the E3DTexture used for the last checked
     *   world is used.
     * @return
     */
    public HashMap getTextureSet() {
        if (getWorldMap() == null || getWorldMap().entrySet() == null)
            return null;

        HashMap textureSet = new HashMap();
        E3DWorld world;

        Iterator it = getWorldMap().entrySet().iterator();
        Map.Entry entry = null;
        while (it.hasNext()) {
            entry = (Map.Entry) it.next();
            world = (E3DWorld) entry.getValue();

            textureSet.putAll(world.getTextureSet());
        }

        return textureSet;

    }

    /**
     * Searches all worlds for this texture.  This returns
     * the first instance of this textureName it encounters.
     * It's faster to get the texture from the world its in if you know
     * that ahead of time.
     * @param textureName
     * @return
     */
    public E3DTexture getTexture(String textureName) {
        if (worldMap == null || worldMap.isEmpty())
            return null;

        E3DWorld world;
        E3DTexture texture;
        Iterator it = worldMap.entrySet().iterator();
        Map.Entry entry = null;
        while (it.hasNext()) {
            entry = (Map.Entry) it.next();
            world = (E3DWorld) entry.getValue();

            texture = world.getTexture(textureName);
            if (texture != null)
                return texture;
        }

        return null;
    }

    /**
     * Gets the current working directory WITHOUT trailing slashes
     * @return
     */
    public String getWorkingDirectory() {
        return System.getProperty("user.dir");
    }

    public void initEngine(int width, int height, int bitsPerPixel, boolean fullscreen, String windowTitle)
            throws Exception {
        initEngine(-1, -1, width, height, bitsPerPixel, fullscreen, windowTitle);
    }

    /**
     * Create window and initialize OpenGL
     */
    public void initEngine(int posX, int posY, int width, int height, int bitsPerPixel, boolean fullscreen,
            String windowTitle) throws Exception {
        logger.writeLine(E3DEngineLogger.SEVERITY_INFO, "Initializing Espresso3D");

        window = new E3DWindow(this, windowTitle, posX, posY, width, height, bitsPerPixel, fullscreen);
        if (!window.isInitialized())
            throw new Exception("Unable to initialize engine window (E3DWindow). Please check the logs.");

        initGL();

        //This must be done after the display is created
        featureChecker = new E3DFeatureChecker(this);
        soundHandler = new E3DSoundHandler(this);
        inputHandler = new E3DInputHandler(this);
        geometryRenderer = new E3DGeometryRenderer(this);

        setRunning(true);

        E3DLogoRunner.displayEngineLogo(this);
    }

    private void initGL() {
        logger.writeLine(E3DEngineLogger.SEVERITY_INFO, "Initializing OpenGL");

        GL11.glShadeModel(GL11.GL_SMOOTH); // Enable Smooth Shading
        GL11.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Black Background  
        GL11.glClearDepth(1.0); // Depth Buffer Setup
        GL11.glEnable(GL11.GL_DEPTH_TEST); // Enables Depth Testing
        GL11.glDepthFunc(GL11.GL_LEQUAL); // The Type Of Depth Testing To Do
        GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST); // Set Perspective Calculations To Most Accurate
    }

    /**
     * change the screen resolution
     */
    public void changeScreenResolution(int width, int height, int bitsPerPixel, boolean fullscreen,
            String windowTitle) {
        Display.destroy();
        try {
            initEngine(width, height, bitsPerPixel, fullscreen, windowTitle);
        } catch (Exception e) {
            e.printStackTrace();
            System.exit(0);
        }
    }

    /**
     * -------Rendering Operations--------
     */
    /**
     *  Render the current world at a given position.  This is automatically called by the engine
     */
    public void render() {
        //Loop through each viewport
        // rendering the viewport's world at the viewport's cameraActors position/orientation
        Iterator viewPortIterator = window.getViewportMap().entrySet().iterator();
        Map.Entry viewPortEntry = null;

        //Loop through each viewport rendering the scene

        //Be sure to clear the screen only once, and depth buffer for each viewport
        GL11.glClear(GL11.GL_COLOR_BUFFER_BIT); // Clear Screen
        while (viewPortIterator.hasNext()) {
            viewPortEntry = (Map.Entry) viewPortIterator.next();
            currentViewport = (E3DViewport) viewPortEntry.getValue();

            if (currentViewport == primarySoundViewport)
                soundHandler.setListener(currentViewport.getCameraActor());

            GL11.glPushMatrix();
            //Setup the viewport - switch to this viewport for rendering
            currentViewport.switchToViewport();

            //Render the world which in turn renders all sectors, actors, etc.
            E3DWorld curWorld = currentViewport.getWorld();
            if (curWorld != null) //Viewports don't necessarily have to have worlds, so skip the world rendering things in that case
            {
                GL11.glPushMatrix(); //Each external renderable should be on its own stack so it can make GL calls and not affect other things
                curWorld.render();
                GL11.glPopMatrix();
            }

            //****************External Renderables****************//
            //render the external renderables added by the users
            E3DExternalRenderable renderable = null;
            for (int i = 0; i < externalRenderables.size(); i++) {
                renderable = (E3DExternalRenderable) externalRenderables.get(i);
                if (!renderable.update()) {
                    externalRenderables.remove(i);
                    i--;
                } else {
                    GL11.glPushMatrix(); //Each external renderable should be on its own stack so it can make GL calls and not affect other things
                    renderable.getRenderable().render();
                    GL11.glPopMatrix();
                }
            }
            //****************2D Text and Image Rendering - Should be last to be on top of everythign else *************//
            //FIXME: TODO: There is a serious bug in projection/unprojection.
            // This fixes fonts by loading the identity matrix, but it doesn't
            // resolve the issue NOR does it resolve the portal projection issue (which is a much bigger deal).
            GL11.glPushMatrix();
            currentViewport.render();
            GL11.glPopMatrix();

            GL11.glPopMatrix();
        } //End viewport list loop

        //Render the windows GUI stuff that is viewport independent
        GL11.glPushMatrix();
        window.getGuiHandler().render();
        GL11.glPopMatrix();

        Display.update();

        //************FPS Timer*********************//
        fpsTimer.update();
        window.update();

        /*** Sound Handler ******/
        soundHandler.discardOldSounds(); //clear out any stopped sound source from memory
    }

    public void checkInput() throws E3DMissingInputHandlerException {
        if (inputHandler == null)
            throw new E3DMissingInputHandlerException();

        getInputHandler().checkInput();
    }

    /**
     * ------World Operations------
     */

    /**
     * Add a world to the engines world list
     */
    public void addWorld(E3DWorld world) {
        world.setEngine(this);
        worldMap.put(world.getWorldID(), world);
    }

    /**
     * Remove a world from the engine's world list
     * @return
     */
    public void removeWorld(String worldID) {
        if (worldMap.containsKey(worldID))
            worldMap.remove(worldID);
    }

    /**
     * Remove all the worlds in the engine.
     *
     */
    public void removeAllWorlds() {
        worldMap.clear();
    }

    /**
     * Get a world from the map of worlds by its ID
     * @param worldID
     * @return
     */
    public E3DWorld getWorld(String worldID) {
        if (getWorldMap().containsKey(worldID))
            return (E3DWorld) getWorldMap().get(worldID);
        else
            return null;
    }

    /**
     * @return Returns the worldMap.
     */
    public HashMap getWorldMap() {
        return worldMap;
    }

    /**
     * @param inputHandler The inputHandler to set.
     */
    public void setInputHandler(E3DInputHandler inputHandler) {
        this.inputHandler = inputHandler;
    }

    /**
     * @return Returns the inputHandler.
     */
    public E3DInputHandler getInputHandler() {
        return inputHandler;
    }

    /**
     * @return Returns whether the engine is running
     */
    public boolean isRunning() {
        return (running && !Display.isCloseRequested());
    }

    /**
     * @param running Set the engines running state
     */
    public void setRunning(boolean running) {
        this.running = running;
    }

    /**
     * @return Returns the engine's  used for determining collisions
     */
    public E3DCollisionHandler getCollisionHandler() {
        return collisionHandler;
    }

    /**
     * @param collisionDetect The collisionDetect to set.  This is private to the engine
     */
    private void setCollisionHandler(E3DCollisionHandler collisionHandler) {
        this.collisionHandler = collisionHandler;
    }

    /**
     * @return Returns the externalRenderables list.  This is private to the negine
     */
    private ArrayList getExternalRenderables() {
        return externalRenderables;
    }

    /**
     * This adds an E3DRenderable item to the ExternalRenderable list.  
     *   All E3DRenderables in this list will be rendered after the main
     *  rendering hierarchy is rendered  (world, actors, particles, etc) but before any 2D static images or text (so the text/images will be on top of external renderables)
     * Each item on the list will be rendered for the # of frames passed in 
     * This is very useful for debugging (adding CMLines to the rendering loop to see direction vectors and things)
     * It could even potentially be used to plug in your rendering algorithm.
     * as timeoutFrames
     * @param renderableItem An E3DRenderable item that you want to render outside of the optimized rendering loop.
     * @param timeoutFrames How many frames to render this renderable for
     */
    public void addExternalRenderable(E3DRenderable renderableItem, int timeoutFrames) {
        getExternalRenderables().add(new E3DExternalRenderable(renderableItem, timeoutFrames));
    }

    public void removeExternalRenderable(E3DRenderable renderableItem) {
        getExternalRenderables().remove(renderableItem);
    }

    /**
     * A primary viewport for sound must be set.  The cameraActor in the viewport this is set to
     * will be used as the listener for the sound.  This automatically sets up the soundHandler's
     * listener.  The viewport's camera actor must NOT be null at this point.
     * 
     * @param viewport
     */
    public void setPrimarySoundViewport(E3DViewport viewport) {
        if (viewport == null)
            primarySoundViewport = null;
        else {
            primarySoundViewport = viewport;
            soundHandler.setListener(viewport.getCameraActor());
        }
    }

    /*
     *     * Todo: Move these to a window when it supports multiple windows
     */
    /**
     * Returns the timer the engine uses to get its FPS.
     * @return
     */
    public E3DFPSTimer getFpsTimer() {
        return fpsTimer;
    }

    /**
     * Returns the viewport the engine is currently rendering to.  This cannot
     * be set by the user, it is for informational purposes only
     * @return
     */
    public E3DViewport getCurrentViewport() {
        return currentViewport;
    }

    public boolean isBackfaceCullingEnabled() {
        return backfaceCullingEnabled;
    }

    public void setBackfaceCullingEnabled(boolean backfaceCullingEnabled) {
        this.backfaceCullingEnabled = backfaceCullingEnabled;
        if (backfaceCullingEnabled) {
            GL11.glCullFace(GL11.GL_BACK);
            GL11.glEnable(GL11.GL_CULL_FACE);
        } else
            GL11.glDisable(GL11.GL_CULL_FACE);
    }

    public E3DSoundHandler getSoundHandler() {
        return soundHandler;
    }

    public E3DViewport getPrimarySoundViewport() {
        return primarySoundViewport;
    }

    public E3DGeometryRenderer getGeometryRenderer() {
        return geometryRenderer;
    }

    public E3DEngineLogger getLogger() {
        return logger;
    }

    public E3DFeatureChecker getFeatureChecker() {
        return featureChecker;
    }

    public E3DWindow getWindow() {
        return window;
    }

    public E3DDebugFlags getDebugFlags() {
        return debugFlags;
    }
}