Application
class represents an instance of a
- * real-time 3D rendering jME application.
- *
- * An Application
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.
- *
+ * The Application
interface represents the minimum exposed
+ * capabilities of a concrete jME3 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 LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
- 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;
-
- protected AppProfiler prof;
-
- private final ConcurrentLinkedQueueApplication
.
- */
- public Application(){
- initStateManager();
- }
+public interface Application {
/**
* Determine the application's behavior when unfocused.
- *
+ *
* @return The lost focus behavior of the application.
*/
- public LostFocusBehavior getLostFocusBehavior() {
- return lostFocusBehavior;
- }
-
+ public LostFocusBehavior getLostFocusBehavior();
+
/**
* Change the application's behavior when unfocused.
- *
- * By default, the application will
- * {@link LostFocusBehavior#ThrottleOnLostFocus throttle the update loop}
+ *
+ * By default, the application will
+ * {@link LostFocusBehavior#ThrottleOnLostFocus throttle the update loop}
* so as to not take 100% CPU usage when it is not in focus, e.g.
* alt-tabbed, minimized, or obstructed by another window.
- *
+ *
* @param lostFocusBehavior The new lost focus behavior to use.
- *
+ *
* @see LostFocusBehavior
*/
- public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
- this.lostFocusBehavior = lostFocusBehavior;
- }
-
+ public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior);
+
/**
* Returns true if pause on lost focus is enabled, false otherwise.
*
* @return true if pause on lost focus is enabled
*
- * @see #getLostFocusBehavior()
+ * @see #getLostFocusBehavior()
*/
- public boolean isPauseOnLostFocus() {
- return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
- }
+ public boolean isPauseOnLostFocus();
/**
* Enable or disable pause on lost focus.
@@ -153,52 +94,10 @@ public class Application implements SystemListener {
*
* @param pauseOnLostFocus True to enable pause on lost focus, false
* otherwise.
- *
+ *
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
*/
- public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
- if (pauseOnLostFocus) {
- setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
- } else {
- setLostFocusBehavior(LostFocusBehavior.Disabled);
- }
- }
-
- @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(){
- URL assetCfgUrl = null;
-
- if (settings != null){
- String assetCfg = settings.getString("AssetConfigURL");
- if (assetCfg != null){
- try {
- assetCfgUrl = new URL(assetCfg);
- } catch (MalformedURLException ex) {
- }
- if (assetCfgUrl == null) {
- assetCfgUrl = Application.class.getClassLoader().getResource(assetCfg);
- if (assetCfgUrl == null) {
- logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
- return;
- }
- }
- }
- }
- if (assetCfgUrl == null) {
- assetCfgUrl = JmeSystem.getPlatformAssetConfigURL();
- }
- if (assetManager == null){
- assetManager = JmeSystem.newAssetManager(assetCfgUrl);
- }
- }
+ public void setPauseOnLostFocus(boolean pauseOnLostFocus);
/**
* Set the display settings to define the display created.
@@ -210,321 +109,83 @@ public class Application implements SystemListener {
*
* @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();
- }
- }
+ public void setSettings(AppSettings settings);
/**
* 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);
-
- if (prof != null) {
- renderManager.setAppProfiler(prof);
- }
-
- 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();
- }
+ public void setTimer(Timer timer);
- 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());
- }
+ public Timer getTimer();
/**
* @return The {@link AssetManager asset manager} for this application.
*/
- public AssetManager getAssetManager(){
- return assetManager;
- }
+ public AssetManager getAssetManager();
/**
* @return the {@link InputManager input manager}.
*/
- public InputManager getInputManager(){
- return inputManager;
- }
+ public InputManager getInputManager();
/**
* @return the {@link AppStateManager app state manager}
*/
- public AppStateManager getStateManager() {
- return stateManager;
- }
+ public AppStateManager getStateManager();
/**
* @return the {@link RenderManager render manager}
*/
- public RenderManager getRenderManager() {
- return renderManager;
- }
+ public RenderManager getRenderManager();
/**
* @return The {@link Renderer renderer} for the application
*/
- public Renderer getRenderer(){
- return renderer;
- }
+ public Renderer getRenderer();
/**
* @return The {@link AudioRenderer audio renderer} for the application
*/
- public AudioRenderer getAudioRenderer() {
- return audioRenderer;
- }
+ public AudioRenderer getAudioRenderer();
/**
* @return The {@link Listener listener} object for audio
*/
- public Listener getListener() {
- return listener;
- }
+ public Listener getListener();
/**
* @return The {@link JmeContext display context} for the application
*/
- public JmeContext getContext(){
- return context;
- }
+ public JmeContext getContext();
/**
- * @return The {@link Camera camera} for the application
+ * @return The main {@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, false);
- }
-
- /**
- * Starts the application in {@link Type#Display display} mode.
- *
- * @see #start(com.jme3.system.JmeContext.Type)
- */
- public void start(boolean waitFor){
- start(JmeContext.Type.Display, waitFor);
- }
+ public Camera getCamera();
/**
* Starts the application.
- * Creating a rendering context and executing
- * the main loop in a separate thread.
*/
- public void start(JmeContext.Type contextType) {
- start(contextType, false);
- }
-
+ public void start();
+
/**
* Starts the application.
- * Creating a rendering context and executing
- * the main loop in a separate thread.
*/
- public void start(JmeContext.Type contextType, boolean waitFor){
- 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(waitFor);
- }
+ public void start(boolean waitFor);
/**
* Sets an AppProfiler hook that will be called back for
* specific steps within a single update frame. Value defaults
* to null.
*/
- public void setAppProfiler(AppProfiler prof) {
- this.prof = prof;
- if (renderManager != null) {
- renderManager.setAppProfiler(prof);
- }
- }
-
- /**
- * Returns the current AppProfiler hook, or null if none is set.
- */
- public AppProfiler getAppProfiler() {
- return prof;
- }
+ public void setAppProfiler(AppProfiler prof);
/**
- * Initializes the application's canvas for use.
- * - * 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. - *
- * Same as calling startCanvas(false) - * - * @see #startCanvas(boolean) - */ - public void startCanvas(){ - startCanvas(false); - } - - /** - * Starts the rendering thread after createCanvas() has been called. - *
- * 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. + * Returns the current AppProfiler hook, or null if none is set. */ - public void reshape(int w, int h){ - renderManager.notifyReshape(w, h); - } + public AppProfiler getAppProfiler(); /** * Restarts the context, applying any changed settings. @@ -533,10 +194,7 @@ public class Application implements SystemListener { * applied immediately; calling this method forces the context * to restart, applying the new settings. */ - public void restart(){ - context.setSettings(settings); - context.restart(); - } + public void restart(); /** * Requests the context to close, shutting down the main loop @@ -546,102 +204,14 @@ public class Application implements SystemListener { * * @see #stop(boolean) */ - public void stop(){ - stop(false); - } + public void stop(); /** * 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. - *
- * Initializes the
+ * 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.
+ *
+ * @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
+ */
+ public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
+ if (pauseOnLostFocus) {
+ setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
+ } else {
+ setLostFocusBehavior(LostFocusBehavior.Disabled);
+ }
+ }
+
+ @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(){
+ URL assetCfgUrl = null;
+
+ if (settings != null){
+ String assetCfg = settings.getString("AssetConfigURL");
+ if (assetCfg != null){
+ try {
+ assetCfgUrl = new URL(assetCfg);
+ } catch (MalformedURLException ex) {
+ }
+ if (assetCfgUrl == null) {
+ assetCfgUrl = LegacyApplication.class.getClassLoader().getResource(assetCfg);
+ if (assetCfgUrl == null) {
+ logger.log(Level.SEVERE, "Unable to access AssetConfigURL in asset config:{0}", assetCfg);
+ return;
+ }
+ }
+ }
+ }
+ if (assetCfgUrl == null) {
+ assetCfgUrl = JmeSystem.getPlatformAssetConfigURL();
+ }
+ if (assetManager == null){
+ assetManager = JmeSystem.newAssetManager(assetCfgUrl);
+ }
+ }
+
+ /**
+ * Set the display settings to define the display created.
+ *
+ * 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);
+
+ if (prof != null) {
+ renderManager.setAppProfiler(prof);
+ }
+
+ 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, false);
+ }
+
+ /**
+ * Starts the application in {@link Type#Display display} mode.
+ *
+ * @see #start(com.jme3.system.JmeContext.Type)
+ */
+ public void start(boolean waitFor){
+ start(JmeContext.Type.Display, waitFor);
+ }
+
+ /**
+ * Starts the application.
+ * Creating a rendering context and executing
+ * the main loop in a separate thread.
+ */
+ public void start(JmeContext.Type contextType) {
+ start(contextType, false);
+ }
+
+ /**
+ * Starts the application.
+ * Creating a rendering context and executing
+ * the main loop in a separate thread.
+ */
+ public void start(JmeContext.Type contextType, boolean waitFor){
+ 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(waitFor);
+ }
+
+ /**
+ * Sets an AppProfiler hook that will be called back for
+ * specific steps within a single update frame. Value defaults
+ * to null.
+ */
+ public void setAppProfiler(AppProfiler prof) {
+ this.prof = prof;
+ if (renderManager != null) {
+ renderManager.setAppProfiler(prof);
+ }
+ }
+
+ /**
+ * Returns the current AppProfiler hook, or null if none is set.
+ */
+ public AppProfiler getAppProfiler() {
+ return prof;
+ }
+
+ /**
+ * Initializes the application's canvas for use.
+ *
+ * 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.
+ *
+ * Same as calling startCanvas(false)
+ *
+ * @see #startCanvas(boolean)
+ */
+ public void startCanvas(){
+ startCanvas(false);
+ }
+
+ /**
+ * Starts the rendering thread after createCanvas() has been called.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * Initializes the
+ * 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.
+ *
+ * @param callable The callable to run in the main jME3 thread
+ */
+ public
+ * Runnables are executed right at the beginning of the main loop.
+ * They are executed even if the application is currently paused
+ * or out of focus.
+ *
+ * @param runnable The runnable to run in the main jME3 thread
+ */
+ public void enqueue(Runnable runnable){
+ enqueue(new RunnableWrapper(runnable));
+ }
+
+ /**
+ * 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);
+
+ if (prof!=null) prof.appStep(AppStep.QueuedTasks);
+ runQueuedTasks();
+
+ if (speed == 0 || paused)
+ return;
+
+ timer.update();
+
+ if (inputEnabled){
+ if (prof!=null) prof.appStep(AppStep.ProcessInput);
+ inputManager.update(timer.getTimePerFrame());
+ }
+
+ if (audioRenderer != null){
+ if (prof!=null) prof.appStep(AppStep.ProcessAudio);
+ 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;
+ }
+
+ private class RunnableWrapper implements Callable{
+ private final Runnable runnable;
+
+ public RunnableWrapper(Runnable runnable){
+ this.runnable = runnable;
+ }
+
+ @Override
+ public Object call(){
+ runnable.run();
+ return null;
+ }
+
+ }
+
+}
diff --git a/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java b/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
index 9433754dc..310191007 100644
--- a/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
+++ b/jme3-core/src/main/java/com/jme3/app/SimpleApplication.java
@@ -59,17 +59,17 @@ import com.jme3.system.JmeSystem;
* Application
, 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 not in headless mode
- if (context.getType() != JmeContext.Type.Headless) {
- 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 (lostFocusBehavior != LostFocusBehavior.Disabled) {
- if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
- paused = false;
- }
- context.setAutoFlushFrames(true);
- if (inputManager != null) {
- inputManager.reset();
- }
- }
- }
-
- /**
- * Internal use only.
- */
- public void loseFocus(){
- if (lostFocusBehavior != LostFocusBehavior.Disabled){
- if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
- paused = true;
- }
- context.setAutoFlushFrames(false);
- }
- }
-
- /**
- * Internal use only.
- */
- public void requestClose(boolean esc){
- context.destroy(false);
- }
+ public void stop(boolean waitFor);
/**
* Enqueues a task/callable object to execute in the jME3
@@ -650,15 +220,11 @@ public class Application implements SystemListener {
* 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.
- *
+ *
* @param callable The callable to run in the main jME3 thread
*/
- public LegacyApplication
class represents an instance of a
+ * real-time 3D rendering jME application.
+ *
+ * An LegacyApplication
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 LegacyApplication implements Application, SystemListener {
+
+ private static final Logger logger = Logger.getLogger(LegacyApplication.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 LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
+ 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;
+
+ protected AppProfiler prof;
+
+ private final ConcurrentLinkedQueueLegacyApplication
.
+ */
+ public LegacyApplication(){
+ initStateManager();
+ }
+
+ /**
+ * Determine the application's behavior when unfocused.
+ *
+ * @return The lost focus behavior of the application.
+ */
+ public LostFocusBehavior getLostFocusBehavior() {
+ return lostFocusBehavior;
+ }
+
+ /**
+ * Change the application's behavior when unfocused.
+ *
+ * By default, the application will
+ * {@link LostFocusBehavior#ThrottleOnLostFocus throttle the update loop}
+ * so as to not take 100% CPU usage when it is not in focus, e.g.
+ * alt-tabbed, minimized, or obstructed by another window.
+ *
+ * @param lostFocusBehavior The new lost focus behavior to use.
+ *
+ * @see LostFocusBehavior
+ */
+ public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
+ this.lostFocusBehavior = lostFocusBehavior;
+ }
+
+ /**
+ * Returns true if pause on lost focus is enabled, false otherwise.
+ *
+ * @return true if pause on lost focus is enabled
+ *
+ * @see #getLostFocusBehavior()
+ */
+ public boolean isPauseOnLostFocus() {
+ return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
+ }
+
+ /**
+ * Enable or disable pause on lost focus.
+ * Application
, 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 not in headless mode
+ if (context.getType() != JmeContext.Type.Headless) {
+ 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 (lostFocusBehavior != LostFocusBehavior.Disabled) {
+ if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
+ paused = false;
+ }
+ context.setAutoFlushFrames(true);
+ if (inputManager != null) {
+ inputManager.reset();
+ }
+ }
+ }
+
+ /**
+ * Internal use only.
+ */
+ public void loseFocus(){
+ if (lostFocusBehavior != LostFocusBehavior.Disabled){
+ if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
+ 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.
+ *
* C - Display the camera position and rotation in the console.
*
- *
+ *
* A {@link com.jme3.app.FlyCamAppState} is by default attached as well and can
* be removed by calling M - Display memory usage in the console. stateManager.detach( stateManager.getState(FlyCamAppState.class) );
*/
-public abstract class SimpleApplication extends Application {
+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;
@@ -77,7 +77,7 @@ public abstract class SimpleApplication extends Application {
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) {
@@ -101,7 +101,7 @@ public abstract class SimpleApplication extends Application {
public SimpleApplication( AppState... initialStates ) {
super();
-
+
if (initialStates != null) {
for (AppState a : initialStates) {
if (a != null) {
@@ -193,7 +193,7 @@ public abstract class SimpleApplication extends Application {
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
@@ -201,7 +201,7 @@ public abstract class SimpleApplication extends Application {
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 );
+ stateManager.getState(FlyCamAppState.class).setCamera( flyCam );
}
if (context.getType() == Type.Display) {
@@ -210,10 +210,10 @@ public abstract class SimpleApplication extends Application {
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_HIDE_STATS);
}
-
- inputManager.addListener(actionListener, INPUT_MAPPING_EXIT);
+
+ inputManager.addListener(actionListener, INPUT_MAPPING_EXIT);
}
if (stateManager.getState(StatsAppState.class) != null) {
@@ -230,37 +230,37 @@ public abstract class SimpleApplication extends Application {
@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);
}
diff --git a/jme3-core/src/main/java/com/jme3/app/StatsAppState.java b/jme3-core/src/main/java/com/jme3/app/StatsAppState.java
index 346733257..d5ffde097 100644
--- a/jme3-core/src/main/java/com/jme3/app/StatsAppState.java
+++ b/jme3-core/src/main/java/com/jme3/app/StatsAppState.java
@@ -46,7 +46,7 @@ import com.jme3.scene.shape.Quad;
/**
* Displays stats in SimpleApplication's GUI node or
- * using the node and font parameters provided.
+ * using the node and font parameters provided.
*
* @author Paul Speed
*/
@@ -58,7 +58,7 @@ public class StatsAppState extends AbstractAppState {
private boolean showFps = true;
private boolean showStats = true;
private boolean darkenBehind = true;
-
+
protected Node guiNode;
protected float secondCounter = 0.0f;
protected int frameCounter = 0;
@@ -68,7 +68,7 @@ public class StatsAppState extends AbstractAppState {
protected Geometry darkenStats;
public StatsAppState() {
- }
+ }
public StatsAppState( Node guiNode, BitmapFont guiFont ) {
this.guiNode = guiNode;
@@ -89,7 +89,7 @@ public class StatsAppState extends AbstractAppState {
public BitmapText getFpsText() {
return fpsText;
}
-
+
public StatsView getStatsView() {
return statsView;
}
@@ -110,7 +110,7 @@ public class StatsAppState extends AbstractAppState {
if (darkenFps != null) {
darkenFps.setCullHint(showFps && darkenBehind ? CullHint.Never : CullHint.Always);
}
-
+
}
}
@@ -138,7 +138,7 @@ public class StatsAppState extends AbstractAppState {
public void initialize(AppStateManager stateManager, Application app) {
super.initialize(stateManager, app);
this.app = app;
-
+
if (app instanceof SimpleApplication) {
SimpleApplication simpleApp = (SimpleApplication)app;
if (guiNode == null) {
@@ -147,21 +147,21 @@ public class StatsAppState extends AbstractAppState {
if (guiFont == null ) {
guiFont = simpleApp.guiFont;
}
- }
-
+ }
+
if (guiNode == null) {
throw new RuntimeException( "No guiNode specific and cannot be automatically determined." );
- }
-
+ }
+
if (guiFont == null) {
guiFont = app.getAssetManager().loadFont("Interface/Fonts/Default.fnt");
}
-
- loadFpsText();
- loadStatsView();
+
+ loadFpsText();
+ loadStatsView();
loadDarken();
}
-
+
/**
* Attaches FPS statistics to guiNode and displays it on the screen.
*
@@ -170,12 +170,12 @@ public class StatsAppState extends AbstractAppState {
if (fpsText == null) {
fpsText = new BitmapText(guiFont, false);
}
-
+
fpsText.setLocalTranslation(0, fpsText.getLineHeight(), 0);
fpsText.setText("Frames per second");
fpsText.setCullHint(showFps ? CullHint.Never : CullHint.Always);
guiNode.attachChild(fpsText);
-
+
}
/**
@@ -184,53 +184,53 @@ public class StatsAppState extends AbstractAppState {
*
*/
public void loadStatsView() {
- statsView = new StatsView("Statistics View",
- app.getAssetManager(),
+ statsView = new StatsView("Statistics View",
+ app.getAssetManager(),
app.getRenderer().getStatistics());
// move it up so it appears above fps text
statsView.setLocalTranslation(0, fpsText.getLineHeight(), 0);
statsView.setEnabled(showStats);
- statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
+ statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
guiNode.attachChild(statsView);
}
-
+
public void loadDarken() {
- Material mat = new Material(app.assetManager, "Common/MatDefs/Misc/Unshaded.j3md");
+ Material mat = new Material(app.getAssetManager(), "Common/MatDefs/Misc/Unshaded.j3md");
mat.setColor("Color", new ColorRGBA(0,0,0,0.5f));
mat.getAdditionalRenderState().setBlendMode(BlendMode.Alpha);
-
+
darkenFps = new Geometry("StatsDarken", new Quad(200, fpsText.getLineHeight()));
darkenFps.setMaterial(mat);
darkenFps.setLocalTranslation(0, 0, -1);
darkenFps.setCullHint(showFps && darkenBehind ? CullHint.Never : CullHint.Always);
guiNode.attachChild(darkenFps);
-
+
darkenStats = new Geometry("StatsDarken", new Quad(200, statsView.getHeight()));
darkenStats.setMaterial(mat);
darkenStats.setLocalTranslation(0, fpsText.getHeight(), -1);
darkenStats.setCullHint(showStats && darkenBehind ? CullHint.Never : CullHint.Always);
guiNode.attachChild(darkenStats);
}
-
+
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
-
+
if (enabled) {
fpsText.setCullHint(showFps ? CullHint.Never : CullHint.Always);
darkenFps.setCullHint(showFps && darkenBehind ? CullHint.Never : CullHint.Always);
statsView.setEnabled(showStats);
- statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
+ statsView.setCullHint(showStats ? CullHint.Never : CullHint.Always);
darkenStats.setCullHint(showStats && darkenBehind ? CullHint.Never : CullHint.Always);
} else {
fpsText.setCullHint(CullHint.Always);
darkenFps.setCullHint(CullHint.Always);
statsView.setEnabled(false);
- statsView.setCullHint(CullHint.Always);
+ statsView.setCullHint(CullHint.Always);
darkenStats.setCullHint(CullHint.Always);
}
}
-
+
@Override
public void update(float tpf) {
if (showFps) {
@@ -241,14 +241,14 @@ public class StatsAppState extends AbstractAppState {
fpsText.setText("Frames per second: " + fps);
secondCounter = 0.0f;
frameCounter = 0;
- }
+ }
}
}
@Override
public void cleanup() {
super.cleanup();
-
+
guiNode.detachChild(statsView);
guiNode.detachChild(fpsText);
guiNode.detachChild(darkenFps);
diff --git a/jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java b/jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java
index 18fc82a61..159bd95ac 100644
--- a/jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java
+++ b/jme3-desktop/src/main/java/com/jme3/app/AppletHarness.java
@@ -50,12 +50,12 @@ import javax.swing.SwingUtilities;
*/
public class AppletHarness extends Applet {
- public static final HashMap