diff --git a/interface/resources/controllers/standard_navigation.json b/interface/resources/controllers/standard_navigation.json index 62c0883142..c9dc90cbe6 100644 --- a/interface/resources/controllers/standard_navigation.json +++ b/interface/resources/controllers/standard_navigation.json @@ -2,9 +2,6 @@ "name": "Standard to Action", "when": "Application.NavigationFocused", "channels": [ - { "disabled_from": { "makeAxis" : [ "Standard.DD", "Standard.DU" ] }, "to": "Actions.UiNavVertical" }, - { "disabled_from": { "makeAxis" : [ "Standard.DL", "Standard.DR" ] }, "to": "Actions.UiNavLateral" }, - { "disabled_from": { "makeAxis" : [ "Standard.LB", "Standard.RB" ] }, "to": "Actions.UiNavGroup" }, { "from": "Standard.DU", "to": "Actions.UiNavVertical" }, { "from": "Standard.DD", "to": "Actions.UiNavVertical", "filters": "invert" }, { "from": "Standard.DL", "to": "Actions.UiNavLateral", "filters": "invert" }, diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 52985d9d9d..a6555352dd 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -402,6 +402,11 @@ void messageHandler(QtMsgType type, const QMessageLogContext& context, const QSt } } +static const QString STATE_IN_HMD = "InHMD"; +static const QString STATE_SNAP_TURN = "SnapTurn"; +static const QString STATE_GROUNDED = "Grounded"; +static const QString STATE_NAV_FOCUSED = "NavigationFocused"; + bool setupEssentials(int& argc, char** argv) { unsigned int listenPort = 0; // bind to an ephemeral port by default const char** constArgv = const_cast(argv); @@ -471,6 +476,7 @@ bool setupEssentials(int& argc, char** argv) { DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); + controller::StateController::setStateVariables({ { STATE_IN_HMD, STATE_SNAP_TURN, STATE_GROUNDED, STATE_NAV_FOCUSED } }); DependencyManager::set(); DependencyManager::set(); DependencyManager::set(); @@ -925,6 +931,11 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } else if (action == controller::toInt(controller::Action::CONTEXT_MENU)) { auto reticlePosition = getApplicationCompositor().getReticlePosition(); offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); + } else if (action == controller::toInt(controller::Action::UI_NAV_SELECT)) { + if (!offscreenUi->navigationFocused()) { + auto reticlePosition = getApplicationCompositor().getReticlePosition(); + offscreenUi->toggleMenu(_glWidget->mapFromGlobal(QPoint(reticlePosition.x, reticlePosition.y))); + } } else if (action == controller::toInt(controller::Action::RETICLE_X)) { auto oldPos = getApplicationCompositor().getReticlePosition(); getApplicationCompositor().setReticlePosition({ oldPos.x + state, oldPos.y }); @@ -937,28 +948,23 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : } }); - // A new controllerInput device used to reflect current values from the application state - _applicationStateDevice = std::make_shared(); + _applicationStateDevice = userInputMapper->getStateDevice(); - _applicationStateDevice->addInputVariant(QString("InHMD"), controller::StateController::ReadLambda([]() -> float { - return (float)qApp->isHMDMode(); - })); - _applicationStateDevice->addInputVariant(QString("SnapTurn"), controller::StateController::ReadLambda([]() -> float { - return (float)qApp->getMyAvatar()->getSnapTurn(); - })); - _applicationStateDevice->addInputVariant(QString("Grounded"), controller::StateController::ReadLambda([]() -> float { - return (float)qApp->getMyAvatar()->getCharacterController()->onGround(); - })); - _applicationStateDevice->addInputVariant(QString("NavigationFocused"), controller::StateController::ReadLambda([]() -> float { - auto offscreenUi = DependencyManager::get(); - return offscreenUi->navigationFocused() ? 1.0 : 0.0; - })); - - userInputMapper->registerDevice(_applicationStateDevice); + _applicationStateDevice->setInputVariant(STATE_IN_HMD, []() -> float { + return qApp->isHMDMode() ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_SNAP_TURN, []() -> float { + return qApp->getMyAvatar()->getSnapTurn() ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_GROUNDED, []() -> float { + return qApp->getMyAvatar()->getCharacterController()->onGround() ? 1 : 0; + }); + _applicationStateDevice->setInputVariant(STATE_NAV_FOCUSED, []() -> float { + return DependencyManager::get()->navigationFocused() ? 1 : 0; + }); // Setup the keyboardMouseDevice and the user input mapper with the default bindings userInputMapper->registerDevice(_keyboardMouseDevice->getInputDevice()); - userInputMapper->loadDefaultMapping(userInputMapper->getStandardDeviceID()); // force the model the look at the correct directory (weird order of operations issue) scriptEngines->setScriptsLocation(scriptEngines->getScriptsLocation()); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 275aeedb6b..c2e64ca19e 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -33,8 +33,10 @@ static QVariantMap createDeviceMap(const controller::InputDevice::Pointer device for (const auto& inputMapping : userInputMapper->getAvailableInputs(device->getDeviceID())) { const auto& input = inputMapping.first; const auto inputName = QString(inputMapping.second).remove(SANITIZE_NAME_EXPRESSION); +#ifdef DEBUG qCDebug(controllers) << "\tInput " << input.getChannel() << (int)input.getType() << QString::number(input.getID(), 16) << ": " << inputName; +#endif deviceMap.insert(inputName, input.getID()); } return deviceMap; diff --git a/libraries/controllers/src/controllers/StateController.cpp b/libraries/controllers/src/controllers/StateController.cpp index 1d39449e4d..9b0301eb14 100644 --- a/libraries/controllers/src/controllers/StateController.cpp +++ b/libraries/controllers/src/controllers/StateController.cpp @@ -19,32 +19,39 @@ namespace controller { +static QStringList stateVariables; + +void StateController::setStateVariables(const QStringList& newStateVariables) { + stateVariables = newStateVariables; +} + StateController::StateController() : InputDevice("Application") { + _deviceID = UserInputMapper::STATE_DEVICE; + for (const auto& variable : stateVariables) { + _namedReadLambdas[variable] = []()->float{ return 0; }; + } } -StateController::~StateController() { -} - -void StateController::update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) {} - -void StateController::focusOutEvent() {} - -void StateController::addInputVariant(QString name, ReadLambda lambda) { - _namedReadLambdas.push_back(NamedReadLambda(name, lambda)); +void StateController::setInputVariant(const QString& name, ReadLambda lambda) { + // All state variables must be predeclared; + Q_ASSERT(_namedReadLambdas.contains(name)); + _namedReadLambdas[name] = lambda; } Input::NamedVector StateController::getAvailableInputs() const { Input::NamedVector availableInputs; int i = 0; - for (auto& pair : _namedReadLambdas) { - availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), pair.first)); + for (const auto& name : stateVariables) { + availableInputs.push_back(Input::NamedPair(Input(_deviceID, i, ChannelType::BUTTON), name)); i++; } return availableInputs; } EndpointPointer StateController::createEndpoint(const Input& input) const { - return std::make_shared(_namedReadLambdas[input.getChannel()].second); + auto name = stateVariables[input.getChannel()]; + ReadLambda& readLambda = const_cast&>(_namedReadLambdas)[name]; + return std::make_shared(readLambda); } } \ No newline at end of file diff --git a/libraries/controllers/src/controllers/StateController.h b/libraries/controllers/src/controllers/StateController.h index ed36e4f838..57414c3ae8 100644 --- a/libraries/controllers/src/controllers/StateController.h +++ b/libraries/controllers/src/controllers/StateController.h @@ -24,26 +24,28 @@ class StateController : public QObject, public InputDevice { Q_PROPERTY(QString name READ getName) public: + using Pointer = std::shared_ptr; + using ReadLambda = std::function; + using NamedReadLambda = QPair; + + static void setStateVariables(const QStringList& stateVariables); + + StateController(); + const QString& getName() const { return _name; } // Device functions virtual Input::NamedVector getAvailableInputs() const override; - virtual void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override; - virtual void focusOutEvent() override; - StateController(); - virtual ~StateController(); + void update(float deltaTime, const InputCalibrationData& inputCalibrationData, bool jointsCaptured) override {} + void focusOutEvent() override {} - using ReadLambda = std::function; - using NamedReadLambda = QPair; - - void addInputVariant(QString name, ReadLambda lambda); - - virtual EndpointPointer createEndpoint(const Input& input) const override; + void setInputVariant(const QString& name, ReadLambda lambda); + EndpointPointer createEndpoint(const Input& input) const override; protected: - QVector _namedReadLambdas; + QHash _namedReadLambdas; }; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 33739c7b9d..2b7d837aa5 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -44,13 +44,15 @@ namespace controller { - const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; const uint16_t UserInputMapper::STANDARD_DEVICE = 0; + const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0x00FF; + const uint16_t UserInputMapper::STATE_DEVICE = Input::INVALID_DEVICE - 0x0100; } // Default contruct allocate the poutput size with the current hardcoded action channels controller::UserInputMapper::UserInputMapper() { registerDevice(std::make_shared()); + registerDevice(_stateDevice = std::make_shared()); registerDevice(std::make_shared()); } @@ -138,7 +140,6 @@ void UserInputMapper::loadDefaultMapping(uint16 deviceID) { return; } - auto mapping = loadMappings(proxyEntry->second->getDefaultMappingConfigs()); if (mapping) { auto prevMapping = _mappingsByDevice[deviceID]; @@ -235,6 +236,10 @@ void fixBisectedAxis(float& full, float& negative, float& positive) { void UserInputMapper::update(float deltaTime) { Locker locker(_lock); + + static uint64_t updateCount = 0; + ++updateCount; + // Reset the axis state for next loop for (auto& channel : _actionStates) { channel = 0.0f; @@ -694,11 +699,17 @@ Pose UserInputMapper::getPose(const Input& input) const { return getPose(endpoint); } -Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { +Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile, bool enable) { Locker locker(_lock); if (jsonFile.isEmpty()) { return Mapping::Pointer(); } + // Each mapping only needs to be loaded once + static QSet loaded; + if (loaded.contains(jsonFile)) { + return Mapping::Pointer(); + } + loaded.insert(jsonFile); QString json; { QFile file(jsonFile); @@ -707,7 +718,11 @@ Mapping::Pointer UserInputMapper::loadMapping(const QString& jsonFile) { } file.close(); } - return parseMapping(json); + auto result = parseMapping(json); + if (enable) { + enableMapping(result->name); + } + return result; } MappingPointer UserInputMapper::loadMappings(const QStringList& jsonFiles) { @@ -961,7 +976,7 @@ Route::Pointer UserInputMapper::parseRoute(const QJsonValue& value) { result->json = QString(QJsonDocument(obj).toJson()); result->source = parseSource(obj[JSON_CHANNEL_FROM]); result->debug = obj[JSON_CHANNEL_DEBUG].toBool(); - result->debug = obj[JSON_CHANNEL_PEEK].toBool(); + result->peek = obj[JSON_CHANNEL_PEEK].toBool(); if (!result->source) { qWarning() << "Invalid route source " << obj[JSON_CHANNEL_FROM]; return Route::Pointer(); @@ -1033,7 +1048,7 @@ Mapping::Pointer UserInputMapper::parseMapping(const QJsonValue& json) { Route::Pointer route = parseRoute(channelIt); if (!route) { - qWarning() << "Couldn't parse route:" << mapping->name << channelIt; + qWarning() << "Couldn't parse route:" << mapping->name << QString(QJsonDocument(channelIt.toObject()).toJson()); continue; } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 1021032b40..95cc629c73 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -30,6 +30,7 @@ #include "DeviceProxy.h" #include "StandardControls.h" #include "Actions.h" +#include "StateController.h" namespace controller { @@ -55,8 +56,9 @@ namespace controller { using uint16 = uint16_t; using uint32 = uint32_t; - static const uint16_t ACTIONS_DEVICE; static const uint16_t STANDARD_DEVICE; + static const uint16_t ACTIONS_DEVICE; + static const uint16_t STATE_DEVICE; UserInputMapper(); virtual ~UserInputMapper(); @@ -100,10 +102,11 @@ namespace controller { const DevicesMap& getDevices() { return _registeredDevices; } uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } InputDevice::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } + StateController::Pointer getStateDevice() { return _stateDevice; } MappingPointer newMapping(const QString& mappingName); MappingPointer parseMapping(const QString& json); - MappingPointer loadMapping(const QString& jsonFile); + MappingPointer loadMapping(const QString& jsonFile, bool enable = false); MappingPointer loadMappings(const QStringList& jsonFiles); void loadDefaultMapping(uint16 deviceID); @@ -120,6 +123,7 @@ namespace controller { // GetFreeDeviceID should be called before registering a device to use an ID not used by a different device. uint16 getFreeDeviceID() { return _nextFreeDeviceID++; } DevicesMap _registeredDevices; + StateController::Pointer _stateDevice; uint16 _nextFreeDeviceID = STANDARD_DEVICE + 1; std::vector _actionStates = std::vector(toInt(Action::NUM_ACTIONS), 0.0f); diff --git a/libraries/controllers/src/controllers/impl/Endpoint.cpp b/libraries/controllers/src/controllers/impl/Endpoint.cpp index e771b1916f..289f83b228 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.cpp +++ b/libraries/controllers/src/controllers/impl/Endpoint.cpp @@ -12,5 +12,8 @@ // warning LNK4221: This object file does not define any previously undefined public symbols, // so it will not be used by any link operation that consumes this library // -//#include "Endpoint.h" +#include "Endpoint.h" +namespace controller { + Endpoint::WriteLambda DEFAULT_WRITE_LAMBDA = [](float) {}; +} diff --git a/libraries/controllers/src/controllers/impl/Endpoint.h b/libraries/controllers/src/controllers/impl/Endpoint.h index 475dc035bb..a938dd30b6 100644 --- a/libraries/controllers/src/controllers/impl/Endpoint.h +++ b/libraries/controllers/src/controllers/impl/Endpoint.h @@ -67,6 +67,23 @@ namespace controller { WriteLambda _writeLambda; }; + extern Endpoint::WriteLambda DEFAULT_WRITE_LAMBDA; + + class LambdaRefEndpoint : public Endpoint { + public: + using Endpoint::apply; + LambdaRefEndpoint(const ReadLambda& readLambda, const WriteLambda& writeLambda = DEFAULT_WRITE_LAMBDA) + : Endpoint(Input::INVALID_INPUT), _readLambda(readLambda), _writeLambda(writeLambda) { + } + + virtual float peek() const override { return _readLambda(); } + virtual void apply(float value, const Pointer& source) override { _writeLambda(value); } + + private: + const ReadLambda& _readLambda; + const WriteLambda& _writeLambda; + }; + class VirtualEndpoint : public Endpoint { public: