You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1537 lines
51 KiB
1537 lines
51 KiB
package com.jme3.app;
|
|
|
|
import com.jme3.app.AppTask;
|
|
import com.jme3.app.Application;
|
|
import com.jme3.app.LegacyApplication;
|
|
import com.jme3.app.LostFocusBehavior;
|
|
import com.jme3.app.ResetStatsState;
|
|
import com.jme3.app.SimpleApplication;
|
|
import com.jme3.app.state.AppState;
|
|
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.InputManager;
|
|
import com.jme3.input.JoyInput;
|
|
import com.jme3.input.KeyInput;
|
|
import com.jme3.input.MouseInput;
|
|
import com.jme3.input.TouchInput;
|
|
import com.jme3.input.controls.KeyTrigger;
|
|
import com.jme3.input.vr.OSVR;
|
|
import com.jme3.input.vr.OpenVR;
|
|
import com.jme3.input.vr.VRAPI;
|
|
import com.jme3.input.vr.VRInputAPI;
|
|
import com.jme3.math.ColorRGBA;
|
|
import com.jme3.math.Quaternion;
|
|
import com.jme3.math.Vector3f;
|
|
import com.jme3.post.PreNormalCaching;
|
|
import com.jme3.profile.AppProfiler;
|
|
import com.jme3.renderer.Camera;
|
|
import com.jme3.renderer.RenderManager;
|
|
import com.jme3.renderer.Renderer;
|
|
import com.jme3.renderer.ViewPort;
|
|
import com.jme3.renderer.queue.RenderQueue.Bucket;
|
|
import com.jme3.scene.Node;
|
|
import com.jme3.scene.Spatial;
|
|
import com.jme3.scene.Spatial.CullHint;
|
|
import com.jme3.system.AppSettings;
|
|
import com.jme3.system.JmeContext;
|
|
import com.jme3.system.JmeContext.Type;
|
|
import com.jme3.system.jopenvr.JOpenVRLibrary;
|
|
import com.jme3.system.JmeSystem;
|
|
import com.jme3.system.NanoTimer;
|
|
import com.jme3.system.SystemListener;
|
|
import com.jme3.system.Timer;
|
|
import com.jme3.system.lwjgl.LwjglDisplayVR;
|
|
import com.jme3.system.lwjgl.LwjglOffscreenBufferVR;
|
|
|
|
import java.awt.GraphicsDevice;
|
|
import java.awt.GraphicsEnvironment;
|
|
import java.awt.HeadlessException;
|
|
import java.io.BufferedReader;
|
|
import java.io.File;
|
|
import java.io.FileReader;
|
|
import java.net.MalformedURLException;
|
|
import java.net.URL;
|
|
import java.util.Locale;
|
|
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 jmevr.util.VRViewManager;
|
|
import jmevr.util.VRGuiManager;
|
|
import jmevr.util.VRGuiManager.POSITIONING_MODE;
|
|
import jmevr.util.VRMouseManager;
|
|
|
|
import org.lwjgl.system.Platform;
|
|
|
|
|
|
/**
|
|
* A JMonkey application dedicated to Virtual Reality. An application that use VR devices (HTC vive, ...) has to extends this one.<br>
|
|
* <p>
|
|
* <b>This class is no more functional and is deprecated. Please use {@link VRAppState VRAppState} instead.</b>
|
|
* @author reden - phr00t - https://github.com/phr00t
|
|
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
|
|
* @deprecated use {@link VRAppState VRAppState} instead.
|
|
*/
|
|
public abstract class VRApplication implements Application, SystemListener {
|
|
|
|
|
|
private static final Logger logger = Logger.getLogger(LegacyApplication.class.getName());
|
|
|
|
/**
|
|
* The default FOV.
|
|
*/
|
|
public float DEFAULT_FOV = 108f;
|
|
|
|
|
|
/**
|
|
* The default aspect ratio.
|
|
*/
|
|
public float DEFAULT_ASPECT = 1f;
|
|
|
|
/**
|
|
* Is the application is based on OSVR (default is <code>false</code>).
|
|
*/
|
|
public boolean CONSTRUCT_WITH_OSVR = false;
|
|
|
|
/**
|
|
* Is the application has not to start within VR mode (default is <code>false</code>).
|
|
*/
|
|
public boolean DISABLE_VR = false;
|
|
|
|
/**
|
|
* VR application configuration parameters.
|
|
* @author reden - phr00t - https://github.com/phr00t
|
|
* @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
|
|
*
|
|
*/
|
|
public static enum PreconfigParameter {
|
|
/**
|
|
* Is the SteamVR compositor is used (kinda needed at the moment)
|
|
*/
|
|
USE_VR_COMPOSITOR,
|
|
|
|
/**
|
|
* Render two eyes, regardless of VR API detection.
|
|
*/
|
|
FORCE_VR_MODE,
|
|
|
|
/**
|
|
* Invert the eyes.
|
|
*/
|
|
FLIP_EYES,
|
|
|
|
/**
|
|
* Show GUI even if it is behind objects.
|
|
*/
|
|
SET_GUI_OVERDRAW,
|
|
|
|
/**
|
|
*
|
|
*/
|
|
SET_GUI_CURVED_SURFACE,
|
|
|
|
/**
|
|
* Display a mirror rendering on the screen. Runs faster when set to <code>false</code>.
|
|
*/
|
|
ENABLE_MIRROR_WINDOW,
|
|
|
|
/**
|
|
*
|
|
*/
|
|
PREFER_OPENGL3,
|
|
|
|
/**
|
|
* Disable VR rendering, regardless VR API and devices are presents.
|
|
*/
|
|
DISABLE_VR,
|
|
|
|
/**
|
|
*
|
|
*/
|
|
SEATED_EXPERIENCE,
|
|
|
|
/**
|
|
* Remove GUI node from the application.
|
|
*/
|
|
NO_GUI,
|
|
|
|
/**
|
|
* Faster VR rendering, requires some vertex shader changes (see Common/MatDefs/VR/Unshaded.j3md)
|
|
*/
|
|
INSTANCE_VR_RENDERING,
|
|
|
|
/**
|
|
*
|
|
*/
|
|
FORCE_DISABLE_MSAA
|
|
}
|
|
|
|
private VRAPI VRhardware = null;
|
|
private VRGuiManager guiManager = null;
|
|
private VRMouseManager mouseManager = null;
|
|
private VRViewManager viewmanager = null;
|
|
|
|
private String OS;
|
|
|
|
private Camera dummyCam;
|
|
private Spatial observer;
|
|
private boolean VRSupportedOS;
|
|
private boolean forceVR;
|
|
private boolean disableSwapBuffers = true;
|
|
private boolean tryOpenGL3 = true;
|
|
private boolean seated;
|
|
private boolean nogui;
|
|
private boolean instanceVR;
|
|
private boolean forceDisableMSAA;
|
|
|
|
// things taken from LegacyApplication
|
|
private AppStateManager stateManager;
|
|
private Camera cam;
|
|
private AppSettings settings;
|
|
private JmeContext context;
|
|
private float speed = 1f;
|
|
private AudioRenderer audioRenderer;
|
|
private LostFocusBehavior lostFocusBehavior = LostFocusBehavior.ThrottleOnLostFocus;
|
|
private final ConcurrentLinkedQueue<AppTask<?>> taskQueue = new ConcurrentLinkedQueue<AppTask<?>>();
|
|
private Timer timer = new NanoTimer();
|
|
private boolean paused = false, inputEnabled = true;
|
|
private InputManager inputManager;
|
|
private RenderManager renderManager;
|
|
private ViewPort viewPort;
|
|
private ViewPort guiViewPort;
|
|
private AssetManager assetManager;
|
|
private Renderer renderer;
|
|
private Listener listener;
|
|
private MouseInput mouseInput;
|
|
private KeyInput keyInput;
|
|
private JoyInput joyInput;
|
|
private TouchInput touchInput;
|
|
|
|
protected Node guiNode, rootNode;
|
|
|
|
private float fFar = 1000f, fNear = 1f;
|
|
private int xWin = 1280, yWin = 720;
|
|
|
|
private float resMult = 1f;
|
|
|
|
private boolean useCompositor = true, compositorOS;
|
|
private final String RESET_HMD = "ResetHMD";
|
|
|
|
/**
|
|
* Create a new VR application and attach the given {@link AppState app states}.<br>
|
|
* The application scene is made of a {@link #getRootNode() root node} that holds the scene spatials
|
|
* and a {@link #getGuiNode() GUI node} that is the root of the Graphical user interface.
|
|
* @param initialStates the {@link AppState app states} to attach to the application.
|
|
*/
|
|
public VRApplication(AppState... initialStates) {
|
|
this();
|
|
|
|
if (initialStates != null) {
|
|
for (AppState a : initialStates) {
|
|
if (a != null) {
|
|
stateManager.attach(a);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new VR application.<br>
|
|
* The application scene is made of a {@link #getRootNode() root node} that holds the scene spatials
|
|
* and a {@link #getGuiNode() GUI node} that is the root of the Graphical user interface.
|
|
*/
|
|
public VRApplication() {
|
|
super();
|
|
|
|
rootNode = new Node("root");
|
|
guiNode = new Node("guiNode");
|
|
|
|
guiNode.setQueueBucket(Bucket.Gui);
|
|
guiNode.setCullHint(CullHint.Never);
|
|
dummyCam = new Camera();
|
|
|
|
initStateManager();
|
|
|
|
// Create the GUI manager.
|
|
guiManager = new VRGuiManager();
|
|
|
|
// Create a new view manager.
|
|
viewmanager = new VRViewManager();
|
|
|
|
// Create a new mouse manager.
|
|
mouseManager = new VRMouseManager();
|
|
|
|
// we are going to use OpenVR now, not the Oculus Rift
|
|
// OpenVR does support the Rift
|
|
OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH);
|
|
VRSupportedOS = !OS.contains("nux") && System.getProperty("sun.arch.data.model").equalsIgnoreCase("64"); //for the moment, linux/unix causes crashes, 64-bit only
|
|
compositorOS = OS.contains("indows");
|
|
|
|
if( !VRSupportedOS ) {
|
|
logger.warning("Non-supported OS: " + OS + ", architecture: " + System.getProperty("sun.arch.data.model"));
|
|
} else if( DISABLE_VR ) {
|
|
logger.warning("VR disabled via code.");
|
|
} else if( VRSupportedOS && DISABLE_VR == false ) {
|
|
if( CONSTRUCT_WITH_OSVR ) {
|
|
//FIXME: WARNING !!
|
|
VRhardware = new OSVR(null);
|
|
logger.config("Creating OSVR wrapper [SUCCESS]");
|
|
} else {
|
|
//FIXME: WARNING !!
|
|
VRhardware = new OpenVR(null);
|
|
logger.config("Creating OpenVR wrapper [SUCCESS]");
|
|
}
|
|
if( VRhardware.initialize() ) {
|
|
setPauseOnLostFocus(false);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get the VR underlying hardware.
|
|
* @return the VR underlying hardware.
|
|
*/
|
|
public VRAPI getVRHardware() {
|
|
return VRhardware;
|
|
}
|
|
|
|
/**
|
|
* Get the VR dedicated input.
|
|
* @return the VR dedicated input.
|
|
*/
|
|
public VRInputAPI getVRinput() {
|
|
if( VRhardware == null ) return null;
|
|
return VRhardware.getVRinput();
|
|
}
|
|
|
|
/**
|
|
* Get the VR view manager.
|
|
* @return the VR view manager.
|
|
*/
|
|
public VRViewManager getVRViewManager() {
|
|
return viewmanager;
|
|
}
|
|
|
|
/**
|
|
* Get the GUI manager attached to this application.
|
|
* @return the GUI manager attached to this application.
|
|
*/
|
|
public VRGuiManager getVRGUIManager(){
|
|
return guiManager;
|
|
}
|
|
|
|
/**
|
|
* Get the VR mouse manager attached to this application.
|
|
* @return the VR mouse manager attached to this application.
|
|
*/
|
|
public VRMouseManager getVRMouseManager(){
|
|
return mouseManager;
|
|
}
|
|
|
|
/**
|
|
* Set the frustrum values for the application.
|
|
* @param near the frustrum near value.
|
|
* @param far the frustrum far value.
|
|
*/
|
|
public void setFrustrumNearFar(float near, float far) {
|
|
fNear = near;
|
|
fFar = far;
|
|
}
|
|
|
|
/**
|
|
* Set the mirror window size in pixel.
|
|
* @param width the width of the mirror window in pixel.
|
|
* @param height the height of the mirror window in pixel.
|
|
*/
|
|
public void setMirrorWindowSize(int width, int height) {
|
|
xWin = width;
|
|
yWin = height;
|
|
}
|
|
|
|
/**
|
|
* Set the resolution multiplier.
|
|
* @param val the resolution multiplier.
|
|
*/
|
|
public void setResolutionMultiplier(float val) {
|
|
resMult = val;
|
|
if( viewmanager != null ) viewmanager.setResolutionMultiplier(resMult);
|
|
}
|
|
|
|
|
|
/**
|
|
* Is the SteamVR compositor is active.
|
|
* @return <code>true</code> if the SteamVR compositor is active and <code>false</code> otherwise.
|
|
*/
|
|
public boolean compositorAllowed() {
|
|
return useCompositor && compositorOS;
|
|
}
|
|
|
|
/**
|
|
* Get if the system currently support VR.
|
|
* @return <code>true</code> if the system currently support VR and <code>false</Code> otherwise.
|
|
*/
|
|
public boolean isOSVRSupported() {
|
|
return VRSupportedOS;
|
|
}
|
|
|
|
/**
|
|
* Simple update of the application, this method should contains {@link #getRootNode() root node} updates.
|
|
* This method is called by the {@link #update() update()} method and should not be called manually.
|
|
* @param tpf the application time.
|
|
*/
|
|
public void simpleUpdate(float tpf) { }
|
|
|
|
/**
|
|
* Rendering callback of the application. This method is called by the {@link #update() update()} method and should not be called manually.
|
|
* @param renderManager the {@link RenderManager render manager}.
|
|
*/
|
|
public void simpleRender(RenderManager renderManager) {
|
|
PreNormalCaching.resetCache(isInVR());
|
|
}
|
|
|
|
|
|
/*
|
|
we do NOT want to get & modify the distortion scene camera, so
|
|
return the left viewport camera instead if we are in VR mode
|
|
*/
|
|
@Override
|
|
public Camera getCamera() {
|
|
if( isInVR() && viewmanager != null && viewmanager.getLeftCamera() != null ) {
|
|
return dummyCam;
|
|
}
|
|
return cam;
|
|
}
|
|
|
|
/**
|
|
* Get the application internal camera.
|
|
* @return the application internal camera.
|
|
* @see #getCamera()
|
|
*/
|
|
public Camera getBaseCamera() {
|
|
return cam;
|
|
}
|
|
|
|
|
|
@Override
|
|
public JmeContext getContext(){
|
|
return context;
|
|
}
|
|
|
|
@Override
|
|
public AssetManager getAssetManager(){
|
|
return assetManager;
|
|
}
|
|
|
|
@Override
|
|
public InputManager getInputManager(){
|
|
return inputManager;
|
|
}
|
|
|
|
@Override
|
|
public AppStateManager getStateManager() {
|
|
return stateManager;
|
|
}
|
|
|
|
@Override
|
|
public RenderManager getRenderManager() {
|
|
return renderManager;
|
|
}
|
|
|
|
@Override
|
|
public Renderer getRenderer(){
|
|
return renderer;
|
|
}
|
|
|
|
@Override
|
|
public AudioRenderer getAudioRenderer() {
|
|
return audioRenderer;
|
|
}
|
|
|
|
@Override
|
|
public Listener getListener() {
|
|
return listener;
|
|
}
|
|
|
|
@Override
|
|
public Timer getTimer(){
|
|
return timer;
|
|
}
|
|
|
|
/**
|
|
* Handle the error given in parameters by creating a log entry and a dialog window. 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
|
|
}
|
|
|
|
|
|
/**
|
|
* Force the focus gain for 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();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Force the focus lost for the application. Internal use only.
|
|
*/
|
|
public void loseFocus(){
|
|
if (lostFocusBehavior != LostFocusBehavior.Disabled){
|
|
if (lostFocusBehavior == LostFocusBehavior.PauseOnLostFocus) {
|
|
paused = true;
|
|
}
|
|
context.setAutoFlushFrames(false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reshape the display window. Internal use only.
|
|
*/
|
|
public void reshape(int w, int h){
|
|
if (renderManager != null) {
|
|
renderManager.notifyReshape(w, h);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Request the application to close. Internal use only.
|
|
*/
|
|
public void requestClose(boolean esc){
|
|
context.destroy(false);
|
|
}
|
|
|
|
/**
|
|
* Set the {@link AppSettings display settings} to define the display created.
|
|
* <p>
|
|
* Examples of display parameters include display frame {@link AppSettings#getWidth() width} and {@link AppSettings#getHeight() height},
|
|
* pixel {@link AppSettings#getBitsPerPixel() color bit depth}, {@link AppSettings#getDepthBits() z-buffer bits}, {@link AppSettings#getSamples() anti-aliasing samples}, {@link AppSettings#getFrequency() update frequency}, ...
|
|
* <br><br>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 {@link Timer} implementation that will be used for calculating
|
|
* frame times.<br><br>
|
|
* By default, Application will use the Timer as returned by the current {@link JmeContext} implementation.
|
|
* @param timer the timer to use.
|
|
*/
|
|
public void setTimer(Timer timer){
|
|
this.timer = timer;
|
|
|
|
if (timer != null) {
|
|
timer.reset();
|
|
}
|
|
|
|
if (renderManager != null) {
|
|
renderManager.setTimer(timer);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* 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 {@link LostFocusBehavior lost focus behavior} to use.
|
|
*/
|
|
public void setLostFocusBehavior(LostFocusBehavior lostFocusBehavior) {
|
|
this.lostFocusBehavior = lostFocusBehavior;
|
|
}
|
|
|
|
/**
|
|
* Get if the application has to pause then it lost the focus.
|
|
* @return <code>true</code> if pause on lost focus is enabled, <code>false</code> otherwise.
|
|
* @see #getLostFocusBehavior()
|
|
*/
|
|
public boolean isPauseOnLostFocus() {
|
|
return getLostFocusBehavior() == LostFocusBehavior.PauseOnLostFocus;
|
|
}
|
|
|
|
/**
|
|
* 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 <code>true</code> to enable pause on lost focus, <code>false</code>
|
|
* otherwise.
|
|
*
|
|
* @see #setLostFocusBehavior(com.jme3.app.LostFocusBehavior)
|
|
*/
|
|
public void setPauseOnLostFocus(boolean pauseOnLostFocus) {
|
|
if (pauseOnLostFocus) {
|
|
setLostFocusBehavior(LostFocusBehavior.PauseOnLostFocus);
|
|
} else {
|
|
setLostFocusBehavior(LostFocusBehavior.Disabled);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
|
|
logger.config("Starting application...");
|
|
|
|
// set some default settings in-case
|
|
// settings dialog is not shown
|
|
boolean loadSettings = false;
|
|
if (settings == null) {
|
|
setSettings(new AppSettings(true));
|
|
loadSettings = true;
|
|
}
|
|
|
|
GraphicsDevice defDev = null;
|
|
try {
|
|
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
|
|
defDev = ge.getDefaultScreenDevice();
|
|
} catch (Throwable e1) {
|
|
logger.log(Level.SEVERE, "Cannot access default screen device: "+e1.getMessage(), e1);
|
|
}
|
|
|
|
if( isInVR() && !compositorAllowed() ) {
|
|
logger.warning("VR Composition is not allowed.");
|
|
// "easy extended" mode
|
|
// TO-DO: JFrame was removed in LWJGL 3, need to use new GLFW library to pick "monitor" display of VR device
|
|
// first, find the VR device
|
|
GraphicsDevice VRdev = null;
|
|
GraphicsDevice[] devs = GraphicsEnvironment.getLocalGraphicsEnvironment().getScreenDevices();
|
|
// pick the display that isn't the default one
|
|
for(GraphicsDevice gd : devs) {
|
|
if( gd != defDev ) {
|
|
VRdev = gd;
|
|
break;
|
|
}
|
|
}
|
|
// did we get the VR device?
|
|
if( VRdev != null ) {
|
|
// set properties for VR acceleration
|
|
try {
|
|
java.awt.DisplayMode useDM = null;
|
|
int max = 0;
|
|
for(java.awt.DisplayMode dm : VRdev.getDisplayModes()) {
|
|
int check = dm.getHeight() + dm.getWidth() + dm.getRefreshRate() + dm.getBitDepth();
|
|
if( check > max ) {
|
|
max = check;
|
|
useDM = dm;
|
|
}
|
|
}
|
|
// create a window for the VR device
|
|
settings.setWidth(useDM.getWidth());
|
|
settings.setHeight(useDM.getHeight());
|
|
settings.setBitsPerPixel(useDM.getBitDepth());
|
|
settings.setFrequency(useDM.getRefreshRate());
|
|
settings.setSwapBuffers(true);
|
|
settings.setVSync(true); // allow vsync on this display
|
|
setSettings(settings);
|
|
//VRdev.setFullScreenWindow(VRwindow);
|
|
// make sure we are in the right display mode
|
|
if( VRdev.getDisplayMode().equals(useDM) == false ) {
|
|
VRdev.setDisplayMode(useDM);
|
|
}
|
|
// make a blank cursor to hide it
|
|
//BufferedImage cursorImg = new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB);
|
|
//Cursor blankCursor = Toolkit.getDefaultToolkit().createCustomCursor(cursorImg, new Point(0, 0), "blank cursor");
|
|
//VRwindow.setCursor(blankCursor);
|
|
//jmeCanvas.getCanvas().setCursor(blankCursor);
|
|
//VRwindow.pack();
|
|
//VRwindow.setVisible(true);
|
|
//startCanvas();
|
|
logger.config("Starting application [SUCCESS]");
|
|
return;
|
|
} catch(Exception e) {
|
|
logger.log(Level.SEVERE, "Error during application start: "+e.getMessage(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !isInVR() ) {
|
|
|
|
logger.config("VR mode disabled.");
|
|
|
|
// not in VR, show settings dialog
|
|
if( Platform.get() != Platform.MACOSX ) {
|
|
if (!JmeSystem.showSettingsDialog(settings, loadSettings)) {
|
|
logger.config("Starting application [SUCCESS]");
|
|
return;
|
|
}
|
|
} else {
|
|
// GLFW workaround on macs
|
|
settings.setFrequency(defDev.getDisplayMode().getRefreshRate());
|
|
settings.setDepthBits(24);
|
|
settings.setVSync(true);
|
|
// try and read resolution from file in local dir
|
|
File resfile = new File("resolution.txt");
|
|
if( resfile.exists() ) {
|
|
try {
|
|
BufferedReader br = new BufferedReader(new FileReader(resfile));
|
|
settings.setWidth(Integer.parseInt(br.readLine()));
|
|
settings.setHeight(Integer.parseInt(br.readLine()));
|
|
try {
|
|
settings.setFullscreen(br.readLine().toLowerCase(Locale.ENGLISH).contains("full"));
|
|
} catch(Exception e) {
|
|
settings.setFullscreen(false);
|
|
}
|
|
br.close();
|
|
} catch(Exception e) {
|
|
settings.setWidth(1280);
|
|
settings.setHeight(720);
|
|
}
|
|
} else {
|
|
settings.setWidth(1280);
|
|
settings.setHeight(720);
|
|
settings.setFullscreen(false);
|
|
}
|
|
settings.setResizable(false);
|
|
}
|
|
settings.setSwapBuffers(true);
|
|
} else {
|
|
|
|
logger.config("VR mode enabled.");
|
|
|
|
// use basic mirroring window, skip settings window
|
|
settings.setWidth(xWin);
|
|
settings.setHeight(yWin);
|
|
settings.setBitsPerPixel(24);
|
|
settings.setFrameRate(0); // never sleep in main loop
|
|
settings.setFrequency(VRhardware.getDisplayFrequency());
|
|
settings.setFullscreen(false);
|
|
settings.setVSync(false); // stop vsyncing on primary monitor!
|
|
settings.setSwapBuffers(!disableSwapBuffers || VRhardware instanceof OSVR);
|
|
settings.setTitle("Put Headset On Now: " + settings.getTitle());
|
|
settings.setResizable(true);
|
|
}
|
|
|
|
if( forceDisableMSAA ) {
|
|
logger.config("Disabling multisampling.");
|
|
// disable multisampling, which is more likely to break things than be useful
|
|
settings.setSamples(1);
|
|
}
|
|
|
|
// set opengl mode
|
|
if( tryOpenGL3 ) {
|
|
logger.config("Using LWJGL OpenGL 3 renderer.");
|
|
settings.setRenderer(AppSettings.LWJGL_OPENGL3);
|
|
} else {
|
|
logger.config("Using LWJGL OpenGL 2 renderer.");
|
|
settings.setRenderer(AppSettings.LWJGL_OPENGL2);
|
|
}
|
|
|
|
|
|
setSettings(settings);
|
|
start(JmeContext.Type.Display, false);
|
|
|
|
// disable annoying warnings about GUI stuff being updated, which is normal behavior
|
|
// for late GUI placement for VR purposes
|
|
Logger.getLogger("com.jme3").setLevel(Level.SEVERE);
|
|
}
|
|
|
|
/**
|
|
* Starts the application in {@link com.jme3.system.JmeContext.Type#Display display} mode.
|
|
* @param waitFor if <code>true</code>, the method will wait until the application is started.
|
|
* @see #start(com.jme3.system.JmeContext.Type, boolean)
|
|
*/
|
|
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.
|
|
* @param contextType the {@link com.jme3.system.JmeContext.Type type} of the context to create.
|
|
* @param waitFor if <code>true</code>, the method will wait until the application is started.
|
|
* @throws IllegalArgumentException if the context type is not supported.
|
|
*/
|
|
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());
|
|
|
|
// Create VR decicated context
|
|
if (contextType == Type.Display){
|
|
context = new LwjglDisplayVR();
|
|
context.setSettings(settings);
|
|
} else if (contextType == Type.OffscreenSurface){
|
|
context = new LwjglOffscreenBufferVR();
|
|
context.setSettings(settings);
|
|
} else {
|
|
logger.severe("Unsupported context type \""+contextType+"\". Supported are \"Display\" and \"OffscreenSurface\"");
|
|
throw new IllegalArgumentException("Unsupported context type \""+contextType+"\". Supported are \"Display\" and \"OffscreenSurface\"");
|
|
}
|
|
|
|
context.setSystemListener(this);
|
|
context.create(waitFor);
|
|
}
|
|
|
|
/**
|
|
* Move filters from the main scene into the eye's.
|
|
* This removes filters from the main scene.
|
|
*/
|
|
public void moveScreenProcessingToVR() {
|
|
if( isInVR() ) {
|
|
viewmanager.moveScreenProcessingToEyes();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set VR application {@link PreconfigParameter specific parameter}.
|
|
* If making changes to default values, this must be called before the VRApplication starts
|
|
* @param parm the parameter to set.
|
|
* @param value the value of the parameter.
|
|
*/
|
|
public void preconfigureVRApp(PreconfigParameter parm, boolean value) {
|
|
switch( parm ) {
|
|
case SET_GUI_OVERDRAW:
|
|
guiManager._enableGuiOverdraw(value);
|
|
break;
|
|
case SET_GUI_CURVED_SURFACE:
|
|
guiManager._enableCurvedSuface(value);
|
|
break;
|
|
case FORCE_VR_MODE:
|
|
forceVR = value;
|
|
break;
|
|
//case USE_CUSTOM_DISTORTION: //deprecated, always using a render manager
|
|
// VRViewManager._setCustomDistortion(value);
|
|
// break;
|
|
case USE_VR_COMPOSITOR:
|
|
useCompositor = value;
|
|
if( value == false ) disableSwapBuffers = false;
|
|
break;
|
|
case FLIP_EYES:
|
|
if( VRhardware == null ) return;
|
|
VRhardware._setFlipEyes(value);
|
|
break;
|
|
case INSTANCE_VR_RENDERING:
|
|
instanceVR = value;
|
|
break;
|
|
case ENABLE_MIRROR_WINDOW:
|
|
if( useCompositor == false ) {
|
|
disableSwapBuffers = false;
|
|
} else disableSwapBuffers = !value;
|
|
break;
|
|
case PREFER_OPENGL3:
|
|
tryOpenGL3 = value;
|
|
break;
|
|
case DISABLE_VR:
|
|
DISABLE_VR = value;
|
|
break;
|
|
case NO_GUI:
|
|
nogui = value;
|
|
break;
|
|
case SEATED_EXPERIENCE:
|
|
seated = value;
|
|
break;
|
|
case FORCE_DISABLE_MSAA:
|
|
forceDisableMSAA = value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Can be used to change seated experience during runtime.
|
|
* @param isSeated <code>true</code> if designed for sitting, <code>false</code> for standing/roomscale
|
|
* @see #isSeatedExperience()
|
|
*/
|
|
public void setSeatedExperience(boolean isSeated) {
|
|
seated = isSeated;
|
|
if( VRhardware instanceof OpenVR ) {
|
|
if( VRhardware.getCompositor() == null ) return;
|
|
if( seated ) {
|
|
((OpenVR)VRhardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
|
|
} else {
|
|
((OpenVR)VRhardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if the application is configured as a seated experience.
|
|
* @return <code>true</code> if the application is configured as a seated experience and <code>false</code> otherwise.
|
|
* @see #setSeatedExperience(boolean)
|
|
*/
|
|
public boolean isSeatedExperience() {
|
|
return seated;
|
|
}
|
|
|
|
/**
|
|
* Reset headset pose if seating experience.
|
|
*/
|
|
public void resetSeatedPose(){
|
|
if( VRSupportedOS == false || isSeatedExperience() == false ) return;
|
|
VRhardware.reset();
|
|
}
|
|
|
|
/**
|
|
* Check if the rendering is instanced (see <a href="https://en.wikipedia.org/wiki/Geometry_instancing">Geometry instancing</a>).
|
|
* @return <code>true</code> if the rendering is instanced and <code>false</code> otherwise.
|
|
*/
|
|
public boolean isInstanceVRRendering() {
|
|
return instanceVR && isInVR();
|
|
}
|
|
|
|
/**
|
|
* Check if the VR mode is enabled.
|
|
* @return <code>true</code> if the VR mode is enabled and <code>false</code> otherwise.
|
|
*/
|
|
public boolean isInVR() {
|
|
return DISABLE_VR == false && (forceVR || VRSupportedOS && VRhardware != null && VRhardware.isInitialized());
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the GUI node from the application.
|
|
* @return the GUI node from the application.
|
|
* @see #setGuiNode(Node)
|
|
*/
|
|
public Node getGuiNode(){
|
|
return guiNode;
|
|
}
|
|
|
|
/**
|
|
* Set the GUI node that is displayed within the GUI viewport.
|
|
* Calling this method involve clearing all the scenes previously attached to the gui viewport.
|
|
* @param node the GUI node to attach.
|
|
* @see #getGuiNode()
|
|
*/
|
|
public void setGuiNode(Node node){
|
|
if (node != null){
|
|
if (guiViewPort != null){
|
|
|
|
enqueue(new Callable<Object>(){
|
|
|
|
@Override
|
|
public Object call() throws Exception {
|
|
guiViewPort.clearScenes();
|
|
guiViewPort.attachScene(node);
|
|
guiNode = node;
|
|
return null;
|
|
}
|
|
|
|
});
|
|
|
|
} else {
|
|
throw new IllegalArgumentException("GUI view port is not initialized.");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Get the root node of the application.
|
|
* @return the root node of the application.
|
|
*/
|
|
public Node getRootNode() {
|
|
return rootNode;
|
|
}
|
|
|
|
/**
|
|
* Check if the application has a GUI overlay attached.
|
|
* @return <code>true</code> if the application has a GUI overlay attached and <code>false</code> otherwise.
|
|
*/
|
|
public boolean hasTraditionalGUIOverlay() {
|
|
return !nogui;
|
|
}
|
|
|
|
|
|
/**
|
|
* Get the scene observer. If no observer has been set, this method return the application {@link #getCamera() camera}.
|
|
* @return the scene observer.
|
|
* @see #setObserver(Spatial)
|
|
*/
|
|
public Object getObserver() {
|
|
if( observer == null ) {
|
|
return getCamera();
|
|
}
|
|
return observer;
|
|
}
|
|
|
|
/**
|
|
* Set the scene observer. The VR headset will be linked to it. If no observer is set, the VR headset is linked to the the application {@link #getCamera() camera}.
|
|
* @param observer the scene observer.
|
|
*/
|
|
public void setObserver(Spatial observer) {
|
|
this.observer = observer;
|
|
}
|
|
|
|
/*
|
|
where is the headset pointing, after all rotations are combined?
|
|
depends on observer rotation, if any
|
|
*/
|
|
private static Quaternion tempq = new Quaternion();
|
|
|
|
/**
|
|
* Get the observer final rotation within the scene.
|
|
* @return the observer final rotation within the scene.
|
|
* @see #getFinalObserverPosition()
|
|
*/
|
|
public Quaternion getFinalObserverRotation() {
|
|
if( viewmanager == null ) {
|
|
if( observer == null ) {
|
|
return getCamera().getRotation();
|
|
} else return observer.getWorldRotation();
|
|
}
|
|
if( observer == null ) {
|
|
tempq.set(dummyCam.getRotation());
|
|
} else {
|
|
tempq.set(observer.getWorldRotation());
|
|
}
|
|
return tempq.multLocal(VRhardware.getOrientation());
|
|
}
|
|
|
|
/**
|
|
* Get the observer final position within the scene.
|
|
* @return the observer position.
|
|
* @see #getFinalObserverRotation()
|
|
*/
|
|
public Vector3f getFinalObserverPosition() {
|
|
if( viewmanager == null ) {
|
|
if( observer == null ) {
|
|
return getCamera().getLocation();
|
|
} else return observer.getWorldTranslation();
|
|
}
|
|
Vector3f pos = VRhardware.getPosition();
|
|
if( observer == null ) {
|
|
dummyCam.getRotation().mult(pos, pos);
|
|
return pos.addLocal(dummyCam.getLocation());
|
|
} else {
|
|
observer.getWorldRotation().mult(pos, pos);
|
|
return pos.addLocal(observer.getWorldTranslation());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the VR headset height from the ground.
|
|
* @param amount the VR headset height from the ground.
|
|
* @see #getVRHeightAdjustment()
|
|
*/
|
|
public void setVRHeightAdjustment(float amount) {
|
|
if( viewmanager != null ) viewmanager.setHeightAdjustment(amount);
|
|
}
|
|
|
|
/**
|
|
* Get the VR headset height from the ground.
|
|
* @return the VR headset height from the ground.
|
|
* @see #setVRHeightAdjustment(float)
|
|
*/
|
|
public float getVRHeightAdjustment() {
|
|
if( viewmanager != null ) return viewmanager.getHeightAdjustment();
|
|
return 0f;
|
|
}
|
|
|
|
/**
|
|
* Get the VR headset left viewport.
|
|
* @return the VR headset left viewport.
|
|
* @see #getRightViewPort()
|
|
*/
|
|
public ViewPort getLeftViewPort() {
|
|
if( viewmanager == null ) return getViewPort();
|
|
return viewmanager.getLeftViewport();
|
|
}
|
|
|
|
/**
|
|
* Get the VR headset right viewport.
|
|
* @return the VR headset right viewport.
|
|
* @see #getLeftViewPort()
|
|
*/
|
|
public ViewPort getRightViewPort() {
|
|
if( viewmanager == null ) return getViewPort();
|
|
return viewmanager.getRightViewport();
|
|
}
|
|
|
|
|
|
/**
|
|
* Set the background color for both left and right view ports.
|
|
* @param clr the background color.
|
|
*/
|
|
public void setBackgroundColors(ColorRGBA clr) {
|
|
if( viewmanager == null ) {
|
|
getViewPort().setBackgroundColor(clr);
|
|
} else if( viewmanager.getLeftViewport() != null ) {
|
|
viewmanager.getLeftViewport().setBackgroundColor(clr);
|
|
if( viewmanager.getRightViewport() != null ) viewmanager.getRightViewport().setBackgroundColor(clr);
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Runs tasks enqueued via {@link #enqueue(Callable)}
|
|
*/
|
|
protected void runQueuedTasks() {
|
|
AppTask<?> task;
|
|
while( (task = taskQueue.poll()) != null ) {
|
|
if (!task.isCancelled()) {
|
|
task.invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void update() {
|
|
// Make sure the audio renderer is available to callables
|
|
AudioContext.setAudioRenderer(audioRenderer);
|
|
|
|
runQueuedTasks();
|
|
|
|
if (speed != 0 && !paused) {
|
|
|
|
timer.update();
|
|
|
|
if (inputEnabled){
|
|
inputManager.update(timer.getTimePerFrame());
|
|
}
|
|
|
|
if (audioRenderer != null){
|
|
audioRenderer.update(timer.getTimePerFrame());
|
|
}
|
|
}
|
|
|
|
if (speed == 0 || paused) {
|
|
try {
|
|
Thread.sleep(50); // throttle the CPU when paused
|
|
} catch (InterruptedException ex) {
|
|
Logger.getLogger(SimpleApplication.class.getName()).log(Level.SEVERE, null, ex);
|
|
}
|
|
return;
|
|
}
|
|
|
|
float tpf = timer.getTimePerFrame() * speed;
|
|
|
|
// update states
|
|
stateManager.update(tpf);
|
|
|
|
// simple update and root node
|
|
simpleUpdate(tpf);
|
|
|
|
|
|
// render states
|
|
stateManager.render(renderManager);
|
|
|
|
// update VR pose & cameras
|
|
if( viewmanager != null ) {
|
|
viewmanager.update(tpf);
|
|
} else if( observer != null ) {
|
|
getCamera().setFrame(observer.getWorldTranslation(), observer.getWorldRotation());
|
|
}
|
|
|
|
//FIXME: check if this code is necessary.
|
|
// Updates scene and gui states.
|
|
rootNode.updateLogicalState(tpf);
|
|
guiNode.updateLogicalState(tpf);
|
|
|
|
rootNode.updateGeometricState();
|
|
|
|
if( isInVR() == false || guiManager.getPositioningMode() == POSITIONING_MODE.MANUAL ) {
|
|
// only update geometric state here if GUI is in manual mode, or not in VR
|
|
// it will get updated automatically in the viewmanager update otherwise
|
|
guiNode.updateGeometricState();
|
|
}
|
|
|
|
renderManager.render(tpf, context.isRenderable());
|
|
simpleRender(renderManager);
|
|
stateManager.postRender();
|
|
|
|
// update compositor?
|
|
if( viewmanager != null ) {
|
|
viewmanager.sendTextures();
|
|
}
|
|
}
|
|
|
|
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);
|
|
logger.config("Created asset manager from "+assetCfgUrl);
|
|
}
|
|
}
|
|
|
|
|
|
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() != JmeContext.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());
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
private void initialize_internal(){
|
|
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..
|
|
}
|
|
|
|
@Override
|
|
public void initialize() {
|
|
|
|
logger.config("Initialize VR application...");
|
|
|
|
initialize_internal();
|
|
cam.setFrustumFar(fFar);
|
|
cam.setFrustumNear(fNear);
|
|
dummyCam = cam.clone();
|
|
if( isInVR() ) {
|
|
|
|
logger.config("VR mode enabled.");
|
|
|
|
if( VRhardware != null ) {
|
|
VRhardware.initVRCompositor(compositorAllowed());
|
|
} else {
|
|
logger.warning("No VR system found.");
|
|
}
|
|
|
|
//FIXME: WARNING !!
|
|
viewmanager = new VRViewManager();
|
|
viewmanager.setResolutionMultiplier(resMult);
|
|
inputManager.addMapping(RESET_HMD, new KeyTrigger(KeyInput.KEY_F9));
|
|
setLostFocusBehavior(LostFocusBehavior.Disabled);
|
|
} else {
|
|
logger.config("VR mode disabled.");
|
|
viewPort.attachScene(rootNode);
|
|
guiViewPort.attachScene(guiNode);
|
|
}
|
|
|
|
if( viewmanager != null ) {
|
|
viewmanager.initialize();
|
|
}
|
|
|
|
simpleInitApp();
|
|
|
|
// any filters created, move them now
|
|
if( viewmanager != null ) {
|
|
viewmanager.moveScreenProcessingToEyes();
|
|
|
|
// print out camera information
|
|
if( isInVR() ) {
|
|
logger.info("VR Initialization Information");
|
|
if( viewmanager.getLeftCamera() != null ){
|
|
logger.info("camLeft: " + viewmanager.getLeftCamera().toString());
|
|
}
|
|
|
|
if( viewmanager.getRightCamera() != null ){
|
|
logger.info("camRight: " + viewmanager.getRightCamera().toString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Initialize the application. This method has to be overridden by implementations.
|
|
*/
|
|
public abstract void simpleInitApp();
|
|
|
|
/**
|
|
* Destroy the application (release all resources).
|
|
*/
|
|
public void destroy() {
|
|
if( VRhardware != null ) {
|
|
VRhardware.destroy();
|
|
VRhardware = null;
|
|
}
|
|
DISABLE_VR = true;
|
|
stateManager.cleanup();
|
|
|
|
destroyInput();
|
|
if (audioRenderer != null)
|
|
audioRenderer.cleanup();
|
|
|
|
timer.reset();
|
|
Runtime.getRuntime().exit(0);
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
@Override
|
|
public ViewPort getGuiViewPort() {
|
|
return guiViewPort;
|
|
}
|
|
|
|
@Override
|
|
public ViewPort getViewPort() {
|
|
return viewPort;
|
|
}
|
|
|
|
@Override
|
|
public <V> Future<V> enqueue(Callable<V> callable) {
|
|
AppTask<V> task = new AppTask<V>(callable);
|
|
taskQueue.add(task);
|
|
return task;
|
|
}
|
|
|
|
/**
|
|
* Enqueues a runnable object to execute in the jME3
|
|
* rendering thread.
|
|
* <p>
|
|
* 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));
|
|
}
|
|
|
|
private class RunnableWrapper implements Callable<Object>{
|
|
private final Runnable runnable;
|
|
|
|
public RunnableWrapper(Runnable runnable){
|
|
this.runnable = runnable;
|
|
}
|
|
|
|
@Override
|
|
public Object call(){
|
|
runnable.run();
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Requests the context to close, shutting down the main loop
|
|
* and making necessary cleanup operations.
|
|
*
|
|
* Same as calling stop(false)
|
|
*
|
|
* @see #stop(boolean)
|
|
*/
|
|
@Override
|
|
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.
|
|
*/
|
|
@Override
|
|
public void stop(boolean waitFor){
|
|
logger.log(Level.FINE, "Closing application: {0}", getClass().getName());
|
|
context.destroy(waitFor);
|
|
}
|
|
|
|
/**
|
|
* 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.
|
|
*/
|
|
@Override
|
|
public void restart(){
|
|
context.setSettings(settings);
|
|
context.restart();
|
|
}
|
|
|
|
/**
|
|
* 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) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Returns the current AppProfiler hook, or null if none is set.
|
|
*/
|
|
public AppProfiler getAppProfiler() {
|
|
return null;
|
|
}
|
|
} |