All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jme3.input.vr.osvr.OSVR Maven / Gradle / Ivy

There is a newer version: 3.7.0-stable
Show newest version
/*

https://github.com/sensics/OSVR-RenderManager/blob/master/examples/RenderManagerOpenGLCAPIExample.cpp

- JVM crashes often. Placing breakpoints during initialization clears it up most of the time (WHY!?)
  - OSVR is just unstable. Any way to improve things?
- Render manager looks good, but left eye seems stretched.

 */
package com.jme3.input.vr.osvr;

import com.jme3.app.VREnvironment;
import com.jme3.input.vr.HmdType;
import com.jme3.input.vr.VRAPI;
import com.jme3.input.vr.VRInputAPI;
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.osvr.osvrclientkit.OsvrClientKitLibrary;
import com.jme3.system.osvr.osvrdisplay.OsvrDisplayLibrary;
import com.jme3.system.osvr.osvrdisplay.OsvrDisplayLibrary.OSVR_DisplayConfig;
import com.jme3.system.osvr.osvrmatrixconventions.OSVR_Pose3;
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_OpenResultsOpenGL;
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_RenderBufferOpenGL;
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_RenderInfoOpenGL;
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_RenderParams;
import com.jme3.system.osvr.osvrrendermanageropengl.OSVR_ViewportDescription;
import com.jme3.system.osvr.osvrrendermanageropengl.OsvrRenderManagerOpenGLLibrary;
import com.ochafik.lang.jnaerator.runtime.NativeSize;
import com.ochafik.lang.jnaerator.runtime.NativeSizeByReference;
import com.sun.jna.Pointer;
import com.sun.jna.ptr.PointerByReference;
import java.nio.FloatBuffer;
import java.util.logging.Logger;

/**
 * A class that wraps an OSVR system.
 * @author reden - phr00t - https://github.com/phr00t
 * @author Julien Seinturier - COMEX SA - http://www.seinturier.fr
 */
public class OSVR implements VRAPI {
    private static final Logger logger = Logger.getLogger(OSVR.class.getName());

    /**
     * The first viewer index.
     */
    public static final int FIRST_VIEWER = 0;

    /**
     * The left eye index.
     */
    public static final int EYE_LEFT = 0;

    /**
     * The right eye index.
     */
    public static final int EYE_RIGHT = 1;

    /**
     * The size of the left eye.
     */
    public static final NativeSize EYE_LEFT_SIZE = new NativeSize(EYE_LEFT);

    /**
     * The size of the right eye.
     */
    public static final NativeSize EYE_RIGHT_SIZE = new NativeSize(EYE_RIGHT);

    /**
     * The default J String.
     */
    public static byte[] defaultJString = { 'j', (byte)0 };

    /**
     * The default OpenGL String.
     */
    public static byte[] OpenGLString = { 'O', 'p', 'e', 'n', 'G', 'L', (byte)0 };

    private final Matrix4f[] eyeMatrix = new Matrix4f[2];

    private PointerByReference grabRM;
    private PointerByReference grabRMOGL;
    private PointerByReference grabRIC;

    OSVR_RenderParams.ByValue renderParams;
    OsvrClientKitLibrary.OSVR_ClientContext context;
    com.jme3.system.osvr.osvrrendermanageropengl.OSVR_GraphicsLibraryOpenGL.ByValue graphicsLibrary;
    Pointer renderManager, renderManagerOpenGL, renderInfoCollection, registerBufferState;
    OSVRInput VRinput;
    NativeSize numRenderInfo;
    NativeSizeByReference grabNumInfo = new NativeSizeByReference();
    OSVR_RenderInfoOpenGL.ByValue eyeLeftInfo, eyeRightInfo;
    Matrix4f hmdPoseLeftEye;
    Matrix4f hmdPoseRightEye;
    Vector3f hmdPoseLeftEyeVec, hmdPoseRightEyeVec, hmdSeatToStand;
    OSVR_DisplayConfig displayConfig;
    OSVR_Pose3 hmdPose = new OSVR_Pose3();
    Vector3f storePos = new Vector3f();
    Quaternion storeRot = new Quaternion();
    PointerByReference presentState = new PointerByReference();
    OSVR_OpenResultsOpenGL openResults = new OSVR_OpenResultsOpenGL();

