diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 55fe438cfe..0c4cc71190 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -1089,6 +1089,9 @@ void Application::keyPressEvent(QKeyEvent* event) { case Qt::Key_Equal: _myAvatar->resetSize(); break; + case Qt::Key_Escape: + OculusManager::abandonCalibration(); + break; default: event->ignore(); break; @@ -1481,6 +1484,9 @@ void Application::setEnableVRMode(bool enableVRMode) { OculusManager::disconnect(); OculusManager::connect(); } + OculusManager::recalibrate(); + } else { + OculusManager::abandonCalibration(); } resizeGL(_glWidget->getDeviceWidth(), _glWidget->getDeviceHeight()); diff --git a/interface/src/Application.h b/interface/src/Application.h index 248c4325bf..7277c87b2e 100644 --- a/interface/src/Application.h +++ b/interface/src/Application.h @@ -365,6 +365,8 @@ public slots: void domainSettingsReceived(const QJsonObject& domainSettingsObject); + void resetSensors(); + private slots: void timer(); void idle(); @@ -381,7 +383,6 @@ private slots: void closeMirrorView(); void restoreMirrorView(); void shrinkMirrorView(); - void resetSensors(); void parseVersionXml(); diff --git a/interface/src/devices/OculusManager.cpp b/interface/src/devices/OculusManager.cpp index ad6a914112..1f43979c03 100644 --- a/interface/src/devices/OculusManager.cpp +++ b/interface/src/devices/OculusManager.cpp @@ -55,6 +55,20 @@ 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.f * 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; +QString OculusManager::CALIBRATION_BILLBOARD_URL = "http://hifi-public.s3.amazonaws.com/images/hold-to-calibrate.svg"; +float OculusManager::CALIBRATION_BILLBOARD_SCALE = 2.f; + #endif glm::vec3 OculusManager::_leftEyePosition = glm::vec3(); @@ -62,6 +76,8 @@ glm::vec3 OculusManager::_rightEyePosition = glm::vec3(); void OculusManager::connect() { #ifdef HAVE_LIBOVR + _calibrationState = UNCALIBRATED; + ovr_Initialize(); _ovrHmd = ovrHmd_Create(0); @@ -172,6 +188,121 @@ void OculusManager::disconnect() { #endif } +#ifdef HAVE_LIBOVR +void OculusManager::positionCalibrationBillboard(BillboardOverlay* 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.f, 0.f, -CALIBRATION_MESSAGE_DISTANCE)); + billboard->setRotation(headOrientation); +} +#endif + +#ifdef HAVE_LIBOVR +void OculusManager::calibrate(glm::vec3 position, glm::quat orientation) { + static QString progressMessage; + static BillboardOverlay* 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 BillboardOverlay(); + billboard->setURL(CALIBRATION_BILLBOARD_URL); + billboard->setScale(CALIBRATION_BILLBOARD_SCALE); + 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 += "."; + } + //qDebug() << progressMessage; // Progress message ready for 3D text overlays. + } + } + } else { + _calibrationPosition = position; + _calibrationOrientation = orientation; + _calibrationState = WAITING_FOR_ZERO; + } + 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() { @@ -325,6 +456,13 @@ void OculusManager::display(const glm::quat &bodyOrientation, const glm::vec3 &p 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 diff --git a/interface/src/devices/OculusManager.h b/interface/src/devices/OculusManager.h index 604580a24e..dfe4a212b6 100644 --- a/interface/src/devices/OculusManager.h +++ b/interface/src/devices/OculusManager.h @@ -18,6 +18,7 @@ #endif #include "renderer/ProgramObject.h" +#include "ui/overlays/BillboardOverlay.h" const float DEFAULT_OCULUS_UI_ANGULAR_SIZE = 72.0f; @@ -30,6 +31,8 @@ public: static void connect(); static void disconnect(); static bool isConnected(); + static void recalibrate(); + static void abandonCalibration(); static void beginFrameTiming(); static void endFrameTiming(); static void configureCamera(Camera& camera, int screenWidth, int screenHeight); @@ -99,6 +102,30 @@ private: static bool _programInitialized; static Camera* _camera; static int _activeEyeIndex; + + static void calibrate(const glm::vec3 position, const glm::quat orientation); + enum CalibrationState { + UNCALIBRATED, + WAITING_FOR_DELTA, + WAITING_FOR_ZERO, + WAITING_FOR_ZERO_HELD, + CALIBRATED + }; + static void positionCalibrationBillboard(BillboardOverlay* billboard); + static float CALIBRATION_DELTA_MINIMUM_LENGTH; + static float CALIBRATION_DELTA_MINIMUM_ANGLE; + static float CALIBRATION_ZERO_MAXIMUM_LENGTH; + static float CALIBRATION_ZERO_MAXIMUM_ANGLE; + static quint64 CALIBRATION_ZERO_HOLD_TIME; + static float CALIBRATION_MESSAGE_DISTANCE; + static CalibrationState _calibrationState; + static glm::vec3 _calibrationPosition; + static glm::quat _calibrationOrientation; + static quint64 _calibrationStartTime; + static int _calibrationMessage; + static QString CALIBRATION_BILLBOARD_URL; + static float CALIBRATION_BILLBOARD_SCALE; + #endif static glm::vec3 _leftEyePosition; diff --git a/interface/src/ui/overlays/BillboardOverlay.cpp b/interface/src/ui/overlays/BillboardOverlay.cpp index c7511ca466..e00011bf6b 100644 --- a/interface/src/ui/overlays/BillboardOverlay.cpp +++ b/interface/src/ui/overlays/BillboardOverlay.cpp @@ -156,9 +156,14 @@ void BillboardOverlay::setProperties(const QScriptValue &properties) { } } +void BillboardOverlay::setURL(const QString& url) { + setBillboardURL(url); +} + void BillboardOverlay::setBillboardURL(const QString& url) { _url = url; QUrl actualURL = url; + _isLoaded = false; // clear the billboard if previously set diff --git a/interface/src/ui/overlays/BillboardOverlay.h b/interface/src/ui/overlays/BillboardOverlay.h index fd594abe67..c0c2b05364 100644 --- a/interface/src/ui/overlays/BillboardOverlay.h +++ b/interface/src/ui/overlays/BillboardOverlay.h @@ -24,6 +24,12 @@ public: BillboardOverlay(); virtual void render(); + + // setters + void setURL(const QString& url); + void setScale(float scale) { _scale = scale; } + void setIsFacingAvatar(bool isFacingAvatar) { _isFacingAvatar = isFacingAvatar; } + virtual void setProperties(const QScriptValue& properties); void setClipFromSource(const QRect& bounds) { _fromImage = bounds; } diff --git a/interface/src/ui/overlays/Overlays.cpp b/interface/src/ui/overlays/Overlays.cpp index fe9a9edca0..922c6a0408 100644 --- a/interface/src/ui/overlays/Overlays.cpp +++ b/interface/src/ui/overlays/Overlays.cpp @@ -124,90 +124,59 @@ void Overlays::render3D() { unsigned int Overlays::addOverlay(const QString& type, const QScriptValue& properties) { unsigned int thisID = 0; - bool created = false; - bool is3D = false; Overlay* thisOverlay = NULL; + bool created = true; if (type == "image") { thisOverlay = new ImageOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; } else if (type == "text") { thisOverlay = new TextOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; } else if (type == "cube") { thisOverlay = new Cube3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "sphere") { thisOverlay = new Sphere3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "circle3d") { thisOverlay = new Circle3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "rectangle3d") { thisOverlay = new Rectangle3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "line3d") { thisOverlay = new Line3DOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "localvoxels") { thisOverlay = new LocalVoxelsOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "localmodels") { thisOverlay = new LocalModelsOverlay(Application::getInstance()->getEntityClipboardRenderer()); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "model") { thisOverlay = new ModelOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; } else if (type == "billboard") { thisOverlay = new BillboardOverlay(); - thisOverlay->init(_parent); - thisOverlay->setProperties(properties); - created = true; - is3D = true; + } else { + created = false; } if (created) { - QWriteLocker lock(&_lock); - thisID = _nextOverlayID; - _nextOverlayID++; - if (is3D) { - _overlays3D[thisID] = thisOverlay; - } else { - _overlays2D[thisID] = thisOverlay; - } + thisOverlay->setProperties(properties); + thisID = addOverlay(thisOverlay); } return thisID; } +unsigned int Overlays::addOverlay(Overlay* overlay) { + overlay->init(_parent); + + QWriteLocker lock(&_lock); + unsigned int thisID = _nextOverlayID; + _nextOverlayID++; + bool is3D = typeid(*overlay) != typeid(ImageOverlay) && typeid(*overlay) != typeid(TextOverlay); + if (is3D) { + _overlays3D[thisID] = overlay; + } else { + _overlays2D[thisID] = overlay; + } + + return thisID; +} + bool Overlays::editOverlay(unsigned int id, const QScriptValue& properties) { Overlay* thisOverlay = NULL; QWriteLocker lock(&_lock); diff --git a/interface/src/ui/overlays/Overlays.h b/interface/src/ui/overlays/Overlays.h index 6676994eed..4851b1f9dc 100644 --- a/interface/src/ui/overlays/Overlays.h +++ b/interface/src/ui/overlays/Overlays.h @@ -44,6 +44,9 @@ public slots: /// adds an overlay with the specific properties unsigned int addOverlay(const QString& type, const QScriptValue& properties); + /// adds an overlay that's already been created + unsigned int addOverlay(Overlay* overlay); + /// edits an overlay updating only the included properties, will return the identified OverlayID in case of /// successful edit, if the input id is for an unknown overlay this function will have no effect bool editOverlay(unsigned int id, const QScriptValue& properties);