| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648 |
- /*
- * 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.input.JoyInput;
- import com.jme3.input.KeyInput;
- import com.jme3.input.MouseInput;
- import com.jme3.input.TouchInput;
- import com.jme3.math.Vector3f;
- import com.jme3.renderer.Camera;
- import com.jme3.renderer.Renderer;
- import com.jme3.asset.AssetManager;
- import com.jme3.audio.AudioContext;
- import com.jme3.audio.AudioRenderer;
- import com.jme3.audio.Listener;
- import com.jme3.input.InputManager;
- import com.jme3.renderer.RenderManager;
- import com.jme3.renderer.ViewPort;
- import com.jme3.system.AppSettings;
- import com.jme3.system.JmeCanvasContext;
- import com.jme3.system.JmeContext;
- 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;
- import com.jme3.system.JmeContext.Type;
- import com.jme3.system.JmeSystem;
- import com.jme3.system.NanoTimer;
- import com.jme3.system.SystemListener;
- import com.jme3.system.Timer;
- /**
- * 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(){
- }
- /**
- * 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);
- }
- }
- 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();
- initStateManager();
- // 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;
- }
- }
|