    long glfwContext;
    long renderManagerContext;
    long wglGLFW;
    long wglRM;

    boolean initSuccess = false;
    boolean flipEyes = false;

    private VREnvironment environment = null;

    /**
     * Create a new OSVR system attached to the given {@link VREnvironment VR environment}.
     * @param environment the {@link VREnvironment VR environment} to which the input is attached.
     */
    public OSVR(VREnvironment environment){
        this.environment = environment;
    }

    /**
     * Access to the underlying OSVR structures.
     * @param leftView the left viewport.
     * @param rightView the right viewport.
     * @param leftBuffer the left buffer.
     * @param rightBuffer the right buffer.
     * @return true if the structure are accessible and false otherwise.
     */
    public boolean handleRenderBufferPresent(OSVR_ViewportDescription.ByValue leftView, OSVR_ViewportDescription.ByValue rightView,
                                             OSVR_RenderBufferOpenGL.ByValue leftBuffer, OSVR_RenderBufferOpenGL.ByValue rightBuffer) {
        if( eyeLeftInfo == null || eyeRightInfo == null ) return false;
        byte retval;
        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerStartPresentRenderBuffers(presentState);
        getEyeInfo();
        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerPresentRenderBufferOpenGL(presentState.getValue(), leftBuffer, eyeLeftInfo, leftView);
        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerPresentRenderBufferOpenGL(presentState.getValue(), rightBuffer, eyeRightInfo, rightView);
        retval = OsvrRenderManagerOpenGLLibrary.osvrRenderManagerFinishPresentRenderBuffers(renderManager, presentState.getValue(), renderParams, (byte)0);
        return retval == 0; // only check the last error, since if something errored above, the last call won't work & all calls will log to syserr
    }

    @Override
    public boolean initialize() {
        logger.config("Initialize OSVR system.");

        hmdPose.setAutoSynch(false);
        context = OsvrClientKitLibrary.osvrClientInit(defaultJString, 0);
        VRinput = new OSVRInput(environment);
        initSuccess = context != null && VRinput.init();
        if( initSuccess ) {
            PointerByReference grabDisplay = new PointerByReference();
            byte retval = OsvrDisplayLibrary.osvrClientGetDisplay(context, grabDisplay);
            if( retval != 0 ) {
                System.out.println("OSVR Get Display Error: " + retval);
                initSuccess = false;
                return false;
            }
            displayConfig = new OSVR_DisplayConfig(grabDisplay.getValue());
            System.out.println("Waiting for the display to fully start up, including receiving initial pose update...");
            int i = 400;
            while (OsvrDisplayLibrary.osvrClientCheckDisplayStartup(displayConfig) != 0) {
                if( i-- < 0 ) {
                    System.out.println("Couldn't get display startup update in time, continuing anyway...");
                    break;
                }
                OsvrClientKitLibrary.osvrClientUpdate(context);
                try {
                    Thread.sleep(5);
                } catch(Exception e) { }
            }
            System.out.println("OK, display startup status is good!");
        }
        return initSuccess;
    }


    /**
     * Grab the current GLFW context.
     */
    public void grabGLFWContext() {
        // get current context
        wglGLFW = org.lwjgl.opengl.WGL.wglGetCurrentContext();
        glfwContext = org.lwjgl.glfw.GLFW.glfwGetCurrentContext();
    }

