diff --git a/interface/resources/images/loader-calibrate-blue.png b/interface/resources/images/loader-calibrate-blue.png new file mode 100644 index 0000000000..808f5fa3d2 Binary files /dev/null and b/interface/resources/images/loader-calibrate-blue.png differ diff --git a/interface/resources/images/loader-calibrate-white.png b/interface/resources/images/loader-calibrate-white.png new file mode 100644 index 0000000000..3b1b7b0e21 Binary files /dev/null and b/interface/resources/images/loader-calibrate-white.png differ diff --git a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml index ffd2a80e58..b8935f5463 100644 --- a/interface/resources/qml/hifi/tablet/CalibratingScreen.qml +++ b/interface/resources/qml/hifi/tablet/CalibratingScreen.qml @@ -25,23 +25,40 @@ Rectangle { signal canceled() signal restart() - property int count: 3 + property int count: 3 + property string calibratingText: "CALIBRATING..." + property string calibratingCountText: "CALIBRATION STARTING IN" + property string calibrationSuccess: "CALIBRATION COMPLETED" + property string calibrationFailed: "CALIBRATION FAILED" HifiConstants { id: hifi } visible: true color: hifi.colors.baseGray - BusyIndicator { + property string whiteIndicator: "../../../images/loader-calibrate-white.png" + property string blueIndicator: "../../../images/loader-calibrate-blue.png" + + Image { id: busyIndicator width: 350 height: 350 + property bool running: true + anchors { horizontalCenter: parent.horizontalCenter top: parent.top topMargin: 60 } - running: true + visible: busyIndicator.running + source: blueIndicator + NumberAnimation on rotation { + id: busyRotation + running: busyIndicator.running + loops: Animation.Infinite + duration: 1000 + from: 0 ; to: 360 + } } @@ -60,7 +77,7 @@ Rectangle { RalewayBold { id: statusText - text: "CALIBRATION STARTING IN" + text: info.calibratingCountText size: 16 color: hifi.colors.blueHighlight @@ -102,10 +119,27 @@ Rectangle { NumberAnimation { id: numberAnimation target: info - property: count + property: "count" to: 0 } + Timer { + id: timer + + repeat: false + interval: 3000 + onTriggered: { + info.calibrating(); + } + } + + Timer { + id: closeWindow + repeat: false + interval: 3000 + onTriggered: stack.pop(); + } + Row { spacing: 20 @@ -124,6 +158,10 @@ Rectangle { onClicked: { restart(); + numberAnimation.stop(); + info.count = (timer.interval / 1000); + numberAnimation.start(); + timer.restart(); } } @@ -134,14 +172,42 @@ Rectangle { text: "CANCEL" onClicked: { - stack.pop(); canceled(); + stack.pop() } } } - function start() { + function start(interval, countNumber) { + countDown.visible = true; + statusText.color = hifi.colors.blueHighlight; + numberAnimation.duration = interval + info.count = countNumber; + timer.interval = interval; + numberAnimation.start(); + timer.start(); } - function callingFunction() { + + function calibrating() { + countDown.visible = false; + busyIndicator.source = whiteIndicator; + busyRotation.from = 360 + busyRotation.to = 0 + statusText.text = info.calibratingText; + statusText.color = hifi.colors.white + } + + function success() { + busyIndicator.running = false; + statusText.text = info.calibrationSuccess + statusText.color = hifi.colors.greenHighlight + closeWindow.start(); + } + + function failure() { + busyIndicator.running = false; + statusText.text = info.calibrationFailed + statusText.color = hifi.colors.redHighlight + closeWindow.start(); } } diff --git a/interface/resources/qml/hifi/tablet/ControllerSettings.qml b/interface/resources/qml/hifi/tablet/ControllerSettings.qml index cc638858e9..e1ba93a840 100644 --- a/interface/resources/qml/hifi/tablet/ControllerSettings.qml +++ b/interface/resources/qml/hifi/tablet/ControllerSettings.qml @@ -103,10 +103,9 @@ StackView { HifiControls.CheckBox { id: checkBox colorScheme: hifi.colorSchemes.dark - text: "show all input plugins" + text: "show all input devices" onClicked: { - console.log("clicked"); inputPlugins(); changeSource(); } @@ -162,12 +161,16 @@ StackView { source: InputConfiguration.configurationLayout(box.currentText); onLoaded: { if (loader.item.hasOwnProperty("pluginName")) { - loader.item.pluginName = box.currentText - - if (loader.item.hasOwnProperty("displayInformation")) { - loader.item.displayInformation(); + if (box.currentText === "Vive") { + loader.item.pluginName = "OpenVR"; + } else { + loader.item.pluginName = box.currentText; } } + + if (loader.item.hasOwnProperty("displayInformation")) { + loader.item.displayConfiguration(); + } } } } @@ -187,7 +190,13 @@ StackView { function changeSource() { loader.source = ""; - var source = InputConfiguration.configurationLayout(box.currentText); + var source = ""; + if (box.currentText == "Vive") { + source = InputConfiguration.configurationLayout("OpenVR"); + } else { + source = InputConfiguration.configurationLayout(box.currentText); + } + loader.source = source; if (source === "") { box.label = "(not configurable)"; diff --git a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml index 7fde6c53eb..9c23cf8677 100644 --- a/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml +++ b/interface/resources/qml/hifi/tablet/OpenVrConfiguration.qml @@ -27,7 +27,7 @@ Rectangle { property int leftMargin: 75 property int countDown: 0 property string pluginName: "" - property var displayInformation: openVrConfiguration.displayConfiguration() + property var displayInformation: null readonly property bool feetChecked: feetBox.checked readonly property bool hipsChecked: hipBox.checked @@ -144,7 +144,7 @@ Rectangle { decimals: 4 width: 112 label: "Y: offset" - value: -0.0254 + minimumValue: -10 stepSize: 0.0254 colorScheme: hifi.colorSchemes.dark @@ -157,7 +157,7 @@ Rectangle { id: headZOffset width: 112 label: "Z: offset" - value: -0.152 + minimumValue: -10 stepSize: 0.0254 decimals: 4 colorScheme: hifi.colorSchemes.dark @@ -249,7 +249,7 @@ Rectangle { decimals: 4 width: 112 label: "Y: offset" - value: -0.0508 + minimumValue: -10 stepSize: 0.0254 colorScheme: hifi.colorSchemes.dark @@ -262,7 +262,7 @@ Rectangle { id: handZOffset width: 112 label: "Z: offset" - value: -0.0254 + minimumValue: -10 stepSize: 0.0254 decimals: 4 colorScheme: hifi.colorSchemes.dark @@ -528,17 +528,19 @@ Rectangle { hoverEnabled: true onClicked: { if (calibrationButton.enabled) { - calibrationTimer.interval = timeToCalibrate.value * 1000 - openVrConfiguration.countDown = timeToCalibrate.value; - numberAnimation.duration = calibrationTimer.interval - numberAnimation.start(); - calibrationTimer.start(); - var calibratingScreen = screen.createObject(); - stack.push(calibratingScreen); - - calibratingScreen.callingFunction(); - calibratingScreen.canceled.connect(cancelCalibration); - calibratingScreen.restart.connect(restartCalibration); + if (openVrConfiguration.state === buttonState.apply) { + InputConfiguration.uncalibratePlugin(pluginName); + updateCalibrationButton(); + } else { + calibrationTimer.interval = timeToCalibrate.value * 1000 + openVrConfiguration.countDown = timeToCalibrate.value; + var calibratingScreen = screen.createObject(); + stack.push(calibratingScreen); + calibratingScreen.canceled.connect(cancelCalibration); + calibratingScreen.restart.connect(restartCalibration); + calibratingScreen.start(calibrationTimer.interval, timeToCalibrate.value); + calibrationTimer.start(); + } } } @@ -547,7 +549,7 @@ Rectangle { } onReleased: { - calibrationButton.pressed = false; + calibrationButton.pressed = false; } onEntered: { @@ -592,7 +594,6 @@ Rectangle { minimumValue: 3 value: 3 - label: "Time til calibration ( in seconds )" colorScheme: hifi.colorSchemes.dark onEditingFinished: { @@ -602,6 +603,31 @@ Rectangle { } } + RalewayBold { + id: delayTextInfo + size: 10 + text: "Delay Before Calibration Starts" + color: hifi.colors.white + + anchors { + left: timeToCalibrate.right + leftMargin: 20 + verticalCenter: timeToCalibrate.verticalCenter + } + } + + RalewayRegular { + size: 12 + text: "sec" + color: hifi.colors.lightGray + + anchors { + left: delayTextInfo.right + leftMargin: 10 + verticalCenter: delayTextInfo.verticalCenter + } + } + NumberAnimation { id: numberAnimation target: openVrConfiguration @@ -610,16 +636,17 @@ Rectangle { } function calibrationStatusInfo(status) { + var calibrationScreen = stack.currentItem; if (status["calibrated"]) { + calibrationScreen.success(); } else if (!status["calibrated"]) { var uncalibrated = status["success"]; - if (uncalibrated) { - - } else { - + if (!uncalibrated) { + calibrationScreen.failure(); } } - displayTimer.start(); + + updateCalibrationButton(); } @@ -650,11 +677,11 @@ Rectangle { } function cancelCalibration() { - console.log("canceling calibration"); + calibrationTimer.stop(); } function restartCalibration() { - console.log("restating calibration"); + calibrationTimer.restart(); } function displayConfiguration() { @@ -681,6 +708,9 @@ Rectangle { handPuckBox.checked = true; handBox.checked = false; } + + initializeButtonState(); + updateCalibrationText(); } function displayTrackerConfiguration(type) { @@ -732,25 +762,45 @@ Rectangle { if (settingsChanged) { if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { state = buttonState.apply; - console.log("apply"); } else { state = buttonState.applyAndCalibrate; - console.log("apply and calibrate"); } } else { if (state == buttonState.apply) { state = buttonState.disabled; - console.log("disable"); + } else if (state == buttonState.applyAndCalibrate) { + state = buttonState.calibrate; } } lastConfiguration = settings; } - function updateCalibrationText() { + function initializeButtonState() { + var settings = composeConfigurationSettings(); + var bodySetting = settings["bodyConfiguration"]; + var headSetting = settings["headConfiguration"]; + var headOverride = headSetting["override"]; + var handSetting = settings["handConfiguration"]; + var handOverride = handSetting["override"]; + + + if ((!handOverride) && (!headOverride) && (bodySetting === "None")) { + state = buttonState.disabled; + } else { + state = buttonState.calibrate; + } + } + + function updateCalibrationButton() { updateButtonState(); + updateCalibrationText(); + } + + function updateCalibrationText() { if (buttonState.disabled == state) { calibrationButton.enabled = false; + calibrationButton.text = "Apply"; } else if (buttonState.apply == state) { calibrationButton.enabled = true; calibrationButton.text = "Apply"; @@ -818,6 +868,6 @@ Rectangle { function sendConfigurationSettings() { var settings = composeConfigurationSettings(); InputConfiguration.setConfigurationSettings(settings, pluginName); - updateCalibrationText(); + updateCalibrationButton(); } } diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 8505e07181..95e3cec3b5 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -109,6 +109,7 @@ #include #include #include +#include #include #include #include @@ -1997,6 +1998,7 @@ void Application::initializeUi() { surfaceContext->setContextProperty("TextureCache", DependencyManager::get().data()); surfaceContext->setContextProperty("ModelCache", DependencyManager::get().data()); surfaceContext->setContextProperty("SoundCache", DependencyManager::get().data()); + surfaceContext->setContextProperty("InputConfiguration", DependencyManager::get().data()); surfaceContext->setContextProperty("Account", AccountScriptingInterface::getInstance()); surfaceContext->setContextProperty("Tablet", DependencyManager::get().data()); diff --git a/interface/src/Menu.cpp b/interface/src/Menu.cpp index 5b5480dbfc..8c9baa7c43 100644 --- a/interface/src/Menu.cpp +++ b/interface/src/Menu.cpp @@ -38,6 +38,7 @@ #include "MainWindow.h" #include "render/DrawStatus.h" #include "scripting/MenuScriptingInterface.h" +#include "scripting/HMDScriptingInterface.h" #include "ui/DialogsManager.h" #include "ui/StandAloneJSConsole.h" #include "InterfaceLogging.h" @@ -310,10 +311,15 @@ Menu::Menu() { QString("../../hifi/tablet/TabletLodPreferences.qml"), "LodPreferencesDialog"); }); - action = addActionToQMenuAndActionHash(settingsMenu, "InputConfiguration"); + action = addActionToQMenuAndActionHash(settingsMenu, "Controller Settings"); connect(action, &QAction::triggered, [] { auto tablet = DependencyManager::get()->getTablet("com.highfidelity.interface.tablet.system"); + auto hmd = DependencyManager::get(); tablet->loadQMLSource("ControllerSettings.qml"); + + if (!hmd->getShouldShowTablet()) { + hmd->toggleShouldShowTablet(); + } }); // Settings > Control with Speech [advanced] diff --git a/libraries/plugins/src/plugins/InputConfiguration.cpp b/libraries/plugins/src/plugins/InputConfiguration.cpp index d20678b972..33948ef70d 100644 --- a/libraries/plugins/src/plugins/InputConfiguration.cpp +++ b/libraries/plugins/src/plugins/InputConfiguration.cpp @@ -28,7 +28,12 @@ QStringList InputConfiguration::inputPlugins() { QStringList inputPlugins; for (auto plugin : PluginManager::getInstance()->getInputPlugins()) { - inputPlugins << QString(plugin->getName()); + QString pluginName = plugin->getName(); + if (pluginName == QString("OpenVR")) { + inputPlugins << QString("Vive"); + } else { + inputPlugins << pluginName; + } } return inputPlugins; } @@ -45,7 +50,12 @@ QStringList InputConfiguration::activeInputPlugins() { QStringList activePlugins; for (auto plugin : PluginManager::getInstance()->getInputPlugins()) { if (plugin->configurable()) { - activePlugins << QString(plugin->getName()); + QString pluginName = plugin->getName(); + if (pluginName == QString("OpenVR")) { + activePlugins << QString("Vive"); + } else { + activePlugins << pluginName; + } } } return activePlugins; @@ -113,3 +123,19 @@ void InputConfiguration::calibratePlugin(QString pluginName) { } } } + + +bool InputConfiguration::uncalibratePlugin(QString pluginName) { + if (QThread::currentThread() != thread()) { + bool result; + QMetaObject::invokeMethod(this, "uncalibratePlugin", Qt::BlockingQueuedConnection, + Q_ARG(bool, result)); + return result; + } + + for (auto plugin : PluginManager::getInstance()->getInputPlugins()) { + if (plugin->getName() == pluginName) { + return plugin->uncalibrate(); + } + } +} diff --git a/libraries/plugins/src/plugins/InputConfiguration.h b/libraries/plugins/src/plugins/InputConfiguration.h index abf0c89ab4..27591edc34 100644 --- a/libraries/plugins/src/plugins/InputConfiguration.h +++ b/libraries/plugins/src/plugins/InputConfiguration.h @@ -28,6 +28,7 @@ public: Q_INVOKABLE void setConfigurationSettings(QJsonObject configurationSettings, QString pluginName); Q_INVOKABLE void calibratePlugin(QString pluginName); Q_INVOKABLE QJsonObject configurationSettings(QString pluginName); + Q_INVOKABLE bool uncalibratePlugin(QString pluginName); signals: void calibrationStatus(const QJsonObject& status); diff --git a/libraries/plugins/src/plugins/InputPlugin.h b/libraries/plugins/src/plugins/InputPlugin.h index 51bff07de9..5d02964c97 100644 --- a/libraries/plugins/src/plugins/InputPlugin.h +++ b/libraries/plugins/src/plugins/InputPlugin.h @@ -29,6 +29,7 @@ public: virtual QJsonObject configurationSettings() { return QJsonObject(); } virtual QString configurationLayout() { return QString(); } virtual void calibrate() {} + virtual bool uncalibrate() { return false; } virtual bool configurable() { return false; } virtual bool isHandController() const { return false; } virtual bool isHeadController() const { return false; } diff --git a/plugins/openvr/src/ViveControllerManager.cpp b/plugins/openvr/src/ViveControllerManager.cpp index f683ee75de..3b62a1a927 100644 --- a/plugins/openvr/src/ViveControllerManager.cpp +++ b/plugins/openvr/src/ViveControllerManager.cpp @@ -127,6 +127,14 @@ void ViveControllerManager::calibrate() { } } +bool ViveControllerManager::uncalibrate() { + if (isSupported()) { + _inputDevice->uncalibrate(); + return true; + } + return false; +} + bool ViveControllerManager::isSupported() const { return openVrSupported(); } @@ -241,10 +249,6 @@ ViveControllerManager::InputDevice::InputDevice(vr::IVRSystem*& system) : contro _configStringMap[Config::FeetAndHips] = QString("FeetAndHips"); _configStringMap[Config::FeetHipsAndChest] = QString("FeetHipsAndChest"); _configStringMap[Config::FeetHipsAndShoulders] = QString("FeetHipsAndShoulders"); - - if (openVrSupported()) { - loadSettings(); - } } void ViveControllerManager::InputDevice::update(float deltaTime, const controller::InputCalibrationData& inputCalibrationData) { @@ -319,7 +323,8 @@ void ViveControllerManager::InputDevice::calibrateFromHandController(const contr void ViveControllerManager::InputDevice::calibrateFromUI(const controller::InputCalibrationData& inputCalibrationData) { if (_calibrate) { - calibrateOrUncalibrate(inputCalibrationData); + uncalibrate(); + calibrate(inputCalibrationData); _calibrate = false; } } @@ -337,6 +342,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHead = headObject["override"].toBool(); if (overrideHead) { _headConfig = HeadConfig::Puck; + HEAD_PUCK_Y_OFFSET = (float)headObject["Y"].toDouble(); + HEAD_PUCK_Z_OFFSET = (float)headObject["Z"].toDouble(); } else { _headConfig = HeadConfig::HMD; } @@ -345,6 +352,8 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso bool overrideHands = handsObject["override"].toBool(); if (overrideHands) { _handConfig = HandConfig::Pucks; + HAND_PUCK_Y_OFFSET = (float)handsObject["Y"].toDouble(); + HAND_PUCK_Z_OFFSET = (float)handsObject["Z"].toDouble(); } else { _handConfig = HandConfig::HandController; } @@ -352,10 +361,6 @@ void ViveControllerManager::InputDevice::configureCalibrationSettings(const QJso iter++; } } - - qDebug() << configToString(_preferedConfig); - - saveSettings(); } void ViveControllerManager::InputDevice::calibrateNextFrame() { @@ -444,15 +449,22 @@ void ViveControllerManager::InputDevice::calibrateOrUncalibrate(const controller void ViveControllerManager::InputDevice::calibrate(const controller::InputCalibrationData& inputCalibration) { qDebug() << "Puck Calibration: Starting..."; + int puckCount = (int)_validTrackedObjects.size(); + qDebug() << "Puck Calibration: " << puckCount << " pucks found for calibration"; + + if (puckCount == 0) { + uncalibrate(); + emitCalibrationStatus(false); + return; + } + glm::mat4 defaultToReferenceMat = glm::mat4(); if (_headConfig == HeadConfig::HMD) { defaultToReferenceMat = calculateDefaultToReferenceForHmd(inputCalibration); } else if (_headConfig == HeadConfig::Puck) { defaultToReferenceMat = calculateDefaultToReferenceForHeadPuck(inputCalibration); } - - int puckCount = (int)_validTrackedObjects.size(); - qDebug() << "Puck Calibration: " << puckCount << " pucks found for calibration"; + _config = _preferedConfig; bool headConfigured = configureHead(defaultToReferenceMat, inputCalibration); @@ -1006,24 +1018,6 @@ void ViveControllerManager::InputDevice::calibrateHead(glm::mat4& defaultToRefer _pucksOffset[head.first] = computeOffset(defaultToReferenceMat, inputCalibration.defaultHeadMat, newHead); } - -void ViveControllerManager::InputDevice::loadSettings() { - Settings settings; - settings.beginGroup("PUCK_CONFIG"); - { - } - settings.endGroup(); -} - -void ViveControllerManager::InputDevice::saveSettings() const { - Settings settings; - settings.beginGroup("PUCK_CONFIG"); - { - - } - settings.endGroup(); -} - QString ViveControllerManager::InputDevice::configToString(Config config) { return _configStringMap[config]; } diff --git a/plugins/openvr/src/ViveControllerManager.h b/plugins/openvr/src/ViveControllerManager.h index 1a4df550d9..4ebe466a04 100644 --- a/plugins/openvr/src/ViveControllerManager.h +++ b/plugins/openvr/src/ViveControllerManager.h @@ -45,6 +45,7 @@ public: void setConfigurationSettings(const QJsonObject configurationSettings) override; QJsonObject configurationSettings() override; void calibrate() override; + bool uncalibrate() override; bool isHeadController() const override { return true; } bool isHeadControllerMounted() const; @@ -91,8 +92,6 @@ private: void partitionTouchpad(int sButton, int xAxis, int yAxis, int centerPsuedoButton, int xPseudoButton, int yPseudoButton); void printDeviceTrackingResultChange(uint32_t deviceIndex); void setConfigFromString(const QString& value); - void loadSettings(); - void saveSettings() const; bool configureHead(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); bool configureHands(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration); bool configureBody(glm::mat4& defaultToReferenceMat, const controller::InputCalibrationData& inputCalibration);