Răsfoiți Sursa

Creating a VRAppState

Julien Seinturier 8 ani în urmă
părinte
comite
41bead60e3

+ 89 - 32
jme3-core/src/main/java/com/jme3/scene/Spatial.java

@@ -170,6 +170,60 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      */
     protected transient int refreshFlags = 0;
 
+    public void refreshFlagOr(int flag){
+    	refreshFlags |= flag;
+    	//logger.warning("Changing refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
+    }
+    
+    public void refreshFlagAnd(int flag){
+    	refreshFlags &= flag;
+    	//logger.warning("Changing refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
+    }
+    
+    public int refreshFlagGetAnd(int flag){
+    	return (refreshFlags & flag);
+    }
+    
+    public int getRefreshFlags(){
+    	return refreshFlags;
+    }
+    
+    public String getRefreshFlagsDescription(){
+    	String str = "";
+    	
+    	if (refreshFlags == 0){
+    		str += "OK";
+    	} else {
+    		
+    		// need light resort + combine transforms
+    		if ((refreshFlags & RF_TRANSFORM) == RF_TRANSFORM){
+    		  str += "RF_TRANSFORM ";
+    		}
+    		
+    		// need light resort + combine transforms
+    		if ((refreshFlags & RF_BOUND) == RF_BOUND){
+    		  str += "RF_BOUND ";
+    		}
+
+    		// changes in light lists 
+    		if ((refreshFlags & RF_LIGHTLIST) == RF_LIGHTLIST){
+    		  str += "RF_LIGHTLIST ";
+    		}
+    		
+    		// some child need geometry update
+    		if ((refreshFlags & RF_CHILD_LIGHTLIST) == RF_CHILD_LIGHTLIST){
+    		  str += "RF_CHILD_LIGHTLIST ";
+    		}
+    		
+    		// some child need geometry update
+    		if ((refreshFlags & RF_MATPARAM_OVERRIDE) == RF_MATPARAM_OVERRIDE){
+    		  str += "RF_MATPARAM_OVERRIDE ";
+    		}
+    	}
+    	
+    	return str;
+    }
+    
     /**
      * Set to true if a subclass requires updateLogicalState() even
      * if it doesn't have any controls.  Defaults to true thus implementing
@@ -209,7 +263,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
 
         localOverrides = new SafeArrayList<>(MatParamOverride.class);
         worldOverrides = new SafeArrayList<>(MatParamOverride.class);
-        refreshFlags |= RF_BOUND;
+        refreshFlagOr(RF_BOUND);
     }
 
     public void setKey(AssetKey key) {
@@ -272,35 +326,35 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      * a refresh is required.
      */
     protected void setTransformRefresh() {
-        refreshFlags |= RF_TRANSFORM;
+    	refreshFlagOr(RF_TRANSFORM);
         setBoundRefresh();
     }
 
     protected void setLightListRefresh() {
-        refreshFlags |= RF_LIGHTLIST;
+    	refreshFlagOr(RF_LIGHTLIST);
         // Make sure next updateGeometricState() visits this branch
         // to update lights.
         Spatial p = parent;
         while (p != null) {
-            if ((p.refreshFlags & RF_CHILD_LIGHTLIST) != 0) {
+            if ((p.refreshFlagGetAnd(RF_CHILD_LIGHTLIST)) != 0) {
                 // The parent already has this flag,
                 // so must all ancestors.
                 return;
             }
-            p.refreshFlags |= RF_CHILD_LIGHTLIST;
+            p.refreshFlagOr(RF_CHILD_LIGHTLIST);
             p = p.parent;
         }
     }
 
     protected void setMatParamOverrideRefresh() {
-        refreshFlags |= RF_MATPARAM_OVERRIDE;
+    	refreshFlagOr(RF_MATPARAM_OVERRIDE);
         Spatial p = parent;
         while (p != null) {
-            if ((p.refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
+            if ((p.refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) != 0) {
                 return;
             }
 
-            p.refreshFlags |= RF_MATPARAM_OVERRIDE;
+            p.refreshFlagOr(RF_MATPARAM_OVERRIDE);
             p = p.parent;
         }
     }
@@ -310,15 +364,15 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      * a refresh is required.
      */
     protected void setBoundRefresh() {
-        refreshFlags |= RF_BOUND;
+    	refreshFlagOr(RF_BOUND);
 
         Spatial p = parent;
         while (p != null) {
-            if ((p.refreshFlags & RF_BOUND) != 0) {
+            if ((p.refreshFlagGetAnd(RF_BOUND)) != 0) {
                 return;
             }
 
-            p.refreshFlags |= RF_BOUND;
+            p.refreshFlagOr(RF_BOUND);
             p = p.parent;
         }
     }
@@ -353,11 +407,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      * (should be rendered), false if outside.
      */
     public boolean checkCulling(Camera cam) {
-        if (refreshFlags != 0) {
+        if (getRefreshFlags() != 0) {
+        	/*
             throw new IllegalStateException("Scene graph is not properly updated for rendering.\n"
                     + "State was changed after rootNode.updateGeometricState() call. \n"
                     + "Make sure you do not modify the scene from another thread!\n"
-                    + "Problem spatial name: " + getName());
+                    + "Problem spatial name: " + getName()+", flags: "+getRefreshFlagsDescription());
+                    */
+        	logger.warning("Invalid refresh flags for spatial " + getName()+", flags: "+getRefreshFlagsDescription());
         }
 
         CullHint cm = getCullHint();
@@ -571,28 +628,28 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
         // for a node, the world bound is a combination of all it's children
         // bounds
         // -> handled by subclass
-        refreshFlags &= ~RF_BOUND;
+    	refreshFlagAnd(~RF_BOUND);
     }
 
     protected void updateWorldLightList() {
         if (parent == null) {
             worldLights.update(localLights, null);
-            refreshFlags &= ~RF_LIGHTLIST;
+            refreshFlagAnd(~RF_LIGHTLIST);
         } else {
-            assert (parent.refreshFlags & RF_LIGHTLIST) == 0;
+            assert (parent.refreshFlagGetAnd(RF_LIGHTLIST)) == 0;
             worldLights.update(localLights, parent.worldLights);
-            refreshFlags &= ~RF_LIGHTLIST;
+            refreshFlagAnd(~RF_LIGHTLIST);
         }
     }
 
     protected void updateMatParamOverrides() {
-        refreshFlags &= ~RF_MATPARAM_OVERRIDE;
+    	refreshFlagAnd(~RF_MATPARAM_OVERRIDE);
 
         worldOverrides.clear();
         if (parent == null) {
             worldOverrides.addAll(localOverrides);
         } else {
-            assert (parent.refreshFlags & RF_MATPARAM_OVERRIDE) == 0;
+            assert (parent.refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) == 0;
             worldOverrides.addAll(parent.worldOverrides);
             worldOverrides.addAll(localOverrides);
         }
@@ -643,13 +700,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
     protected void updateWorldTransforms() {
         if (parent == null) {
             worldTransform.set(localTransform);
-            refreshFlags &= ~RF_TRANSFORM;
+            refreshFlagAnd(~RF_TRANSFORM);
         } else {
             // check if transform for parent is updated
-            assert ((parent.refreshFlags & RF_TRANSFORM) == 0);
+            assert ((parent.refreshFlagGetAnd(RF_TRANSFORM)) == 0);
             worldTransform.set(localTransform);
             worldTransform.combineWithParent(parent.worldTransform);
-            refreshFlags &= ~RF_TRANSFORM;
+            refreshFlagAnd(~RF_TRANSFORM);
         }
     }
 
@@ -658,13 +715,13 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      * efficient manner possible.
      */
     void checkDoTransformUpdate() {
-        if ((refreshFlags & RF_TRANSFORM) == 0) {
+        if ((refreshFlagGetAnd(RF_TRANSFORM)) == 0) {
             return;
         }
 
         if (parent == null) {
             worldTransform.set(localTransform);
-            refreshFlags &= ~RF_TRANSFORM;
+            refreshFlagAnd(~RF_TRANSFORM);
         } else {
             TempVars vars = TempVars.get();
 
@@ -675,14 +732,14 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
                 Spatial hisParent = rootNode.parent;
                 if (hisParent == null) {
                     rootNode.worldTransform.set(rootNode.localTransform);
-                    rootNode.refreshFlags &= ~RF_TRANSFORM;
+                    rootNode.refreshFlagAnd(~RF_TRANSFORM);
                     i--;
                     break;
                 }
 
                 stack[i] = rootNode;
 
-                if ((hisParent.refreshFlags & RF_TRANSFORM) == 0) {
+                if ((hisParent.refreshFlagGetAnd(RF_TRANSFORM)) == 0) {
                     break;
                 }
 
@@ -707,7 +764,7 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
      * manner possible.
      */
     void checkDoBoundUpdate() {
-        if ((refreshFlags & RF_BOUND) == 0) {
+        if ((refreshFlagGetAnd(RF_BOUND)) == 0) {
             return;
         }
 
@@ -897,20 +954,20 @@ public abstract class Spatial implements Savable, Cloneable, Collidable, Cloneab
 
         // NOTE: Update world transforms first because
         // bound transform depends on them.
-        if ((refreshFlags & RF_LIGHTLIST) != 0) {
+        if ((refreshFlagGetAnd(RF_LIGHTLIST)) != 0) {
             updateWorldLightList();
         }
-        if ((refreshFlags & RF_TRANSFORM) != 0) {
+        if ((refreshFlagGetAnd(RF_TRANSFORM)) != 0) {
             updateWorldTransforms();
         }
-        if ((refreshFlags & RF_BOUND) != 0) {
+        if ((refreshFlagGetAnd(RF_BOUND)) != 0) {
             updateWorldBound();
         }
-        if ((refreshFlags & RF_MATPARAM_OVERRIDE) != 0) {
+        if ((refreshFlagGetAnd(RF_MATPARAM_OVERRIDE)) != 0) {
             updateMatParamOverrides();
         }
         
-        assert refreshFlags == 0;
+        assert getRefreshFlags() == 0;
     }
 
     /**

+ 843 - 0
jme3-vr/src/main/java/com/jme3/app/VRAppState.java

@@ -0,0 +1,843 @@
+package com.jme3.app;
+
+
+import com.jme3.app.Application;
+import com.jme3.app.state.AbstractAppState;
+import com.jme3.app.state.AppStateManager;
+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.renderer.Camera;
+import com.jme3.renderer.RenderManager;
+import com.jme3.renderer.ViewPort;
+import com.jme3.scene.Spatial;
+import com.jme3.system.AppSettings;
+import com.jme3.system.jopenvr.JOpenVRLibrary;
+
+import java.awt.GraphicsDevice;
+import java.awt.GraphicsEnvironment;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import jmevr.util.VRGuiManager;
+import jmevr.util.VRMouseManager;
+import jmevr.util.VRViewManager;
+import jmevr.util.VRGuiManager.POSITIONING_MODE;
+
+/**
+ * A JMonkey app state dedicated to Virtual Reality. 
+ * An application that want to use VR devices (HTC vive, ...) has to use this app state.<br>
+ * As this app state and the main {@link Application application} have to share {@link AppSettings application settings}, 
+ * the common way to use this app state is:<br>
+ * <ul>
+ * <li>To create {@link AppSettings application settings} and set the VR related settings (see {@link VRConstants}).
+ * <li>To instantiate this app state with the created settings.
+ * <li>To instantiate the main {@link Application application} and to attach it to the created settings (with {@link Application#setSettings(AppSettings) setSettings(AppSettings)}).
+ * <li>To start the main {@link Application application}.
+ * </ul>
+ * Attaching an instance of this app state to an already started application may cause crashes.
+ * @author Julien Seinturier - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
+ */
+public class VRAppState extends AbstractAppState {
+
+    private static final Logger logger = Logger.getLogger(VRAppState.class.getName());
+    
+    /**
+     * The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}.
+     */
+    public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE;
+    
+    /**
+     * Is the application has not to start within VR mode (default is <code>false</code>).
+     */
+    public boolean DISABLE_VR = false;
+    
+    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 = null;
+    
+    private boolean VRSupportedOS;
+    private boolean forceVR            = false;;
+    private boolean disableSwapBuffers = true;
+    private boolean disableVR          = false;
+    private boolean seated;
+    private boolean nogui;
+    private boolean instanceVR         = false;
+
+	private float defaultFOV           = 108f;
+    private float defaultAspect        = 1f;
+    
+   
+    
+    private float fFar  = 1000f;
+    private float fNear = 0.1f;
+    private int xWin    = 1920;
+    private int yWin    = 1080;
+    
+    private float resMult = 1f;
+    
+    private boolean useCompositor = true;
+    private boolean compositorOS;
+    
+    /*
+     where is the headset pointing, after all rotations are combined?
+     depends on observer rotation, if any
+     */
+    private Quaternion tempq = new Quaternion();
+    
+    private Application application      = null;
+    private AppStateManager stateManager = null;
+    private AppSettings settings         = null;
+    
+    
+    /**
+     * Create a new default VR app state.
+     */
+    public VRAppState() {
+        super();
+
+        dummyCam = new Camera();
+        
+        // Create the GUI manager.
+        guiManager = new VRGuiManager();
+        
+        // Create a new view manager.
+        viewmanager = new VRViewManager();
+
+        // Create a new mouse manager.
+        mouseManager = new VRMouseManager();
+      
+     }
+    
+    /**
+     * Create a new VR app state with given settings. 
+     * @param settings the settings to use.
+     */
+    public VRAppState(AppSettings settings){
+      this();
+      this.settings = settings;
+      processSettings(settings);
+    }
+
+    
+    /**
+     * Simple update of the app state, this method should contains any spatial updates.
+     * This method is called by the {@link #update(float) update()} method and should not be called manually.
+     * @param tpf the application time.
+     */
+    public void simpleUpdate(float tpf) {
+    	return;
+    }
+    
+    /**
+     * Rendering callback of the app state. This method is called by the {@link #update(float) update()} method and should not be called manually.
+     * @param renderManager the {@link RenderManager render manager}.
+     */
+    public void simpleRender(RenderManager renderManager) {
+        PreNormalCaching.resetCache(isInVR());
+    }
+
+    /**
+     * 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 VR compositor is active.
+     * @return <code>true</code> if the VR 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 isVRSupported() {
+        return VRSupportedOS;
+    }
+    
+    /**
+     * Get the {@link Camera camera} attached to this application state. 
+     * If the VR mode is {@link #isInVR() active}, this method return a dummy camera, otherwise, 
+     * this method return the camera of the attached application.
+     * @return the camera attached to this application state.
+     */
+    public Camera getCamera() {
+        if( isInVR() && viewmanager != null && viewmanager.getLeftCamera() != null ) {
+            return dummyCam;
+        }
+        
+        return application.getCamera();
+    }
+
+    /**
+     * 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 default Field Of View (FOV) value.
+     * @return the default Field Of View (FOV) value.
+     * @see #setDefaultFOV(float)
+     */
+    public float getDefaultFOV() {
+		return defaultFOV;
+	}
+
+    /**
+     * Set the default Field Of View (FOV) value.
+     * @param defaultFOV the default Field Of View (FOV) value.
+     * @see #getDefaultFOV()
+     */
+	public void setDefaultFOV(float defaultFOV) {
+		this.defaultFOV = defaultFOV;
+	}
+
+	/**
+	 * Get the default aspect ratio.
+	 * @return the default aspect ratio.
+	 * @see #setDefaultAspect(float)
+	 */
+	public float getDefaultAspect() {
+		return defaultAspect;
+	}
+
+	/**
+	 * Set the default aspect ratio.
+	 * @param defaultAspect the default aspect ratio.
+	 * @see #getDefaultAspect()
+	 */
+	public void setDefaultAspect(float defaultAspect) {
+		this.defaultAspect = defaultAspect;
+	}
+	
+    /**
+     * Move filters from the main scene into the eye's.
+     * This removes filters from the main scene.
+     */
+    public void moveScreenProcessingToVR() {
+        if( isInVR() ) {
+        	viewmanager.moveScreenProcessingToEyes();
+        }
+    }
+ 
+    /**
+     * 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;
+    }
+    
+    /**
+     * 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 application.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 application.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 ) {
+            application.getViewPort().setBackgroundColor(clr);
+        } else if( viewmanager.getLeftViewport() != null ) {
+        	viewmanager.getLeftViewport().setBackgroundColor(clr);
+            if( viewmanager.getRightViewport() != null ) viewmanager.getRightViewport().setBackgroundColor(clr);
+        }
+    }
+    
+    /**
+     * Get the {@link Application} to which this app state is attached.
+     * @return the {@link Application} to which this app state is attached.
+     * @see #getStateManager()
+     */
+    public Application getApplication(){
+    	return application;
+    }
+    
+    /**
+     * Get the {@link AppStateManager state manager} to which this app state is attached.
+     * @return the {@link AppStateManager state manager} to which this app state is attached.
+     * @see #getApplication()
+     */
+    public AppStateManager getStateManager(){
+    	return stateManager;
+    }
+    
+	/**
+	 * 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;
+	}
+    
+	/**
+	 * Get the {@link AppSettings settings} attached to this app state.
+	 * @return the {@link AppSettings settings} attached to this app state.
+	 * @see #setSettings(AppSettings)
+	 */
+	public AppSettings getSettings(){
+		return settings;
+	}
+	
+	/**
+	 * Set the {@link AppSettings settings} attached to this app state.
+	 * @param settings the {@link AppSettings settings} attached to this app state.
+	 * @see #getSettings()
+	 */
+	public void setSettings(AppSettings settings){
+		this.settings = settings;
+		processSettings(settings);
+	}
+	
+    @Override
+    public void update(float tpf) {    
+        
+        // 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.
+        Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator();
+        Spatial spatial = null;
+        while(spatialIter.hasNext()){
+        	spatial = spatialIter.next();
+        	spatial.updateLogicalState(tpf);
+        	spatial.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
+        	spatialIter = application.getGuiViewPort().getScenes().iterator();
+            spatial = null;
+            while(spatialIter.hasNext()){
+            	spatial = spatialIter.next();
+            	spatial.updateGeometricState();
+            }    
+        }
+        
+        
+        // use the analog control on the first tracked controller to push around the mouse
+        getVRMouseManager().updateAnalogAsMouse(0, null, null, null, tpf);
+    }
+
+    @Override
+    public void postRender() {
+        super.postRender();
+        // update compositor?
+        if( viewmanager != null ) {
+        	viewmanager.sendTextures();
+        }
+    }
+
+    @Override
+    public void initialize(AppStateManager stateManager, Application app) {
+        super.initialize(stateManager, app);
+        
+        this.application  = app;
+        this.stateManager = stateManager;
+        
+        // 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);     
+        
+        // VR module attch
+        guiManager.attach(this, app);
+        viewmanager.attach(this, app);
+        mouseManager.attach(this,  app);
+        
+        app.getCamera().setFrustumFar(fFar);
+        app.getCamera().setFrustumNear(fNear);
+        dummyCam = app.getCamera().clone();
+        
+        if( isInVR() ) {
+        	
+        	logger.config("VR mode enabled.");
+        	
+            if( VRhardware != null ) {
+                VRhardware.initVRCompositor(compositorAllowed());
+            } else {
+            	logger.warning("No VR system found.");
+            }
+            
+            
+            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();
+        }
+    }
+    
+    @Override
+    public void stateAttached(AppStateManager stateManager) {
+        super.stateAttached(stateManager); //To change body of generated methods, choose Tools | Templates.
+        
+        if (settings == null) {
+            settings = new AppSettings(true);
+            logger.config("Using default settings.");
+        } else {
+        	logger.config("Using given settings.");
+        }
+        
+        // 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 && disableVR == false ) {
+        	if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) {
+                VRhardware = new OSVR(this);
+                logger.config("Creating OSVR wrapper [SUCCESS]");
+            } else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) {
+                VRhardware = new OpenVR(this);
+                logger.config("Creating OpenVR wrapper [SUCCESS]");
+            } else {
+            	logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]");
+            }
+        	
+            if( VRhardware.initialize() ) {
+            	logger.config("VR native wrapper initialized [SUCCESS]");
+            } else {
+            	logger.warning("VR native wrapper initialized [FAILED]");
+            }
+            }
+        
+        GraphicsDevice defDev = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
+                                    
+        if( isInVR() && !compositorAllowed() ) {
+            // "easy extended" mode
+            // setup experimental JFrame on external 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
+                    stateManager.getApplication().setSettings(settings);
+                    logger.config("Updated underlying application settings.");
+                    
+                    //VRdev.setFullScreenWindow(VRwindow);
+                    // make sure we are in the right display mode
+                    if( VRdev.getDisplayMode().equals(useDM) == false ) {
+                        VRdev.setDisplayMode(useDM);
+                    }
+                    
+                    return;
+                } catch(Exception e) { 
+                    logger.log(Level.SEVERE, e.getMessage(), e);
+                }
+            } else {
+            	logger.config("Cannot access to external screen.");
+            }
+        } else {
+        	if (!isInVR()){
+        	  logger.config("Cannot switch to VR mode (VR disabled by user).");
+        	} else if (!compositorAllowed()){
+        	  logger.warning("Cannot switch to VR mode (VR not supported).");
+        	}
+        }
+        
+        if( !isInVR() ) {
+        	
+        	//FIXME: Handling GLFW workaround on MacOS
+        	boolean macOs = false;
+            if (macOs) {
+                // 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 {
+            // use basic mirroring window, skip settings window
+            settings.setSamples(1);
+            settings.setWidth(xWin);
+            settings.setHeight(yWin);
+            settings.setBitsPerPixel(32);     
+            settings.setFrameRate(0);
+            settings.setFrequency(VRhardware.getDisplayFrequency());
+            settings.setFullscreen(false);
+            settings.setVSync(false); // stop vsyncing on primary monitor!
+            settings.setSwapBuffers(disableSwapBuffers);
+        }
+
+        // Updating application settings
+        stateManager.getApplication().setSettings(settings);
+        logger.config("Updated underlying application settings.");
+        
+    }
+
+    @Override
+    public void cleanup() {
+        if( VRhardware != null ) {
+            VRhardware.destroy();
+            VRhardware = null;
+        }        
+        disableVR = true;
+        
+        this.application  = null;
+        this.stateManager = null;
+    }
+    
+    @Override
+    public void stateDetached(AppStateManager stateManager) {
+      super.stateDetached(stateManager);
+    }
+    
+    /**
+     * Process the attached settings and apply changes to this app state.
+     * @param settings the app settings to process.
+     */
+    protected void processSettings(AppSettings settings){
+    	if (settings != null){
+    		if (settings.get(VRConstants.SETTING_USE_COMPOSITOR) != null){
+    			useCompositor = settings.getBoolean(VRConstants.SETTING_USE_COMPOSITOR);
+                if( useCompositor == false ){
+                	disableSwapBuffers = false;
+                }
+    		}
+
+            if (settings.get(VRConstants.SETTING_VR_FORCE) != null){
+            	forceVR = settings.getBoolean(VRConstants.SETTING_VR_FORCE);
+    		}
+    		
+            if (settings.get(VRConstants.SETTING_FLIP_EYES) != null){
+                if( VRhardware != null ){
+                	VRhardware._setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES));
+                } 
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){
+            	guiManager._enableGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW));
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){
+            	guiManager._enableCurvedSuface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE));
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_ENABLE_MIRROR_WINDOW) != null){
+                if( useCompositor == false ) {
+                    disableSwapBuffers = false;
+                } else {
+                	disableSwapBuffers = !settings.getBoolean(VRConstants.SETTING_ENABLE_MIRROR_WINDOW);
+                }
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_DISABLE_VR) != null){
+                DISABLE_VR = settings.getBoolean(VRConstants.SETTING_DISABLE_VR);
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){
+            	seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE);
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_NO_GUI) != null){
+            	nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI);
+    		}
+    	    
+            if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){
+            	instanceVR = settings.getBoolean(VRConstants.SETTING_INSTANCE_RENDERING);
+    		}
+
+            if (settings.get(VRConstants.SETTING_DEFAULT_FOV) != null){
+            	defaultFOV = settings.getFloat(VRConstants.SETTING_DEFAULT_FOV);
+    		}
+            
+            if (settings.get(VRConstants.SETTING_DEFAULT_ASPECT_RATIO) != null){
+            	defaultAspect = settings.getFloat(VRConstants.SETTING_DEFAULT_ASPECT_RATIO);
+    		}
+            
+            if (settings.get(VRConstants.SETTING_VRAPI) != null){
+            	vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI);
+    		}
+            
+    	}
+    }
+}

+ 19 - 15
jme3-vr/src/main/java/com/jme3/app/VRApplication.java

@@ -255,13 +255,13 @@ public abstract class VRApplication implements Application, SystemListener {
         initStateManager();
         
         // Create the GUI manager.
-        guiManager = new VRGuiManager(this);
+        guiManager = new VRGuiManager();
         
         // Create a new view manager.
-        viewmanager = new VRViewManager(this);
+        viewmanager = new VRViewManager();
         
         // Create a new mouse manager.
-        mouseManager = new VRMouseManager(this);
+        mouseManager = new VRMouseManager();
         
         // we are going to use OpenVR now, not the Oculus Rift
         // OpenVR does support the Rift
@@ -275,10 +275,12 @@ public abstract class VRApplication implements Application, SystemListener {
         	logger.warning("VR disabled via code.");
         } else if( VRSupportedOS && DISABLE_VR == false ) {
             if( CONSTRUCT_WITH_OSVR ) {
-                VRhardware = new OSVR(this);
+            	//FIXME: WARNING !!
+                VRhardware = new OSVR(null);
                 logger.config("Creating OSVR wrapper [SUCCESS]");
             } else {
-                VRhardware = new OpenVR(this);
+            	//FIXME: WARNING !!
+                VRhardware = new OpenVR(null);
                 logger.config("Creating OpenVR wrapper [SUCCESS]");
             }
             if( VRhardware.initialize() ) {
@@ -820,6 +822,16 @@ public abstract class VRApplication implements Application, SystemListener {
         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
@@ -924,15 +936,6 @@ public abstract class VRApplication implements Application, SystemListener {
         return DISABLE_VR == false && (forceVR || VRSupportedOS && VRhardware != null && VRhardware.isInitialized());
     }  
 
-    /**
-     * Move filters from the main scene into the eye's.
-     * This removes filters from the main scene.
-     */
-    public void moveScreenProcessingToVR() {
-        if( isInVR() ) {
-        	viewmanager.moveScreenProcessingToEyes();
-        }
-    }
     
     /**
      * Get the GUI node from the application.
@@ -1354,7 +1357,8 @@ public abstract class VRApplication implements Application, SystemListener {
             	logger.warning("No VR system found.");
             }
             
-            viewmanager = new VRViewManager(this);
+            //FIXME: WARNING !!
+            viewmanager = new VRViewManager();
             viewmanager.setResolutionMultiplier(resMult);
             inputManager.addMapping(RESET_HMD, new KeyTrigger(KeyInput.KEY_F9));
             setLostFocusBehavior(LostFocusBehavior.Disabled);

+ 147 - 0
jme3-vr/src/main/java/com/jme3/app/VRConstants.java

@@ -0,0 +1,147 @@
+package com.jme3.app;
+
+import java.util.HashMap;
+
+import com.jme3.system.AppSettings;
+
+/**
+ * Some constants dedicated to the VR module.
+ * @author Julien Seinturier - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
+ * @since 3.1.0
+ */
+public class VRConstants {
+	
+	/**
+	 * An AppSettings parameter that set if the VR compositor has to be used.
+	 * <p>
+	 * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_USE_COMPOSITOR, value)</code>
+	 */
+    public static final String SETTING_USE_COMPOSITOR = "VRUseCompositor";
+    
+    /**
+     * An AppSettings parameter that set if the rendering has to use two eyes, 
+     * regardless of VR API detection (turning this setting on without a VR system should lead to errors).
+     * <p>
+	 * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VR_FORCE, value)</code>
+
+     */
+    public static final String SETTING_VR_FORCE = "VRForce";
+    
+    /**
+     * An AppSettings parameter that set to invert the eyes of the HMD.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_FLIP_EYES, value)</code>
+     */
+    public static final String SETTING_FLIP_EYES = "VRFlipEyes";
+    
+    /**
+     * An AppSettings parameter that set if the GUI has to be displayed even if it is behind objects.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_OVERDRAW, value)</code>
+     * 
+     */
+    public static final String SETTING_GUI_OVERDRAW = "VRGUIOverdraw";
+    
+    /**
+     * An AppSettings parameter that set if the GUI surface has to be curved.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_CURVED_SURFACE, value)</code>
+     */
+    public static final String SETTING_GUI_CURVED_SURFACE = "VRGUICurvedSurface";
+  
+    /**
+     * An AppSettings parameter that set if a mirror rendering has to be displayed on the screen. 
+     * Runs faster when set to <code>false</code>.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_ENABLE_MIRROR_WINDOW, value)</code>
+     */
+    public static final String SETTING_ENABLE_MIRROR_WINDOW = "VREnableMirrorWindow";
+    
+    /**
+     * An AppSettings parameter that set if the VR rendering has to be disabled, 
+     * regardless VR API and devices are presents.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_VR, value)</code>
+     */
+    public static final String SETTING_DISABLE_VR = "VRDisable";
+    
+    
+    /**
+     * An AppSettings parameter that set if the VR user is seated.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_SEATED_EXPERIENCE, value)</code>
+     */
+    public static final String SETTING_SEATED_EXPERIENCE = "VRSeatedExperience";
+    
+    /**
+     * An AppSettings parameter that set if the GUI has to be ignored.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_NO_GUI, value)</code>
+     */
+    public static final String SETTING_NO_GUI = "VRNoGUI";
+    
+    /**
+     * An AppSettings parameter that set if instance rendering has to be used. 
+     * This setting requires some vertex shader changes (see Common/MatDefs/VR/Unshaded.j3md).
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_INSTANCE_RENDERING, value)</code>
+     */
+    public static final String SETTING_INSTANCE_RENDERING = "VRInstanceRendering";
+    
+    /**
+     * An AppSettings parameter that set if Multi Sample Anti Aliasing has to be enabled.
+     * <b>Type: </b><code>boolean</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_MSAA, value)</code>
+     */
+    public static final String SETTING_DISABLE_MSAA = "VRDisableMSAA";
+    
+    /**
+     * An AppSettings parameter that set the default field of view (FOV) value.
+     * <b>Type: </b><code>float</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_FOV, value)</code>
+     */
+    public static final String SETTING_DEFAULT_FOV = "VRDefaultFOV";
+    
+    /**
+     * An AppSettings parameter that set the default aspect ratio.
+     * <b>Type: </b><code>float</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_ASPECT_RATIO, value)</code>
+     */
+    public static final String SETTING_DEFAULT_ASPECT_RATIO = "VRDefaultAspectRatio";
+    
+    /**
+     * An AppSettings parameter that specifies the underlying VR API. Possible values are:<br>
+     * <ul>
+     * <li>{@link VRConstants#SETTING_VRAPI_OPENVR_VALUE SETTING_VRAPI_OPENVR_VALUE}: Use OpenVR binding.
+     * <li>{@link VRConstants#SETTING_VRAPI_OSVR_VALUE SETTING_VRAPI_OSVR_VALUE}: Use OSVR binding.
+     * <li>{@link VRConstants#SETTING_VRAPI_OPENVR_LWJGL_VALUE SETTING_VRAPI_OPENVR_LWJGL_VALUE}: Use OpenVR binding from LWJGL.
+     * </ul>
+     * <b>Type: </b><code>int</code><br>
+	 * <b>Usage: </b><code>{@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VRAPI, value)</code>
+
+     */
+    public static final String SETTING_VRAPI = "VRAPI";
+    
+    /**
+     * The identifier of the OpenVR system.
+     * @see #SETTING_VRAPI
+     */
+    public static final int SETTING_VRAPI_OPENVR_VALUE       = 1;
+    
+    /**
+     * The identifier of the OSVR system.
+     * @see #SETTING_VRAPI
+     */
+    public static final int SETTING_VRAPI_OSVR_VALUE         = 2;
+    
+    /**
+     * The identifier of the OpenVR from LWJGL system.
+     * @see #SETTING_VRAPI
+     */
+    public static final int SETTING_VRAPI_OPENVR_LWJGL_VALUE = 3;
+    
+    
+    
+}

+ 9 - 8
jme3-vr/src/main/java/com/jme3/input/vr/OSVR.java

@@ -9,6 +9,7 @@ https://github.com/sensics/OSVR-RenderManager/blob/master/examples/RenderManager
  */
 package com.jme3.input.vr;
 
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Quaternion;
@@ -110,14 +111,14 @@ public class OSVR implements VRAPI {
     boolean initSuccess = false;
     boolean flipEyes = false;
     
-    private VRApplication application = null;
+    private VRAppState app = null;
     
     /**
-     * Create a new <a href="http://www.osvr.org/">OSVR</a> system attached to the given application.
-     * @param application the application to which the input is attached.
+     * Create a new <a href="http://www.osvr.org/">OSVR</a> system attached to the given {@link VRAppState app state}.
+     * @param app the app state to which the input is attached.
      */
-    public OSVR(VRApplication application){
-    	this.application = application;
+    public OSVR(VRAppState app){
+    	this.app = app;
     }
     
     /**
@@ -149,7 +150,7 @@ public class OSVR implements VRAPI {
     	
         hmdPose.setAutoSynch(false);
         context = OsvrClientKitLibrary.osvrClientInit(defaultJString, 0);
-        VRinput = new OSVRInput(application);
+        VRinput = new OSVRInput(app);
         initSuccess = context != null && VRinput.init();
         if( initSuccess ) {
             PointerByReference grabDisplay = new PointerByReference();
@@ -462,8 +463,8 @@ public class OSVR implements VRAPI {
     }
 
 	@Override
-	public VRApplication getApplication() {
-		return application;
+	public VRAppState getVRAppState() {
+		return app;
 	}
 
 }

+ 13 - 12
jme3-vr/src/main/java/com/jme3/input/vr/OSVRInput.java

@@ -7,6 +7,7 @@ package com.jme3.input.vr;
 
 import java.util.logging.Logger;
 
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
@@ -63,7 +64,7 @@ public class OSVRInput implements VRInputAPI {
     private static final Vector2f lastCallAxis[] = new Vector2f[16];
     private static float axisMultiplier = 1f;
     
-    private VRApplication application = null;
+    private VRAppState app = null;
     
     /**
      * Get the system String that identifies a controller.
@@ -90,11 +91,11 @@ public class OSVRInput implements VRInputAPI {
 
     
     /**
-     * Create a new <a href="http://www.osvr.org/">OSVR</a> input attached to the given application.
-     * @param application the application to which the input is attached.
+     * Create a new <a href="http://www.osvr.org/">OSVR</a> input attached to the given {@link VRAppState app state}.
+     * @param app the app state to which the input is attached.
      */
-    public OSVRInput(VRApplication application){
-      this.application = application;
+    public OSVRInput(VRAppState app){
+      this.app = app;
     }
     
     
@@ -166,7 +167,7 @@ public class OSVRInput implements VRInputAPI {
     
     private OSVR_ClientInterface getInterface(byte[] str) {
         PointerByReference pbr = new PointerByReference();
-        OsvrClientKitLibrary.osvrClientGetInterface((OsvrClientKitLibrary.OSVR_ClientContext)application.getVRHardware().getVRSystem(), str, pbr);
+        OsvrClientKitLibrary.osvrClientGetInterface((OsvrClientKitLibrary.OSVR_ClientContext)app.getVRHardware().getVRSystem(), str, pbr);
         return new OSVR_ClientInterface(pbr.getValue());
     }
 
@@ -302,9 +303,9 @@ public class OSVRInput implements VRInputAPI {
 
     @Override
     public Quaternion getFinalObserverRotation(int index) {
-        VRViewManager vrvm = application.getVRViewManager();
+        VRViewManager vrvm = app.getVRViewManager();
         if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
-        Object obs = application.getObserver();
+        Object obs = app.getObserver();
         if( obs instanceof Camera ) {
             tempq.set(((Camera)obs).getRotation());
         } else {
@@ -315,9 +316,9 @@ public class OSVRInput implements VRInputAPI {
     
     @Override
     public Vector3f getFinalObserverPosition(int index) {
-        VRViewManager vrvm = application.getVRViewManager();
+        VRViewManager vrvm = app.getVRViewManager();
         if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
-        Object obs = application.getObserver();
+        Object obs = app.getObserver();
         Vector3f pos = getPosition(index);
         if( obs instanceof Camera ) {
             ((Camera)obs).getRotation().mult(pos, pos);
@@ -350,8 +351,8 @@ public class OSVRInput implements VRInputAPI {
 
 
 	@Override
-	public VRApplication getApplication() {
-		return application;
+	public VRAppState getVRAppState() {
+		return app;
 	}
 
 

+ 14 - 12
jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java

@@ -5,6 +5,7 @@
  */
 package com.jme3.input.vr;
 
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Quaternion;
@@ -83,14 +84,15 @@ public class OpenVR implements VRAPI {
     private static long frameCount;
     private static OpenVRInput VRinput;
     
-    private VRApplication application = null;
+    private VRAppState app = null;
     
     /**
-     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system attached to the given application.
-     * @param application the application to which the input is attached.
+     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system 
+     * attached to the given {@link VRAppState VR app state}.
+     * @param appState the VR app state to which the api is attached.
      */
-    public OpenVR(VRApplication application){
-      this.application = application;
+    public OpenVR(VRAppState appState){
+      this.app = appState;
     }
     
     @Override
@@ -178,7 +180,7 @@ public class OpenVR implements VRAPI {
             }
             
             // init controllers for the first time
-            VRinput = new OpenVRInput(application);
+            VRinput = new OpenVRInput(app);
             VRinput.init();
             VRinput.updateConnectedControllers();
             
@@ -204,7 +206,7 @@ public class OpenVR implements VRAPI {
                     if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){          
                         compositorFunctions.setAutoSynch(false);
                         compositorFunctions.read();
-                        if( application.isSeatedExperience() ) {                    
+                        if( app.isSeatedExperience() ) {                    
                             compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
                         } else {
                             compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);                
@@ -354,7 +356,7 @@ public class OpenVR implements VRAPI {
             frameCount = nowCount;
             
             vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
-                    application.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
+                    app.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
                                                        JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
                     fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);   
         }
@@ -371,7 +373,7 @@ public class OpenVR implements VRAPI {
             VRInput._updateConnectedControllers();
         }*/
         //update controllers pose information
-        application.getVRinput().updateControllerStates();
+        app.getVRinput().updateControllerStates();
                 
         // read pose data from native
         for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
@@ -445,7 +447,7 @@ public class OpenVR implements VRAPI {
     
     @Override
     public Vector3f getSeatedToAbsolutePosition() {
-        if( application.isSeatedExperience() == false ) return Vector3f.ZERO;
+        if( app.isSeatedExperience() == false ) return Vector3f.ZERO;
         if( hmdSeatToStand == null ) {
             hmdSeatToStand = new Vector3f();
             HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
@@ -524,8 +526,8 @@ public class OpenVR implements VRAPI {
     }
 
 	@Override
-	public VRApplication getApplication() {
-		return application;
+	public VRAppState getVRAppState() {
+		return app;
 	}
     
 }

+ 17 - 16
jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java

@@ -9,6 +9,7 @@ import java.util.List;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
@@ -97,7 +98,7 @@ public class OpenVRInput implements VRInputAPI {
     
     private final Quaternion tempq = new Quaternion();
 
-    private VRApplication application;
+    private VRAppState app;
 
     private List<VRTrackedController> trackedControllers = null;
     
@@ -105,8 +106,8 @@ public class OpenVRInput implements VRInputAPI {
      * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> input attached to the given application.
      * @param application the application to which the input is attached.
      */
-    public OpenVRInput(VRApplication application){
-      this.application = application;
+    public OpenVRInput(VRAppState appState){
+      this.app = appState;
     }
     
     @Override
@@ -296,7 +297,7 @@ public class OpenVRInput implements VRInputAPI {
     
     @Override
     public boolean isInputFocused() {
-        return ((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0;       
+        return ((VR_IVRSystem_FnTable)app.getVRHardware().getVRSystem()).IsInputFocusCapturedByAnotherProcess.apply() == 0;       
     }
     
     @Override
@@ -326,9 +327,9 @@ public class OpenVRInput implements VRInputAPI {
     
     @Override
     public Quaternion getFinalObserverRotation(int index) {
-        VRViewManager vrvm = application.getVRViewManager();
+        VRViewManager vrvm = app.getVRViewManager();
         if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
-        Object obs = application.getObserver();
+        Object obs = app.getObserver();
         if( obs instanceof Camera ) {
             tempq.set(((Camera)obs).getRotation());
         } else {
@@ -339,9 +340,9 @@ public class OpenVRInput implements VRInputAPI {
     
     @Override 
     public Vector3f getFinalObserverPosition(int index) {
-        VRViewManager vrvm = application.getVRViewManager();
+        VRViewManager vrvm = app.getVRViewManager();
         if( vrvm == null || isInputDeviceTracking(index) == false ) return null;
-        Object obs = application.getObserver();
+        Object obs = app.getObserver();
         Vector3f pos = getPosition(index);
         if( obs instanceof Camera ) {
             ((Camera)obs).getRotation().mult(pos, pos);
@@ -354,9 +355,9 @@ public class OpenVRInput implements VRInputAPI {
     
     @Override
     public void triggerHapticPulse(int controllerIndex, float seconds) {
-        if( application.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ) return;
+        if( app.isInVR() == false || isInputDeviceTracking(controllerIndex) == false ) return;
         // apparently only axis ID of 0 works
-        ((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex],
+        ((VR_IVRSystem_FnTable)app.getVRHardware().getVRSystem()).TriggerHapticPulse.apply(OpenVRInput.controllerIndex[controllerIndex],
                                                                                                      0, (short)Math.round(3f * seconds / 1e-3f));
     }
     
@@ -365,13 +366,13 @@ public class OpenVRInput implements VRInputAPI {
     	logger.config("Updating connected controllers.");
     	controllerCount = 0;
     	for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
-    		if( ((OpenVR)application.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i) == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller ) {
+    		if( ((OpenVR)app.getVRHardware()).getVRSystem().GetTrackedDeviceClass.apply(i) == JOpenVRLibrary.ETrackedDeviceClass.ETrackedDeviceClass_TrackedDeviceClass_Controller ) {
     			
     			String controllerName   = "Unknown";
 				String manufacturerName = "Unknown";
 				try {
-					controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)application.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String);
-					manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)application.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String);
+					controllerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)app.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_TrackingSystemName_String);
+					manufacturerName = OpenVRUtil.getTrackedDeviceStringProperty(((OpenVR)app.getVRHardware()).getVRSystem(), i, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String);
 				} catch (Exception e) {
                   logger.log(Level.WARNING, e.getMessage(), e);
 				}
@@ -393,7 +394,7 @@ public class OpenVRInput implements VRInputAPI {
     public void updateControllerStates() {
     	for(int i=0;i<controllerCount;i++) {
     		int index = controllerIndex[i];
-    		((OpenVR)application.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 5);
+    		((OpenVR)app.getVRHardware()).getVRSystem().GetControllerState.apply(index, cStates[index], 5);
     		cStates[index].readField("ulButtonPressed");
     		cStates[index].readField("rAxis");
     		needsNewVelocity[index] = true;
@@ -402,8 +403,8 @@ public class OpenVRInput implements VRInputAPI {
     }
 
 	@Override
-	public VRApplication getApplication() {
-		return application;
+	public VRAppState getVRAppState() {
+		return app;
 	}
 
 }

+ 4 - 4
jme3-vr/src/main/java/com/jme3/input/vr/VRAPI.java

@@ -5,7 +5,7 @@
  */
 package com.jme3.input.vr;
 
-import com.jme3.app.VRApplication;
+import com.jme3.app.VRAppState;
 import com.jme3.math.Matrix4f;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
@@ -33,10 +33,10 @@ public interface VRAPI {
     public boolean initVRCompositor(boolean allowed);
     
     /**
-     * Get the VR application to which this input is attached.
-     * @return the VR application to which this input is attached.
+     * Get the {@link VRAppState VR app state} to which this api is attached.
+     * @return the VR app state to which this input is attached.
      */
-    public VRApplication getApplication();
+    public VRAppState getVRAppState();
     
     /**
      * Get the object that wraps natively the VR system.

+ 4 - 3
jme3-vr/src/main/java/com/jme3/input/vr/VRInputAPI.java

@@ -5,6 +5,7 @@
  */
 package com.jme3.input.vr;
 
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
 import com.jme3.math.Quaternion;
 import com.jme3.math.Vector2f;
@@ -197,8 +198,8 @@ public interface VRInputAPI {
     public void triggerHapticPulse(int controllerIndex, float seconds);
     
     /**
-     * Get the VR application to which this input is attached.
-     * @return the VR application to which this input is attached.
+     * Get the {@link VRAppState VR app state} to which this api is attached.
+     * @return the VR app state to which this input is attached.
      */
-    public VRApplication getApplication();
+    public VRAppState getVRAppState();
 }

+ 4 - 3
jme3-vr/src/main/java/jmevr/util/MeshUtil.java

@@ -5,6 +5,7 @@
 package jmevr.util;
 
 import com.jme3.app.VRApplication;
+import com.jme3.input.vr.VRAPI;
 import com.jme3.scene.Mesh;
 import com.jme3.scene.VertexBuffer;
 import com.jme3.system.jopenvr.DistortionCoordinates_t;
@@ -17,7 +18,7 @@ import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
  */
 public class MeshUtil {
 
-    public static Mesh setupDistortionMesh(int eye, VRApplication application) {
+    public static Mesh setupDistortionMesh(int eye, VRAPI api) {
         Mesh distortionMesh = new Mesh();
         float m_iLensGridSegmentCountH = 43, m_iLensGridSegmentCountV = 43;
         
@@ -45,7 +46,7 @@ public class MeshUtil {
                 vertPos += 3;
 
                 DistortionCoordinates_t dc0 = new DistortionCoordinates_t();
-                if( application.getVRHardware().getVRSystem() == null ) {
+                if( api.getVRSystem() == null ) {
                     // default to no distortion
                     texcoordR[coordPos] = u;
                     texcoordR[coordPos + 1] = 1 - v;
@@ -54,7 +55,7 @@ public class MeshUtil {
                     texcoordB[coordPos] = u;
                     texcoordB[coordPos + 1] = 1 - v;                    
                 } else {
-                    ((VR_IVRSystem_FnTable)application.getVRHardware().getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0);
+                    ((VR_IVRSystem_FnTable)api.getVRSystem()).ComputeDistortion.apply(eye, u, v, dc0);
                     
                     texcoordR[coordPos] = dc0.rfRed[0];
                     texcoordR[coordPos + 1] = 1 - dc0.rfRed[1];

+ 40 - 18
jme3-vr/src/main/java/jmevr/util/VRGuiManager.java

@@ -4,7 +4,10 @@
  */
 package jmevr.util;
 
+import com.jme3.app.Application;
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
+import com.jme3.app.state.AppState;
 import com.jme3.material.Material;
 import com.jme3.material.RenderState.BlendMode;
 import com.jme3.math.ColorRGBA;
@@ -24,6 +27,7 @@ import com.jme3.texture.Image.Format;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
 import java.awt.GraphicsEnvironment;
+import java.util.Iterator;
 
 /**
  *
@@ -44,22 +48,35 @@ public class VRGuiManager {
     private Vector2f screenSize;
     protected boolean wantsReposition;
 
-    private VRApplication application = null;
+    private VRAppState app = null;
+    private Application application = null;
     
     /**
-     * Create a new GUI manager attached to the given application.
-     * @param application the VR application that this manager is attached to.
+     * Create a new GUI manager attached to the given app state.
+     * @param app the VR app state that this manager is attached to.
      */
-    public VRGuiManager(VRApplication application){
-    	this.application = application;
+    public VRGuiManager(){
     }
     
     /**
-     * Get the VR application to which this GUI manager is attached.
-     * @return the VR application to which this GUI manager is attached.
+     * Get the VR app state to which this GUI manager is attached.
+     * @return the VR app state to which this GUI manager is attached.
      */
-    public VRApplication getApplication(){
-    	return application;
+    public VRAppState getVRAppState(){
+    	return app;
+    }
+    
+    /**
+     * Attach the GUI manager to an app state and an Application. 
+     * The application has to be the one that the app state is attached.
+     * This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} 
+     * method of the {@link AppState} instance.
+     * @param app the VR app state that this manager is attached to.
+     * @param application the application to whitch the app state is attcached.
+     */
+    public void attach(VRAppState app, Application application){
+    	this.app = app;
+    	this.application = application;
     }
     
     /**
@@ -84,10 +101,10 @@ public class VRGuiManager {
     
     public Vector2f getCanvasSize() {
         if( screenSize == null ) {
-            if( application.isInVR() && application.getVRHardware() != null ) {
+            if( app.isInVR() && app.getVRHardware() != null ) {
                 screenSize = new Vector2f();
-                application.getVRHardware().getRenderSize(screenSize);
-                screenSize.multLocal(application.getVRViewManager().getResolutionMuliplier());
+                app.getVRHardware().getRenderSize(screenSize);
+                screenSize.multLocal(app.getVRViewManager().getResolutionMuliplier());
             } else {
                 AppSettings as = application.getContext().getSettings();
                 screenSize = new Vector2f(as.getWidth(), as.getHeight());
@@ -127,7 +144,7 @@ public class VRGuiManager {
         guiPos.set(0f, 0f, guiDistance);
         dir.mult(guiPos, guiPos);
         guiPos.x += pos.x;
-        guiPos.y += pos.y + application.getVRHeightAdjustment();
+        guiPos.y += pos.y + app.getVRHeightAdjustment();
         guiPos.z += pos.z;        
         if( guiPositioningElastic > 0f && posMode != POSITIONING_MODE.MANUAL ) {
             // mix pos & dir with current pos & dir            
@@ -142,7 +159,7 @@ public class VRGuiManager {
     
     protected void positionGuiNow(float tpf) {
         wantsReposition = false;
-        if( application.isInVR() == false ) return;
+        if( app.isInVR() == false ) return;
         guiQuadNode.setLocalScale(guiDistance * guiScale * 4f, 4f * guiDistance * guiScale, 1f);
         
         switch( posMode ) {
@@ -158,7 +175,7 @@ public class VRGuiManager {
 
                 break;
             case AUTO_OBSERVER_POS_CAM_ROTATION:
-                Object obs = application.getObserver();
+                Object obs = app.getObserver();
                 if( obs != null ) {
                     if( obs instanceof Camera ) {
                         positionTo(((Camera)obs).getLocation(), camLeft.getRotation(), tpf);
@@ -171,7 +188,7 @@ public class VRGuiManager {
                 break;
             case AUTO_OBSERVER_ALL:
             case AUTO_OBSERVER_ALL_CAMHEIGHT:
-                obs = application.getObserver();
+                obs = app.getObserver();
                 if( obs != null ) {
                     Quaternion q;
                     if( obs instanceof Camera ) {
@@ -230,7 +247,7 @@ public class VRGuiManager {
     }
     
     protected void setupGui(Camera leftcam, Camera rightcam, ViewPort left, ViewPort right) {
-        if( application.hasTraditionalGUIOverlay() ) {
+        if( app.hasTraditionalGUIOverlay() ) {
             camLeft = leftcam;
             camRight = rightcam;            
             Spatial guiScene = getGuiQuad(camLeft);
@@ -259,6 +276,7 @@ public class VRGuiManager {
     private Node guiQuadNode;
     private ViewPort offView;
     private Texture2D guiTexture;
+    
     private Spatial getGuiQuad(Camera sourceCam){
         if( guiQuadNode == null ) {
             Vector2f guiCanvasSize = getCanvasSize();
@@ -287,7 +305,11 @@ public class VRGuiManager {
             offView.setOutputFrameBuffer(offBuffer);
 
             // setup framebuffer's scene
-            offView.attachScene(application.getGuiNode());
+            Iterator<Spatial> spatialIter = application.getGuiViewPort().getScenes().iterator();
+            while(spatialIter.hasNext()){
+            	offView.attachScene(spatialIter.next());
+            }
+            
 
             if( useCurvedSurface ) {
                 guiQuad = (Geometry)application.getAssetManager().loadModel("Common/Util/gui_mesh.j3o");

+ 45 - 26
jme3-vr/src/main/java/jmevr/util/VRMouseManager.java

@@ -5,9 +5,15 @@
  */
 package jmevr.util;
 
+import java.util.logging.Level;
 import java.util.logging.Logger;
 
+import org.lwjgl.glfw.GLFW;
+
+import com.jme3.app.Application;
+import com.jme3.app.VRAppState;
 import com.jme3.app.VRApplication;
+import com.jme3.app.state.AppState;
 import com.jme3.input.MouseInput;
 import com.jme3.input.controls.AnalogListener;
 import com.jme3.input.lwjgl.GlfwMouseInputVR;
@@ -16,6 +22,7 @@ import com.jme3.material.RenderState.BlendMode;
 import com.jme3.math.Vector2f;
 import com.jme3.scene.Node;
 import com.jme3.system.AppSettings;
+import com.jme3.system.lwjgl.LwjglWindow;
 import com.jme3.system.lwjgl.LwjglWindowVR;
 import com.jme3.texture.Texture;
 import com.jme3.texture.Texture2D;
@@ -29,8 +36,9 @@ public class VRMouseManager {
  
 	private static final Logger logger = Logger.getLogger(VRMouseManager.class.getName());
 	
-	private VRApplication application = null;
-
+	private Application application = null;
+	private VRAppState app = null;
+	
 	private final int AVERAGE_AMNT = 4;
     private int avgCounter;
     
@@ -48,16 +56,20 @@ public class VRMouseManager {
         return amt / arr.length;
     }
     
-    public VRMouseManager(VRApplication application){
-      this.application = application;
+    public VRMouseManager(){
     }
     
     /**
-     * Get the VR application to which this mouse manager is attached.
-     * @return the VR application to which this mouse manager is attached.
+     * Attach the mouse manager to an app state and an Application. 
+     * The application has to be the one that the app state is attached.
+     * This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} 
+     * method of the {@link AppState} instance.
+     * @param app the VR app state that this manager is attached to.
+     * @param application the application to whitch the app state is attcached.
      */
-    public VRApplication getApplication(){
-    	return application;
+    public void attach(VRAppState app, Application application){
+    	this.app = app;
+    	this.application = application;
     }
     
     protected void init() {
@@ -73,6 +85,8 @@ public class VRMouseManager {
         	((GlfwMouseInputVR)mi).hideActiveCursor();
         }
         centerMouse();
+        
+        logger.config("Initialized VR mouse manager [SUCCESS]");
     }
     
     public void setThumbstickMode(boolean set) {
@@ -101,7 +115,7 @@ public class VRMouseManager {
     }
     
     public void setImage(String texture) {
-        if( application.isInVR() == false ){
+        if( app.isInVR() == false ){
         	Texture tex = application.getAssetManager().loadTexture(texture);
             mouseImage.setTexture(application.getAssetManager(), (Texture2D)tex, true);
             ySize = tex.getImage().getHeight();
@@ -123,14 +137,14 @@ public class VRMouseManager {
     
     public void updateAnalogAsMouse(int inputIndex, AnalogListener mouseListener, String mouseXName, String mouseYName, float tpf) {
         // got a tracked controller to use as the "mouse"
-        if( application.isInVR() == false || 
-            application.getVRinput() == null ||
-        	application.getVRinput().isInputDeviceTracking(inputIndex) == false ) return;
+        if( app.isInVR() == false || 
+        	app.getVRinput() == null ||
+        	app.getVRinput().isInputDeviceTracking(inputIndex) == false ) return;
         Vector2f tpDelta;
         if( thumbstickMode ) {
-            tpDelta = application.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis);
+            tpDelta = app.getVRinput().getAxis(inputIndex, VRInputType.ViveTrackpadAxis);
         } else {
-            tpDelta = application.getVRinput().getAxisDeltaSinceLastCall(inputIndex, VRInputType.ViveTrackpadAxis);            
+            tpDelta = app.getVRinput().getAxisDeltaSinceLastCall(inputIndex, VRInputType.ViveTrackpadAxis);            
         }
         float Xamount = (float)Math.pow(Math.abs(tpDelta.x) * sensitivity, acceleration);
         float Yamount = (float)Math.pow(Math.abs(tpDelta.y) * sensitivity, acceleration);
@@ -147,7 +161,7 @@ public class VRMouseManager {
             lastYmv[index] = Yamount * 133f;
             cursorPos.x -= avg(lastXmv);
             cursorPos.y -= avg(lastYmv);
-            Vector2f maxsize = application.getVRGUIManager().getCanvasSize();
+            Vector2f maxsize = app.getVRGUIManager().getCanvasSize();
             if( cursorPos.x > maxsize.x ) cursorPos.x = maxsize.x;
             if( cursorPos.x < 0f ) cursorPos.x = 0f;
             if( cursorPos.y > maxsize.y ) cursorPos.y = maxsize.y;
@@ -156,7 +170,7 @@ public class VRMouseManager {
     }
     
     public Vector2f getCursorPosition() {
-        if( application.isInVR() ) {
+        if( app.isInVR() ) {
             return cursorPos;
         }
         return application.getInputManager().getCursorPosition();
@@ -164,11 +178,11 @@ public class VRMouseManager {
     
     public void centerMouse() {
         // set mouse in center of the screen if newly added
-        Vector2f size = application.getVRGUIManager().getCanvasSize();
+        Vector2f size = app.getVRGUIManager().getCanvasSize();
         MouseInput mi = application.getContext().getMouseInput();
         AppSettings as = application.getContext().getSettings();
         if( mi instanceof GlfwMouseInputVR ) ((GlfwMouseInputVR)mi).setCursorPosition((int)(as.getWidth() / 2f), (int)(as.getHeight() / 2f));
-        if( application.isInVR() ) {
+        if( app.isInVR() ) {
             cursorPos.x = size.x / 2f;
             cursorPos.y = size.y / 2f;
             recentCenterCount = 2;
@@ -180,11 +194,13 @@ public class VRMouseManager {
 
         if( application.getInputManager().isCursorVisible() ) {
             if( mouseImage.getParent() == null ) {
-            	application.getGuiNode().attachChild(mouseImage);                
+            	
+            	application.getGuiViewPort().attachScene(mouseImage);         
                 centerMouse();
                 // the "real" mouse pointer should stay hidden
-                org.lwjgl.glfw.GLFW.glfwSetInputMode(((LwjglWindowVR)application.getContext()).getWindowHandle(),
-                                                      org.lwjgl.glfw.GLFW.GLFW_CURSOR, org.lwjgl.glfw.GLFW.GLFW_CURSOR_DISABLED);
+                if (application.getContext() instanceof LwjglWindow){
+                	GLFW.glfwSetInputMode(((LwjglWindow)application.getContext()).getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_DISABLED);
+                }
             }
             // handle mouse movements, which may be in addition to (or exclusive from) tracked movement
             MouseInput mi = application.getContext().getMouseInput();
@@ -195,21 +211,24 @@ public class VRMouseManager {
                     cursorPos.y += ((GlfwMouseInputVR)mi).getLastDeltaY();// * winratio.y;
                     if( cursorPos.x < 0f ) cursorPos.x = 0f;
                     if( cursorPos.y < 0f ) cursorPos.y = 0f;
-                    if( cursorPos.x > application.getVRGUIManager().getCanvasSize().x ) cursorPos.x = application.getVRGUIManager().getCanvasSize().x;
-                    if( cursorPos.y > application.getVRGUIManager().getCanvasSize().y ) cursorPos.y = application.getVRGUIManager().getCanvasSize().y;
+                    if( cursorPos.x > app.getVRGUIManager().getCanvasSize().x ) cursorPos.x = app.getVRGUIManager().getCanvasSize().x;
+                    if( cursorPos.y > app.getVRGUIManager().getCanvasSize().y ) cursorPos.y = app.getVRGUIManager().getCanvasSize().y;
                 } else recentCenterCount--;
                 ((GlfwMouseInputVR)mi).clearDeltas();
             }
             // ok, update the cursor graphic position
             Vector2f currentPos = getCursorPosition();
-            mouseImage.setLocalTranslation(currentPos.x, currentPos.y - ySize, application.getVRGUIManager().getGuiDistance() + 1f);
+            mouseImage.setLocalTranslation(currentPos.x, currentPos.y - ySize, app.getVRGUIManager().getGuiDistance() + 1f);
+		    
             mouseImage.updateGeometricState();
-            mouseImage.getParent().updateGeometricState();
             
         } else if( mouseImage.getParent() != null ) {
         	Node n = mouseImage.getParent();
             mouseImage.removeFromParent();
-            n.updateGeometricState();
+            
+            if (n != null){
+              n.updateGeometricState();
+            }
         }
     }    
 }

+ 863 - 852
jme3-vr/src/main/java/jmevr/util/VRViewManager.java

@@ -1,852 +1,863 @@
-/*
- * To change this template, choose Tools | Templates
- * and open the template in the editor.
- */
-package jmevr.util;
-
-import com.jme3.app.VRApplication;
-import com.jme3.input.vr.OSVR;
-import com.jme3.input.vr.OpenVR;
-import com.jme3.input.vr.VRAPI;
-import com.jme3.material.Material;
-import com.jme3.math.ColorRGBA;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.post.CartoonSSAO;
-import com.jme3.post.Filter;
-import com.jme3.post.FilterPostProcessor;
-import com.jme3.post.SceneProcessor;
-import com.jme3.post.filters.FogFilter;
-import com.jme3.post.filters.TranslucentBucketFilter;
-import com.jme3.post.ssao.SSAOFilter;
-import com.jme3.renderer.Camera;
-import com.jme3.renderer.ViewPort;
-import com.jme3.renderer.queue.RenderQueue.Bucket;
-import com.jme3.scene.Geometry;
-import com.jme3.scene.Node;
-import com.jme3.scene.Spatial;
-import com.jme3.shadow.DirectionalLightShadowFilter;
-import com.jme3.shadow.VRDirectionalLightShadowRenderer;
-import com.jme3.system.jopenvr.JOpenVRLibrary;
-import com.jme3.system.jopenvr.OpenVRUtil;
-import com.jme3.system.jopenvr.Texture_t;
-import com.jme3.system.jopenvr.VRTextureBounds_t;
-import com.jme3.system.lwjgl.LwjglWindow;
-import com.jme3.texture.FrameBuffer;
-import com.jme3.texture.Image;
-import com.jme3.texture.Texture;
-import com.jme3.texture.Texture2D;
-import com.jme3.ui.Picture;
-import com.sun.jna.Pointer;
-import com.sun.jna.ptr.IntByReference;
-import com.sun.jna.ptr.PointerByReference;
-
-import java.awt.GraphicsEnvironment;
-import java.util.logging.Logger;
-
-import osvrrendermanageropengl.OSVR_RenderBufferOpenGL;
-import osvrrendermanageropengl.OSVR_ViewportDescription;
-import osvrrendermanageropengl.OsvrRenderManagerOpenGLLibrary;
-
-/**
- * A VR view manager. This class enable to submit 3D views to the VR compositor.
- * @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 class VRViewManager {
-
-	private static final Logger logger = Logger.getLogger(VRViewManager.class.getName());
-	
-    /**
-     * The name of the left view.
-     */
-    public final static String LEFT_VIEW_NAME = "Left View";
-    
-    /**
-     * The name of the right view.
-     */
-    public final static String RIGHT_VIEW_NAME = "Right View";
-	
-    private final VRApplication app;
-    
-    private Camera leftCamera;
-    private ViewPort leftViewport;
-    private FilterPostProcessor leftPostProcessor;
-    private Texture2D leftEyeTexture;
-    private Texture2D leftEyeDepth;
-    
-    private Camera rightCamera;
-    private ViewPort rightViewport;
-    private FilterPostProcessor rightPostProcessor;
-    private Texture2D rightEyeTexture;
-    private Texture2D rightEyeDepth;
-    
-    // OpenVR values
-    private VRTextureBounds_t leftTextureBounds;
-    private Texture_t leftTextureType;
-    
-    private VRTextureBounds_t rightTextureBounds;
-    private Texture_t rightTextureType;
-    
-    // OSVR values
-    OSVR_RenderBufferOpenGL.ByValue[] osvr_renderBuffer;
-    OSVR_ViewportDescription.ByValue osvr_viewDescFull;
-    OSVR_ViewportDescription.ByValue osvr_viewDescLeft;
-    OSVR_ViewportDescription.ByValue osvr_viewDescRight;
-    Pointer osvr_rmBufferState;
-    
-    //private static boolean useCustomDistortion;
-    private float heightAdjustment;
-
-    private Texture2D dualEyeTex;
-
-    private final PointerByReference grabRBS = new PointerByReference();
-    
-    private float resMult = 1f;
-    
-    //final & temp values for camera calculations
-    private final Vector3f finalPosition   = new Vector3f();
-    private final Quaternion finalRotation = new Quaternion();
-    private final Vector3f hmdPos          = new Vector3f();
-    private final Quaternion hmdRot        = new Quaternion();
-    
-    /**
-     * Create a new VR view manager attached to the given {@link VRApplication VR application}.
-     * @param application the {@link VRApplication VR application} to which this manager is linked.
-     */
-    public VRViewManager(VRApplication application){
-        this.app = application;
-    }
-    
-    /**
-     * Get the {@link Camera camera} attached to the left eye.
-     * @return the {@link Camera camera} attached to the left eye.
-     * @see #getRightCamera()
-     */
-    public Camera getLeftCamera() {
-        return leftCamera;
-    }
-    
-    /**
-     * Get the {@link Camera camera} attached to the right eye.
-     * @return the {@link Camera camera} attached to the right eye.
-     * @see #getLeftCamera()
-     */
-    public Camera getRightCamera() {
-        return rightCamera;
-    }
-    
-    /**
-     * Get the {@link ViewPort viewport} attached to the left eye.
-     * @return the {@link ViewPort viewport} attached to the left eye.
-     * @see #getRightViewport()
-     */
-    public ViewPort getLeftViewport() {
-        return leftViewport;
-    }
-    
-    /**
-     * Get the {@link ViewPort viewport} attached to the right eye.
-     * @return the {@link ViewPort viewport} attached to the right eye.
-     * @see #getLeftViewport()
-     */
-    public ViewPort getRightViewport() {
-        return rightViewport;
-    }
-    
-    /**
-     * Get the identifier of the left eye texture.
-     * @return the identifier of the left eye texture.
-     * @see #getRightTexId()
-     * @see #getFullTexId()
-     */
-    private int getLeftTexId() {
-        return (int)leftEyeTexture.getImage().getId();
-    }
-    
-    /**
-     * Get the identifier of the right eye texture.
-     * @return the identifier of the right eye texture.
-     * @see #getLeftTexId()
-     * @see #getFullTexId()
-     */
-    private int getRightTexId() {
-        return (int)rightEyeTexture.getImage().getId();
-    }
-    
-    /**
-     * Get the identifier of the full (dual eye) texture.
-     * @return the identifier of the full (dual eye) texture.
-     * @see #getLeftTexId()
-     * @see #getRightTexId()
-     */
-    private int getFullTexId() {
-        return (int)dualEyeTex.getImage().getId();
-    }
-    
-    /**
-     * Get the height adjustment to apply to the cameras before rendering.
-     * @return the height adjustment to apply to the cameras before rendering.
-     * @see #setHeightAdjustment(float)
-     */
-    public float getHeightAdjustment() {
-        return heightAdjustment;
-    }
-    
-    /**
-     * Set the height adjustment to apply to the cameras before rendering.
-     * @param amount the height adjustment to apply to the cameras before rendering.
-     * @see #getHeightAdjustment()
-     */
-    public void setHeightAdjustment(float amount) {
-        heightAdjustment = amount;
-    }
-    
-    /**
-     * Get the resolution multiplier.
-     * @return the resolution multiplier.
-     * @see #setResolutionMultiplier(float)
-     */
-    public float getResolutionMuliplier() {
-        return resMult;
-    }
-    
-    /**
-     * Set the resolution multiplier.
-     * @param resMult the resolution multiplier.
-     * @see #getResolutionMuliplier()
-     */
-    public void setResolutionMultiplier(float resMult) {
-        this.resMult = resMult;
-    }
-    
-    /**
-     * Initialize the system binds of the textures.
-     */
-    private void initTextureSubmitStructs() {
-        leftTextureType = new Texture_t();
-        rightTextureType = new Texture_t();
-        
-
-        if( app.getVRHardware() instanceof OpenVR ) {
-            leftTextureBounds = new VRTextureBounds_t();
-            rightTextureBounds = new VRTextureBounds_t();
-            // left eye
-            leftTextureBounds.uMax = 0.5f;
-            leftTextureBounds.uMin = 0f;
-            leftTextureBounds.vMax = 1f;
-            leftTextureBounds.vMin = 0f;
-            leftTextureBounds.setAutoSynch(false);
-            leftTextureBounds.setAutoRead(false);
-            leftTextureBounds.setAutoWrite(false);
-            leftTextureBounds.write();
-            // right eye
-            rightTextureBounds.uMax = 1f;
-            rightTextureBounds.uMin = 0.5f;
-            rightTextureBounds.vMax = 1f;
-            rightTextureBounds.vMin = 0f;
-            rightTextureBounds.setAutoSynch(false);
-            rightTextureBounds.setAutoRead(false);
-            rightTextureBounds.setAutoWrite(false);
-            rightTextureBounds.write();
-            // texture type
-            // FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
-            leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma;
-            //leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
-            leftTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL;
-            leftTextureType.setAutoSynch(false);
-            leftTextureType.setAutoRead(false);
-            leftTextureType.setAutoWrite(false);
-            leftTextureType.handle = -1;
-            // FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
-            rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma;
-            //rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
-            rightTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL;
-            rightTextureType.setAutoSynch(false);
-            rightTextureType.setAutoRead(false);
-            rightTextureType.setAutoWrite(false);
-            rightTextureType.handle = -1;
-            
-            
-            logger.config("Init eyes native texture binds");
-            logger.config("  Left eye texture");
-            logger.config("           address: "+leftTextureType.getPointer());
-            logger.config("              size: "+leftTextureType.size()+" bytes");
-            logger.config("       color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace));
-            logger.config("              type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType));
-            logger.config("         auto read: "+leftTextureType.getAutoRead());
-            logger.config("        auto write: "+leftTextureType.getAutoWrite());
-            logger.config("    handle address: "+leftTextureType.handle);
-            logger.config("      handle value: "+leftTextureType.handle);
-            logger.config("");
-            logger.config("  Right eye texture");
-            logger.config("           address: "+rightTextureType.getPointer());
-            logger.config("              size: "+rightTextureType.size()+" bytes");
-            logger.config("       color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace));
-            logger.config("              type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType));
-            logger.config("         auto read: "+rightTextureType.getAutoRead());
-            logger.config("        auto write: "+rightTextureType.getAutoWrite());
-            logger.config("    handle address: "+rightTextureType.handle);
-            logger.config("      handle value: "+rightTextureType.handle);
-            
-            
-        } else if( app.getVRHardware() instanceof OSVR ) {
-            // must be OSVR
-            osvr_renderBuffer = new OSVR_RenderBufferOpenGL.ByValue[2];
-            osvr_renderBuffer[OSVR.EYE_LEFT] = new OSVR_RenderBufferOpenGL.ByValue();
-            osvr_renderBuffer[OSVR.EYE_RIGHT] = new OSVR_RenderBufferOpenGL.ByValue();
-            osvr_renderBuffer[OSVR.EYE_LEFT].setAutoSynch(false);
-            osvr_renderBuffer[OSVR.EYE_RIGHT].setAutoSynch(false);
-            osvr_viewDescFull = new OSVR_ViewportDescription.ByValue();
-            osvr_viewDescFull.setAutoSynch(false);
-            osvr_viewDescFull.left = osvr_viewDescFull.lower = 0.0;
-            osvr_viewDescFull.width = osvr_viewDescFull.height = 1.0;    
-            osvr_viewDescLeft = new OSVR_ViewportDescription.ByValue();
-            osvr_viewDescLeft.setAutoSynch(false);
-            osvr_viewDescLeft.left = osvr_viewDescLeft.lower = 0.0;
-            osvr_viewDescLeft.width = 0.5;
-            osvr_viewDescLeft.height = 1.0;    
-            osvr_viewDescRight = new OSVR_ViewportDescription.ByValue();
-            osvr_viewDescRight.setAutoSynch(false);
-            osvr_viewDescRight.left = 0.5;
-            osvr_viewDescRight.lower = 0.0;
-            osvr_viewDescRight.width = 0.5;
-            osvr_viewDescRight.height = 1.0;
-            osvr_viewDescRight.write();
-            osvr_viewDescLeft.write();
-            osvr_viewDescFull.write();
-            osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = -1;
-            osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = -1;
-            osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = -1;
-            osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = -1;
-        }
-    }
-
-    /**
-     * Register the OSVR OpenGL buffer.
-     * @param buf the OSVR OpenGL buffer.
-     */
-    private void registerOSVRBuffer(OSVR_RenderBufferOpenGL.ByValue buf) {
-        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerStartRegisterRenderBuffers(grabRBS);
-        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerRegisterRenderBufferOpenGL(grabRBS.getValue(), buf);
-        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerFinishRegisterRenderBuffers(((OSVR)app.getVRHardware()).getCompositor(), grabRBS.getValue(), (byte)0);
-    }
-    
-    /**
-     * Send the textures to the two eyes.
-     */
-    public void sendTextures() {
-        if( app.isInVR() ) {
-            VRAPI api = app.getVRHardware();
-            if( api.getCompositor() != null ) {
-                // using the compositor...
-                int errl = 0, errr = 0;
-                if( app.isInstanceVRRendering() ) {
-                    if( leftTextureType.handle == -1 || leftTextureType.handle != getFullTexId() ) {
-                    	leftTextureType.handle = getFullTexId();
-                        if( leftTextureType.handle != -1 ) {
-                            leftTextureType.write();
-                            if( api instanceof OSVR ) {
-                                osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle;
-                                osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = dualEyeTex.getImage().getId();
-                                osvr_renderBuffer[OSVR.EYE_LEFT].write();
-                                registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]);
-                            }
-                        }
-                    } else {
-                        if( api instanceof OpenVR ) {
-                            int submitFlag = JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default;
-                            errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, leftTextureType, rightTextureBounds, submitFlag);
-                            errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, leftTextureBounds, submitFlag);
-                        } else if( api instanceof OSVR ) {
-                            ((OSVR)api).handleRenderBufferPresent(osvr_viewDescLeft, osvr_viewDescRight,
-                                                                  osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_LEFT]);
-                        }
-                    }
-                } else if( leftTextureType.handle == -1 || rightTextureType.handle == -1 ||
-                           leftTextureType.handle != getLeftTexId() || rightTextureType.handle != getRightTexId() ) {
-                    leftTextureType.handle = getLeftTexId();
-                    if( leftTextureType.handle != -1 ) {
-                    	logger.fine("Writing Left texture to native memory at " + leftTextureType.getPointer());
-                        leftTextureType.write();
-                        if( api instanceof OSVR ) {
-                            osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle;
-                            if( leftEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = leftEyeDepth.getImage().getId();
-                            osvr_renderBuffer[OSVR.EYE_LEFT].write();
-                            registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]);
-                        }
-                    }
-                    rightTextureType.handle = getRightTexId();
-                    if( rightTextureType.handle != -1 ) {
-                    	logger.fine("Writing Right texture to native memory at " + leftTextureType.getPointer());
-                        rightTextureType.write();
-                        if( api instanceof OSVR ) {
-                            osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = rightTextureType.handle;
-                            if( rightEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = rightEyeDepth.getImage().getId();
-                            osvr_renderBuffer[OSVR.EYE_RIGHT].write();
-                            registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_RIGHT]);
-                        }
-                    }                    
-                } else {
-                    if( api instanceof OpenVR ) {
-                        errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, null,
-                                                               JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default);
-                        errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, rightTextureType, null,
-                                                               JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default);
-                    } else if( api instanceof OSVR ) {
-                        ((OSVR)api).handleRenderBufferPresent(osvr_viewDescFull, osvr_viewDescFull,
-                                                              osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_RIGHT]);
-                    }
-                }
-                
-                if( errl != 0 ){
-                	logger.severe("Submit to left compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")");
-                	logger.severe("  Texture color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace));
-                	logger.severe("  Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType));
-                	logger.severe("  Texture handle: "+leftTextureType.handle);
-                	
-                    logger.severe("  Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")");
-                    logger.severe("                 Type: "+leftEyeTexture.getType());
-                    logger.severe("                 Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight());
-                    logger.severe("          Image depth: "+leftEyeTexture.getImage().getDepth());
-                    logger.severe("         Image format: "+leftEyeTexture.getImage().getFormat());
-                    logger.severe("    Image color space: "+leftEyeTexture.getImage().getColorSpace());
-                	
-                }
-                
-                if( errr != 0 ){
-                	logger.severe("Submit to right compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")");
-                	logger.severe("  Texture color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace));
-                	logger.severe("  Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType));
-                	logger.severe("  Texture handle: "+rightTextureType.handle);
-                	
-                    logger.severe("  Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")");
-                    logger.severe("                 Type: "+rightEyeTexture.getType());
-                    logger.severe("                 Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight());
-                    logger.severe("          Image depth: "+rightEyeTexture.getImage().getDepth());
-                    logger.severe("         Image format: "+rightEyeTexture.getImage().getFormat());
-                    logger.severe("    Image color space: "+rightEyeTexture.getImage().getColorSpace());
-                }
-            }
-        }                
-    }
-
-
-    /**
-     * Initialize the VR view manager.
-     */
-    public void initialize() {     
-    	
-    	logger.config("Initializing VR view manager.");
-    	
-        initTextureSubmitStructs();
-        setupCamerasAndViews();        
-        setupVRScene();                    
-        moveScreenProcessingToEyes();       
-        if( app.hasTraditionalGUIOverlay() ) {
-        	
-            app.getVRMouseManager().init();
-            
-            // update the pose to position the gui correctly on start
-            update(0f);
-            app.getVRGUIManager().positionGui();
-        }       
-        // if we are OSVR, our primary mirror window needs to be the same size as the render manager's output...
-        if( app.getVRHardware() instanceof OSVR ) {
-            int origWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth();
-            int origHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight();
-            long window = ((LwjglWindow)app.getContext()).getWindowHandle();
-            Vector2f windowSize = new Vector2f();
-            ((OSVR)app.getVRHardware()).getRenderSize(windowSize);
-            windowSize.x = Math.max(windowSize.x * 2f, leftCamera.getWidth());
-            org.lwjgl.glfw.GLFW.glfwSetWindowSize(window, (int)windowSize.x, (int)windowSize.y);
-            app.getContext().getSettings().setResolution((int)windowSize.x, (int)windowSize.y);
-            app.reshape((int)windowSize.x, (int)windowSize.y);            
-            org.lwjgl.glfw.GLFW.glfwSetWindowPos(window, origWidth - (int)windowSize.x, 32);
-            
-            org.lwjgl.glfw.GLFW.glfwFocusWindow(window);
-            
-            org.lwjgl.glfw.GLFW.glfwSetCursorPos(window, origWidth / 2.0, origHeight / 2.0);
-        }       
-    }
-    
-    /**
-     * Prepare the size of the given {@link Camera camera} to adapt it to the underlying rendering context.
-     * @param cam the {@link Camera camera} to prepare.
-     * @param xMult the camera width multiplier.
-     */
-    private void prepareCameraSize(Camera cam, float xMult) {
-        Vector2f size = new Vector2f();
-        VRAPI vrhmd = app.getVRHardware();
-
-        if( vrhmd == null ) {
-            size.x = 1280f;
-            size.y = 720f;
-        } else {
-            vrhmd.getRenderSize(size);
-        }
-        
-        if( size.x < app.getContext().getSettings().getWidth() ) {
-            size.x = app.getContext().getSettings().getWidth();
-        }
-        if( size.y < app.getContext().getSettings().getHeight() ) {
-            size.y = app.getContext().getSettings().getHeight();
-        }
-        
-        if( app.isInstanceVRRendering() ) size.x *= 2f;
-        
-        // other adjustments
-        size.x *= xMult;
-        size.x *= resMult;
-        size.y *= resMult;
-        
-        if( cam.getWidth() != size.x || cam.getHeight() != size.y ) cam.resize((int)size.x, (int)size.y, false);
-    }
-    
-    /**
-     * Replaces rootNode as the main cameras scene with the distortion mesh
-     */
-    private void setupVRScene(){
-        // no special scene to setup if we are doing instancing
-        if( app.isInstanceVRRendering() ) {
-            // distortion has to be done with compositor here... we want only one pass on our end!
-            if( app.getContext().getSettings().isSwapBuffers() ) {
-                setupMirrorBuffers(app.getCamera(), dualEyeTex, true);
-            }       
-            return;
-        }
-        
-        leftEyeTexture  = (Texture2D) leftViewport.getOutputFrameBuffer().getColorBuffer().getTexture();
-        rightEyeTexture = (Texture2D)rightViewport.getOutputFrameBuffer().getColorBuffer().getTexture();        
-        leftEyeDepth    = (Texture2D) leftViewport.getOutputFrameBuffer().getDepthBuffer().getTexture();
-        rightEyeDepth   = (Texture2D)rightViewport.getOutputFrameBuffer().getDepthBuffer().getTexture();        
-/*        
-        logger.config("Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")");
-        logger.config("               Type: "+leftEyeTexture.getType());
-        logger.config("               Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight());
-        logger.config("        Image depth: "+leftEyeTexture.getImage().getDepth());
-        logger.config("       Image format: "+leftEyeTexture.getImage().getFormat());
-        logger.config("  Image color space: "+leftEyeTexture.getImage().getColorSpace());
-        
-        logger.config("Left eye depth "+leftEyeDepth.getName()+" ("+leftEyeDepth.getImage().getId()+")");
-        logger.config("               Type: "+leftEyeDepth.getType());
-        logger.config("               Size: "+leftEyeDepth.getImage().getWidth()+"x"+leftEyeDepth.getImage().getHeight());
-        logger.config("        Image depth: "+leftEyeDepth.getImage().getDepth());
-        logger.config("       Image format: "+leftEyeDepth.getImage().getFormat());
-        logger.config("  Image color space: "+leftEyeDepth.getImage().getColorSpace());
-        
-        logger.config("Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")");
-        logger.config("               Type: "+rightEyeTexture.getType());
-        logger.config("               Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight());
-        logger.config("        Image depth: "+rightEyeTexture.getImage().getDepth());
-        logger.config("       Image format: "+rightEyeTexture.getImage().getFormat());
-        logger.config("  Image color space: "+rightEyeTexture.getImage().getColorSpace());
-        
-        logger.config("Right eye depth "+rightEyeDepth.getName()+" ("+rightEyeDepth.getImage().getId()+")");
-        logger.config("               Type: "+rightEyeDepth.getType());
-        logger.config("               Size: "+rightEyeDepth.getImage().getWidth()+"x"+rightEyeDepth.getImage().getHeight());
-        logger.config("        Image depth: "+rightEyeDepth.getImage().getDepth());
-        logger.config("       Image format: "+rightEyeDepth.getImage().getFormat());
-        logger.config("  Image color space: "+rightEyeDepth.getImage().getColorSpace());
-*/        
-        // main viewport is either going to be a distortion scene or nothing
-        // mirroring is handled by copying framebuffers
-        app.getViewPort().detachScene(app.getRootNode());
-        app.getViewPort().detachScene(app.getGuiNode());
-        
-        // only setup distortion scene if compositor isn't running (or using custom mesh distortion option)
-        if( app.getVRHardware().getCompositor() == null ) {
-            Node distortionScene = new Node();
-            Material leftMat = new Material(app.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md");
-            leftMat.setTexture("Texture", leftEyeTexture);
-            Geometry leftEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Left, app));
-            leftEye.setMaterial(leftMat);
-            distortionScene.attachChild(leftEye);
-
-            Material rightMat = new Material(app.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md");
-            rightMat.setTexture("Texture", rightEyeTexture);
-            Geometry rightEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Right, app));
-            rightEye.setMaterial(rightMat);
-            distortionScene.attachChild(rightEye);
-
-            distortionScene.updateGeometricState();
-
-            app.getViewPort().attachScene(distortionScene);
-            
-            //if( useCustomDistortion ) setupFinalFullTexture(app.getViewPort().getCamera());
-        }
-        
-        if( app.getContext().getSettings().isSwapBuffers() ) {
-            setupMirrorBuffers(app.getCamera(), leftEyeTexture, false);
-        }       
-    }
-    
-    /**
-     * Update the VR view manager. 
-     * This method is called by the attached {@link VRApplication VR application} and should not be called manually.
-     * @param tpf the time per frame.
-     */
-    public void update(float tpf) {
-        
-        // grab the observer
-        Object obs = app.getObserver();
-        Quaternion objRot;
-        Vector3f objPos;
-        if( obs instanceof Camera ) {
-            objRot = ((Camera)obs).getRotation();
-            objPos = ((Camera)obs).getLocation();
-        } else {
-            objRot = ((Spatial)obs).getWorldRotation();
-            objPos = ((Spatial)obs).getWorldTranslation();
-        }
-        // grab the hardware handle
-        VRAPI dev = app.getVRHardware();
-        if( dev != null ) {
-            // update the HMD's position & orientation
-            dev.updatePose();
-            dev.getPositionAndOrientation(hmdPos, hmdRot);
-            if( obs != null ) {
-                // update hmdPos based on obs rotation
-                finalRotation.set(objRot);
-                finalRotation.mult(hmdPos, hmdPos);
-                finalRotation.multLocal(hmdRot);
-            }
-            
-            finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera);
-            finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera);
-        } else {
-            leftCamera.setFrame(objPos, objRot);
-            rightCamera.setFrame(objPos, objRot);
-        }
-        
-        if( app.hasTraditionalGUIOverlay() ) {
-            // update the mouse?
-        	app.getVRMouseManager().update(tpf);
-        
-            // update GUI position?
-            if( app.getVRGUIManager().wantsReposition || app.getVRGUIManager().getPositioningMode() != VRGuiManager.POSITIONING_MODE.MANUAL ) {
-            	app.getVRGUIManager().positionGuiNow(tpf);
-            	app.getVRGUIManager().updateGuiQuadGeometricState();
-            }
-        }
-    }
-    
-    /**
-     * Place the camera within the scene.
-     * @param eyePos the eye position.
-     * @param obsPosition the observer position.
-     * @param cam the camera to place.
-     */
-    private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) {
-        finalRotation.mult(eyePos, finalPosition);
-        finalPosition.addLocal(hmdPos);
-        if( obsPosition != null ) finalPosition.addLocal(obsPosition);
-        finalPosition.y += heightAdjustment;
-        cam.setFrame(finalPosition, finalRotation);
-    }
-    
-    /**
-     * Handles moving filters from the main view to each eye
-     */
-    public void moveScreenProcessingToEyes() {
-        if( rightViewport == null ) return;
-        syncScreenProcessing(app.getViewPort());
-        app.getViewPort().clearProcessors();
-    }
-    
-    /**
-     * Sets the two views to use the list of {@link SceneProcessor processors}.
-     * @param sourceViewport the {@link ViewPort viewport} that contains the processors to use.
-     */
-    public void syncScreenProcessing(ViewPort sourceViewport) {
-        if( rightViewport == null ) return;
-        // setup post processing filters
-        if( rightPostProcessor == null ) {
-            rightPostProcessor = new FilterPostProcessor(app.getAssetManager());               
-            leftPostProcessor =  new FilterPostProcessor(app.getAssetManager());
-        }
-        // clear out all filters & processors, to start from scratch
-        rightPostProcessor.removeAllFilters();
-        leftPostProcessor.removeAllFilters();
-        leftViewport.clearProcessors();
-        rightViewport.clearProcessors();
-        // if we have no processors to sync, don't add the FilterPostProcessor
-        if( sourceViewport.getProcessors().isEmpty() ) return;
-        // add post processors we just made, which are empty
-        leftViewport.addProcessor(leftPostProcessor);
-        rightViewport.addProcessor(rightPostProcessor);
-        // go through all of the filters in the processors list
-        // add them to the left viewport processor & clone them to the right
-        for(SceneProcessor sceneProcessor : sourceViewport.getProcessors()) {
-            if (sceneProcessor instanceof FilterPostProcessor) {
-                for(Filter f : ((FilterPostProcessor)sceneProcessor).getFilterList() ) {
-                    if( f instanceof TranslucentBucketFilter ) {
-                        // just remove this filter, we will add it at the end manually
-                        ((FilterPostProcessor)sceneProcessor).removeFilter(f);
-                    } else {
-                        leftPostProcessor.addFilter(f);
-                        // clone to the right
-                        Filter f2;
-                        if(f instanceof FogFilter){
-                            f2 = FilterUtil.cloneFogFilter((FogFilter)f); 
-                        } else if (f instanceof CartoonSSAO ) {
-                            f2 = new CartoonSSAO((CartoonSSAO)f);
-                        } else if (f instanceof SSAOFilter){
-                            f2 = FilterUtil.cloneSSAOFilter((SSAOFilter)f);
-                        } else if (f instanceof DirectionalLightShadowFilter){
-                            f2 = FilterUtil.cloneDirectionalLightShadowFilter(app.getAssetManager(), (DirectionalLightShadowFilter)f);
-                        } else {
-                            f2 = f; // dof, bloom, lightscattering etc.
-                        }                    
-                        rightPostProcessor.addFilter(f2);
-                    }
-                }
-            } else if (sceneProcessor instanceof VRDirectionalLightShadowRenderer) {
-                // shadow processing
-                // TODO: make right shadow processor use same left shadow maps for performance
-                VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor;
-                VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone();
-                dlsrRight.setLight(dlsr.getLight());
-                rightViewport.getProcessors().add(0, dlsrRight);
-                leftViewport.getProcessors().add(0, sceneProcessor);
-            }
-        }
-        // make sure each has a translucent filter renderer
-        leftPostProcessor.addFilter(new TranslucentBucketFilter());
-        rightPostProcessor.addFilter(new TranslucentBucketFilter());
-    }
-    
-    private void setupCamerasAndViews() {        
-        // get desired frustrum from original camera
-        Camera origCam = app.getBaseCamera();        
-        float fFar = origCam.getFrustumFar();
-        float fNear = origCam.getFrustumNear();
-        
-        // if we are using OSVR get the eye info here
-        if( app.getVRHardware() instanceof OSVR ) {
-            ((OSVR)app.getVRHardware()).getEyeInfo();
-        }
-        
-        // restore frustrum on distortion scene cam, if needed
-        if( app.isInstanceVRRendering() ) {
-            leftCamera = origCam;
-        } else if( app.compositorAllowed() == false ) {
-            origCam.setFrustumFar(100f);
-            origCam.setFrustumNear(1f); 
-            leftCamera = origCam.clone();  
-            prepareCameraSize(origCam, 2f);
-        } else {
-            leftCamera = origCam.clone();
-        }
-        
-        leftCamera.setFrustumPerspective(app.DEFAULT_FOV, app.DEFAULT_ASPECT, fNear, fFar);                     
-                
-        prepareCameraSize(leftCamera, 1f);
-        if( app.getVRHardware() != null ) leftCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionLeftEye(leftCamera));
-        //org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
-        
-        if( app.isInstanceVRRendering() == false ) {
-            leftViewport = setupViewBuffers(leftCamera, LEFT_VIEW_NAME);
-            rightCamera = leftCamera.clone();
-            if( app.getVRHardware() != null ) rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera));
-            rightViewport = setupViewBuffers(rightCamera, RIGHT_VIEW_NAME);
-        } else {
-            leftViewport = app.getViewPort();
-            leftViewport.attachScene(app.getRootNode());
-            rightCamera = leftCamera.clone();
-            if( app.getVRHardware() != null ) rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera));
-            
-            org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0);
-            
-            //FIXME: [jme-vr] Fix with JMonkey next release
-            //RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix();
-            
-            setupFinalFullTexture(app.getViewPort().getCamera());            
-        }
-        
-        // setup gui
-        app.getVRGUIManager().setupGui(leftCamera, rightCamera, leftViewport, rightViewport);
-        
-        if( app.getVRHardware() != null ) {
-            // call these to cache the results internally
-        	app.getVRHardware().getHMDMatrixPoseLeftEye();
-        	app.getVRHardware().getHMDMatrixPoseRightEye();
-        }
-
-    }
-    
-    private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) {        
-        Camera clonecam = cam.clone();
-        ViewPort viewPort = app.getRenderManager().createPostView("MirrorView", clonecam);
-        clonecam.setParallelProjection(true);
-        viewPort.setClearFlags(true, true, true);
-        viewPort.setBackgroundColor(ColorRGBA.Black);
-        Picture pic = new Picture("fullscene");
-        pic.setLocalTranslation(-0.75f, -0.5f, 0f);
-        if( expand ) {
-            pic.setLocalScale(3f, 1f, 1f);
-        } else {
-            pic.setLocalScale(1.5f, 1f, 1f);            
-        }
-        pic.setQueueBucket(Bucket.Opaque);
-        pic.setTexture(app.getAssetManager(), (Texture2D)tex, false);
-        viewPort.attachScene(pic);
-        viewPort.setOutputFrameBuffer(null);
-        
-        pic.updateGeometricState();
-        
-        return viewPort;
-    }
-    
-    private void setupFinalFullTexture(Camera cam) {
-        // create offscreen framebuffer
-        FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1);
-        //offBuffer.setSrgb(true);
-
-        //setup framebuffer's texture
-        dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8);
-        dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
-        dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear);
-
-        logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")");
-        logger.config("               Type: "+dualEyeTex.getType());
-        logger.config("               Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight());
-        logger.config("        Image depth: "+dualEyeTex.getImage().getDepth());
-        logger.config("       Image format: "+dualEyeTex.getImage().getFormat());
-        logger.config("  Image color space: "+dualEyeTex.getImage().getColorSpace());
-        
-        //setup framebuffer to use texture
-        out.setDepthBuffer(Image.Format.Depth);
-        out.setColorTexture(dualEyeTex);       
-
-        ViewPort viewPort = this.app.getViewPort();
-        viewPort.setClearFlags(true, true, true);
-        viewPort.setBackgroundColor(ColorRGBA.Black);
-        viewPort.setOutputFrameBuffer(out);
-        
-    }
-    
-    private ViewPort setupViewBuffers(Camera cam, String viewName){
-        // create offscreen framebuffer
-        FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1);
-        //offBufferLeft.setSrgb(true);
-        
-        //setup framebuffer's texture
-        Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8);
-        offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
-        offTex.setMagFilter(Texture.MagFilter.Bilinear);
-
-        //setup framebuffer to use texture
-        offBufferLeft.setDepthBuffer(Image.Format.Depth);
-        offBufferLeft.setColorTexture(offTex);        
-        
-        ViewPort viewPort = app.getRenderManager().createPreView(viewName, cam);
-        viewPort.setClearFlags(true, true, true);
-        viewPort.setBackgroundColor(ColorRGBA.Black);
-        viewPort.attachScene(this.app.getRootNode());
-        //set viewport to render to offscreen framebuffer
-        viewPort.setOutputFrameBuffer(offBufferLeft);
-        return viewPort;
-    }
-}
+/*
+ * To change this template, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package jmevr.util;
+
+import com.jme3.app.Application;
+import com.jme3.app.VRAppState;
+import com.jme3.app.VRApplication;
+import com.jme3.app.state.AppState;
+import com.jme3.input.vr.OSVR;
+import com.jme3.input.vr.OpenVR;
+import com.jme3.input.vr.VRAPI;
+import com.jme3.material.Material;
+import com.jme3.math.ColorRGBA;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.post.CartoonSSAO;
+import com.jme3.post.Filter;
+import com.jme3.post.FilterPostProcessor;
+import com.jme3.post.SceneProcessor;
+import com.jme3.post.filters.FogFilter;
+import com.jme3.post.filters.TranslucentBucketFilter;
+import com.jme3.post.ssao.SSAOFilter;
+import com.jme3.renderer.Camera;
+import com.jme3.renderer.ViewPort;
+import com.jme3.renderer.queue.RenderQueue.Bucket;
+import com.jme3.scene.Geometry;
+import com.jme3.scene.Node;
+import com.jme3.scene.Spatial;
+import com.jme3.shadow.DirectionalLightShadowFilter;
+import com.jme3.shadow.VRDirectionalLightShadowRenderer;
+import com.jme3.system.jopenvr.JOpenVRLibrary;
+import com.jme3.system.jopenvr.OpenVRUtil;
+import com.jme3.system.jopenvr.Texture_t;
+import com.jme3.system.jopenvr.VRTextureBounds_t;
+import com.jme3.system.lwjgl.LwjglWindow;
+import com.jme3.texture.FrameBuffer;
+import com.jme3.texture.Image;
+import com.jme3.texture.Texture;
+import com.jme3.texture.Texture2D;
+import com.jme3.ui.Picture;
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.PointerByReference;
+
+import java.awt.GraphicsEnvironment;
+import java.util.Iterator;
+import java.util.logging.Logger;
+
+import osvrrendermanageropengl.OSVR_RenderBufferOpenGL;
+import osvrrendermanageropengl.OSVR_ViewportDescription;
+import osvrrendermanageropengl.OsvrRenderManagerOpenGLLibrary;
+
+/**
+ * A VR view manager. This class enable to submit 3D views to the VR compositor.
+ * @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 class VRViewManager {
+
+	private static final Logger logger = Logger.getLogger(VRViewManager.class.getName());
+	
+    /**
+     * The name of the left view.
+     */
+    public final static String LEFT_VIEW_NAME = "Left View";
+    
+    /**
+     * The name of the right view.
+     */
+    public final static String RIGHT_VIEW_NAME = "Right View";
+	
+    private VRAppState app;
+    private Application application;
+    
+    private Camera leftCamera;
+    private ViewPort leftViewport;
+    private FilterPostProcessor leftPostProcessor;
+    private Texture2D leftEyeTexture;
+    private Texture2D leftEyeDepth;
+    
+    private Camera rightCamera;
+    private ViewPort rightViewport;
+    private FilterPostProcessor rightPostProcessor;
+    private Texture2D rightEyeTexture;
+    private Texture2D rightEyeDepth;
+    
+    // OpenVR values
+    private VRTextureBounds_t leftTextureBounds;
+    private Texture_t leftTextureType;
+    
+    private VRTextureBounds_t rightTextureBounds;
+    private Texture_t rightTextureType;
+    
+    // OSVR values
+    OSVR_RenderBufferOpenGL.ByValue[] osvr_renderBuffer;
+    OSVR_ViewportDescription.ByValue osvr_viewDescFull;
+    OSVR_ViewportDescription.ByValue osvr_viewDescLeft;
+    OSVR_ViewportDescription.ByValue osvr_viewDescRight;
+    Pointer osvr_rmBufferState;
+    
+    //private static boolean useCustomDistortion;
+    private float heightAdjustment;
+
+    private Texture2D dualEyeTex;
+
+    private final PointerByReference grabRBS = new PointerByReference();
+    
+    private float resMult = 1f;
+    
+    //final & temp values for camera calculations
+    private final Vector3f finalPosition   = new Vector3f();
+    private final Quaternion finalRotation = new Quaternion();
+    private final Vector3f hmdPos          = new Vector3f();
+    private final Quaternion hmdRot        = new Quaternion();
+    
+    /**
+     * Create a new VR view manager attached to the given {@link VRAppState VR app state}.<br>
+     * in order to be used, this manager has to be attached to an app state and to an application.
+     */
+    public VRViewManager(){
+    }
+    
+    /**
+     * Attach this manager to the given {@link VRAppState app state} and the given {@link Application application}.
+     * The application has to be the one that the app state is attached.
+     * This method should be called from the {@link AppState#initialize(com.jme3.app.state.AppStateManager, Application) initialize} 
+     * method of the {@link AppState} instance.
+     * @param app the {@link VRAppState VR app state} to which this manager is linked.
+     * @param application the {@link Application} which the app state is attached.
+     */
+    public void attach(VRAppState app, Application application){
+        this.app         = app;
+        this.application = application;
+    }
+    
+    /**
+     * Get the {@link Camera camera} attached to the left eye.
+     * @return the {@link Camera camera} attached to the left eye.
+     * @see #getRightCamera()
+     */
+    public Camera getLeftCamera() {
+        return leftCamera;
+    }
+    
+    /**
+     * Get the {@link Camera camera} attached to the right eye.
+     * @return the {@link Camera camera} attached to the right eye.
+     * @see #getLeftCamera()
+     */
+    public Camera getRightCamera() {
+        return rightCamera;
+    }
+    
+    /**
+     * Get the {@link ViewPort viewport} attached to the left eye.
+     * @return the {@link ViewPort viewport} attached to the left eye.
+     * @see #getRightViewport()
+     */
+    public ViewPort getLeftViewport() {
+        return leftViewport;
+    }
+    
+    /**
+     * Get the {@link ViewPort viewport} attached to the right eye.
+     * @return the {@link ViewPort viewport} attached to the right eye.
+     * @see #getLeftViewport()
+     */
+    public ViewPort getRightViewport() {
+        return rightViewport;
+    }
+    
+    /**
+     * Get the identifier of the left eye texture.
+     * @return the identifier of the left eye texture.
+     * @see #getRightTexId()
+     * @see #getFullTexId()
+     */
+    private int getLeftTexId() {
+        return (int)leftEyeTexture.getImage().getId();
+    }
+    
+    /**
+     * Get the identifier of the right eye texture.
+     * @return the identifier of the right eye texture.
+     * @see #getLeftTexId()
+     * @see #getFullTexId()
+     */
+    private int getRightTexId() {
+        return (int)rightEyeTexture.getImage().getId();
+    }
+    
+    /**
+     * Get the identifier of the full (dual eye) texture.
+     * @return the identifier of the full (dual eye) texture.
+     * @see #getLeftTexId()
+     * @see #getRightTexId()
+     */
+    private int getFullTexId() {
+        return (int)dualEyeTex.getImage().getId();
+    }
+    
+    /**
+     * Get the height adjustment to apply to the cameras before rendering.
+     * @return the height adjustment to apply to the cameras before rendering.
+     * @see #setHeightAdjustment(float)
+     */
+    public float getHeightAdjustment() {
+        return heightAdjustment;
+    }
+    
+    /**
+     * Set the height adjustment to apply to the cameras before rendering.
+     * @param amount the height adjustment to apply to the cameras before rendering.
+     * @see #getHeightAdjustment()
+     */
+    public void setHeightAdjustment(float amount) {
+        heightAdjustment = amount;
+    }
+    
+    /**
+     * Get the resolution multiplier.
+     * @return the resolution multiplier.
+     * @see #setResolutionMultiplier(float)
+     */
+    public float getResolutionMuliplier() {
+        return resMult;
+    }
+    
+    /**
+     * Set the resolution multiplier.
+     * @param resMult the resolution multiplier.
+     * @see #getResolutionMuliplier()
+     */
+    public void setResolutionMultiplier(float resMult) {
+        this.resMult = resMult;
+    }
+    
+    /**
+     * Initialize the system binds of the textures.
+     */
+    private void initTextureSubmitStructs() {
+        leftTextureType = new Texture_t();
+        rightTextureType = new Texture_t();
+        
+
+        if( app.getVRHardware() instanceof OpenVR ) {
+            leftTextureBounds = new VRTextureBounds_t();
+            rightTextureBounds = new VRTextureBounds_t();
+            // left eye
+            leftTextureBounds.uMax = 0.5f;
+            leftTextureBounds.uMin = 0f;
+            leftTextureBounds.vMax = 1f;
+            leftTextureBounds.vMin = 0f;
+            leftTextureBounds.setAutoSynch(false);
+            leftTextureBounds.setAutoRead(false);
+            leftTextureBounds.setAutoWrite(false);
+            leftTextureBounds.write();
+            // right eye
+            rightTextureBounds.uMax = 1f;
+            rightTextureBounds.uMin = 0.5f;
+            rightTextureBounds.vMax = 1f;
+            rightTextureBounds.vMin = 0f;
+            rightTextureBounds.setAutoSynch(false);
+            rightTextureBounds.setAutoRead(false);
+            rightTextureBounds.setAutoWrite(false);
+            rightTextureBounds.write();
+            // texture type
+            // FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
+            leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma;
+            //leftTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
+            leftTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL;
+            leftTextureType.setAutoSynch(false);
+            leftTextureType.setAutoRead(false);
+            leftTextureType.setAutoWrite(false);
+            leftTextureType.handle = -1;
+            // FIXME: Synchronize with JMonkey given texture (at this time is linear but was Gamma with phr00t implementation)
+            rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Gamma;
+            //rightTextureType.eColorSpace = JOpenVRLibrary.EColorSpace.EColorSpace_ColorSpace_Linear;
+            rightTextureType.eType = JOpenVRLibrary.ETextureType.ETextureType_TextureType_OpenGL;
+            rightTextureType.setAutoSynch(false);
+            rightTextureType.setAutoRead(false);
+            rightTextureType.setAutoWrite(false);
+            rightTextureType.handle = -1;
+            
+            
+            logger.config("Init eyes native texture binds");
+            logger.config("  Left eye texture");
+            logger.config("           address: "+leftTextureType.getPointer());
+            logger.config("              size: "+leftTextureType.size()+" bytes");
+            logger.config("       color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace));
+            logger.config("              type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType));
+            logger.config("         auto read: "+leftTextureType.getAutoRead());
+            logger.config("        auto write: "+leftTextureType.getAutoWrite());
+            logger.config("    handle address: "+leftTextureType.handle);
+            logger.config("      handle value: "+leftTextureType.handle);
+            logger.config("");
+            logger.config("  Right eye texture");
+            logger.config("           address: "+rightTextureType.getPointer());
+            logger.config("              size: "+rightTextureType.size()+" bytes");
+            logger.config("       color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace));
+            logger.config("              type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType));
+            logger.config("         auto read: "+rightTextureType.getAutoRead());
+            logger.config("        auto write: "+rightTextureType.getAutoWrite());
+            logger.config("    handle address: "+rightTextureType.handle);
+            logger.config("      handle value: "+rightTextureType.handle);
+            
+            
+        } else if( app.getVRHardware() instanceof OSVR ) {
+            // must be OSVR
+            osvr_renderBuffer = new OSVR_RenderBufferOpenGL.ByValue[2];
+            osvr_renderBuffer[OSVR.EYE_LEFT] = new OSVR_RenderBufferOpenGL.ByValue();
+            osvr_renderBuffer[OSVR.EYE_RIGHT] = new OSVR_RenderBufferOpenGL.ByValue();
+            osvr_renderBuffer[OSVR.EYE_LEFT].setAutoSynch(false);
+            osvr_renderBuffer[OSVR.EYE_RIGHT].setAutoSynch(false);
+            osvr_viewDescFull = new OSVR_ViewportDescription.ByValue();
+            osvr_viewDescFull.setAutoSynch(false);
+            osvr_viewDescFull.left = osvr_viewDescFull.lower = 0.0;
+            osvr_viewDescFull.width = osvr_viewDescFull.height = 1.0;    
+            osvr_viewDescLeft = new OSVR_ViewportDescription.ByValue();
+            osvr_viewDescLeft.setAutoSynch(false);
+            osvr_viewDescLeft.left = osvr_viewDescLeft.lower = 0.0;
+            osvr_viewDescLeft.width = 0.5;
+            osvr_viewDescLeft.height = 1.0;    
+            osvr_viewDescRight = new OSVR_ViewportDescription.ByValue();
+            osvr_viewDescRight.setAutoSynch(false);
+            osvr_viewDescRight.left = 0.5;
+            osvr_viewDescRight.lower = 0.0;
+            osvr_viewDescRight.width = 0.5;
+            osvr_viewDescRight.height = 1.0;
+            osvr_viewDescRight.write();
+            osvr_viewDescLeft.write();
+            osvr_viewDescFull.write();
+            osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = -1;
+            osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = -1;
+            osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = -1;
+            osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = -1;
+        }
+    }
+
+    /**
+     * Register the OSVR OpenGL buffer.
+     * @param buf the OSVR OpenGL buffer.
+     */
+    private void registerOSVRBuffer(OSVR_RenderBufferOpenGL.ByValue buf) {
+        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerStartRegisterRenderBuffers(grabRBS);
+        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerRegisterRenderBufferOpenGL(grabRBS.getValue(), buf);
+        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerFinishRegisterRenderBuffers(((OSVR)app.getVRHardware()).getCompositor(), grabRBS.getValue(), (byte)0);
+    }
+    
+    /**
+     * Send the textures to the two eyes.
+     */
+    public void sendTextures() {
+        if( app.isInVR() ) {
+            VRAPI api = app.getVRHardware();
+            if( api.getCompositor() != null ) {
+                // using the compositor...
+                int errl = 0, errr = 0;
+                if( app.isInstanceVRRendering() ) {
+                    if( leftTextureType.handle == -1 || leftTextureType.handle != getFullTexId() ) {
+                    	leftTextureType.handle = getFullTexId();
+                        if( leftTextureType.handle != -1 ) {
+                            leftTextureType.write();
+                            if( api instanceof OSVR ) {
+                                osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle;
+                                osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = dualEyeTex.getImage().getId();
+                                osvr_renderBuffer[OSVR.EYE_LEFT].write();
+                                registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]);
+                            }
+                        }
+                    } else {
+                        if( api instanceof OpenVR ) {
+                            int submitFlag = JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default;
+                            errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, leftTextureType, rightTextureBounds, submitFlag);
+                            errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, leftTextureBounds, submitFlag);
+                        } else if( api instanceof OSVR ) {
+                            ((OSVR)api).handleRenderBufferPresent(osvr_viewDescLeft, osvr_viewDescRight,
+                                                                  osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_LEFT]);
+                        }
+                    }
+                } else if( leftTextureType.handle == -1 || rightTextureType.handle == -1 ||
+                           leftTextureType.handle != getLeftTexId() || rightTextureType.handle != getRightTexId() ) {
+                    leftTextureType.handle = getLeftTexId();
+                    if( leftTextureType.handle != -1 ) {
+                    	logger.fine("Writing Left texture to native memory at " + leftTextureType.getPointer());
+                        leftTextureType.write();
+                        if( api instanceof OSVR ) {
+                            osvr_renderBuffer[OSVR.EYE_LEFT].colorBufferName = leftTextureType.handle;
+                            if( leftEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_LEFT].depthStencilBufferName = leftEyeDepth.getImage().getId();
+                            osvr_renderBuffer[OSVR.EYE_LEFT].write();
+                            registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_LEFT]);
+                        }
+                    }
+                    rightTextureType.handle = getRightTexId();
+                    if( rightTextureType.handle != -1 ) {
+                    	logger.fine("Writing Right texture to native memory at " + leftTextureType.getPointer());
+                        rightTextureType.write();
+                        if( api instanceof OSVR ) {
+                            osvr_renderBuffer[OSVR.EYE_RIGHT].colorBufferName = rightTextureType.handle;
+                            if( rightEyeDepth != null ) osvr_renderBuffer[OSVR.EYE_RIGHT].depthStencilBufferName = rightEyeDepth.getImage().getId();
+                            osvr_renderBuffer[OSVR.EYE_RIGHT].write();
+                            registerOSVRBuffer(osvr_renderBuffer[OSVR.EYE_RIGHT]);
+                        }
+                    }                    
+                } else {
+                    if( api instanceof OpenVR ) {
+                        errl = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, leftTextureType, null,
+                                                               JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default);
+                        errr = ((OpenVR)api).getCompositor().Submit.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, rightTextureType, null,
+                                                               JOpenVRLibrary.EVRSubmitFlags.EVRSubmitFlags_Submit_Default);
+                    } else if( api instanceof OSVR ) {
+                        ((OSVR)api).handleRenderBufferPresent(osvr_viewDescFull, osvr_viewDescFull,
+                                                              osvr_renderBuffer[OSVR.EYE_LEFT], osvr_renderBuffer[OSVR.EYE_RIGHT]);
+                    }
+                }
+                
+                if( errl != 0 ){
+                	logger.severe("Submit to left compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")");
+                	logger.severe("  Texture color space: "+OpenVRUtil.getEColorSpaceString(leftTextureType.eColorSpace));
+                	logger.severe("  Texture type: "+OpenVRUtil.getETextureTypeString(leftTextureType.eType));
+                	logger.severe("  Texture handle: "+leftTextureType.handle);
+                	
+                    logger.severe("  Left eye texture "+leftEyeTexture.getName()+" ("+leftEyeTexture.getImage().getId()+")");
+                    logger.severe("                 Type: "+leftEyeTexture.getType());
+                    logger.severe("                 Size: "+leftEyeTexture.getImage().getWidth()+"x"+leftEyeTexture.getImage().getHeight());
+                    logger.severe("          Image depth: "+leftEyeTexture.getImage().getDepth());
+                    logger.severe("         Image format: "+leftEyeTexture.getImage().getFormat());
+                    logger.severe("    Image color space: "+leftEyeTexture.getImage().getColorSpace());
+                	
+                }
+                
+                if( errr != 0 ){
+                	logger.severe("Submit to right compositor error: " + OpenVRUtil.getEVRCompositorErrorString(errl)+" ("+Integer.toString(errl)+")");
+                	logger.severe("  Texture color space: "+OpenVRUtil.getEColorSpaceString(rightTextureType.eColorSpace));
+                	logger.severe("  Texture type: "+OpenVRUtil.getETextureTypeString(rightTextureType.eType));
+                	logger.severe("  Texture handle: "+rightTextureType.handle);
+                	
+                    logger.severe("  Right eye texture "+rightEyeTexture.getName()+" ("+rightEyeTexture.getImage().getId()+")");
+                    logger.severe("                 Type: "+rightEyeTexture.getType());
+                    logger.severe("                 Size: "+rightEyeTexture.getImage().getWidth()+"x"+rightEyeTexture.getImage().getHeight());
+                    logger.severe("          Image depth: "+rightEyeTexture.getImage().getDepth());
+                    logger.severe("         Image format: "+rightEyeTexture.getImage().getFormat());
+                    logger.severe("    Image color space: "+rightEyeTexture.getImage().getColorSpace());
+                }
+            }
+        }                
+    }
+
+
+    /**
+     * Initialize the VR view manager.
+     */
+    public void initialize() {     
+    	
+    	logger.config("Initializing VR view manager.");
+    	
+        initTextureSubmitStructs();
+        setupCamerasAndViews();        
+        setupVRScene();                    
+        moveScreenProcessingToEyes();       
+        if( app.hasTraditionalGUIOverlay() ) {
+        	
+            app.getVRMouseManager().init();
+            
+            // update the pose to position the gui correctly on start
+            update(0f);
+            app.getVRGUIManager().positionGui();
+        }       
+        // if we are OSVR, our primary mirror window needs to be the same size as the render manager's output...
+        if( app.getVRHardware() instanceof OSVR ) {
+            int origWidth = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getWidth();
+            int origHeight = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().getDisplayMode().getHeight();
+            long window = ((LwjglWindow)application.getContext()).getWindowHandle();
+            Vector2f windowSize = new Vector2f();
+            ((OSVR)app.getVRHardware()).getRenderSize(windowSize);
+            windowSize.x = Math.max(windowSize.x * 2f, leftCamera.getWidth());
+            org.lwjgl.glfw.GLFW.glfwSetWindowSize(window, (int)windowSize.x, (int)windowSize.y);
+            application.getContext().getSettings().setResolution((int)windowSize.x, (int)windowSize.y);
+            
+            if (application.getRenderManager() != null) {
+            	application.getRenderManager().notifyReshape((int)windowSize.x, (int)windowSize.y);
+            }
+                   
+            org.lwjgl.glfw.GLFW.glfwSetWindowPos(window, origWidth - (int)windowSize.x, 32);
+            
+            org.lwjgl.glfw.GLFW.glfwFocusWindow(window);
+            
+            org.lwjgl.glfw.GLFW.glfwSetCursorPos(window, origWidth / 2.0, origHeight / 2.0);
+        }  
+        
+        logger.config("Initialized VR view manager [SUCCESS]");
+    }
+    
+    /**
+     * Prepare the size of the given {@link Camera camera} to adapt it to the underlying rendering context.
+     * @param cam the {@link Camera camera} to prepare.
+     * @param xMult the camera width multiplier.
+     */
+    private void prepareCameraSize(Camera cam, float xMult) {
+        Vector2f size = new Vector2f();
+        VRAPI vrhmd = app.getVRHardware();
+
+        if( vrhmd == null ) {
+            size.x = 1280f;
+            size.y = 720f;
+        } else {
+            vrhmd.getRenderSize(size);
+        }
+        
+        if( size.x < application.getContext().getSettings().getWidth() ) {
+            size.x = application.getContext().getSettings().getWidth();
+        }
+        if( size.y < application.getContext().getSettings().getHeight() ) {
+            size.y = application.getContext().getSettings().getHeight();
+        }
+        
+        if( app.isInstanceVRRendering() ) size.x *= 2f;
+        
+        // other adjustments
+        size.x *= xMult;
+        size.x *= resMult;
+        size.y *= resMult;
+        
+        if( cam.getWidth() != size.x || cam.getHeight() != size.y ) cam.resize((int)size.x, (int)size.y, false);
+    }
+    
+    /**
+     * Replaces rootNode as the main cameras scene with the distortion mesh
+     */
+    private void setupVRScene(){
+        // no special scene to setup if we are doing instancing
+        if( app.isInstanceVRRendering() ) {
+            // distortion has to be done with compositor here... we want only one pass on our end!
+            if( application.getContext().getSettings().isSwapBuffers() ) {
+                setupMirrorBuffers(app.getCamera(), dualEyeTex, true);
+            }       
+            return;
+        }
+        
+        leftEyeTexture  = (Texture2D) leftViewport.getOutputFrameBuffer().getColorBuffer().getTexture();
+        rightEyeTexture = (Texture2D)rightViewport.getOutputFrameBuffer().getColorBuffer().getTexture();        
+        leftEyeDepth    = (Texture2D) leftViewport.getOutputFrameBuffer().getDepthBuffer().getTexture();
+        rightEyeDepth   = (Texture2D)rightViewport.getOutputFrameBuffer().getDepthBuffer().getTexture();        
+      
+        // main viewport is either going to be a distortion scene or nothing
+        // mirroring is handled by copying framebuffers
+        Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator();
+        while(spatialIter.hasNext()){
+        	application.getViewPort().detachScene(spatialIter.next());
+        }
+        
+        spatialIter = application.getGuiViewPort().getScenes().iterator();
+        while(spatialIter.hasNext()){
+        	application.getGuiViewPort().detachScene(spatialIter.next());
+        }
+        
+        // only setup distortion scene if compositor isn't running (or using custom mesh distortion option)
+        if( app.getVRHardware().getCompositor() == null ) {
+            Node distortionScene = new Node();
+            Material leftMat = new Material(application.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md");
+            leftMat.setTexture("Texture", leftEyeTexture);
+            Geometry leftEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Left, app.getVRHardware()));
+            leftEye.setMaterial(leftMat);
+            distortionScene.attachChild(leftEye);
+
+            Material rightMat = new Material(application.getAssetManager(), "Common/MatDefs/VR/OpenVR.j3md");
+            rightMat.setTexture("Texture", rightEyeTexture);
+            Geometry rightEye = new Geometry("box", MeshUtil.setupDistortionMesh(JOpenVRLibrary.EVREye.EVREye_Eye_Right, app.getVRHardware()));
+            rightEye.setMaterial(rightMat);
+            distortionScene.attachChild(rightEye);
+
+            distortionScene.updateGeometricState();
+
+            application.getViewPort().attachScene(distortionScene);
+            
+            //if( useCustomDistortion ) setupFinalFullTexture(app.getViewPort().getCamera());
+        }
+        
+        if( application.getContext().getSettings().isSwapBuffers() ) {
+            setupMirrorBuffers(app.getCamera(), leftEyeTexture, false);
+        }       
+    }
+    
+    /**
+     * Update the VR view manager. 
+     * This method is called by the attached {@link VRApplication VR application} and should not be called manually.
+     * @param tpf the time per frame.
+     */
+    public void update(float tpf) {
+        
+        // grab the observer
+        Object obs = app.getObserver();
+        Quaternion objRot;
+        Vector3f objPos;
+        if( obs instanceof Camera ) {
+            objRot = ((Camera)obs).getRotation();
+            objPos = ((Camera)obs).getLocation();
+        } else {
+            objRot = ((Spatial)obs).getWorldRotation();
+            objPos = ((Spatial)obs).getWorldTranslation();
+        }
+        // grab the hardware handle
+        VRAPI dev = app.getVRHardware();
+        if( dev != null ) {
+            // update the HMD's position & orientation
+            dev.updatePose();
+            dev.getPositionAndOrientation(hmdPos, hmdRot);
+            if( obs != null ) {
+                // update hmdPos based on obs rotation
+                finalRotation.set(objRot);
+                finalRotation.mult(hmdPos, hmdPos);
+                finalRotation.multLocal(hmdRot);
+            }
+            
+            finalizeCamera(dev.getHMDVectorPoseLeftEye(), objPos, leftCamera);
+            finalizeCamera(dev.getHMDVectorPoseRightEye(), objPos, rightCamera);
+        } else {
+            leftCamera.setFrame(objPos, objRot);
+            rightCamera.setFrame(objPos, objRot);
+        }
+        
+        if( app.hasTraditionalGUIOverlay() ) {
+            // update the mouse?
+        	app.getVRMouseManager().update(tpf);
+        
+            // update GUI position?
+            if( app.getVRGUIManager().wantsReposition || app.getVRGUIManager().getPositioningMode() != VRGuiManager.POSITIONING_MODE.MANUAL ) {
+            	app.getVRGUIManager().positionGuiNow(tpf);
+            	app.getVRGUIManager().updateGuiQuadGeometricState();
+            }
+        }
+    }
+    
+    /**
+     * Place the camera within the scene.
+     * @param eyePos the eye position.
+     * @param obsPosition the observer position.
+     * @param cam the camera to place.
+     */
+    private void finalizeCamera(Vector3f eyePos, Vector3f obsPosition, Camera cam) {
+        finalRotation.mult(eyePos, finalPosition);
+        finalPosition.addLocal(hmdPos);
+        if( obsPosition != null ) finalPosition.addLocal(obsPosition);
+        finalPosition.y += heightAdjustment;
+        cam.setFrame(finalPosition, finalRotation);
+    }
+    
+    /**
+     * Handles moving filters from the main view to each eye
+     */
+    public void moveScreenProcessingToEyes() {
+        if( rightViewport == null ) return;
+        syncScreenProcessing(application.getViewPort());
+        application.getViewPort().clearProcessors();
+    }
+    
+    /**
+     * Sets the two views to use the list of {@link SceneProcessor processors}.
+     * @param sourceViewport the {@link ViewPort viewport} that contains the processors to use.
+     */
+    public void syncScreenProcessing(ViewPort sourceViewport) {
+        if( rightViewport == null ) return;
+        // setup post processing filters
+        if( rightPostProcessor == null ) {
+            rightPostProcessor = new FilterPostProcessor(application.getAssetManager());               
+            leftPostProcessor =  new FilterPostProcessor(application.getAssetManager());
+        }
+        // clear out all filters & processors, to start from scratch
+        rightPostProcessor.removeAllFilters();
+        leftPostProcessor.removeAllFilters();
+        leftViewport.clearProcessors();
+        rightViewport.clearProcessors();
+        // if we have no processors to sync, don't add the FilterPostProcessor
+        if( sourceViewport.getProcessors().isEmpty() ) return;
+        // add post processors we just made, which are empty
+        leftViewport.addProcessor(leftPostProcessor);
+        rightViewport.addProcessor(rightPostProcessor);
+        // go through all of the filters in the processors list
+        // add them to the left viewport processor & clone them to the right
+        for(SceneProcessor sceneProcessor : sourceViewport.getProcessors()) {
+            if (sceneProcessor instanceof FilterPostProcessor) {
+                for(Filter f : ((FilterPostProcessor)sceneProcessor).getFilterList() ) {
+                    if( f instanceof TranslucentBucketFilter ) {
+                        // just remove this filter, we will add it at the end manually
+                        ((FilterPostProcessor)sceneProcessor).removeFilter(f);
+                    } else {
+                        leftPostProcessor.addFilter(f);
+                        // clone to the right
+                        Filter f2;
+                        if(f instanceof FogFilter){
+                            f2 = FilterUtil.cloneFogFilter((FogFilter)f); 
+                        } else if (f instanceof CartoonSSAO ) {
+                            f2 = new CartoonSSAO((CartoonSSAO)f);
+                        } else if (f instanceof SSAOFilter){
+                            f2 = FilterUtil.cloneSSAOFilter((SSAOFilter)f);
+                        } else if (f instanceof DirectionalLightShadowFilter){
+                            f2 = FilterUtil.cloneDirectionalLightShadowFilter(application.getAssetManager(), (DirectionalLightShadowFilter)f);
+                        } else {
+                            f2 = f; // dof, bloom, lightscattering etc.
+                        }                    
+                        rightPostProcessor.addFilter(f2);
+                    }
+                }
+            } else if (sceneProcessor instanceof VRDirectionalLightShadowRenderer) {
+                // shadow processing
+                // TODO: make right shadow processor use same left shadow maps for performance
+                VRDirectionalLightShadowRenderer dlsr = (VRDirectionalLightShadowRenderer) sceneProcessor;
+                VRDirectionalLightShadowRenderer dlsrRight = dlsr.clone();
+                dlsrRight.setLight(dlsr.getLight());
+                rightViewport.getProcessors().add(0, dlsrRight);
+                leftViewport.getProcessors().add(0, sceneProcessor);
+            }
+        }
+        // make sure each has a translucent filter renderer
+        leftPostProcessor.addFilter(new TranslucentBucketFilter());
+        rightPostProcessor.addFilter(new TranslucentBucketFilter());
+    }
+    
+    private void setupCamerasAndViews() {        
+        // get desired frustrum from original camera
+        Camera origCam = app.getCamera();        
+        float fFar = origCam.getFrustumFar();
+        float fNear = origCam.getFrustumNear();
+        
+        // if we are using OSVR get the eye info here
+        if( app.getVRHardware() instanceof OSVR ) {
+            ((OSVR)app.getVRHardware()).getEyeInfo();
+        }
+        
+        // restore frustrum on distortion scene cam, if needed
+        if( app.isInstanceVRRendering() ) {
+            leftCamera = origCam;
+        } else if( app.compositorAllowed() == false ) {
+            origCam.setFrustumFar(100f);
+            origCam.setFrustumNear(1f); 
+            leftCamera = origCam.clone();  
+            prepareCameraSize(origCam, 2f);
+        } else {
+            leftCamera = origCam.clone();
+        }
+        
+        leftCamera.setFrustumPerspective(app.getDefaultFOV(), app.getDefaultAspect(), fNear, fFar);                     
+                
+        prepareCameraSize(leftCamera, 1f);
+        if( app.getVRHardware() != null ) leftCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionLeftEye(leftCamera));
+        //org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_FRAMEBUFFER_SRGB);
+        
+        if( !app.isInstanceVRRendering()) {
+            leftViewport = setupViewBuffers(leftCamera, LEFT_VIEW_NAME);
+            rightCamera = leftCamera.clone();
+            if( app.getVRHardware() != null ){
+            	rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera));
+            }
+            rightViewport = setupViewBuffers(rightCamera, RIGHT_VIEW_NAME);
+        } else {
+        	
+        	System.err.println("[VRViewManager] THIS CODE NEED CHANGES !!!");
+            leftViewport = application.getViewPort();
+            //leftViewport.attachScene(app.getRootNode());
+            rightCamera = leftCamera.clone();
+            if( app.getVRHardware() != null ){
+            	rightCamera.setProjectionMatrix(app.getVRHardware().getHMDMatrixProjectionRightEye(rightCamera));
+            }
+            
+            org.lwjgl.opengl.GL11.glEnable(org.lwjgl.opengl.GL30.GL_CLIP_DISTANCE0);
+            
+            //FIXME: [jme-vr] Fix with JMonkey next release
+            //RenderManager._VRInstancing_RightCamProjection = camRight.getViewProjectionMatrix();
+            setupFinalFullTexture(application.getViewPort().getCamera());            
+        }
+        
+        // setup gui
+        app.getVRGUIManager().setupGui(leftCamera, rightCamera, leftViewport, rightViewport);
+        
+        if( app.getVRHardware() != null ) {
+            // call these to cache the results internally
+        	app.getVRHardware().getHMDMatrixPoseLeftEye();
+        	app.getVRHardware().getHMDMatrixPoseRightEye();
+        }
+
+    }
+    
+    private ViewPort setupMirrorBuffers(Camera cam, Texture tex, boolean expand) {        
+        Camera clonecam = cam.clone();
+        ViewPort viewPort = application.getRenderManager().createPostView("MirrorView", clonecam);
+        clonecam.setParallelProjection(true);
+        viewPort.setClearFlags(true, true, true);
+        viewPort.setBackgroundColor(ColorRGBA.Black);
+        Picture pic = new Picture("fullscene");
+        pic.setLocalTranslation(-0.75f, -0.5f, 0f);
+        if( expand ) {
+            pic.setLocalScale(3f, 1f, 1f);
+        } else {
+            pic.setLocalScale(1.5f, 1f, 1f);            
+        }
+        pic.setQueueBucket(Bucket.Opaque);
+        pic.setTexture(application.getAssetManager(), (Texture2D)tex, false);
+        viewPort.attachScene(pic);
+        viewPort.setOutputFrameBuffer(null);
+        
+        pic.updateGeometricState();
+        
+        return viewPort;
+    }
+    
+    private void setupFinalFullTexture(Camera cam) {
+        // create offscreen framebuffer
+        FrameBuffer out = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1);
+        //offBuffer.setSrgb(true);
+
+        //setup framebuffer's texture
+        dualEyeTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8);
+        dualEyeTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
+        dualEyeTex.setMagFilter(Texture.MagFilter.Bilinear);
+
+        logger.config("Dual eye texture "+dualEyeTex.getName()+" ("+dualEyeTex.getImage().getId()+")");
+        logger.config("               Type: "+dualEyeTex.getType());
+        logger.config("               Size: "+dualEyeTex.getImage().getWidth()+"x"+dualEyeTex.getImage().getHeight());
+        logger.config("        Image depth: "+dualEyeTex.getImage().getDepth());
+        logger.config("       Image format: "+dualEyeTex.getImage().getFormat());
+        logger.config("  Image color space: "+dualEyeTex.getImage().getColorSpace());
+        
+        //setup framebuffer to use texture
+        out.setDepthBuffer(Image.Format.Depth);
+        out.setColorTexture(dualEyeTex);       
+
+        ViewPort viewPort = application.getViewPort();
+        viewPort.setClearFlags(true, true, true);
+        viewPort.setBackgroundColor(ColorRGBA.Black);
+        viewPort.setOutputFrameBuffer(out);
+        
+    }
+    
+    private ViewPort setupViewBuffers(Camera cam, String viewName){
+        // create offscreen framebuffer
+        FrameBuffer offBufferLeft = new FrameBuffer(cam.getWidth(), cam.getHeight(), 1);
+        //offBufferLeft.setSrgb(true);
+        
+        //setup framebuffer's texture
+        Texture2D offTex = new Texture2D(cam.getWidth(), cam.getHeight(), Image.Format.RGBA8);
+        offTex.setMinFilter(Texture.MinFilter.BilinearNoMipMaps);
+        offTex.setMagFilter(Texture.MagFilter.Bilinear);
+
+        //setup framebuffer to use texture
+        offBufferLeft.setDepthBuffer(Image.Format.Depth);
+        offBufferLeft.setColorTexture(offTex);        
+        
+        ViewPort viewPort = application.getRenderManager().createPreView(viewName, cam);
+        viewPort.setClearFlags(true, true, true);
+        viewPort.setBackgroundColor(ColorRGBA.Black);
+        
+        Iterator<Spatial> spatialIter = application.getViewPort().getScenes().iterator();
+        while(spatialIter.hasNext()){
+        	viewPort.attachScene(spatialIter.next());
+        }
+
+        //set viewport to render to offscreen framebuffer
+        viewPort.setOutputFrameBuffer(offBufferLeft);
+        return viewPort;
+    }
+}