Переглянути джерело

Conform to master... boring....

jseinturier 8 роки тому
батько
коміт
0ecd38892c
1 змінених файлів з 524 додано та 525 видалено
  1. 524 525
      jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java

+ 524 - 525
jme3-vr/src/main/java/com/jme3/input/vr/OpenVR.java

@@ -1,525 +1,524 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
-package com.jme3.input.vr;
-
-import com.jme3.app.VREnvironment;
-import com.jme3.math.Matrix4f;
-import com.jme3.math.Quaternion;
-import com.jme3.math.Vector2f;
-import com.jme3.math.Vector3f;
-import com.jme3.renderer.Camera;
-import com.jme3.system.jopenvr.HmdMatrix34_t;
-import com.jme3.system.jopenvr.HmdMatrix44_t;
-import com.jme3.system.jopenvr.JOpenVRLibrary;
-import com.jme3.system.jopenvr.OpenVRUtil;
-import com.jme3.system.jopenvr.TrackedDevicePose_t;
-import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable;
-import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
-import com.jme3.util.VRUtil;
-import com.sun.jna.Memory;
-import com.sun.jna.Pointer;
-import com.sun.jna.ptr.FloatByReference;
-import com.sun.jna.ptr.IntByReference;
-import com.sun.jna.ptr.LongByReference;
-
-import java.nio.IntBuffer;
-import java.util.Locale;
-import java.util.concurrent.TimeUnit;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-/**
- * A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system. 
- * @author reden - phr00t - https://github.com/phr00t
- * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
- */
-public class OpenVR implements VRAPI {
-    
-	private static final Logger logger = Logger.getLogger(OpenVR.class.getName());
-	
-    private static VR_IVRCompositor_FnTable compositorFunctions;
-    private static VR_IVRSystem_FnTable vrsystemFunctions;
-    
-    private static boolean initSuccess = false;
-    private static boolean flipEyes    = false;
-    
-    private IntBuffer hmdDisplayFrequency;
-    private TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference;
-    protected TrackedDevicePose_t[] hmdTrackedDevicePoses;
-    
-    protected IntByReference hmdErrorStore;
-    
-    private final Quaternion rotStore = new Quaternion();
-    private final Vector3f posStore = new Vector3f();
-    
-    private static FloatByReference tlastVsync;
-    
-    /**
-     * The actual frame count.
-     */
-    public static LongByReference _tframeCount;
-    
-    // for debugging latency
-    private int frames = 0;    
-    
-    protected Matrix4f[] poseMatrices;
-    
-    private final Matrix4f hmdPose = Matrix4f.IDENTITY.clone();
-    private Matrix4f hmdProjectionLeftEye;
-    private Matrix4f hmdProjectionRightEye;
-    private Matrix4f hmdPoseLeftEye;
-    private Matrix4f hmdPoseRightEye;
-    
-    private Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand;
-    
-    private float vsyncToPhotons;
-    private double timePerFrame, frameCountRun;
-    private long frameCount;
-    private OpenVRInput VRinput;
-    
-    
-    private VREnvironment environment = null;
-    
-    /**
-     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system 
-     * attached to the given {@link VREnvironment VR environment}.
-     * @param environment the VR environment to which this API is attached.
-     */
-    public OpenVR(VREnvironment environment){
-      this.environment = environment;
-    }
-    
-    @Override
-    public OpenVRInput getVRinput() {
-        return VRinput;
-    }
-    
-    @Override
-    public VR_IVRSystem_FnTable getVRSystem() {
-        return vrsystemFunctions;
-    }
-    
-    @Override
-    public VR_IVRCompositor_FnTable getCompositor() {
-        return compositorFunctions;
-    }
-    
-    @Override
-    public String getName() {
-        return "OpenVR";
-    }
-    
-    private static long latencyWaitTime = 0;
-    
-    @Override
-    public void setFlipEyes(boolean set) {
-        flipEyes = set;
-    }
-    
-    private boolean enableDebugLatency = false;
-    
-    @Override
-    public void printLatencyInfoToConsole(boolean set) {
-        enableDebugLatency = set;
-    }
-
-    @Override
-    public int getDisplayFrequency() {
-        if( hmdDisplayFrequency == null ) return 0;
-        return hmdDisplayFrequency.get(0);
-    }
-    
-    @Override
-    public boolean initialize() {
-    	
-    	logger.config("Initializing OpenVR system...");
-    	
-        hmdErrorStore = new IntByReference();
-        vrsystemFunctions = null;
-        JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene);
-        if( hmdErrorStore.getValue() == 0 ) {
-            vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer());
-        }
-        
-        if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) {
-            logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0));
-            logger.severe("Initializing OpenVR system [FAILED]");
-            return false;
-        } else {
-            logger.config("OpenVR initialized & VR connected.");
-            
-            vrsystemFunctions.setAutoSynch(false);
-            vrsystemFunctions.read();
-            
-            tlastVsync = new FloatByReference();
-            _tframeCount = new LongByReference();
-            
-            hmdDisplayFrequency = IntBuffer.allocate(1);
-            hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float);
-            hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference();
-            hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount);
-            poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount];
-            for(int i=0;i<poseMatrices.length;i++) poseMatrices[i] = new Matrix4f();
-
-            timePerFrame = 1.0 / hmdDisplayFrequency.get(0);
-            
-            // disable all this stuff which kills performance
-            hmdTrackedDevicePoseReference.setAutoRead(false);
-            hmdTrackedDevicePoseReference.setAutoWrite(false);
-            hmdTrackedDevicePoseReference.setAutoSynch(false);
-            for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
-                hmdTrackedDevicePoses[i].setAutoRead(false);
-                hmdTrackedDevicePoses[i].setAutoWrite(false);
-                hmdTrackedDevicePoses[i].setAutoSynch(false);
-            }
-            
-            // init controllers for the first time
-            VRinput = new OpenVRInput(environment);
-            VRinput.init();
-            VRinput.updateConnectedControllers();
-            
-            // init bounds & chaperone info
-            environment.getVRBounds().init(this);
-            
-            logger.config("Initializing OpenVR system [SUCCESS]");
-            initSuccess = true;
-            return true;
-        }
-    }
-    
-    @Override
-    public boolean initVRCompositor(boolean allowed) {
-        hmdErrorStore.setValue(0); // clear the error store
-        if( allowed && vrsystemFunctions != null ) {
-        	
-        	IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRCompositor_Version, hmdErrorStore);
-        	if (intptr != null){
-        	
-        		if (intptr.getPointer() != null){
-            		compositorFunctions = new VR_IVRCompositor_FnTable(intptr.getPointer());
-                    if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){          
-                        compositorFunctions.setAutoSynch(false);
-                        compositorFunctions.read();
-                        if( environment.isSeatedExperience() ) {                    
-                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
-                        } else {
-                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);                
-                        }
-                        logger.config("OpenVR Compositor initialized");
-                    } else {
-                        logger.severe("OpenVR Compositor error: " + hmdErrorStore.getValue());
-                        compositorFunctions = null;
-                    }
-        		} else {
-        			logger.log(Level.SEVERE, "Cannot get valid pointer for generic interface \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
-        			compositorFunctions = null;
-        		}
-
-        	} else {
-        		logger.log(Level.SEVERE, "Cannot get generic interface for \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
-        		compositorFunctions = null;
-        	}
-        	
-            
-        }
-        if( compositorFunctions == null ) {
-            logger.severe("Skipping VR Compositor...");
-            if( vrsystemFunctions != null ) {
-                vsyncToPhotons = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_SecondsFromVsyncToPhotons_Float, hmdErrorStore);
-            } else {
-                vsyncToPhotons = 0f;
-            }
-        }
-        return compositorFunctions != null;
-    }
-
-    @Override
-    public void destroy() {
-        JOpenVRLibrary.VR_ShutdownInternal();
-    }
-
-    @Override
-    public boolean isInitialized() {
-        return initSuccess;
-    }
-
-    @Override
-    public void reset() {
-        if( vrsystemFunctions == null ) return;
-        vrsystemFunctions.ResetSeatedZeroPose.apply();
-        hmdSeatToStand = null;
-    }
-
-    @Override
-    public void getRenderSize(Vector2f store) {
-        if( vrsystemFunctions == null ) {
-            // 1344x1512
-            store.x = 1344f;
-            store.y = 1512f;
-        } else {
-            IntByReference x = new IntByReference();
-            IntByReference y = new IntByReference();
-            vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y);
-            store.x = x.getValue();
-            store.y = y.getValue();
-        }
-    }
-    /*
-    @Override
-    public float getFOV(int dir) {
-        float val = 0f;
-        if( vrsystemFunctions != null ) {      
-            val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore);
-        }
-        // verification of number
-        if( val == 0f ) {
-            return 55f;
-        } else if( val <= 10f ) {
-            // most likely a radian number
-            return val * 57.2957795f;
-        }
-        return val;
-    }
-    */
-    
-    @Override
-    public float getInterpupillaryDistance() {
-        if( vrsystemFunctions == null ) return 0.065f;
-        return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore);
-    }
-    
-    @Override
-    public Quaternion getOrientation() {
-        VRUtil.convertMatrix4toQuat(hmdPose, rotStore);
-        return rotStore;
-    }
-
-    @Override
-    public Vector3f getPosition() {
-        // the hmdPose comes in rotated funny, fix that here
-        hmdPose.toTranslationVector(posStore);
-        posStore.x = -posStore.x;
-        posStore.z = -posStore.z;
-        return posStore;
-    }
-    
-    @Override
-    public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) {
-        hmdPose.toTranslationVector(storePos);
-        storePos.x = -storePos.x;
-        storePos.z = -storePos.z;
-        storeRot.set(getOrientation());
-    }    
-    
-    @Override
-    public void updatePose(){
-        if(vrsystemFunctions == null) return;
-        if(compositorFunctions != null) {
-           compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0);
-        } else {
-            // wait
-            if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime);
-                        
-            vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount);
-            float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons;
-            
-            if( enableDebugLatency ) {
-                if( frames == 10 ) {
-                    System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime));
-                    System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons));
-                }
-                frames = (frames + 1) % 60;            
-            }            
-            
-            // handle skipping frame stuff
-            long nowCount = _tframeCount.getValue();
-            if( nowCount - frameCount > 1 ) {
-                // skipped a frame!
-                if( enableDebugLatency ) System.out.println("Frame skipped!");
-                frameCountRun = 0;
-                if( latencyWaitTime > 0 ) {
-                    latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1);
-                    if( latencyWaitTime < 0 ) latencyWaitTime = 0;
-                }
-            } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) {
-                // didn't skip a frame, lets try waiting longer to improve latency
-                frameCountRun++;
-                latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0));
-            }
-
-            frameCount = nowCount;
-            
-            vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
-                    environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
-                                                       JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
-                    fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);   
-        }
-        
-        // deal with controllers being plugged in and out
-        // causing an invalid memory crash... skipping for now
-        /*boolean hasEvent = false;
-        while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) {
-            // wait until the events are clear..
-            hasEvent = true;
-        }
-        if( hasEvent ) {
-            // an event probably changed controller state
-            VRInput._updateConnectedControllers();
-        }*/
-        //update controllers pose information
-        environment.getVRinput().updateControllerStates();
-                
-        // read pose data from native
-        for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
-            hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid");
-            if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){
-                hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking");
-                VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]);
-            }            
-        }
-        
-        if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){
-            hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]);
-        } else {
-            hmdPose.set(Matrix4f.IDENTITY);
-        }
-    }
-
-    @Override
-    public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){
-        if( hmdProjectionLeftEye != null ) {
-            return hmdProjectionLeftEye;
-        } else if(vrsystemFunctions == null){
-            return cam.getProjectionMatrix();
-        } else {
-            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar());
-            hmdProjectionLeftEye = new Matrix4f();
-            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye);
-            return hmdProjectionLeftEye;
-        }
-    }
-        
-    @Override
-    public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){
-        if( hmdProjectionRightEye != null ) {
-            return hmdProjectionRightEye;
-        } else if(vrsystemFunctions == null){
-            return cam.getProjectionMatrix();
-        } else {
-            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar());
-            hmdProjectionRightEye = new Matrix4f();
-            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye);
-            return hmdProjectionRightEye;
-        }
-    }
-    
-    @Override
-    public Vector3f getHMDVectorPoseLeftEye() {
-        if( hmdPoseLeftEyeVec == null ) {
-            hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector();
-            // set default IPD if none or broken
-            if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) {
-                hmdPoseLeftEyeVec.x = 0.065f * -0.5f;
-            }
-            if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping
-        }
-        return hmdPoseLeftEyeVec;
-    }
-    
-    @Override
-    public Vector3f getHMDVectorPoseRightEye() {
-        if( hmdPoseRightEyeVec == null ) {
-            hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector();
-            // set default IPD if none or broken
-            if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) {
-                hmdPoseRightEyeVec.x = 0.065f * 0.5f;
-            }
-            if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping
-        }
-        return hmdPoseRightEyeVec;
-    }
-    
-    @Override
-    public Vector3f getSeatedToAbsolutePosition() {
-        if( environment.isSeatedExperience() == false ) return Vector3f.ZERO;
-        if( hmdSeatToStand == null ) {
-            hmdSeatToStand = new Vector3f();
-            HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
-            Matrix4f tempmat = new Matrix4f();
-            VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat);
-            tempmat.toTranslationVector(hmdSeatToStand);
-        }
-        return hmdSeatToStand;
-    }
-    
-    @Override
-    public Matrix4f getHMDMatrixPoseLeftEye(){
-        if( hmdPoseLeftEye != null ) {
-            return hmdPoseLeftEye;
-        } else if(vrsystemFunctions == null) {
-            return Matrix4f.IDENTITY;
-        } else {
-            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left);
-            hmdPoseLeftEye = new Matrix4f();
-            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye);
-        }
-    }
-    
-    @Override
-    public HmdType getType() {
-        if( vrsystemFunctions != null ) {      
-            Pointer str1 = new Memory(128);
-            Pointer str2 = new Memory(128);
-            String completeName = "";
-            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
-                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String,
-                                                                   str1, 128, hmdErrorStore);
-            if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0);
-            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
-                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String,
-                                                                   str2, 128, hmdErrorStore);
-            if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0);
-            if( completeName.length() > 0 ) {
-                completeName = completeName.toLowerCase(Locale.ENGLISH).trim();
-                if( completeName.contains("htc") || completeName.contains("vive") ) {
-                    return HmdType.HTC_VIVE;
-                } else if( completeName.contains("osvr") ) {
-                    return HmdType.OSVR;
-                } else if( completeName.contains("oculus") || completeName.contains("rift") ||
-                           completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) {
-                    return HmdType.OCULUS_RIFT;
-                } else if( completeName.contains("fove") ) {
-                    return HmdType.FOVE;
-                } else if( completeName.contains("game") && completeName.contains("face") ) {
-                    return HmdType.GAMEFACE;
-                } else if( completeName.contains("morpheus") ) {
-                    return HmdType.MORPHEUS;
-                } else if( completeName.contains("gear") ) {
-                    return HmdType.GEARVR;
-                } else if( completeName.contains("star") ) {
-                    return HmdType.STARVR;
-                } else if( completeName.contains("null") ) {
-                    return HmdType.NULL;
-                }
-            }
-        } else return HmdType.NONE;
-        return HmdType.OTHER;
-    }
-    
-    @Override
-    public Matrix4f getHMDMatrixPoseRightEye(){
-        if( hmdPoseRightEye != null ) {
-            return hmdPoseRightEye;
-        } else if(vrsystemFunctions == null) {
-            return Matrix4f.IDENTITY;
-        } else {
-            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right);
-            hmdPoseRightEye = new Matrix4f();
-            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye);
-        }
-    }
-  
-}
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package com.jme3.input.vr;
+
+import com.jme3.app.VREnvironment;
+import com.jme3.math.Matrix4f;
+import com.jme3.math.Quaternion;
+import com.jme3.math.Vector2f;
+import com.jme3.math.Vector3f;
+import com.jme3.renderer.Camera;
+import com.jme3.system.jopenvr.HmdMatrix34_t;
+import com.jme3.system.jopenvr.HmdMatrix44_t;
+import com.jme3.system.jopenvr.JOpenVRLibrary;
+import com.jme3.system.jopenvr.OpenVRUtil;
+import com.jme3.system.jopenvr.TrackedDevicePose_t;
+import com.jme3.system.jopenvr.VR_IVRCompositor_FnTable;
+import com.jme3.system.jopenvr.VR_IVRSystem_FnTable;
+import com.jme3.util.VRUtil;
+import com.sun.jna.Memory;
+import com.sun.jna.Pointer;
+import com.sun.jna.ptr.FloatByReference;
+import com.sun.jna.ptr.IntByReference;
+import com.sun.jna.ptr.LongByReference;
+
+import java.nio.IntBuffer;
+import java.util.Locale;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A class that wraps an <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system. 
+ * @author reden - phr00t - https://github.com/phr00t
+ * @author Julien Seinturier - (c) 2016 - JOrigin project - <a href="http://www.jorigin.org">http:/www.jorigin.org</a>
+ */
+public class OpenVR implements VRAPI {
+    
+	private static final Logger logger = Logger.getLogger(OpenVR.class.getName());
+	
+    private static VR_IVRCompositor_FnTable compositorFunctions;
+    private static VR_IVRSystem_FnTable vrsystemFunctions;
+    
+    private static boolean initSuccess = false;
+    private static boolean flipEyes    = false;
+    
+    private static IntBuffer hmdDisplayFrequency;
+    private static TrackedDevicePose_t.ByReference hmdTrackedDevicePoseReference;
+    protected static TrackedDevicePose_t[] hmdTrackedDevicePoses;
+    
+    protected static IntByReference hmdErrorStore;
+    
+    private static final Quaternion rotStore = new Quaternion();
+    private static final Vector3f posStore = new Vector3f();
+    
+    private static FloatByReference tlastVsync;
+    
+    /**
+     * The actual frame count.
+     */
+    public static LongByReference _tframeCount;
+    
+    // for debugging latency
+    private int frames = 0;    
+    
+    protected static Matrix4f[] poseMatrices;
+    
+    private static final Matrix4f hmdPose = Matrix4f.IDENTITY.clone();
+    private static Matrix4f hmdProjectionLeftEye;
+    private static Matrix4f hmdProjectionRightEye;
+    private static Matrix4f hmdPoseLeftEye;
+    private static Matrix4f hmdPoseRightEye;
+    
+    private static Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand;
+    
+    private static float vsyncToPhotons;
+    private static double timePerFrame, frameCountRun;
+    private static long frameCount;
+    private static OpenVRInput VRinput;
+    
+    private VREnvironment environment = null;
+    
+    /**
+     * Create a new <a href="https://github.com/ValveSoftware/openvr/wiki/API-Documentation">OpenVR</a> system 
+     * attached to the given {@link VREnvironment VR environment}.
+     * @param environment the VR environment to which this API is attached.
+     */
+    public OpenVR(VREnvironment environment){
+      this.environment = environment;
+    }
+    
+    @Override
+    public OpenVRInput getVRinput() {
+        return VRinput;
+    }
+    
+    @Override
+    public VR_IVRSystem_FnTable getVRSystem() {
+        return vrsystemFunctions;
+    }
+    
+    @Override
+    public VR_IVRCompositor_FnTable getCompositor() {
+        return compositorFunctions;
+    }
+    
+    @Override
+    public String getName() {
+        return "OpenVR";
+    }
+    
+    private static long latencyWaitTime = 0;
+    
+    @Override
+    public void setFlipEyes(boolean set) {
+        flipEyes = set;
+    }
+    
+    private boolean enableDebugLatency = false;
+    
+    @Override
+    public void printLatencyInfoToConsole(boolean set) {
+        enableDebugLatency = set;
+    }
+
+    @Override
+    public int getDisplayFrequency() {
+        if( hmdDisplayFrequency == null ) return 0;
+        return hmdDisplayFrequency.get(0);
+    }
+    
+    @Override
+    public boolean initialize() {
+    	
+    	logger.config("Initializing OpenVR system...");
+    	
+        hmdErrorStore = new IntByReference();
+        vrsystemFunctions = null;
+        JOpenVRLibrary.VR_InitInternal(hmdErrorStore, JOpenVRLibrary.EVRApplicationType.EVRApplicationType_VRApplication_Scene);
+        if( hmdErrorStore.getValue() == 0 ) {
+            vrsystemFunctions = new VR_IVRSystem_FnTable(JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRSystem_Version, hmdErrorStore).getPointer());
+        }
+        
+        if( vrsystemFunctions == null || hmdErrorStore.getValue() != 0 ) {
+            logger.severe("OpenVR Initialize Result: " + JOpenVRLibrary.VR_GetVRInitErrorAsEnglishDescription(hmdErrorStore.getValue()).getString(0));
+            logger.severe("Initializing OpenVR system [FAILED]");
+            return false;
+        } else {
+            logger.config("OpenVR initialized & VR connected.");
+            
+            vrsystemFunctions.setAutoSynch(false);
+            vrsystemFunctions.read();
+            
+            tlastVsync = new FloatByReference();
+            _tframeCount = new LongByReference();
+            
+            hmdDisplayFrequency = IntBuffer.allocate(1);
+            hmdDisplayFrequency.put( (int) JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_DisplayFrequency_Float);
+            hmdTrackedDevicePoseReference = new TrackedDevicePose_t.ByReference();
+            hmdTrackedDevicePoses = (TrackedDevicePose_t[])hmdTrackedDevicePoseReference.toArray(JOpenVRLibrary.k_unMaxTrackedDeviceCount);
+            poseMatrices = new Matrix4f[JOpenVRLibrary.k_unMaxTrackedDeviceCount];
+            for(int i=0;i<poseMatrices.length;i++) poseMatrices[i] = new Matrix4f();
+
+            timePerFrame = 1.0 / hmdDisplayFrequency.get(0);
+            
+            // disable all this stuff which kills performance
+            hmdTrackedDevicePoseReference.setAutoRead(false);
+            hmdTrackedDevicePoseReference.setAutoWrite(false);
+            hmdTrackedDevicePoseReference.setAutoSynch(false);
+            for(int i=0;i<JOpenVRLibrary.k_unMaxTrackedDeviceCount;i++) {
+                hmdTrackedDevicePoses[i].setAutoRead(false);
+                hmdTrackedDevicePoses[i].setAutoWrite(false);
+                hmdTrackedDevicePoses[i].setAutoSynch(false);
+            }
+            
+            // init controllers for the first time
+            VRinput = new OpenVRInput(environment);
+            VRinput.init();
+            VRinput.updateConnectedControllers();
+            
+            // init bounds & chaperone info
+            VRBounds.init();
+            
+            logger.config("Initializing OpenVR system [SUCCESS]");
+            initSuccess = true;
+            return true;
+        }
+    }
+    
+    @Override
+    public boolean initVRCompositor(boolean allowed) {
+        hmdErrorStore.setValue(0); // clear the error store
+        if( allowed && vrsystemFunctions != null ) {
+        	
+        	IntByReference intptr = JOpenVRLibrary.VR_GetGenericInterface(JOpenVRLibrary.IVRCompositor_Version, hmdErrorStore);
+        	if (intptr != null){
+        	
+        		if (intptr.getPointer() != null){
+            		compositorFunctions = new VR_IVRCompositor_FnTable(intptr.getPointer());
+                    if(compositorFunctions != null && hmdErrorStore.getValue() == 0 ){          
+                        compositorFunctions.setAutoSynch(false);
+                        compositorFunctions.read();
+                        if( environment.isSeatedExperience() ) {                    
+                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated);
+                        } else {
+                            compositorFunctions.SetTrackingSpace.apply(JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding);                
+                        }
+                        logger.config("OpenVR Compositor initialized");
+                    } else {
+                        logger.severe("OpenVR Compositor error: " + hmdErrorStore.getValue());
+                        compositorFunctions = null;
+                    }
+        		} else {
+        			logger.log(Level.SEVERE, "Cannot get valid pointer for generic interface \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
+        			compositorFunctions = null;
+        		}
+
+        	} else {
+        		logger.log(Level.SEVERE, "Cannot get generic interface for \""+JOpenVRLibrary.IVRCompositor_Version+"\", "+OpenVRUtil.getEVRInitErrorString(hmdErrorStore.getValue())+" ("+hmdErrorStore.getValue()+")");
+        		compositorFunctions = null;
+        	}
+        	
+            
+        }
+        if( compositorFunctions == null ) {
+            logger.severe("Skipping VR Compositor...");
+            if( vrsystemFunctions != null ) {
+                vsyncToPhotons = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_SecondsFromVsyncToPhotons_Float, hmdErrorStore);
+            } else {
+                vsyncToPhotons = 0f;
+            }
+        }
+        return compositorFunctions != null;
+    }
+
+    @Override
+    public void destroy() {
+        JOpenVRLibrary.VR_ShutdownInternal();
+    }
+
+    @Override
+    public boolean isInitialized() {
+        return initSuccess;
+    }
+
+    @Override
+    public void reset() {
+        if( vrsystemFunctions == null ) return;
+        vrsystemFunctions.ResetSeatedZeroPose.apply();
+        hmdSeatToStand = null;
+    }
+
+    @Override
+    public void getRenderSize(Vector2f store) {
+        if( vrsystemFunctions == null ) {
+            // 1344x1512
+            store.x = 1344f;
+            store.y = 1512f;
+        } else {
+            IntByReference x = new IntByReference();
+            IntByReference y = new IntByReference();
+            vrsystemFunctions.GetRecommendedRenderTargetSize.apply(x, y);
+            store.x = x.getValue();
+            store.y = y.getValue();
+        }
+    }
+    /*
+    @Override
+    public float getFOV(int dir) {
+        float val = 0f;
+        if( vrsystemFunctions != null ) {      
+            val = vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, dir, hmdErrorStore);
+        }
+        // verification of number
+        if( val == 0f ) {
+            return 55f;
+        } else if( val <= 10f ) {
+            // most likely a radian number
+            return val * 57.2957795f;
+        }
+        return val;
+    }
+    */
+    
+    @Override
+    public float getInterpupillaryDistance() {
+        if( vrsystemFunctions == null ) return 0.065f;
+        return vrsystemFunctions.GetFloatTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd, JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_UserIpdMeters_Float, hmdErrorStore);
+    }
+    
+    @Override
+    public Quaternion getOrientation() {
+        VRUtil.convertMatrix4toQuat(hmdPose, rotStore);
+        return rotStore;
+    }
+
+    @Override
+    public Vector3f getPosition() {
+        // the hmdPose comes in rotated funny, fix that here
+        hmdPose.toTranslationVector(posStore);
+        posStore.x = -posStore.x;
+        posStore.z = -posStore.z;
+        return posStore;
+    }
+    
+    @Override
+    public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) {
+        hmdPose.toTranslationVector(storePos);
+        storePos.x = -storePos.x;
+        storePos.z = -storePos.z;
+        storeRot.set(getOrientation());
+    }    
+    
+    @Override
+    public void updatePose(){
+        if(vrsystemFunctions == null) return;
+        if(compositorFunctions != null) {
+           compositorFunctions.WaitGetPoses.apply(hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount, null, 0);
+        } else {
+            // wait
+            if( latencyWaitTime > 0 ) VRUtil.sleepNanos(latencyWaitTime);
+                        
+            vrsystemFunctions.GetTimeSinceLastVsync.apply(tlastVsync, _tframeCount);
+            float fSecondsUntilPhotons = (float)timePerFrame - tlastVsync.getValue() + vsyncToPhotons;
+            
+            if( enableDebugLatency ) {
+                if( frames == 10 ) {
+                    System.out.println("Waited (nanos): " + Long.toString(latencyWaitTime));
+                    System.out.println("Predict ahead time: " + Float.toString(fSecondsUntilPhotons));
+                }
+                frames = (frames + 1) % 60;            
+            }            
+            
+            // handle skipping frame stuff
+            long nowCount = _tframeCount.getValue();
+            if( nowCount - frameCount > 1 ) {
+                // skipped a frame!
+                if( enableDebugLatency ) System.out.println("Frame skipped!");
+                frameCountRun = 0;
+                if( latencyWaitTime > 0 ) {
+                    latencyWaitTime -= TimeUnit.MILLISECONDS.toNanos(1);
+                    if( latencyWaitTime < 0 ) latencyWaitTime = 0;
+                }
+            } else if( latencyWaitTime < timePerFrame * 1000000000.0 ) {
+                // didn't skip a frame, lets try waiting longer to improve latency
+                frameCountRun++;
+                latencyWaitTime += Math.round(Math.pow(frameCountRun / 10.0, 2.0));
+            }
+
+            frameCount = nowCount;
+            
+            vrsystemFunctions.GetDeviceToAbsoluteTrackingPose.apply(
+                    environment.isSeatedExperience()?JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseSeated:
+                                                       JOpenVRLibrary.ETrackingUniverseOrigin.ETrackingUniverseOrigin_TrackingUniverseStanding,
+                    fSecondsUntilPhotons, hmdTrackedDevicePoseReference, JOpenVRLibrary.k_unMaxTrackedDeviceCount);   
+        }
+        
+        // deal with controllers being plugged in and out
+        // causing an invalid memory crash... skipping for now
+        /*boolean hasEvent = false;
+        while( JOpenVRLibrary.VR_IVRSystem_PollNextEvent(OpenVR.getVRSystemInstance(), tempEvent) != 0 ) {
+            // wait until the events are clear..
+            hasEvent = true;
+        }
+        if( hasEvent ) {
+            // an event probably changed controller state
+            VRInput._updateConnectedControllers();
+        }*/
+        //update controllers pose information
+        environment.getVRinput().updateControllerStates();
+                
+        // read pose data from native
+        for (int nDevice = 0; nDevice < JOpenVRLibrary.k_unMaxTrackedDeviceCount; ++nDevice ){
+            hmdTrackedDevicePoses[nDevice].readField("bPoseIsValid");
+            if( hmdTrackedDevicePoses[nDevice].bPoseIsValid != 0 ){
+                hmdTrackedDevicePoses[nDevice].readField("mDeviceToAbsoluteTracking");
+                VRUtil.convertSteamVRMatrix3ToMatrix4f(hmdTrackedDevicePoses[nDevice].mDeviceToAbsoluteTracking, poseMatrices[nDevice]);
+            }            
+        }
+        
+        if ( hmdTrackedDevicePoses[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd].bPoseIsValid != 0 ){
+            hmdPose.set(poseMatrices[JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd]);
+        } else {
+            hmdPose.set(Matrix4f.IDENTITY);
+        }
+    }
+
+    @Override
+    public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam){
+        if( hmdProjectionLeftEye != null ) {
+            return hmdProjectionLeftEye;
+        } else if(vrsystemFunctions == null){
+            return cam.getProjectionMatrix();
+        } else {
+            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left, cam.getFrustumNear(), cam.getFrustumFar());
+            hmdProjectionLeftEye = new Matrix4f();
+            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionLeftEye);
+            return hmdProjectionLeftEye;
+        }
+    }
+        
+    @Override
+    public Matrix4f getHMDMatrixProjectionRightEye(Camera cam){
+        if( hmdProjectionRightEye != null ) {
+            return hmdProjectionRightEye;
+        } else if(vrsystemFunctions == null){
+            return cam.getProjectionMatrix();
+        } else {
+            HmdMatrix44_t mat = vrsystemFunctions.GetProjectionMatrix.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right, cam.getFrustumNear(), cam.getFrustumFar());
+            hmdProjectionRightEye = new Matrix4f();
+            VRUtil.convertSteamVRMatrix4ToMatrix4f(mat, hmdProjectionRightEye);
+            return hmdProjectionRightEye;
+        }
+    }
+    
+    @Override
+    public Vector3f getHMDVectorPoseLeftEye() {
+        if( hmdPoseLeftEyeVec == null ) {
+            hmdPoseLeftEyeVec = getHMDMatrixPoseLeftEye().toTranslationVector();
+            // set default IPD if none or broken
+            if( hmdPoseLeftEyeVec.x <= 0.080f * -0.5f || hmdPoseLeftEyeVec.x >= 0.040f * -0.5f ) {
+                hmdPoseLeftEyeVec.x = 0.065f * -0.5f;
+            }
+            if( flipEyes == false ) hmdPoseLeftEyeVec.x *= -1f; // it seems these need flipping
+        }
+        return hmdPoseLeftEyeVec;
+    }
+    
+    @Override
+    public Vector3f getHMDVectorPoseRightEye() {
+        if( hmdPoseRightEyeVec == null ) {
+            hmdPoseRightEyeVec = getHMDMatrixPoseRightEye().toTranslationVector();
+            // set default IPD if none or broken
+            if( hmdPoseRightEyeVec.x >= 0.080f * 0.5f || hmdPoseRightEyeVec.x <= 0.040f * 0.5f ) {
+                hmdPoseRightEyeVec.x = 0.065f * 0.5f;
+            }
+            if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping
+        }
+        return hmdPoseRightEyeVec;
+    }
+    
+    @Override
+    public Vector3f getSeatedToAbsolutePosition() {
+        if( environment.isSeatedExperience() == false ) return Vector3f.ZERO;
+        if( hmdSeatToStand == null ) {
+            hmdSeatToStand = new Vector3f();
+            HmdMatrix34_t mat = vrsystemFunctions.GetSeatedZeroPoseToStandingAbsoluteTrackingPose.apply();
+            Matrix4f tempmat = new Matrix4f();
+            VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, tempmat);
+            tempmat.toTranslationVector(hmdSeatToStand);
+        }
+        return hmdSeatToStand;
+    }
+    
+    @Override
+    public Matrix4f getHMDMatrixPoseLeftEye(){
+        if( hmdPoseLeftEye != null ) {
+            return hmdPoseLeftEye;
+        } else if(vrsystemFunctions == null) {
+            return Matrix4f.IDENTITY;
+        } else {
+            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Left);
+            hmdPoseLeftEye = new Matrix4f();
+            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseLeftEye);
+        }
+    }
+    
+    @Override
+    public HmdType getType() {
+        if( vrsystemFunctions != null ) {      
+            Pointer str1 = new Memory(128);
+            Pointer str2 = new Memory(128);
+            String completeName = "";
+            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
+                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ManufacturerName_String,
+                                                                   str1, 128, hmdErrorStore);
+            if( hmdErrorStore.getValue() == 0 ) completeName += str1.getString(0);
+            vrsystemFunctions.GetStringTrackedDeviceProperty.apply(JOpenVRLibrary.k_unTrackedDeviceIndex_Hmd,
+                                                                   JOpenVRLibrary.ETrackedDeviceProperty.ETrackedDeviceProperty_Prop_ModelNumber_String,
+                                                                   str2, 128, hmdErrorStore);
+            if( hmdErrorStore.getValue() == 0 ) completeName += " " + str2.getString(0);
+            if( completeName.length() > 0 ) {
+                completeName = completeName.toLowerCase(Locale.ENGLISH).trim();
+                if( completeName.contains("htc") || completeName.contains("vive") ) {
+                    return HmdType.HTC_VIVE;
+                } else if( completeName.contains("osvr") ) {
+                    return HmdType.OSVR;
+                } else if( completeName.contains("oculus") || completeName.contains("rift") ||
+                           completeName.contains("dk1") || completeName.contains("dk2") || completeName.contains("cv1") ) {
+                    return HmdType.OCULUS_RIFT;
+                } else if( completeName.contains("fove") ) {
+                    return HmdType.FOVE;
+                } else if( completeName.contains("game") && completeName.contains("face") ) {
+                    return HmdType.GAMEFACE;
+                } else if( completeName.contains("morpheus") ) {
+                    return HmdType.MORPHEUS;
+                } else if( completeName.contains("gear") ) {
+                    return HmdType.GEARVR;
+                } else if( completeName.contains("star") ) {
+                    return HmdType.STARVR;
+                } else if( completeName.contains("null") ) {
+                    return HmdType.NULL;
+                }
+            }
+        } else return HmdType.NONE;
+        return HmdType.OTHER;
+    }
+    
+    @Override
+    public Matrix4f getHMDMatrixPoseRightEye(){
+        if( hmdPoseRightEye != null ) {
+            return hmdPoseRightEye;
+        } else if(vrsystemFunctions == null) {
+            return Matrix4f.IDENTITY;
+        } else {
+            HmdMatrix34_t mat = vrsystemFunctions.GetEyeToHeadTransform.apply(JOpenVRLibrary.EVREye.EVREye_Eye_Right);
+            hmdPoseRightEye = new Matrix4f();
+            return VRUtil.convertSteamVRMatrix3ToMatrix4f(mat, hmdPoseRightEye);
+        }
+    }
+  
+}