//// 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. Load more
// // 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; // usec float 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 OR void 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_LIBOVR void 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_LIBOVR void 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_LIBOVR void 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 purposes void 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 timing void OculusManager::endFrameTiming() { #ifdef HAVE_LIBOVR ovrHmd_EndFrameTiming(_ovrHmd); _frameIndex++; _frameTimingActive = false; #endif } //Sets the camera FoV and aspect ratio void 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 active void 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);