913 lines · 30.8 KB · C++
RawDownload
////  OculusManager.cpp//  interface/src/devices////  Created by Stephen Birarda on 5/9/13.//  Refactored by Ben Arnold on 6/30/2014//  Copyright 2012 High Fidelity, Inc.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html// #include "InterfaceConfig.h"#include "OculusManager.h"#include "ui/overlays/Text3DOverlay.h" #include <CursorManager.h>#include <QDesktopWidget>#include <QGuiApplication>#include <QScreen>#include <QOpenGLTimerQuery> #include <glm/glm.hpp> #include <avatar/AvatarManager.h>#include <avatar/MyAvatar.h>#include <GlowEffect.h>#include <GlWindow.h>#include <gpu/GLBackend.h>#include <OglplusHelpers.h>#include <PathUtils.h>#include <SharedUtil.h>#include <UserActivityLogger.h> #include <OVR_CAPI_GL.h> #include "InterfaceLogging.h"#include "Application.h" template <typename Function>void for_each_eye(Function function) {    for (ovrEyeType eye = ovrEyeType::ovrEye_Left;        eye < ovrEyeType::ovrEye_Count;        eye = static_cast<ovrEyeType>(eye + 1)) {        function(eye);    }} template <typename Function>void for_each_eye(const ovrHmd & hmd, Function function) {    for (int i = 0; i < ovrEye_Count; ++i) {        ovrEyeType eye = hmd->EyeRenderOrder[i];        function(eye);    }}enum CalibrationState {    UNCALIBRATED,    WAITING_FOR_DELTA,    WAITING_FOR_ZERO,    WAITING_FOR_ZERO_HELD,    CALIBRATED}; inline glm::mat4 toGlm(const ovrMatrix4f & om) {    return glm::transpose(glm::make_mat4(&om.M[0][0]));} inline glm::mat4 toGlm(const ovrFovPort & fovport, float nearPlane = 0.01f, float farPlane = 10000.0f) {    return toGlm(ovrMatrix4f_Projection(fovport, nearPlane, farPlane, true));} inline glm::vec3 toGlm(const ovrVector3f & ov) {    return glm::make_vec3(&ov.x);} inline glm::vec2 toGlm(const ovrVector2f & ov) {    return glm::make_vec2(&ov.x);} inline glm::ivec2 toGlm(const ovrVector2i & ov) {    return glm::ivec2(ov.x, ov.y);} inline glm::uvec2 toGlm(const ovrSizei & ov) {    return glm::uvec2(ov.w, ov.h);} inline glm::quat toGlm(const ovrQuatf & oq) {    return glm::make_quat(&oq.x);} inline glm::mat4 toGlm(const ovrPosef & op) {    glm::mat4 orientation = glm::mat4_cast(toGlm(op.Orientation));    glm::mat4 translation = glm::translate(glm::mat4(), toGlm(op.Position));    return translation * orientation;} inline ovrMatrix4f ovrFromGlm(const glm::mat4 & m) {    ovrMatrix4f result;    glm::mat4 transposed(glm::transpose(m));    memcpy(result.M, &(transposed[0][0]), sizeof(float) * 16);    return result;} inline ovrVector3f ovrFromGlm(const glm::vec3 & v) {    return{ v.x, v.y, v.z };} inline ovrVector2f ovrFromGlm(const glm::vec2 & v) {    return{ v.x, v.y };} inline ovrSizei ovrFromGlm(const glm::uvec2 & v) {    return{ (int)v.x, (int)v.y };} inline ovrQuatf ovrFromGlm(const glm::quat & q) {    return{ q.x, q.y, q.z, q.w };}   #ifdef Q_OS_WIN // A base class for FBO wrappers that need to use the Oculus C// API to manage textures via ovrHmd_CreateSwapTextureSetGL,// ovrHmd_CreateMirrorTextureGL, etctemplate <typename C>struct RiftFramebufferWrapper : public FramebufferWrapper<C, char> {    ovrHmd hmd;    RiftFramebufferWrapper(const ovrHmd & hmd) : hmd(hmd) {        color = 0;        depth = 0;    };     void Resize(const uvec2 & size) {        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);        this->size = size;        initColor();        initDone();    } protected:    virtual void initDepth() override final {    }}; // A wrapper for constructing and using a swap texture set,// where each frame you draw to a texture via the FBO,// then submit it and increment to the next texture.// The Oculus SDK manages the creation and destruction of// the texturesstruct SwapFramebufferWrapper : public RiftFramebufferWrapper<ovrSwapTextureSet*> {    SwapFramebufferWrapper(const ovrHmd & hmd)        : RiftFramebufferWrapper(hmd) {    }     ~SwapFramebufferWrapper() {        if (color) {            ovrHmd_DestroySwapTextureSet(hmd, color);            color = nullptr;        }    }     void Increment() {        ++color->CurrentIndex;        color->CurrentIndex %= color->TextureCount;    } protected:    virtual void initColor() override {        if (color) {            ovrHmd_DestroySwapTextureSet(hmd, color);            color = nullptr;        }         ovrResult result = ovrHmd_CreateSwapTextureSetGL(hmd, GL_RGBA, size.x, size.y, &color);        Q_ASSERT(OVR_SUCCESS(result));         for (int i = 0; i < color->TextureCount; ++i) {            ovrGLTexture& ovrTex = (ovrGLTexture&)color->Textures[i];            glBindTexture(GL_TEXTURE_2D, ovrTex.OGL.TexId);            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);        }        glBindTexture(GL_TEXTURE_2D, 0);    }     virtual void initDone() override {    }     virtual void onBind(oglplus::Framebuffer::Target target) override {        ovrGLTexture& tex = (ovrGLTexture&)(color->Textures[color->CurrentIndex]);        glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex.OGL.TexId, 0);    }     virtual void onUnbind(oglplus::Framebuffer::Target target) override {        glFramebufferTexture2D(toEnum(target), GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);    }};  // We use a FBO to wrap the mirror texture because it makes it easier to// render to the screen via glBlitFramebufferstruct MirrorFramebufferWrapper : public RiftFramebufferWrapper<ovrGLTexture*> {    MirrorFramebufferWrapper(const ovrHmd & hmd)        : RiftFramebufferWrapper(hmd) {    }     virtual ~MirrorFramebufferWrapper() {        if (color) {            ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);            color = nullptr;        }    } private:    void initColor() override {        if (color) {            ovrHmd_DestroyMirrorTexture(hmd, (ovrTexture*)color);            color = nullptr;        }        ovrResult result = ovrHmd_CreateMirrorTextureGL(hmd, GL_RGBA, size.x, size.y, (ovrTexture**)&color);        Q_ASSERT(OVR_SUCCESS(result));    }     void initDone() override {        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, oglplus::GetName(fbo));        glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color->OGL.TexId, 0);        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);    }}; static SwapFramebufferWrapper* _swapFbo{ nullptr };static MirrorFramebufferWrapper* _mirrorFbo{ nullptr };static ovrLayerEyeFov _sceneLayer; #else static ovrTexture _eyeTextures[ovrEye_Count];static GlWindow* _outputWindow{ nullptr }; #endif static bool _isConnected = false;static ovrHmd _ovrHmd;static ovrFovPort _eyeFov[ovrEye_Count];static ovrVector3f _eyeOffset[ovrEye_Count];static ovrEyeRenderDesc _eyeRenderDesc[ovrEye_Count];static ovrSizei _renderTargetSize;static glm::mat4 _eyeProjection[ovrEye_Count];static unsigned int _frameIndex = 0;static bool _frameTimingActive = false;static Camera* _camera = NULL;static ovrEyeType _activeEye = ovrEye_Count;static bool _hswDismissed = false; static const float CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;static const float CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;static const float CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;static const float CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;static const quint64 CALIBRATION_ZERO_HOLD_TIME = 3000000; // usecstatic const float CALIBRATION_MESSAGE_DISTANCE = 2.5f;static CalibrationState _calibrationState;static glm::vec3 _calibrationPosition;static glm::quat _calibrationOrientation;static quint64 _calibrationStartTime;static int _calibrationMessage = 0;static glm::vec3 _eyePositions[ovrEye_Count];// TODO expose this as a developer togglestatic bool _eyePerFrameMode = false;static ovrEyeType _lastEyeRendered = ovrEye_Count;static ovrSizei _recommendedTexSize = { 0, 0 };static float _offscreenRenderScale = 1.0;static glm::mat4 _combinedProjection;static ovrPosef _eyeRenderPoses[ovrEye_Count];static ovrRecti _eyeViewports[ovrEye_Count];static ovrVector3f _eyeOffsets[ovrEye_Count]; glm::vec3 OculusManager::getLeftEyePosition() { return _eyePositions[ovrEye_Left]; }glm::vec3 OculusManager::getRightEyePosition() { return _eyePositions[ovrEye_Right]; } void OculusManager::connect(QOpenGLContext* shareContext) {    qCDebug(interfaceapp) << "Oculus SDK" << OVR_VERSION_STRING;     ovrInitParams initParams; memset(&initParams, 0, sizeof(initParams)); #ifdef DEBUG    initParams.Flags |= ovrInit_Debug;#endif     ovr_Initialize(&initParams); #ifdef Q_OS_WIN     ovrResult res = ovrHmd_Create(0, &_ovrHmd);#ifdef DEBUG    if (!OVR_SUCCESS(res)) {        res = ovrHmd_CreateDebug(ovrHmd_DK2, &_ovrHmd);        Q_ASSERT(OVR_SUCCESS(res));    }#endif #else     _ovrHmd = ovrHmd_Create(0);#ifdef DEBUG    if (!_ovrHmd) {        _ovrHmd = ovrHmd_CreateDebug(ovrHmd_DK2);    }#endif    #endif     if (!_ovrHmd) {        _isConnected = false;         // we're definitely not in "VR mode" so tell the menu that        Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false);        ovr_Shutdown();        return;    }     _calibrationState = UNCALIBRATED;    if (!_isConnected) {        UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");    }    _isConnected = true;     for_each_eye([&](ovrEyeType eye) {        _eyeFov[eye] = _ovrHmd->DefaultEyeFov[eye];        _eyeProjection[eye] = toGlm(ovrMatrix4f_Projection(_eyeFov[eye],             DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));        ovrEyeRenderDesc erd = ovrHmd_GetRenderDesc(_ovrHmd, eye, _eyeFov[eye]);        _eyeOffsets[eye] = erd.HmdToEyeViewOffset;    });    ovrFovPort combinedFov = _ovrHmd->MaxEyeFov[0];    combinedFov.RightTan = _ovrHmd->MaxEyeFov[1].RightTan;    _combinedProjection = toGlm(ovrMatrix4f_Projection(combinedFov,         DEFAULT_NEAR_CLIP, DEFAULT_FAR_CLIP, ovrProjection_RightHanded));     _recommendedTexSize = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left, _eyeFov[ovrEye_Left], 1.0f);    _renderTargetSize = { _recommendedTexSize.w * 2, _recommendedTexSize.h }; #ifdef Q_OS_WIN     _mirrorFbo = new MirrorFramebufferWrapper(_ovrHmd);    _swapFbo = new SwapFramebufferWrapper(_ovrHmd);    _swapFbo->Init(toGlm(_renderTargetSize));    _sceneLayer.ColorTexture[0] = _swapFbo->color;    _sceneLayer.ColorTexture[1] = nullptr;    _sceneLayer.Viewport[0].Pos = { 0, 0 };    _sceneLayer.Viewport[0].Size = _recommendedTexSize;    _sceneLayer.Viewport[1].Pos = { _recommendedTexSize.w, 0 };    _sceneLayer.Viewport[1].Size = _recommendedTexSize;    _sceneLayer.Header.Type = ovrLayerType_EyeFov;    _sceneLayer.Header.Flags = ovrLayerFlag_TextureOriginAtBottomLeft;    for_each_eye([&](ovrEyeType eye) {        _eyeViewports[eye] = _sceneLayer.Viewport[eye];        _sceneLayer.Fov[eye] = _eyeFov[eye];    });   #else    _outputWindow = new GlWindow(shareContext);    _outputWindow->show();//    _outputWindow->setFlags(Qt::FramelessWindowHint );//    _outputWindow->resize(_ovrHmd->Resolution.w, _ovrHmd->Resolution.h);//    _outputWindow->setPosition(_ovrHmd->WindowsPos.x, _ovrHmd->WindowsPos.y);    ivec2 desiredPosition = toGlm(_ovrHmd->WindowsPos);    foreach(QScreen* screen, qGuiApp->screens()) {        ivec2 screenPosition = toGlm(screen->geometry().topLeft());        if (screenPosition == desiredPosition) {            _outputWindow->setScreen(screen);            break;        }    }    _outputWindow->showFullScreen();    _outputWindow->makeCurrent();        ovrGLConfig cfg;    memset(&cfg, 0, sizeof(cfg));    cfg.OGL.Header.API = ovrRenderAPI_OpenGL;    cfg.OGL.Header.BackBufferSize = _ovrHmd->Resolution;    cfg.OGL.Header.Multisample = 0;     int distortionCaps = 0        | ovrDistortionCap_Vignette        | ovrDistortionCap_Overdrive        | ovrDistortionCap_TimeWarp;     int configResult = ovrHmd_ConfigureRendering(_ovrHmd, &cfg.Config,        distortionCaps, _eyeFov, _eyeRenderDesc);    assert(configResult);    _outputWindow->doneCurrent();     for_each_eye([&](ovrEyeType eye) {        //Get texture size        _eyeTextures[eye].Header.API = ovrRenderAPI_OpenGL;        _eyeTextures[eye].Header.TextureSize = _renderTargetSize;        _eyeTextures[eye].Header.RenderViewport.Pos = { 0, 0 };        _eyeTextures[eye].Header.RenderViewport.Size = _renderTargetSize;        _eyeTextures[eye].Header.RenderViewport.Size.w /= 2;    });    _eyeTextures[ovrEye_Right].Header.RenderViewport.Pos.x = _recommendedTexSize.w;    for_each_eye([&](ovrEyeType eye) {        _eyeViewports[eye] = _eyeTextures[eye].Header.RenderViewport;    });#endif     ovrHmd_SetEnabledCaps(_ovrHmd,            ovrHmdCap_LowPersistence | ovrHmdCap_DynamicPrediction);     ovrHmd_ConfigureTracking(_ovrHmd,            ovrTrackingCap_Orientation | ovrTrackingCap_Position | ovrTrackingCap_MagYawCorrection,            ovrTrackingCap_Orientation);     if (!_camera) {        _camera = new Camera;        configureCamera(*_camera); // no need to use screen dimensions; they're ignored    }} //Disconnects and deallocates the ORvoid OculusManager::disconnect() {    if (_isConnected) { #ifdef Q_OS_WIN        if (_swapFbo) {            delete _swapFbo;            _swapFbo = nullptr;        }         if (_mirrorFbo) {            delete _mirrorFbo;            _mirrorFbo = nullptr;        }#else        _outputWindow->showNormal();        _outputWindow->deleteLater();        _outputWindow = nullptr;#endif         if (_ovrHmd) {            ovrHmd_Destroy(_ovrHmd);            _ovrHmd = nullptr;        }        ovr_Shutdown();            _isConnected = false;        // Prepare to potentially have to dismiss the HSW again        // if the user re-enables VR        _hswDismissed = false;    }} void positionCalibrationBillboard(Text3DOverlay* billboard) {    MyAvatar* myAvatar = DependencyManager::get<AvatarManager>()->getMyAvatar();    glm::quat headOrientation = myAvatar->getHeadOrientation();    headOrientation.x = 0;    headOrientation.z = 0;    glm::normalize(headOrientation);    billboard->setPosition(myAvatar->getHeadPosition()        + headOrientation * glm::vec3(0.0f, 0.0f, -CALIBRATION_MESSAGE_DISTANCE));    billboard->setRotation(headOrientation);} void calibrate(const glm::vec3& position, const glm::quat& orientation) {    static QString instructionMessage = "Hold still to calibrate";    static QString progressMessage;    static Text3DOverlay* billboard;     switch (_calibrationState) {         case UNCALIBRATED:            if (position != glm::vec3() && orientation != glm::quat()) {  // Handle zero values at start-up.                _calibrationPosition = position;                _calibrationOrientation = orientation;                _calibrationState = WAITING_FOR_DELTA;            }            break;         case WAITING_FOR_DELTA:            if (glm::length(position - _calibrationPosition) > CALIBRATION_DELTA_MINIMUM_LENGTH                || glm::angle(orientation * glm::inverse(_calibrationOrientation)) > CALIBRATION_DELTA_MINIMUM_ANGLE) {                _calibrationPosition = position;                _calibrationOrientation = orientation;                _calibrationState = WAITING_FOR_ZERO;            }            break;         case WAITING_FOR_ZERO:            if (glm::length(position - _calibrationPosition) < CALIBRATION_ZERO_MAXIMUM_LENGTH                && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) {                _calibrationStartTime = usecTimestampNow();

Lines 1–500 of 913.