aboutsummaryrefslogtreecommitdiff
path: root/engine/src/core/com/jme3/app/Application.java
diff options
context:
space:
mode:
Diffstat (limited to 'engine/src/core/com/jme3/app/Application.java')
-rw-r--r--engine/src/core/com/jme3/app/Application.java642
1 files changed, 642 insertions, 0 deletions
diff --git a/engine/src/core/com/jme3/app/Application.java b/engine/src/core/com/jme3/app/Application.java
new file mode 100644
index 0000000..517ec61
--- /dev/null
+++ b/engine/src/core/com/jme3/app/Application.java
@@ -0,0 +1,642 @@
+/*
+ * 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.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.JmeContext.Type;
+import com.jme3.system.*;
+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 extend this class and call start() to begin the
+ * application.
+ *
+ */
+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);
+ }
+
+ /**
+ * @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){
+ logger.log(Level.SEVERE, errMsg, t);
+ // user should add additional code to handle the error.
+ 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;
+ }
+
+ /**
+ * Do not call manually.
+ * Callback from ContextListener.
+ */
+ public void update(){
+ // Make sure the audio renderer is available to callables
+ AudioContext.setAudioRenderer(audioRenderer);
+
+ AppTask<?> task = taskQueue.poll();
+ toploop: do {
+ if (task == null) break;
+ while (task.isCancelled()) {
+ task = taskQueue.poll();
+ if (task == null) break toploop;
+ }
+ task.invoke();
+ } while (((task = taskQueue.poll()) != null));
+
+ 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;
+ }
+
+}