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:
*
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