    /**
     * Enable context sharing.
     * @return true if the context is successfully shared and false otherwise.
     */
    public boolean shareContext() {
        if( org.lwjgl.opengl.WGL.wglShareLists(wglRM, wglGLFW)) {
            System.out.println("Context sharing success!");
            return true;
        } else {
            System.out.println("Context sharing problem...");
            return false;
        }
    }

    @Override
    public boolean initVRCompositor(boolean allowed) {
        if( !allowed || renderManager != null ) return false;
        grabGLFWContext();
        graphicsLibrary = new com.jme3.system.osvr.osvrrendermanageropengl.OSVR_GraphicsLibraryOpenGL.ByValue();
        graphicsLibrary.toolkit = null;
        graphicsLibrary.setAutoSynch(false);
        grabRM = new PointerByReference(); grabRMOGL = new PointerByReference();
        byte retval = OsvrRenderManagerOpenGLLibrary.osvrCreateRenderManagerOpenGL(context, OpenGLString, graphicsLibrary, grabRM, grabRMOGL);
        if( retval == 0 ) {
            renderManager = grabRM.getValue(); renderManagerOpenGL = grabRMOGL.getValue();
            if( renderManager == null || renderManagerOpenGL == null ) {
                System.out.println("Render Manager Created NULL, error!");
                return false;
            }
            openResults.setAutoSynch(false);
            retval = OsvrRenderManagerOpenGLLibrary.osvrRenderManagerOpenDisplayOpenGL(renderManager, openResults);
            if( retval == 0 ) {
                wglRM = org.lwjgl.opengl.WGL.wglGetCurrentContext();
                renderManagerContext = org.lwjgl.glfw.GLFW.glfwGetCurrentContext();
                shareContext();
                OsvrClientKitLibrary.osvrClientUpdate(context);
                renderParams = new OSVR_RenderParams.ByValue();
                renderParams.setAutoSynch(false);
                OsvrRenderManagerOpenGLLibrary.osvrRenderManagerGetDefaultRenderParams(renderParams);
                grabRIC = new PointerByReference();
                retval = OsvrRenderManagerOpenGLLibrary.osvrRenderManagerGetRenderInfoCollection(renderManager, renderParams, grabRIC);
                if( retval == 0 ) {
                    renderInfoCollection = grabRIC.getValue();
                    OsvrRenderManagerOpenGLLibrary.osvrRenderManagerGetNumRenderInfoInCollection(renderInfoCollection, grabNumInfo);
                    numRenderInfo = grabNumInfo.getValue();
                    eyeLeftInfo = new OSVR_RenderInfoOpenGL.ByValue();
                    eyeRightInfo = new OSVR_RenderInfoOpenGL.ByValue();
                    eyeLeftInfo.setAutoSynch(false);
                    eyeRightInfo.setAutoSynch(false);
                    return true;
                }
                OsvrRenderManagerOpenGLLibrary.osvrDestroyRenderManager(renderManager);
                System.out.println("OSVR Render Manager Info Collection Error: " + retval);
                return false;
            }
            OsvrRenderManagerOpenGLLibrary.osvrDestroyRenderManager(renderManager);
            System.out.println("OSVR Open Render Manager Display Error: " + retval);
            return false;
        }
        System.out.println("OSVR Create Render Manager Error: " + retval);
        return false;
    }

    @Override
    public OsvrClientKitLibrary.OSVR_ClientContext getVRSystem() {
        return context;
    }

    @Override
    public Pointer getCompositor() {
        return renderManager;
    }

    @Override
    public String getName() {
        return "OSVR";
    }

    @Override
    public VRInputAPI getVRinput() {
        return VRinput;
    }

    @Override
    public void setFlipEyes(boolean set) {
        flipEyes = set;
    }

    @Override
    public void printLatencyInfoToConsole(boolean set) {
    }

    @Override
    public int getDisplayFrequency() {
        return 60; //debug display frequency
    }

    @Override
    public void destroy() {
        if( renderManager != null ) OsvrRenderManagerOpenGLLibrary.osvrDestroyRenderManager(renderManager);
        if( displayConfig != null ) OsvrDisplayLibrary.osvrClientFreeDisplay(displayConfig);
    }

