/* * Copyright (c) 2009-2010 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.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.math.Quaternion; import com.jme3.math.Vector3f; 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; import com.jme3.util.BufferUtils; /** * SimpleApplication extends the {@link com.jme3.app.Application} * class to provide default functionality like a first-person camera, * and an accessible root node that is updated and rendered regularly. * Additionally, SimpleApplication will display a statistics view * using the {@link com.jme3.app.StatsView} class. It will display * the current frames-per-second value on-screen in addition to the statistics. * Several keys have special functionality in SimpleApplication:
* * * * * *
Esc- Close the application
C- Display the camera position and rotation in the console.
M- Display memory usage in the console.
*/ public abstract class SimpleApplication extends Application { 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 DebugKeysAppState() ); } public SimpleApplication( AppState... initialStates ) { super(); if (initialStates != null) { for (AppState a : initialStates) { if (a != null) { stateManager.attach(a); } } } } @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; } @Override public void initialize() { super.initialize(); // Several things rely on having this guiFont = assetManager.loadFont("Interface/Fonts/Default.fnt"); 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() { super.update(); // makes sure to execute AppTasks if (speed == 0 || paused) { return; } float tpf = timer.getTimePerFrame() * speed; // update states stateManager.update(tpf); // simple update and root node simpleUpdate(tpf); rootNode.updateLogicalState(tpf); guiNode.updateLogicalState(tpf); rootNode.updateGeometricState(); guiNode.updateGeometricState(); // render states stateManager.render(renderManager); renderManager.render(tpf, context.isRenderable()); simpleRender(renderManager); stateManager.postRender(); } 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) { } }