From 41c4ecd465559570743956c991326fb43cdbca23 Mon Sep 17 00:00:00 2001 From: David Rowe Date: Wed, 1 Aug 2018 15:20:51 +1200 Subject: [PATCH] Add HMD play area to API; Oculus and Vive --- .../src/scripting/HMDScriptingInterface.cpp | 9 +++ .../src/scripting/HMDScriptingInterface.h | 11 ++- libraries/plugins/src/plugins/DisplayPlugin.h | 3 + libraries/shared/src/RegisteredMetaTypes.cpp | 79 +++++++++++++++---- libraries/shared/src/RegisteredMetaTypes.h | 6 +- .../oculus/src/OculusBaseDisplayPlugin.cpp | 50 ++++++++++++ plugins/oculus/src/OculusBaseDisplayPlugin.h | 3 + plugins/openvr/src/OpenVrDisplayPlugin.cpp | 33 ++++++++ plugins/openvr/src/OpenVrDisplayPlugin.h | 2 + 9 files changed, 177 insertions(+), 19 deletions(-) diff --git a/interface/src/scripting/HMDScriptingInterface.cpp b/interface/src/scripting/HMDScriptingInterface.cpp index ad8e265a01..ea24d6c793 100644 --- a/interface/src/scripting/HMDScriptingInterface.cpp +++ b/interface/src/scripting/HMDScriptingInterface.cpp @@ -201,3 +201,12 @@ bool HMDScriptingInterface::isKeyboardVisible() { void HMDScriptingInterface::centerUI() { QMetaObject::invokeMethod(qApp, "centerUI", Qt::QueuedConnection); } + +QVariant HMDScriptingInterface::getPlayAreaRect() { + auto rect = qApp->getActiveDisplayPlugin()->getPlayAreaRect(); + return qRectFToVariant(rect); +} + +QVector HMDScriptingInterface::getSensorPositions() { + return qApp->getActiveDisplayPlugin()->getSensorPositions(); +} diff --git a/interface/src/scripting/HMDScriptingInterface.h b/interface/src/scripting/HMDScriptingInterface.h index 3d7cc5afb2..554bff8fa4 100644 --- a/interface/src/scripting/HMDScriptingInterface.h +++ b/interface/src/scripting/HMDScriptingInterface.h @@ -56,8 +56,10 @@ class QScriptEngine; * @property {Uuid} tabletID - The UUID of the tablet body model overlay. * @property {Uuid} tabletScreenID - The UUID of the tablet's screen overlay. * @property {Uuid} homeButtonID - The UUID of the tablet's "home" button overlay. - * @property {Uuid} homeButtonHighlightMaterialID - The UUID of the material entity used to highlight tablet button - * @property {Uuid} homeButtonUnhighlightMaterialID - The UUID of the material entity use to unhighlight the entity + * @property {Uuid} homeButtonHighlightMaterialID - The UUID of the material entity used to highlight tablet button. + * @property {Uuid} homeButtonUnhighlightMaterialID - The UUID of the material entity use to unhighlight the tablet button. + * @property {Rect} playArea=0,0,0,0 - The size and position of the HMD play area in sensor coordinates. Read-only. + * @property {Vec3[]} sensorPositions=[]] - The positions of the VR system sensors in sensor coordinates. Read-only. */ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Dependency { Q_OBJECT @@ -71,6 +73,8 @@ class HMDScriptingInterface : public AbstractHMDScriptingInterface, public Depen Q_PROPERTY(QUuid tabletScreenID READ getCurrentTabletScreenID WRITE setCurrentTabletScreenID) Q_PROPERTY(QUuid homeButtonHighlightMaterialID READ getCurrentHomeButtonHighlightMaterialID WRITE setCurrentHomeButtonHighlightMaterialID) Q_PROPERTY(QUuid homeButtonUnhighlightMaterialID READ getCurrentHomeButtonUnhighlightMaterialID WRITE setCurrentHomeButtonUnhighlightMaterialID) + Q_PROPERTY(QVariant playArea READ getPlayAreaRect); + Q_PROPERTY(QVector sensorPositions READ getSensorPositions); public: @@ -385,6 +389,9 @@ public: void setCurrentHomeButtonUnhighlightMaterialID(QUuid homeButtonUnhighlightMaterialID) { _homeButtonUnhighlightMaterialID = homeButtonUnhighlightMaterialID; } QUuid getCurrentHomeButtonUnhighlightMaterialID() { return _homeButtonUnhighlightMaterialID; } + QVariant getPlayAreaRect(); + QVector getSensorPositions(); + private: bool _showTablet { false }; bool _tabletContextualMode { false }; diff --git a/libraries/plugins/src/plugins/DisplayPlugin.h b/libraries/plugins/src/plugins/DisplayPlugin.h index e330f6d7ec..ad49ceafe6 100644 --- a/libraries/plugins/src/plugins/DisplayPlugin.h +++ b/libraries/plugins/src/plugins/DisplayPlugin.h @@ -112,6 +112,9 @@ public: virtual bool suppressKeyboard() { return false; } virtual void unsuppressKeyboard() {}; virtual bool isKeyboardVisible() { return false; } + + virtual QRectF getPlayAreaRect() { return QRectF(); } + virtual QVector getSensorPositions() { return QVector(); } }; class DisplayPlugin : public Plugin, public HmdDisplay { diff --git a/libraries/shared/src/RegisteredMetaTypes.cpp b/libraries/shared/src/RegisteredMetaTypes.cpp index a9dbe83b06..0c8f4f0466 100644 --- a/libraries/shared/src/RegisteredMetaTypes.cpp +++ b/libraries/shared/src/RegisteredMetaTypes.cpp @@ -552,6 +552,14 @@ glm::vec2 vec2FromVariant(const QVariant &object) { return vec2FromVariant(object, valid); } +/**jsdoc + * Defines a rectangular portion of an image or screen, or similar. + * @typedef {object} Rect + * @property {number} x - Left, x-coordinate value. + * @property {number} y - Top, y-coordinate value. + * @property {number} width - Width of the rectangle. + * @property {number} height - Height of the rectangle. + */ QScriptValue qRectToScriptValue(QScriptEngine* engine, const QRect& rect) { QScriptValue obj = engine->newObject(); obj.setProperty("x", rect.x()); @@ -568,22 +576,6 @@ void qRectFromScriptValue(const QScriptValue &object, QRect& rect) { rect.setHeight(object.property("height").toVariant().toInt()); } -QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { - QScriptValue obj = engine->newObject(); - obj.setProperty("red", color.red); - obj.setProperty("green", color.green); - obj.setProperty("blue", color.blue); - return obj; -} - -/**jsdoc - * Defines a rectangular portion of an image or screen. - * @typedef {object} Rect - * @property {number} x - Integer left, x-coordinate value. - * @property {number} y - Integer top, y-coordinate value. - * @property {number} width - Integer width of the rectangle. - * @property {number} height - Integer height of the rectangle. - */ QVariant qRectToVariant(const QRect& rect) { QVariantMap obj; obj["x"] = rect.x(); @@ -615,6 +607,61 @@ QRect qRectFromVariant(const QVariant& object) { return qRectFromVariant(object, valid); } +QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect) { + QScriptValue obj = engine->newObject(); + obj.setProperty("x", rect.x()); + obj.setProperty("y", rect.y()); + obj.setProperty("width", rect.width()); + obj.setProperty("height", rect.height()); + return obj; +} + +void qRectFFromScriptValue(const QScriptValue &object, QRectF& rect) { + rect.setX(object.property("x").toVariant().toFloat()); + rect.setY(object.property("y").toVariant().toFloat()); + rect.setWidth(object.property("width").toVariant().toFloat()); + rect.setHeight(object.property("height").toVariant().toFloat()); +} + +QVariant qRectFToVariant(const QRectF& rect) { + QVariantMap obj; + obj["x"] = rect.x(); + obj["y"] = rect.y(); + obj["width"] = rect.width(); + obj["height"] = rect.height(); + return obj; +} + +QRectF qRectFFromVariant(const QVariant& objectVar, bool& valid) { + QVariantMap object = objectVar.toMap(); + QRectF rect; + valid = false; + rect.setX(object["x"].toFloat(&valid)); + if (valid) { + rect.setY(object["y"].toFloat(&valid)); + } + if (valid) { + rect.setWidth(object["width"].toFloat(&valid)); + } + if (valid) { + rect.setHeight(object["height"].toFloat(&valid)); + } + return rect; +} + +QRectF qRectFFromVariant(const QVariant& object) { + bool valid; + return qRectFFromVariant(object, valid); +} + + +QScriptValue xColorToScriptValue(QScriptEngine *engine, const xColor& color) { + QScriptValue obj = engine->newObject(); + obj.setProperty("red", color.red); + obj.setProperty("green", color.green); + obj.setProperty("blue", color.blue); + return obj; +} void xColorFromScriptValue(const QScriptValue &object, xColor& color) { if (!object.isValid()) { diff --git a/libraries/shared/src/RegisteredMetaTypes.h b/libraries/shared/src/RegisteredMetaTypes.h index db63237c73..9952004547 100644 --- a/libraries/shared/src/RegisteredMetaTypes.h +++ b/libraries/shared/src/RegisteredMetaTypes.h @@ -97,7 +97,11 @@ void qRectFromScriptValue(const QScriptValue& object, QRect& rect); QRect qRectFromVariant(const QVariant& object, bool& isValid); QRect qRectFromVariant(const QVariant& object); QVariant qRectToVariant(const QRect& rect); - +QScriptValue qRectFToScriptValue(QScriptEngine* engine, const QRectF& rect); +void qRectFFromScriptValue(const QScriptValue& object, QRectF& rect); +QRectF qRectFFromVariant(const QVariant& object, bool& isValid); +QRectF qRectFFromVariant(const QVariant& object); +QVariant qRectFToVariant(const QRectF& rect); // xColor QScriptValue xColorToScriptValue(QScriptEngine* engine, const xColor& color); diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp index 5aa1e45943..321ee217b9 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.cpp +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.cpp @@ -167,3 +167,53 @@ void OculusBaseDisplayPlugin::updatePresentPose() { _currentPresentFrameInfo.presentPose = ovr::toGlm(trackingState.HeadPose.ThePose); _currentPresentFrameInfo.renderPose = _currentPresentFrameInfo.presentPose; } + +QRectF OculusBaseDisplayPlugin::getPlayAreaRect() { + if (!_session) { + return QRectF(); + } + + int floorPointsCount = 0; + auto result = ovr_GetBoundaryGeometry(_session, ovrBoundary_PlayArea, nullptr, &floorPointsCount); + if (!OVR_SUCCESS(result) || floorPointsCount != 4) { + return QRectF(); + } + + auto floorPoints = new ovrVector3f[floorPointsCount]; + result = ovr_GetBoundaryGeometry(_session, ovrBoundary_PlayArea, floorPoints, nullptr); + if (!OVR_SUCCESS(result)) { + return QRectF(); + } + + auto minXZ = ovr::toGlm(floorPoints[0]); + auto maxXZ = minXZ; + for (int i = 1; i < floorPointsCount; i++) { + auto point = ovr::toGlm(floorPoints[i]); + minXZ.x = std::min(minXZ.x, point.x); + minXZ.z = std::min(minXZ.z, point.z); + maxXZ.x = std::max(maxXZ.x, point.x); + maxXZ.z = std::max(maxXZ.z, point.z); + } + + glm::vec2 center = glm::vec2((minXZ.x + maxXZ.x) / 2, (minXZ.z + maxXZ.z) / 2); + glm::vec2 dimensions = glm::vec2(maxXZ.x - minXZ.x, maxXZ.z - minXZ.z); + + return QRectF(center.x, center.y, dimensions.x, dimensions.y); +} + +QVector OculusBaseDisplayPlugin::getSensorPositions() { + if (!_session) { + return QVector(); + } + + QVector result; + auto numTrackers = ovr_GetTrackerCount(_session); + for (uint i = 0; i < numTrackers; i++) { + auto trackerPose = ovr_GetTrackerPose(_session, i); + if (trackerPose.TrackerFlags & ovrTracker_PoseTracked) { + result.append(ovr::toGlm(trackerPose.Pose.Position)); + } + } + + return result; +} diff --git a/plugins/oculus/src/OculusBaseDisplayPlugin.h b/plugins/oculus/src/OculusBaseDisplayPlugin.h index d70d14dc28..3c989e8a62 100644 --- a/plugins/oculus/src/OculusBaseDisplayPlugin.h +++ b/plugins/oculus/src/OculusBaseDisplayPlugin.h @@ -27,6 +27,9 @@ public: void resetSensors() override final; bool beginFrameRender(uint32_t frameIndex) override; float getTargetFrameRate() const override { return _hmdDesc.DisplayRefreshRate; } + + QRectF getPlayAreaRect() override; + QVector getSensorPositions() override; protected: void customizeContext() override; diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.cpp b/plugins/openvr/src/OpenVrDisplayPlugin.cpp index 5e4079cbcf..cef7cbc490 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.cpp +++ b/plugins/openvr/src/OpenVrDisplayPlugin.cpp @@ -765,3 +765,36 @@ QString OpenVrDisplayPlugin::getPreferredAudioOutDevice() const { return device; } +QRectF OpenVrDisplayPlugin::getPlayAreaRect() { + auto chaperone = vr::VRChaperone(); + if (!chaperone) { + qWarning() << "No chaperone"; + return QRectF(); + } + + if (chaperone->GetCalibrationState() >= vr::ChaperoneCalibrationState_Error) { + qWarning() << "Chaperone status =" << chaperone->GetCalibrationState(); + return QRectF(); + } + + vr::HmdQuad_t rect; + if (!chaperone->GetPlayAreaRect(&rect)) { + qWarning() << "Chaperone rect not obtained"; + return QRectF(); + } + + auto minXZ = transformPoint(_sensorResetMat, toGlm(rect.vCorners[0])); + auto maxXZ = minXZ; + for (int i = 1; i < 4; i++) { + auto point = transformPoint(_sensorResetMat, toGlm(rect.vCorners[i])); + minXZ.x = std::min(minXZ.x, point.x); + minXZ.z = std::min(minXZ.z, point.z); + maxXZ.x = std::max(maxXZ.x, point.x); + maxXZ.z = std::max(maxXZ.z, point.z); + } + + glm::vec2 center = glm::vec2((minXZ.x + maxXZ.x) / 2, (minXZ.z + maxXZ.z) / 2); + glm::vec2 dimensions = glm::vec2(maxXZ.x - minXZ.x, maxXZ.z - minXZ.z); + + return QRectF(center.x, center.y, dimensions.x, dimensions.y); +} diff --git a/plugins/openvr/src/OpenVrDisplayPlugin.h b/plugins/openvr/src/OpenVrDisplayPlugin.h index 15a434341d..773b315449 100644 --- a/plugins/openvr/src/OpenVrDisplayPlugin.h +++ b/plugins/openvr/src/OpenVrDisplayPlugin.h @@ -64,6 +64,8 @@ public: QString getPreferredAudioInDevice() const override; QString getPreferredAudioOutDevice() const override; + QRectF getPlayAreaRect() override; + protected: bool internalActivate() override; void internalDeactivate() override;