    @Override
    public boolean isInitialized() {
        return initSuccess;
    }

    @Override
    public void reset() {
        // TODO: no native OSVR reset function
        // may need to take current position and negate it from future values
    }

    @Override
    public void getRenderSize(Vector2f store) {
        if( eyeLeftInfo == null || eyeLeftInfo.viewport.width == 0.0 ) {
            store.x = 1280f; store.y = 720f;
        } else {
            store.x = (float)eyeLeftInfo.viewport.width;
            store.y = (float)eyeLeftInfo.viewport.height;
        }
    }

    /**
     * Read and update the eye info from the underlying OSVR system.
     */
    public void getEyeInfo() {
        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerGetRenderInfoFromCollectionOpenGL(renderInfoCollection, EYE_LEFT_SIZE, eyeLeftInfo);
        OsvrRenderManagerOpenGLLibrary.osvrRenderManagerGetRenderInfoFromCollectionOpenGL(renderInfoCollection, EYE_RIGHT_SIZE, eyeRightInfo);
        eyeLeftInfo.read(); eyeRightInfo.read();
    }
/*
    @Override
    public float getFOV(int dir) {
        return 105f; //default FOV
    }
*/
    @Override
    public float getInterpupillaryDistance() {
        return 0.065f; //default IPD
    }

    @Override
    public Quaternion getOrientation() {
        storeRot.set((float)-hmdPose.rotation.data[1],
                     (float)hmdPose.rotation.data[2],
                     (float)-hmdPose.rotation.data[3],
                     (float)hmdPose.rotation.data[0]);
        if( storeRot.equals(Quaternion.ZERO) ) storeRot.set(Quaternion.DIRECTION_Z);
        return storeRot;
    }

    @Override
    public Vector3f getPosition() {
        storePos.x = (float)-hmdPose.translation.data[0];
        storePos.y = (float)hmdPose.translation.data[1];
        storePos.z = (float)-hmdPose.translation.data[2];
        return storePos;
    }

    @Override
    public void getPositionAndOrientation(Vector3f storePos, Quaternion storeRot) {
        storePos.x = (float)-hmdPose.translation.data[0];
        storePos.y = (float)hmdPose.translation.data[1];
        storePos.z = (float)-hmdPose.translation.data[2];
        storeRot.set((float)-hmdPose.rotation.data[1],
                     (float)hmdPose.rotation.data[2],
                     (float)-hmdPose.rotation.data[3],
                     (float)hmdPose.rotation.data[0]);
        if( storeRot.equals(Quaternion.ZERO) ) storeRot.set(Quaternion.DIRECTION_Z);
    }

    @Override
    public void updatePose() {
        if( context == null || displayConfig == null ) return;
        OsvrClientKitLibrary.osvrClientUpdate(context);
        OsvrDisplayLibrary.osvrClientGetViewerPose(displayConfig, FIRST_VIEWER, hmdPose.getPointer());
        VRinput.updateControllerStates();
        hmdPose.read();
    }

    @Override
    public Matrix4f getHMDMatrixProjectionLeftEye(Camera cam) {
        if( eyeLeftInfo == null ) return cam.getProjectionMatrix();
        if( eyeMatrix[EYE_LEFT] == null ) {
            FloatBuffer tfb = FloatBuffer.allocate(16);
            com.jme3.system.osvr.osvrdisplay.OsvrDisplayLibrary.osvrClientGetViewerEyeSurfaceProjectionMatrixf(displayConfig, 0, (byte)EYE_LEFT, 0, cam.getFrustumNear(), cam.getFrustumFar(), (short)0, tfb);
            eyeMatrix[EYE_LEFT] = new Matrix4f();
            eyeMatrix[EYE_LEFT].set(tfb.get(0), tfb.get(4), tfb.get(8), tfb.get(12),
                                    tfb.get(1), tfb.get(5), tfb.get(9), tfb.get(13),
                                    tfb.get(2), tfb.get(6), tfb.get(10), tfb.get(14),
                                    tfb.get(3), tfb.get(7), tfb.get(11), tfb.get(15));
        }
        return eyeMatrix[EYE_LEFT];
    }

