|
@@ -1,656 +1,656 @@
|
|
-/*
|
|
|
|
- * 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.AppStateManager;
|
|
|
|
-import com.jme3.asset.AssetManager;
|
|
|
|
-import com.jme3.audio.AudioContext;
|
|
|
|
-import com.jme3.audio.AudioRenderer;
|
|
|
|
-import com.jme3.audio.Listener;
|
|
|
|
-import com.jme3.input.*;
|
|
|
|
-import com.jme3.math.Vector3f;
|
|
|
|
-import com.jme3.renderer.Camera;
|
|
|
|
-import com.jme3.renderer.RenderManager;
|
|
|
|
-import com.jme3.renderer.Renderer;
|
|
|
|
-import com.jme3.renderer.ViewPort;
|
|
|
|
-import com.jme3.system.*;
|
|
|
|
-import com.jme3.system.JmeContext.Type;
|
|
|
|
-import java.net.MalformedURLException;
|
|
|
|
-import java.net.URL;
|
|
|
|
-import java.util.concurrent.Callable;
|
|
|
|
-import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
-import java.util.concurrent.Future;
|
|
|
|
-import java.util.logging.Level;
|
|
|
|
-import java.util.logging.Logger;
|
|
|
|
-
|
|
|
|
-/**
|
|
|
|
- * The <code>Application</code> class represents an instance of a
|
|
|
|
- * real-time 3D rendering jME application.
|
|
|
|
- *
|
|
|
|
- * An <code>Application</code> provides all the tools that are commonly used in jME3
|
|
|
|
- * applications.
|
|
|
|
- *
|
|
|
|
- * jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.appSimpleApplication} instead.
|
|
|
|
- *
|
|
|
|
- */
|
|
|
|
-public class Application implements SystemListener {
|
|
|
|
-
|
|
|
|
- private static final Logger logger = Logger.getLogger(Application.class.getName());
|
|
|
|
-
|
|
|
|
- protected AssetManager assetManager;
|
|
|
|
-
|
|
|
|
- protected AudioRenderer audioRenderer;
|
|
|
|
- protected Renderer renderer;
|
|
|
|
- protected RenderManager renderManager;
|
|
|
|
- protected ViewPort viewPort;
|
|
|
|
- protected ViewPort guiViewPort;
|
|
|
|
-
|
|
|
|
- protected JmeContext context;
|
|
|
|
- protected AppSettings settings;
|
|
|
|
- protected Timer timer = new NanoTimer();
|
|
|
|
- protected Camera cam;
|
|
|
|
- protected Listener listener;
|
|
|
|
-
|
|
|
|
- protected boolean inputEnabled = true;
|
|
|
|
- protected boolean pauseOnFocus = true;
|
|
|
|
- protected float speed = 1f;
|
|
|
|
- protected boolean paused = false;
|
|
|
|
- protected MouseInput mouseInput;
|
|
|
|
- protected KeyInput keyInput;
|
|
|
|
- protected JoyInput joyInput;
|
|
|
|
- protected TouchInput touchInput;
|
|
|
|
- protected InputManager inputManager;
|
|
|
|
- protected AppStateManager stateManager;
|
|
|
|
-
|
|
|
|
- private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Create a new instance of <code>Application</code>.
|
|
|
|
- */
|
|
|
|
- public Application(){
|
|
|
|
- initStateManager();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Returns true if pause on lost focus is enabled, false otherwise.
|
|
|
|
- *
|
|
|
|
- * @return true if pause on lost focus is enabled
|
|
|
|
- *
|
|
|
|
- * @see #setPauseOnLostFocus(boolean)
|
|
|
|
- */
|
|
|
|
- public boolean isPauseOnLostFocus() {
|
|
|
|
- return pauseOnFocus;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Enable or disable pause on lost focus.
|
|
|
|
- * <p>
|
|
|
|
- * By default, pause on lost focus is enabled.
|
|
|
|
- * If enabled, the application will stop updating
|
|
|
|
- * when it loses focus or becomes inactive (e.g. alt-tab).
|
|
|
|
- * For online or real-time applications, this might not be preferable,
|
|
|
|
- * so this feature should be set to disabled. For other applications,
|
|
|
|
- * it is best to keep it on so that CPU usage is not used when
|
|
|
|
- * not necessary.
|
|
|
|
- *
|
|
|
|
- * @param pauseOnLostFocus True to enable pause on lost focus, false
|
|
|
|
- * otherwise.
|
|
|
|
- */
|
|
|
|
- public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
|
|
|
|
- this.pauseOnFocus = pauseOnLostFocus;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- @Deprecated
|
|
|
|
- public void setAssetManager(AssetManager assetManager){
|
|
|
|
- if (this.assetManager != null)
|
|
|
|
- throw new IllegalStateException("Can only set asset manager"
|
|
|
|
- + " before initialization.");
|
|
|
|
-
|
|
|
|
- this.assetManager = assetManager;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void initAssetManager(){
|
|
|
|
- if (settings != null){
|
|
|
|
- String assetCfg = settings.getString("AssetConfigURL");
|
|
|
|
- if (assetCfg != null){
|
|
|
|
- URL url = null;
|
|
|
|
- try {
|
|
|
|
- url = new URL(assetCfg);
|
|
|
|
- } catch (MalformedURLException ex) {
|
|
|
|
- }
|
|
|
|
- if (url == null) {
|
|
|
|
- url = Application.class.getClassLoader().getResource(assetCfg);
|
|
|
|
- if (url == null) {
|
|
|
|
- logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- assetManager = JmeSystem.newAssetManager(url);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- if (assetManager == null){
|
|
|
|
- assetManager = JmeSystem.newAssetManager(
|
|
|
|
- Thread.currentThread().getContextClassLoader()
|
|
|
|
- .getResource("com/jme3/asset/Desktop.cfg"));
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Set the display settings to define the display created.
|
|
|
|
- * <p>
|
|
|
|
- * Examples of display parameters include display pixel width and height,
|
|
|
|
- * color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
|
|
|
|
- * If this method is called while the application is already running, then
|
|
|
|
- * {@link #restart() } must be called to apply the settings to the display.
|
|
|
|
- *
|
|
|
|
- * @param settings The settings to set.
|
|
|
|
- */
|
|
|
|
- public void setSettings(AppSettings settings){
|
|
|
|
- this.settings = settings;
|
|
|
|
- if (context != null && settings.useInput() != inputEnabled){
|
|
|
|
- // may need to create or destroy input based
|
|
|
|
- // on settings change
|
|
|
|
- inputEnabled = !inputEnabled;
|
|
|
|
- if (inputEnabled){
|
|
|
|
- initInput();
|
|
|
|
- }else{
|
|
|
|
- destroyInput();
|
|
|
|
- }
|
|
|
|
- }else{
|
|
|
|
- inputEnabled = settings.useInput();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Sets the Timer implementation that will be used for calculating
|
|
|
|
- * frame times. By default, Application will use the Timer as returned
|
|
|
|
- * by the current JmeContext implementation.
|
|
|
|
- */
|
|
|
|
- public void setTimer(Timer timer){
|
|
|
|
- this.timer = timer;
|
|
|
|
-
|
|
|
|
- if (timer != null) {
|
|
|
|
- timer.reset();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (renderManager != null) {
|
|
|
|
- renderManager.setTimer(timer);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public Timer getTimer(){
|
|
|
|
- return timer;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void initDisplay(){
|
|
|
|
- // aquire important objects
|
|
|
|
- // from the context
|
|
|
|
- settings = context.getSettings();
|
|
|
|
-
|
|
|
|
- // Only reset the timer if a user has not already provided one
|
|
|
|
- if (timer == null) {
|
|
|
|
- timer = context.getTimer();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- renderer = context.getRenderer();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void initAudio(){
|
|
|
|
- if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
|
|
|
|
- audioRenderer = JmeSystem.newAudioRenderer(settings);
|
|
|
|
- audioRenderer.initialize();
|
|
|
|
- AudioContext.setAudioRenderer(audioRenderer);
|
|
|
|
-
|
|
|
|
- listener = new Listener();
|
|
|
|
- audioRenderer.setListener(listener);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Creates the camera to use for rendering. Default values are perspective
|
|
|
|
- * projection with 45° field of view, with near and far values 1 and 1000
|
|
|
|
- * units respectively.
|
|
|
|
- */
|
|
|
|
- private void initCamera(){
|
|
|
|
- cam = new Camera(settings.getWidth(), settings.getHeight());
|
|
|
|
-
|
|
|
|
- cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
|
|
|
|
- cam.setLocation(new Vector3f(0f, 0f, 10f));
|
|
|
|
- cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
|
|
|
|
-
|
|
|
|
- renderManager = new RenderManager(renderer);
|
|
|
|
- //Remy - 09/14/2010 setted the timer in the renderManager
|
|
|
|
- renderManager.setTimer(timer);
|
|
|
|
- viewPort = renderManager.createMainView("Default", cam);
|
|
|
|
- viewPort.setClearFlags(true, true, true);
|
|
|
|
-
|
|
|
|
- // Create a new cam for the gui
|
|
|
|
- Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
|
|
|
|
- guiViewPort = renderManager.createPostView("Gui Default", guiCam);
|
|
|
|
- guiViewPort.setClearFlags(false, false, false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Initializes mouse and keyboard input. Also
|
|
|
|
- * initializes joystick input if joysticks are enabled in the
|
|
|
|
- * AppSettings.
|
|
|
|
- */
|
|
|
|
- private void initInput(){
|
|
|
|
- mouseInput = context.getMouseInput();
|
|
|
|
- if (mouseInput != null)
|
|
|
|
- mouseInput.initialize();
|
|
|
|
-
|
|
|
|
- keyInput = context.getKeyInput();
|
|
|
|
- if (keyInput != null)
|
|
|
|
- keyInput.initialize();
|
|
|
|
-
|
|
|
|
- touchInput = context.getTouchInput();
|
|
|
|
- if (touchInput != null)
|
|
|
|
- touchInput.initialize();
|
|
|
|
-
|
|
|
|
- if (!settings.getBoolean("DisableJoysticks")){
|
|
|
|
- joyInput = context.getJoyInput();
|
|
|
|
- if (joyInput != null)
|
|
|
|
- joyInput.initialize();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void initStateManager(){
|
|
|
|
- stateManager = new AppStateManager(this);
|
|
|
|
-
|
|
|
|
- // Always register a ResetStatsState to make sure
|
|
|
|
- // that the stats are cleared every frame
|
|
|
|
- stateManager.attach(new ResetStatsState());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link AssetManager asset manager} for this application.
|
|
|
|
- */
|
|
|
|
- public AssetManager getAssetManager(){
|
|
|
|
- return assetManager;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return the {@link InputManager input manager}.
|
|
|
|
- */
|
|
|
|
- public InputManager getInputManager(){
|
|
|
|
- return inputManager;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return the {@link AppStateManager app state manager}
|
|
|
|
- */
|
|
|
|
- public AppStateManager getStateManager() {
|
|
|
|
- return stateManager;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return the {@link RenderManager render manager}
|
|
|
|
- */
|
|
|
|
- public RenderManager getRenderManager() {
|
|
|
|
- return renderManager;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link Renderer renderer} for the application
|
|
|
|
- */
|
|
|
|
- public Renderer getRenderer(){
|
|
|
|
- return renderer;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link AudioRenderer audio renderer} for the application
|
|
|
|
- */
|
|
|
|
- public AudioRenderer getAudioRenderer() {
|
|
|
|
- return audioRenderer;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link Listener listener} object for audio
|
|
|
|
- */
|
|
|
|
- public Listener getListener() {
|
|
|
|
- return listener;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link JmeContext display context} for the application
|
|
|
|
- */
|
|
|
|
- public JmeContext getContext(){
|
|
|
|
- return context;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The {@link Camera camera} for the application
|
|
|
|
- */
|
|
|
|
- public Camera getCamera(){
|
|
|
|
- return cam;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Starts the application in {@link Type#Display display} mode.
|
|
|
|
- *
|
|
|
|
- * @see #start(com.jme3.system.JmeContext.Type)
|
|
|
|
- */
|
|
|
|
- public void start(){
|
|
|
|
- start(JmeContext.Type.Display);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Starts the application.
|
|
|
|
- * Creating a rendering context and executing
|
|
|
|
- * the main loop in a separate thread.
|
|
|
|
- */
|
|
|
|
- public void start(JmeContext.Type contextType){
|
|
|
|
- if (context != null && context.isCreated()){
|
|
|
|
- logger.warning("start() called when application already created!");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (settings == null){
|
|
|
|
- settings = new AppSettings(true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
|
|
- context = JmeSystem.newContext(settings, contextType);
|
|
|
|
- context.setSystemListener(this);
|
|
|
|
- context.create(false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Initializes the application's canvas for use.
|
|
|
|
- * <p>
|
|
|
|
- * After calling this method, cast the {@link #getContext() context} to
|
|
|
|
- * {@link JmeCanvasContext},
|
|
|
|
- * then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
|
|
|
|
- * and attach it to an AWT/Swing Frame.
|
|
|
|
- * The rendering thread will start when the canvas becomes visible on
|
|
|
|
- * screen, however if you wish to start the context immediately you
|
|
|
|
- * may call {@link #startCanvas() } to force the rendering thread
|
|
|
|
- * to start.
|
|
|
|
- *
|
|
|
|
- * @see JmeCanvasContext
|
|
|
|
- * @see Type#Canvas
|
|
|
|
- */
|
|
|
|
- public void createCanvas(){
|
|
|
|
- if (context != null && context.isCreated()){
|
|
|
|
- logger.warning("createCanvas() called when application already created!");
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (settings == null){
|
|
|
|
- settings = new AppSettings(true);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
|
|
- context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
|
|
|
|
- context.setSystemListener(this);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Starts the rendering thread after createCanvas() has been called.
|
|
|
|
- * <p>
|
|
|
|
- * Same as calling startCanvas(false)
|
|
|
|
- *
|
|
|
|
- * @see #startCanvas(boolean)
|
|
|
|
- */
|
|
|
|
- public void startCanvas(){
|
|
|
|
- startCanvas(false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Starts the rendering thread after createCanvas() has been called.
|
|
|
|
- * <p>
|
|
|
|
- * Calling this method is optional, the canvas will start automatically
|
|
|
|
- * when it becomes visible.
|
|
|
|
- *
|
|
|
|
- * @param waitFor If true, the current thread will block until the
|
|
|
|
- * rendering thread is running
|
|
|
|
- */
|
|
|
|
- public void startCanvas(boolean waitFor){
|
|
|
|
- context.create(waitFor);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Internal use only.
|
|
|
|
- */
|
|
|
|
- public void reshape(int w, int h){
|
|
|
|
- renderManager.notifyReshape(w, h);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Restarts the context, applying any changed settings.
|
|
|
|
- * <p>
|
|
|
|
- * Changes to the {@link AppSettings} of this Application are not
|
|
|
|
- * applied immediately; calling this method forces the context
|
|
|
|
- * to restart, applying the new settings.
|
|
|
|
- */
|
|
|
|
- public void restart(){
|
|
|
|
- context.setSettings(settings);
|
|
|
|
- context.restart();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Requests the context to close, shutting down the main loop
|
|
|
|
- * and making necessary cleanup operations.
|
|
|
|
- *
|
|
|
|
- * Same as calling stop(false)
|
|
|
|
- *
|
|
|
|
- * @see #stop(boolean)
|
|
|
|
- */
|
|
|
|
- public void stop(){
|
|
|
|
- stop(false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Requests the context to close, shutting down the main loop
|
|
|
|
- * and making necessary cleanup operations.
|
|
|
|
- * After the application has stopped, it cannot be used anymore.
|
|
|
|
- */
|
|
|
|
- public void stop(boolean waitFor){
|
|
|
|
- logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
|
|
|
|
- context.destroy(waitFor);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Do not call manually.
|
|
|
|
- * Callback from ContextListener.
|
|
|
|
- * <p>
|
|
|
|
- * Initializes the <code>Application</code>, by creating a display and
|
|
|
|
- * default camera. If display settings are not specified, a default
|
|
|
|
- * 640x480 display is created. Default values are used for the camera;
|
|
|
|
- * perspective projection with 45° field of view, with near
|
|
|
|
- * and far values 1 and 1000 units respectively.
|
|
|
|
- */
|
|
|
|
- public void initialize(){
|
|
|
|
- if (assetManager == null){
|
|
|
|
- initAssetManager();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- initDisplay();
|
|
|
|
- initCamera();
|
|
|
|
-
|
|
|
|
- if (inputEnabled){
|
|
|
|
- initInput();
|
|
|
|
- }
|
|
|
|
- initAudio();
|
|
|
|
-
|
|
|
|
- // update timer so that the next delta is not too large
|
|
|
|
-// timer.update();
|
|
|
|
- timer.reset();
|
|
|
|
-
|
|
|
|
- // user code here..
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Internal use only.
|
|
|
|
- */
|
|
|
|
- public void handleError(String errMsg, Throwable t){
|
|
|
|
- // Print error to log.
|
|
|
|
- logger.log(Level.SEVERE, errMsg, t);
|
|
|
|
- // Display error message on screen
|
|
|
|
- if (t != null) {
|
|
|
|
- JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
|
|
|
|
- (t.getMessage() != null ? ": " + t.getMessage() : ""));
|
|
|
|
- } else {
|
|
|
|
- JmeSystem.showErrorDialog(errMsg);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- stop(); // stop the application
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Internal use only.
|
|
|
|
- */
|
|
|
|
- public void gainFocus(){
|
|
|
|
- if (pauseOnFocus) {
|
|
|
|
- paused = false;
|
|
|
|
- context.setAutoFlushFrames(true);
|
|
|
|
- if (inputManager != null) {
|
|
|
|
- inputManager.reset();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Internal use only.
|
|
|
|
- */
|
|
|
|
- public void loseFocus(){
|
|
|
|
- if (pauseOnFocus){
|
|
|
|
- paused = true;
|
|
|
|
- context.setAutoFlushFrames(false);
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Internal use only.
|
|
|
|
- */
|
|
|
|
- public void requestClose(boolean esc){
|
|
|
|
- context.destroy(false);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Enqueues a task/callable object to execute in the jME3
|
|
|
|
- * rendering thread.
|
|
|
|
- * <p>
|
|
|
|
- * Callables are executed right at the beginning of the main loop.
|
|
|
|
- * They are executed even if the application is currently paused
|
|
|
|
- * or out of focus.
|
|
|
|
- */
|
|
|
|
- public <V> Future<V> enqueue(Callable<V> callable) {
|
|
|
|
- AppTask<V> task = new AppTask<V>(callable);
|
|
|
|
- taskQueue.add(task);
|
|
|
|
- return task;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Runs tasks enqueued via {@link #enqueue(Callable)}
|
|
|
|
- */
|
|
|
|
- protected void runQueuedTasks() {
|
|
|
|
- AppTask<?> task;
|
|
|
|
- while( (task = taskQueue.poll()) != null ) {
|
|
|
|
- if (!task.isCancelled()) {
|
|
|
|
- task.invoke();
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Do not call manually.
|
|
|
|
- * Callback from ContextListener.
|
|
|
|
- */
|
|
|
|
- public void update(){
|
|
|
|
- // Make sure the audio renderer is available to callables
|
|
|
|
- AudioContext.setAudioRenderer(audioRenderer);
|
|
|
|
-
|
|
|
|
- runQueuedTasks();
|
|
|
|
-
|
|
|
|
- if (speed == 0 || paused)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- timer.update();
|
|
|
|
-
|
|
|
|
- if (inputEnabled){
|
|
|
|
- inputManager.update(timer.getTimePerFrame());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (audioRenderer != null){
|
|
|
|
- audioRenderer.update(timer.getTimePerFrame());
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- // user code here..
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- protected void destroyInput(){
|
|
|
|
- if (mouseInput != null)
|
|
|
|
- mouseInput.destroy();
|
|
|
|
-
|
|
|
|
- if (keyInput != null)
|
|
|
|
- keyInput.destroy();
|
|
|
|
-
|
|
|
|
- if (joyInput != null)
|
|
|
|
- joyInput.destroy();
|
|
|
|
-
|
|
|
|
- if (touchInput != null)
|
|
|
|
- touchInput.destroy();
|
|
|
|
-
|
|
|
|
- inputManager = null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * Do not call manually.
|
|
|
|
- * Callback from ContextListener.
|
|
|
|
- */
|
|
|
|
- public void destroy(){
|
|
|
|
- stateManager.cleanup();
|
|
|
|
-
|
|
|
|
- destroyInput();
|
|
|
|
- if (audioRenderer != null)
|
|
|
|
- audioRenderer.cleanup();
|
|
|
|
-
|
|
|
|
- timer.reset();
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- /**
|
|
|
|
- * @return The GUI viewport. Which is used for the on screen
|
|
|
|
- * statistics and FPS.
|
|
|
|
- */
|
|
|
|
- public ViewPort getGuiViewPort() {
|
|
|
|
- return guiViewPort;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public ViewPort getViewPort() {
|
|
|
|
- return viewPort;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
-}
|
|
|
|
|
|
+/*
|
|
|
|
+ * 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.AppStateManager;
|
|
|
|
+import com.jme3.asset.AssetManager;
|
|
|
|
+import com.jme3.audio.AudioContext;
|
|
|
|
+import com.jme3.audio.AudioRenderer;
|
|
|
|
+import com.jme3.audio.Listener;
|
|
|
|
+import com.jme3.input.*;
|
|
|
|
+import com.jme3.math.Vector3f;
|
|
|
|
+import com.jme3.renderer.Camera;
|
|
|
|
+import com.jme3.renderer.RenderManager;
|
|
|
|
+import com.jme3.renderer.Renderer;
|
|
|
|
+import com.jme3.renderer.ViewPort;
|
|
|
|
+import com.jme3.system.*;
|
|
|
|
+import com.jme3.system.JmeContext.Type;
|
|
|
|
+import java.net.MalformedURLException;
|
|
|
|
+import java.net.URL;
|
|
|
|
+import java.util.concurrent.Callable;
|
|
|
|
+import java.util.concurrent.ConcurrentLinkedQueue;
|
|
|
|
+import java.util.concurrent.Future;
|
|
|
|
+import java.util.logging.Level;
|
|
|
|
+import java.util.logging.Logger;
|
|
|
|
+
|
|
|
|
+/**
|
|
|
|
+ * The <code>Application</code> class represents an instance of a
|
|
|
|
+ * real-time 3D rendering jME application.
|
|
|
|
+ *
|
|
|
|
+ * An <code>Application</code> provides all the tools that are commonly used in jME3
|
|
|
|
+ * applications.
|
|
|
|
+ *
|
|
|
|
+ * jME3 applications *SHOULD NOT EXTEND* this class but extend {@link com.jme3.app.SimpleApplication} instead.
|
|
|
|
+ *
|
|
|
|
+ */
|
|
|
|
+public class Application implements SystemListener {
|
|
|
|
+
|
|
|
|
+ private static final Logger logger = Logger.getLogger(Application.class.getName());
|
|
|
|
+
|
|
|
|
+ protected AssetManager assetManager;
|
|
|
|
+
|
|
|
|
+ protected AudioRenderer audioRenderer;
|
|
|
|
+ protected Renderer renderer;
|
|
|
|
+ protected RenderManager renderManager;
|
|
|
|
+ protected ViewPort viewPort;
|
|
|
|
+ protected ViewPort guiViewPort;
|
|
|
|
+
|
|
|
|
+ protected JmeContext context;
|
|
|
|
+ protected AppSettings settings;
|
|
|
|
+ protected Timer timer = new NanoTimer();
|
|
|
|
+ protected Camera cam;
|
|
|
|
+ protected Listener listener;
|
|
|
|
+
|
|
|
|
+ protected boolean inputEnabled = true;
|
|
|
|
+ protected boolean pauseOnFocus = true;
|
|
|
|
+ protected float speed = 1f;
|
|
|
|
+ protected boolean paused = false;
|
|
|
|
+ protected MouseInput mouseInput;
|
|
|
|
+ protected KeyInput keyInput;
|
|
|
|
+ protected JoyInput joyInput;
|
|
|
|
+ protected TouchInput touchInput;
|
|
|
|
+ protected InputManager inputManager;
|
|
|
|
+ protected AppStateManager stateManager;
|
|
|
|
+
|
|
|
|
+ private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Create a new instance of <code>Application</code>.
|
|
|
|
+ */
|
|
|
|
+ public Application(){
|
|
|
|
+ initStateManager();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Returns true if pause on lost focus is enabled, false otherwise.
|
|
|
|
+ *
|
|
|
|
+ * @return true if pause on lost focus is enabled
|
|
|
|
+ *
|
|
|
|
+ * @see #setPauseOnLostFocus(boolean)
|
|
|
|
+ */
|
|
|
|
+ public boolean isPauseOnLostFocus() {
|
|
|
|
+ return pauseOnFocus;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Enable or disable pause on lost focus.
|
|
|
|
+ * <p>
|
|
|
|
+ * By default, pause on lost focus is enabled.
|
|
|
|
+ * If enabled, the application will stop updating
|
|
|
|
+ * when it loses focus or becomes inactive (e.g. alt-tab).
|
|
|
|
+ * For online or real-time applications, this might not be preferable,
|
|
|
|
+ * so this feature should be set to disabled. For other applications,
|
|
|
|
+ * it is best to keep it on so that CPU usage is not used when
|
|
|
|
+ * not necessary.
|
|
|
|
+ *
|
|
|
|
+ * @param pauseOnLostFocus True to enable pause on lost focus, false
|
|
|
|
+ * otherwise.
|
|
|
|
+ */
|
|
|
|
+ public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
|
|
|
|
+ this.pauseOnFocus = pauseOnLostFocus;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ @Deprecated
|
|
|
|
+ public void setAssetManager(AssetManager assetManager){
|
|
|
|
+ if (this.assetManager != null)
|
|
|
|
+ throw new IllegalStateException("Can only set asset manager"
|
|
|
|
+ + " before initialization.");
|
|
|
|
+
|
|
|
|
+ this.assetManager = assetManager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void initAssetManager(){
|
|
|
|
+ if (settings != null){
|
|
|
|
+ String assetCfg = settings.getString("AssetConfigURL");
|
|
|
|
+ if (assetCfg != null){
|
|
|
|
+ URL url = null;
|
|
|
|
+ try {
|
|
|
|
+ url = new URL(assetCfg);
|
|
|
|
+ } catch (MalformedURLException ex) {
|
|
|
|
+ }
|
|
|
|
+ if (url == null) {
|
|
|
|
+ url = Application.class.getClassLoader().getResource(assetCfg);
|
|
|
|
+ if (url == null) {
|
|
|
|
+ logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ assetManager = JmeSystem.newAssetManager(url);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ if (assetManager == null){
|
|
|
|
+ assetManager = JmeSystem.newAssetManager(
|
|
|
|
+ Thread.currentThread().getContextClassLoader()
|
|
|
|
+ .getResource("com/jme3/asset/Desktop.cfg"));
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Set the display settings to define the display created.
|
|
|
|
+ * <p>
|
|
|
|
+ * Examples of display parameters include display pixel width and height,
|
|
|
|
+ * color bit depth, z-buffer bits, anti-aliasing samples, and update frequency.
|
|
|
|
+ * If this method is called while the application is already running, then
|
|
|
|
+ * {@link #restart() } must be called to apply the settings to the display.
|
|
|
|
+ *
|
|
|
|
+ * @param settings The settings to set.
|
|
|
|
+ */
|
|
|
|
+ public void setSettings(AppSettings settings){
|
|
|
|
+ this.settings = settings;
|
|
|
|
+ if (context != null && settings.useInput() != inputEnabled){
|
|
|
|
+ // may need to create or destroy input based
|
|
|
|
+ // on settings change
|
|
|
|
+ inputEnabled = !inputEnabled;
|
|
|
|
+ if (inputEnabled){
|
|
|
|
+ initInput();
|
|
|
|
+ }else{
|
|
|
|
+ destroyInput();
|
|
|
|
+ }
|
|
|
|
+ }else{
|
|
|
|
+ inputEnabled = settings.useInput();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Sets the Timer implementation that will be used for calculating
|
|
|
|
+ * frame times. By default, Application will use the Timer as returned
|
|
|
|
+ * by the current JmeContext implementation.
|
|
|
|
+ */
|
|
|
|
+ public void setTimer(Timer timer){
|
|
|
|
+ this.timer = timer;
|
|
|
|
+
|
|
|
|
+ if (timer != null) {
|
|
|
|
+ timer.reset();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (renderManager != null) {
|
|
|
|
+ renderManager.setTimer(timer);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public Timer getTimer(){
|
|
|
|
+ return timer;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void initDisplay(){
|
|
|
|
+ // aquire important objects
|
|
|
|
+ // from the context
|
|
|
|
+ settings = context.getSettings();
|
|
|
|
+
|
|
|
|
+ // Only reset the timer if a user has not already provided one
|
|
|
|
+ if (timer == null) {
|
|
|
|
+ timer = context.getTimer();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ renderer = context.getRenderer();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void initAudio(){
|
|
|
|
+ if (settings.getAudioRenderer() != null && context.getType() != Type.Headless){
|
|
|
|
+ audioRenderer = JmeSystem.newAudioRenderer(settings);
|
|
|
|
+ audioRenderer.initialize();
|
|
|
|
+ AudioContext.setAudioRenderer(audioRenderer);
|
|
|
|
+
|
|
|
|
+ listener = new Listener();
|
|
|
|
+ audioRenderer.setListener(listener);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Creates the camera to use for rendering. Default values are perspective
|
|
|
|
+ * projection with 45° field of view, with near and far values 1 and 1000
|
|
|
|
+ * units respectively.
|
|
|
|
+ */
|
|
|
|
+ private void initCamera(){
|
|
|
|
+ cam = new Camera(settings.getWidth(), settings.getHeight());
|
|
|
|
+
|
|
|
|
+ cam.setFrustumPerspective(45f, (float)cam.getWidth() / cam.getHeight(), 1f, 1000f);
|
|
|
|
+ cam.setLocation(new Vector3f(0f, 0f, 10f));
|
|
|
|
+ cam.lookAt(new Vector3f(0f, 0f, 0f), Vector3f.UNIT_Y);
|
|
|
|
+
|
|
|
|
+ renderManager = new RenderManager(renderer);
|
|
|
|
+ //Remy - 09/14/2010 setted the timer in the renderManager
|
|
|
|
+ renderManager.setTimer(timer);
|
|
|
|
+ viewPort = renderManager.createMainView("Default", cam);
|
|
|
|
+ viewPort.setClearFlags(true, true, true);
|
|
|
|
+
|
|
|
|
+ // Create a new cam for the gui
|
|
|
|
+ Camera guiCam = new Camera(settings.getWidth(), settings.getHeight());
|
|
|
|
+ guiViewPort = renderManager.createPostView("Gui Default", guiCam);
|
|
|
|
+ guiViewPort.setClearFlags(false, false, false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Initializes mouse and keyboard input. Also
|
|
|
|
+ * initializes joystick input if joysticks are enabled in the
|
|
|
|
+ * AppSettings.
|
|
|
|
+ */
|
|
|
|
+ private void initInput(){
|
|
|
|
+ mouseInput = context.getMouseInput();
|
|
|
|
+ if (mouseInput != null)
|
|
|
|
+ mouseInput.initialize();
|
|
|
|
+
|
|
|
|
+ keyInput = context.getKeyInput();
|
|
|
|
+ if (keyInput != null)
|
|
|
|
+ keyInput.initialize();
|
|
|
|
+
|
|
|
|
+ touchInput = context.getTouchInput();
|
|
|
|
+ if (touchInput != null)
|
|
|
|
+ touchInput.initialize();
|
|
|
|
+
|
|
|
|
+ if (!settings.getBoolean("DisableJoysticks")){
|
|
|
|
+ joyInput = context.getJoyInput();
|
|
|
|
+ if (joyInput != null)
|
|
|
|
+ joyInput.initialize();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ inputManager = new InputManager(mouseInput, keyInput, joyInput, touchInput);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ private void initStateManager(){
|
|
|
|
+ stateManager = new AppStateManager(this);
|
|
|
|
+
|
|
|
|
+ // Always register a ResetStatsState to make sure
|
|
|
|
+ // that the stats are cleared every frame
|
|
|
|
+ stateManager.attach(new ResetStatsState());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link AssetManager asset manager} for this application.
|
|
|
|
+ */
|
|
|
|
+ public AssetManager getAssetManager(){
|
|
|
|
+ return assetManager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return the {@link InputManager input manager}.
|
|
|
|
+ */
|
|
|
|
+ public InputManager getInputManager(){
|
|
|
|
+ return inputManager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return the {@link AppStateManager app state manager}
|
|
|
|
+ */
|
|
|
|
+ public AppStateManager getStateManager() {
|
|
|
|
+ return stateManager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return the {@link RenderManager render manager}
|
|
|
|
+ */
|
|
|
|
+ public RenderManager getRenderManager() {
|
|
|
|
+ return renderManager;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link Renderer renderer} for the application
|
|
|
|
+ */
|
|
|
|
+ public Renderer getRenderer(){
|
|
|
|
+ return renderer;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link AudioRenderer audio renderer} for the application
|
|
|
|
+ */
|
|
|
|
+ public AudioRenderer getAudioRenderer() {
|
|
|
|
+ return audioRenderer;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link Listener listener} object for audio
|
|
|
|
+ */
|
|
|
|
+ public Listener getListener() {
|
|
|
|
+ return listener;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link JmeContext display context} for the application
|
|
|
|
+ */
|
|
|
|
+ public JmeContext getContext(){
|
|
|
|
+ return context;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The {@link Camera camera} for the application
|
|
|
|
+ */
|
|
|
|
+ public Camera getCamera(){
|
|
|
|
+ return cam;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Starts the application in {@link Type#Display display} mode.
|
|
|
|
+ *
|
|
|
|
+ * @see #start(com.jme3.system.JmeContext.Type)
|
|
|
|
+ */
|
|
|
|
+ public void start(){
|
|
|
|
+ start(JmeContext.Type.Display);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Starts the application.
|
|
|
|
+ * Creating a rendering context and executing
|
|
|
|
+ * the main loop in a separate thread.
|
|
|
|
+ */
|
|
|
|
+ public void start(JmeContext.Type contextType){
|
|
|
|
+ if (context != null && context.isCreated()){
|
|
|
|
+ logger.warning("start() called when application already created!");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (settings == null){
|
|
|
|
+ settings = new AppSettings(true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
|
|
+ context = JmeSystem.newContext(settings, contextType);
|
|
|
|
+ context.setSystemListener(this);
|
|
|
|
+ context.create(false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Initializes the application's canvas for use.
|
|
|
|
+ * <p>
|
|
|
|
+ * After calling this method, cast the {@link #getContext() context} to
|
|
|
|
+ * {@link JmeCanvasContext},
|
|
|
|
+ * then acquire the canvas with {@link JmeCanvasContext#getCanvas() }
|
|
|
|
+ * and attach it to an AWT/Swing Frame.
|
|
|
|
+ * The rendering thread will start when the canvas becomes visible on
|
|
|
|
+ * screen, however if you wish to start the context immediately you
|
|
|
|
+ * may call {@link #startCanvas() } to force the rendering thread
|
|
|
|
+ * to start.
|
|
|
|
+ *
|
|
|
|
+ * @see JmeCanvasContext
|
|
|
|
+ * @see Type#Canvas
|
|
|
|
+ */
|
|
|
|
+ public void createCanvas(){
|
|
|
|
+ if (context != null && context.isCreated()){
|
|
|
|
+ logger.warning("createCanvas() called when application already created!");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (settings == null){
|
|
|
|
+ settings = new AppSettings(true);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ logger.log(Level.FINE, "Starting application: {0}", getClass().getName());
|
|
|
|
+ context = JmeSystem.newContext(settings, JmeContext.Type.Canvas);
|
|
|
|
+ context.setSystemListener(this);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Starts the rendering thread after createCanvas() has been called.
|
|
|
|
+ * <p>
|
|
|
|
+ * Same as calling startCanvas(false)
|
|
|
|
+ *
|
|
|
|
+ * @see #startCanvas(boolean)
|
|
|
|
+ */
|
|
|
|
+ public void startCanvas(){
|
|
|
|
+ startCanvas(false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Starts the rendering thread after createCanvas() has been called.
|
|
|
|
+ * <p>
|
|
|
|
+ * Calling this method is optional, the canvas will start automatically
|
|
|
|
+ * when it becomes visible.
|
|
|
|
+ *
|
|
|
|
+ * @param waitFor If true, the current thread will block until the
|
|
|
|
+ * rendering thread is running
|
|
|
|
+ */
|
|
|
|
+ public void startCanvas(boolean waitFor){
|
|
|
|
+ context.create(waitFor);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Internal use only.
|
|
|
|
+ */
|
|
|
|
+ public void reshape(int w, int h){
|
|
|
|
+ renderManager.notifyReshape(w, h);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Restarts the context, applying any changed settings.
|
|
|
|
+ * <p>
|
|
|
|
+ * Changes to the {@link AppSettings} of this Application are not
|
|
|
|
+ * applied immediately; calling this method forces the context
|
|
|
|
+ * to restart, applying the new settings.
|
|
|
|
+ */
|
|
|
|
+ public void restart(){
|
|
|
|
+ context.setSettings(settings);
|
|
|
|
+ context.restart();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Requests the context to close, shutting down the main loop
|
|
|
|
+ * and making necessary cleanup operations.
|
|
|
|
+ *
|
|
|
|
+ * Same as calling stop(false)
|
|
|
|
+ *
|
|
|
|
+ * @see #stop(boolean)
|
|
|
|
+ */
|
|
|
|
+ public void stop(){
|
|
|
|
+ stop(false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Requests the context to close, shutting down the main loop
|
|
|
|
+ * and making necessary cleanup operations.
|
|
|
|
+ * After the application has stopped, it cannot be used anymore.
|
|
|
|
+ */
|
|
|
|
+ public void stop(boolean waitFor){
|
|
|
|
+ logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
|
|
|
|
+ context.destroy(waitFor);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Do not call manually.
|
|
|
|
+ * Callback from ContextListener.
|
|
|
|
+ * <p>
|
|
|
|
+ * Initializes the <code>Application</code>, by creating a display and
|
|
|
|
+ * default camera. If display settings are not specified, a default
|
|
|
|
+ * 640x480 display is created. Default values are used for the camera;
|
|
|
|
+ * perspective projection with 45° field of view, with near
|
|
|
|
+ * and far values 1 and 1000 units respectively.
|
|
|
|
+ */
|
|
|
|
+ public void initialize(){
|
|
|
|
+ if (assetManager == null){
|
|
|
|
+ initAssetManager();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ initDisplay();
|
|
|
|
+ initCamera();
|
|
|
|
+
|
|
|
|
+ if (inputEnabled){
|
|
|
|
+ initInput();
|
|
|
|
+ }
|
|
|
|
+ initAudio();
|
|
|
|
+
|
|
|
|
+ // update timer so that the next delta is not too large
|
|
|
|
+// timer.update();
|
|
|
|
+ timer.reset();
|
|
|
|
+
|
|
|
|
+ // user code here..
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Internal use only.
|
|
|
|
+ */
|
|
|
|
+ public void handleError(String errMsg, Throwable t){
|
|
|
|
+ // Print error to log.
|
|
|
|
+ logger.log(Level.SEVERE, errMsg, t);
|
|
|
|
+ // Display error message on screen
|
|
|
|
+ if (t != null) {
|
|
|
|
+ JmeSystem.showErrorDialog(errMsg + "\n" + t.getClass().getSimpleName() +
|
|
|
|
+ (t.getMessage() != null ? ": " + t.getMessage() : ""));
|
|
|
|
+ } else {
|
|
|
|
+ JmeSystem.showErrorDialog(errMsg);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ stop(); // stop the application
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Internal use only.
|
|
|
|
+ */
|
|
|
|
+ public void gainFocus(){
|
|
|
|
+ if (pauseOnFocus) {
|
|
|
|
+ paused = false;
|
|
|
|
+ context.setAutoFlushFrames(true);
|
|
|
|
+ if (inputManager != null) {
|
|
|
|
+ inputManager.reset();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Internal use only.
|
|
|
|
+ */
|
|
|
|
+ public void loseFocus(){
|
|
|
|
+ if (pauseOnFocus){
|
|
|
|
+ paused = true;
|
|
|
|
+ context.setAutoFlushFrames(false);
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Internal use only.
|
|
|
|
+ */
|
|
|
|
+ public void requestClose(boolean esc){
|
|
|
|
+ context.destroy(false);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Enqueues a task/callable object to execute in the jME3
|
|
|
|
+ * rendering thread.
|
|
|
|
+ * <p>
|
|
|
|
+ * Callables are executed right at the beginning of the main loop.
|
|
|
|
+ * They are executed even if the application is currently paused
|
|
|
|
+ * or out of focus.
|
|
|
|
+ */
|
|
|
|
+ public <V> Future<V> enqueue(Callable<V> callable) {
|
|
|
|
+ AppTask<V> task = new AppTask<V>(callable);
|
|
|
|
+ taskQueue.add(task);
|
|
|
|
+ return task;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Runs tasks enqueued via {@link #enqueue(Callable)}
|
|
|
|
+ */
|
|
|
|
+ protected void runQueuedTasks() {
|
|
|
|
+ AppTask<?> task;
|
|
|
|
+ while( (task = taskQueue.poll()) != null ) {
|
|
|
|
+ if (!task.isCancelled()) {
|
|
|
|
+ task.invoke();
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Do not call manually.
|
|
|
|
+ * Callback from ContextListener.
|
|
|
|
+ */
|
|
|
|
+ public void update(){
|
|
|
|
+ // Make sure the audio renderer is available to callables
|
|
|
|
+ AudioContext.setAudioRenderer(audioRenderer);
|
|
|
|
+
|
|
|
|
+ runQueuedTasks();
|
|
|
|
+
|
|
|
|
+ if (speed == 0 || paused)
|
|
|
|
+ return;
|
|
|
|
+
|
|
|
|
+ timer.update();
|
|
|
|
+
|
|
|
|
+ if (inputEnabled){
|
|
|
|
+ inputManager.update(timer.getTimePerFrame());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (audioRenderer != null){
|
|
|
|
+ audioRenderer.update(timer.getTimePerFrame());
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ // user code here..
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ protected void destroyInput(){
|
|
|
|
+ if (mouseInput != null)
|
|
|
|
+ mouseInput.destroy();
|
|
|
|
+
|
|
|
|
+ if (keyInput != null)
|
|
|
|
+ keyInput.destroy();
|
|
|
|
+
|
|
|
|
+ if (joyInput != null)
|
|
|
|
+ joyInput.destroy();
|
|
|
|
+
|
|
|
|
+ if (touchInput != null)
|
|
|
|
+ touchInput.destroy();
|
|
|
|
+
|
|
|
|
+ inputManager = null;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * Do not call manually.
|
|
|
|
+ * Callback from ContextListener.
|
|
|
|
+ */
|
|
|
|
+ public void destroy(){
|
|
|
|
+ stateManager.cleanup();
|
|
|
|
+
|
|
|
|
+ destroyInput();
|
|
|
|
+ if (audioRenderer != null)
|
|
|
|
+ audioRenderer.cleanup();
|
|
|
|
+
|
|
|
|
+ timer.reset();
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /**
|
|
|
|
+ * @return The GUI viewport. Which is used for the on screen
|
|
|
|
+ * statistics and FPS.
|
|
|
|
+ */
|
|
|
|
+ public ViewPort getGuiViewPort() {
|
|
|
|
+ return guiViewPort;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ public ViewPort getViewPort() {
|
|
|
|
+ return viewPort;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+}
|