From e8be92cab837ec32764134663261440af0d73d7a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 09:18:44 -0700 Subject: [PATCH 1/6] Adding input action event --- interface/src/Application.cpp | 1 - .../scripting/ControllerScriptingInterface.h | 2 -- .../src/controllers/ScriptingInterface.cpp | 3 +++ .../src/controllers/ScriptingInterface.h | 3 +++ .../src/controllers/UserInputMapper.cpp | 18 ++++++++++++++++++ .../src/controllers/UserInputMapper.h | 2 ++ 6 files changed, 26 insertions(+), 3 deletions(-) diff --git a/interface/src/Application.cpp b/interface/src/Application.cpp index 0a634425bc..92680ed3e0 100644 --- a/interface/src/Application.cpp +++ b/interface/src/Application.cpp @@ -627,7 +627,6 @@ Application::Application(int& argc, char** argv, QElapsedTimer& startupTimer) : // Setup the userInputMapper with the actions auto userInputMapper = DependencyManager::get(); - connect(userInputMapper.data(), &UserInputMapper::actionEvent, _controllerScriptingInterface, &ControllerScriptingInterface::actionEvent); connect(userInputMapper.data(), &UserInputMapper::actionEvent, [this](int action, float state) { if (state && action == toInt(controller::Action::TOGGLE_MUTE)) { DependencyManager::get()->toggleMute(); diff --git a/interface/src/scripting/ControllerScriptingInterface.h b/interface/src/scripting/ControllerScriptingInterface.h index 8be530c6ce..3133f93804 100644 --- a/interface/src/scripting/ControllerScriptingInterface.h +++ b/interface/src/scripting/ControllerScriptingInterface.h @@ -120,8 +120,6 @@ signals: void wheelEvent(const WheelEvent& event); - void actionEvent(int action, float state); - private: QString sanatizeName(const QString& name); /// makes a name clean for inclusing in JavaScript diff --git a/libraries/controllers/src/controllers/ScriptingInterface.cpp b/libraries/controllers/src/controllers/ScriptingInterface.cpp index 40c65549a8..bb09705684 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.cpp +++ b/libraries/controllers/src/controllers/ScriptingInterface.cpp @@ -44,6 +44,9 @@ static QVariantMap createDeviceMap(const controller::DeviceProxy::Pointer device controller::ScriptingInterface::ScriptingInterface() { auto userInputMapper = DependencyManager::get(); + connect(userInputMapper.data(), &UserInputMapper::actionEvent, this, &controller::ScriptingInterface::actionEvent); + connect(userInputMapper.data(), &UserInputMapper::inputEvent, this, &controller::ScriptingInterface::inputEvent); + // FIXME make this thread safe connect(userInputMapper.data(), &UserInputMapper::hardwareChanged, [=] { updateMaps(); diff --git a/libraries/controllers/src/controllers/ScriptingInterface.h b/libraries/controllers/src/controllers/ScriptingInterface.h index 85b1c3c6d9..db724044fa 100644 --- a/libraries/controllers/src/controllers/ScriptingInterface.h +++ b/libraries/controllers/src/controllers/ScriptingInterface.h @@ -129,6 +129,9 @@ namespace controller { virtual void captureActionEvents() { _actionsCaptured = true; } virtual void releaseActionEvents() { _actionsCaptured = false; } + signals: + void actionEvent(int action, float state); + void inputEvent(int action, float state); private: // Update the exposed variant maps reporting active hardware diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 8cd6618bfc..07f1f975a2 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -516,6 +516,24 @@ void UserInputMapper::update(float deltaTime) { } // TODO: emit signal for pose changes } + + auto standardInputs = getStandardInputs(); + if (_lastStandardStates.size() != standardInputs.size()) { + _lastStandardStates.resize(standardInputs.size()); + for (auto& lastValue : _lastStandardStates) { + lastValue = 0; + } + } + + for (int i = 0; i < standardInputs.size(); ++i) { + const auto& input = standardInputs[i].first; + float value = getValue(input); + float& oldValue = _lastStandardStates[i]; + if (value != oldValue) { + oldValue = value; + emit inputEvent(input.id, value); + } + } } Input::NamedVector UserInputMapper::getAvailableInputs(uint16 deviceID) const { diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 4bfedfcf1a..0989fdb311 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -117,6 +117,7 @@ namespace controller { signals: void actionEvent(int action, float state); + void inputEvent(int input, float state); void hardwareChanged(); protected: @@ -130,6 +131,7 @@ namespace controller { std::vector _actionScales = std::vector(toInt(Action::NUM_ACTIONS), 1.0f); std::vector _lastActionStates = std::vector(toInt(Action::NUM_ACTIONS), 0.0f); std::vector _poseStates = std::vector(toInt(Action::NUM_ACTIONS)); + std::vector _lastStandardStates = std::vector(); glm::mat4 _sensorToWorldMat; From be148686a76d5c6b8115dc79a61effb041f011b2 Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 09:34:01 -0700 Subject: [PATCH 2/6] Fixing omnitool --- examples/libraries/omniTool.js | 54 ++++++++++++++++++---------------- examples/libraries/utils.js | 8 +---- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index 26c299cdfb..a7fee1a4cf 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -15,16 +15,18 @@ Script.include("omniTool/models/invisibleWand.js"); OmniToolModules = {}; OmniToolModuleType = null; +LOG_DEBUG = 1; -OmniTool = function(side) { +OmniTool = function(left) { this.OMNI_KEY = "OmniTool"; this.MAX_FRAMERATE = 60; this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE - this.SIDE = side; - this.PALM = 2 * side; - this.ACTION = findAction(side ? "ACTION2" : "ACTION1"); - this.ALT_ACTION = findAction(side ? "ACTION1" : "ACTION2"); - + this.left = left; + var actions = Controller.Actions; + var standard = Controller.Standard; + this.palmControl = left ? actions.LeftHand : actions.RightHand; + this.action = left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb; + logDebug("Init OmniTool " + (left ? "left" : "right")); this.highlighter = new Highlighter(); this.ignoreEntities = {}; this.nearestOmniEntity = { @@ -47,21 +49,21 @@ OmniTool = function(side) { this.showWand(false); // Connect to desired events - var _this = this; - Controller.actionEvent.connect(function(action, state) { - _this.onActionEvent(action, state); + var that = this; + Controller.inputEvent.connect(function(action, state) { + that.onInputEvent(action, state); }); Script.update.connect(function(deltaTime) { - _this.lastUpdateInterval += deltaTime; - if (_this.lastUpdateInterval >= _this.UPDATE_INTERVAL) { - _this.onUpdate(_this.lastUpdateInterval); - _this.lastUpdateInterval = 0; + that.lastUpdateInterval += deltaTime; + if (that.lastUpdateInterval >= that.UPDATE_INTERVAL) { + that.onUpdate(that.lastUpdateInterval); + that.lastUpdateInterval = 0; } }); Script.scriptEnding.connect(function() { - _this.onCleanup(); + that.onCleanup(); }); } @@ -86,15 +88,14 @@ OmniTool.prototype.onCleanup = function(action) { this.unloadModule(); } -OmniTool.prototype.onActionEvent = function(action, state) { - // FIXME figure out the issues when only one spatial controller is active - // logDebug("Action: " + action + " " + state); - - if (this.module && this.module.onActionEvent) { - this.module.onActionEvent(action, state); +OmniTool.prototype.onInputEvent = function(action, state) { + // FIXME figure out the issues when only one spatial controller is active + var actionNames = Controller.getActionNames(); + if (this.module && this.module.onInputEvent) { + this.module.onInputEvent(action, state); } - if (action == this.ACTION) { + if (action == this.action) { if (state) { this.onClick(); } else { @@ -127,7 +128,7 @@ OmniTool.prototype.setActive = function(active) { if (active === this.active) { return; } - logDebug("OmniTool changing active state: " + active); + logDebug("OmniTool " + this.left + " changing active state: " + active); this.active = active; this.model.setVisible(this.active); if (this.module && this.module.onActiveChanged) { @@ -138,17 +139,17 @@ OmniTool.prototype.setActive = function(active) { OmniTool.prototype.onUpdate = function(deltaTime) { // FIXME this returns data if either the left or right controller is not on the base - this.position = Controller.getSpatialControlPosition(this.PALM); + this.pose = Controller.getPoseValue(this.palmControl); + this.position = this.left ? MyAvatar.leftHandTipPosition : MyAvatar.rightHandTipPosition; // When on the base, hydras report a position of 0 this.setActive(Vec3.length(this.position) > 0.001); if (!this.active) { return; } - if (this.model) { // Update the wand - var rawRotation = Controller.getSpatialControlRawRotation(this.PALM); + var rawRotation = this.pose.rotation; this.rotation = Quat.multiply(MyAvatar.orientation, rawRotation); this.model.setTransform({ rotation: this.rotation, @@ -306,6 +307,7 @@ OmniTool.prototype.scan = function() { } OmniTool.prototype.unloadModule = function() { + logDebug("Unloading omniTool module") if (this.module && this.module.onUnload) { this.module.onUnload(); } @@ -348,4 +350,4 @@ OmniTool.prototype.activateNewOmniModule = function() { } // FIXME find a good way to sync the two omni tools -OMNI_TOOLS = [ new OmniTool(0), new OmniTool(1) ]; +OMNI_TOOLS = [ new OmniTool(true), new OmniTool(false) ]; diff --git a/examples/libraries/utils.js b/examples/libraries/utils.js index ab86007e4b..25900471c1 100644 --- a/examples/libraries/utils.js +++ b/examples/libraries/utils.js @@ -31,13 +31,7 @@ scaleLine = function (start, end, scale) { } findAction = function(name) { - var actions = Controller.getAllActions(); - for (var i = 0; i < actions.length; i++) { - if (actions[i].actionName == name) { - return i; - } - } - return 0; + return Controller.findAction(name); } addLine = function(origin, vector, color) { From 56deef9d6ebfb1950e08eed5cb141f1dfbdacf2d Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 12:15:30 -0700 Subject: [PATCH 3/6] Moving omniTool to a route mapped input --- examples/libraries/omniTool.js | 31 ++++----- .../controllers/src/controllers/Endpoint.h | 1 + .../controllers/src/controllers/Forward.h | 37 +++++++++++ .../src/controllers/UserInputMapper.cpp | 51 ++++++++++++++- .../src/controllers/UserInputMapper.h | 64 +++++++++---------- .../controllers/impl/RouteBuilderProxy.cpp | 5 ++ .../src/controllers/impl/RouteBuilderProxy.h | 1 + 7 files changed, 139 insertions(+), 51 deletions(-) create mode 100644 libraries/controllers/src/controllers/Forward.h diff --git a/examples/libraries/omniTool.js b/examples/libraries/omniTool.js index a7fee1a4cf..4c995d6528 100644 --- a/examples/libraries/omniTool.js +++ b/examples/libraries/omniTool.js @@ -22,10 +22,10 @@ OmniTool = function(left) { this.MAX_FRAMERATE = 60; this.UPDATE_INTERVAL = 1.0 / this.MAX_FRAMERATE this.left = left; + this.triggered = false; var actions = Controller.Actions; var standard = Controller.Standard; this.palmControl = left ? actions.LeftHand : actions.RightHand; - this.action = left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb; logDebug("Init OmniTool " + (left ? "left" : "right")); this.highlighter = new Highlighter(); this.ignoreEntities = {}; @@ -50,9 +50,6 @@ OmniTool = function(left) { // Connect to desired events var that = this; - Controller.inputEvent.connect(function(action, state) { - that.onInputEvent(action, state); - }); Script.update.connect(function(deltaTime) { that.lastUpdateInterval += deltaTime; @@ -65,6 +62,12 @@ OmniTool = function(left) { Script.scriptEnding.connect(function() { that.onCleanup(); }); + + this.mapping = Controller.newMapping(); + this.mapping.from(left ? standard.LeftPrimaryThumb : standard.RightPrimaryThumb).to(function(value){ + that.onUpdateTrigger(value); + }) + this.mapping.enable(); } OmniTool.prototype.showWand = function(show) { @@ -83,29 +86,23 @@ OmniTool.prototype.showWand = function(show) { } } - OmniTool.prototype.onCleanup = function(action) { + this.mapping.disable(); this.unloadModule(); } -OmniTool.prototype.onInputEvent = function(action, state) { - // FIXME figure out the issues when only one spatial controller is active - var actionNames = Controller.getActionNames(); - if (this.module && this.module.onInputEvent) { - this.module.onInputEvent(action, state); - } - if (action == this.action) { - if (state) { +OmniTool.prototype.onUpdateTrigger = function (value) { + //logDebug("Trigger update value " + value); + var triggered = value != 0; + if (triggered != this.triggered) { + this.triggered = triggered; + if (this.triggered) { this.onClick(); } else { this.onRelease(); } } - - // FIXME Does not work - //// with only one controller active (listed as 2 here because 'tip' + 'palm') - //// then treat the alt action button as the action button } OmniTool.prototype.getOmniToolData = function(entityId) { diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 7a94b06e7e..36ff9388b0 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -36,6 +36,7 @@ namespace controller { using WriteLambda = std::function; Endpoint(const Input& input) : _input(input) {} + virtual uint8_t priority() const { return 0x7f; } virtual float value() = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; virtual Pose pose() { return Pose(); } diff --git a/libraries/controllers/src/controllers/Forward.h b/libraries/controllers/src/controllers/Forward.h new file mode 100644 index 0000000000..e1a62556d4 --- /dev/null +++ b/libraries/controllers/src/controllers/Forward.h @@ -0,0 +1,37 @@ +// +// Created by Bradley Austin Davis 2015/10/20 +// Copyright 2015 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 +// + +#pragma once +#ifndef hifi_Controllers_Forward_h +#define hifi_Controllers_Forward_h + +namespace controller { + +class Endpoint; +using EndpointPointer = std::shared_ptr; +using EndpointList = std::list; + +class Filter; +using FilterPointer = std::shared_ptr; +using FilterList = std::list; + +class Route; +using RoutePointer = std::shared_ptr; +using RouteList = std::list; + +class Conditional; +using ConditionalPointer = std::shared_ptr; +using ConditionalList = std::list; + +class Mapping; +using MappingPointer = std::shared_ptr; +using MappingList = std::list; + +} + +#endif diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 07f1f975a2..0ec9ce899d 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -21,8 +21,13 @@ #include #include "StandardController.h" + #include "Logging.h" +#include "Endpoint.h" +#include "Route.h" +#include "Mapping.h" + namespace controller { const uint16_t UserInputMapper::ACTIONS_DEVICE = Input::INVALID_DEVICE - 0xFF; const uint16_t UserInputMapper::STANDARD_DEVICE = 0; @@ -211,6 +216,9 @@ public: } } + // Process later than normal + virtual uint8_t priority() const override { return 0x6F; } + virtual float value() override { float result = 0; for (auto& child : _children) { @@ -674,19 +682,27 @@ Input UserInputMapper::makeStandardInput(controller::StandardPoseChannel pose) { static auto lastDebugTime = usecTimestampNow(); static auto debugRoutes = false; +static auto debuggableRoutes = false; static const auto DEBUG_INTERVAL = USECS_PER_SECOND; void UserInputMapper::runMappings() { auto now = usecTimestampNow(); - if (now - lastDebugTime > DEBUG_INTERVAL) { + if (debuggableRoutes && now - lastDebugTime > DEBUG_INTERVAL) { lastDebugTime = now; debugRoutes = true; } + + if (debugRoutes) { + qCDebug(controllers) << "Beginning mapping frame"; + } static auto deviceNames = getDeviceNames(); for (auto endpointEntry : this->_endpointsByInput) { endpointEntry.second->reset(); } + if (debugRoutes) { + qCDebug(controllers) << "Processing device routes"; + } // Now process the current values for each level of the stack for (const auto& route : _deviceRoutes) { if (!route) { @@ -695,12 +711,19 @@ void UserInputMapper::runMappings() { applyRoute(route); } + if (debugRoutes) { + qCDebug(controllers) << "Processing standard routes"; + } for (const auto& route : _standardRoutes) { if (!route) { continue; } applyRoute(route); } + + if (debugRoutes) { + qCDebug(controllers) << "Done with mappings"; + } debugRoutes = false; } @@ -1174,6 +1197,22 @@ Mapping::Pointer UserInputMapper::parseMapping(const QString& json) { return parseMapping(doc.object()); } +template +bool hasDebuggableRoute(const T& routes) { + for (auto route : routes) { + if (route->debug) { + return true; + } + } + return false; +} + +void sortRoutes(Route::List& routes) { + std::stable_sort(routes.begin(), routes.end(), [](const Route::Pointer a, const Route::Pointer b) { + return a->source->priority() < b->source->priority(); + }); +} + void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { Locker locker(_lock); @@ -1186,12 +1225,18 @@ void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { return (value->source->getInput().device != STANDARD_DEVICE); }); _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); + sortRoutes(_standardRoutes); Route::List deviceRoutes = mapping->routes; deviceRoutes.remove_if([](const Route::Pointer& value) { return (value->source->getInput().device == STANDARD_DEVICE); }); _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end()); + sortRoutes(_standardRoutes); + + if (!debuggableRoutes) { + debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes); + } } void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { @@ -1204,6 +1249,10 @@ void UserInputMapper::disableMapping(const Mapping::Pointer& mapping) { _standardRoutes.remove_if([&](const Route::Pointer& value) { return routeSet.count(value) != 0; }); + + if (debuggableRoutes) { + debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes); + } } } diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 0989fdb311..31924b4724 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -23,13 +23,12 @@ #include #include +#include "Forward.h" #include "Pose.h" #include "Input.h" #include "InputDevice.h" #include "DeviceProxy.h" #include "StandardControls.h" -#include "Mapping.h" -#include "Endpoint.h" #include "Actions.h" namespace controller { @@ -45,15 +44,14 @@ namespace controller { public: using InputPair = Input::NamedPair; // FIXME move to unordered set / map - using EndpointToInputMap = std::map; - using MappingNameMap = std::map; - using MappingDeviceMap = std::map; - using MappingStack = std::list; - using InputToEndpointMap = std::map; - using EndpointSet = std::unordered_set; - using EndpointOverrideMap = std::map; - using EndpointPair = std::pair; - using EndpointPairMap = std::map; + using EndpointToInputMap = std::map; + using MappingNameMap = std::map; + using MappingDeviceMap = std::map; + using MappingStack = std::list; + using InputToEndpointMap = std::map; + using EndpointSet = std::unordered_set; + using EndpointPair = std::pair; + using EndpointPairMap = std::map; using DevicesMap = std::map; using uint16 = uint16_t; using uint32 = uint32_t; @@ -107,9 +105,9 @@ namespace controller { uint16 getStandardDeviceID() const { return STANDARD_DEVICE; } DeviceProxy::Pointer getStandardDevice() { return _registeredDevices[getStandardDeviceID()]; } - Mapping::Pointer newMapping(const QString& mappingName); - Mapping::Pointer parseMapping(const QString& json); - Mapping::Pointer loadMapping(const QString& jsonFile); + MappingPointer newMapping(const QString& mappingName); + MappingPointer parseMapping(const QString& json); + MappingPointer loadMapping(const QString& jsonFile); void enableMapping(const QString& mappingName, bool enable = true); float getValue(const Input& input) const; @@ -138,30 +136,30 @@ namespace controller { int recordDeviceOfType(const QString& deviceName); QHash _deviceCounts; - float getValue(const Endpoint::Pointer& endpoint) const; - Pose getPose(const Endpoint::Pointer& endpoint) const; + float getValue(const EndpointPointer& endpoint) const; + Pose getPose(const EndpointPointer& endpoint) const; friend class RouteBuilderProxy; friend class MappingBuilderProxy; void runMappings(); - void applyRoute(const Route::Pointer& route); - void enableMapping(const Mapping::Pointer& mapping); - void disableMapping(const Mapping::Pointer& mapping); - Endpoint::Pointer endpointFor(const QJSValue& endpoint); - Endpoint::Pointer endpointFor(const QScriptValue& endpoint); - Endpoint::Pointer endpointFor(const Input& endpoint) const; - Endpoint::Pointer compositeEndpointFor(Endpoint::Pointer first, Endpoint::Pointer second); + void applyRoute(const RoutePointer& route); + void enableMapping(const MappingPointer& mapping); + void disableMapping(const MappingPointer& mapping); + EndpointPointer endpointFor(const QJSValue& endpoint); + EndpointPointer endpointFor(const QScriptValue& endpoint); + EndpointPointer endpointFor(const Input& endpoint) const; + EndpointPointer compositeEndpointFor(EndpointPointer first, EndpointPointer second); - Mapping::Pointer parseMapping(const QJsonValue& json); - Route::Pointer parseRoute(const QJsonValue& value); - Endpoint::Pointer parseDestination(const QJsonValue& value); - Endpoint::Pointer parseSource(const QJsonValue& value); - Endpoint::Pointer parseEndpoint(const QJsonValue& value); - Conditional::Pointer parseConditional(const QJsonValue& value); + MappingPointer parseMapping(const QJsonValue& json); + RoutePointer parseRoute(const QJsonValue& value); + EndpointPointer parseDestination(const QJsonValue& value); + EndpointPointer parseSource(const QJsonValue& value); + EndpointPointer parseEndpoint(const QJsonValue& value); + ConditionalPointer parseConditional(const QJsonValue& value); - static Filter::Pointer parseFilter(const QJsonValue& value); - static Filter::List parseFilters(const QJsonValue& value); + static FilterPointer parseFilter(const QJsonValue& value); + static FilterList parseFilters(const QJsonValue& value); InputToEndpointMap _endpointsByInput; EndpointToInputMap _inputsByEndpoint; @@ -170,8 +168,8 @@ namespace controller { MappingNameMap _mappingsByName; MappingDeviceMap _mappingsByDevice; - Route::List _deviceRoutes; - Route::List _standardRoutes; + RouteList _deviceRoutes; + RouteList _standardRoutes; using Locker = std::unique_lock; diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp index 186cf2e84e..c0d0758e4e 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.cpp @@ -39,6 +39,11 @@ void RouteBuilderProxy::to(const Endpoint::Pointer& destination) { deleteLater(); } +QObject* RouteBuilderProxy::debug(bool enable) { + _route->debug = enable; + return this; +} + QObject* RouteBuilderProxy::filterQml(const QJSValue& expression) { if (expression.isCallable()) { addFilter([=](float value) { diff --git a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h index 6bceba995a..0484c5890d 100644 --- a/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h +++ b/libraries/controllers/src/controllers/impl/RouteBuilderProxy.h @@ -35,6 +35,7 @@ class RouteBuilderProxy : public QObject { Q_INVOKABLE QObject* filterQml(const QJSValue& expression); Q_INVOKABLE void to(const QScriptValue& destination); + Q_INVOKABLE QObject* debug(bool enable = true); Q_INVOKABLE QObject* filter(const QScriptValue& expression); Q_INVOKABLE QObject* clamp(float min, float max); Q_INVOKABLE QObject* pulse(float interval); From 4110324b35ade2d8c71377b8aafaa73c1dbd572f Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 12:55:04 -0700 Subject: [PATCH 4/6] Add another bit to channel types so we can fit rumble --- libraries/controllers/src/controllers/Input.cpp | 9 ++++----- libraries/controllers/src/controllers/Input.h | 10 ++++++---- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/libraries/controllers/src/controllers/Input.cpp b/libraries/controllers/src/controllers/Input.cpp index 4f645c3f95..6f8bd547a2 100644 --- a/libraries/controllers/src/controllers/Input.cpp +++ b/libraries/controllers/src/controllers/Input.cpp @@ -9,10 +9,9 @@ #include "Input.h" namespace controller { - - const uint16_t Input::INVALID_DEVICE = 0xffff; - const uint16_t Input::INVALID_CHANNEL = 0x1fff; - const uint16_t Input::INVALID_TYPE = (uint16_t)ChannelType::INVALID; - const Input Input::INVALID_INPUT = Input(INVALID_DEVICE, INVALID_CHANNEL, ChannelType::INVALID); + const Input Input::INVALID_INPUT = Input(0x7fffffff); + const uint16_t Input::INVALID_DEVICE = Input::INVALID_INPUT.device; + const uint16_t Input::INVALID_CHANNEL = Input::INVALID_INPUT.channel; + const uint16_t Input::INVALID_TYPE = Input::INVALID_INPUT.type; } diff --git a/libraries/controllers/src/controllers/Input.h b/libraries/controllers/src/controllers/Input.h index 7382d365ec..db99e820da 100644 --- a/libraries/controllers/src/controllers/Input.h +++ b/libraries/controllers/src/controllers/Input.h @@ -16,10 +16,12 @@ namespace controller { enum class ChannelType { - INVALID = 0, - BUTTON = 1, + UNKNOWN = 0, + BUTTON, AXIS, POSE, + RUMBLE, + INVALID = 0x7 }; // Input is the unique identifier to find a n input channel of a particular device @@ -30,8 +32,8 @@ struct Input { uint32_t id{ 0 }; // by default Input is 0 meaning invalid struct { uint16_t device; // Up to 64K possible devices - uint16_t channel : 13 ; // 2^13 possible channel per Device - uint16_t type : 2; // 2 bits to store the Type directly in the ID + uint16_t channel : 12 ; // 2^12 possible channel per Device + uint16_t type : 3; // 2 bits to store the Type directly in the ID uint16_t padding : 1; // 2 bits to store the Type directly in the ID }; }; From 54c20a8dd7f094c752a469671e66aacb67ea062a Mon Sep 17 00:00:00 2001 From: Brad Davis Date: Fri, 23 Oct 2015 12:56:03 -0700 Subject: [PATCH 5/6] Taking a different tack on proper ordering of routes --- .../controllers/src/controllers/Endpoint.h | 1 - .../src/controllers/UserInputMapper.cpp | 94 +++++++++++-------- .../src/controllers/UserInputMapper.h | 7 +- 3 files changed, 60 insertions(+), 42 deletions(-) diff --git a/libraries/controllers/src/controllers/Endpoint.h b/libraries/controllers/src/controllers/Endpoint.h index 36ff9388b0..7a94b06e7e 100644 --- a/libraries/controllers/src/controllers/Endpoint.h +++ b/libraries/controllers/src/controllers/Endpoint.h @@ -36,7 +36,6 @@ namespace controller { using WriteLambda = std::function; Endpoint(const Input& input) : _input(input) {} - virtual uint8_t priority() const { return 0x7f; } virtual float value() = 0; virtual void apply(float newValue, float oldValue, const Pointer& source) = 0; virtual Pose pose() { return Pose(); } diff --git a/libraries/controllers/src/controllers/UserInputMapper.cpp b/libraries/controllers/src/controllers/UserInputMapper.cpp index 0ec9ce899d..e924718fca 100755 --- a/libraries/controllers/src/controllers/UserInputMapper.cpp +++ b/libraries/controllers/src/controllers/UserInputMapper.cpp @@ -216,9 +216,6 @@ public: } } - // Process later than normal - virtual uint8_t priority() const override { return 0x6F; } - virtual float value() override { float result = 0; for (auto& child : _children) { @@ -234,7 +231,15 @@ public: qFatal("AnyEndpoint is read only"); } - virtual bool writeable() const override { return false; } + // AnyEndpoint is used for reading, so return false if any child returns false (has been written to) + virtual bool writeable() const override { + for (auto& child : _children) { + if (!child->writeable()) { + return false; + } + } + return true; + } virtual bool readable() const override { for (auto& child : _children) { @@ -286,13 +291,11 @@ public: virtual void apply(const Pose& newValue, const Pose& oldValue, const Pointer& source) override { } - virtual bool writeable() const { return !_written; } + virtual bool writeable() const { return false; } virtual bool readable() const { return !_read; } - virtual void reset() { _written = _read = false; } + virtual void reset() { _read = false; } private: - - bool _written { false }; bool _read { false }; }; @@ -695,7 +698,6 @@ void UserInputMapper::runMappings() { if (debugRoutes) { qCDebug(controllers) << "Beginning mapping frame"; } - static auto deviceNames = getDeviceNames(); for (auto endpointEntry : this->_endpointsByInput) { endpointEntry.second->reset(); } @@ -704,22 +706,12 @@ void UserInputMapper::runMappings() { qCDebug(controllers) << "Processing device routes"; } // Now process the current values for each level of the stack - for (const auto& route : _deviceRoutes) { - if (!route) { - continue; - } - applyRoute(route); - } + applyRoutes(_deviceRoutes); if (debugRoutes) { qCDebug(controllers) << "Processing standard routes"; } - for (const auto& route : _standardRoutes) { - if (!route) { - continue; - } - applyRoute(route); - } + applyRoutes(_standardRoutes); if (debugRoutes) { qCDebug(controllers) << "Done with mappings"; @@ -727,21 +719,53 @@ void UserInputMapper::runMappings() { debugRoutes = false; } -void UserInputMapper::applyRoute(const Route::Pointer& route) { +// Encapsulate the logic that routes should not be read before they are written +void UserInputMapper::applyRoutes(const Route::List& routes) { + Route::List deferredRoutes; + + for (const auto& route : routes) { + if (!route) { + continue; + } + + // Try all the deferred routes + deferredRoutes.remove_if([](Route::Pointer route) { + return UserInputMapper::applyRoute(route); + }); + + if (!applyRoute(route)) { + deferredRoutes.push_back(route); + } + } + + bool force = true; + for (const auto& route : deferredRoutes) { + UserInputMapper::applyRoute(route, force); + } +} + + +bool UserInputMapper::applyRoute(const Route::Pointer& route, bool force) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Applying route " << route->json; } + // If the source hasn't been written yet, defer processing of this route + auto source = route->source; + if (!force && source->writeable()) { + return false; + } + if (route->conditional) { + // FIXME for endpoint conditionals we need to check if they've been written if (!route->conditional->satisfied()) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Conditional failed"; } - return; + return true; } } - auto source = route->source; // Most endpoints can only be read once (though a given mapping can route them to // multiple places). Consider... If the default is to wire the A button to JUMP @@ -752,7 +776,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Source unreadable"; } - return; + return true; } auto destination = route->destination; @@ -762,14 +786,14 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Bad Destination"; } - return; + return true; } if (!destination->writeable()) { if (debugRoutes && route->debug) { qCDebug(controllers) << "Destination unwritable"; } - return; + return true; } // Fetch the value, may have been overriden by previous loopback routes @@ -805,6 +829,7 @@ void UserInputMapper::applyRoute(const Route::Pointer& route) { destination->apply(value, 0, source); } + return true; } Endpoint::Pointer UserInputMapper::endpointFor(const QJSValue& endpoint) { @@ -910,12 +935,12 @@ void UserInputMapper::enableMapping(const QString& mappingName, bool enable) { } } -float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) const { - Locker locker(_lock); +float UserInputMapper::getValue(const Endpoint::Pointer& endpoint) { return endpoint->value(); } float UserInputMapper::getValue(const Input& input) const { + Locker locker(_lock); auto endpoint = endpointFor(input); if (!endpoint) { return 0; @@ -923,7 +948,7 @@ float UserInputMapper::getValue(const Input& input) const { return endpoint->value(); } -Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) const { +Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) { if (!endpoint->isPose()) { return Pose(); } @@ -931,6 +956,7 @@ Pose UserInputMapper::getPose(const Endpoint::Pointer& endpoint) const { } Pose UserInputMapper::getPose(const Input& input) const { + Locker locker(_lock); auto endpoint = endpointFor(input); if (!endpoint) { return Pose(); @@ -1207,12 +1233,6 @@ bool hasDebuggableRoute(const T& routes) { return false; } -void sortRoutes(Route::List& routes) { - std::stable_sort(routes.begin(), routes.end(), [](const Route::Pointer a, const Route::Pointer b) { - return a->source->priority() < b->source->priority(); - }); -} - void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { Locker locker(_lock); @@ -1225,14 +1245,12 @@ void UserInputMapper::enableMapping(const Mapping::Pointer& mapping) { return (value->source->getInput().device != STANDARD_DEVICE); }); _standardRoutes.insert(_standardRoutes.begin(), standardRoutes.begin(), standardRoutes.end()); - sortRoutes(_standardRoutes); Route::List deviceRoutes = mapping->routes; deviceRoutes.remove_if([](const Route::Pointer& value) { return (value->source->getInput().device == STANDARD_DEVICE); }); _deviceRoutes.insert(_deviceRoutes.begin(), deviceRoutes.begin(), deviceRoutes.end()); - sortRoutes(_standardRoutes); if (!debuggableRoutes) { debuggableRoutes = hasDebuggableRoute(_deviceRoutes) || hasDebuggableRoute(_standardRoutes); diff --git a/libraries/controllers/src/controllers/UserInputMapper.h b/libraries/controllers/src/controllers/UserInputMapper.h index 31924b4724..319037fcb1 100644 --- a/libraries/controllers/src/controllers/UserInputMapper.h +++ b/libraries/controllers/src/controllers/UserInputMapper.h @@ -136,14 +136,15 @@ namespace controller { int recordDeviceOfType(const QString& deviceName); QHash _deviceCounts; - float getValue(const EndpointPointer& endpoint) const; - Pose getPose(const EndpointPointer& endpoint) const; + static float getValue(const EndpointPointer& endpoint); + static Pose getPose(const EndpointPointer& endpoint); friend class RouteBuilderProxy; friend class MappingBuilderProxy; void runMappings(); - void applyRoute(const RoutePointer& route); + static void applyRoutes(const RouteList& route); + static bool applyRoute(const RoutePointer& route, bool force = false); void enableMapping(const MappingPointer& mapping); void disableMapping(const MappingPointer& mapping); EndpointPointer endpointFor(const QJSValue& endpoint); From 7e3192d0f6d4e15baafc74e1205bb06f2251d8b0 Mon Sep 17 00:00:00 2001 From: Brad Hefta-Gaub Date: Fri, 23 Oct 2015 14:04:06 -0700 Subject: [PATCH 6/6] fix drumstick.js fix drumstick.js --- examples/controllers/hydra/drumStick.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/controllers/hydra/drumStick.js b/examples/controllers/hydra/drumStick.js index e59528aa80..97a8bd856d 100644 --- a/examples/controllers/hydra/drumStick.js +++ b/examples/controllers/hydra/drumStick.js @@ -43,7 +43,8 @@ strokeSpeed[1] = 0.0; function checkSticks(deltaTime) { for (var palm = 0; palm < 2; palm++) { - var palmVelocity = Controller.getSpatialControlVelocity(palm * 2 + 1); + var handPose = (palm == 0) ? MyAvatar.leftHandPose : MyAvatar.rightHandPose; + var palmVelocity = handPose.velocity; var speed = length(palmVelocity); const TRIGGER_SPEED = 0.30; // Lower this value to let you 'drum' more gently @@ -64,7 +65,7 @@ function checkSticks(deltaTime) { if ((palmVelocity.y > 0.0) || (speed < STOP_SPEED)) { state[palm] = 0; - var options = { position: Controller.getSpatialControlPosition(palm * 2 + 1) }; + var options = { position: handPose.translation }; if (strokeSpeed[palm] > 1.0) { strokeSpeed[palm] = 1.0; } options.volume = strokeSpeed[palm];