777 lines · 30.0 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 <QDesktopWidget>#include <QGuiApplication>#include <QOpenGLFramebufferObject>#include <QScreen> #include <glm/glm.hpp> #include <SharedUtil.h>#include <UserActivityLogger.h> #include "Application.h" #ifdef HAVE_LIBOVR using namespace OVR; ProgramObject OculusManager::_program;int OculusManager::_textureLocation;int OculusManager::_eyeToSourceUVScaleLocation;int OculusManager::_eyeToSourceUVOffsetLocation;int OculusManager::_eyeRotationStartLocation;int OculusManager::_eyeRotationEndLocation;int OculusManager::_positionAttributeLocation;int OculusManager::_colorAttributeLocation;int OculusManager::_texCoord0AttributeLocation;int OculusManager::_texCoord1AttributeLocation;int OculusManager::_texCoord2AttributeLocation;bool OculusManager::_isConnected = false; ovrHmd OculusManager::_ovrHmd;ovrHmdDesc OculusManager::_ovrHmdDesc;ovrFovPort OculusManager::_eyeFov[ovrEye_Count];ovrEyeRenderDesc OculusManager::_eyeRenderDesc[ovrEye_Count];ovrSizei OculusManager::_renderTargetSize;ovrVector2f OculusManager::_UVScaleOffset[ovrEye_Count][2];GLuint OculusManager::_vertices[ovrEye_Count] = { 0, 0 };GLuint OculusManager::_indices[ovrEye_Count] = { 0, 0 };GLsizei OculusManager::_meshSize[ovrEye_Count] = { 0, 0 };ovrFrameTiming OculusManager::_hmdFrameTiming;ovrRecti OculusManager::_eyeRenderViewport[ovrEye_Count];unsigned int OculusManager::_frameIndex = 0;bool OculusManager::_frameTimingActive = false;bool OculusManager::_programInitialized = false;Camera* OculusManager::_camera = NULL;int OculusManager::_activeEyeIndex = -1; float OculusManager::CALIBRATION_DELTA_MINIMUM_LENGTH = 0.02f;float OculusManager::CALIBRATION_DELTA_MINIMUM_ANGLE = 5.0f * RADIANS_PER_DEGREE;float OculusManager::CALIBRATION_ZERO_MAXIMUM_LENGTH = 0.01f;float OculusManager::CALIBRATION_ZERO_MAXIMUM_ANGLE = 2.0f * RADIANS_PER_DEGREE;quint64 OculusManager::CALIBRATION_ZERO_HOLD_TIME = 3000000; // usecfloat OculusManager::CALIBRATION_MESSAGE_DISTANCE = 2.5f;OculusManager::CalibrationState OculusManager::_calibrationState;glm::vec3 OculusManager::_calibrationPosition;glm::quat OculusManager::_calibrationOrientation;quint64 OculusManager::_calibrationStartTime;int OculusManager::_calibrationMessage = NULL; #endif glm::vec3 OculusManager::_leftEyePosition = glm::vec3();glm::vec3 OculusManager::_rightEyePosition = glm::vec3(); void OculusManager::connect() {#ifdef HAVE_LIBOVR    _calibrationState = UNCALIBRATED;    qDebug() << "Oculus SDK" << OVR_VERSION_STRING;    ovr_Initialize();     _ovrHmd = ovrHmd_Create(0);    if (_ovrHmd) {        if (!_isConnected) {            UserActivityLogger::getInstance().connectedDevice("hmd", "oculus");        }        _isConnected = true;        #if defined(__APPLE__) || defined(_WIN32)        _eyeFov[0] = _ovrHmd->DefaultEyeFov[0];        _eyeFov[1] = _ovrHmd->DefaultEyeFov[1];#else        ovrHmd_GetDesc(_ovrHmd, &_ovrHmdDesc);        _eyeFov[0] = _ovrHmdDesc.DefaultEyeFov[0];        _eyeFov[1] = _ovrHmdDesc.DefaultEyeFov[1];#endif        //Get texture size        ovrSizei recommendedTex0Size = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Left,                                                                _eyeFov[0], 1.0f);        ovrSizei recommendedTex1Size = ovrHmd_GetFovTextureSize(_ovrHmd, ovrEye_Right,                                                                _eyeFov[1], 1.0f);        _renderTargetSize.w = recommendedTex0Size.w + recommendedTex1Size.w;        _renderTargetSize.h = recommendedTex0Size.h;        if (_renderTargetSize.h < recommendedTex1Size.h) {            _renderTargetSize.h = recommendedTex1Size.h;        }         _eyeRenderDesc[0] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Left, _eyeFov[0]);        _eyeRenderDesc[1] = ovrHmd_GetRenderDesc(_ovrHmd, ovrEye_Right, _eyeFov[1]); #if defined(__APPLE__) || defined(_WIN32)        ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence);#else        ovrHmd_SetEnabledCaps(_ovrHmd, ovrHmdCap_LowPersistence | ovrHmdCap_LatencyTest);#endif #if defined(__APPLE__) || defined(_WIN32)        ovrHmd_ConfigureTracking(_ovrHmd, ovrTrackingCap_Orientation | ovrTrackingCap_Position |                                 ovrTrackingCap_MagYawCorrection,                                 ovrTrackingCap_Orientation);#else        ovrHmd_StartSensor(_ovrHmd, ovrSensorCap_Orientation | ovrSensorCap_YawCorrection |                           ovrSensorCap_Position,                           ovrSensorCap_Orientation);#endif         if (!_camera) {            _camera = new Camera;            configureCamera(*_camera, 0, 0); // no need to use screen dimensions; they're ignored        }         if (!_programInitialized) {            // Shader program            _programInitialized = true;            _program.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/oculus.vert");            _program.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/oculus.frag");            _program.link();             // Uniforms            _textureLocation = _program.uniformLocation("texture");            _eyeToSourceUVScaleLocation = _program.uniformLocation("EyeToSourceUVScale");            _eyeToSourceUVOffsetLocation = _program.uniformLocation("EyeToSourceUVOffset");            _eyeRotationStartLocation = _program.uniformLocation("EyeRotationStart");            _eyeRotationEndLocation = _program.uniformLocation("EyeRotationEnd");             // Attributes            _positionAttributeLocation = _program.attributeLocation("position");            _colorAttributeLocation = _program.attributeLocation("color");            _texCoord0AttributeLocation = _program.attributeLocation("texCoord0");            _texCoord1AttributeLocation = _program.attributeLocation("texCoord1");            _texCoord2AttributeLocation = _program.attributeLocation("texCoord2");        }         //Generate the distortion VBOs        generateDistortionMesh();     } else {        _isConnected = false;                // we're definitely not in "VR mode" so tell the menu that        Menu::getInstance()->getActionForOption(MenuOption::EnableVRMode)->setChecked(false);                ovrHmd_Destroy(_ovrHmd);        ovr_Shutdown();    }#endif} //Disconnects and deallocates the ORvoid OculusManager::disconnect() {#ifdef HAVE_LIBOVR    if (_isConnected) {        _isConnected = false;        ovrHmd_Destroy(_ovrHmd);        ovr_Shutdown();         //Free the distortion mesh data        for (int i = 0; i < ovrEye_Count; i++) {            if (_vertices[i] != 0) {                glDeleteBuffers(1, &(_vertices[i]));                _vertices[i] = 0;            }            if (_indices[i] != 0) {                glDeleteBuffers(1, &(_indices[i]));                _indices[i] = 0;            }        }    }#endif} #ifdef HAVE_LIBOVRvoid OculusManager::positionCalibrationBillboard(Text3DOverlay* billboard) {    glm::quat headOrientation = Application::getInstance()->getAvatar()->getHeadOrientation();    headOrientation.x = 0;    headOrientation.z = 0;    glm::normalize(headOrientation);    billboard->setPosition(Application::getInstance()->getAvatar()->getHeadPosition()        + headOrientation * glm::vec3(0.0f, 0.0f, -CALIBRATION_MESSAGE_DISTANCE));    billboard->setRotation(headOrientation);}#endif #ifdef HAVE_LIBOVRvoid OculusManager::calibrate(glm::vec3 position, 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();                _calibrationState = WAITING_FOR_ZERO_HELD;                 if (!_calibrationMessage) {                    qDebug() << "Hold still to calibrate HMD";                     billboard = new Text3DOverlay();                    billboard->setDimensions(glm::vec2(2.0f, 1.25f));                    billboard->setTopMargin(0.35f);                    billboard->setLeftMargin(0.28f);                    billboard->setText(instructionMessage);                    billboard->setAlpha(0.5f);                    billboard->setLineHeight(0.1f);                    billboard->setIsFacingAvatar(false);                    positionCalibrationBillboard(billboard);                     _calibrationMessage = Application::getInstance()->getOverlays().addOverlay(billboard);                }                 progressMessage = "";            } else {                _calibrationPosition = position;                _calibrationOrientation = orientation;            }            break;         case WAITING_FOR_ZERO_HELD:            if (glm::length(position - _calibrationPosition) < CALIBRATION_ZERO_MAXIMUM_LENGTH                && glm::angle(orientation * glm::inverse(_calibrationOrientation)) < CALIBRATION_ZERO_MAXIMUM_ANGLE) {                if ((usecTimestampNow() - _calibrationStartTime) > CALIBRATION_ZERO_HOLD_TIME) {                    _calibrationState = CALIBRATED;                    qDebug() << "HMD calibrated";                    Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage);                    _calibrationMessage = NULL;                    Application::getInstance()->resetSensors();                } else {                    quint64 quarterSeconds = (usecTimestampNow() - _calibrationStartTime) / 250000;                    if (quarterSeconds + 1 > progressMessage.length()) {                        // 3...2...1...                        if (quarterSeconds == 4 * (quarterSeconds / 4)) {                            quint64 wholeSeconds = CALIBRATION_ZERO_HOLD_TIME / 1000000 - quarterSeconds / 4;                             if (wholeSeconds == 3) {                                positionCalibrationBillboard(billboard);                            }                             progressMessage += QString::number(wholeSeconds);                        } else {                            progressMessage += ".";                        }                        billboard->setText(instructionMessage + "\n\n" + progressMessage);                    }                }            } else {                _calibrationPosition = position;                _calibrationOrientation = orientation;                _calibrationState = WAITING_FOR_ZERO;            }            break;        default:            break;                }}#endif void OculusManager::recalibrate() {#ifdef HAVE_LIBOVR    _calibrationState = UNCALIBRATED;#endif} void OculusManager::abandonCalibration() {#ifdef HAVE_LIBOVR    _calibrationState = CALIBRATED;    if (_calibrationMessage) {        qDebug() << "Abandoned HMD calibration";        Application::getInstance()->getOverlays().deleteOverlay(_calibrationMessage);        _calibrationMessage = NULL;    }#endif} #ifdef HAVE_LIBOVRvoid OculusManager::generateDistortionMesh() {     //Check if we already have the distortion mesh    if (_vertices[0] != 0) {        printf("WARNING: Tried to generate Oculus distortion mesh twice without freeing the VBOs.");        return;    }     //Viewport for the render target for each eye    _eyeRenderViewport[0].Pos = Vector2i(0, 0);    _eyeRenderViewport[0].Size = Sizei(_renderTargetSize.w / 2, _renderTargetSize.h);    _eyeRenderViewport[1].Pos = Vector2i((_renderTargetSize.w + 1) / 2, 0);    _eyeRenderViewport[1].Size = _eyeRenderViewport[0].Size;     for (int eyeNum = 0; eyeNum < ovrEye_Count; eyeNum++) {        // Allocate and generate distortion mesh vertices        ovrDistortionMesh meshData;        ovrHmd_CreateDistortionMesh(_ovrHmd, _eyeRenderDesc[eyeNum].Eye, _eyeRenderDesc[eyeNum].Fov, _ovrHmdDesc.DistortionCaps, &meshData);                ovrHmd_GetRenderScaleAndOffset(_eyeRenderDesc[eyeNum].Fov, _renderTargetSize, _eyeRenderViewport[eyeNum],                                       _UVScaleOffset[eyeNum]);         // Parse the vertex data and create a render ready vertex buffer        DistortionVertex* pVBVerts = (DistortionVertex*)OVR_ALLOC(sizeof(DistortionVertex) * meshData.VertexCount);        _meshSize[eyeNum] = meshData.IndexCount;         // Convert the oculus vertex data to the DistortionVertex format.        DistortionVertex* v = pVBVerts;        ovrDistortionVertex* ov = meshData.pVertexData;        for (unsigned int vertNum = 0; vertNum < meshData.VertexCount; vertNum++) {#if defined(__APPLE__) || defined(_WIN32)            v->pos.x = ov->ScreenPosNDC.x;            v->pos.y = ov->ScreenPosNDC.y;            v->texR.x = ov->TanEyeAnglesR.x;            v->texR.y = ov->TanEyeAnglesR.y;            v->texG.x = ov->TanEyeAnglesG.x;            v->texG.y = ov->TanEyeAnglesG.y;            v->texB.x = ov->TanEyeAnglesB.x;            v->texB.y = ov->TanEyeAnglesB.y;#else            v->pos.x = ov->Pos.x;            v->pos.y = ov->Pos.y;            v->texR.x = ov->TexR.x;            v->texR.y = ov->TexR.y;            v->texG.x = ov->TexG.x;            v->texG.y = ov->TexG.y;            v->texB.x = ov->TexB.x;            v->texB.y = ov->TexB.y;#endif            v->color.r = v->color.g = v->color.b = (GLubyte)(ov->VignetteFactor * 255.99f);            v->color.a = (GLubyte)(ov->TimeWarpFactor * 255.99f);            v++;             ov++;        }         //vertices        glGenBuffers(1, &(_vertices[eyeNum]));        glBindBuffer(GL_ARRAY_BUFFER, _vertices[eyeNum]);        glBufferData(GL_ARRAY_BUFFER, sizeof(DistortionVertex) * meshData.VertexCount, pVBVerts, GL_STATIC_DRAW);        glBindBuffer(GL_ARRAY_BUFFER, 0);         //indices        glGenBuffers(1, &(_indices[eyeNum]));        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _indices[eyeNum]);        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned short) * meshData.IndexCount, meshData.pIndexData, GL_STATIC_DRAW);        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);                //Now that we have the VBOs we can get rid of the mesh data        OVR_FREE(pVBVerts);        ovrHmd_DestroyDistortionMesh(&meshData);    } }#endif bool OculusManager::isConnected() {#ifdef HAVE_LIBOVR    return _isConnected && Menu::getInstance()->isOptionChecked(MenuOption::EnableVRMode);#else    return false;#endif} //Begins the frame timing for oculus prediction purposesvoid OculusManager::beginFrameTiming() {#ifdef HAVE_LIBOVR     if (_frameTimingActive) {        printf("WARNING: Called OculusManager::beginFrameTiming() twice in a row, need to call OculusManager::endFrameTiming().");    }    _hmdFrameTiming = ovrHmd_BeginFrameTiming(_ovrHmd, _frameIndex);   _frameTimingActive = true;#endif} //Ends frame timingvoid OculusManager::endFrameTiming() {#ifdef HAVE_LIBOVR    ovrHmd_EndFrameTiming(_ovrHmd);    _frameIndex++;    _frameTimingActive = false;#endif} //Sets the camera FoV and aspect ratiovoid OculusManager::configureCamera(Camera& camera, int screenWidth, int screenHeight) {#ifdef HAVE_LIBOVR    camera.setAspectRatio(_renderTargetSize.w * 0.5f / _renderTargetSize.h);    camera.setFieldOfView(atan(_eyeFov[0].UpTan) * DEGREES_PER_RADIAN * 2.0f);#endif    } //Displays everything for the oculus, frame timing must be activevoid OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &position, Camera& whichCamera) {#ifdef HAVE_LIBOVR    //beginFrameTiming must be called before display    if (!_frameTimingActive) {        printf("WARNING: Called OculusManager::display() without calling OculusManager::beginFrameTiming() first.");        return;    }     ApplicationOverlay& applicationOverlay = Application::getInstance()->getApplicationOverlay();     // We only need to render the overlays to a texture once, then we just render the texture on the hemisphere    // PrioVR will only work if renderOverlay is called, calibration is connected to Application::renderingOverlay()     applicationOverlay.renderOverlay(true);       //Bind our framebuffer object. If we are rendering the glow effect, we let the glow effect shader take care of it    if (Menu::getInstance()->isOptionChecked(MenuOption::EnableGlowEffect)) {        Application::getInstance()->getGlowEffect()->prepare();    } else {        Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind();        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);    }     ovrPosef eyeRenderPose[ovrEye_Count];     glMatrixMode(GL_PROJECTION);    glPushMatrix();     glMatrixMode(GL_MODELVIEW);    glPushMatrix();      glm::quat orientation;    glm::vec3 trackerPosition;    #if defined(__APPLE__) || defined(_WIN32)    ovrTrackingState ts = ovrHmd_GetTrackingState(_ovrHmd, ovr_GetTimeInSeconds());    ovrVector3f ovrHeadPosition = ts.HeadPose.ThePose.Position;        trackerPosition = glm::vec3(ovrHeadPosition.x, ovrHeadPosition.y, ovrHeadPosition.z);     if (_calibrationState != CALIBRATED) {        ovrQuatf ovrHeadOrientation = ts.HeadPose.ThePose.Orientation;        orientation = glm::quat(ovrHeadOrientation.w, ovrHeadOrientation.x, ovrHeadOrientation.y, ovrHeadOrientation.z);        calibrate(trackerPosition, orientation);    }     trackerPosition = bodyOrientation * trackerPosition;#endif        //Render each eye into an fbo    for (int eyeIndex = 0; eyeIndex < ovrEye_Count; eyeIndex++) {        _activeEyeIndex = eyeIndex;        #if defined(__APPLE__) || defined(_WIN32)        ovrEyeType eye = _ovrHmd->EyeRenderOrder[eyeIndex];#else        ovrEyeType eye = _ovrHmdDesc.EyeRenderOrder[eyeIndex];#endif        // Set the camera rotation for this eye        eyeRenderPose[eye] = ovrHmd_GetEyePose(_ovrHmd, eye);        orientation.x = eyeRenderPose[eye].Orientation.x;        orientation.y = eyeRenderPose[eye].Orientation.y;        orientation.z = eyeRenderPose[eye].Orientation.z;        orientation.w = eyeRenderPose[eye].Orientation.w;                // Update the application camera with the latest HMD position        whichCamera.setHmdPosition(trackerPosition);        whichCamera.setHmdRotation(orientation);

Lines 1–500 of 777.