/* * Copyright (c) 2009-2012 jMonkeyEngine * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of 'jMonkeyEngine' nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.jme3.app; import com.jme3.app.state.AppState; import com.jme3.audio.AudioListenerState; import com.jme3.font.BitmapFont; import com.jme3.font.BitmapText; import com.jme3.input.FlyByCamera; import com.jme3.input.KeyInput; import com.jme3.input.controls.ActionListener; import com.jme3.input.controls.KeyTrigger; import com.jme3.profile.AppStep; import com.jme3.renderer.RenderManager; import com.jme3.renderer.queue.RenderQueue.Bucket; import com.jme3.scene.Node; import com.jme3.scene.Spatial.CullHint; import com.jme3.system.AppSettings; import com.jme3.system.JmeContext.Type; import com.jme3.system.JmeSystem; /** * <code>SimpleApplication</code> is the base class for all jME3 Applications. * <code>SimpleApplication</code> will display a statistics view * using the {@link com.jme3.app.StatsAppState} AppState. It will display * the current frames-per-second value on-screen in addition to the statistics. * Several keys have special functionality in <code>SimpleApplication</code>:<br/> * * <table> * <tr><td>Esc</td><td>- Close the application</td></tr> * <tr><td>C</td><td>- Display the camera position and rotation in the console.</td></tr> * <tr><td>M</td><td>- Display memory usage in the console.</td></tr> * </table> * * A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can * be removed by calling <code>stateManager.detach( stateManager.getState(FlyCamAppState.class) );</code> */ public abstract class SimpleApplication extends LegacyApplication { public static final String INPUT_MAPPING_EXIT = "SIMPLEAPP_Exit"; public static final String INPUT_MAPPING_CAMERA_POS = DebugKeysAppState.INPUT_MAPPING_CAMERA_POS; public static final String INPUT_MAPPING_MEMORY = DebugKeysAppState.INPUT_MAPPING_MEMORY; public static final String INPUT_MAPPING_HIDE_STATS = "SIMPLEAPP_HideStats"; protected Node rootNode = new Node("Root Node"); protected Node guiNode = new Node("Gui Node"); protected BitmapText fpsText; protected BitmapFont guiFont; protected FlyByCamera flyCam; protected boolean showSettings = true; private AppActionListener actionListener = new AppActionListener(); private class AppActionListener implements ActionListener { public void onAction(String name, boolean value, float tpf) { if (!value) { return; } if (name.equals(INPUT_MAPPING_EXIT)) { stop(); }else if (name.equals(INPUT_MAPPING_HIDE_STATS)){ if (stateManager.getState(StatsAppState.class) != null) { stateManager.getState(StatsAppState.class).toggleStats(); } } } } public SimpleApplication() { this(new StatsAppState(), new FlyCamAppState(), new AudioListenerState(), new DebugKeysAppState()); } public SimpleApplication( AppState... initialStates ) { super(initialStates); } @Override public void start() { // set some default settings in-case // settings dialog is not shown boolean loadSettings = false; if (settings == null) { setSettings(new AppSettings(true)); loadSettings = true; } // show settings dialog if (showSettings) { if (!JmeSystem.showSettingsDialog(settings, loadSettings)) { return; } } //re-setting settings they can have been merged from the registry. setSettings(settings); super.start(); } /** * Retrieves flyCam * @return flyCam Camera object * */ public FlyByCamera getFlyByCamera() { return flyCam; } /** * Retrieves guiNode * @return guiNode Node object * */ public Node getGuiNode() { return guiNode; } /** * Retrieves rootNode * @return rootNode Node object * */ public Node getRootNode() { return rootNode; } public boolean isShowSettings() { return showSettings; } /** * Toggles settings window to display at start-up * @param showSettings Sets true/false * */ public void setShowSettings(boolean showSettings) { this.showSettings = showSettings; } /** * Creates the font that will be set to the guiFont field * and subsequently set as the font for the stats text. */ protected BitmapFont loadGuiFont() { return assetManager.loadFont("Interface/Fonts/Default.fnt"); } @Override public void initialize() { super.initialize(); // Several things rely on having this guiFont = loadGuiFont(); guiNode.setQueueBucket(Bucket.Gui); guiNode.setCullHint(CullHint.Never); viewPort.attachScene(rootNode); guiViewPort.attachScene(guiNode); if (inputManager != null) { // We have to special-case the FlyCamAppState because too // many SimpleApplication subclasses expect it to exist in // simpleInit(). But at least it only gets initialized if // the app state is added. if (stateManager.getState(FlyCamAppState.class) != null) { flyCam = new FlyByCamera(cam); flyCam.setMoveSpeed(1f); // odd to set this here but it did it before stateManager.getState(FlyCamAppState.class).setCamera( flyCam ); } if (context.getType() == Type.Display) { inputManager.addMapping(INPUT_MAPPING_EXIT, new KeyTrigger(KeyInput.KEY_ESCAPE)); } if (stateManager.getState(StatsAppState.class) != null) { inputManager.addMapping(INPUT_MAPPING_HIDE_STATS, new KeyTrigger(KeyInput.KEY_F5)); inputManager.addListener(actionListener, INPUT_MAPPING_HIDE_STATS); } inputManager.addListener(actionListener, INPUT_MAPPING_EXIT); } if (stateManager.getState(StatsAppState.class) != null) { // Some of the tests rely on having access to fpsText // for quick display. Maybe a different way would be better. stateManager.getState(StatsAppState.class).setFont(guiFont); fpsText = stateManager.getState(StatsAppState.class).getFpsText(); } // call user code simpleInitApp(); } @Override public void update() { if (prof!=null) prof.appStep(AppStep.BeginFrame); super.update(); // makes sure to execute AppTasks if (speed == 0 || paused) { return; } float tpf = timer.getTimePerFrame() * speed; // update states if (prof!=null) prof.appStep(AppStep.StateManagerUpdate); stateManager.update(tpf); // simple update and root node simpleUpdate(tpf); if (prof!=null) prof.appStep(AppStep.SpatialUpdate); rootNode.updateLogicalState(tpf); guiNode.updateLogicalState(tpf); rootNode.updateGeometricState(); guiNode.updateGeometricState(); // render states if (prof!=null) prof.appStep(AppStep.StateManagerRender); stateManager.render(renderManager); if (prof!=null) prof.appStep(AppStep.RenderFrame); renderManager.render(tpf, context.isRenderable()); simpleRender(renderManager); stateManager.postRender(); if (prof!=null) prof.appStep(AppStep.EndFrame); } public void setDisplayFps(boolean show) { if (stateManager.getState(StatsAppState.class) != null) { stateManager.getState(StatsAppState.class).setDisplayFps(show); } } public void setDisplayStatView(boolean show) { if (stateManager.getState(StatsAppState.class) != null) { stateManager.getState(StatsAppState.class).setDisplayStatView(show); } } public abstract void simpleInitApp(); public void simpleUpdate(float tpf) { } public void simpleRender(RenderManager rm) { } }