    @Override
    public Matrix4f getHMDMatrixProjectionRightEye(Camera cam) {
        if( eyeRightInfo == null ) return cam.getProjectionMatrix();
        if( eyeMatrix[EYE_RIGHT] == null ) {
            FloatBuffer tfb = FloatBuffer.allocate(16);
            com.jme3.system.osvr.osvrdisplay.OsvrDisplayLibrary.osvrClientGetViewerEyeSurfaceProjectionMatrixf(displayConfig, 0, (byte)EYE_RIGHT, 0, cam.getFrustumNear(), cam.getFrustumFar(), (short)0, tfb);
            eyeMatrix[EYE_RIGHT] = new Matrix4f();
            eyeMatrix[EYE_RIGHT].set(tfb.get(0), tfb.get(4), tfb.get(8), tfb.get(12),
                                    tfb.get(1), tfb.get(5), tfb.get(9), tfb.get(13),
                                    tfb.get(2), tfb.get(6), tfb.get(10), tfb.get(14),
                                    tfb.get(3), tfb.get(7), tfb.get(11), tfb.get(15));
        }
        return eyeMatrix[EYE_RIGHT];
    }

    @Override
    public Vector3f getHMDVectorPoseLeftEye() {
        if( hmdPoseLeftEyeVec == null ) {
            hmdPoseLeftEyeVec = new Vector3f();
            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 = new Vector3f();
            hmdPoseRightEyeVec.x = 0.065f * 0.5f;
            if( flipEyes == false ) hmdPoseRightEyeVec.x *= -1f; // it seems these need flipping
        }
        return hmdPoseRightEyeVec;
    }

    @Override
    public Vector3f getSeatedToAbsolutePosition() {
        return Vector3f.ZERO;
    }

    @Override
    public Matrix4f getHMDMatrixPoseLeftEye() {
        // not actually used internally...
        /*if( hmdPoseLeftEye != null ) {
            return hmdPoseLeftEye;
        } else {
            FloatBuffer mat = FloatBuffer.allocate(16);
            OsvrDisplayLibrary.osvrClientGetViewerEyeViewMatrixf(displayConfig, FIRST_VIEWER, (byte)EYE_LEFT,
                     (short)(OsvrMatrixConventionsLibrary.OSVR_MatrixVectorFlags.OSVR_MATRIX_COLVECTORS |
                             OsvrMatrixConventionsLibrary.OSVR_MatrixOrderingFlags.OSVR_MATRIX_COLMAJOR), tempfb);
            hmdPoseLeftEye = new Matrix4f(tempfb.array());
            return hmdPoseLeftEye;
        }*/
        return null;
    }

    @Override
    public Matrix4f getHMDMatrixPoseRightEye() {
        // not actually used internally...
        /*if( hmdPoseRightEye != null ) {
            return hmdPoseRightEye;
        } else {
            OsvrDisplayLibrary.osvrClientGetViewerEyeViewMatrixf(displayConfig, FIRST_VIEWER, (byte)EYE_RIGHT,
                     (short)(OsvrMatrixConventionsLibrary.OSVR_MatrixVectorFlags.OSVR_MATRIX_COLVECTORS |
                             OsvrMatrixConventionsLibrary.OSVR_MatrixOrderingFlags.OSVR_MATRIX_COLMAJOR), tempfb);
            hmdPoseRightEye = new Matrix4f(tempfb.array());
            return hmdPoseRightEye;
        }*/
        return null;
    }

    @Override
    public HmdType getType() {
        return HmdType.OSVR;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy