diff --git a/jme3-core/src/main/java/com/jme3/scene/Spatial.java b/jme3-core/src/main/java/com/jme3/scene/Spatial.java
index bbb795fdf..10b96aa7f 100644
--- a/jme3-core/src/main/java/com/jme3/scene/Spatial.java
+++ b/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;
}
/**
diff --git a/jme3-vr/src/main/java/com/jme3/app/VRAppState.java b/jme3-vr/src/main/java/com/jme3/app/VRAppState.java
new file mode 100644
index 000000000..1c91977f3
--- /dev/null
+++ b/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.
+ * 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:
+ *
+ * - To create {@link AppSettings application settings} and set the VR related settings (see {@link VRConstants}).
+ *
- To instantiate this app state with the created settings.
+ *
- To instantiate the main {@link Application application} and to attach it to the created settings (with {@link Application#setSettings(AppSettings) setSettings(AppSettings)}).
+ *
- To start the main {@link Application application}.
+ *
+ * Attaching an instance of this app state to an already started application may cause crashes.
+ * @author Julien Seinturier - JOrigin project - http:/www.jorigin.org
+ */
+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 false
).
+ */
+ 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 true
if the VR compositor is active and false
otherwise.
+ */
+ public boolean compositorAllowed() {
+ return useCompositor && compositorOS;
+ }
+
+ /**
+ * Get if the system currently support VR.
+ * @return true
if the system currently support VR and false
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 true
if designed for sitting, false
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 true
if the application is configured as a seated experience and false
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 Geometry instancing).
+ * @return true
if the rendering is instanced and false
otherwise.
+ */
+ public boolean isInstanceVRRendering() {
+ return instanceVR && isInVR();
+ }
+
+ /**
+ * Check if the VR mode is enabled.
+ * @return true
if the VR mode is enabled and false
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 true
if the application has a GUI overlay attached and false
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 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);
+ }
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java b/jme3-vr/src/main/java/com/jme3/app/VRApplication.java
index 0113dac92..f4fffbb89 100644
--- a/jme3-vr/src/main/java/com/jme3/app/VRApplication.java
+++ b/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);
diff --git a/jme3-vr/src/main/java/com/jme3/app/VRConstants.java b/jme3-vr/src/main/java/com/jme3/app/VRConstants.java
new file mode 100644
index 000000000..6e23ec2bf
--- /dev/null
+++ b/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 - http:/www.jorigin.org
+ * @since 3.1.0
+ */
+public class VRConstants {
+
+ /**
+ * An AppSettings parameter that set if the VR compositor has to be used.
+ *
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_USE_COMPOSITOR, value)
+ */
+ 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).
+ *
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VR_FORCE, value)
+
+ */
+ public static final String SETTING_VR_FORCE = "VRForce";
+
+ /**
+ * An AppSettings parameter that set to invert the eyes of the HMD.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_FLIP_EYES, value)
+ */
+ 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.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_OVERDRAW, value)
+ *
+ */
+ public static final String SETTING_GUI_OVERDRAW = "VRGUIOverdraw";
+
+ /**
+ * An AppSettings parameter that set if the GUI surface has to be curved.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_GUI_CURVED_SURFACE, value)
+ */
+ 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 false
.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_ENABLE_MIRROR_WINDOW, value)
+ */
+ 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.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_VR, value)
+ */
+ public static final String SETTING_DISABLE_VR = "VRDisable";
+
+
+ /**
+ * An AppSettings parameter that set if the VR user is seated.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_SEATED_EXPERIENCE, value)
+ */
+ public static final String SETTING_SEATED_EXPERIENCE = "VRSeatedExperience";
+
+ /**
+ * An AppSettings parameter that set if the GUI has to be ignored.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_NO_GUI, value)
+ */
+ 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).
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_INSTANCE_RENDERING, value)
+ */
+ public static final String SETTING_INSTANCE_RENDERING = "VRInstanceRendering";
+
+ /**
+ * An AppSettings parameter that set if Multi Sample Anti Aliasing has to be enabled.
+ * Type: boolean
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DISABLE_MSAA, value)
+ */
+ public static final String SETTING_DISABLE_MSAA = "VRDisableMSAA";
+
+ /**
+ * An AppSettings parameter that set the default field of view (FOV) value.
+ * Type: float
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_FOV, value)
+ */
+ public static final String SETTING_DEFAULT_FOV = "VRDefaultFOV";
+
+ /**
+ * An AppSettings parameter that set the default aspect ratio.
+ * Type: float
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_DEFAULT_ASPECT_RATIO, value)
+ */
+ public static final String SETTING_DEFAULT_ASPECT_RATIO = "VRDefaultAspectRatio";
+
+ /**
+ * An AppSettings parameter that specifies the underlying VR API. Possible values are:
+ *
+ * - {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE SETTING_VRAPI_OPENVR_VALUE}: Use OpenVR binding.
+ *
- {@link VRConstants#SETTING_VRAPI_OSVR_VALUE SETTING_VRAPI_OSVR_VALUE}: Use OSVR binding.
+ *
- {@link VRConstants#SETTING_VRAPI_OPENVR_LWJGL_VALUE SETTING_VRAPI_OPENVR_LWJGL_VALUE}: Use OpenVR binding from LWJGL.
+ *
+ * Type: int
+ * Usage: {@link AppSettings appSettings}.{@link HashMap#put(Object, Object) put}(VRConstants.SETTING_VRAPI, value)
+
+ */
+ 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;
+
+
+
+}
diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OSVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OSVR.java
index 47ed23327..515e57d17 100644
--- a/jme3-vr/src/main/java/com/jme3/input/vr/OSVR.java
+++ b/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 OSVR system attached to the given application.
- * @param application the application to which the input is attached.
+ * Create a new OSVR 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;
}
}
diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OSVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OSVRInput.java
index 23f51655b..85fc9a931 100644
--- a/jme3-vr/src/main/java/com/jme3/input/vr/OSVRInput.java
+++ b/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 OSVR input attached to the given application.
- * @param application the application to which the input is attached.
+ * Create a new OSVR 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;
}
diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java
index 35e16285c..3e7ff985d 100644
--- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java
+++ b/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 OpenVR system attached to the given application.
- * @param application the application to which the input is attached.
+ * Create a new OpenVR 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;
}
}
diff --git a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java b/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java
index e2df488ea..cb432b3da 100644
--- a/jme3-vr/src/main/java/com/jme3/input/vr/OpenVRInput.java
+++ b/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 trackedControllers = null;
@@ -105,8 +106,8 @@ public class OpenVRInput implements VRInputAPI {
* Create a new OpenVR 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 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 spatialIter = application.getGuiViewPort().getScenes().iterator();
+ while(spatialIter.hasNext()){
+ offView.attachScene(spatialIter.next());
+ }
+
if( useCurvedSurface ) {
guiQuad = (Geometry)application.getAssetManager().loadModel("Common/Util/gui_mesh.j3o");
diff --git a/jme3-vr/src/main/java/jmevr/util/VRMouseManager.java b/jme3-vr/src/main/java/jmevr/util/VRMouseManager.java
index 38fc705eb..5dbcd04ac 100644
--- a/jme3-vr/src/main/java/jmevr/util/VRMouseManager.java
+++ b/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();
+ }
}
}
}
diff --git a/jme3-vr/src/main/java/jmevr/util/VRViewManager.java b/jme3-vr/src/main/java/jmevr/util/VRViewManager.java
index 4558c950a..4878a42ab 100644
--- a/jme3-vr/src/main/java/jmevr/util/VRViewManager.java
+++ b/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 - http:/www.jorigin.org
- */
-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 - http:/www.jorigin.org
+ */
+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}.
+ * 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 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 spatialIter = application.getViewPort().getScenes().iterator();
+ while(spatialIter.hasNext()){
+ viewPort.attachScene(spatialIter.next());
+ }
+
+ //set viewport to render to offscreen framebuffer
+ viewPort.setOutputFrameBuffer(offBufferLeft);
+ return viewPort;
+ }
+}