package com.jme3.app;
import java.util.Locale;
import java.util.logging.Level;
import java.util.logging.Logger;
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.input.vr.VRInputAPI;
import com.jme3.renderer.Camera;
import com.jme3.scene.Spatial;
import com.jme3.system.AppSettings;
import com.jme3.system.jopenvr.JOpenVRLibrary;
import com.jme3.util.VRGuiManager;
import com.jme3.util.VRMouseManager;
import com.jme3.util.VRViewManager;
import com.jme3.util.VRViewManagerOSVR;
import com.jme3.util.VRViewManagerOpenVR;
public class VREnvironment {
private static final Logger logger = Logger.getLogger(VREnvironment.class.getName());
private VRAPI hardware = null;
private VRGuiManager guiManager = null;
private VRMouseManager mouseManager = null;
private VRViewManager viewmanager = null;
/**
* The underlying system VR API. By default set to {@link VRConstants#SETTING_VRAPI_OPENVR_VALUE}.
*/
public int vrBinding = VRConstants.SETTING_VRAPI_OPENVR_VALUE;
private boolean seated = false;
private Spatial observer = null;
private boolean forceVR = false;
private boolean vrSupportedOS = false;
private boolean nogui = false;
private boolean compositorOS;
private boolean useCompositor = true;
private boolean instanceRendering = false;
private boolean disableSwapBuffers = true;
private float defaultFOV = 108f;
private float defaultAspect = 1f;
private AppSettings settings = null;
private Application application = null;
private Camera dummyCam = null;
private AppState app = null;
private boolean initialized = false;
private boolean attached = false;
public VREnvironment(AppSettings settings){
this.settings = settings;
guiManager = new VRGuiManager(this);
mouseManager = new VRMouseManager(this);
// dummyCam = new Camera(settings.getWidth(), settings.getHeight());
processSettings();
}
/**
* Get the VR underlying hardware.
* @return the VR underlying hardware.
*/
public VRAPI getVRHardware() {
return hardware;
}
/**
* Get the VR dedicated input.
* @return the VR dedicated input.
*/
public VRInputAPI getVRinput() {
if( hardware == null ){
return null;
}
return hardware.getVRinput();
}
/**
* Get the VR view manager.
* @return the VR view manager.
*/
public VRViewManager getVRViewManager() {
return viewmanager;
}
/**
* Get the GUI manager attached to this environment.
* @return the GUI manager attached to this environment.
*/
public VRGuiManager getVRGUIManager(){
return guiManager;
}
/**
* Get the VR mouse manager attached to this environment.
* @return the VR mouse manager attached to this environment.
*/
public VRMouseManager getVRMouseManager(){
return mouseManager;
}
/**
* Can be used to change seated experience during runtime.
* @param isSeated <code>true</code> if designed for sitting, <code>false</code> for standing/roomscale
* @see #isSeatedExperience()
*/
public void setSeatedExperience(boolean isSeated) {
seated = isSeated;
if( hardware instanceof OpenVR ) {
if( hardware.getCompositor() == null ) {
return;
}
if( seated ) {
((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
} else {
((OpenVR)hardware).getCompositor().SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);
}
}
}
/**
* Check if the application is configured as a seated experience.
* @return <code>true</code> if the application is configured as a seated experience and <code>false</code> otherwise.
* @see #setSeatedExperience(boolean)
*/
public boolean isSeatedExperience() {
return seated;
}
/**
* 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 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 ) {
if (application != null){
return application.getCamera();
} else {
throw new IllegalStateException("VR environment is not attached to any application.");
}
}
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 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;
}
/**
* Get the {@link AppSettings settings} attached to this environment.
* @return the {@link AppSettings settings} attached to this environment.
* @see #setSettings(AppSettings)
*/
public AppSettings getSettings(){
return settings;
}
/**
* Set the {@link AppSettings settings} attached to this environment.
* @param settings the {@link AppSettings settings} attached to this environment.
* @see #getSettings()
*/
public void setSettings(AppSettings settings){
this.settings = settings;
processSettings();
}
/**
* Get if the system currently support VR.
* @return <code>true</code> if the system currently support VR and <code>false</Code> otherwise.
*/
public boolean isVRSupported() {
return vrSupportedOS;
}
/**
* Check if the VR mode is enabled.
* @return <code>true</code> if the VR mode is enabled and <code>false</code> otherwise.
*/
public boolean isInVR() {
return (forceVR || vrSupportedOS && hardware != null && hardware.isInitialized() && isInitialized());
}
/**
* Check if the rendering is instanced (see <a href="https://en.wikipedia.org/wiki/Geometry_instancing">Geometry instancing</a>).
* @return <code>true</code> if the rendering is instanced and <code>false</code> otherwise.
*/
public boolean isInstanceRendering() {
return instanceRendering;
}
public boolean isSwapBuffers(){
return disableSwapBuffers;
}
/**
* Check if the application has a GUI overlay attached.
* @return <code>true</code> if the application has a GUI overlay attached and <code>false</code> otherwise.
*/
public boolean hasTraditionalGUIOverlay() {
return !nogui;
}
/**
* Check if the VR environment is initialized. A call to the {@link #initialize() initialize()} method should set this value to <code>true</code>
* @return <code>true</code> if the VR environment is initialized and <code>false</code> otherwise.
*/
public boolean isInitialized(){
return initialized;
}
/**
* Is the VR compositor is active.
* @return <code>true</code> if the VR compositor is active and <code>false</code> otherwise.
*/
public boolean compositorAllowed() {
return useCompositor && compositorOS;
}
/**
* Reset headset pose if seating experience.
*/
public void resetSeatedPose(){
if( vrSupportedOS == false || isSeatedExperience() == false ){
return;
}
getVRHardware().reset();
}
public AppState getAppState(){
return app;
}
public Application getApplication(){
return application;
}
/**
* Get the {@link Camera camera} used for rendering.
* 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 used for rendering.
*/
public Camera getCamera() {
if( isInVR() && getVRViewManager() != null && getVRViewManager().getLeftCamera() != null ) {
return getDummyCamera();
}
return application.getCamera();
}
public Camera getDummyCamera(){
if (dummyCam == null){
if (application != null){
if (application.getCamera() != null){
dummyCam = application.getCamera().clone();
} else {
return new Camera(settings.getWidth(), settings.getHeight());
}
} else {
throw new IllegalStateException("VR environment is not attached to any application.");
}
}
return dummyCam;
}
/**
* Attach the VR environment to the given app state and application.
* This method should be called within the {@link AppState#stateAttached(com.jme3.app.state.AppStateManager) stateAttached(com.jme3.app.state.AppStateManager)} method
* from the app state.
* @param appState the app state to attach.
* @param application the application to attach.
*/
public void atttach(AppState appState, Application application){
this.application = application;
this.app = appState;
// Instanciate view manager
if (vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE){
viewmanager = new VRViewManagerOpenVR(this);
} else if (vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE){
viewmanager = new VRViewManagerOSVR(this);
} else {
logger.severe("Cannot instanciate view manager, unknown VRAPI type: "+vrBinding);
}
}
/**
* Initialize this VR environment. This method enable the system bindings and configure all the VR system modules.
* A call to this method has to be made before any use of VR capabilities.
* @return <code>true</code> if the VR environment is successfully initialized and <code>false</code> otherwise.
*/
public boolean initialize(){
logger.config("Initializing VR environment.");
initialized = false;
// we are going to use OpenVR now, not the Oculus Rift
// OpenVR does support the Rift
String 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) {
if( vrBinding == VRConstants.SETTING_VRAPI_OSVR_VALUE ) {
hardware = new OSVR(this);
initialized = true;
logger.config("Creating OSVR wrapper [SUCCESS]");
} else if( vrBinding == VRConstants.SETTING_VRAPI_OPENVR_VALUE ) {
hardware = new OpenVR(this);
initialized = true;
logger.config("Creating OpenVR wrapper [SUCCESS]");
} else {
logger.config("Cannot create VR binding: "+vrBinding+" [FAILED]");
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]");
}
if( hardware.initialize() ) {
initialized &= true;
logger.config("VR native wrapper initialized [SUCCESS]");
} else {
initialized &= false;
logger.warning("VR native wrapper initialized [FAILED]");
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]");
}
} else {
logger.log(Level.SEVERE, "System does not support VR capabilities.");
logger.log(Level.SEVERE, "Cannot initialize VR environment [FAILED]");
}
return initialized;
}
private void processSettings(){
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_ENABLE_MIRROR_WINDOW) != null){
if( useCompositor == false ) {
disableSwapBuffers = false;
} else {
disableSwapBuffers = !settings.getBoolean(VRConstants.SETTING_ENABLE_MIRROR_WINDOW);
}
}
if (settings.get(VRConstants.SETTING_GUI_OVERDRAW) != null){
getVRGUIManager().setGuiOverdraw(settings.getBoolean(VRConstants.SETTING_GUI_OVERDRAW));
}
if (settings.get(VRConstants.SETTING_GUI_CURVED_SURFACE) != null){
getVRGUIManager().setCurvedSurface(settings.getBoolean(VRConstants.SETTING_GUI_CURVED_SURFACE));
}
if (settings.get(VRConstants.SETTING_NO_GUI) != null){
nogui = settings.getBoolean(VRConstants.SETTING_NO_GUI);
}
if (settings.get(VRConstants.SETTING_VRAPI) != null){
vrBinding = settings.getInteger(VRConstants.SETTING_VRAPI);
}
if (settings.get(VRConstants.SETTING_SEATED_EXPERIENCE) != null){
seated = settings.getBoolean(VRConstants.SETTING_SEATED_EXPERIENCE);
}
if (settings.get(VRConstants.SETTING_INSTANCE_RENDERING) != null){
instanceRendering = 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_FLIP_EYES) != null){
if( getVRHardware() != null ){
getVRHardware().setFlipEyes(settings.getBoolean(VRConstants.SETTING_FLIP_EYES));
}
}
}
}
}