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);